| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 자바
- Eclipse
- Java
- 인텔리제이
- Visual Studio Code
- HashMap
- js
- input
- SpringBoot
- ArrayList
- math
- junit5
- 문자열
- 자바스크립트
- javascript
- IntelliJ
- 테스트자동화
- string
- list
- 스프링부트
- 정규식
- html
- 단위테스트
- CSS
- vscode
- Array
- 배열
- java테스트
- 자바문법
- junit
- Today
- Total
어제 오늘 내일
[Spring Boot] "객체 관리는 스프링에게 맡기세요!" 의존성 주입(DI) 완벽 가이드 본문
Spring Boot를 공부하다 보면 "DI(Dependency Injection, 의존성 주입)"라는 단어를 정말 많이 듣게 됩니다.
면접 단골 질문이기도 하고, 좋은 객체지향 설계를 위해 반드시 이해해야 하는 개념이죠.
오늘은 DI가 도대체 무엇인지, 그리고 왜 필드 주입보다 생성자 주입을 권장하는지 명쾌하게 정리해 드리겠습니다.
1. 의존성 주입(DI)이 뭔가요?
요리를 한다고 상상해 봅시다.
- DI가 없는 경우: 요리사가 요리할 때마다 직접 농장에 가서 재료를 캐오고, 칼을 대장간에서 만들어옵니다. (객체가 의존 객체를 직접 생성
new) - DI가 있는 경우: 요리사는 요리에만 집중하고, 누군가가 손질된 재료와 좋은 칼을 주방에 딱 놔줍니다. (외부에서 의존 객체를 주입)
프로그래밍에서 DI(Dependency Injection)란, 객체가 자신이 사용할 의존성(다른 객체)을 직접 생성하지 않고, 외부(스프링 컨테이너)로부터 주입받아 사용하는 디자인 패턴입니다.
이로 인해 개발자는 객체 간의 결합도를 낮추고 유연한 코드를 짤 수 있게 됩니다. 이것이 바로 IoC(Inversion of Control, 제어의 역전)의 핵심입니다.
2. 스프링에게 "이거 관리해 줘"라고 말하는 법 (Bean 등록)
DI를 받으려면 먼저 스프링 컨테이너에 "이 객체는 네가 관리해!"라고 등록해야 합니다. 이렇게 등록된 객체를 빈(Bean)이라고 부릅니다.
주로 다음과 같은 어노테이션을 클래스 위에 붙여 사용합니다.
@Component: 일반적인 컴포넌트@Service: 비즈니스 로직을 담당하는 서비스@Repository: DB 접근을 담당하는 리포지토리@Controller: 웹 요청을 처리하는 컨트롤러
3. 의존성을 주입받는 3가지 방법
빈으로 등록된 객체를 가져다 쓰는(주입받는) 방법은 크게 세 가지가 있습니다.
① 필드 주입 (Field Injection)
가장 코드가 짧고 편해서 과거에 많이 쓰였습니다.
@Service
public class UserService {
@Autowired // 필드 위에 바로 붙임
private UserRepository userRepository;
}
- 단점: 외부에서 변경이 불가능하여 테스트하기 어렵고, 프레임워크(Spring)에 너무 의존적입니다. (비추천)
② 세터 주입 (Setter Injection)
Setter 메서드를 통해 주입받습니다.
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
- 특징: 선택적인 의존성이나 변경 가능성이 있는 의존성에 사용합니다. 하지만 실무에서는 거의 쓸 일이 없습니다.
③ 생성자 주입 (Constructor Injection) ★강력 추천★
생성자를 통해 주입받습니다. (Spring 4.3부터는 생성자가 하나면 @Autowired 생략 가능)
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
4. 왜 "생성자 주입"을 써야 할까요?
IntelliJ 같은 IDE에서도 필드 주입을 쓰면 경고를 띄우며 생성자 주입을 권장합니다. 그 이유는 명확합니다.
- 불변성(Immutability) 보장:
final키워드를 사용할 수 있습니다. 한 번 주입되면 절대 변하지 않으므로 안전합니다. - 테스트 용이성: 순수한 자바 코드로 단위 테스트를 작성할 때, 생성자로 가짜 객체(Mock)를 주입하기 쉽습니다.
- NPE(Null Pointer Exception) 방지: 객체 생성 시점에 의존성이 없으면 아예 컴파일 에러나 실행 에러가 발생하므로,
Null상태로 돌아가는 것을 원천 봉쇄합니다. - 순환 참조 감지: A가 B를 참조하고 B가 A를 참조하는 악순환을 앱 구동 시점에 바로 잡아낼 수 있습니다.
💡 꿀팁: Lombok으로 코드 다이어트하기
생성자 주입이 좋은 건 알겠는데, 코드가 너무 길어진다고요? Lombok 라이브러리의 @RequiredArgsConstructor를 쓰면 마법처럼 해결됩니다.
@Service
@RequiredArgsConstructor // final이 붙은 필드의 생성자를 자동 생성!
public class UserService {
private final UserRepository userRepository;
private final MemberRepository memberRepository;
// 생성자 코드를 짤 필요가 없습니다.
}
실무에서는 99% 이 방식을 사용합니다. 깔끔하고 안전하죠!
마치며
오늘의 결론입니다.
- DI는 객체를 직접 만들지 않고 외부에서 받는 것이다.
@Autowired필드 주입은 이제 그만!private final+ 생성자 주입 (with Lombok) 패턴을 사용하자.
이 기본기만 탄탄해도 Spring Boot 애플리케이션을 훨씬 더 안정적으로 설계하실 수 있습니다.
다음 포스팅에서는 Spring Bean의 생명주기(Lifecycle)와 Scope에 대해 다뤄보겠습니다.
'IT > SpringBoot' 카테고리의 다른 글
| [Spring Boot] 마법이 아니라 과학입니다! 자동 설정(Auto Configuration)의 비밀 (0) | 2026.03.09 |
|---|---|
| [Spring Boot] 빈(Bean)의 탄생과 죽음: 생명주기(Lifecycle)와 스코프(Scope) 완벽 정리 (0) | 2026.03.09 |
| [Spring Boot] 톰캣(Tomcat)을 따로 설치 안 해도 된다고? 내장 서버의 비밀 (0) | 2026.03.04 |
| [Spring Boot] Docker로 3분 만에 모니터링 구축하기 (Prometheus + Grafana) (0) | 2026.03.04 |
| [Spring Boot] 서버의 건강검진 리포트, Actuator 완벽 가이드 (0) | 2026.03.03 |
