Spring 숙련 과제(최종)
2022. 12. 21. 23:06ㆍ스파르타 내일배움캠프/Spring 강의 정리
오늘은 미처 하지 못했던 예외 처리와 엔티티 맵핑을 완성하였습니다.
- 예외 처리
- 토큰이 필요한 API 요청에서 토큰을 전달하지 않았거나 정상 토큰이 아닐 때는 "토큰이 유효하지 않습니다." 라는 에러메시지와 statusCode: 400을 Client에 반환하기
- 토큰이 있고, 유효한 토큰이지만 해당 사용자가 작성한 게시글/댓글이 아닌 경우에는 “작성자만 삭제/수정할 수 있습니다.”라는 에러메시지와 statusCode: 400을 Client에 반환하기
- DB에 이미 존재하는 username으로 회원가입을 요청한 경우 "중복된 username 입니다." 라는 에러메시지와 statusCode: 400을 Client에 반환하기
- 로그인 시, 전달된 username과 password 중 맞지 않는 정보가 있다면 "회원을 찾을 수 없습니다."라는 에러메시지와 statusCode: 400을 Client에 반환하기
이 부분을
이렇게 우아하지 못하게 단순한 형식으로 내보냈습니다.
Internal Server Error 가 아닌 내가 지정한 예외 메세지를 내보낼 방법을 찾아야 했습니다.
return new SignupLoginResponseDto("fail", "400");
실패할 때도 예외 메세지 대신에, 이런 식으로 직접 값을 지정해서 내보냈기 때문에 출력되는 값이 한정되었습니다.
이문제를 @RestControllerAdvice 로 해결하였습니다.
package com.nbcamp.myserver.exception;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import static com.nbcamp.myserver.exception.ErrorCode.DUPLICATE_RESOURCE;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = { ConstraintViolationException.class, DataIntegrityViolationException.class})
protected ResponseEntity<ErrorResponse> handleDataException() {
log.error("handleDataException throw Exception : {}", DUPLICATE_RESOURCE);
return ErrorResponse.toResponseEntity(DUPLICATE_RESOURCE);
}
@ExceptionHandler(value = { CustomException.class })
protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
log.error("handleCustomException throw CustomException : {}", e.getErrorCode());
return ErrorResponse.toResponseEntity(e.getErrorCode());
}
}
에러 메시지는 enum 형식으로 지정해두었습니다.
package com.nbcamp.myserver.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.*;
@Getter
@AllArgsConstructor
public enum ErrorCode {
/* 400 BAD_REQUEST : 잘못된 요청 */
INVALID_TOKEN(BAD_REQUEST, "토큰이 유효하지 않습니다."),
/* 401 UNAUTHORIZED : 인증되지 않은 사용자 */
INVALID_AUTH_TOKEN(UNAUTHORIZED, "작성자만 삭제/수정할 수 있습니다."),
/* 404 NOT_FOUND : 회원을 찾을 수 없음 || id 또는 password 오류 */
MEMBER_NOT_FOUND(NOT_FOUND, "회원을 찾을 수 없습니다."),
/* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */
DUPLICATE_RESOURCE(CONFLICT, "중복된 username 입니다."),
;
private final HttpStatus httpStatus;
private final String detail;
}
이렇게 4가지로 지정하였더니 요구사항을 모두 만족하면서 각기 다른 예외 메세지를 드디어 띄울 수 있었습니다.
엔티티 맵핑에서도 다음과 같은 오류 메세지가 발생하였습니다.
"org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [\"FKOH58Q92B96DBFV4TCUNRE9AVL: PUBLIC.BOARD_COMMENTS FOREIGN KEY(COMMENTS_ID) REFERENCES PUBLIC.COMMENT(ID) (CAST(1 AS BIGINT))\" ....
mappedby를 지정하지 않은 오류였습니다.
Board.java 중에서
@OneToMany(mappedBy = "board", cascade = CascadeType.REMOVE)
private List<Comment> comments = new ArrayList<>();
이렇게 mappedBy를 추가해주고, cascade로 연관있는 테이블 행들도 삭제해주어서 해당 오류를 해결하였습니다.
이번 과제도 정말 힘들었지만, 무사히 마무리 되어서 기쁩니다. 다만, Service 부분에서 관리자/유저 인증 부분이 함께 들어 있는 부분을 수정해야 할 것 같습니다.
Controller에서 HttpServlet 관련 부분을 모두 처리하여 좀 더 "객체 지향"적인 코드로 리펙토링 해볼 예정입니다.
'스파르타 내일배움캠프 > Spring 강의 정리' 카테고리의 다른 글
Spring 심화 pjt (0) | 2023.01.03 |
---|---|
Spring Security (0) | 2022.12.26 |
Spring 숙련 과제(2) (0) | 2022.12.20 |
Spring 숙련 과제 (0) | 2022.12.19 |
Spring project (0) | 2022.12.16 |