프로덕션 급 Spring Boot API — 파트 2: 클린 코드 구조, 컨트롤러 및 DTO
발행: (2026년 1월 18일 오전 04:52 GMT+9)
4 min read
원문: Dev.to
Source: Dev.to
이 게시물에서 다루는 내용
- 컨트롤러 책임 (API 경계, 로직이 아님)
@RestController가 단순히 “JSON 반환” 이상의 의미를 갖는 이유- 생성자 주입 (그리고 왜 필수인지)
- Spring 스테레오타입 어노테이션과 의미가 중요한 이유
- 엔티티 vs DTO (엔티티 노출이 위험한 이유)
- 프로덕션 시스템에서 Lombok
- ModelMapper가 레이어를 깨끗하게 유지하는 이유
컨트롤러 레이어 (API 경계)
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
}
@RestController가 실제 의미하는 바
- 클래스를 HTTP 요청 핸들러로 등록
- JSON 반환 (뷰가 아님)
DispatcherServlet과 통합
이 클래스는 비즈니스 로직이 아니라 API 계약을 정의합니다.
생성자 주입 (프로덕션에서 협상 불가)
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
생성자 주입이 필요한 이유:
- 의존성이 명시적
- 객체가 불변
- 테스트가 용이
- 시작 시 빠르게 실패 (의존성이 없으면 애플리케이션이 시작되지 않아야 함)
스테레오타입 어노테이션 (의미가 중요함)
| Annotation | Purpose |
|---|---|
@Service | 비즈니스 로직 |
@Repository | 데이터 접근 |
@Controller | MVC 레이어 |
@RestController | REST API |
모두 @Component를 확장하지만 의미, 가독성 및 레이어별 동작을 추가합니다.
엔티티 vs DTO (엔티티는 절대 노출하지 말 것)
엔티티
- 데이터베이스 테이블에 매핑
- 영속성에만 사용
DTO
- API 통신에 사용
- 내부 구조를 보호
엔티티는 DB 요구에 따라 변경되고, DTO는 API 요구에 따라 변경됩니다.
Lombok (프로덕션 안정성)
Lombok 없이:
- setter가 없으면 → JSON 필드가
null이 됩니다 - getter가 없으면 → 응답이 비어 있습니다
- 엄청난 보일러플레이트
Lombok 사용 시:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Order {
private Long id;
private String description;
// other fields...
}
- getter / setter, 생성자, 빌더 등을 자동 생성
- 엔티티를 깔끔하게 유지
Lombok은 대규모 Spring Boot 코드베이스에서 사실상 필수입니다.
ModelMapper (레이어 분리)
- 엔티티 ⇄ DTO 변환
- 컨트롤러를 깔끔하게 유지
- 영속성 모델 유출 방지
Persistence는 데이터가 애플리케이션 실행 시간 이후에도 지속됨(데이터베이스 저장)을 의미합니다.
이 글은 Production‑Grade Spring Boot API Design 시리즈의 3부 구성 중 Part 2입니다.
👉 Part 3에서는 일관된 API 응답, BaseResponse, ResponseEntity, 전역 예외 처리에 대해 다룹니다.