Between-Departments / dev-BE

Between-Departments BE
0 stars 1 forks source link

Cascade를 이용한 데이터 생성 VS 독자적인 Repository 사용 #6

Closed L-ilac closed 1 year ago

L-ilac commented 1 year ago

Post 엔티티와 관련된 엔티티(PostRecommend, PostBookmark, PostReport)를 개발하던 중, 고민해볼만한 내용이 생겨서 공유합니다.

현재, PostRecommend, PostBookmark, PostReport의 경우 PostService에서 각각 개별 Repository를 주입하여 저장하는 형태의 로직을 갖고 있습니다. PostRecommend, PostBookmark, PostReport 에서 'ManyToOne' 을 이용하여 Post와 연관관계를 매핑하고 있고, DB 상에서도 post_id 값을 외래키로 참조하여 관리하기 때문에 이렇게 개발하는 것이 가장 간단하고 직관적인 방법이라고 생각했습니다.

그런데, 위의 독자적인 Repository 사용이 아니더라도 Post 엔티티에 Cascade 옵션을 주는 것으로 PostRecommend, PostBookmark, PostReport 데이터를 생성할 수 있습니다. Post 엔티티에 PostRecommend, PostBookmark, PostReport를 'OneToMany' 를 이용하여 컬렉션 형태로 저장하면, Repository를 만들지 않더라도 JPA 영속성 전이 기능을 활용해 데이터 저장이 가능합니다.

<예시>

// 기존 코드 -> postReportRepository 를 사용하는 경우

public void reportPost(PostReportDto postReportDto , Long postId, long memberId) {
        Member reporter = memberRepository.findById(memberId).orElseThrow(() -> new NotFoundException(ExceptionCode.NOT_FOUND_MEMBER));
        Post findPost = find(postId);

        Optional<PostReport> findPostReport = postReportRepository.findByReporterAndPost(reporter, findPost);

        findPostReport.ifPresentOrElse(report -> { new CustomException(ExceptionCode.DUPLICATE_REPORT); },
                () ->{
                    PostReport newPostReport = PostReport.builder()
                            .reporter(reporter)
                            .post(findPost)
                            .type(postReportDto.getType())
                            .content(postReportDto.getContent())
                            .build();

                    postReportRepository.save(newPostReport);
                });
    }

// 변경 후 코드(예상) -> Cascade를 사용하는 경우

public void reportPost(PostReportDto postReportDto , Long postId, long memberId) {
        Member reporter = memberRepository.findById(memberId).orElseThrow(() -> new NotFoundException(ExceptionCode.NOT_FOUND_MEMBER));
        Post findPost = find(postId);

        List<PostReport> reports = findPost.getReports();
        reports.stream().filter(postReport -> postReport.getReporter().getMemberId() == memberId).findAny()
                .ifPresentOrElse(report -> { new CustomException(ExceptionCode.DUPLICATE_REPORT); },
                    () ->{
                        findPost.addNewReport(reporter, postReportDto.getType(),postReportDto.getContent());
                    });
    }

public void addNewReport(Member reporter, Report.Type type, String content){
        PostReport newPostReport = PostReport.builder()
                .reporter(reporter)
                .post(this)
                .type(type)
                .content(content)
                .build();

        this.reports.add(newPostReport);
    }

위의 두 방법 모두 정상적으로 새로운 PostReport 데이터를 저장합니다. 이에 대한 의견이나 피드백을 해주시면 감사하겠습니다.

jeongmin0709 commented 1 year ago

넵저도 해당 방법에 대해서 알고 있습니다. 기본적으로 여러 테이블이랑 연관관계가 있는 테이블은 양방향 매핑을 잘안쓰는 경향이 있어서 한테이블에만 연관관계가 있는 경우에만 양방행 매핑을 사용하는 편입니다. 하지만 관리를 잘한다면 충분히 좋은 방식이고 스타일의 차이라고 생각합니다 저도 현재 MemberImage와 Member를 양방향으로 매핑해서 한번에 저장하고 있습니다.

Member member = Member.builder()
                .mail(mail)
                .nickname(nickname)
                .password(passwordEncoder.encode(password))
                .major(major)
                .school(school)
                .build();
        member.setImage(MemberImage.builder().url(imageUrl).build());
        member.addRole(Member.Role.ROLE_USER);
L-ilac commented 1 year ago

음 그럼 제가 지금 개발중인 PostRecommend, PostBookmark, PostReport는 Post 뿐만 아니라 Member도 걸려있는 상태라서, 일단은 각 엔티티별로 Repository를 사용하는 방향으로 코드를 작성하고 이를 기반으로 테스트 코드 짠 뒤에, 좀 더 생각을 해보고 변경하겠습니다 🤩