티스토리 뷰

스프링 로고

 

 

 

개발 환경

 

Spring Boot 3.2.2
JAVA 17
ES6
MacOS
Postman v10.22

 

해당 블로그 코드들은 GitHub에 남겨져 있습니다.

 

 

 

 

 

작성하게된 이유

 

데이터 유효성 검사를 Front단 에만 쳐 내는 것이 아닌 Back단 에서도 유효성 검사를 해서 막아야합니다.

근데 실무에서는 관리자만 사용하는 웹 어플리케이션 ( CMS 라 칭함 )을 만들었기 때문에 사용자가 악용할 일이 없어서 배제해 왔지만 플랫폼처럼 서비스 하기 위해서는 이제는 Back단에서도 유효성 검사를 해야겠다고 생각했습니다.

 

또한, 클라이언트에서 언제든지 데이터 값을 변경하여 백엔드에게 전송할 수 있기 때문에 필수적으로 Validation을 알아야한다고 생각했습니다.

 

 

 

 

조건

 

  1. 이름, 휴대폰 번호, 이메일은 필수 값입니다.
  2. 휴대폰 번호는 10자리 or 11자리 숫자로 이루어져야 합니다.
  3. 이메일은 이메일 양식을 지켜야 합니다. (xxx@xxx)
  4. 이메일은 중복 등록 할 수 없습니다.
  5. DB로 저장하지 않고 메모리로 저장합니다.
  6. jQuery, bootstrap 을 이용한 간단한 회원가입 기능입니다.

 

 

 

 

 

코드

 

코드들이 길어서 더보기를 눌러서 확인해주세요!

build.gradle

더보기
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.2'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'aoxx'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

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

 

Member.class

원래 엔티티(Entity)로 사용해야 했지만 JPA, DB를 사용하지 않고 메모리로 저장하기 때문에 그냥 Member 객체로 사용하였습니다.

더보기
@Setter
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class Member {
    private Long id;
    private String name;
    private String phoneNumber;
    private String email;

}

 

MemberRequestDto.class

변화에 대응하기 위해 요청객체를 만들었습니다.

더보기
@Setter
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class MemberRequestDto {

    private Long id;
    @NotBlank(message = "이름은 공백일 수 없습니다.")
    private String name;

    @NotBlank
    @Pattern(regexp = "[0-9]{10,11}", message = "10~11자리의 숫자만 입력가능합니다.")
    private String phoneNumber;

    @NotBlank
    @Email(message = "이메일 양식을 지켜주세요")
    private String email;

}

 

MemberResponseDto.class

변화에 대응하기 위해 응답객체를 만들었습니다.

더보기
@Setter
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class MemberResponseDto {

    private Long id;
    private String name;
    private String phoneNumber;
    private String email;

    public MemberResponseDto(Member member) {
        id = member.getId();
        name = member.getName();
        phoneNumber = member.getPhoneNumber();
        email = member.getEmail();
    }
}

 

MemberController.class

@Validated를 사용하여 유효성 검사를 해주었고 MemberRuquestDto에 걸려있는 Validation 어노테이션이 검증 해줍니다.

더보기
@Slf4j
@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    // 저장
    @PostMapping("/member")
    public MemberResponseDto join(@Validated @RequestBody MemberRequestDto memberRequestDto){
        return memberService.save(memberRequestDto);
    }

    // 회원 목록
    @GetMapping("/members")
    public List<MemberResponseDto> findAll(){
        return memberService.findAll();
    }
}

 

MemberService.class

더보기
@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;

    // 저장
    public MemberResponseDto save(MemberRequestDto memberRequestDto){
        Member member = new Member();
        member.setName(memberRequestDto.getName());
        member.setEmail(memberRequestDto.getEmail());
        member.setPhoneNumber(memberRequestDto.getPhoneNumber());

        Member saveMember = memberRepository.save(member);

        return new MemberResponseDto(saveMember);
    }

    // 회원목록
    public List<MemberResponseDto> findAll() {
        return memberRepository.findAll().stream().map(MemberResponseDto::new).collect(Collectors.toList());
    }
}

 

MemberRespository.class

더보기
@Repository
public class MemberRepository {
    private Map<Long, Member> store = new HashMap<>();
    private Long sequence = 1L;


    @PostConstruct
    public void init(){

        Member member1 = new Member(sequence++, "김길동", "01012345678", "example1@tt.com");
        Member member2 = new Member(sequence++, "이천수", "01055665678", "example2@tt.com");
        Member member3 = new Member(sequence++, "조규성", "01012342234", "example3@tt.com");

        store.put(member1.getId(), member1);
        store.put(member2.getId(), member2);
        store.put(member3.getId(), member3);
    }

