5장 순환참조

  • 두개 이상의 객체나 컴포넌트가 서로를 참조함으로써 의존관계에 사이클이 생기는 상황

@Data  
class Team {  
    private long id;    
    private String name;    
    private List<Member> members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;
}
  • 팀과 팀원의 관계를 표시한 클래스 -> 순환참조임

양방향 매핑

  • JPA Entity의 양방향 매핑은 순환참조다.

 


 

순환참조의 문제점

 

무한루프

  • 순환참조가 있다는 것은 무한 루프가 발생할 수 있다는 것임.

 

시스템 복잡도

  • 순환참조는 시스템의 복잡도를 높임. (의존성 전이 등)
  • "팀내 모든 구성원의 월급을 합산해주세요 라는 요구사항이 있을 떄"

@Data  
class Team {  
    private long id;    
    private String name;    
    private List<Member> members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;  
    // 팀 내 모든 팀원의 월급의 합을 반환함  
    public int calculateTeamMemberTotalSalary() {
        int result = 0;
        for (Member member : myTeam.getMembers()) {
            result += member.getSalary();       
        }   
        return result;    
    }
}
  • 위 코드는 팀원이 팀 내 전체 구성원의 월급을 알 수 있는 시스템. 현실세계에서는 벌어질 수 없는 이상한 상황.
  • -> 잘못된 설계로 인해 이러한 이상한 코드가 만들어질 수 있는 구조가 되는 것이 문제 !
  • -> 의미상으로 그렇게 되어서는 안되는 코드는 컴파일 타임에 만들어지지 않게 하는 것이 좋다..
// 하위 객체에서 식별자를 통한 간접 참조로 변경  
@Data  
class Team {  
    private long id;   
    private String name;    
    private List<Member> members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private int myTeamId; // 애초에 이상한 개발을 하지 못하도록 ..
}
// 혹은 상위 객체에서 참조를 제거  
@Data  
class Team {  
    private long id;    
    private String name;//    
    private List<Member> members;  
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;
}  

//"팀내 모든 구성원의 월급을 합산해주세요 라는 요구사항이 있을 떄"  
//요구사항은 서비스에서 구현  
@Service  
@RequriedArgsConstructor  
class TeamService {  
    private final MemberService memberService;  
    public long getTeamSalary(long id) {        
        return memberService.findByTeamId(memberId).steam()
        .mapToLong(Member::getSalary)
        .sum();    
    }
}
  • 순환참조가 있으면 어떤 객체에 접근할 수 있는 접근 경로가 너무 많아짐.

순환 참조를 해결하는 방법

 

불필요한 참조 제거

  • 꼭 필요하지 않은 참조를 제거하거나 한쪽이 식별자를 갖게 하는 간접 참조로 바꾸기

 

간접 참조 활용

  • 식별자를 갖고 있는 방식

 

공통 컴포넌트 분리

  • 양쪽 서비스에 있던 공통 기능을 하나의 컴포넌트로 분리
  • -> 공통 기능을 분리하는 과정에서 책임 분리가 적절하게 재조정된다.

 

이벤트 기반 컴포넌트 사용

  • 서비스를 공통 컴포넌트로 분리할 수 없다면 이벤트 기반 프로그래을 시스템에 적용

 

양방향 매핑

  • 양방향 매핑은 순환참조 -> 왜 존재하는 것일까? 고민해보기
  • -> 양방향 매핑은 어쩔수 없이 존재하는 도메인 문제에 최소한으로 적용하는게 바람직.

 


 

상위 수준의 순환참조

  • 순환참조는 객체 뿐만아니라 패키지나 시스템 수준에서도 발생할 수 있는 문제임을 주의