spring

Entity와 DTO, 어디까지 분리해야 할까?

태오님 2025. 8. 8.

서비스를 설계할 때 흔히 접하는 질문 중 하나가 바로 “Entity와 DTO를 어디까지 분리해야 하는가?”이다. 프로젝트 초반에는 모든 계층에서 Entity를 직접 사용하는 방식으로 시작하기 쉽지만, 시간이 지날수록 데이터 흐름이 복잡해지고, 여러 계층에서 도메인 객체를 공유하는 구조는 다양한 문제를 초래한다.

이 글에서는 Entity와 DTO의 역할을 명확히 나누는 이유와, 이를 어떤 기준으로 설계해야 하는지 단계적으로 정리해본다.


1. Entity와 DTO는 역할부터 다르다

개념 목적 책임
Entity DB 매핑, 영속성 관리 도메인 상태를 표현하고 저장
DTO (Data Transfer Object) 계층 간 데이터 전달 외부와의 인터페이스, 직렬화/역직렬화, UI 모델

Entity는 비즈니스 로직과 밀접한 관계를 가지며, ORM 매핑에 최적화된 구조로 설계된다. 반면 DTO는 데이터 전달에 집중하며, 필요에 따라 필드 이름을 바꾸거나, 특정 계산값을 포함시키는 등의 유연성이 필요하다.


2. Entity를 직접 반환하면 생기는 문제들

처음에는 Controller → Service → Repository 흐름 속에서 Entity를 그대로 리턴하는 구조가 간결해 보일 수 있다. 하지만 다음과 같은 문제들이 누적된다:

1) Lazy 로딩으로 인한 예외 (LazyInitializationException)

컨트롤러에서 Entity를 반환했는데, 연관된 필드가 LAZY로 설정되어 있을 경우 트랜잭션 밖에서 프록시 초기화가 시도되며 예외가 발생한다.

2) 응답 필드 과다 노출

직렬화 시 Jackson 등의 라이브러리는 Entity의 모든 getter를 순회하며 필드를 JSON으로 변환한다. 비밀번호, 내부 ID 등 민감한 정보까지 노출될 수 있다.

3) API 변경이 Entity 구조에 영향을 줌

외부 인터페이스와 내부 도메인을 동일 객체로 묶으면, UI 요구사항에 따라 Entity 구조까지 수정해야 하는 문제가 생긴다.


3. DTO 설계 기준

DTO는 단순히 “Entity를 흉내 낸 클래스”가 아니다. 다음의 기준으로 설계해야 한다.

  • 필요한 필드만 포함 (최소 정보 원칙)
  • 불변성 유지 (가능하면 val, 생성자 기반)
  • UI 또는 클라이언트 친화적 구조
  • 계층 구조를 단순화 (Entity는 연관 객체를 포함하지만, DTO는 평탄화 가능)

예시:

// Entity
public class Post {
    private Long id;
    private String title;
    private User author; // 연관관계
}

// DTO
public class PostResponse {
    private Long id;
    private String title;
    private String authorName; // 평탄화
}

4. Mapper 계층의 도입과 전략

Entity → DTO, DTO → Entity로의 변환을 담당하는 계층을 Mapper 또는 Converter라고 부른다. 이 계층을 별도로 두면 다음과 같은 이점이 있다:

  • 변환 로직이 서비스 로직에 섞이지 않아 코드가 명확해진다
  • 테스트가 용이하다
  • Entity 변경 시에도 API가 안정적으로 유지된다

전략적으로는 다음과 같은 방법이 있다:

  1. 수동 변환 방식 – 가장 직관적이고 디버깅이 쉬움
  2. ModelMapper, MapStruct – 대규모 DTO가 많거나 자동 변환이 필요한 경우 유용

예시 (수동 변환):

public class PostMapper {
    public static PostResponse toResponse(Post post) {
        return new PostResponse(
            post.getId(),
            post.getTitle(),
            post.getAuthor().getName()
        );
    }
}

5. 계층 구조 설계와 응집도

아래는 Entity와 DTO의 역할을 분리한 아키텍처 흐름이다.

Controller → DTO ↔ Service ↔ Entity ↔ Repository
  • Controller는 DTO만 알고, Entity를 직접 다루지 않음
  • Service는 Entity를 기준으로 비즈니스 로직 처리
  • DTO ↔ Entity 변환은 명확히 분리된 Mapper에서 수행

이렇게 하면 계층 간 의존도가 낮아지고, 유지보수가 쉬워진다.


마무리

Entity와 DTO의 경계를 분리하는 것은 단순히 코드 스타일의 문제가 아니다.
응답 속도, 보안, 유지보수, 확장성 등 다양한 측면에서 영향이 큰 설계 결정이다.
초기에는 단순한 구조를 선호할 수 있지만, 일정 규모 이상의 프로젝트에서는 반드시 역할을 명확히 분리하고, 각 계층이 자신의 책임만을 수행하도록 설계하는 것이 안정적인 시스템 운영에 도움이 된다.


참고자료


댓글