생성자 주입은 왜 권장되며 순환참조란 뭘까?

Walker·2021년 3월 29일
7

Spring

목록 보기
1/1
post-thumbnail

면접에서 수정자 주입과 생성자 주입 중 어느 것이 바람직한가에 대해 질문 받은 적이 있다.
그때 생성자 주입이 권장된다는 사실은 기억났지만 정확한 이유를 설명하지 못했다.
그때의 아쉬움이 남아 다시 글로 정리해보려한다.


의존성 주입(DI)에는 크게 3가지 방법이 있고

  1. Field Injection(필드 주입)
  2. Setter Injection(수정자 주입)
  3. Constructor Injection(생성자 주입)

다양한 블로그 글을 보며 다시 정리한 결과
(이분 글에서 가장 많이 배웠다 - 추천!)
https://yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad

필드 주입수정자 주입은 의존성이 있는 객체가 생성되지 않아도
이를 포함하고 있는 객체가 생성 가능(컴파일시 오류가 발생하지 않음)하여
이를 미리 인지하지 못하다가 런타임시에 오류가 발생하여 위험하다는 것이 핵심!

이에 반해 생성자 주입은 생성자에서 의존관계 주입이 일어나기 때문에
생성자가 실행 될 때 즉 객체가 생성 될 때 의존 객체의 null 여부를 검사하므로
컴파일시에 오류를 발생시켜 런타임시에 오류가 발생하는 참사를 미연에 방지해준다.
(Code를 통한 설명은 위 블로그를 읽어보시길...)


또한 생성자 주입 방식이 순환참조도 컴파일시에 미리 파악 할 수 있다는 내용이 있는데
여기서 순환참조가 무엇인지에 대한 부분은 Code로 작성해보았다.

<닭이 먼저냐 달걀이 먼저냐 - 필드 주입>

@Component
public class Chicken {
    @Autowired
    Egg egg;

    public void layEgg(){
        egg.becomeChicken();
    }

}

닭은 달걀을 낳고, 달걀은 닭이 된다.

@Component
public class Egg {
    @Autowired
    Chicken chicken;

    public void becomeChicken() {
        chicken.layEgg();
    }

}

달걀은 닭이 되고, 닭은 달걀을 낳는다.

@Bean
public CommandLineRunner run(Chicken chicken, Egg egg) throws Exception {
    return (String[] args) -> {
        chicken.layEgg(); // 실행
    };
}

여기에서 닭이 달걀을 낳는다를 실행시키면!

java.lang.StackOverflowError: null

스택오버플로우와 함께 문제가 되는 부분이 null이라는 에러메시지가 발생한다.


닭이 달걀을 낳는다 > 달걀은 닭이 된다 > 닭이 달걀을 낳는다 > 달걀은 닭이 된다 > (무한반복...)

위와 같이 서로가 서로의 객체를 참조하며 실행되는 메소드가 실행되면 무한루프가 되어
Stack에 메소드를 쌓다가 Stack 메모리가 터져버리고 마는 것이다.
(이러한 문제가 필드 주입이나 수정자 주입에서는 런타임시에 발견된다.)


마지막으로 생성자 주입의 경우 생성자를 Code로 작성 해줘야 하는 번거로움이 있을 수 있으나

@NoArgsConstructor - 기본 생성자 생성
@AllArgsConstructor - 모든 필드를 초기화하는 생성자 생성
@RequiredArgsConstructor - 초기화 되지 않은 모든 final 필드 & @NonNull 마크가 있는 필드를 초기화하는 생성자 생성

위 어노테이션들을 적절히 활용하면 좀 더 깔끔하게 Code를 작성 할 수 있다.
(각 어노테이션 별 주의사항이 있으니 실사용시에는 한번 더 알아보고 쓸 것!)

profile
I walk slowly, but I never walk backward. -Abraham Lincoln-

1개의 댓글

comment-user-thumbnail
2022년 7월 8일

글 잘봤습니다

답글 달기