본문 바로가기
ZB 백엔드 스쿨/블로그 과제

스프링 핵심가이드) 북스터디 6주차 : 10장 유효성 검사와 예외처리

by son_i 2023. 10. 29.
728x90

10 유효성 검사와 예외처리

유효성 검사 / 데이터 검증 : 비즈니스 로직이 올바르게 동작하기 위해 데이터를 사전 검증하는 작업

 

10.1 일반적인 애플리케이션 유효성 검사의 문제점

일반적으로 사용되는 데이터 검증 로직의 문제점 : 

  1. 계층별로 진행하는 유효성 검사는 검증 로직이 각 클래스 별로 분산되어 있어 관리가 어려움.

  2. 검증로직에 중복이 많아 여러 곳에 유사한 기능의 코드가 존재할 수 있음.

  3. 검증해야 할 값이 많으면 검증하는 코드가 길어져 가독성이 떨어짐.

 

=> Bean Validation이라는 데이터 유효성 검사 프레임워크로 위의 문제들을 해결.

Bean Validation은 어노테이션을 통해 다양한 데이터를 검증하는 기능을 제공.

  - 유효성 검사를 위한 로직을 DTO같은 도메인 모델과 묶어서 각 계층에서 사용하면서 검증 자체를 도메인 모델에 얹는 방식으로 수행.

  - 어노테이션을 사용한 검증방식이라 코드의 간결함도 유지할 수 있음.

 


10.2 Hibernate Validator

- Bean Validation 명세의 구현체. 스프링 부트에서 Hibernate Validator를 유효성 검사 표준으로 채택해 사용.

- 도메인 모델에서 어노테이션을 통한 필드값 검증을 가능하게 도와줌.

 


10.3 스프링 부트에서의 유효성 검사

 1. 의존성 추가

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

 

 2. 유효성 검사는 각 계층으로 데이터가 넘어오는 시점에 해당 데이터에 대한 검사를 실시함.

따라서 DTO 객체의 필드들에 어노테이션으로 검증

 

  - 문자열 검증

  · @Null : null 값만 허용

  · @NotNull : null 허용하지 않음. "", " "는 허용

  · @NotEmpty : null, ""는 허용하지 않음. " "는 허용

  · @NotBlank : null, "", " " 허용하지 않음.

 

 - 최대/최소값 검증

  · BigDecimal, BigInteger, int, long 타입 지원

  ·  @DemicalMax(value = "$numberString") : $numberString보다 작은 값을 허용

  · @DemicalMin(value = "$numberString") : $numberString보다 큰 값 허용

  · @Min(value = $number) : $number 이상의 값을 허용

  · @Max(value = $number) : $number 이하의 값을 허용

 

 - 값의 범위 검증

  · BigDecimal, BigInteger, int, long등의 타입 지원

  · @Positive : 양수 허용

  · @PositiveOrZero : 0을 포함한 양수 허용

  · @Negative : 음수 허용

  · @NegativeOrZero : 0을 포함한 음수 허용

 

 - 시간에 대한 검증

  · Date, LocalDate, LocalDateTime 등의 타입지원

  · @Future : 현재보다 미래의 날짜 허용.

  · @FutureOrPresent : 현재를 포함한 미래의 날짜 허용.

  · @Past : 현재보다 과거의 날짜 허용

  · @PastOrPresent : 현재를 포함한 과거의 날짜를 허용.

 

 - 이메일 검증

  · @Email : 이메일 형식을 검사. ""는 허용

  

 - 자릿수 범위 검증

  · BigDemical, BigInteger, int, long 등의 타입 지원.

  · @Digits(integer = $number1, fraction = $number2) : $number1의 정수 자릿수와 $number2의 소수 자릿수를 허용합니다.

 

- Boolean 검증

  · @AssertTrue : true인지 체크. null 값은 체크하지 않음.

  · @AssertFalse : false인지 체크. null 값은 체크하지 않음.

 

 - 문자열 길이 검증

  · @Size(min = $number1, max = $number2) : $number1 이상 $number2 이하의 범위 허용

 

 - 정규식 검증

  · @Pattern(regexp = "$expression") : 정규식을 검사. 정규식은 자바의 java.util.regec.Pattern 패키지의 컨벤션을 따름.

 

@Valid 어노테이션을 지정해야 DTO 객체에 대해 유효성 검사를 수행할 수 있음.

 

- 정규식 : 특정한 규칙을 가진 문자열 집합을 표현하기 위해 쓰이는 형식.
전화번호, 주민등록번호, 이메일과 같이 특정형식의 값을 검증해야 할 때가 있다. 이런 값은 정규식을 통해 쉽게 검증할 수 있다.

- 정규식에서 사용하는 요소
 ^ : 문자열의 시작
 $ : 문자열의 종료
 . : 임의의 한 문자
 * : 앞 문자가 없거나 무한정 많음
 + : 앞 문자가 하나 이상
 ? : 앞 문자가 없거나 하나 존재
 [,] : 문자의 집합이나 범위를 나타내며, 두 문자 사이는 - 기호로 범위를 표현
 {, } : 횟수 또는 범위를 의미
 (, ) : 괄호 안의 문자를 하나의 문자로 인식
 | : 패턴 안에서 OR 연산을 수행
 \ : 정규식에서 역슬래시는 확장문자로 취급, 역슬래시 다음에 특수문자가 오면 문자로 인식
 \b : 단어의 경계
 \B : 단어가 아닌 것에 대한 경계
 \A : 입력이 시작 부분
 \G : 이전 매치의 끝
 \Z : 종결자가 있는 경우 입력의 끝
 \z : 입력의 끝
 \s : 공백 문자
 \S : 공백 문자가 아닌 나머지 문자(^\s와 동일)
 \w : 알파벳이나 숫자
 \W : 알파벳이나 숫자가 아닌 문자(^/w와 동일)
 \d : 숫자 [0 - 9] 와 동일하게 취급
 \D : 숫자를 제외한 모든 문자 (^0-9)와 동일

