본문 바로가기
TIL

2024.05.27 66일차 SPRING Validation, AJAX, fetch API 1

by Song.dev 2024. 5. 27.

SPRING Validation 모듈

  • 설치
    - build.gradle dependecies에 추가
implementation 'org.springframework.boot:spring-boot-starter-validation'

 

  • 설치 확인
    - 외부라이브러리 / jakarta.validation, spring-boot-starter-validation

 

 

  • 사용
    - 주로 RequestDTO에서 검증
    - 컨트롤러 중 RequestDTO 파라미터로 사용하는 메서드에서 검증 결과 처리
  • @NotNull : null 만 허용 안됨
  • @NotEmpty : null 은 되는데 빈문자는 안됨
  • @NotBlank : null 도 안되고 빈문자도 안됨
public class ReplyPostDto {
    @NotBlank
    private String text; // 댓글 내용
    @NotBlank
    private String author;  // 댓글 작성자
    @NotNull 
    private long bno; // 원본 글번호
}

 

검증 관련 어노테이션은 위의 경로에서 확인 가능

 

@PostMapping
public ResponseEntity<?> posts(@Validated @RequestBody ReplyPostDto dto
    , BindingResult result // 입력값 검증 결과 데이터를 갖고 있는 객체
) {

    log.debug("검증결과: {}", result.toString());

    if(result.hasErrors()) {
        Map<String, String> errors = makeValidationMessageMap(result);
        return ResponseEntity
                .badRequest()
                .body(errors);
    }

	...
}

 

 

검증 결과 BindingResult

- 입력값 검증 결과 데이터를 갖고 있는 객체 

 

public ResponseEntity<?> posts(
     @Validated @RequestBody ReplyPostDto dto ,
     BindingResult result) { ... }

 

 

** 검증결과 예시

더보기
검증결과: org.springframework.validation.BeanPropertyBindingResult: 2 errors

Field error in object 'replyPostDto' on field 'text': rejected value [];
codes [Size.replyPostDto.text,Size.text,Size.java.lang.String,Size];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [replyPostDto.text,text]; arguments []; default message [text],300,1];
default message [크기가 1에서 300 사이여야 합니다]

Field error in object 'replyPostDto' on field 'text': rejected value [];
codes [NotBlank.replyPostDto.text,NotBlank.text,NotBlank.java.lang.String,NotBlank];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [replyPostDto.text,text]; arguments [];
default message [text]]; default message [공백일 수 없습니다]

 

검증 결과 메세지

  • key: 에러가 발생한 프로퍼티
          ex) error.getField()
  • value: 에러 원인 메세지
          ex) error.getDefaultMessage()
private Map<String, String> makeValidationMessageMap(BindingResult result) {
    Map<String, String> errors = new HashMap<>();
    // 에러정보가 모여있는 리스트
    List<FieldError> fieldErrors = result.getFieldErrors();
    for (FieldError error : fieldErrors) {
        errors.put(error.getField(), error.getDefaultMessage());
    }
    return errors;
}

 

 


 

AJAX

  • 비동기 관리 라이브러리 

 

동기코드(sync) & 비동기코드(async)

  • 동기코드 : 한 작업이 완료될 때까지 다음 작업은 대기 상태
  • 비동기코드 : 특정 작업이 완료되기를 기다리지 않고 다음 작업을 실행
                       - 콜백, 프로미스, fetch API, async/await 등의 패턴을 사용하여 비동기 코드를 관리

 

XMLHttpRequest 객체

  • method: HTTP 메서드 (예: "GET", "POST")
  • url: 요청을 보낼 URL
  • async: 비동기적으로 요청을 할 것인지를 결정 (기본값은 `true`)
// 1. 객체 생성
const xhr = new XMLHttpRequest();

// 2. 요청 초기화
xhr.open(method, url, async);

// 3. 응답 처리
xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 400) {
        console.log(xhr.responseText);
    } else {
        console.error('서버에서 에러 응답:', xhr.statusText);
    }
};

