Spring - 생성자 주입방식 4가지 완전 정리
@RequiredArgsConstructor vs @Autowired vs 생성자 메소드(수동 작성) vs @Resource
Spring 생성자 주입 완전 정리
@Autowired vs 생성자 주입 vs Lombok @RequiredArgsConstructor
사전 필수 지식
이 글을 이해하기 위해 아래 개념을 알고 있으면 좋습니다.
서비스 / 컴포넌트 주입(Dependency Injection) 개념
생성자 주입이 무엇인지
스프링의 기본 어노테이션 사용법
스프링이 빈을 관리하는 방식
자바 기본 문법
오늘 알아볼 내용
서비스/컴포넌트를 주입하는 3가지 방식 비교
// 빈을 오토와이어드해서 전달한다
라는 말의 정확한 의미
3. @RequiredArgsConstructor의 정체
private final 필드만으로 생성자를 만든다는 의미
Controller / Service에
Lombok + 생성자 주입을 적용한 실전 예제
스프링에서 @Autowired 없이도 주입이 가능한 이유
결론부터 말하면,
스프링 4.3 이상에서는
생성자가 하나뿐이면 @Autowired가 없어도
스프링이 자동으로 의존성을 주입합니다.
즉,
“생성자 = 주입 포인트” 가 되는 구조입니다.
1️⃣ 생성자 주입 (Constructor Injection) 기본 형태
가장 정석적인 코드
@RestController
public class SiteInfoController {
private final SiteInfoService siteInfoService;
public SiteInfoController(SiteInfoService siteInfoService) {
this.siteInfoService = siteInfoService;
}
// ...
}
여기서 스프링이 하는 일
SiteInfoController빈 생성생성자 파라미터 타입 확인
SiteInfoService타입의 빈 탐색자동으로 주입
👉 생성자가 1개뿐이기 때문에@Autowired 없이도 동작합니다.
✅ 생성자 주입의 장점
불변성 보장
final필드 사용 가능
NPE 방지
생성 시점에 무조건 주입
테스트 용이
생성자로 Mock 객체 주입 가능
의존성이 명확
클래스 설계가 눈에 보임
👉 그래서 스프링 공식 권장 방식
2️⃣ @Autowired 필드 주입 방식 (비권장)
@RestController
public class SiteInfoController {
@Autowired
private SiteInfoService siteInfoService;
// ...
}
동작 방식
스프링이 리플렉션으로
private 필드에 강제로 값 주입
장점
코드가 짧다
단점 (치명적)
final 사용 불가 → 불변성 깨짐
테스트 시 Mock 주입 어려움
의존성이 코드에 드러나지 않음
객체 생성 시점이 불명확
👉 실무에서는 거의 사용하지 않음
3️⃣ Lombok @RequiredArgsConstructor란?
핵심 한 줄 요약
final 또는 @NonNull 이 붙은 필드만을
파라미터로 갖는 생성자를 자동 생성
Lombok 적용 전 (순수 생성자)
public class SiteInfoController {
private final SiteInfoService siteInfoService;
public SiteInfoController(SiteInfoService siteInfoService) {
this.siteInfoService = siteInfoService;
}
}
Lombok 적용 후
@RequiredArgsConstructor
public class SiteInfoController {
private final SiteInfoService siteInfoService;
}
✔ 코드 동일
✔ 생성자 자동 생성
✔ @Autowired 필요 없음
4️⃣ Controller / Service 실전 예제 (권장 구조)
📌 Service 계층
@Service
@RequiredArgsConstructor
public class SiteInfoService {
private final SiteInfoRepository siteInfoRepository;
public String getSiteName() {
return siteInfoRepository.findSiteName();
}
}
Service도 동일하게 생성자 주입
의존성이 한눈에 보임
테스트 코드 작성 매우 쉬움
📌 Controller 계층
@RestController
@RequiredArgsConstructor
@RequestMapping("/site")
public class SiteInfoController {
private final SiteInfoService siteInfoService;
@GetMapping("/name")
public String getSiteName() {
return siteInfoService.getSiteName();
}
}
이 코드의 특징
생성자 코드 ❌
@Autowired ❌
final 필드 ⭕
Lombok ⭕
👉 가장 깔끔하고 실무에서 많이 쓰는 형태
5️⃣ 자주 나오는 질문 정리
Q. @Autowired를 왜 안 쓰나요?
필요 없기 때문
단일 생성자는 스프링이 자동 주입
Q. @Builder랑 같이 써도 되나요?
엔티티 / DTO 에서는 OK
Controller / Service 에서는 ❌ (주입 목적과 충돌)
Q. 모든 필드에 final을 붙여야 하나요?
주입받는 의존성에는 무조건 final 권장
🔹 요약 정리
| 방식 | 특징 | 실무 사용 |
|---|---|---|
| 필드 주입 (@Autowired) | 간단하지만 문제 많음 | ❌ |
| 생성자 주입 | 안정적, 테스트 쉬움 | ⭕ |
| @RequiredArgsConstructor | 생성자 주입 + 간결함 | ⭕⭕ |
💡 결론
스프링 4.3 이상 + 단일 생성자
→@Autowired필요 없음final + 생성자 주입
→ 가장 안전한 구조Lombok @RequiredArgsConstructor
→ 실무 최적의 선택
지금 사용하는 코드 스타일이
“스프링이 가장 좋아하는 구조” 입니다.
후속 추천 질문
[1] 면접용 답변 +
[2] 스프링 내부 동작 기준 설명을
실제 면접에서 바로 써먹을 수 있게 정리해 드리겠습니다.
[1] 면접에서 생성자 주입을 설명하는 모범 답변
🔹 한 줄 요약 답변 (신입·주니어용)
스프링에서는 생성자 주입을 권장합니다.
스프링 4.3 이상에서는 생성자가 하나뿐이면@Autowired없이도 자동 주입이 가능하고,
final 필드를 사용할 수 있어 불변성을 보장하고 테스트가 쉬워지기 때문입니다.
🔹 조금 더 자세한 답변 (중급자용)
생성자 주입은 객체 생성 시점에 의존성이 반드시 주입되도록 보장합니다.
그래서 NullPointerException을 사전에 방지할 수 있고,
final 필드를 사용할 수 있어 객체가 불완전한 상태로 생성되는 것을 막을 수 있습니다.
또한 테스트 코드에서 생성자를 통해 Mock 객체를 쉽게 주입할 수 있어
필드 주입보다 테스트와 유지보수에 훨씬 유리합니다.
🔹 Lombok까지 언급하는 답변 (가산점)
실무에서는 Lombok의
@RequiredArgsConstructor를 사용해
final 필드만을 대상으로 생성자를 자동 생성합니다.
이 방식은 생성자 주입의 장점은 그대로 유지하면서
보일러플레이트 코드를 줄일 수 있어 가장 많이 사용됩니다.
❌ 이렇게 말하면 감점
“@Autowired가 귀찮아서요”
“요즘은 다 이렇게 씁니다”
“그냥 회사 코드가 그래서요”
👉 이유 + 내부 동작 + 장점을 말해야 점수가 올라갑니다.
[2] 스프링 팀이 필드 주입을 지양하는 이유 (내부 동작 기준)
이건 시니어/아키텍처 질문으로 자주 나옵니다.
핵심은 객체 생성 시점과 스프링 컨테이너의 역할입니다.
1️⃣ 객체 생성 시점이 다르다
필드 주입의 실제 흐름
기본 생성자로 객체 생성
객체는 비어 있는 상태
스프링이 리플렉션으로 필드에 값 주입
👉 즉, 객체가 불완전한 상태로 잠시 존재
생성자 주입의 흐름
생성자 호출
필요한 의존성을 모두 전달
완전한 객체 생성
👉 스프링이 가장 선호하는 객체 생성 방식
2️⃣ final을 사용할 수 없다 = 설계가 깨진다
필드 주입
@Autowired
private SiteInfoService siteInfoService;
final ❌
언제든 null 상태 가능
객체의 상태를 신뢰할 수 없음
생성자 주입
private final SiteInfoService siteInfoService;
final ⭕
생성 이후 변경 불가
객체 상태 안정적
👉 자바 객체지향 설계 원칙(OOP)에 더 부합
3️⃣ 테스트가 구조적으로 불리하다
필드 주입 테스트
리플렉션 사용
스프링 컨텍스트 띄워야 함
단위 테스트 어려움
생성자 주입 테스트
new SiteInfoController(mockService);
스프링 없이 테스트 가능
순수 자바 테스트 가능
테스트 속도 빠름
👉 스프링 팀이 강조하는 “단위 테스트 친화성”
4️⃣ 순환 참조를 조기에 발견할 수 있다
생성자 주입
애플리케이션 시작 시점에 즉시 오류 발생
설계 문제를 빨리 인지
필드 주입
런타임까지 숨어 있다가 터짐
디버깅 난이도 상승
👉 Fail Fast 원칙
5️⃣ 스프링 공식 문서 입장
스프링 팀의 철학:
가능한 한 객체는
생성 시점에 완전해야 한다.
그래서
생성자 주입 ✅
필드 주입 ❌
setter 주입 ⚠️ (선택적 의존성일 때만)
💡 최종 정리 (면접용 핵심 문장)
스프링 팀이 필드 주입을 지양하는 이유는
객체가 불완전한 상태로 생성되고,
불변성을 보장할 수 없으며,
테스트와 유지보수에 불리하기 때문입니다.
생성자 주입은 객체 생성 시점에 의존성을 확정하므로
설계적으로 더 안전한 방식입니다.
댓글
댓글 쓰기