정규식 연습 사이트 

 https://regexr.com/

 https://regex101.com/

 

10.3.4 @Validated 활용

@Valid는 자바에서 지원하는 어노테이션.

스프링에서도 @Validated라는 별도의 어노테이션을 지원함.

@Validated는 @Valid의 기능을 포함하고 있기 때문에 @Validate로 변경할 수 있고

@Validated는 유효성 검사를 그룹으로 묶어 대상을 특정할 수 있는 기능이 있음

 

 

- @Validated 어노테이션에 특정 그룹을 설정하지 않은 경우에는 groups가 설정되지 않은 필드에 대해 유효성 검사를 수행.

- @Validated 어노테이션에 특정 그룹을 설정하는 경우에는 지정된 그룹으로 설정된 필드에 대해서만 유효성 검사를 수행.

커스텀 Validation을 만들 수도 있음.


10.4 예외처리

10.4.1 예외와 에러

예외 : 입력 값의 처리가 불가능하거나 참조된 값이 잘못된 경우 등 애플리케이션이 정상적으로 동작하지 못 하는 상황.

   - 개발자가 직접 처리할 수 있는 것이므로 미리 코드 설계를 통해 처리할 수 있음.

 

에러 : 자바 가상머신에서 주로 발생시키는 것으로 애플리케이션 코드에서 처리할 수 있는 것이 거의 없음.

  - ex) 메모리 부족(OutOfMemory), 스택 오버플로우(StackOverFlow)가 있음. 이런한 에러는 예방해서 원칙적으로 차단해야함.

 

10.4.2 예외 클래스

모든 예외 클래스는 Throwable 클래스를 상속받음. Exception은 다양한 자식클래스를 가지는데 Checked Exception과 Unchecked Exception으로 구분할 수 있음.

 

  Checked Exception Unchecked Exception
처리 여부 반드시 예외처리 필요 명시적 처리를 강제하지 않음
확인 시점 컴파일 단계 실행 단계
대표적인 예외 클래스 IOException
SQLException
RuntimeException
NullPointerException
IllegalArgumentException
IndexOutOfBoundException
SystemException

- Checked Exception : 컴파일 단계에서 확인 가능한 예외 상황. IDE에서 캐치해서 반드시 예외처리를 할 수 있게 표시해줌. * 에러 발생시 roll-back 되지 않음.

 

- UncheckedException : 런타임 단계에서 확인되는 예외 상황. 문법상 문제는 없지만 프로그램이 돌아가는 도중 예기치 않은 상황이 생겨 발생하는 예외 의미. 

  * 에러 발생시 roll-back

 

10.4.3 예외 처리 방법

예외 발생 시 이를 처리하는 방법은 크게 3가지

 

- 예외 복구 : 예외 상황을 파악해서 문제를 해결하는 방식 try/catch 구문

- 예외 처리 회피 : 예외가 발생한 메서드를 호출한 곳에서 에러처리를 할 수 있게 전가하는 방식.

   throw를 사용해 어떤 예외가 발생했ㄴ느지 호출부에 내용을 전달할 수 있음.

- 예외 전환 : 예외 발생 시 어떤 예외가 발생했는지에 따라 호출부로 예외 내용을 전달하면서 좀 더 적합한 예외 타입으로 전달할 필요가 있음. try/catch 방식을 활용하면서 catch 블록에서 throw 키워드를 통해 다른 예외 타입으로 전달하면 됨.

 

10.4.4 스프링 부트의 예외 처리 방식

예외 발생했을 때 클라이언트에 오류 메세지 전달하려면 각 레이어에서 발생한 예외를 엔드포인트 레벨인 컨트롤러로 전달해야함. 이렇게 전달받은 예외를 스프링 부트에서 처리하는 방식 두 가지

 1. @(Rest)ControllerAdvice와 @ExceptionHandler를 통해 모든 컨트롤러의 예외를 처리

 2. @ExceptionHandler를 통한 특정 컨트롤러의 예외를 처리

 

@RestControllerAdvice 대신 @RestControllerAdvice를 사용하면 결과값을 JSON 형태로 반환할 수 있음.

 


- @RestControllerAdvice : 스프링에서 제공하는 어노테이션. 이 어노테이션은 @RestController에서 발생하는 예외를 한 곳에서 관리하고 처리할 수 있게 하는 기능을 수행.

 

별도 설정을 통해 예외 관제하는 범위 지정 가능

@RestControllerAdvice(basePackages = "com.springboot.valid_exception")

- @ExceptionHandler : @Controller나 @RestController가 적용된 빈에서 발생하는 예외를 잡아 처리하는 메서드를 정의할 때 사용. 어떤 예외 클래스를 처리할 지는 value 속성으로 등록

 

@ExceptionHandler(value = RuntumeException.class)

 value 속성은 배열 형식으로도 전달받을 수 있어서 여러 예외 클래스를 등록할 수도 있음.


구체적인 클래스가 지정된 쪽이 우선순위를 갖게 된다.

@ControllerAdvice의 글로벌 예외처리와 @Controller내의 컨트롤러 예외처리에 동일한 예외처리를 하면 범위가 좁은 컨트롤러의 핸들러 메서드가 우선순위를 갖게됨.

 

10.4.5 커스텀 예외