Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feature: 전체 예외 처리, 로그 처리 #387

Merged
merged 32 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2d4bc90
feat: auth, global exception 추가
wugawuga Aug 10, 2023
e7263fb
feat: memberNotFound exception 추가
wugawuga Aug 10, 2023
f870bca
feat: bean validation 의존성 추가
wugawuga Aug 10, 2023
c153ca3
feat: Member api 예외 처리, 로그 추가
wugawuga Aug 10, 2023
3079853
feat: Category 관련 예외 처리, 로그 추가
wugawuga Aug 10, 2023
7d90622
refactor: bad request -> not found 로 수정
wugawuga Aug 10, 2023
b06f423
feat: Product 관련 예외 처리, 로그 추가
wugawuga Aug 10, 2023
ec02b6c
refactor: RecipeService 커스텀 예외로 수정
wugawuga Aug 10, 2023
3519184
refactor: 패키지명 수정
wugawuga Aug 10, 2023
113f9f1
feat: Review 관련 예외 처리, 로그 추가
wugawuga Aug 10, 2023
48ae9f8
feat: 예외 발생 지점 로그 추가
wugawuga Aug 10, 2023
7de6bde
feat: 카테고리 타입 예외 처리
wugawuga Aug 11, 2023
d375511
feat: categoryType 대소문자 구분 안하고 비교하게 수정
wugawuga Aug 11, 2023
5a7226f
refactor: 테스트 커스텀 예외로 수정
wugawuga Aug 11, 2023
8e475e3
Merge branch 'develop' into feat/issue-367
wugawuga Aug 11, 2023
4cd0023
Merge branch 'develop' into feat/issue-367
wugawuga Aug 11, 2023
8cd8781
fix: custom 예외로 test 통과하게 수정
wugawuga Aug 11, 2023
6e7a9f4
feat: MemberController 실패 테스트 추가
wugawuga Aug 11, 2023
8333ba3
refactor: 카테고리 목록 조회 파라미터 수정
wugawuga Aug 11, 2023
700d547
feat: 카테고리 목록 조회 예외 테스트 추가
wugawuga Aug 11, 2023
c4a217d
feat: 상품 정렬 예외 테스트 추가
wugawuga Aug 11, 2023
c896a74
style: 코드 컨벤션 수정
wugawuga Aug 11, 2023
7afc0cd
feat: 상품 조회 예외 테스트 추가
wugawuga Aug 11, 2023
7285a22
feat: Recipe 예외 추가
wugawuga Aug 11, 2023
dc747d1
refactor: static 제거
wugawuga Aug 11, 2023
c9f5bbb
feat: 꿀조합 작성, 조회 예외 테스트 추가
wugawuga Aug 11, 2023
6a39e2a
refactor: test 메소드 분리
wugawuga Aug 11, 2023
dbc42b0
feat: 리뷰 작성, 조회 예외 테스트 추가
wugawuga Aug 12, 2023
35a6f8b
refactor: 컨벤션 맞게 수정, 검증 추가
wugawuga Aug 12, 2023
4df248f
feat: review, recipe content 검증 테스트 추가
wugawuga Aug 12, 2023
23884d7
refactor: 오타 수정, api 명세에 따라 코드 변경
wugawuga Aug 12, 2023
c16e323
refactor: 승인되지 않음 -> 인증되지 않음으로 수정
wugawuga Aug 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public AuthService(final MemberService memberService, final PlatformUserProvider

public SignUserDto loginWithKakao(final String code) {
final UserInfoDto userInfoDto = platformUserProvider.getPlatformUser(code);
final SignUserDto signUserDto = memberService.findOrCreateMember(userInfoDto);
return signUserDto;
return memberService.findOrCreateMember(userInfoDto);
}

public String getLoginRedirectUri() {
Expand Down
31 changes: 31 additions & 0 deletions backend/src/main/java/com/funeat/auth/exception/AuthErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.funeat.auth.exception;

import org.springframework.http.HttpStatus;

public enum AuthErrorCode {

LOGIN_MEMBER_NOT_FOUND(HttpStatus.UNAUTHORIZED, "로그인 하지 않은 회원입니다. 로그인을 해주세요.", "6001"),
;

private final HttpStatus status;
private final String message;
private final String code;

AuthErrorCode(final HttpStatus status, final String message, final String code) {
this.status = status;
this.message = message;
this.code = code;
}

public HttpStatus getStatus() {
return status;
}

public String getMessage() {
return message;
}

public String getCode() {
return code;
}
}
18 changes: 18 additions & 0 deletions backend/src/main/java/com/funeat/auth/exception/AuthException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.funeat.auth.exception;

import com.funeat.exception.ErrorCode;
import com.funeat.exception.GlobalException;
import org.springframework.http.HttpStatus;

public class AuthException extends GlobalException {

public AuthException(final HttpStatus status, final ErrorCode errorCode) {
super(status, errorCode);
}

public static class NotLoggedInException extends AuthException {
public NotLoggedInException(final AuthErrorCode errorCode) {
super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage()));
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.funeat.auth.util;

import com.funeat.auth.exception.LoginException;
import com.funeat.auth.exception.AuthErrorCode;
import com.funeat.auth.exception.AuthException.NotLoggedInException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
Expand All @@ -14,7 +15,7 @@ public class AuthHandlerInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
final HttpSession session = request.getSession();
if (session.getAttribute("member") == null) {
throw new LoginException("login error");
throw new NotLoggedInException(AuthErrorCode.LOGIN_MEMBER_NOT_FOUND);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ public class StringToCategoryTypeConverter implements Converter<String, Category

@Override
public CategoryType convert(final String source) {
return CategoryType.valueOf(source.toUpperCase());
return CategoryType.findCategoryType(source);
}
}
2 changes: 1 addition & 1 deletion backend/src/main/java/com/funeat/common/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.funeat.auth.util.AuthArgumentResolver;
import com.funeat.auth.util.AuthHandlerInterceptor;
import com.funeat.recipe.utill.RecipeHandlerInterceptor;
import com.funeat.recipe.util.RecipeHandlerInterceptor;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
Expand Down
32 changes: 32 additions & 0 deletions backend/src/main/java/com/funeat/exception/CommonErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.funeat.exception;

import org.springframework.http.HttpStatus;

public enum CommonErrorCode {

UNKNOWN_SERVER_ERROR_CODE(HttpStatus.INTERNAL_SERVER_ERROR, "알 수 없는 에러입니다.", "0000"),
REQUEST_VALID_ERROR_CODE(HttpStatus.BAD_REQUEST, "요청을 다시 확인해주세요.", "0001"),
;

private final HttpStatus status;
private final String message;
private final String code;

CommonErrorCode(final HttpStatus status, final String message, final String code) {
this.status = status;
this.message = message;
this.code = code;
}

public HttpStatus getStatus() {
return status;
}

public String getMessage() {
return message;
}

public String getCode() {
return code;
}
}
34 changes: 34 additions & 0 deletions backend/src/main/java/com/funeat/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.funeat.exception;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorCode<T> {

private final String code;
private final String message;
private T info;

public ErrorCode(final String code, final String message, final T info) {
this.code = code;
this.message = message;
this.info = info;
}

public ErrorCode(final String code, final String message) {
this.code = code;
this.message = message;
}

public String getCode() {
return code;
}

public String getMessage() {
return message;
}

public T getInfo() {
return info;
}
}
23 changes: 23 additions & 0 deletions backend/src/main/java/com/funeat/exception/GlobalException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.funeat.exception;

import org.springframework.http.HttpStatus;

public class GlobalException extends RuntimeException {

private final HttpStatus status;
private final ErrorCode errorCode;

public GlobalException(final HttpStatus status, final ErrorCode errorCode) {
super(errorCode.getMessage());
this.status = status;
this.errorCode = errorCode;
}

public HttpStatus getStatus() {
return status;
}

public ErrorCode getErrorCode() {
return errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,102 @@
package com.funeat.exception.presentation;

import com.funeat.auth.exception.LoginException;
import static com.funeat.exception.CommonErrorCode.REQUEST_VALID_ERROR_CODE;
import static com.funeat.exception.CommonErrorCode.UNKNOWN_SERVER_ERROR_CODE;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.funeat.exception.ErrorCode;
import com.funeat.exception.GlobalException;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@RestControllerAdvice
public class GlobalControllerAdvice {

private final Logger log = LoggerFactory.getLogger(this.getClass());
private final ObjectMapper objectMapper;

public GlobalControllerAdvice(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@ExceptionHandler({MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class})
public ResponseEntity<?> handleParamValidationException(final Exception e, final HttpServletRequest request) {
log.warn("{} = {}, code = {} message = {}", request.getMethod(), request.getRequestURI(),
REQUEST_VALID_ERROR_CODE.getCode(), e.getMessage());

final ErrorCode<?> errorCode = new ErrorCode<>(REQUEST_VALID_ERROR_CODE.getCode(),
REQUEST_VALID_ERROR_CODE.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorCode);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleParamValidationException(final MethodArgumentNotValidException e,
final HttpServletRequest request) {
final String filedErrorLogMessages = getMethodArgumentExceptionLogMessage(e);

final String errorMessage = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "));

final String responseErrorMessage = errorMessage + ". " + REQUEST_VALID_ERROR_CODE.getMessage();

final ErrorCode<?> errorCode = new ErrorCode<>(REQUEST_VALID_ERROR_CODE.getCode(), responseErrorMessage);

@ExceptionHandler(LoginException.class)
public ResponseEntity<?> loginExceptionHandler(final LoginException e, final HttpServletRequest request) {
log.warn("{} = {}, message = {} ", request.getMethod(), request.getRequestURI(),
filedErrorLogMessages);
return ResponseEntity.status(REQUEST_VALID_ERROR_CODE.getStatus()).body(errorCode);
}

private static String getMethodArgumentExceptionLogMessage(final MethodArgumentNotValidException e) {
final String filedErrorMessages = e.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getField)
.collect(Collectors.joining(", "));

return filedErrorMessages + " 요청 실패";
}

@ExceptionHandler(GlobalException.class)
public ResponseEntity<?> handleGlobalException(final GlobalException e, final HttpServletRequest request)
throws JsonProcessingException {
final String exceptionSource = getExceptionSource(e);
log.warn("source = {} , {} = {} code = {} message = {} info = {}", exceptionSource, request.getMethod(),
request.getRequestURI(), e.getErrorCode().getCode(), e.getErrorCode().getMessage(),
objectMapper.writeValueAsString(e.getErrorCode().getInfo()));

final ErrorCode<?> errorCode = new ErrorCode<>(e.getErrorCode().getCode(), e.getMessage());
return ResponseEntity.status(e.getStatus()).body(errorCode);
}

private String getExceptionSource(final Exception e) {
final StackTraceElement[] stackTrace = e.getStackTrace();
if (stackTrace.length > 0) {
return stackTrace[0].toString();
}
return "Unknown location";
}

log.warn("URI: {}, 쿠키값: {}, 저장된 JSESSIONID 값: {}", request.getRequestURI(), request.getHeader("Cookie"),
request.getSession().getId());
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleServerException(final Exception e) {
log.error("", e);

return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
final ErrorCode<?> errorCode = new ErrorCode<>(UNKNOWN_SERVER_ERROR_CODE.getCode(),
UNKNOWN_SERVER_ERROR_CODE.getMessage());
return ResponseEntity.status(UNKNOWN_SERVER_ERROR_CODE.getStatus()).body(errorCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.funeat.member.domain.Member;
import com.funeat.member.dto.MemberProfileResponse;
import com.funeat.member.dto.MemberRequest;
import com.funeat.member.exception.MemberErrorCode;
import com.funeat.member.exception.MemberException.MemberNotFoundException;
import com.funeat.member.persistence.MemberRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -39,15 +41,15 @@ private SignUserDto save(final UserInfoDto userInfoDto) {

public MemberProfileResponse getMemberProfile(final Long memberId) {
final Member findMember = memberRepository.findById(memberId)
.orElseThrow(IllegalArgumentException::new);
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId));

return MemberProfileResponse.toResponse(findMember);
}

@Transactional
public void modify(final Long memberId, final MemberRequest request) {
final Member findMember = memberRepository.findById(memberId)
.orElseThrow(IllegalArgumentException::new);
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId));

final String nickname = request.getNickname();
final String profileImage = request.getProfileImage();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.funeat.member.dto;

import javax.validation.constraints.NotBlank;

public class MemberRequest {

@NotBlank(message = "닉네임을 확인해주세요")
private final String nickname;

@NotBlank(message = "프로필 이미지를 확인해주세요")
private final String profileImage;

public MemberRequest(final String nickname, final String profileImage) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.funeat.member.exception;

import org.springframework.http.HttpStatus;

public enum MemberErrorCode {

MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다. 회원 id를 확인하세요.", "5001"),
;

private final HttpStatus status;
private final String message;
private final String code;

MemberErrorCode(final HttpStatus status, final String message, final String code) {
this.status = status;
this.message = message;
this.code = code;
}

public HttpStatus getStatus() {
return status;
}

public String getMessage() {
return message;
}

public String getCode() {
return code;
}
}
Loading
Loading