xhr.onerror = function() {
    console.error('요청 중 에러 발생');
};
// 4. 요청 보내기
xhr.send();

 

** 주의사항

  1. 보안: 같은 출처 정책(Same-Origin Policy)로 인해 다른 도메인의 리소스에 대한 요청을 제한적 수행
  2. 현대적인 대안: XMLHttpRequest는 오래된 API이므로 fetch API나 다른 라이브러리(예: Axios)를 사용하여 HTTP 요청 수행을 권장
    - fetch는 더 깔끔한 API와 프로미스 기반의 접근 방식을 제공

 

콜백 지옥 Callback Hell

  • 여러 비동기 연산을 순서대로 처리해야 할 때 발생하는 현상
  • 각 비동기 연산이 완료된 후 다음 연산을 수행하기 위해 콜백 함수 내부에 또 다른 콜백 함수를 중첩해 사용

콜백 지옥 예시

더보기
fetchFirstData(function(error1, data1) {
    if (error1) {
        console.error('Error:', error1);
    } else {
        fetchSecondData(data1, function(error2, data2) {
            if (error2) {
                console.error('Error:', error2);
            } else {
                fetchThirdData(data2, function(error3, data3) {
                    if (error3) {
                        console.error('Error:', error3);
                    } else {
                        console.log('Final Data:', data3);
                    }
                });
            }
        });
    }
});

 

 

프로미스 Promise

  • 비동기 작업의 최종 완료 또는 실패를 나타내는 객체
  • Promise를 사용하면, 중첩된 콜백 구조 대신 then 메서드를 연속적으로 체인화하여 각 단계의 결과를 순차적으로 처리 가능
  • then() 메서드를 제공하여 비동기 연산이 완료되었을 때의 처리를 지정 가능
  • catch() 메서드를 통해 오류 처리 가능  
  • promiseState 
    • pending: 아직 이행되거나 거부되지 않은 초기 상태
    • fulfilled: 연산이 성공적으로 완료 → resolve() 함수 실행
    • rejected: 연산 중 에러가 발생  → reject() 함수 실행

 

let promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
                resolve('Done!');
        }, 1000);
});
promise.then(function(value) {
        console.log(value); // Outputs: 'Done!'
});

 

* Promise 메서드: Promise.all, Promise.race, Promise.allSettled 에 대해서는 추후에 자세히 다룰 예정

 


fetch API

  • Fetch API는 네트워크 요청을 수행하기 위한 인터페이스를 제공
  • XMLHttpRequest에 비해 더 유연하고 강력하며, Promise 기반의 구조로 설계
  • Fetch API는 response 객체(Promise 타입)을 반환하며, 이를 통해 여러 메서드와 속성에 액세스
  • 가장 기본적인 형태의 Fetch 요청은 URL을 지정하여 리소스를 가져오는 것
  • fetch 함수의 두 번째 인수로 여러 옵션을 설정할 수 있는 객체를 전달 가능
// fetch 함수
fetch(url, 옵션).then()

 

 

** 옵션 설정

  • method: HTTP 메서드 (GET, POST, PUT, DELETE 등)
  • headers: 요청 헤더
  • body: 요청 본문
  • mode: 요청 모드 (no-cors, cors, same-origin 등)
  • credentials: 인증 설정 (include, same-origin, omit)

 

** response 객체

  • response.text(): 응답을 텍스트로 반환
  • response.json(): 응답을 JSON으로 파싱하여 반환
  • response.blob(): 응답을 Blob 객체로 반환 (예: 이미지 파일)
  • response.headers: 응답 헤더를 반환
  • response.status: HTTP 상태 코드를 반환 (예: 200, 404 등)

 

GET 요청

fetch('url')
   .then(response => response.json()
   .then(data => data처리)

 

  • data를 배열 형태로 받아서 HTML tag를 만들고 추가하는 DOM 을 통해 화면에 렌더링 가능

(좌) fetch(url) 결과, (우) json() 결과

 

 

 

** JSON 실습 참고 사이트

https://jsonplaceholder.typicode.com