[Spring/TIL] Autowired와 RequiredArgsConstructor의 비교

resilient

·

2024. 2. 28. 18:00

728x90
반응형

최근에 이직을 해서 새로운 회사의 코드를 들여다보는 중입니다.

SpringBoot를 사용하지 않고 Spring 프레임워크를 자체적으로 사용하는 프로젝트들이 있고 이들을 리팩토링 하는 작업도 진행 중에 있습니다.

 

기존 Spring만 사용하던 프로젝트 코드들을 보면 lombok 라이브러리를 사용하지 않고 Autowired 어노테이션을 사용해서 의존성 주입을 진행했습니다.

 

제가 전에 다니던 회사, 사이드 프로젝트를 진행 할 때는 당연하게 lombok 라이브러리를 사용했고 RequiredArgsConstructor 어노테이션을 사용해서 의존성 주입을 했는데요.

 

이번 포스팅에서는 왜 Autowired말고 RequiredArgsConstructor를 사용했고, 어떤 장점이 있고 단점이 있는지 살펴보려고 합니다.

 

0. 의존성 주입 방법들에는 어떤 것들이 있을까?

 

Autowired와 RequiredArgsConstructor는 단순하게 어노테이션만 다른 것이 아닙니다. 의존성 주입 방법의 차이도 존재하죠.

 

의존성 주입 방법에는 필드 주입, 생성자 주입, 메서드 주입 이렇게 세가지가 있습니다.

 

Autowired는 필드 주입 방법이고 RequiredArgsConstructor는 생성자 주입 방법이죠. 메서드 주입은 이번시간에 따로 다루지는 않겠습니다.

 

1. Autowired

 

1-1. @Autowired 필드 주입의 특징

필드 주입은 말 그대로 필드에 바로 의존 관계를 주입하는 방법입니다.

 

@Autowired의 특징은 다음과 같습니다.

  • Autowired는 필드 주입 방식의 의존성 관리 방법입니다.
  • Field, Setter Method, Constructor(생성자)에 사용 가능합니다.
  • 기본적으로 Type을 기준으로 의존성을 주입(DI) 합니다.
  • 동일한 Type의 빈(Bean)이 여러 개 존재할 경우 기본적으로 참조 변수의 이름과 동일한 Bean을 찾아서 의존성을 주입합니다.
  • 이름을 기준으로 의존성 주입을 할 때 @Qualifier 어노테이션을 사용해서 DI 될 Bean을 지정합니다.
  • 만약 동일한 Type의 Bean이 여러 개 존재할 경우에는 @Primary 어노테이션을 사용해서 의존성 주입이 될 Bean을 지정 가능합니다.

 

1-2. @Autowired 필드 주입의 단점

 

하지만 단점들도 물론 존재합니다.

 

첫 번째, 바로 외부에서 접근이 불가능하다는 단점이 있습니다.

 

최근 테스트 코드의 중요성이 부각되고 있고 테스트 코드는 선택이 아닌 필수가 되었습니다.

이런 흐름에서 필드의 객체를 수정할 수 없는 필드 주입은 거의 사용되지 않게 되었습니다. 비즈니스 로직과 결합이 약한 테스트 코드를 작성할 경우나 config 설정 등을 위해 불가피하게 사용해야 할 경우가 아니라면 굳이 사용할 이유가 없죠.

 

두 번째, final 옵션을 사용할 수 없기에 코드가 변질될 가능성이 존재합니다.

 

이는 로직에 따라 큰 에러가 발생할 가능성이 있기 때문에 중요한 문제가 될 수도 있습니다.

 

세번째, @Autowired는 DI가 타입(Type)이 같은 빈(Bean)이 발견되면 그냥 주입합니다.

 

위 Autowired의 특징 중 세번째, 네번째와 관련된 내용이기도 합니다.

 

지금까지 의존성 주입이 간편했던 이유인데 이것이 문제가 되는 이유는 같은 Type의 다른 객체가 여러 개일 때 문제가 발생합니다.

DI가 Type만 보고 내려주기에 서로 다른  A타입의 객체가 2개 존재한다면 @Autowired는 Error를 띄울 수밖에 없게 되죠.

 

2. RequiredArgsConstructor 생성자 주입

 

