[Section3] Spring MVC - API 계층 2

2023. 6. 12. 22:38

🧑🏻‍💻 TIL(Today I Learned)


✔️ DTO(Data Transfer Object)

 

💡 DTO(Data Transfer Object)란?

➡️ 마틴 파울러가 'Patterns of Enterprise Application Architecture'라는 책에서 처음 소개한 엔터프라이즈 애플리케이션 아키텍처 패턴 중 하나 

➡️ 데이터를 전송하기 위한 용도의 객체라고 생각하면 편함

➡️ 클라이언트에서 서버 쪽으로 전송하는 요청 데이터, 서버에서 클라이언트 쪽으로 전송하는 응답 데이터의 형식으로 클라이언트와 서버 간의 데이터 전송이 이루어지고 이 사이에서 DTO를 사용할 수 있음 

 

 

🔎 DTO 가 필요한 이유?

1. 코드의 간결성

➡️ 이전 수업 시간에서 작성했던 Memeber Controller 코드를 보면 DTO 가 필요한 이유에 대해서 알 수 있음 

@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name,
                                     @RequestParam("phone") String phone) {
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
        map.put("phone", phone);

        return new ResponseEntity<Map>(map, HttpStatus.CREATED);
    }
}
  • 이 코드에서는 회원 정보를 저장하기 위해서 @RequestParam 애너테이션을 사용하고 있음 코드에서는 요청 데이터가 세 개밖에 없지만 실제로 회원 정보에 더 많은 정보들이 포함될 수 있고 그러면 @RequestParam은 계속해서 늘어날 수 밖에 없음

➡️ 하지만 클라이언트 요청 데이터를 하나의 객체로 모두 전달받을 수 있다면? 코드는 매우 간결해짐 

➡️ 즉 DTO는 요청 데이터를 하나의 객체로 전달받는 역할을 한다!

➡️ 만약 DTO 클래스를 적용한다면 아래와 같이 나타낼 수 있음

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }
}

 

2. 데이터 유효성(Validation) 검증의 단순화

➡️ 만약 클라이언트 쪽에서 이메일 주소를 "000@gmail.com"과 같은 바른 형식이 아니라 "000@", "000gmail.com" 이렇게 바르지 않은 형식으로 전송을 할 경우 데이터 유효성 검증이 없다면 올바르지 않은 형식도 핸들러 메서드에서 전달받게 됨 

➡️ 즉, 유효성 검증이란 서버 쪽에서 유효한 데이터를 전달받기 위해 데이터를 검증하는 것 

if (!email.matches("^[a-zA-Z0-9_!#$%&'\\*+/=?{|}~^.-]+@[a-zA-Z0-9.-]+$")) {
            throw new InvalidParameterException();
}
  • 유효성 검증을 위해 핸들러 메서드 내에 위와 같은 정규 표현식을 직접 포함하여 사용할 수 있지만 그렇게 된다면 name이나, phone 과 같은 요청 데이터 또한 검증하기 위한 코드들을 다 써줘야 함 
    → HTTP 요청을 전달받는 핸들러 메서드는 요청을 전달받는 것이 주목적이기 때문에 최대한 간결하게 작성되어야 함 

➡️ 핸들러 메서드 내부에 코드를 작성하지 말고 외부로 빼는 것이 간결함 유지에 더 좋다!

➡️ DTO 클래스로 유효성 검증 로직을 빼내어 핸들러 메서드의 간결함을 유지 할 수 있음

public class MemberDto {
    @Email
    private String email;
    private String name;
    private String phone;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}
  • @Email 애너테이션을 추가하면 클라이언트 요청 데이터에 유효한 이메일 주소가 포함되어 있지 않을 경우 유효성 검증에 실패하기 때문에 클라이언트의 요청은 거부된다!
@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@Valid MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }
}
  • @Valid 애너테이션 : MemberDTO 객체에 유효성 검증을 적용하게 해주는 애너테이션

💡 실습

✔️ @RequeatBody 애너테이션 

➡️ JSON 형식의 Request Body를 MemberPostDto 클래스의 객체로 변환 시켜주는 역할 

➡️ 즉 클라이언트 쪽에서 전송하는 Request Body가 JSON 형식이어야 한다는 것 

 

