ecsimsw
생성자 주입을 통한 순환 참조 막기 본문
양방향 의존성을 피하라
양방향 의존성은 정말 안좋은 참조 패턴이다. '의존'이라는 말마따라 A가 변경될 때 B가 변경되어야하고 B가 변경되면서 다시 A가 영향을 받게된다.
'우아한테크세미나'에서 조영호님은 이를 '하나의 클래스로 봐야할 것을 억지로 찢은 형태'라고 표현하셨다. 양방향 의존성은 성능 이슈를 만들고, 양쪽 객체의 싱크를 맞추기 위한 노력이 필요하게 된다. 또 메소드 순환 호출을 야기한다.
이런 양방향 의존을 피하기 위한 패턴으로 다음 두 가지 방식이 자주 소개된다.
1. 인터페이스 사용으로 의존성 분리
2. 중간 객체를 만들어 사이클을 끊는다.
이 두 가지 방식에 대한 예시는 다음에 정리하려고 하고, 이번 글에서는 스프링에서 생성자 주입을 이용하면 이런 양방향 의존을 피할 수 있음을 소개하고자 한다.
스프링 : 생성자 주입을 통한 양방향 참조 피하기
먼저 생성자 주입이 아닌 필드 주입이나 setter을 이용한 주입의 경우를 보자.
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
이렇게 A와 B가 양방향 의존, 순환 참조하고 있는 형태에서 필드로 서로가 주입된다면 정상 작동한다. 아래 테스트 코드로 A 객체와 B객체가 문제없이 생성됨을 확인하였다.
class AppTest {
private ApplicationContext ctx;
@BeforeEach
void initApplicationContext(){
ctx = new AnnotationConfigApplicationContext(App.class);
}
@DisplayName("A, B 빈이 모두 정상 생성되었는지 확인한다.")
@Test
void beanInstanceTest(){
boolean isAExist = Arrays.stream(ctx.getBeanDefinitionNames())
.anyMatch(name -> name.equals("a"));
boolean isBExist = Arrays.stream(ctx.getBeanDefinitionNames())
.anyMatch(name -> name.equals("b"));
assertThat(isAExist && isBExist).isTrue();
}
}
이번에는 생성자 주입을 이용했다. 이번에도 정상 동작할까?
@Component
public class A {
private B b;
public A(B b){
this.b = b;
}
}
@Component
public class B {
private final A a;
public B(A a){
this.a = a;
}
}
조금 생각해보면 알 수 있다. A가 생성되기 위해선 B 생성이 필요하고, B가 생성되기 위해선 A의 생성이 필요하여 생성자가 순환 호출되면서 빈이 정상 생성되지 않는다. (StackOverFlowError가 발생한다.)
반면 앞선 Setter와 필드로 주입하는 경우, A, B 빈이 생성될 때는 각자의 영향을 받지 않는다. 주입이 되는 시점은 빈으로 만들어진 이후이고 따라서 그 방식들로는 각각의 관계가 양방향 참조인지, 아닌지 확인할 수 없다. 또 순환 참조에 의한 부작용, 이를 테면 메소드 순환 호출과 같은 심각한 버그를 런타임 시점에서야 알 수 있게 된다.
이렇게 생성자를 통한 주입을 사용하면 의존하는 관계가 양방향 관계인지 바로 확인할 수 있다.
'Server application > Spring' 카테고리의 다른 글
Spring boot의 CORS 설정 (2) | 2021.06.13 |
---|---|
Dispatcher servlet의 역할, FrontController 패턴이란 (2) | 2021.05.03 |
DispatcherServlet이 요청을 처리하고 응답하는 과정 (0) | 2021.04.16 |
Spring Mail 전송 / Google Smtp, 비동기 메일 인증 (1) | 2020.10.10 |
Spring 에서 정적 리소스를 다루는 네가지 방법 (2) | 2020.09.13 |