스프링 부트를 사용하면서 자연스럽게 접한 개념이 의존성 주입(Dependency Injection, DI)이다. 처음에는 객체 간의 의존성을 관리하는 설계 패턴이라는 설명이 추상적으로 느껴졌으나, 직접 코드를 작성하며 경험해 보니 이 개념이 코드 구조와 유지보수성에 얼마나 중요한지 깨닫게 되었다. 이 글에서는 의존성 주입의 개념, 장점, 사용하는 이유, 실제 예시와 함께 이를 이해하기 쉽게 비유를 통해 정리하려 한다.
의존성 주입(Dependency Injection)이란?
의존성 주입은 객체가 필요한 의존성을 스스로 생성하지 않고, 외부에서 전달받아 사용하는 설계 패턴이다. 이를 통해 객체 간의 결합도를 줄이고, 코드의 유연성과 재사용성을 높일 수 있다. 스프링 부트에서는 IoC(Inversion of Control, 제어의 역전)의 구현 방법 중 하나로, 스프링 컨테이너가 객체의 생명주기를 관리하며 필요한 의존성을 자동으로 주입한다.
의존성 주입의 장점
1. 결합도 감소
클래스 간의 강한 결합을 줄이고, 인터페이스나 추상 클래스에 의존하도록 설계하여 유연성을 높일 수 있다.
2. 테스트 용이성 증가
Mock 객체를 주입하여 독립적으로 테스트할 수 있기 때문에, 테스트 작성이 쉬워지고 품질이 향상된다.
Mock 객체란?
테스트 환경에서 실제 객체를 대신하여 동작하도록 만든 가짜 객체
- 주로 단위 테스트(Unit Test)를 수행할 때 사용되며, 실제 객체의 의존성을 제거하고 테스트하고자 하는 코드의 동작만 검증할 수 있도록 도와줍니다.
1. 유지보수성 향상
객체 간의 강한 의존성이 사라지므로 특정 구현체가 변경되더라도 코드 수정 범위가 최소화된다.
2. 재사용성과 확장성 증가
동일한 인터페이스를 구현하는 여러 객체를 상황에 따라 주입할 수 있으므로, 코드 재사용성과 확장성이 높아진다.
의존성 주입을 사용하는 이유
의존성 주입은 단순히 객체를 주입받는 것을 넘어, 객체 관리의 효율성과 코드의 구조적 개선을 가능하게 한다.
1. 객체 생성 및 관리 자동화
스프링 컨테이너가 객체의 생성과 생명주기를 관리하기 때문에, 개발자는 객체 관리에 대한 부담을 줄이고 핵심 로직 구현에 집중할 수 있다.
2. 테스트와 구현 분리
테스트 환경에서는 Mock 객체를, 운영 환경에서는 실제 구현체를 주입받아 사용할 수 있으므로 테스트와 구현을 분리하기 쉽다.
3. 변경에 유연한 설계
특정 구현체가 변경되어도 코드 수정 없이 새로운 객체를 주입받아 사용할 수 있어 확장성과 유지보수성이 증가한다.
의존성 주입의 비유
의존성 주입을 이해하기 위해 배달 음식을 예로 들면 이렇다.
1. 직접 요리하는 경우
배가 고플 때, 모든 재료를 사고 요리를 직접 해야 한다. 시간이 오래 걸리고 번거롭다.
2. 배달 서비스를 이용하는 경우
배달 앱(스프링 컨테이너)에 필요한 음식을 요청하면, 배달원이 음식을 가져다준다. 개발자는 필요한 음식(의존성)을 바로 사용할 수 있다. 이는 의존성 주입을 통해 객체를 자동으로 주입받는 것과 유사하다.
결론적으로, 의존성 주입은 객체를 직접 생성하지 않고 외부에서 필요한 것을 가져와 사용하도록 함으로써 효율성과 유연성을 높여준다.
의존성 주입의 방식
1. 생성자 주입(Constructor Injection)
의존성을 생성자를 통해 주입하는 방식이다. 스프링 부트에서 가장 권장되는 방식이며, 불변성을 보장하고 테스트 작성이 용이하다.
@Service
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
2. 세터 주입(Setter Injection)
세터 메서드를 통해 의존성을 주입하는 방식이다.
@Service
public class MyService {
private MyRepository myRepository;
@Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
3. 필드 주입(Field Injection)
필드에 직접 주입하는 방식으로, 가장 간단하지만 테스트가 어렵고 결합도가 높아 권장되지 않는다.
@Service
public class MyService {
@Autowired
private MyRepository myRepository;
}
나는 프로젝트를 진행하면서 아래와 같이 필드 주입(Field Injection)을 사용했었는데 되도록이면 사용하지 않도록 주의해야겠다.
@Autowired
private JuUserService juUserService;
@Autowired
private S3Service s3Service;
@Autowired
private JuSmsService smsService;
생성자를 통한 의존성 주입 방식은 이렇다.
@RestController
public class JuUserController {
private final JuUserService juUserService;
private final S3Service s3Service;
private final JuSmsService smsService;
public JuUserController(JuUserService juUserService, S3Service s3Service, JuSmsService smsService) {
this.juUserService = juUserService;
this.s3Service = s3Service;
this.smsService = smsService;
}
}
'SpringBoot' 카테고리의 다른 글
[SpringBoot] Twilio를 활용한 기념일 알림 기능 개발 (0) | 2025.02.23 |
---|---|
[SpringBoot] Twilio를 활용한 React & Spring Boot 휴대폰 인증 기능 개발 (0) | 2025.02.21 |
[SpringBoot] Spring Boot + React를 활용한 OAuth 2.0 로그인 구현 (카카오 기준) (0) | 2025.02.15 |
[SpringBoot] jsp 만들기 (0) | 2025.02.10 |
[SpringBoot] SpringBoot 설치부터 프로젝트 생성, 의존성 추가까지 (0) | 2025.02.09 |