Networks/SpringBoot

SK networks AI Camp - SpringBoot(2)

코딩하는 Español되기 2024. 9. 6. 18:30

Validation(유효성 검사)

: 서비스의 비즈니스 로직이 올바르게 동작하기 위해 사용되는 데이터에 대한 사전 검증

  즉, Input 데이터에 대해 의도한 형식의 값이 제대로 들어오는지 체크하는 과정

(역시 이렇게만 봐도 Gradle이 편하다)

// Gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'

// Maven
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

 

○ 일반적인 유효성검사

    ● 간단한 검증을 하더라도 검증관련 로직이 길어지는 경향이 有

    ● 검증 로직이 중복으로 여러 Layer(Controller, Service, DAO 등)에 존재

    ● Layer에 검증 로직이 섞여있기 때문에 추측이 어렵고 Application이 복잡해짐

if (itemDTO == null) {
    throw new IllegalArgumentException("아이템 정보가 존재하지 않습니다.");
}

if (itemDTO.getItemName() == null || itemDTO.getItemName().isEmpty()) {
    throw new IllegalArgumentException("아이템 명은 필수 입니다.");
}

if (itemDTO.getQuantity() == null) {
    throw new IllegalArgumentException("아이템 개수는 필수 입니다.");
}
...

 

○ @Valid를 이용한 유효성 검사

public ResponseEntity<String> save(@Valid @RequestBody ItemDTO itemDTO) {
    itemService.save(itemDTO);
    return new ResponseEntity<>(HttpStatus.CREATED);
}

Validation 관련 주요 어노테이션

○ 문자열

    ● @Size : 문자의 길이 조건

    ● @NotNull : null 값 불가

    ● @NotEmpty : @NotNull + "" 값 불가

    ● @NotBlank : @NotEmpty + " " 값 불가

    ● @Pattern(regexp="", message="") : 정규식을 통한 조건

    ● @Email : 이메일

 

○ 날짜

    ● @Past : 과거 날짜

    ● @PastOrPresent : @Past + 오늘 날짜

    ● @Future : 미래 날짜

    ● @FutureOrPresent : @Future + 오늘 날짜

 

○ 숫자

    ● @Max / @Min : 최대/최소값

    ● @Positive / @PositiveOrZero : 양수와 0만

    ● @Negative / @NegativeOrZero : 음수와 0만

 

○ 직접 검증

    ● AssertTrue : 참

    ● AssertFalse : 거짓

 

public class ProductDto {
  
  private String productId;

  @NotBlank
  @Size(min = 4, max = 10)
  private String ProductName;

  @Min(value = 50)
  private int ProductPrice;

  @Min(value = 0)
  @Max(value = 99)
  private int productStock;

}

Exception

○ 예외 클래스

    ● 모든 예외 클래스는 Throwable 클래스를 상속 받고 있음

    ● RuntimeException은 Unchecked Exception이며, 그 외 Exception은 Checked Exception

  Checked Exception Unchecked Exception
처리 여부 반드시 예외 처리 명시적 처리 강제 X
확인 시점 컴파일 단계 실행 중 단계
예외 발생 시 트랜잭션 롤백하지 X 롤백
대표 예외 IOException
SQLException
NullPointerException
Illegal ArgumentException
IndexOutOfBoundException
SystemException

 

○ SpringBoot의 예외 처리 방식

    ● @ControllerAdvice를 통한 모든 Controller에서 발생할 수 있는 예외 처리

    ● @exceptionHandler를 통한 특정 Controller의 예외처리

       @ControllerAdvice로 모든 컨트롤러에서 발생할 예외 정의

        → @ExceptionHandler를 통해 발생하는 예외마다 처리할 메소드 정의

 

○ @ExceptionHandler

    ● Controller 계층에서 발생하는 에러를 잡아 메서드로 처리해주는 기능

    ● Service, Repository에서 발생하는 에러는 제외

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}

    ● 여러 개의 Exception 처리

        - @ExceptionHandler의 value 값으로 어떤 Exception을 처리할 것인지 넘겨 줄 수 있음

        - value를 설정하지 않으면 모든 Exception을 잡게되어 구체적으로 명시해주는 것이 좋음

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler({FileSystemException.class, RemoteException.class})
    public ResponseEntity<String> handle(Exception ex) {
        // ...
    }
}

    ● 우선 순위 : Exception.class 보다 구체적 오류클래스(NullPointerException.class)가 우선순위 높음

 

