2022.12.16.FRI

ronglong·2022년 12월 16일
0

코드스테이츠 Day42

1. DTO (Data Transfer Object)

  • 클라이언트-서버 간 데이터 전송 시 사용하는 Java 객체
  • 핸들러 메서드에 전달되는 여러 파라미터(클라이언트의 요청 데이터)를 하나의 객체로 주고 받을 수 있음 ---> 코드 간결화, DTO 클래스에서 유효성 검사 진행 가능
  • DTO 사용 목적 : HTTP 요청 수를 줄여서 비용 줄이기
  • DTO 클래스에 파라미터로 사용되는 필드값게터, 세터 작성
  • @RequestBody(역직렬화), @ResponseBody or ResponseEntity 리턴타입(직렬화)
  • HttpMessageConverter : Java 객체와 JSON 상호 변환(직렬화, 역직렬화)
  1. 유효성 검사
  • 프런트엔드 쪽의 유효성 검사는 사용자 편의를 위한 것(소스코드가 다 공개되어 있어서 조작이 가능함) ---> 따라서, 백엔드 쪽에서 유효성 검사를 해야함.
  • build.gradle 파일의 dependencies에
    implementation 'org.springframework.boot:spring-boot-starter-validation' 추가
    1) 컨트롤러의 @RequestBody 앞에 @Vaild 를 추가함으로써, DTO에서 @NotBlank, @Email, @Pattern, @Range(min = , max =) 등으로 유효성 검사 가능.
    유효하지 않으면 400 오류 발생
    2) @PathVariable의 유효성을 검사할 때는, 컨트롤러 클래스에 @Validated를 붙이고 식별자 변수 타입 앞에 @Min 등을 통해서 유효성 검사를 한다.
    유효하지 않으면 500 오류 발생
    3) Custom Annotation을 만들고, 해당 애너테이션에 @Constraint를 붙여서 실제로 유효성 검사를 하는 Custom Validator 클래스를 만든다(ConstraintValidator<Custom Annotation, 유효성 검사 대상 타입> 구현).
  1. 페어 실습 과제
  • 어려웠던 부분은 딱 두 가지였다.
    1) 정규 표현식
    2) @RequestBody 필드의 선택적 포함(null일 경우 유효성 검증 통과)
  • 정규 표현식은 아래 블로그를 참고하여 작성해서 꽤 근사치에 가깝게 만들었다.
    Quantifiers가 아주 유용하다. 한 가지 경우를 빼먹은 건 나중에 세션 때 정답 보고 참고해서 수정함.
    https://codechacha.com/ko/java-regex/
  • 필드의 선택적 포함에 대해서 Optional 사용을 생각도 못했고, 검색해도 잘 안 나와서 고생하다가(@Nullable 등 온갖 것을 다 찾아봄), 나중에 세션 때 정답 보고 참고해서 과제 완성 및 제출..⭐️
  • 아래 블로그에서 Optional을 필드로 사용 금지라고 해서 생각도 못했다.
    대부분의 글에서 Optional 사용을 지양하고 있었다.
    https://homoefficio.github.io/2019/10/03/Java-Optional-%EB%B0%94%EB%A5%B4%EA%B2%8C-%EC%93%B0%EA%B8%B0/
  • 밑에는 과제 때 페어님과 함께 만든 코드.
@RestController
@RequestMapping("/v1/coffees")
@Validated
public class CoffeeController {
    // 1. DTO 클래스 및 유효성 검증을 적용하세요.
    @PostMapping
    public ResponseEntity postCoffee(@Valid @RequestBody CoffeePostDto coffeePostDto) {
        return new ResponseEntity<>(coffeePostDto, HttpStatus.CREATED);
    }

