Cloud Native Spring in Action

데이터 유효성 검사

기록해연 2025. 2. 6. 15:37

109p ~ 111p

 

유효성 검사를 해야할 조건은 아래와 같다.

 

ISBN은 올바른 형식으로 정의되어야 한다(ISBN-10 or 13)

제목, 저자, 가격은 반드시 있어야하고 가격은 0보다 큰 값이어야 한다.

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.1'
	id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.polarbookshop'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
	implementation 'org.springframework.boot:spring-boot-starter-validation' // 유효성 검사 의존성 추가
}

tasks.named('test') {
	useJUnitPlatform()
}

유효성 검사를 위한 디펜던시 추가

 

gradlew build --refresh-dependencies 로 빌드 한 번 해줘야 적용된다.

 

빌드는 성공했는데 javax.validation.constraints.* 를 인식못하는 것... 챗지선생에게 물어보니

스프링부트3.n 이후 버전에선 javax.validation 대신 jakarta.validation 을 써야한다고 함. 수정해주니 잘 인식된다.

 

수정 완료

package com.polarbookshop.catalogservice.domain;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;

// 도메인 객체(Entity) 정의
// 도메인 모델은 불가변 객체인 레코드로 구현
public record Book(
        @NotBlank(message = "The book ISBN must be defined")
        @Pattern(
                regexp = "^[0-9]{10}|[0-9]{13}$",
                message = "The ISBN format must be valid"
        )
        String isbn,    // 책 고유번호(식별)
        @NotBlank(message = "The book title must be defined")
        String title,
        @NotBlank(message = "The book author must be defined")
        String author,
        @NotBlank(message = "The book price must be defined")
        // @Positive: 널 값이어서는 안되고 0보다 큰 값이어야함.
        @Positive(message = "The book price must be greater than zero")
        Double price
) { }

 

스프링에게 BookController 클래스의 Book 객체에 대한 유효성을 검사하도록 지시.

=> @RequestBody가 메서드 인수로 지정될 때 @Valid 애너테이션 사용하여 지시.

package com.polarbookshop.catalogservice;

import com.polarbookshop.catalogservice.domain.Book;
import com.polarbookshop.catalogservice.domain.BookService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("books")
public class BookController {
    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping
    public Iterable<Book> get() {
        return bookService.viewBookList();
    }

    // 루트 패스 URI에 추가되는 URI 템플릿 변수("/books/{isbn}")
    @GetMapping("{isbn}")
    public Book getByIsbn(@PathVariable String isbn) {
        return bookService.viewBookDetails(isbn);
    }

    @PostMapping
    // 책이 성공적으로 생성되면 201 상태코드를 반환
    @ResponseStatus(HttpStatus.CREATED) // 성공시 201 상태코드 반환
    //@RequestBody는 웹 요청의 본문을 메서드 변수로 바인드
    public Book post(@Valid @RequestBody Book book) {
        return bookService.addBookToCatalog(book);
    }

    @DeleteMapping("{isbn}")
    @ResponseStatus(HttpStatus.NO_CONTENT) // 성공시 204 상태코드 반환
    public void delete(@PathVariable String isbn, @RequestBody Book book) {
        bookService.removeBookFromCatalog(isbn);
    }

    @PutMapping("{isbn}")
    //@RequestBody : 요청 본문(JSON 형식의 데이터를 Book 객체로 변환)하여 컨트롤러 메서드에 전달
    public Book put(@PathVariable String isbn, @Valid @RequestBody Book book) {
        return bookService.editBookDetails(isbn, book);
    }

}