반응형
DTO vs 엔티티: 왜 엔티티만으로 데이터를 주고받으면 안 될까?
스프링 공부를 하면서 DTO를 사용해서 계층 간 데이터를 전달했습니다.
DTO가 무엇이고 왜 엔티티로 데이터를 주고 받지 않을까 하는 궁금증으로 이 글을 작성하게 되었습니다.
엔티티와 DTO 기본 개념
엔티티(Entity)란?
- 정의
엔티티는 데이터베이스의 테이블과 1:1로 매핑되는 클래스입니다.
예를 들어, 데이터베이스에 users라는 테이블이 있다면, 이 테이블의 각 행(row)을 자바 객체로 표현한 것이 엔티티입니다. - 특징
- 데이터베이스와의 직접적인 매핑: 엔티티 클래스는 보통 JPA(Java Persistence API)를 사용하여 데이터베이스와 직접 연동됩니다.
- 비즈니스 로직 포함 가능: 일부 엔티티는 데이터베이스와 관련된 기본적인 비즈니스 로직을 포함하기도 합니다.
- 모든 필드를 포함: 데이터베이스의 모든 컬럼에 해당하는 정보, 즉 ID, 이름, 비밀번호, 생성일 등 모든 데이터를 포함할 수 있습니다.
DTO(Data Transfer Object)란?
- 정의
DTO는 계층 간 데이터 전달에만 집중하는 객체입니다.
예를 들어, 컨트롤러에서 클라이언트로 응답을 보낼 때나, 외부 API와 데이터를 주고받을 때 사용됩니다. - 특징
- 데이터 전송 전용: DTO는 비즈니스 로직이 전혀 포함되지 않으며, 오직 필요한 데이터만 담아 전달합니다.
- 선택적 데이터 포함: 클라이언트에게 보여줄 정보만 선택해서 담을 수 있으므로, 불필요하거나 민감한 정보는 제거할 수 있습니다.
- 유연한 구조: API의 버전 변경이나 프론트엔드 요구사항에 맞게 데이터를 재구성할 수 있어, 엔티티 구조와는 별개로 자유롭게 설계할 수 있습니다.
엔티티를 그대로 사용하는 것이 위험한 이유
1) 보안 문제
엔티티에는 민감한 정보가 포함될 수 있습니다.
- 민감 정보 노출
예를 들어, User 엔티티에는 비밀번호, 주민등록번호, 내부 권한 등의 정보가 포함될 수 있습니다.
이 정보를 클라이언트에 그대로 노출하면 보안 취약점이 될 수 있습니다. - Ex
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password; // 절대 외부에 노출하면 안 되는 정보
private String email;
// 기타 내부 관리용 필드...
}
엔티티를 그대로 응답 데이터로 사용한다면, 비밀번호나 내부 식별자 등이 클라이언트에 노출되어 악용될 위험이 있습니다.
2) 결합도(Coupling) 문제
- 데이터베이스와 UI의 결합
엔티티는 데이터베이스 스키마(구조)에 매우 밀접하게 연결되어 있습니다.
만약 데이터베이스 구조가 변경되면, 그 영향을 받는 모든 계층에 수정이 필요할 수 있습니다. - API의 유연성 저해
클라이언트는 데이터 전송에 필요한 최소한의 정보만 필요로 하는데, 엔티티를 그대로 사용하면 불필요한 데이터까지 전송되어 API 응답이 복잡해집니다. - 결과
엔티티와 클라이언트 사이의 강한 결합은 유지보수와 확장성을 떨어뜨리며, 데이터베이스 변경 시 클라이언트에도 영향을 미칩니다.
3) 성능 및 네트워크 효율 문제
- 불필요한 데이터 전송
엔티티는 모든 정보를 담고 있기 때문에, 클라이언트에 필요한 데이터보다 훨씬 많은 양의 데이터를 전송할 수 있습니다.
이는 네트워크 대역폭 낭비와 직렬화/역직렬화 과정의 부담을 증가시킵니다. - DTO로 해결
DTO를 사용하면 필요한 데이터만 포함하여 전송할 수 있으므로, 응답 데이터의 크기를 줄이고 성능을 최적화할 수 있습니다.
4) 유지보수 및 확장성
- 역할의 분리 부족
엔티티는 데이터베이스와의 매핑, 비즈니스 로직, 데이터 전송 등 여러 역할을 동시에 수행하는 경우가 많습니다.
이는 코드의 복잡도를 높이고, 변경 시 여러 부분에 영향을 미칠 수 있습니다. - DTO 사용의 이점
DTO는 오직 데이터 전송에만 집중하기 때문에, 엔티티는 순수하게 데이터베이스와의 연동과 비즈니스 로직에 집중할 수 있습니다.
결과적으로 코드의 유지보수성과 확장성이 크게 향상됩니다.
DTO 사용의 실제 예시와 적용 방법
엔티티 예시
예를 들어, 사용자 정보를 관리하는 User 엔티티가 있다고 가정해 봅니다.
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String email;
private LocalDateTime createdAt;
// 내부에서만 사용하는 기타 필드...
// Getter, Setter 생략
}
엔티티에는 비밀번호, 생성일, 내부 식별자 등 여러 정보가 포함되어 있습니다.
DTO 예시
클라이언트에게는 사용자 ID, 사용자 이름, 이메일만 전달하고 싶다고 가정해 봅니다.\
public class UserDTO {
private Long id;
private String username;
private String email;
// 생성자, Getter, Setter
public UserDTO() {}
public UserDTO(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
// Getter, Setter 생략
}
DTO에는 클라이언트에게 필요한 데이터만 포함되어 있으므로, 비밀번호와 같은 민감 정보는 자연스럽게 배제됩니다.
엔티티를 DTO로 변환하는 방법
Service 계층이나 별도의 매퍼 클래스를 통해 엔티티를 DTO로 변환할 수 있습니다.
public class UserMapper {
public static UserDTO toDTO(User user) {
return new UserDTO(
user.getId(),
user.getUsername(),
user.getEmail()
);
}
}
이렇게 변환된 DTO를 컨트롤러에서 클라이언트에게 응답으로 전달하게 되면, 민감 정보는 물론 불필요한 데이터도 전송되지 않아 보안과 성능 측면에서 유리합니다.
DTO 사용의 장점
1) 보안 강화
- 민감 정보 차단
DTO를 사용하면 엔티티에 포함된 비밀번호, 내부 식별자 등 외부에 노출되면 안 되는 정보를 선택적으로 제외할 수 있습니다. - 데이터 노출 최소화
클라이언트에게 꼭 필요한 정보만 전달하여, 불필요한 데이터 유출을 방지합니다.
2) 결합도 감소 및 유연성 확보
- 계층 간 의존성 완화
엔티티와 API 응답 형식을 분리함으로써, 데이터베이스 구조 변경이 클라이언트에 미치는 영향을 최소화합니다. - 유연한 API 설계
DTO를 사용하면 요구사항 변화에 따라 응답 데이터를 손쉽게 확장하거나 수정할 수 있습니다.
예를 들어, 새롭게 추가된 필드를 DTO에만 반영하고 엔티티는 그대로 유지할 수 있습니다.
3) 성능 최적화
- 전송 데이터 경량화: 필요한 데이터만 포함하는 DTO는 응답 데이터의 크기를 줄여 네트워크 대역폭을 효율적으로 사용할 수 있게 합니다.
- 직렬화 효율: JSON이나 XML과 같은 형식으로 직렬화할 때 불필요한 정보가 제거되어, 처리 속도가 향상됩니다.
4) 유지보수 및 확장성
- 관심사의 분리: 엔티티는 데이터베이스와의 매핑, 비즈니스 로직에 집중하고, DTO는 데이터 전송 역할에 집중하게 되어 각 계층의 역할이 명확해집니다.
- 코드 관리 용이: 데이터 포맷이 변경되더라도, DTO만 수정하면 되므로 전체 시스템에 미치는 영향이 적어집니다.
정리
엔티티와 DTO를 분리하여 사용하는 것은 실제 운영 환경에서 발생할 수 있는 여러 문제(보안, 성능, 유지보수 등)를 예방할 수 있습니다.
- 보안: 엔티티에 포함된 민감 정보를 클라이언트에 노출하지 않도록 DTO를 통해 안전하게 데이터를 전달합니다.
- 유연성: 데이터베이스 스키마와 API 응답 구조가 분리되어, 각 계층이 독립적으로 진화할 수 있습니다.
- 성능: 필요한 데이터만 전송함으로써 네트워크 부하를 줄이고, 직렬화/역직렬화 과정에서 효율을 높입니다.
- 유지보수: 관심사의 분리로 코드의 가독성과 유지보수성이 크게 향상되며, 요구사항 변화에도 유연하게 대응할 수 있습니다.
실무에서는 이러한 이유로 엔티티를 그대로 사용하기보다, 필요한 데이터를 선별하여 DTO로 변환한 후 전달하는 방식을 널리 채택하고 있습니다.
이를 통해 보안 위험을 줄이고, API 설계를 보다 명확하고 유연하게 만들 수 있습니다.
반응형
'SPRING' 카테고리의 다른 글
[Q] 스프링 컨트롤러에서 뷰 파일 반환 vs Redirect: 무엇이 다를까? (0) | 2025.03.12 |
---|---|
[Q] 스프링 페이징 기법은 무엇일까? (2) | 2025.03.07 |
[Q] Model과 ModelAndView의 차이 (3) | 2025.02.27 |
[Q] 의존성 주입은 무엇이고 스프링에서는 어떻게 관리할까? (0) | 2025.02.26 |
[SPRING]#85 도서 쇼핑몰 구현 (DB 연동10) (0) | 2024.03.04 |