회원가입을 할 때 생길 수 있는 예외들에는 뭐가 있을까?
Custom Exception을 적용시키지 않았던 이 편에 이어서...
기존에 작성했던 예외 처리 코드를 조금 바꿔보았다.
Custom Exception을 해보았다.
음... 어찌저찌 테스트 통과하긴 하는데,
문제는, 내가 짠 테스트코드가 틀린 로직일 수도 있다ㅋㅋㅠ
근데 일단 통과는 함...
원래 통과 못했는데, 이것저것 바꾸다보니 됐음.
암튼
회원 이름이 겹치는 중복 회원가입 상황에 대한 예외처리를 해보았다.
exception 패키지를 생성한다.
그리고 그 안에 RuntimeException을 상속받는 AppException 클래스를 생성한다.
[AppException.java]
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class AppException extends RuntimeException {
private ErrorCode errorCode; // Enum Type, Custom ErrorCode이다.
private String message;
}
그 다음
Enum 타입 ErrorCode를 생성한다.
[ErrorCode.java]
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@AllArgsConstructor
@Getter
public enum ErrorCode {
// 회원 이름 중복
MEMBERNAME_DUPLICATED(HttpStatus.CONFLICT, "");
private HttpStatus httpStatus;
private String message;
}
→ 회원 이름 중복 시 http status가 conflict로 처리한다.
다음은 @RestControllerAdvice를 선언할 ExceptionManager 클래스를 생성한다.
[ExceptionManager.java]
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ExceptionManager {
@ExceptionHandler(AppException.class)
public ResponseEntity<?> appExceptionHandler(AppException e) {
return ResponseEntity.status(e.getErrorCode().getHttpStatus())
.body(e.getErrorCode().name() + " " + e.getMessage()); // String으로 처리
}
// 특정 Exception을 받아서 처리할 수 있음
// ? : 뭐든지 받을 수 있음
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<?> runtimeExceptionHandler(RuntimeException e) {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(e.getMessage());
}
}
→ @ExceptionHandler로 RuntimeException과 AppException(특정 Exception)을 받는다.
그리고 중복 검사를 하는 MemberService를 수정한다.
[MemberService.java]
private void validateDuplicateMember(Member member) {
Member findMember = memberRepository.findByEmail(member.getEmail());
if(findMember != null) {
throw new AppException(ErrorCode.MEMBERNAME_DUPLICATED, findMember.getName() + "은 이미 있습니다.");
}
}
그 다음 테스트 코드를 작성한다.
[MemberServiceTest]
@Test
@DisplayName("중복 회원 가입 테스트")
public void saveDuplicateMemberTest(){
Member member1 = createMember();
Member member2 = createMember();
memberService.saveMember(member1);
Throwable e = assertThrows(IllegalStateException.class, () -> {
memberService.saveMember(member2);});
assertEquals("이미 가입된 회원입니다.", e.getMessage());
}
→ 테스트 코드는 전에 했던 코드를 그대로 썼더니,
테스트 코드가 실패했다.
fail한 이유를 보니,
org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <java.lang.IllegalStateException> but was: <com.myrecipe.exception.AppException>
이런 에러가 났다.
IllegalStateException을 기대했는데, AppException가 발생했다는 문장인 것 같다.
따라서 Test 코드를 수정한다.
[MemberServiceTest 수정 후]
@Test
@DisplayName("중복 회원 가입 테스트")
public void saveDuplicateMemberTest(){
Member member1 = createMember();
Member member2 = createMember();
memberService.saveMember(member1);
Throwable e = assertThrows(AppException.class, () -> {
memberService.saveMember(member2);});
assertEquals("김길동은 이미 있습니다.", e.getMessage());
}
결과는
통과한다.
참고로 내가 짠 테스트코드 불신해서(ㅋ) 한번 에러 메세지를 바꿔보았다.
assertEquals("김길동은dkdk 이미 있습니다.", e.getMessage());
이렇게 말이다.
그랬더니
이렇게 결과가 나온다.
다행히 코드를 이상하게 짜진 않았나보다.
@ControllerAdvice란?
간단하게 말하자면 @ExceptionHandler, @ModelAttribute, @InitBinder 가 적용된 메서드들을
AOP를 적용해 컨트롤러 단에 적용하기 위해 고안된 애너테이션 입니다.
클래스에 선언하면 되며, 모든 @Controller에 대한,
전역적으로 발생할 수 있는 예외를 잡아서 처리할 수 있다.
@RestControllerAdvice란?
@ResponseBody + @ControllerAdvice => @RestControllerAdvice
@ControllerAdvice와 동일한 역할을 수행하고,
추가적으로 @ResponseBody를 통해 객체를 리턴할 수도 있다.
따라서 단순히 예외만 처리하고 싶다면 @ControllerAdvice를 적용하면 되고,
응답으로 객체를 리턴해야 한다면 @RestControllerAdvice를 적용하면 된다.
@ExceptionHandler란?
위에서 @ControllerAdvice에 대해서 이야기할 때 언급한 어노테이션이다.
이 어노테이션을 메서드에 선언하고 특정 예외 클래스를 지정해주면
해당 예외가 발생했을 때 메서드에 정의한 로직으로 처리할 수 있다.
@ControllerAdvice 또는 @RestControllerAdvice에 정의된 메서드가 아닌
일반 컨트롤러 단에 존재하는 메서드에 선언할 경우, 해당 Controller에만 적용된다.
Controller, RestController에만 적용이 가능하다(@Service 등의 빈에서는 안된다.)
출처 : https://javachoi.tistory.com/253
https://velog.io/@banjjoknim/RestControllerAdvice
'프로젝트' 카테고리의 다른 글
Spring Security를 이용하여 로그인을 구현해보자 (0) | 2023.01.26 |
---|---|
'도대체 DTO를 왜 만드는 것인가'에 대한 고찰 (2) | 2023.01.25 |
프로젝트 진행 중 (2) - JPA 연결 (0) | 2022.12.19 |
프로젝트 진행 중 (1) - JPA 삽질 (2) | 2022.12.19 |
프로젝트 배포 - AWS EC2 CentOS (0) | 2022.12.19 |