📝 TIL

[TIL] 5주차 주특기 심화ㅣ예외처리

오늘 ONEUL 2022. 12. 15. 00:01

 

오늘의 궁금증

 

@Valid에 대한 예외처리는 어떻게 해야 할까?

과제 요구사항인 예외처리를 어느 정도 마무리하고 테스트를 하던 중
회원가입 유효성 검사에 대한 예외처리는 제대로 되어 있지 않다는 걸 알게 되었다.

현재 회원가입 유효성 검사는 requestDto에 있는 Valid 어노테이션이 해주고 있는데
이 어노테이션은 MethodArgumentNotValidException라는 예외를 발생시킨다.
전역으로 예외처리를 하고 있는 핸들러에서 해당 예외를 잡아 에러에 대한 응답을 보내주어야 한다.

 

핸들러에서 예외 처리

@RestControllerAdvice 어노테이션을 사용한 GlobalExceptionHandler를 이용해 다음과 같이 처리해주었다.

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleArgumentNotValidException(MethodArgumentNotValidException ex) {
        return ResponseEntity
                .status(HttpStatus.BAD_REQUEST); // 400 상태 코드 반환
    }
}

 

그러나 BeanCreationException이 발생했다..!

 

왜 안될까?

찾아보니 @ExceptionHandler에 이미 MethodArgumentNotValidException이 구현되어 있기 때문에
GlobalExceptionHandler에서 동일한 예외 처리를 하게 되면 Ambihuouo(모호성) 문제가 발생한다고 한다.

 

핸들러를 오버라이드 해서 예외 처리

@ExceptionHandler를 사용하는 대신 해당 핸들러(handleMethodArgumentNotValid)를 직접 오버라이드 해서 사용하였다.

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    // Validation Exception
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                                                                  HttpHeaders headers,
                                                                  HttpStatus status,
                                                                  WebRequest request) {
        ErrorCode errorCode = ErrorCode.INVALID_FORMAT;
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(new ErrorResponse(errorCode));
    }
}

 

📚 참고자료

 

트러블 슈팅 221029

BeanCreationException Ambiguous 해결

velog.io

 

좋아요 로직 한 번에 insert와 delete 해도 괜찮은지?

현재 Post 메소드로 요청을 받은 좋아요 로직은 다음과 같이 구현되어 있다.

@Service
@RequiredArgsConstructor
public class BoardService {

    //의존성 주입
    private final BoardRepository boardRepository;
    private final BoardLikeRepository boardLikeRepository;
    
    ...

    @Transactional(readOnly = true)
    public boolean checkBoardLike(Long boardId, User user) {
        // 해당 회원의 좋아요 여부 확인
        return boardLikeRepository.existsByBoardIdAndUserId(boardId, user.getId());
    }

    @Transactional
    public MsgResponseDto saveBoardLike(Long boardId, User user) {
        // 입력 받은 게시글 id와 일치하는 DB 조회
        Board board = boardRepository.findById(boardId).orElseThrow(
                () -> new CustomException(NOT_FOUND_BOARD)
        );

        // 해당 회원의 좋아요 여부를 확인하고 비어있으면 좋아요, 아니면 좋아요 취소
        if (!checkBoardLike(boardId, user)) {
            boardLikeRepository.saveAndFlush(new BoardLike(board, user));
            return new MsgResponseDto("좋아요 완료", HttpStatus.OK.value());
        } else {
            boardLikeRepository.deleteByBoardIdAndUserId(boardId, user.getId());
            return new MsgResponseDto("좋아요 취소", HttpStatus.OK.value());
        }
    }
}

Post 요청임에도 상황에 따라 데이터베이스 insert, delete가 발생할 수 있어 고민이 되었다.
이게 과연 RESTful 한 설계가 맞을까?

기술 매니저님께 조언을 구해 본 결과, 크게 중요한 로직이 아니므로 한 번에 해도 괜찮다는 의견을 주셨다!
좀 더 본질을 바라보고 유연하게 대처할 필요가 있다는 생각이 들었다.

 

 

소셜 로그인의 흐름은 어떻게 될까?

소셜 로그인(카카오)의 흐름

OAuth(Open Standard for Authorization)란?

  • 개방형 Authorization의 표준이며 API 허가(Authorize)를 목적으로 JSON 형식으로 개발된 HTTP 기반의 보안 프로토콜이다.
  • 사용자들이 사용하고자 하는 웹사이트 및 애플리케이션에 비밀번호를 제공하지 않고 접근 권한을 부여받을 수 있게 해주는 공통적 수단으로써 사용되는 기술이다.

 

OAuth flow

  1. 인가 코드로 access token 요청
  2. token으로 카카오 API 호출 → access token으로 ‘카카오 사용자 정보’ 가져오기
  3. 카카오에서 받은 토큰으로 유저 정보 활용하여 필요시 회원가입
  4. 우리 서비스의 전용 JWT 토큰 반환

 

 

dispatcher servlet 디스패처 서블릿이 뭔데

 

Dispatcher-Servlet(디스패처 서블릿) 이란?

  • HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)를 의미한다.

 

Dispatcher-Servlet(디스패처 서블릿)의 장점

  • Spring MVC는 DispatcherServlet이 등장함에 따라 web.xml의 역할을 상당히 축소시켜주었다.
  • dispatcher-servlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리하기 때문에 컨트롤러를 구현해두기만 하면 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임을 해주는 구조가 된다.

 

Dispatcher-Servlet(디스패처 서블릿)의 동작 과정

  1. 클라이언트의 요청을 디스패처 서블릿이 받음
  2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
  3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
  4. 핸들러 어댑터가 컨트롤러로 요청을 위임함
  5. 비즈니스 로직을 처리함
  6. 컨트롤러가 반환 값을 반환함
  7. HandlerAdapter가 반환 값을 처리함
  8. 서버의 응답을 클라이언트로 반환함

 

📚 참고자료

 

[Spring] Dispatcher-Servlet(디스패처 서블릿)이란? 디스패처 서블릿의 개념과 동작 과정

이번에는 servlet의 심화 또는 대표주자인 dispatcher-servlet에 대해서 알아보도록 하겠습니다. 1. Dispatcher-Servlet(디스패처 서블릿)의 개념 [ Dispatcher-Servlet(디스패처 서블릿) 이란? ] 디스패처 서블릿의

mangkyu.tistory.com

 

 

 

오늘의 나는

이제 이번 주차 과제는 어느 정도 마무리가 된 것 같다.
팀 과제였지만 개인적으로도 끝까지 구현한 나에게 칭찬을~

내일은 시험이 없다!
여태까지 진행한 과제에 대해 조원분들과 얘기해보고,
해당 내용들을 정리해서 ReadMe에 적어서 과제를 제출할 예정이다.

이후에 더 추가할만한 기능이 있다면 도전해봐도 좋을 것 같다😎