DTO 만들 때 Lombok 꿀팁 대방출!
'훌륭한 프로그래머 되는 법' 포스팅을 끝내려고 하다보니... 정작 개발 이야기는 한번도 하지 않은 것 같아서, 오늘은 그걸 풀어보려 합니다.
DTO, Data Transfer Object. 한글로 직역하면 데이터 전송 객체입니다. 주로 계층간에 데이터를 전달할 때 사용합니다. 스타일마다 다르지만 저는 주로 dao, service까지는 entity 객체를 그대로 사용하고 controller에서 응답을 줄 때 dto로 바꿔서 응답을 줍니다. 엔티티의 필드 변화로 API의 응답값의 변화를 주는 것은 많은 사이드 이펙트를 발생시키기 때문입니다.
돌아와서 자바에서 DTO를 만들 때 주로 사용하는 코드를 보도록 하겠습니다.
@Data
public class UserDto {
private String email;
private String name;
private Integer age;
private LocalDateTime registeredAt;
}
주로 lombok의 Data 어노테이션을 사용하여 만듭니다. 간단하기 때문이죠. 하지만 이에 치명적인 문제가 있습니다.
Data 어노테이션은 총 5개, Getter, Setter, RequiredArgsConstructor, ToString, EqualsAndHashCode 어노테이션이 합쳐져 있습니다. 여기에서 바로 Setter가 문제가 됩니다. 데이터 변조가 가능하다는 것이죠. 이는 생각보다 치명적인 문제입니다. '그냥 set 메서드 안쓰면 되는 거 아냐?' 라고 생각할 수 있지만, 코드는 혼자 작성하는 것이 아니며, 다른 개발자가 사용할 수 있습니다. 따라서 세터 사용을 막아야 합니다. 그래서 우리는 @Data
어노테이션을 이렇게 바꿀수 있습니다.
@Getter
@RequriredArgsConstructor // final 필드가 없어 추가할 필요 없음
@ToString
@EqualsAndHashCode
public class UserDto {
...
}
이로써 문제 해결!
아닙니다... X나 구려요... 다른 방법을 찾아야 합니다. 나는 Data에서 Setter만 빼고 싶다고! 저렇게 어노테이션 덕지덕지 붙여놓느니 차라리 Intellij 자동완성으로 Setter빼고 다 만들겠어... 진짜 방법이 없을까...
방법이 있습니다! 바로 바로... AccessLevel을 이용하는 것입니다. Setter 어노테이션을 한번 까봅시다.
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
/**
* If you want your setter to be non-public, you can specify an alternate access level here.
*
* @return The setter method will be generated with this access modifier.
*/
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
...
어노테이션 기본 파라미터로 AccessLevel을 설정하고 기본으로 퍼블릭으로 되어있네요. 이걸 바꾸면 됩니다. 아예 안만드는 옵션도 있네요. 이걸 써보도록 합시다.
@Data
@Setter(AccessLevel.NONE)
public class UserDto {
...
}
이렇게 하면 Setter를 사용할 수 없게 됩니다. 이제 진짜 끝~이 아니아니죠... 만약에 나이를 업데이트 하면 DB에서 다시 읽어와야 하는데, 성능상의 문제가 있어 다시 읽어오지 않고 로직으로 DTO의 값을 변경만 해야한다. 이럴 땐 정말 Setter가 필요하게 됩니다. 다 안쓴다고 해버렸는데 어쩌나...
@Data
@Setter(AccessLevel.NONE)
public class UserDto {
....
@Setter
private Integer age;
}
간단합니다. 그냥 해당 어트리뷰트 위에 선언해주면 됩니다. 앞서 살펴보신 Setter 어노테이션 구현체에 보시면 @Target이라는 옵션이 있는데 필드와 타입이 있습니다. 초 간단히 설명드리면 필드가 각 속성이고 타입이 클래스입니다. age속성에 @Setter를 선언해줌으로써 setAge 메서드를 사용할 수 있게 됩니다.
아주 훌륭한 개비스콘이네요! 그리고 이 포스팅을 작성하다가 갑자기 생각난 방법도 공유합니다.
@Data
public class UserDto {
private final String email;
private final String name;
private Integer age;
private final LocalDateTime registeredAt;
}
로직적으로 변경될 일이 없는 속성들을 final로 선언해두면 됩니다. 이러면 setter가 생성되지도 않죠! 다만 제가 기억하기에 빈 생성자가 없으면 Jackson 라이브러리가 json으로 바꾸지 못하는 이슈가 있었던 거 같아요. 그렇다고 @NoArgsConstructor 어노테이션을 만들면 final 속성들에 기본값이 없어서 에러가 나고, 빈 생성자를 만들고 final 속성들에 기본값을 주면 추후에 값을 바꾸지 못하는 문제가 있습니다. 나중에 파볼일이 있으면 더 파보겠습니다. 그러면 여기서 끝~
Author And Source
이 문제에 관하여(DTO 만들 때 Lombok 꿀팁 대방출!), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@bwjhj1030/DTO-만들-때-Lombok-꿀팁-대방출저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)