4 분 소요


현재까지 진행 상황

  • 회원가입 기능 완료
    • BaseTimeEntity 생성 완료
    • User Entity 생성 완료
    • UserRepositoryImpl, UserService를 통해서 DB에 저장되는지 Test Code로 확인.
    • 중복 회원 검증
    • 회원가입 관련 html thymeleaf 적용해서 동적인 코드로 변경
    • 회원가입시 사용할 error code 생성
    • 회원가입시 검증기를 통한 text 출력
    • Controller 생성 후 연결해서 화면에서 확인
  • 로그인 기능 완료
    • 로그인시 사용할 Dto 생성
    • 로그인 Service 생성
    • 로그인 Controller에서 로그인 처리
    • 로그인 관련 HTML 동적인 코드로 변경(+오류 코드)
  • 게시판 Entity, DTO, Repository 개발
    • 게시판 관련 Entity 생성
    • 필요한 정보만 받아올 DTO 생성
    • Error Code 작성
    • 게시판 관련 Repository 개발
    • 게시판 Repository Test Code 작성
  • 게시판 Controller, Service 개발
    • 게시판 비즈니스 로직 구현
    • 비즈니스 로직 테스트 코드 작성
    • 컨트롤러 제작
    • 게시판 html을 thymeleaf를 통해 동적인 코드로 수정
    • 실제 실행 테스트
  • 로그인된 사용자만 특정 사이트에 들어갈 수 있도록 인터셉터 제작
  • 오류 페이지 적용
  • 로그인 후 모든 페이지에서 사용자가 로그인된 상태인 것을 인지할 수 있도록 로직 수정
  • 게시판 검색기능 기능 추가
    • Repository, Service, Controller 코드 수정
    • 테스트 코드 작성
    • 검색기능 tymeleaf 추가
  • 메인 서비스 개발
    • 임시 메인 서비스 화면 제작
    • NaverMovieApiService 개발(네이버 API를 활용한 값 읽어오기)
    • AutoComplete 기능 개발
      • MainService 로직 개발
      • 서비스 페이지에 ajax 코드 추가
      • AutoComplete 관련 Controller 코드 작성
    • 검색어의 개수에 따른 동작 구현
      • MainService.java에 영화를 검색하는 로직 작성
      • MainServiceController 관련 코드 작성
      • searvicePage.html, compareServicePage.html thymeleaf 코드 작성
      • 에러 페이지 제작
    • 네이버 영화 페이지에서 리뷰 정보 크롤링 해오기
      • MainService 크롤링 관련 코드 작성
      • MainServiceController 관련 코드 추가
      • servicePage, compareServicePage 코드 추가
  • 전체적인 디자인 변경 및 부가적인 페이지 제작
  • 로그인, 회원가입 관련 검증에 대한 우선순위 설정
  • 메시지 알림창(alert) 기능을 위한 message.html 제작
    • 게시글 삭제시에 적용
    • 회원 가입시 사용
      • 회원 가입 실패에 대한 예외 처리
  • 회원 정보 페이지 제작
    • 관련 엔티티 생성
      • 연관관계 매핑 코드 추가


이번에 해야할 목록

  • RecordRepository 생성
  • RecordService 코드 작성
    • RecordService Test code 작성
  • PostingService 코드 수정
  • RecordRepositoryCustom 생성
    • RecordRepositoryCustomImpl 생성 및 코드 작성
    • RecordRepositoryCustomImpl Test Code 작성

RecordRepository

public interface RecordRepository extends JpaRepository<Movie, Long> {

}
  • 그동안 JPA, QueryDsl을 사용했기 때문에 이번에는 Spring Data JPA를 사용해봤다.
    • JpaRepository를 상속받아서 사용하는데, 기본적인 CRUD는 바로 사용할 수 있다.
  • 회원 정보 페이지에서 사용하기 위한 영화 정보를 관리하는데 사용할 것이다.(CRUD)

RecordService

@Service
@Transactional
@RequiredArgsConstructor
public class RecordService {

