Controller를 자세히 배우기 이전에
나는 오늘 알게 된 DTO나 Mapper 빼고는
(django에서 model이나 dto,
serializer가 mapper라 생각하기 때문에..)
모든 구현 사항을 Controller에 다 구현했었다.
그래서 내 django 프로젝트를 보면
모든 기능별로 views.py가 엄청 길다..
DTO는
Data Transfer Object로
직역하자면 데이터 전송 객체이다.
@RestController
@RequestMapping("/test")
class Controller{
@PostMapping
public ResponseEntity post(@RequestParam("email") String email,
@RequestParam("name") String name
...
}
DTO 적용 이전
@RestController
@RequestMapping("/test")
class Controller{
@PostMapping
public ResponseEntity post(@RequestBody PostDto postDto)
...
}
DTO 적용 이후
DTO를 통하면
파라미터들을 RequestParam annotation없이
DTO 객체 하나에 다 전달되기 때문에
코드가 간결해지고 유지보수성이 늘어난다.
import javax.validation.constraints.*;
class PostDto {
@Email
private String email;
@NotBlank
private String name;
@Pattern("^010-\\d{4}-\\d{4}$")
private String phone;
...
// getter / setter
}
또한,
DTO에서 검증절차를 적용시켜
전달받은 데이터에 유효성 검사를 할 수 있다.
javax라이브러리에 있는 validator 외에도
직접 validator를 작성할 수 있다.
@Target(ElementType.Field)
@Retention(RetentionPolicyt.Runtime)
@Contstraint(validatedBy = {CustomValidator.class})
public @interface CustomValid {
String message() default "오류 메세지";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
boolean value() default false;
}
위와 같이 Custom annotation을 정의하고,
public class CustomValidator implements ConstraintValidator<CustomValid, 검사할 필드의 타입> {
private boolean param;
@Override
public void initialize(CustomValid constraintAnnotation){
// custom annotation을 통해 파라미터를 받음
this.param = constraintAnnotation.value();
ContstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(검사할 필드의 타입 value, ConstraintValidatorContext context){
유효성 검사...
return 유효성검사 결과;
}
}
validator class를 구현하면 된다.
Controller에서 mapper는 DTO와 Entity를
상호 변환 해주는 역할을 한다.
public class Mapper{
public Entity postDtoToEntity(PostDto postDto){
...
return entity
}
public Entity patchDtoToEntity...
...
public ResponseDto entityToResponseDto(Entity entity){
...
return responseDto;
}
}
그냥 클래식하게
하나의 클래스에서 여러 메서드로
각 DTO를 Entity로 바꾸는 방법도 있고,
import org.mapstruct.Mapper;
@Mapper(compoentModel="spring")
public interface Mapper{
Entity PostDtoToEntity(PostDto postDto);
...
ResponseDto entityToResponseDto(Entity entity);
}
mapstruct 라이브러리를 추가하여
mapper 구현 크랠스를 자동으로 생성해줄 수도 있다.
Mapper annotation
- componentModel에 "spring"을 넣어 Bean으로 등록된다
Gradle에서 build를 하면, Mapperlmpl 클래스가 생성된다.
Controller의 생성자에서
Mapper객체를 Service객체와 함께 주입받고,
Mapper의 각 기능을 HTTP 메서드에 맞게 호출하여 사용하면 된다.