    // 2. DTO 클래스 및 유효성 검증을 적용하세요.
    @PatchMapping("/{coffee-id}")
    public ResponseEntity patchCoffee(@PathVariable("coffee-id") @Min(1) long coffeeId,
                                      @Valid @RequestBody CoffeePatchDto coffeePatchDto) {
        coffeePatchDto.setCoffeeId(coffeeId);
        return new ResponseEntity<>(coffeePatchDto, HttpStatus.OK);
    }
}
public class CoffeePatchDto {
    private long CoffeeId;

    private Optional<@NotBlank String> korName = Optional.empty();

    private Optional<@Pattern(regexp = "[a-zA-Z]+(\\s?[a-zA-Z])*",
            message = "영문만 가능하며, 워드 사이에 한칸의 공백(스페이스)만 포함 될 수 있습니다.") String> engName = Optional.empty();

    private Optional<@Range(min = 100, max = 50000) Integer> price = Optional.empty();
    
    ... 이하 생략
  • 정규표현식이 어려워서 아예 커스텀 애너테이션 만들까도 생각하면서 시도했다가 실패한 코드
package com.codestates.coffee;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {EngNameValidator.class})
public @interface EngNameAnnotation {
    String message = "영문 작성해야하며, 공백은 사이에 하나만 가능합니다.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
package com.codestates.coffee;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EngNameValidator implements ConstraintValidator<EngNameAnnotation, String> {
    @Override
    public void initialize(EngNameAnnotation constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (value.replaceAll("[^\\s]","")).length() <2 && ... ;
    }
}
  1. 기타 공부한 것
  • 전날은 그냥 모든 클라이언트 데이터를 @RequestParam으로 받았었는데,
    (우연히 전날 과제에서는 Get method가 없었지만), HTTP 통신에서 Get method는 body를 가지지 않으므로 엔드포인트에 식별자를 써야하고,
    따라서, @RequestParam 말고 @PathVarialbe을 써야한다.
    @PathVariable은 URI 엔드포인트와 동일하게 설정 필요. ("/{coffee-id}")
    https://hangjastar.tistory.com/226
  • @NotNull, @NotEmpty, @NotBlank의 차이점
    https://sanghye.tistory.com/36
  • 유효성 검사 애너테이션
    https://dev-code-notepad.tistory.com/136
  • 현재 배우는 방식은 백엔드에서 HTML 파일을 포함하지 않은, CSR 방식을 배우고 있으며, 손쉬운 JSON 변환을 위해 @Controller가 아닌, @RestController를 쓴다.
  • application properties 파일에서 속성을 설정할 수 있는데,
    여기서 server.port = 7070 이런 식으로 포트 번호를 바꿔서 IntelliJ를 2개 동시 실행할 수 있다.
  1. 오전 데일리 코딩
  • 오늘 데일리 코딩 실패.. 월요일에 재도전!

<느낀 점>
양심고백(?) 하자면, 지금까지 주말에 공부한 적 한 번도 없다.
그런데 이번 주말은 진짜 해야겠다.
처음으로 평일 저녁에 공부해도 시간이 모자를정도로 양이 너무 많아서 심화 파트를 거의 못 봄. 주말에 심화 파트 공부하자★★★

섹션 3 튜터님 설명 너무 잘해주시고, 컨텐츠도, 세션도 빵빵하다.
실습이 늘어나니까 더 재밌는 것 같다.

1개의 댓글

comment-user-thumbnail
2022년 12월 17일

댓글을 작성 안하려고 했는데, "양심고백(?) 하자면, 지금까지 주말에 공부한 적 한 번도 없다." 이 말 때문에 댓글을 잠깐 남기게 되네요.ㅎㅎ
이제 주말에도 공부를 하시는게..ㅎㅎ 우선 양이 굉장히 많습니다. 그리고 뒤로 갈수록 점점 어려워집니다.ㅎㅎ 물론 그만큼 재밌긴하지만서도.. ^^;
안하면 정신적으로 힘들어질거에요..ㅎ 잘 하고 계신거 같지만 더 잘 하시라고 댓글을..ㅎㅎ
저도 주말에 열심히 공부하겠습니다. 화이팅 하셔요~

답글 달기