    private final RecordRepository recordRepository;
    private final UserRepository userRepository;

    /*
    * 사용자가 검색한 영화 정보를 DB에 저장
    * User Entity와 연관관계 매핑하여 어느 회원이 영화를 검색했는지 회원 정보를 같이 저장
    * */
    public Long saveMovie(String movie_title, User user) {

        Movie movie = new Movie(movie_title, userRepository.loadUserByUserId(user.getUserId()));
        recordRepository.save(movie);

        return movie.getId();
    }
}
  • 파라미터로 넘어오는 User 엔티티의 userId를 통해서 User 엔티티를 찾는다.
    • 파라미터로 넘어오는 User 엔티티는 세션에서 가져온 값이다.
    • 세션에서 값을 가져오면 엔티티의 ID(PK)값이 들어가있지 않아서 다시 DB에서 찾는 과정을 진행했다.
  • Movie 엔티티의 생성자를 통해서 영화의 제목과 User 엔티티를 묶어 저장했다.

RecordServiceTest

RecordService Test Code 작성

@SpringBootTest
@Transactional
@Slf4j
public class RecordServiceTest {

    @Autowired UserRepository userRepository;
    @Autowired RecordService recordService;
    @Autowired RecordRepository recordRepository;

    @Test
    void save() {
        //given
        User user1 = new User("홍길동", "aaa1223233", "aaaa1234@");
        User user2 = new User("홍김동", "aaa1234", "1221@");

        userRepository.save(user1);
        userRepository.save(user2);

        //when
        recordService.saveMovie("공조2", user1);
        recordService.saveMovie("공조1", user2);

        List<Movie> findMovie = recordRepository.findAll();
        //then
        Assertions.assertEquals(2, findMovie.size());
    }

    @Test
    void findById() {
        //given
        User user1 = new User("홍길동", "aaa1223233", "aaaa1234@");
        User user2 = new User("홍김동", "aaa1234", "1221@");

        userRepository.save(user1);
        userRepository.save(user2);

        //when
        Long saved_movie1 = recordService.saveMovie("공조2", user1);
        recordService.saveMovie("공조1", user2);

        //then
        Movie search_Movie = recordRepository.findById(saved_movie1).get();
        User search_User = search_Movie.getUser();

        Assertions.assertEquals("홍길동", search_User.getUserName());
    }
}
  • 기본적으로 저장이 잘 되는지, 또한 값이 잘 들어가서 정상적인 값을 가져올 수 있는지 확인했다.

PostingService - 수정

    /*
    * 게시글 최초 저장
    * 생성된 id를 Controller로 반환
    * */
    public Long create_posting(PostingForm form, String userId) {
        Posting posting = new Posting(form.getTitle(), form.getContent(), form.getWriter(), form.getPassword(), 1, userRepository.loadUserByUserId(userId));
        return postingRepository.create(posting);
    }
  • 기존과 다른점은 userId를 파라미터로 같이 넘겨주어 게시글을 저장할 때 회원 엔티티 값을 같이 저장한다.

RecordRepositoryCustom 생성

public interface RecordRepositoryCustom {

    List<Movie> searchMovieList(Long userId, int offset, int limit);

    List<Posting> searchPostingList(Long userId, int offset, int limit);
}
  • 회원 정보 페이지에서 검색어와 게시글을 출력하기 위해 DB에서 정보를 가져오는 메소드.
  • userId를 통해서 검색하며, 출력 개수를 제한하기 위해 파라미터로 offset과 limit가 들어간다.

RecordRepositoryCustomImpl

Spring Data JPA를 사용했기 때문에 기능을 추가하기 위해서는 사용자 정의 Repository가 필요하다.

@RequiredArgsConstructor
public class RecordRepositoryCustomImpl implements RecordRepositoryCustom{

    private final EntityManager em;

    @Override
    public List<Movie> searchMovieList(Long userId, int offset, int limit) {
        return em.createQuery("select m from Movie m" +
                               " where m.user.id =: userId" +
                               " order by m.createdDate desc", Movie.class)
                .setParameter("userId", userId)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .getResultList();
    }

