프로젝트

Custom Exception 해보기

ummchicken 2023. 2. 19. 01:36

기존 예외처리 코드를 바꿔보았다.

 

 

회원가입 or 로그인 같은 회원 서비스에서 발생할 수 있는 예외들을 

MemberException으로 모아 공통 예외 처리를 할 것이다.

 

 

먼저 BaseException을 생성한다.

[BaseException]

public abstract class BaseException extends RuntimeException {

    public abstract BaseExceptionType getExceptionType();

}
  • BaseException은 앞으로 정의할 모든 Custom 예외의 부모 클래스이다.
  • 앞으로 BaseException 타입으로 처리할 수 있다.
  • RuntimeException을 상속받았다.
  • BaseException은 BaseExceptionType을 반환하는 getExceptionType()을 가지고 있다.
    • 이후 Enum 설정

 

 

 

다음은 BaseExceptionType을 생성한다.

[BaseExceptionType]

public interface BaseExceptionType {

    // 에러 코드
    int getErrorCode();

    // Http 상태
    HttpStatus getHttpStatus();

    // 에러 메세지
    String getErrorMessage();

}

 

 

 

다음은 Member에 대한 예외처리를 생성한다.

[MemberException]

public class MemberException extends BaseException {

    private BaseExceptionType exceptionType;

    public MemberException(BaseExceptionType exceptionType) {
        this.exceptionType = exceptionType;
    }

    @Override
    public BaseExceptionType getExceptionType() {
        return exceptionType;
    }
}
  • BaseExceptionType을 멤버변수로 가지고 있다.
  • 생성자를 통해 생성하는 순간 BaseExceptionType을 설정한다.

 

 

 

다음은 MemberExceptionType을 생성한다.

[MemberExceptionType]

public enum MemberExceptionType implements BaseExceptionType {

    // 회원가입, 로그인시
    ALREADY_EXIST_USERNAME(600, HttpStatus.CONFLICT, "이미 존재하는 아이디입니다."),
    NOT_FOUND_MEMBER(602, HttpStatus.NOT_FOUND, "회원 정보가 없습니다.");

    private int errorCode;
    private HttpStatus httpStatus;
    private String errorMessage;

    MemberExceptionType(int errorCode, HttpStatus httpStatus, String errorMessage) {
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
        this.errorMessage = errorMessage;
    }

    @Override
    public int getErrorCode() {
        return this.errorCode;
    }

    @Override
    public HttpStatus getHttpStatus() {
        return this.httpStatus;
    }

    @Override
    public String getErrorMessage() {
        return this.errorMessage;
    }
}
  • enum으로 생성한다.
  • 에러코드, Http 상태코드, 에러 메세지가 존재한다.

 

 

 

이제 예외처리를 수정해보자.

먼저, 기존의 중복회원 처리 코드이다.

[기존 Service]

/**
 * 회원가입 중복 검사
 * 이메일 중복 불가능
 * */
private void validateDuplicateMember(Member member) {
    Member findMember = memberRepository.findByEmail(member.getEmail());
    if(findMember != null) {
        // 예외처리
        throw new AppException(ErrorCode.MEMBERNAME_DUPLICATED, findMember.getName() + "은 이미 있습니다.");
    }
}

 

 

[수정 후 Service]

private void validateDuplicateMember(Member member) {
    Member findMember = memberRepository.findByEmail(member.getEmail()); 

    if(findMember != null) {
        throw new MemberException(MemberExceptionType.ALREADY_EXIST_USERNAME);
    }
}
  • 만약 먼저 가입한 회원이 있으면, MemberException의 ALREADY_EXIST_USERNAME을 발생시킨다.

 

 

 

그 다음은 Controller를 수정한다.

[기존 Controller]

@PostMapping("/new")
public String newMember(@Valid MemberFormDto memberFormDto, BindingResult bindingResult, Model model){

    ...

    try {
        Member member = Member.createMember(memberFormDto, passwordEncoder);
        memberService.saveMember(member);
    } catch (AppException e){
        model.addAttribute("errorMessage", e.getMessage());
        return "member/memberForm";
    }

    return "redirect:/";
}
  • 내가 전에 만들어 놓았던 AppException인데, 이것도 Custom한 것이다.
    • 다만 다른 방식으로 바꿨을 뿐...

 

 

[수정 후 Controller]

@PostMapping("/new")
public String newMember(@Valid MemberFormDto memberFormDto, BindingResult bindingResult, Model model){

    ...

    try {
        Member member = Member.createMember(memberFormDto, passwordEncoder);
        memberService.saveMember(member);
    } catch (MemberException e){
        model.addAttribute("errorMessage", e.getExceptionType().getErrorMessage());
        return "member/memberForm";
    }

    return "redirect:/";
}
  • MemberException의 ErrorMessage를 viewPage에 보내는 코드이다.
    • 뷰페이지가 없고 API 호출만 하는 것이라면, 이 과정은 생략해도 된다.

 

 

 

코드는 다 변경했으니, 테스트코드를 변경해보자.

[기존 TestCode]

@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());
}
  • 기존에 Custom한 AppException이다.

 

 

[수정 후 TestCode]

@Test
@DisplayName("중복 회원 가입 테스트")
public void saveDuplicateMemberTest(){
    Member member1 = createMember();
    Member member2 = createMember();
    memberService.saveMember(member1);

    assertThat(assertThrows(MemberException.class,
            () -> memberService.saveMember(member2)).getExceptionType()).isEqualTo(MemberExceptionType.ALREADY_EXIST_USERNAME);

}
  • 같은 정보의 회원을 가입시켰을 때, MemberExceptionType.ALREADY_EXIST_USERNAME 즉, "이미 존재하는 아이디입니다."와 일치하는지 테스트한다.

 

 

 

통과한다.

 

 

 

 

에러를 처리하는 방법은 정말 다양하게 있구나.

역시 끝이 없는 코딩의 세계이다.

 

 

 

 

- 다른 방식으로 해본 Custom Exception 끝 -