Spring 프로젝트를 하다 보면 종종 다음과 같은 생각이 들 수 있다:
"DTO 클래스에 서비스나 레포지토리를 주입해서 뭔가 처리하면 안 될까?"
하지만 결론부터 말하자면 DTO 클래스에 의존성을 주입하는 것은 바람직하지 않으며, 실제로도 잘 작동하지 않는다.
왜 그런 걸까?
DTO(Data Transfer Object)는 말 그대로 데이터를 전달하는 객체이다.
public class UserRequestDTO {
private String username;
private String email;
// Getter / Setter
}
public class UserRequestDTO {
@Autowired
private UserService userService; // 이런 식으로 서비스 주입
...
}
이렇게 하면 실제로 Spring이 빈을 주입해주지 않는다. 이유는 다음과 같다:
Spring은 @Component
, @Service
, @Controller
등으로 등록된 클래스만 스프링 컨테이너가 관리하는 빈으로 인식하고, 여기에만 @Autowired
로 의존성을 주입할 수 있다.
DTO는 대부분 외부에서 new로 생성되며, 스프링이 관리하는 대상이 아니다.
즉, Spring 입장에서 UserRequestDTO는 관심 대상이 아니다.
DTO는 오직 데이터 전달에만 집중해야 한다.
DTO에 빈을 주입하는 행위는 단일 책임 원칙(SRP) 과 의존성 역전 원칙(DIP) 을 위배한다.
DTO에서 뭔가 비즈니스 로직이 필요한 상황이라면 그 로직은 DTO가 아니라 Service 또는 Facade 계층에 위치해야 한다.
즉, “DTO는 단순히 전달하고, 판단과 처리는 Service가 한다” 는 원칙을 지키면 된다.
항목 | DTO | Service/Component |
---|---|---|
역할 | 데이터 전달 | 비즈니스 로직 |
빈 등록 | ❌ 일반적으로 아님 | ✅ 스프링 관리 대상 |
의존성 주입 | ❌ 불가능 / 지양 | ✅ 사용 가능 |
상태 보존 | 가능하지만 최소화 | 상태 및 로직 중심 |
설계 책임 | 단일 책임 (데이터 보관) | 업무 처리 책임 |
DTO는 단순하고 깨끗해야 한다.
간혹 “DTO 안에서 뭔가 처리가 되면 더 편하지 않을까?”라는 유혹이 들 수 있지만, 그렇게 되면 결국 계층 간 책임이 섞이고 유지보수가 어려워진다.
Spring의 구조적 특성을 이해하고, DTO는 순수하게, 로직은 로직답게, 분리해서 작성하자.
“DTO에 @Autowired를 붙이고 싶을 때는, 설계가 어긋난 건 아닌지 다시 돌아보자.”