    public Member save(Member member){
        member.setId(sequence++);
        store.put(member.getId(), member);
        return member;
    }

    public List<Member> findAll(){
        return new ArrayList<>(store.values());
    }

}

 

index.html

Spring Boot의 경우 경로 / 에 매핑된 메서드가 없다면 / 접속 시 자동으로 index.html을 호출하게 됩니다.

더보기
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Validation 확인하기</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script>
</head>
<body>
    <div class="myContainer">
        <div class="col-md-6" style="width: 40.5%; float : left; margin :50px;">
            <h1> 회원가입 </h1>
            <form>
                <div class="form-group" style="margin-bottom: 15px">
                    <label for="name">이름</label>
                    <input type="text" class="form-control" id="name" placeholder="이름을 입력하세요">
                </div>
                <div class="form-group" style="margin-bottom: 15px">
                    <label for="phoneNumber"> 휴대폰번호 </label>
                    <input type="text" class="form-control" id="phoneNumber" placeholder="휴대폰 번호를 입력하세요">
                </div>
                <div class="form-group" style="margin-bottom: 15px">
                    <label for="email">이메일</label>
                    <input type="text" class="form-control" id="email" placeholder="이메일을 입력하세요">

                </div>
                <button type="button" class="btn btn-primary" id="btnSave">등록</button>
            </form>
        </div>
        <div class="col-md-6" style="width: 40.5%; float: right; margin :50px;">
            <h1> 회원목록 </h1>
            <div style="border: 1px solid blue; width: 100%; height:500px;" id="member-list">
            </div>
        </div>
    </div>

</body>

<script>

    // 함수 호출
    memberAll();

    // 회원 가입 등록
    $('#btnSave').click(function(){
        memberJoin();
    })

    // 회원 가입 등록 함수
    async function memberJoin(){
        const url = '/member';
        const member = {
            name: $('#name').val(),
            phoneNumber: $('#phoneNumber').val(),
            email: $('#email').val()
        };

        $.ajax({
            url: url,
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(member)
        })
        .done(function(data) {
            memberListHTML(data);
        })
        .fail(function(error) {
            errorShowHTML(error);
        });


    }

    // 회원 목록 조회
    function memberAll(){
        const url = '/members'
        $.ajax({
            url: url,
            type : 'GET',
            contentType: "application/json; charset=utf-8",
        })
        .done(function(data) {
            data.map(member => { memberListHTML(member); })
        })
        .fail(function(xhr, status, error) {
            console.log('Error: ' + error);
            console.log('Status: ' + status);
            console.dir(xhr);
        });

    }

    // 회원 목록 HTML 만들기
    function memberListHTML(member){
        let template = `
            <div>${member.id} | ${member.name} | ${member.phoneNumber} | ${member.email} | </div>
        `;
        $('#member-list').append(template);
    }

    // 에러 메시지 출력 함수
    function errorShowHTML(response){
        const errorFields = response.responseJSON.errors;

        errorFields.map(error => {
            let field = error.field;
            let template = `
                <span class="error-message text-danger text-small" id="error-${field}">${error.defaultMessage}</span>
            `;
            $(`#${field}`).after(template);
        });
    }
</script>
</html>

 

 

 

 

 

 

 

테스트 시작

 

스프링을 기동시켜주어서 http://localhost:8080/ 로 접속해 줍니다.

기동 시킨 메인 페이지
기동 시킨 메인 페이지

 

 

 

순서1) 포스트맨으로 확인

포스트맨을 열고 http://localhost:8080/member 경로에 POST 메서드로 3개 필드 @NotBlank이고,  null 값으로 날리면 400에러가 갈 것입니다.

Postman 실행
Postman 실행

 

아래와 같이 봐야할 것은 4가지 입니다.

  • HTTP 상태 코드
  • errors
  • 오류난 메시지 (=defaultMessage)
  • 오류난 필드 (= field)

Response 값

 

 

순서2) 웹 어플리케이션으로 확인

index.html에서 이러한 오류가 나면 errorShowHTML 이라는 함수로 처리하도록 했습니다.

그냥 아무 것도 입력하지 않았으니 다 null 값으로 데이터를 보내겠죠!

그래서 각각의 에러난 필드에 맞춰서 에러 코드를 보이게 해주었습니다.

에러 코드 노출
에러 코드 노출

짠! 잘 작동하는 것을 볼 수 있겠지요!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

한계점과 궁금증

 