○ @ControllerAdvice

    ● @ControllerAdvice 안에서 @ExceptionHandler를 사용하여 에러를 잡을 수 있음

@ControllerAdvice
public class ExceptionHandlers {

    @ExceptionHandler(FileNotFoundException.class)
    public ResponseEntity handleFileException() {
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }

}

    ● 범위 설정

        - @ControllerAdvice는 모든 에러를 잡아주어 일부 에러만 처리하고 싶을 경우 따로 설정해주면 됨

// 1.
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// 2.
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// 3.
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

○ @RestControllerAdvice

    ● @RestControllerAdvice는 @ControllerAdvice와 @ResponseBody로 이루어져 있어 오류 메세지를

        Response body(= json)으로 리턴 가능

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
	// ...	
}

Custom Exception

: 비지니스 로직에 의한 논리 오류인 경우 사용자 정의 예외를 이용하는 것이 좋을 수 있음

@Getter
public class HubException extends Exception {
  
  private HttpStatus httpStatus;

  public HubException(HttpStatus httpStatus, String message) {
    super(message);
    this.httpStatus = httpStatus;
  }
}

○ Custom Exception 장점

    ● 예외 이름으로도 정보 전달 가능 : 상품 오류, 고객 오류 등

    ● 상세한 예외 정의 가능 : 재고상품 50 미만 오류, 고객 잔고 10만원 미만 오류 등

    ● 예외에 대한 응집도 향상 : 예외 메시지, 데이터 가공 메소드 등 각 예외에 맞는 메시지/메소드 관리 용이

    ● 예외 발생 후 처리가 용이 : 각 예외별 후 처리용 메소드 정의 가능

 

○ Enum

: 일정 개수의 상수 값을 정의하고 그 외의 값은 허용하지 않음

  (과거의 경우 : 특정 상수 값을 사용하기 위해선 모두 상수로 선언해 사용했음)

    ● 정의 및 생성

public enum Day1 {
    MON, TUE, WED, THU, FRI, SAT, SUN
};

Day1.MON.name(); // MON

public enum Day2 {
    MON("Monday"), TUE("Tuesday"), WED("Wednesday");

    private final String label;

    Day2(String label) {
        this.label = label;
    }

    public String label() {
        return label;
    }
};

Day2.MON.name(); // MON
Day2.MON.label(); // Monday

    ● Enum 값 찾기 : name을 이용해 Enum 클래스 정의

Day2 day = Day2.valueOf("TUE");

    ● values() : enum 타입의 모든 값들을 배열로 만들어 리턴해주는 메소드

for (Day2 day : Day2.values()) {
  day.label();
}

TDD(Test Drive Development)

: 테스트 주도 개발이라는 의미(테스트를 먼저 설계 및 구축 후 테스트를 통과할 수 있는 코드를 작성하는 것)

  코드 작성 후 테스트를 진행하는 지금까지 사용된 일반적인 방식과 다소 차이가 있음

 

○ 애자일 개발 방식 중 하나

    ● 단계적 목표에 대해 설정해 진행하고자 하는 것에 대한 결정 방향의 갭을 줄이고자 함

    ● 최초 목표에 맞춘 테스트를 구축해 그에 맞게 코드를 설계하기 때문에 보다 적은 의견 충돌 기대 가능

 

○ 테스트 코드 작성 목적

    ● 코드의 안정성 높일 수 있음

    ● 기능을 추가하거나 변경하는 과정에서 발생할 수 있는 Side-Effect를 줄일 수 있음

    ● 해당 코드가 작성된 목적을 명확하게 표현할 수 있음

        - 코드에 불필요한 내용이 들어가는 것을 비교적 줄일 수 있음

JUnit

