[Spring Boot/JPA] #2 서비스 개발

뀨뀨찬찬·2021년 2월 5일
1

spring

목록 보기
2/4
post-custom-banner

개인적으로 시작한 개발이며, 틀린 점이나 부족한 부분이 많을 수 있으니 보완할 사항이나 질문은 댓글로 남겨주세요!

개발 환경

Database : mysql community Server 8.0.23
language : Java 11
Framework : Spring
IDE : IntelliJ ultimate ver.
OS : MS Win10 64bit

서비스 개발

지난 번 리포지토리까지 개발한 데에 이어 서비스를 개발했다.
리포지토리가 Entity와 연관된 작업만을 수행한다면, 서비스는 클라이언트로부터의 요청을 받아 Entity 작업의 전까지의 과정을 수행한다.

PostService

public interface PostService {
    public Long post(Long memberId, Long categoryId, String title, String contents);
    public Long updatePost(Long postId, Long categoryId, String title, String contents);
    public Post findOne(Long postId);
    public List<Post> findAll();
    public List<Post> findByMember(Member member);
    public List<Post> findByCategory(Category category);
    public void deletePost(Long postId);
}

Post의 대표적인 CRUD 기능에 대한 메서드가 있는 인터페이스를 생성한다.

JpaRepository에서 제공하는 기능들에 대한 메서드는
단순히 서비스에서 리포지토리로 작업을 넘기는 것이기 때문에
직접 postRepository에 접근할 수 있지만

  • 클라이언트에서 직접 리포지토리로 접근하는 것을 막고,
  • 이후 유지보수 단계에서 리포지토리와 서비스를 둘 다 체크하는 것보다 서비스만을 수정하여 사용하기 위해

위와 같이 구성했다.

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class PostServiceImpl implements PostService{

    private final PostRepository postRepository;
    private final MemberRepository memberRepository;
    private final CategoryRepository categoryRepository;

    @Override
    @Transactional
    public Long post(Long memberId, Long categoryId,
                     String title, String contents) {
        Member member = memberRepository.getOne(memberId);
        Category category = categoryRepository.getOne(categoryId);
        Post post = new Post();
        post.createPost(member, category, title, contents);
        postRepository.save(post);

        return post.getId();
    }

    @Override
    @Transactional
    public Long updatePost(Long postId, Long categoryId,
                           String title, String contents) {
        Post post = postRepository.getOne(postId);
        Category category = categoryRepository.getOne(categoryId);
        post.setCategory(category);
        post.setTitle(title);
        post.setContents(contents);
        post.setUpdateDate(LocalDateTime.now());

        return post.getId();
    }

    @Override
    @Transactional
    public void deletePost(Long postId) {
        Post post = postRepository.getOne(postId);
        post.getCategory().getPosts()
                .removeIf(targetPost -> targetPost.equals(post));
        post.getMember().getPosts()
                .removeIf(targetPost -> targetPost.equals(post));
        postRepository.deleteById(postId);
    }

    @Override
    public Post findOne(Long postId) {
        return postRepository.getOne(postId);
    }

    @Override
    public List<Post> findAll() {
        return postRepository.findAll();
    }

    @Override
    public List<Post> findByMember(Member member) {
        return postRepository.findByMember(member);
    }

    @Override
    public List<Post> findByCategory(Category category) {
        return postRepository.findByCategory(category);
    }
}

post 메소드에선

  • 파라미터로 받은 member와 category의 id로 해당 Entity를 찾고
  • 생성 메소드를 통해 post 객체에 필요한 필드들을 set한다.

updatePost 메소드에서는 update 기능을 수행하는데,

  • 해당 포스트의 필드를 파라미터로 받은 변수로 대체한다.
  • 이 경우 Spring JPA의 변경감지를 이용해 postRepository의 save를 하지 않더라도 변경된 내용이 저장된다.

deletePost 메소드에서는 delete 기능을 수행하고,

  • post의 member에 있는 List<Post>와 category에 있는 List<Category>에서 해당 객체를 뺀 후
  • postRepository의 deleteById 메소드를 호출한다.

JPA delete가 되지 않는 문제

기존에 연관관계에 대한 부분을 막연히 JPA가 해결해줄 거라 생각해 아래와 같이 코드를 짰었는데

    @Override
    @Transactional
    public void deletePost(Long postId) {
        postRepository.deleteById(postId);
    }

테스트 결과 삭제가 되지 않는 문제가 발생했다.
stack overflow에서도 비슷한 문제가 있는 개발자가 있던 모양인데, 명확한 해답을 찾지 못했다.

다른 팀원이 작성한 Member에 대해 삭제를 진행해봤을 때
문제 없이 잘 되는 걸 보고 차이점을 생각하다 연관관계에 대한 문제인가 싶어
Member와 Category의 리스트에서 해당 포스트를 지워주는 로직을 추가해봤다.

    @Override
    @Transactional
    public void deletePost(Long postId) {
        Post post = postRepository.getOne(postId);
        post.getCategory().getPosts()
                .removeIf(targetPost -> targetPost.equals(post));
        post.getMember().getPosts()
                .removeIf(targetPost -> targetPost.equals(post));
        postRepository.deleteById(postId);
    }

그 결과 삭제 로직이 정상적으로 작동했다.

이 부분이 왜 그런지는 조금 더 공부해봐야할 것 같은데,
삭제 로직을 구현할 때에는 연관관계에 대해서 꼭 다시 살펴봐야 할 것 같다.

Test

테스트 코드

post() 테스트

@SpringBootTest
@Transactional
public class PostServiceTest {