✔️ @ResponseBody 애너테이션

➡️ JSON 형식의 Response Body를 클라이언트에게 전달하기 위해 DTO 클래스의 객체를 Response Body로 변환하는 역할 

 

Spring MVC에서는 핸들러 메서드에 @ResponseBody 애너테이션이 붙거나 핸들러 메서드의 리턴 값이 ResponseEntity일 경우
내부적으로  HttpMessageConverter가 동작하게 되어 응답 객체(여기서는 DTO 클래스의 객체)를 JSON 형식으로 바꿔줌 

 

✍🏻 JSON 직렬화(Serializaion)와 역직렬화(Deserialization)

➡️ 역직렬화(Serialization)
     : 클라이언트 쪽에서 JSON 형식의 데이터를 서버 쪽으로 전송하면 서버 쪽의 웹 애플리케이션은 전달받은 JSON 형식의 데이터를 DTO 같은 Java 객체로 변환하는 것 

➡️ 직렬화(Deserialization)

     : 서버 쪽에서 클라이언트에게 응답 데이터를 전송하기 위해서 DTO와 같은 Java 객체를 JSON 형식으로 변환하는 것

 

🔎 DTO 클래스에 유효성 검증 적용하기 

  • 유효성 검증을 위한 의존 라이브러리 추가 

MemberPostDto

  • @NotBlank
    • 이메일 정보가 비어있지 않은지 검증
    • null 값이나 공백, 스페이스 같은 값들을 허용하지 않음
    • 유효성 검증 실패하면 내장된 디폴트 에러 메세지 콘솔에 출력
  • @Email
    • 유효한 이메일 주소인지 검증
    • 유효성 검증에 실패하면 내장된 디폴트 에러 메세지가 콘솔에 출력
  • @Pattern
    • 휴대폰 정보가 정규표현식에 매치되는 유효한 번호인지 검증
    • 유효성 검증에 실패하면 내장된 디폴트 메세지가 콘솔에 출력

  • 유효성 검증 로직이 실행되게 하기 위해서는 @Valid 애너테이션을 추가해야함

 

MemberPatchDto

  • 정규표현식 ->  다양한 조건을 선택적으로 검증하고자 할 때 유용한 방법 중 하나
name 멤버 변수에 사용한 “^\\S+(\\s?\\S+)*$” 정규 표현식
: ‘^’은 문자열의 시작을 의미합니다.‘$’는 문자열의 끝을 의미
  ‘*’는 ‘*’ 앞에 평가할 대상이 0개 또는 1개 이상인지를 평가
  ‘\s’는 공백 문자열을 의미
  ‘\S’ 공백 문자열이 아닌 나머지 문자열을 의미
  ‘?’는 ‘?’ 앞에 평가할 대상이 0개 또는 1개인지를 의미
  ‘+’는 ‘+’ 앞에 평가할 대상이 1개인지를 의미

 

  • 여기서도 유효성 검증 로직이 실행되게 만들기 위해서는 @Valid  애너테이션 추가
  • @Min(2) → 쿼리 파라미터 및 @Pathvariable에 대한 유효성 검증
    • 2  이상의 숫자일 경우에만 유효성 검증에 통과하도록 애너테이션 추가 
    • 또한 이것에 대한 유효성 검증 정상적으로 수행하기 위해서는 클래스 레벨에 @Validated 애너테이션 반드시 추가해야함!

➡️ 그리고 포스트맨 URI Path에 올바르지 않은 값을 입력하면 아래와 같이 나옴

 

➡️ 콘솔창도 확인해보면 아래와 같이 나옴

 

🔎 Jakarta Bean Validation?

➡️ 오늘 유효성 검증을 위해 사용한 애너테이션은 Jakarta Bean Validation이라는 유효성 검증을 위한 표준 스펙에서 지원하는 내장 애너테이션

➡️ 일종의 기능 명세라고 할 수 있음

 

🔎 Custom Validator

➡️ 유효성을 검증을 적용하다보면 목적에 맞는 애너테이션이 존재하지 않을 수도 있음 이런 경우 목적에 맞는 애너테이션을 직접 만들어서 유효성 검증에 적용할 수 있음 

 


 

    •  

BELATED ARTICLES

more