왜 dto에서 항상 @Getter와 @NoArgsConstructor를 붙여줘야 할까?
Web application 개발을 계속 하면서 request-POJO-response로 데이터가 흘러가는 것을 파악했습니다.
이를 위해서 dto에 항상 의무적으로 @Getter와 @NoArgsConstructor를 붙여가며 기능을 구현했습니다.
이렇게 json 데이터를 dto 객체로 변환하여 작업한 후에 처리된 데이터를 다시 json 형식으로 변환하는 일은 누가 하는지에 대해 궁금했습니다.
이런 일을 주로 처리하는 라이브러리는 SpringBoot starter에 포함되어 있는 jackson 라이브러리가 합니다.
Jackson라이브러리에 대해 좀 더 자세히 알아보고, 왜 @Getter와 @NoArgsConstructor를 붙여줘야 하는지 파악하려고 합니다.
jackson은 json 데이터 구조를 손쉽게 처리해주는 라이브러리입니다.
dto 객체를 json으로 변환하는 것을 직렬화라고 하고, json 데이터를 dto로 변환하는 것을 역직렬화라고 합니다.
@Getter
@NoArgsConstructor
public class ReviewUpdateDto {
private Long id;
private String review;
}
저희 프로젝트에서 dto객체의 property는 모두 private으로 선언했습니다.
이렇게 public이 아닌, private 또는 protected로 선언하는 경우에는 getter를 같이 설정함으로써 json 데이터와 매핑할 수 있습니다.
getter를 설정해줌으로써, {"id":"1", "review":"랄랄"} 이와 같이 getId(), getReview()메서드를 통해서 각각의 필드와 매핑이 되는 것입니다.
@Getter 어노테이션을 붙여줌으로써, json 데이터에 내보내는 필드명과 객체에 담긴 필드명을 손쉽게 일치시킬 수 있습니다.
만약 이 두 필드명이 불일치하다면, JsonProcessingException이 발생할 수도 있습니다.
또한 Getter 메서드를 기반으로 프로퍼티명을 확인하기 때문에,
postman을 사용하여 테스트할 경우에는 필드명이 일치하는지 꼭꼭 확인할 필요도 있습니다!(자주 발생했던 실수입니다)
만약 @Getter 어노테이션이 아닌 메서드를 직접 정의하고, 2번처럼 return 값을 고정시켜놨다고 가정해보겠습니다.
//1. 일반적인 getter 메서드
public int getAge(Entity entity) {
return this.age;
}
//2. 값을 고정한 getter 메서드
public int getAge(Entity entity) {
return 23;
}
이런 경우 json 데이터 변환작업이 이뤄진다면 json 데이터에도 {"age" : 23} 이렇게 담겨진다고 합니다.
이런 상황을 통해서 getter의 역할을 알 수 있습니다.
1. getter는 매핑관계를 설정해줄 수 있습니다.
2. 직렬화를 통해 json 데이터를 내보낼 때, getter의 return값을 데이터로 내보냅니다.
그렇기 때문에 json 변환에 있어서 getter는 빠져서는 안되는 필수적인 어노테이션이라는 것을 알 수 있습니다.
결론부터 말하자면, 저희 프로젝트의 변환작업에서 기본생성자는 있어도 되고, 없어도 되는 어노테이션이라고 생각했습니다.
ObjectMapper가 데이터 바인딩을 하기 위해서는,
1. 기본 생성자를 통해서 새 객체를 생성합니다.
2. getter, setter 혹은 public field 등을 통해서 프로퍼티명을 찾습니다.
3. java.lang.reflection 패키지를 통해 값을 주입합니다.
이런 과정을 거쳐야 합니다.
그렇기 때문에 @NoArgsConstructor를 통해서 기본 생성자를 만들어줬습니다.
그러나 기본 생성자가 없어도 이런 변환작업은 가능하다고 합니다.
ObjectMapper 내부에는 property와 생성자가 위임된 경우 그 정보를 이용해서 직렬화/역직렬화에 사용하는 로직이 있습니다.
위임 어노테이션에는 @JsonProperty, @JsonAutoDetect, @JsonCreator가 있습니다.
여기서 위임이라는 것은 어노테이션을 사용해 객체를 변환(직렬화/역직렬화)할 때 쓰일 정보를 직접 선언하는 것인데,
이 위임을 자동으로 해주는 jackson-datatype-jdk8이라는 라이브러리가 있습니다.
현재 저희의 프로젝트에도 포함되어 있습니다. 위의 3가지 모듈을 모두 지원해주고 있습니다.
여기에서 @JsonProperty 어노테이션을 통해서 jackson-module-parameter-names 모듈을 지원받을 수 있습니다.
이 모듈은 기본 생성자가 없어도 다른 생성자로 대체하여 역직렬화를 수행할 수 있다고 합니다.
SpringBoot에서는 jackson binding을 할 때 ObjectMapper에 이 모듈이 기본적으로 등록되어 있다고 합니다.
그래서 controller에 들어오는 request dto에 기본생성자가 없어도 역직렬화를 수행할 수 있습니다.