반정규화를 통해 리뷰 별점 조회 최적화하기

이동엽·2023년 5월 22일
4

spring

목록 보기
2/15

아보카도 프로젝트를 진행하면서, 데이터베이스 설계 시에 고민했던 문제가 있었습니다.


당시 일부 요구사항 간단 요약

회원병원에 진료를 예약할 수 있다.”
회원병원할 수 있다.”
회원병원에 대한 리뷰를 작성할 수 있다.”


작성된 ERD 초안



이에 따른 엔티티 설계

회원 → 병원 예약 리스트, 찜한 병원 리스트, 작성한 리뷰 리스트가 존재
리뷰 → 작성한 회원, 타겟 병원이 존재
병원 → 예약 리스트, 찜 리스트, 리뷰 리스트가 존재


위 경우, 계속해서 순환 참조가 일어날 수 있다.

또한 병원 리뷰 플랫폼인 만큼, 리뷰 데이터가 늘어날 경우 함께 조인되는 쿼리의 개수들이 많아질 수 있다.


이때, 순환 참조를 없애는 방법을 떠올릴 순 있지만 다른 방법도 있다! → 반정규화

⇒ 따라서 기존 엔티티에 있던 참조를 없애고, 직접 target_hospital 컬럼을 추가하여 관리하기



  • 기존 엔티티
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Review extends BaseEntity {

    @Id @GeneratedValue
    @Column(name = "review_id")
    private Long id;

		...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital hospital;
		
		...
}


  • 반정규화한 엔티티
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Review extends BaseEntity {

    @Id @GeneratedValue
    @Column(name = "review_id")
    private Long id;

		...

    @Column(name = "target_hospital", nullable = false)
    private String targetHospital;
	
		...
}


그렇다면 조회 방법에는 차이가 없을까?

: 아보카도 병원 의 리뷰를 검색하고 싶을 때

  • 기존 방법
    1. HospitalRepository에서 findByName()으로 “아보카도 병원” 검색
    2. 반환된 hospital.getReviewList().getReview()로 순회하며 별점 계산 (이때 지연 로딩이 발생)
      → 위 과정에서 N+1 문제가 발생하기 쉬움!
  • 새로운 방법
    1. ReviewRepository에서 findByName()으로 “아보카도 병원” 검색
    2. 반환된 reviewList.getReview()로 바로 별점 필드로 접근!
      → 이는 데미테르의 법칙을 지키기도 한다!

최종 ERD

profile
백엔드 개발자로 등 따숩고 배 부르게 되는 그 날까지

1개의 댓글

comment-user-thumbnail
2023년 5월 22일

멘트 탈락입니다. "그래서, 비정규화가 뭔데?"로 변경하세요.

답글 달기