Back-end/Spring

[Spring] DTO와 ResponseEntity로 깔끔한 응답 처리하기

랑 이 2025. 8. 28. 14:54
반응형

Spring Boot로 API를 개발할 때 응답을 어떻게 반환할지에 대한 고민이 생깁니다

 

API 개발을 할때할 때 DB와 같이 사용하여 응답하도록 구현하는데 여기서 엔티티 자체를 반환한다면 불필요한 값도 같이 포함하여 반환하게 되기 때문에 비효율적인 방식입니다 또한 상태 코드나 헤더를 포함하여 반환해야 할 때도 존재합니다

 

이러한 문제점을 보완 하기 위해 DTO와 ResponseEntity를 같이 사용하게 되면 안정적이고 명확한 응답 처리가 가능합니다

DTO(Data Transfer Object)란?

API 요청/응답 전용 객체로 엔티티를 직접 노출하지 않고 필요한 데이터만 클라이언트 및 서버에 전달하는 역할을 합니다

유지보수성과 안정성을 높여주며 많이 사용되는 방식입니다

 

다음과 같은 User 엔티티가 존재한다고 했을 때 DTO를 생성하여 응답과 요청을 관리할 수 있습니다

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long user_id;

    @Column(name = "username", nullable = false,length = 10,unique = true)
    private String username;

    @Column(name = "email", nullable = false,length = 50)
    private String email;

    @Enumerated(EnumType.STRING)
    @Column(name = "role", nullable = false)
    private Role role;

    @Column(name = "created_at", nullable = false,updatable = false)
    private LocalDateTime createdAt;
 }

 

UserCreateRequest

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserCreateRequest {

    @NotBlank(message = "이름은 필수 입력값입니다.")
    @Size(min = 2, max = 20, message = "이름은 2~20자 이내여야 합니다.")
    private String userName;
}

소셜 로그인을 통해 회원가입을 진행하고 이름을 입력 받는 요청에 대한 DTO입니다

 

DTO를 사용하여 값을 입력받는다면 필요한 값만 받을 수 있지만 DTO를 사용하지 않는다면 User 엔티티 자체를 받아야 하며 이는 불필요한 데이터까지 받아야 하는 문제점이 존재합니다 또한 목적에 맞는 DTO를 만들어 활용할 수 있습니다

 

유저를 생성하는 DTO, 유저의 이름을 변경하는 DTO 등 여러 가지 목적에 대한 값만 받을 수 있도록 구현이 가능합니다

 

UserResponse

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class UserResponse {
    private Long id;
    private String name;
    private String email;
}

유저의 정보를 조회하는 요청에 대한 DTO입니다

 

일반적으로 조회 API에서는 엔티티 전체를 내려주지 않고 응답이 필요한 데이터만 JSON으로 변환하여 반환합니다

만약에 엔티티에 민감한 개인 정보가 존재한다면 제외하고 내려주는 게 보안적으로 안전합니다

 

또한 Java의 record 문법을 활용해 DTO를 간결하게 정의할 수도 있습니다

// record를 활용한 응답 DTO 예시
public record UserResponse(Long id, String username, String email) {}

record는 Java 16부터 정식 지원되는 문법으로 불변 데이터를 간단히 표현하기 위해 만들어진 문법입니다

선언만 하면 getter,equals,hashCode,toString 등이 자동 생성되며 모든 필드는 기본적으로 final로 취급됩니다

 

기존 class 문법의 DTO는 getter,equals를 직접 구현 및 라이브러리로 구현하지만 record 문법을 사용하면 따로 정의하지 않아도 사용 가능하다는 장점이 존재합니다

 

Class DTO vs Record DTO 비교

구분 Class DTO Record DTO
선언 방식 필드 + 생성자 + getter/setter 직접 작성
(Lombok 어노테이션 사용)
record UserDto(Long id,String name) 한줄로 선언
불변성 기본적으로 가변 →  final 지정 or setter 제거 필요 모든 필드 private final → 기본 불변
보일러플레이트 많음(getter,toString,equals,hashCode 구현 필요) 자동 생성 (getter, equals, hashCode, toString)
빌더 패턴 Lombok @Builder 등 지원 기본 제공 없음
직렬화/역직렬화 JPA,Jackson 등과 친화적 Spring Boot 2.6 + Jackson 2.12 부터 안정 지원
상속 가능 final 특성으로 상속 불가능
적합한 용도 - 복잡한 생성 로직이 필요한 DTO
- 빌더 패턴이 필요한 경우
- JPA 엔티티는 반드시 Class
- 요청/응답 DTO
- 조회 전용 데이터
- 단순 이벤트/메시지 전송용

ResponseEntity란?

Spring에서 제공하는 HTTP 응답 객체로 상태 코드 + 헤더 + 바디를 모두 제어 가능합니다

제네릭 <T>으로 다양한 타입의 응답을 안정하게 반환할 수 있도록 합니다

@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
    UserResponse dto = new UserResponse(id, "test", "test@test.com");
    return ResponseEntity.ok(dto); // 200 OK + JSON 응답
}

간단하게 구현한 해당 id를 가지는 유저의 정보를 조회하는 API의 기본 예제입니다

 

컨트롤러 메서드의 반환 타입을 ResponseEntity <T>로 지정하면 T → 응답 바디에 담길 객체의 타입을 의미합니다

따라서 T와 일치하는 타입을 반환해야 하며 그렇지 않으면 컴파일 에러가 발생하게 됩니다

 

타입에 대한 안정성을 높일 수 있다는 장점이 있습니다

 

또한 상태코드(200 Ok,201 Created,404 Not Found 등), 헤더까지 함께 제어할 수 있습니다

DTO와 ResponseEntity를 같이 사용하는 이유

1. 타입 안정성과 명확성 계약

- DTO는 엔티티를 그대로 두고, 필요한 데이터만 담아 응답하는 구조로 만들어 API의 보안성과 명확성을 높여줍니다

- ResponseEntity<DTO>로 반환 타입을 지정하면 컴파일 시점에서 DTO 형태로만 응답해야 하는 제약이 생깁니다

잘못된 타입 반환 방지 + API 일관성 보장

 

2. REST API 규약 준수

- RESTFul API는 상태 코드 + 응답 바디 + 헤더 조합이 중요합니다 

- DTO는 바디 구조, ResponseEntity는 상태 코드/헤더를 맡아 역할 분리

REST 표준에 맞는 응답 제공

 

3. 협업/문서화 용이

- 프론트엔드/모바일 팀과 협업할 때 DTO가 API 응답 모델 역할을 하여 문서화가 명확해지는 장점이 있습니다 

- Swagger/OpenAPI를 사용하면 DTO의 클래스를 스펙에 노출하여 어떤 용도로 사용하는지 명확히 알 수 있습니다

개발/테스트/협업이 수월해짐

 

DTO + ResponseEntity 조합은 “응답 데이터 구조는 명확하고 안전하게, 응답 메타정보(상태/헤더)는 자유롭게 제어”할 수 있게 하여 신뢰성 높은 REST API를 만드는 핵심 패턴입니다.

반응형