해결된 질문
작성
·
205
·
수정됨
0
[질문 내용]
우선 단방향 연관관계를 적용하고 꼭 필요한 경우에 양방향 연관관계를 사용하라는 내용을 적용해보다가 이해가 안되는 부분이 생겨서 질문남깁니다.
Team과 Member로 양방향 연관관계를 적용한다고 생각해보면 다른 질문의 답변을 봤을 때 Team을 조회할 때 Member가 거의 같이 조회가 되는 경우와 fetch join으로 조회 시 양방향 연관관계를 사용하여 더 편리하게 조회할 때 사용한다는 답변을 봤습니다. (제대로 이해한 것인지는 모르겠습니다.)
그래서 테스트를 해봤습니다.
@Test
void 멤버목록과_함께_팀조회() {
Team team = new Team("teamA");
entityManager.persist(team);
Member member = new Member("m1", 0, team);
Member member2 = new Member("m2", 0, team);
entityManager.persist(member);
entityManager.persist(member2);
entityManager.flush();
entityManager.clear();
System.out.println("====테스트 1 양방향 연관관계===");
Team findTeam = teamJpaRepository.findById(team.getId()).get();
List<Member> members = findTeam.getMembers();
for (Member member1 : members) {
System.out.println("member1 = " + member1.getUsername());
}
entityManager.flush();
entityManager.clear();
System.out.println("===테스트 2 fetch join 양방향 연관관계 사용==");
Team findTeam2 = teamRepository.findTeamWithMembersById(team.getId()).get();
List<Member> members2 = findTeam2.getMembers();
for (Member member1 : members2) {
System.out.println("member1 = " + member1.getUsername());
}
entityManager.flush();
entityManager.clear();
System.out.println("===테스트 3 fetch join 그냥 엔티티 사용==");
Team findTeam3 = teamRepository.findTeamWithMemberById(team.getId()).get();
List<Member> members3 = findTeam3.getMembers();
for (Member member1 : members3) {
System.out.println("member1 = " + member1.getUsername());
}
}
public interface TeamRepository extends JpaRepository<Team, Long> {
@Query("select t from Team t join fetch t.members")
Optional<Team> findTeamWithMembersById(Long id);
@Query("select t from Team t join fetch Member m")
Optional<Team> findTeamWithMemberById(Long id);
}
테스트 1의 경우 두개의 쿼리가 나가는 것을 확인했습니다.
select
t1_0.team_id,
t1_0.create_by,
t1_0.created_date,
t1_0.last_modified_by,
t1_0.last_modified_date,
t1_0.name
from
team t1_0
where
t1_0.team_id=?
select
m1_0.team_id,
m1_0.member_id,
m1_0.age,
m1_0.create_by,
m1_0.created_date,
m1_0.last_modified_by,
m1_0.last_modified_date,
m1_0.username
from
member m1_0
where
m1_0.team_id=?
테스트 2의 경우 한개의 쿼리만 나가는 것을 확인했습니다.
select
t1_0.team_id,
t1_0.create_by,
t1_0.created_date,
t1_0.last_modified_by,
t1_0.last_modified_date,
m1_0.team_id,
m1_0.member_id,
m1_0.age,
m1_0.create_by,
m1_0.created_date,
m1_0.last_modified_by,
m1_0.last_modified_date,
m1_0.username,
t1_0.name
from
team t1_0
join
member m1_0
테스트 3의 경우 오류가 났습니다.
위의 테스트 1(getMembers로 접근), 테스트2(fetch join)의 경우 쿼리의 개수로 봤을 때 테스트 2를 사용하는 게 더 좋은 것 같은데 맞을까요?
테스트 3은 단방향인 경우 팀과 멤버를 같이 조회를 어떻게 해야되나요?
양방향 연관관계가 필요한 경우가 뭔가요?
원래 api 설계할 때 team, member를 각각 조회하는 경우만 만들었었는데, team과 member가 같이 조회되는 api를 만드는 경우도 있나요? (team/{id}, member/{id} 와 같은 경우만 만들어봤습니다.)
답변 1
0
안녕하세요. 안채연님, 공식 서포터즈 y2gcoder입니다.
조회 성능의 관점에서 봤을 때는 fetch join을 사용해서 Team 과 Member를 한번에 가져오는 것이 더 좋은 것이 맞습니다!
단방향이고, 팀과 팀에 해당하는 멤버들을 모두 조회해야 한다면, 아래와 같은 절차로 이루어집니다. 그 후에 필요하다면 응답용 DTO를 새로 만들어 둘을 합쳐서 반환해줄 수 있을 것 같습니다.
Team을 먼저 조회
해당 Team 에 해당하는 Member 조회
위와 같이 Team을 조회할 때 해당 Team에 대한 Member 들을 조회해야 하는 상황이 빈번하게 발생한다면 양방향 연관관계를 사용하여 편의를 도모할 수 있을 것 같습니다. 다른 예로 주문:주문상품의 관계가 있다고 할 때, 주문 상세 조회를 하면 해당 주문에 대한 주문 상품들은 모두 표시해야 한다고 가정해보겠습니다. 그럴 때 단방향 관계라면 매번 위와 같이 코드적으로 Order를 조회하는 로직, OrderItem을 조회하는 로직을 각각 작성해야 하지만, 양방향 연관관계라면 코드적으로 order.getOrderItems() 와 같은 메서드를 불러주는 것만으로도 가능하니 좀 더 개발자가 편해질 것이라 말씀드릴 수 있을 것 같습니다!
(다만 성능적인 관점에서 보면 문제가 있을 수 있습니다! 이 부분은 활용 2편 강의를 학습해보시는 것을 추천합니다!)
위에 말씀해주신 예에서 논해보자면 주문 상세 조회 가 있을 것 같습니다. 주문 상세 조회라면 API Endpoint 적으로는 /orders/{id} 와 같이 설계할 수 있으나, 세부로 들어가면 주문에 대한 상세정보를 표현하기 위해 Order, OrderItems, Items, Delivery 등의 정보가 모두 필요할 수 있습니다. 팀-멤버의 관계에서도 팀의 상세 정보에서 소속 멤버들의 간단한 정보(이름)을 목록으로 항상 보여줘야 한다면, teams/{id}에서 member를 같이 조회할 수도 있을 것이라 생각합니다.
감사합니다.