이 문서는 Brian Vermeer의 How to use Java DTOs to stay secure를 번역한 문서입니다.
자바에서 데이터 전송 객체(DTOs)는 하위 시스템들 사이에서 데이터를 운반하는 객체로, 데이터를 종합적으로 관리하기위한 엔터프라이즈 디자인 패턴이다. DTO를 사용하는 주요한 이유 중 하나는 하위 시스템 간의 호출 숫자를 줄여 서비스의 시스템 비용(overhead)을 감소시키기 위해서다.
이 글은 DTO가 어떻게 자바 어플리케이션에서 사용되고 DTO의 사용이 어떻게 데이터 유출을 방지하는지를 설명한다.
__
영문 이름에서 알수있듯, POJO(Plain Old Java Object = 그냥 평범한 자바 객체)는 우리와 친숙한 아주 일반적인 자바 객체이다. 아무 클래스나 POJO가 될 수 있으며 자바 언어가 제한하는 사항들 빼고는 특별한 제약이나 조건도 존재하지 않는다. POJO는 코드의 재사용과 가독성을 위해 만들어졌다.
public class CoffeePOJO {
public String name;
private List<String> ingredients;
public CoffeePOJO(String name, List<String> ingredients) {
this.name = name;
this.ingredients = ingredients;
}
void addIngredient(String ingredient) {
ingredients.add(ingredient);
}
}
__
공식 자바 빈 문서에 따르면 자바 빈은 아래의 조건을 모두 충족하는 POJO이다.
public class CoffeeBEAN implements Serializable {
private String name;
private List<String> ingredients;
public CoffeeBEAN() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getIngredients() {
return ingredients;
}
public void setIngredients(List<String> ingredients) {
this.ingredients = ingredients;
}
}
__
값 객체는 간단한 데이터 개체(entity)를 나타내는 작은 객체(object)이다. DTO나 POJO와는 다르게 생소하게 들릴수도 있는데, 이는 현재(2022-11-22) 자바에서 값 객체를 제공하지 않기 때문이다(JDK 관리자들이 JEP 401에 추가하려는 움직임이 있긴하다). 결국 값 객체가 자바 언어에 존재하지 않기 때문에 POJO를 통하여 이 기능을 대신하여야한다.
__
DTO는 자바 빈과 같은 POJO의 형식으로 구현될 수 있다. 여기서 가장 중요한 것은 DTO가 프레젠테이션 층(presentation layer)과 도메인 모델과 같은 개체(entity)들을 분리시킨다는 것이다.
소규모 REST 서비스를 가지고 이해해보자. 커피와 손님이라는 객체를 가진 카페가 있다고 가정했을 때 이객체들(커피, 손님)은 시스템 속 서로 다른 도메인의 개체들이다. 만약에 우리가 한 손님의 최애 커피를 알고싶다고 할 때, 우리는 FavoriteCoffeeDTO에 나타나있는 종합적인 정보(손님 - 커피)를 제공하는 API를 만들 것이다.
위 구조를 표현한 코드는 아래와 같다.
public class Coffee {
private Long id;
private String name;
private List<String> ingredients;
private String preparation;
}
public class Customer {
private Long id;
private String firstName;
private String lastName;
private List<Coffee> coffees;
}
public class FavoriteCoffeeDTO {
private String name;
private List<String> coffees;
}
위와 같이 도메인 층과 프레젠테이션 층은 분리되어 있어서 컨트롤러가 두 도메인 개체를 DTO에 맵핑 할 수 있다.
DTO는 자바 빈 외의 객체로도 구현 가능하다.
이 예시에서 변수들은 모두 Private임으로 변수 접근을 위해 getter와 setter를 생성하여야한다. 사용자들은 대다수의 경우 자바 빈 표준을 따라 DTO를 만드는데, 꼭 이 기준을 따라야 할 필요는 없다. 다른 선택지로는 아래와 같이 모든 변수를 Public으로 만든 다음 직접 접근하거나, 객체를 불변(immutable)하게 만들고 모든 변수를 입력값으로 받는 생성자와 몇개의 getter를 이용하는 것도 가능하다.
public class FavoriteCoffeeDTO {
public String name;
public List<String> coffees;
}
String name = favCoffeeDTO.name;
마지막으로, 만약 자바를 비교적 최신 버전으로 업데이트 하였다면 자바 레코드(Java records)로 DTO를 만들 수 있다. 자바 레코드는 간단히 말하자면 자동적으로 모든 변수를 입력 값으로 받는 생성자와 접근 함수, toString() 함수와 hashCode()까지 제공하는 불변형 클래스이다. 이러한 특징 때문에 코드는 좀 더 짧아지고 가독성이 높아지는 효과가 있다. 자바 레코드는 자바 빈의 형태을 따르지 않는 것을 볼 수 있다.
public record FavoriteCoffeeDTO(String name, List<String> coffees) {}
String name = favoriteCoffeeDTO.name();