    @Autowired
    PostService postService;
    @Autowired
    PostRepository postRepository;
    @Autowired
    MemberRepository memberRepository;
    @Autowired
    CategoryRepository categoryRepository;
    @Autowired
    EntityManager em;

    @Test
    public void 포스트() throws Exception {
        // given
        Member member = new Member();
        Category category = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category.setName("Anony");
        categoryRepository.save(category);

        Long memberId = member.getId();
        Long categoryId = category.getId();
        // when
        Long post = postService.post(memberId, categoryId, "test", "this is test");

        // then
        assertThat(post).isNotNull();
    }

findById() 테스트

@Test
    public void 아이디로찾기() throws Exception {
        // given
        Member member = new Member();
        Category category = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category.setName("Anony");
        categoryRepository.save(category);

        Long memberId = member.getId();
        Long categoryId = category.getId();
        Long postId = postService.post(memberId, categoryId, "test", "this is test");

        // when
        Post findPost = postService.findOne(postId);

        // then
        assertThat(findPost.getId()).isEqualTo(postId);
        assertThat(findPost.getMember().getId()).isEqualTo(memberId);
        assertThat(findPost.getCategory().getId()).isEqualTo(categoryId);
        assertThat(findPost.getTitle()).isEqualTo("test");
        assertThat(findPost.getContents()).isEqualTo("this is test");
    }

findByMember() 테스트

@Test
    public void 멤버로찾기() throws Exception {
        // given
        Member member = new Member();
        Category category = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category.setName("Anony");
        categoryRepository.save(category);

        Long memberId = member.getId();
        Long categoryId = category.getId();

        // when
        postService.post(memberId, categoryId, "test", "this is test");
        postService.post(memberId,categoryId,"test2","this is 2nd test");
        Member findMember = memberRepository.getOne(memberId);
        List<Post> posts = postService.findByMember(findMember);

        // then
        assertThat(posts.size()).isEqualTo(2);
        for (Post post: posts){
            System.out.println("post.getTitle() = " + post.getTitle());
            assertThat(post.getMember().getId()).isEqualTo(memberId);
        }
    }

findAll() 테스트

@Test
    public void 모두찾기() throws Exception {
        // given
        Member member = new Member();
        Category category = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category.setName("Anony");
        categoryRepository.save(category);

        Long memberId = member.getId();
        Long categoryId = category.getId();

        // when
        postService.post(memberId, categoryId, "test", "this is test");
        postService.post(memberId,categoryId,"test2","this is 2nd test");
        postService.post(memberId, categoryId, "test3", "this is 3nd test");

        List<Post> all = postService.findAll();

        // then
        assertThat(all.size()).isEqualTo(3);
    }

findByCategory() 테스트

@Test
    public void 카테고리로찾기() throws Exception {
        Member member = new Member();
        Category category = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category.setName("Anony");
        categoryRepository.save(category);

        Long memberId = member.getId();
        Long categoryId = category.getId();

        // when
        postService.post(memberId, categoryId, "test", "this is test");
        postService.post(memberId,categoryId,"test2","this is 2nd test");
        postService.post(memberId, categoryId, "test3", "this is 3nd test");
        Category findCategory = categoryRepository.getOne(categoryId);
        List<Post> byCategory = postService.findByCategory(findCategory);
        // then
        assertThat(byCategory.size()).isEqualTo(3);
    }

updatePost() 테스트

@Test
    public void 업데이트() throws Exception {
        // given
        Member member = new Member();
        Category category1 = new Category();
        Category category2 = new Category();
        member.setUsername("dd");
        member.setPassword("dddd");
        memberRepository.save(member);
        category1.setName("Anony");
        category2.setName("Anony2");
        categoryRepository.save(category1);
        categoryRepository.save(category2);

        Long memberId = member.getId();
        Long categoryId = category1.getId();
        Long category2Id = category2.getId();

        // when
        Long postId = postService.post(memberId, categoryId, "test", "this is test");
        Long updated = postService.updatePost(postId, category2Id, "test2", "this is updated test");

        // then
        assertThat(postId).isEqualTo(updated);
        assertThat(postService.findOne(updated).getCategory().getId()).isEqualTo(category2Id);
        assertThat(postService.findOne(updated).getCategory()).isEqualTo(category2);
        assertThat(postService.findOne(updated).getTitle()).isEqualTo("test2");
        assertThat(postService.findOne(updated).getContents()).isEqualTo("this is updated test");

    }

deletePost() 테스트

@Test
    public void 삭제() throws Exception {
        // given
        Member member = new Member();
        member.setUsername("p");
        member.setPassword("d");
        memberRepository.save(member);

        Category category = new Category();
        category.setName("dd");
        categoryRepository.save(category);

        Long postId = postService.post(member.getId(), category.getId(), "title", "contents");

        List<Post> before = postService.findAll();
        System.out.println("before = " + before);
        // when
        postService.deletePost(postId);

        List<Post> after = postService.findAll();
        System.out.println("after = " + after);

        // then
        assertThat(after.size()).isEqualTo(before.size()-1);
    }

테스트 결과

모든 테스트가 잘 통과하는 걸 확인할 수 있다.

테스트 코드의 메소드 이름이 한글로 되어 있어 깨지는 경우가 발생할 수 있는데,

Help -> Edit Custom VM Options..
-Dfile.encoding=UTF-8 을 추가

IntelliJ를 다시 실행하면 한글이 깨지지 않고 잘 보인다.

profile
공부하고 있어요!
post-custom-banner

0개의 댓글