회원가입(1) - 예외처리 도입

유수민·2022년 11월 24일
2

DDD 프로젝트

목록 보기
4/7
post-thumbnail

📌개요

이전 글까지는 회원가입을 구성한 것이긴 했지만 전체적인 기본 틀을 구성하기 위한 과정이었고 이제는 기존의 legacy에서 구현되었던 회원가입 로직을 user-api의 회원가입 로직에도 적용했다. 로직 적용과 더불어 이전과 달라진 점에 대해서도 이야기해보려고 한다.

먼저 내가 해나간 일의 순서대로 간략하게 정리하면,
1) 예외처리 도입 - common 모듈 생성
2) JPA로의 전환기 + MariaDB 적용
3) 회원가입 로직 흐름 개선 (user 엔티티에서 userId 제외 및 email과 password 객체화)

관련 깃허브 PR ) https://github.com/sue4869/ottsharing/pull/1

📌예외처리

📖도입 계기

legacy 모듈의 회원 가입 로직을 user-api에 적용하려고 하니 가장 먼저 든 의문은 'ResultCode 어떡하지?' 였다.

  • UserService

  • ResultCode

legacy버전의 UserService에는 이메일이 중복되거나 비밀번호, 이메일의 유효한 조건회원가입 입력 정보이 아니라면,예외 대신 ResultCode로 응답하도록 되어있다. 이는 '이팩티브 자바'에 나온 성능 저하 즉, 속도 저하라는 단점으로 인해 예외 대신 도입한 결과이다. 좀 더 설명을 덪붙이자면, 예외는 예외 상황을 대비해 설계되어 있어서 JVM에서는 예외 처리 속도가 빠를 필요성이 없기 때문에 일반 코드보다 예외처리 코드 속도가 느리다는 이유였다.

고민한 결과, ResultCode로 적용된 부분에서 예외 처리로 바꾸려는 부분이 제대로 된 정보가 들어오지 않아 어차피 로직 진행을 멈추고 새롭게 사용자가 다시 기입을 해줘야 하는 부분이기 때문에 성능 저하라는 단점을 감수하고라도 예외 처리를 해줘야겠다고 생각했다. 또한, 이전에는 예외를 적용하지 않은 프로젝트이니 이번에 적용하면서 다시 공부를 해봐도 좋다고 생각했다.

📖적용

1) domain

처음에 든 의문은 '4가지 레이어층에서 Exception은 어디에 속하는가?'였다. 고민끝에 정확히 알지 못한다는 결론에 정확성을 위해 하비님께 질문한 결과, 해당 Exception들은 비지니스 규칙이기 때문에 도메인에 속한다는 것이었다. 예를 들어, 이메일 중복 예외 같은 경우, 비지니스 규칙에 해당하므로 도메인에 속해야 한다. http형식에 맞지 않게 들어와서 발생한 예외는 비지니스와 상관없기 때문에 이때는 도메인에 속하지 않는 예외라고 하셨다.
따라서, 도메인 층에 예외 클래스들을 만들어 주었고 (중복 이메일 예외, 유효하지 않는 이메일 예외, 약한 비밀번호예외)

각 예외 클래스는 DomainExcetion을 상속받도록 하였다. DomainException은 앞으로 생길 각 api마다의 도메인층 예외 클래스들이 메세지와 errorCode를 뱉도록 공통된 형식의 구성을 알려주는 틀이기 때문에 새롭게 도입된 common 모듈에 넣어주었다. 이외에도 예외 메세지에 들어갈 ErrorCode와 결과 응답을 어떤 구성으로 내보낼지를 결정하는 CommonApiResponse를 common 모듈에 넣었다.

🔔common
common모듈은 모든 모듈의 모든 층이 다 의존한다. user-api의 경우 domain에만 연결하여도 모든 layer층에서 common모듈을 의존하게 되어 있다.

2) presentation

application 층에서 비지니스로직에 의해 예외가 호출되었을 경우, spring이 만들어준 예외 형식이 아닌 내가 원하는 방식으로 예외가 보여지기 위해서는 handler가 필요하다. presentation 층에 각각의 예외들을 위한 UserExceptionHandler를 만들어주었다. 사실 난 server에 ExceptionHandler를 만들어서 domainException을 공통적으로 상속받은 예외들을 통틀어 묶어서 만들려고 했다.

하지만 하비님이
"추상화된 DomainException을 확장한 구체적인 DomainException을 처리하는 핸들러가 필요할 것 같아요. 구체적인 도메인 예외 별로 응답을 어떻게 할지 결정하면 api 클라이언트가 훨씬 더 명확한 예외 상황을 인지할 수 있을 것 같습니다."

라는 피드백을 해주셨고, 각각의 예외마다 처리할 수 있는 핸들러를 작성하였다. 하지만 이것이 server모듈에 있는 것이 맞는가? 는 다시 생각해볼 필요가 있었다. 처음에는 각각의 예외가 도메인 층에 있기 때문에 각 예외들을 접근하려면 도메인층에 접근할 수밖에 없기에 presentation 층에서 접근하는 것은 옳지 않다고 생각했다. import는 의존성이라고 생각했기 때문이다. 하지만, ddd관점으로 볼때, 사용자의 요청을 받고 응답하는 책임은 presentation에 있으니 presentation층에 있는 것이 옳았다. (다만, 각각의 예외를 변경을 하지 않아야 한다는 전제하에 있어야 한다.) 또한, user-api의 도메인 예외에만 해당하는 것이기 때문에 user-api쪽에 이 예외들에 대한 책임을 갖는 것이 옳았다.

그렇다면 server 모듈에는 어떤 예외가 있어야 하는 것일까? 모든 api에서 발생할 수 있는 예외를 server모듈에 두어야 한다. 즉, 특정 예외클래스에서 잡아주지 못한 예상치 못한 예외들이 여기에 해당한다고 볼 수 있다.

1) @ExceptionHandler

  • Exception 클래스들을 속성으로 받아 처리할 예외를 지정해줄 수 있다.
  • @Controller , @RestController 가 적용된 Bean 에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능

2) @RestControllerAdvice

  • @ResponseBody + @ControllerAdvice
  • 응답을 Json으로 내려준다 (@ResponseBody)
  • 하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리(@ControllerAdvice )

3) @Order
user-presentaion의 UserExceptionHandler와 server의 CommonHandlerCollection중 UserExceptionHandler를 먼저 확인하고 CommonHandlerCollection으로 가도록 우선 순위를 @Order를 통해 부여하였다.

profile
배우는 것이 즐겁다!

0개의 댓글