    @Override
    public List<Posting> searchPostingList(Long userId, int offset, int limit) {
        return em.createQuery("select p from Posting p" +
                                " where p.user.id =: userId" +
                                " order by p.createdDate desc", Posting.class)
                .setParameter("userId", userId)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .getResultList();
    }
}
  • 생성일자를 기준으로 내림차순하여 DB에서 가져온다.
    • 생성일자가 최신인것을 기준으로 원하는 개수만큼 자르기 위해.

RecordRepository(+)

public interface RecordRepository extends JpaRepository<Movie, Long>, RecordRepositoryCustom {
    
}
  • RecordRepository에 RecordRepositoryCustom 추가 상속

RecordRepositoryCustomImple Test

@SpringBootTest
@Transactional
@Slf4j
public class RecordRepositoryCustomImplTest {

    @Autowired UserRepositoryImpl userRepository;
    @Autowired RecordService recordService;
    @Autowired RecordRepository recordRepositoryCustom;
    @Autowired PostingRepository postingRepository;
    /*
    * 회원 아이디를 통해 회원이 검색한 영화 제목 리스트를 가져오는 메서드 테스트
    * */
    @Test
    void searchMovieList_Test() {
        //given
        User user1 = new User("김길이", "abc123", "abcd1234@");
        User user2 = new User("김길삼", "zbc321", "abcd1234@");

        userRepository.save(user1);
        userRepository.save(user2);

        recordService.saveMovie("공조1", user1);
        recordService.saveMovie("공조2", user1);

        recordService.saveMovie("터미네이터1", user2);
        recordService.saveMovie("터미네이터2", user2);
        recordService.saveMovie("터미네이터3", user2);

        //when
        List<Movie> movies = recordRepositoryCustom.searchMovieList(user2.getId(), 0, 3);

        //then
        assertThat(movies)
                .extracting("movie_title")
                .containsExactly("터미네이터3", "터미네이터2", "터미네이터1");

        Assertions.assertEquals(3, movies.size());
    }

    /*
    * 회원 아이디를 통해 회원이 작성한 게시글 리스트를 가져오는 메서드 테스트
    * */
    @Test
    void searchPostingList_Test() {
        //given
        User user1 = new User("김길이", "abc123", "abcd1234@");
        User user2 = new User("김길삼", "zbc321", "abcd1234@");

        userRepository.save(user1);
        userRepository.save(user2);

        Posting posting1 = new Posting("테스트 게시글1", "내용입니다.", "김길이", "abcd1233", 1, user1);
        Posting posting2 = new Posting("테스트 게시글2", "내용입니다.", "김길이", "abcd1233", 1, user1);

        Posting posting3 = new Posting("테스트 게시글3", "내용입니다.", "김길삼", "abcd1233", 1, user2);
        Posting posting4 = new Posting("테스트 게시글4", "내용입니다.", "김길삼", "abcd1233", 1, user2);
        Posting posting5 = new Posting("테스트 게시글5", "내용입니다.", "김길삼", "abcd1233", 1, user2);

        postingRepository.create(posting1);
        postingRepository.create(posting2);
        postingRepository.create(posting3);
        postingRepository.create(posting4);
        postingRepository.create(posting5);

        //when
        List<Posting> postings = recordRepositoryCustom.searchPostingList(user2.getId(), 0, 3);

        //then
        assertThat(postings)
                .extracting("title")
                .containsExactly("테스트 게시글5","테스트 게시글4", "테스트 게시글3");

        Assertions.assertEquals(3, postings.size());
    }
}


정리

사용자 정의 Repository인 RecordRepositoryCustomImpl을 생성하여 DB에서 영화, 게시글 정보를 생성일자 기준 내림차순하여 가져올 수 있게 되었다.
RecordService에서 이 데이터들을 가공하여 컨트롤러에 넘겨주면, 컨트롤러는 model을 통해 front단에 넘겨주어 출력만 잘 해주면 될 것 같다.

댓글남기기