MethodArgumentNotValidException 이라는 예외가 발생하여 DefaultHandlerExceptionResolver 가 자체적으로 오류 메시지와 JSON 을 응답해주고 있었습니다...

 

다시 한번 포스트맨의 JSON 응답 값을 보면 아무 것도 입력하지 않은 상태로 요청을 보냈기 때문에 rejectedValue 값이 null로 요청되었다고 나오는 것입니다.

만약 값을 입력했는데 유효성이 다르다면? 제가 값 입력한 것이 거절 되었으니 rejectedValue에 입력 값이 나옵니다.

 

JSON 예외 응답 값

 

 

우린 Front 대 Back,  기업 대 기업,  기업 대 개인  끼리 API 통신을 합니다.

그런데 되게 어지럽고 불필요한 데이터를 API Response 해줄 필요가 있을까요?

 

제가 Back 단 코드를 만지고 그냥 스프링이 내려주는 대로 API Response 해주면 편하지만 이 코드를 받는 Front 분들은 어떤 코드를 써야하고 어디가 필요한 값인지 헷갈릴거 같습니다..

한마디로 너무 불친절하다는 것이죠

 

 

 

 

 

해결 방안으로 @RestControllerAdvice 와 @ExceptionHandler 를 사용

 

@RestControllerAdvice

@ResponseBody + @ControllerAdvice 어노테이션이 합쳐진 것이며,

@RestControllerAdvice는 여러 컨트롤러에서 발생하는 예외를 한 곳에서 관리할 수 있는 어노테이션 입니다.

 

 

@ExceptionHandler

해당 컨트롤러에서 발생한 예외를 처리하는 역할을 합니다.

 

 

 

 

ErrorResult.class

예외 처리했을 때 내가 커스텀하여 내려주고 싶은 클래스 생성

더보기
@Setter
@Getter
@AllArgsConstructor
public class ErrorResult {

    private Integer code;
    private String message;
    private Map<String, Object> data;

}

 

ValidControllerAdvice.class

중요!) 유효성 검사로 인하여 예외가 났을 때 처리할 함수!

설명은 어노테이션과 함께 하겠습니다.

// RestController 어노테이션 붙은 컨트롤러에 오류 났을 때만 호출
@RestControllerAdvice(annotations = RestController.class) 
public class ValidControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)  // HTTP 상태코드 400으로 응답
    @ExceptionHandler(MethodArgumentNotValidException.class) // MethodArgumentNotValidException 예외 났을 때 처리
    public ErrorResult MethodArgumentNotValidExHandler(MethodArgumentNotValidException e) {
        Map<String, Object> data = new HashMap<>();

        List<Map<String, Object>> list = new ArrayList<>();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            Map<String, Object> field = new HashMap<>();
            field.put("field", fieldError.getField());
            field.put("msg", fieldError.getDefaultMessage());
            field.put("rejectedValue", fieldError.getRejectedValue());

            list.add(field);
        }
        data.put("connStatus", "fail");
        data.put("errorList", list);
        
        return new ErrorResult(HttpStatus.BAD_REQUEST.value(), "실패", data);
    }
}

 

 

@RestControllerAdvice와 @ExceptionHandler의 사용법은 이글에 주제가 벗어나니 사용법은 익혀오시면 감사하겠습니다.

이제 보면 유효성 검사를 하여 예외가 발생하여 위에 메서드를 호출 할텐데 매개변수인 MethodArgumentNotValidException e을 어떻게 처리할 지 아래와 같이 보여 드리겠습니다.

 

 

 

 

 

데이터를 잘못 넣었을 상황 데이터 예시

 

1) 모두 null 값으로 넣었을 때

@RestControllerAdvice(annotations = RestController.class)
public class ValidControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorResult MethodArgumentNotValidExHandler(MethodArgumentNotValidException e) {
        Map<String, Object> data = new HashMap<>();
        data.put("connStatus", "fail");

        log.info("getBindingResult : {}", e.getBindingResult());
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        for (ObjectError allError : allErrors) {
            log.info("allError.getDefaultMessage : {}", allError.getDefaultMessage());
            log.info("allError.getObjectName : {}", allError.getObjectName());
        }
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            log.info("fieldError.getDefaultMessage : {}", fieldError.getDefaultMessage());
            log.info("fieldError.getField : {}", fieldError.getField());
            log.info("fieldError.getObjectName : {}", fieldError.getObjectName());
            log.info("fieldError.getRejectedValue : {}", fieldError.getRejectedValue());
        }

        log.info("getGlobalError : {}", e.getGlobalError());
        log.info("getMessage : {}", e.getMessage());
        log.info("getDetailMessageArguments : {}", e.getDetailMessageArguments());
        log.info("getFieldError : {}", e.getFieldError());
        log.info("getStatusCode : {}", e.getStatusCode());



        return new ErrorResult(HttpStatus.BAD_REQUEST.value(), "실패", data);
    }
}