2-1. @RequiredArgsConstructor 생성자 주입 특징

    • 어노테이션은 Lombok에서 지원하는 어노테이션입니다.
    • 필드 주입처럼 보이지만 @RequiredArgsConstructor 사용하면 생성자로 의존성 주입을 합니다.
    • final 키워드가 붙은 필드에 대해 생성자를 만들어 줍니다.
    • 한번 의존성 주입을 받은 객체는 프로그램이 끝날 때까지 변하지 않는 특징을 가지므로 불변성을 표시해주는 것이 좋기 때문 -> 객체의 불변성(Immutability) 보장합니다.

 

2-2. @RequiredArgsConstructor의 단점

  1. 명시적인 의도 부재: @RequiredArgsConstructor를 사용하면 클래스의 필수 필드가 무엇인지 명시적으로 표현하지 않고도 생성자를 생성할 수 있습니다. 이는 클래스의 의도를 이해하기 어렵게 만들 수 있습니다. 많은 사람들이 함께 작성하는 코드의 경우에 혼란을 야기할 수 있게 되죠.
  2. 유연성 제한: @RequiredArgsConstructor는 클래스의 필수 필드만을 고려하여 생성자를 생성합니다. 그러므로 선택적으로 값이 설정될 수 있는 필드에 대해서는 생성자를 자동으로 생성하지 않습니다. 이 경우, 해당 필드에 대한 생성자 오버로딩을 수동으로 추가해야 합니다. 또한 기본 생성자도 따로 만들어 줘야 한다는 단점이 있습니다.
  3. Lombok 의존성: 프로젝트에 Lombok 의존성을 추가해야만 @RequiredArgsConstructor를 사용할 수 있습니다. SpringBoot가 아닌 Spring프레임워크 프로젝트의 경우 dependency추가가 까다롭고 버전에 따라서 동작하지 않을 수도 있음을 고려해야 합니다.

 

3. 생성자 주입을 권장하는 이유

 

Spring 공식 문서를 봐도 알 수 있지만 많은 DI 프레임워크의 대부분이 생성자 주입을 권장하고 있습니다.

위의 @RequiredArgsConstructor의 특징과 비슷한 이유들이 있는데요. 이유들은 아래와 같습니다.

 

1. 객체의 불변성 확보

2. 테스트 코드의 작성

3. 순환 참조 에러 방지

 

1. 객체의 불변성 확보

 

개발 도중 의존 관계를 변경하는 일이 얼마나 있을까요? 거의 없다고 생각합니다.

하지만 수정자 주입, 메서드 주입을 사용하면 불필요하게 수정될 수 있는 가능성을 열어두게 되고 유지보수성을 떨어뜨립니다. 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋습니다.

 

2. 테스트 코드의 작성

 

@Autowired의 단점에서도 언급을 했던 부분입니다.

테스트 코드를 작성할 때 생성자 주입이 아닌 다른 주입으로 작성된 코드는 순수한 자바 코드로 유닛 테스트를 작성하는 것이 꽤나 복잡합니다.

 

3. 순환 참조 에러 방지

 

생성자 주입을 사용하면 애플리케이션이 실행되고 객체의 생성 시점에 순환 참조 에러를 예방할 수 있습니다.

(이 문제는 SpringBoot 2.6부터 순환 참조가 기본적으로 허용되지 않도록 변경되어서 2.6 버전 이상을 사용하는 경우에는 해당되지 않습니다.)

 

예를 들어 다음과 같이 필드 주입을 사용해서 호출하는 코드가 있다고 가정해 보겠습니다.

 

@Service
public class UserService {

    @Autowired
    private MemberService memberService;
    
    @Override
    public void addName(String name) {
        memberService.add(name);
    }

}

 

UserService가 이미 MemberService에 의존하고 있는데 MemberService 역시 UserService에 의존하게 되는 상황이죠.

@Service
public class MemberService {

    @Autowired
    private UserService userService;

    public void add(String name){
        userService.addName(name);
    }

}

 

위와 같은 경우에는 순환참조가 발생합니다.

 

두 개의 메서드가 계속해서 서로를 호출하게 되고 Stackoverflow 에러가 발생하게 됩니다.

 

4. 정리

 

이번 포스팅에서는 Autrowired와 RequiredArgsConstructor의 차이점 그리고 특징들을 알아봤습니다.

 

올해 목표 중 하나는 명확한 이유를 모른 채 무지성으로 사용하던 패턴, 어노테이션들을 하나씩 정리해서 제대로 알고 가는 데에 초점을 두려고 합니다.

 

앞으로의 포스팅에서는 흔히 쓰이는 어노테이션을 까보고 해당 내용들을 정리해보려고 합니다.

 

긴 글 읽어주셔서 감사합니다.

반응형