: Java 진영의 대표적인 Test Framework, Annotation을 기반으로 테스트를 지원

 단정문(Assert)로 테스트 케이스의 기대값에 대해 수행 결과 확인 

 단위 테스트를 위한 도구를 제공

    - 코드의 특정 모듈이 의도된 대로 동작하는지 테스트

    - 모든 함수(메소드)에 대한 테스트 케이스를 작성

 

○ JUnit LifeCycle Annotation

Annotation Description
@Test 테스트용 메소드를 표현하는 어노테이션
@BeforeEach 각 테스트 메소드가 시작되기 전에 실행되어야 하는 메소드 표현
@AfterEach 각 테스트 메소드가 시작된 후 실행되어야 하는 메소드 표현 
@BeforeAll 테스트 시작 전에 실행되어야 하는 메소드 표현(static 처리 필요)
@AfterAll 테스트 종류 후에 실행되어야 하는 메소드 표현(static 처리 필요)

 

○ JUnit Main Annotation

    ● @SpringBootTest

        - 통합 테스트 용도로 사용

        - @SpringBootApplication을 찾아가 하위 모든 Bean을 스캔해 로드

        - 그 후 Test용 Application Context를 만들어 Bean을 추가하고, MockBean을 찾아 교체

 

    ● @ExtendWith

        - @ExtendWith는 메인으로 실행될 Class를 지정 가능

        - @SpringBootTest : 기본적으로 @ExtendWith가 추가되어 있음

 

    ● @WebMvcTest(Class명.class)

        - ()에 작성된 클래스만 실제로 로드해 테스트 진행

        - 매개변수 지정하지 않으면 컨트롤러와 연관된 Bean 모두 로드

           ( @Controller, @RestController, @RestControllerAdvice 등 )

        - @SpringBootTest 대신 컨트롤러 관련 코드만 테스트할 경우 사용

 

    ● @Autowired about Mockbean

        - Controller의 API를 테스트하는 용도인 MockMvc객체를 주입 받음

        - perform() 메소드를 활용해 컨트롤러의 동작 확인 가능

        - andExpect(), andDo(), andReturn() 등의 메소드를 같이 활용

 

    ● @MockBean

        - 테스트할 클래스에서 주입 받고 있는 객체에 대해 가짜 객체를 생성해주는 어노테이션

        - 실제 행위는 하지 않음

        - given() 메소드를 활용해 가짜 객체의 동작에 대해 정의해 사용 가능

 

    ● @AutoConfigureMockMvc

        - spring.test.mockmvc의 설정을 로드하면서 MockMvc의 의존성을 자동 주입

        - MockMvc 클래스 : RestAPI 테스트를 할 수 있는 클래스

 

    ● @Import

        - 필요한 Class들을 Configuration으로 만들어 사용 가능

        - Configuration Component 클래스도 의존성 설정 가능

        - Import된 클래스는 주입으로 사용 가능

통합 테스트

: 여러 기능을 조합해 전체 비지니스 로직이 제대로 동작하는 지 확인하는 것

  @SpringBootTest를 사용해 진행

    ● 대규모 프로젝트에서 사용할 경우, 테스트를 실행할 때마다 모든 빈을 스캔하고 로드하는 작업이 반복

        So, 매번 무거운 작업을 수행해야함

 

단위 테스트

: 프로젝트에 필요한 모든 기능에 대한 테스트를 각각 진행하는 것

  일반적으로 SpringBoot에서는 'spring-boot-starter-test' 디펜던시만으로 의존성을 모두 가질 수 있음

 

○ F.I.R.S.T 원칙

    ● Fast : 테스트 코드의 실행은 빠르게 진행되어야 함

    ● Independent : 독립적인 테스트가 가능해야 함

    ● Repeatable : 테스트는 매번 같은 결과를 만들어야 함

    ● Self-Validating : 테스트는 그 자체로 실행해 결과를 확인 가능해야함

    ● Timely : 단위 테스트는 비지니스 코드가 완성되기 전에 테스트가 가능해야함

        - TDD의 원칙 : 코드가 완성되기 전부터 테스트가 따라와야 한다는 원칙

 

'Networks > SpringBoot' 카테고리의 다른 글

SK networks AI Camp - SpringBoot(3)  (0) 2024.09.09
SK networks AI Camp - SpringBoot  (5) 2024.09.06