[Spring] 의존성 주입

LDB·2025년 1월 20일
0

Spring

목록 보기
3/6
post-thumbnail

(해당 게시글은 Spring 계열 프레임워크를 기준으로 작성했습니다.)

작성계기

회사 프로젝트 혹은 개인 프로젝트를 진행할 때 Controller든 Service든 반드시 한다고 해도 과언이 아닌게 의존성 주입이라고 생각한다. 의존성 주입은 사람들마다 말이 다른데 나는 일단 Spring Boot로 프로젝트를 개발하는 사람으로써 의존성 주입에 대해서 긍정적이다. 다시 돌아와서 작성하는 이유는 이론적으로 알고 있고 실전에서도 많이 이용하고 있지만 과거에는 생각없이 썼다는 생각이 문득 들었고 다시는 그러지 말자라는 의미에서 작성하게 되었다.

의존성 주입(Dependency Injection)

의존성 주입 방법을 알려주기 전에 먼저 의존성 주입의 개념을 알필요가 있다.

의존성 : 객체 지향 프로그래밍에서 클래스나 모듈 간의 관계를 의미한다.

그렇다면 의존성 주입은 외부에서 두 객체간의 관계를 결정해주는 디자인 패턴으로 인터페이스를 사이에 두어 클래스 레벨에서 의존관계가 고정되지 않고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다. 또한 테스트가 용이하다는 장점이 존재한다.

의존성 주입이 필요한 이유

프레임워크마다 다르다고 생각하지만 Spring Boot 혹은 Spring Framework를 사용하여 개발하는 개발자라면 필요하다고 생각한다.

  1. 코드의 복잡성이 줄어들고 모듈화를 통한 유지보수가 편하다,
  2. 테스트 용이성이 높아진다, 모의객체(Mock)을 주입하여 테스트가 가능하기에 테스트 환경을 쉽게 구축할 수 있다.
  3. 협업할 경우 충돌이 줄어들고 협업을 용이하게 한다.
  4. 객체들간의 결합도는 줄어들고 유연성이 높아진다.

Spring의 DI 컨테이너

원래 순수 Java에서는 의존성 주입을 하고 DI 컨테이너를 개발자가 만들어 애플리케이션 런타임에 필요한 객체를 생성하여 의존성을 주입해주어야 Spring 계열 프레임워크를 사용한다면 프레임워크 내의 DI 컨테이너가 개발자가 해야할 필요한 객체 생성을 프레임워크에서 해준다.

Spring의 DI 컨테이너의 특징은 하단의 글을 참조하기 바란다.

https://velog.io/@half-phycho/spring-IoC-DI-컨테이너


의존성 주입 방법(Spring)

정확히는 Spring 프레임워크 에서의 의존성 주입방법이다.

생성자 주입

생성자 주입방식은 Spring 4버전 이후에는 생성자 주입방식이 권장되고 있다. 이유는 다음과 같다.

  1. 객체의 불변성 확보
  2. 테스트 코드 작성용이
  3. final 키워드로 NullPointException을 방지할 수 있다.
  4. 순환 참조 에러가 방지된다. (Spring Boot 2.6부터는 기본적으로 허용되지 않는다.)
  5. 스프링에 비침투적인 코드 작성

예시

@Controller
public class MainController {

	private MainService mainService;
    private SubService subService;
    
    // 생성자 주입
    public MainController(MainService mainService, SubService subService) {
    	this.mainService = mainService;
        this.subService = subService;
    }
}

그리고 생성자 주입은 final 키워드를 주입하여 불변으로도 선언이 가능하다고 했는데 다음과 같은 형태로 작성이 가능해진다.

@Controller
public class MainController {

	private final MainService mainService;
    private final SubService subService;
    
    // 생성자 주입
    public MainController(MainService mainService, SubService subService) {
    	this.mainService = mainService;
        this.subService = subService;
    }
}

또한 Lombok 라이브러리라고 존재하는데 이 Lombok 라이브러리는 Java기반 Spring 프레임워크와 만났을 때 강력한 시너지를 발생하는데 Lombok의 @RequiredArgsConstructor 어노테이션을 사용하면 다음과 같이 생성자를 생략하지만 같은 기능으로 동작하는 코드를 작성할 수 있다.

@Controller
@RequiredArgsConstructor
public class MainController {

	private final MainService mainService;
    private final SubService subService;
    
}

이방식의 가장큰 장점은 DI 프레임워크에 의존하지 않고, 순수 자바로도 작동이 되고 객체 지향의 특징을 잘 살릴수 있다. 그리고 파라미터에서 하나라도 누락이되면 바로 컴파일 예외가 발생하는데 컴파일 하지 않고 문제점을 찾을 수 있다.


필드 주입

코드를 정말 심플하게 작성할 수 있지만 외부에서 접근이 불가능해 테스트가 어렵고 프레임워크의 DI가 없으면 사용이 불가능 하다는 단점이 있다. 그래서 사용하지 않는 것을 권장한다.

@Controller
public class MainController {

	@Autowired
	private MainService mainService;
    
}

Setter 주입

Setter 주입은 Setter 메서드를 통해 의존관계를 주입하는 방법이다. 이 방법은 생성자 호출 이후에 필드 변수에 변경이 일어나기 때문에 final 키워드를 붙일 수 없다. 하지만 Setter 매개변수에 붙여서 사용할 수 있다.

주로 의존관계에 변경 가능성이 있는 의존 관계에 사용한다.

@Controller
public class MainController {

	private MainService mainService;
   	
    @Autowired
    public void setMainService(final MainService mainService){
    	this.mainService = mainService;
    }
    
}

Setter 메서드가 public이면 외부에서 수정이 가능해지는데 이렇게 되면 안정성이 위협당한다, 그리고 DI 프레임워크 없이 순수 Java로 객체를 생성한 후 setter를 호출하지 않은 채로 다른 메서드를 호출하게 되면, 누락된 필드의 변수는 null로 남아있어 NullPointerException이 발생하게 된다.


결론

현재로써 가장 좋은 방법은 생성자 주입방식이 가장 좋다고 볼 수 있겠다. 그래서 이 게시글을 작성하기 전에도 주로 생성자 주입방식으로 의존성 주입을 진행해왔다. 이번 정리로 누군가 어째서 생성자 주입을 했냐고 물어보면 잘 대답할 수 있겠다.

  • 가장 큰 이유는 Spring 프레임워크의 권장사항이다.
  • 테스트코드 작성이 용이하다.
  • 생성자를 불변으로 설계가 가능하다.

참고 사이트

https://mangkyu.tistory.com/150

https://mangkyu.tistory.com/125

https://engineerinsight.tistory.com/46

https://velog.io/@sana/DI-의존성-주입Dependency-Injection-의-개념과-방법

https://velog.io/@wlsdks12/Spring-의존성-주입DI-Dependency-Injection

(항상 감사합니다.)

profile
가끔은 정신줄 놓고 멍 때리는 것도 필요하다.

0개의 댓글

관련 채용 정보