Spring 프로젝트에서 서버가 클라이언트에게 응답을 보낼 때,
Map
이나 DTO
둘 다 사용할 수 있다.
특히 예외 처리에서 Map<String, Object>
형태로 응답을 구성하는 경우가 많은데,
처음엔 "왜 DTO를 안 만들고 Map으로 하지?" 의문이 들어서 확인해봤다
그래서 이 글에서는 Map
을 사용하는 이유와 함께,
언제 DTO
를 써야 하고 언제 Map
이 좋은지에 대해 정리해보았다.
Map<String, Object>
로 응답을 구성하는 이유Map.of("status", 400, "message", "잘못된 요청입니다");
fieldErrors
가 있고, 어떤 예외는 없을 수도 있음Map
을 쓰면 조건에 따라 응답 필드를 넣고 뺄 수 있음if (ex.hasFieldErrors()) {
body.put("fieldErrors", errors);
}
Map
vs DTO
비교항목 | Map<String, Object> | DTO (Data Transfer Object) |
---|---|---|
✅ 장점 | 빠른 개발, 유연함, 가벼움 | 타입 안정성, IDE 지원, 명확한 구조 |
❌ 단점 | 타입 안정성 부족, IDE 자동완성 X | 클래스 파일이 많아짐, 수정 시 코드량 증가 |
사용 용도 | 일회성 응답, 테스트, 빠른 구현 | 재사용, 문서화, 유지보수 중심의 응답 |
Swagger 연동 | 어려움 (명세 없음) | 문서 자동화 가능 (Model 명세화) |
상황 | 추천 응답 방식 |
---|---|
✅ 간단한 에러 응답 (400, 500 등) | Map<String, Object> |
✅ 예외 핸들링에서 유동적인 필드 필요할 때 | Map<String, Object> |
✅ API 응답을 Swagger에 문서화해야 할 때 | DTO 클래스 사용 |
✅ 응답 형식이 고정되어 있거나 재사용할 경우 | DTO 클래스 사용 |
Map
은 빠르게 응답을 만들고 유연하게 필드를 구성할 수 있어 예외 처리에 최적화 되어 있다.@ExceptionHandler(CustomException.class)
public ResponseEntity<Map<String, Object>> handleCustomException(CustomException ex, HttpServletRequest request) {
return ResponseEntity
.status(ex.getErrorCode().getStatus())
.body(Map.of(
"timestamp", LocalDateTime.now(),
"status", ex.getErrorCode().getStatus().value(),
"error", ex.getErrorCode().getStatus().name(),
"message", ex.getErrorCode().getMessage(),
"path", request.getRequestURI()
));
}
@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponseDto> handleCustomException(CustomException ex, HttpServletRequest request) {
return ResponseEntity
.status(ex.getErrorCode().getStatus())
.body(ErrorResponseDto.from(ex.getErrorCode(), request.getRequestURI()));
}