/** 출력 값
    getBindingResult : org.springframework.validation.BeanPropertyBindingResult: 3 errors
    Field error in object 'memberRequestDto' on field 'phoneNumber': rejected value [null]; codes [Pattern.memberRequestDto.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljakarta.validation.constraints.Pattern$Flag;@5a32ffe5,[0-9]{10,11}]; default message [10~11자리의 숫자만 입력가능합니다.]
    Field error in object 'memberRequestDto' on field 'email': rejected value [null]; codes [NotBlank.memberRequestDto.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email]]; default message [공백일 수 없습니다]
    Field error in object 'memberRequestDto' on field 'name': rejected value [null]; codes [NotBlank.memberRequestDto.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.name,name]; arguments []; default message [name]]; default message [이름은 공백일 수 없습니다.]
    allError.getDefaultMessage : 공백일 수 없습니다
    allError.getObjectName : memberRequestDto
    allError.getDefaultMessage : 이름은 공백일 수 없습니다.
    allError.getObjectName : memberRequestDto
    allError.getDefaultMessage : 10~11자리의 숫자만 입력가능합니다.
    allError.getObjectName : memberRequestDto
    fieldError.getDefaultMessage : 공백일 수 없습니다
    fieldError.getField : email
    fieldError.getObjectName : memberRequestDto
    fieldError.getRejectedValue : null
    fieldError.getDefaultMessage : 이름은 공백일 수 없습니다.
    fieldError.getField : name
    fieldError.getObjectName : memberRequestDto
    fieldError.getRejectedValue : null
    fieldError.getDefaultMessage : 10~11자리의 숫자만 입력가능합니다.
    fieldError.getField : phoneNumber
    fieldError.getObjectName : memberRequestDto
    fieldError.getRejectedValue : null
    getGlobalError : null
    getMessage : Validation failed for argument [0] in public aoxx.validation.domain.member.dto.MemberResponseDto aoxx.validation.domain.web.MemberController.join(aoxx.validation.domain.member.dto.MemberRequestDto) with 3 errors: [Field error in object 'memberRequestDto' on field 'phoneNumber': rejected value [null]; codes [Pattern.memberRequestDto.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljakarta.validation.constraints.Pattern$Flag;@5a32ffe5,[0-9]{10,11}]; default message [10~11자리의 숫자만 입력가능합니다.]] [Field error in object 'memberRequestDto' on field 'email': rejected value [null]; codes [NotBlank.memberRequestDto.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email]]; default message [공백일 수 없습니다]] [Field error in object 'memberRequestDto' on field 'name': rejected value [null]; codes [NotBlank.memberRequestDto.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.name,name]; arguments []; default message [name]]; default message [이름은 공백일 수 없습니다.]] 
    getDetailMessageArguments : 
    getFieldError : Field error in object 'memberRequestDto' on field 'phoneNumber': rejected value [null]; codes [Pattern.memberRequestDto.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljakarta.validation.constraints.Pattern$Flag;@5a32ffe5,[0-9]{10,11}]; default message [10~11자리의 숫자만 입력가능합니다.]
    getStatusCode : 400 BAD_REQUEST
    Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public aoxx.validation.domain.member.dto.MemberResponseDto aoxx.validation.domain.web.MemberController.join(aoxx.validation.domain.member.dto.MemberRequestDto) with 3 errors: [Field error in object 'memberRequestDto' on field 'phoneNumber': rejected value [null]; codes [Pattern.memberRequestDto.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljakarta.validation.constraints.Pattern$Flag;@5a32ffe5,[0-9]{10,11}]; default message [10~11자리의 숫자만 입력가능합니다.]] [Field error in object 'memberRequestDto' on field 'email': rejected value [null]; codes [NotBlank.memberRequestDto.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email]]; default message [공백일 수 없습니다]] [Field error in object 'memberRequestDto' on field 'name': rejected value [null]; codes [NotBlank.memberRequestDto.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.name,name]; arguments []; default message [name]]; default message [이름은 공백일 수 없습니다.]] ]
 */

 

 

 

3) 이메일 필드만 제대로 입력 안했을 때

@RestControllerAdvice(annotations = RestController.class)
public class ValidControllerAdvice {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorResult MethodArgumentNotValidExHandler(MethodArgumentNotValidException e) {
        Map<String, Object> data = new HashMap<>();
        data.put("connStatus", "fail");

