일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- docker
- Java
- container
- Set
- STREAM
- ansible
- AOP
- mybatis
- IntelliJ
- 소켓
- LAN어댑터
- cloud
- post
- tomcat
- gradle
- jdk
- 캐시서버
- Spring
- 방화벽
- 액세스회선
- 라우터
- sonarQube
- map
- Linux
- Pipeline
- Jenkins
- Collection
- DevOps
- JPA
- 허브
- Today
- Total
거북이-https://velog.io/@violet_evgadn 이전완료
DTO 클래스 본문
DTO
이전 Section에서 말했듯 DTO는 Entity의 모든 필드값 중 사용할 필드만 뽑아서 새로운 Class를 만들면 된다.
DTO에 대한 정해진 Rule이나 무조건 활용해야할 Annotation 등은 존재하지 않는다.
하지만 개인적으로는 Lombok의 @Data를 달아주는 걸 추천한다.
@Data에는 Equals와 Hashcode가 달려 있어 DTO로써도 객체의 동일성 판단 여부가 가능해지며 toString을 구현해줘 디버그나 Junit Test에도 편리하고 @Getter와 @Setter를 자동으로 달아줘 코드량을 확 줄일 수 있다는 장점을 가진다.
두 번째로 toEntity() 메서드를 달아주는 것을 추천한다.
DTO의 목적이 데이터를 Layer간 전달해주는 것이지만, 결국 DB와 통신하기 위해서는 DTO를 Entity로 바꿔야 할 필요성이 존재한다.
따라서 DTO에 toEntity() 메서드를 생성하여 간편하게 DTO에 저장된 변수 값을 활용하여 Entity 객체를 반환하도록 만든다면 귀찮은 작업이 조금 줄어들 것이다.
굳이 둘을 나누지 않아도 되지만, 개인적으로는 responseDto와 requestDto를 나누어 활용하는 편이다.
responseDto는 JSON 형태로 데이터를 Web에 보내기 위한 DTO로써 User에게 데이터를 보내기 위한 DTO이다.
유저에게 데이터를 보여주기 위한 객체로 굳이 Setter로 데이터를 수정할 필요가 없으며 Equals로 데이터의 동일성 여부를 판단할 필요성도 없다.
따라서 Getter와 생성자만 달아주는 편이며, 생성자의 입력값은 DB에서 뽑은 데이터를 담은 Entity로 설정하여 Entity 클래스의 데이터를 바로 DTO에 달아줄 수 있도록 만든다.
requestDto는 JSON 형태로 데이터를 Web으로부터 받기 위한 DTO로써 User로부터 데이터를 받기 위한 DTO이다.
일반적으로 @NoArgsConstructor 어노테이션을 달아주는데, 이는 Web으로부터 데이터를 받을 때 기본 생성자가 무조건 필요하기 때문이다. 일반적으로 데이터를 JSON 형태로 받을 때 @RequestBody를 많이 활용하는데 Spring은 이 상황에서 먼저 기본 생성자를 통해 객체를 먼저 생성한 뒤 Getter나 Setter를 통해 생성한 객체에 User로부터 받은 데이터를 주입받는 형식으로 객체를 생성하기 때문이다.
RequestDto는 Web으로부터 데이터를 받아 DB에 처리하는 식이기 때문에 RequestDto는 Entity 형식으로 변환한 이후 DB에 적용할 필요가 존재한다. 따라서 toEntity() 메서드 사용을 추천한다.
Reflection
위에서 말했듯 Web에서 받은 데이터를 처리하기 위한 RequestDto와 DB에서 데이터를 받을 Entity 클래스는 @NoArgsConstructor, 즉 기본 생성자를 무조건 가지고 있어야 한다.
그리고 이 기본 생성자를 가지고 있어야 하는 이유는 바로 "Jav Reflection" 때문이다.
Java Reflection은 다른 언어에서 볼 수 없는 자바에서 제공하는 특수한 기능이다.
구체적인 클래스 타입을 알지 못해도 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 Java API를 Java Reflection이라고 한다.
이 Reflection은 로딩이 완료된 클래스에서 또 다른 클래스를 동적 로딩(Dynamic Loading)하여 생성자, 멤버 필드, 멤버 메서드를 활용할 수 있게 한다.
그런데 "클래스 타입을 알지 못한다"는 무슨 의미일까?
우리는 직접 클래스 타입을 명시해주는데 어떻게 클래스 타입을 모를 수 있을까?
이를 알기 위해서는 언어의 Type 결정 시기를 알아야 한다. Java와 C, C++은 Compile 시점에 Type이 결정된다. 즉, 우리가 생성한 객체들은 자신이 Object 타입이라는 것은 알지만, 정확히 어떤 클래스인지는 알지 못하는 거싱다.
즉, "Object 타입이란는 것은 알지만 정확히 어떤 클래스인지 모르는 상태"를 클래스 타입을 알지 못한다고 할 수 있다.
그렇다면 RequestDto와 Entity에는 왜 기본 생성자가 필요한 것일까?
RequestDTO는 Web에서부터 데이터를 받을 것이다. 이때 Java는 "어떤 객체 형태로 데이터가 올 것이다" 정도는 알 수 있지만, 정확히 어떤 객체인지는 Controller에 도착해야 알 수 있을 것이다.
즉, Web에서부터 JSON 형식으로 온 데이터는 "클래스 타입을 알지 못하는 상태"가 되는 것이다.
이제 이런 데이터가 도착할 때는 Java는 특정 클래스에 이 타입이 저장됨을 알리고 해당 데이터를 RequestDto에 주입해야 할 것이다. 이때 Java Reflection을 통해 모호한 클래스 타입을 명확히 해 줄 수 있는 것이다.
그런데 이 Java Reflection의 특징은 "생성자의 인자 정보들"을 가지고 올 수 없는 것이다. 예를 들어 public Member(Long id, String name) 일 경우 Java Reflection은 Member 객체임을 명시하는 역할까지만 수행하지 Long id와 String name으로 이어주는 과정까지는 수행해주지 않는 것이다.
그래서 자바는 먼저 public Member(){}, 기본 생성자를 통해 먼저 Member Instance를 하나 생성하고 이후 Setter나 해당 Instance에 존재하는 또다른 생성자를 활용해 JSON 데이터를 주입하는 형식으로 Web에서 온 데이터를 처리하는 것이다.(Entity와 DB의 관계도 동일하다)
즉, 기본 생성자를 생성하지 않을 경우 JSON으로 온 데이터를 Object 객체로 만드는 것까지는 성공하겠으나 Java Reflection에 의해 이 객체를 특정 객체(ex. Member Instance)로 만들어줘야 하는데 기본 생성자가 없으니 Instance 생성이 불가능해지며, Object 객체를 내가 원하는 객체로 변환하는 것이 불가능해질 것이다.
Hibernate 같은 구현체들은 기본 생성자가 없어도 유연하게 이를 처리하는 로직을 가지고 있기도 하지만 될 때도 있고 안 될 때도 있기 때문에 안전한 구동을 위해 기본 생성자를 꼭 넣어주도록 하자.
RequestDTO 예시
@Getter
@Setter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;
@Builder
public PostsSaveRequestDto(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
public Posts toEntity(){
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
ResponseDTO 예시
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
private String author;
public PostsResponseDto(Posts posts) {
this.id = posts.getId();
this.title = posts.getTitle();
this.content = posts.getContent();
this.author = posts.getAuthor();
}
}
'웹 개발 > Spring(이론)' 카테고리의 다른 글
WAR와 JAR (0) | 2022.10.17 |
---|---|
Spring Web 계층 (0) | 2022.09.19 |
Spring의 데이터 처리 방법 (0) | 2022.09.19 |
AOP (0) | 2022.08.04 |
IoC와 DI (0) | 2022.08.03 |