        log.info("getBindingResult : {}", e.getBindingResult());
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        for (ObjectError allError : allErrors) {
            log.info("allError.getDefaultMessage : {}", allError.getDefaultMessage());
            log.info("allError.getObjectName : {}", allError.getObjectName());
        }
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            log.info("fieldError.getDefaultMessage : {}", fieldError.getDefaultMessage());
            log.info("fieldError.getField : {}", fieldError.getField());
            log.info("fieldError.getObjectName : {}", fieldError.getObjectName());
            log.info("fieldError.getRejectedValue : {}", fieldError.getRejectedValue());
        }

        log.info("getGlobalError : {}", e.getGlobalError());
        log.info("getMessage : {}", e.getMessage());
        log.info("getDetailMessageArguments : {}", e.getDetailMessageArguments());
        log.info("getFieldError : {}", e.getFieldError());
        log.info("getStatusCode : {}", e.getStatusCode());



        return new ErrorResult(HttpStatus.BAD_REQUEST.value(), "실패", data);
    }
}

/** 출력 값
    getBindingResult : org.springframework.validation.BeanPropertyBindingResult: 3 errors
    Field error in object 'memberRequestDto' on field 'phoneNumber': rejected value [null]; codes [Pattern.memberRequestDto.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljakarta.validation.constraints.Pattern$Flag;@5a32ffe5,[0-9]{10,11}]; default message [10~11자리의 숫자만 입력가능합니다.]
    Field error in object 'memberRequestDto' on field 'email': rejected value [null]; codes [NotBlank.memberRequestDto.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email]]; default message [공백일 수 없습니다]
    Field error in object 'memberRequestDto' on field 'name': rejected value [null]; codes [NotBlank.memberRequestDto.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.name,name]; arguments []; default message [name]]; default message [이름은 공백일 수 없습니다.]
    allError.getDefaultMessage : 이메일 양식을 지켜주세요
    allError.getObjectName : memberRequestDto
    fieldError.getDefaultMessage : 이메일 양식을 지켜주세요
    fieldError.getField : email
    fieldError.getObjectName : memberRequestDto
    fieldError.getRejectedValue : aoxx.co.kr
    getGlobalError : null
    getMessage : Validation failed for argument [0] in public aoxx.validation.domain.member.dto.MemberResponseDto aoxx.validation.domain.web.MemberController.join(aoxx.validation.domain.member.dto.MemberRequestDto): [Field error in object 'memberRequestDto' on field 'email': rejected value [aoxx.co.kr]; codes [Email.memberRequestDto.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email],[Ljakarta.validation.constraints.Pattern$Flag;@71633d19,.*]; default message [이메일 양식을 지켜주세요]]
    getDetailMessageArguments : 
    getFieldError : Field error in object 'memberRequestDto' on field 'email': rejected value [aoxx.co.kr]; codes [Email.memberRequestDto.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email],[Ljakarta.validation.constraints.Pattern$Flag;@71633d19,.*]; default message [이메일 양식을 지켜주세요]
    getStatusCode : 400 BAD_REQUEST
    Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public aoxx.validation.domain.member.dto.MemberResponseDto aoxx.validation.domain.web.MemberController.join(aoxx.validation.domain.member.dto.MemberRequestDto): [Field error in object 'memberRequestDto' on field 'email': rejected value [aoxx.co.kr]; codes [Email.memberRequestDto.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberRequestDto.email,email]; arguments []; default message [email],[Ljakarta.validation.constraints.Pattern$Flag;@71633d19,.*]; default message [이메일 양식을 지켜주세요]] ]
 */

 

 

 

 

 

 

기능 완료

 

값을 입력하지 않고 요청했을 때 null 값으로 갔을 겁니다.

에러 응답값

 

Index.html

errorShowHTML JS 함수에서 제가 만든 JSON 규격으로 변경해야하므로 아래 코드로 변경해줍니다.

// 에러 메시지 출력 함수
function errorShowHTML(response){
    const errorList = response.responseJSON.data.errorList;

    errorList.map(error => {
        let field = error.field;
        let template = `
            <div class="error-message text-danger text-small" id="error-${field}">${error.msg}</div>
        `;
        $(`#${field}`).after(template);
    });
}

 

 

응답 결과 적용

 

 

짠, 잘 출력되는걸 볼 수 있죠!!!!

이제 이렇게 유효성 검사해서 에러를 컨트롤 하도록 합니다!!

 

 

감사합니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함