협업하다보면 에러 메세지를 작성하는 방식이 모두 다릅니다.
더하여 리턴할 숫자도 정해주어야 하는데요!
이번에 스프링으로 개발하며 사용했던 에러 통일 방법을 알려드리겠습니다.
먼저 ApiPayload 패키지를 만들어줍니다.
com/
└── sparta/
└── newsfeed/
├── ApiPayload/
│ ├── Code/
│ └──── dto/
| └────── ErrorReasonDto.java
| └────── ReasonDto.java
│ ├──── Status/
│ ├────── ErrorStatus.java
│ ├────── SuccessStatus.java
│ ├──── BaseCode.java
│ └──── BaseErrorCode.java
└──────── ApiResponse.java
파일 경로입니다.
코드를 넣어줍시다.
1. ErrorReasonDto
package com.sparta.nbcampnewsfeed.ApiPayload.Code.dto;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Builder
@Getter
public class ErrorReasonDto {
private String code;
private String message;
private HttpStatus httpStatus;
private Boolean isSuccess;
}
2. ReasonDto
package com.sparta.nbcampnewsfeed.ApiPayload.Code.dto;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Builder
@Getter
public class ReasonDto {
private String code;
private String message;
private HttpStatus httpStatus;
private Boolean isSuccess;
}
3. ErrorStatus
package com.sparta.nbcampnewsfeed.ApiPayload.Code.Status;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.BaseErrorCode;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.dto.ErrorReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "500", "Interval server error"),
_BAD_REQUEST(HttpStatus.BAD_REQUEST,"400","잘못된 요청입니다."),
_BAD_REQUEST_EMAIL(HttpStatus.BAD_REQUEST,"400","중복된 이메일입니다."),
_BAD_REQUEST_USER(HttpStatus.BAD_REQUEST,"400","탈퇴한 회원입니다."),
_BAD_REQUEST_PASSWORD(HttpStatus.BAD_REQUEST,"400","잘못된 비밀번호입니다."),
_BAD_REQUEST_SELF_LIKE(HttpStatus.BAD_REQUEST,"400","자신의 게시물에 좋아요를 남길 수 없습니다."),
_BAD_REQUEST_DOUBLE_LIKE(HttpStatus.BAD_REQUEST,"400","같은 게시물에는 좋아요를 한 번만 남길 수 있습니다."),
_BAD_REQUEST_SAME_PASSWORD(HttpStatus.BAD_REQUEST,"400","이전 비밀번호와 같은 비밀번호로 설정 불가합니다."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"401","인증이 필요합니다."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "403", "금지된 요청입니다."),
_NOT_FOUND(HttpStatus.NOT_FOUND, "404", "존재하지 않는 리소스입니다."),
_NOT_FOUND_USER(HttpStatus.NOT_FOUND, "404", "존재하지 않는 유저입니다."),
_NOT_FOUND_POST(HttpStatus.NOT_FOUND, "404", "존재하지 않는 게시물입니다."),
_NOT_FOUND_COMMENT(HttpStatus.NOT_FOUND, "404", "존재하지 않는 댓글입니다."),
_NOT_FOUND_LIKE(HttpStatus.NOT_FOUND, "404", "존재하지 않는 좋아요입니다."),
// friend 예외처리
_BAD_REQUEST_SELF_FRIEND(HttpStatus.BAD_REQUEST, "400","자기 자신에게 친구 신청을 할 수 없습니다."),
_BAD_REQUEST_FROM_USER(HttpStatus.BAD_REQUEST, "400", "이미 친구이거나 친구 신청을 한 회원입니다."),
_BAD_REQUEST_TO_USER(HttpStatus.BAD_REQUEST, "400", "이미 해당 회원으로부터 친구 신청을 받은 상태입니다."),
_NOT_FOUND_FRIEND(HttpStatus.NOT_FOUND, "404", "해당 회원에게 받은 친구 추가 요청이 없습니다."),
_BAD_REQUEST_ALREADY_FRIEND(HttpStatus.BAD_REQUEST,"400", "이미 친구인 회원입니다."),
_BAD_REQUEST_NOT_FRIEND(HttpStatus.BAD_REQUEST, "400", "친구가 아닌 회원입니다.");
private HttpStatus httpStatus;
private String code;
private String message;
@Override
public ErrorReasonDto getReasonHttpStatus() {
return ErrorReasonDto.builder()
.code(code)
.message(message)
.isSuccess(false)
.httpStatus(httpStatus)
.build();
}
}
이 코드에서 직접 에러에 대해 적을 수 있는데요!
여기서 저는 FriendSerivce 역할을 맡아, friend와 관련된 오류를 추가해주었습니다.
내용에 맞게 반환할 것이 없다면 NOT_FOUND로, 요청이 잘못되었다면 BAD_REQUEST로 작성했습니다!
4. SuccessStatus
package com.sparta.nbcampnewsfeed.ApiPayload.Code.Status;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.BaseCode;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.dto.ReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
@AllArgsConstructor
public enum SuccessStatus implements BaseCode {
_OK(HttpStatus.OK, "200", "Ok");
private HttpStatus httpStatus;
private String code;
private String message;
@Override
public ReasonDto getReasonHttpStatus() {
return ReasonDto.builder()
.code(code)
.message(message)
.isSuccess(true)
.httpStatus(httpStatus)
.build();
}
}
5. BaseCode
package com.sparta.nbcampnewsfeed.ApiPayload.Code;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.dto.ReasonDto;
public interface BaseCode {
public ReasonDto getReasonHttpStatus();
}
6. BaseErrorCode
package com.sparta.nbcampnewsfeed.ApiPayload.Code;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.dto.ErrorReasonDto;
public interface BaseErrorCode {
public ErrorReasonDto getReasonHttpStatus();
}
7. ApiResponse
package com.sparta.nbcampnewsfeed.ApiPayload;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.BaseCode;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.BaseErrorCode;
import com.sparta.nbcampnewsfeed.ApiPayload.Code.Status.SuccessStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class ApiResponse<T> {
@JsonProperty("isSuccess")
private final Boolean isSuccess;
private final String code;
private final String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final T result;
// 일반적인 응답 생성
public static <T> ApiResponse<T> onSuccess(T result) {
return new ApiResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result);
}
public static <T> ApiResponse<T> of(BaseCode code, T result) {
return new ApiResponse<>(true, code.getReasonHttpStatus().getCode(), code.getReasonHttpStatus().getMessage(), result);
}
// 실패한 경우 응답 생성
public static <T> ApiResponse<T> onFailure(String code, String message, T data) {
return new ApiResponse<>(false, code, message, data);
}
public static ApiResponse<String> onFailure(BaseErrorCode baseErrorCode) {
return new ApiResponse<>(false, baseErrorCode.getReasonHttpStatus().getCode(), baseErrorCode.getReasonHttpStatus().getMessage(), "null");
}
}
이 코드들을 추가하여 실제로 원래 Serivce에 있던 예외들을 다 통일하여 다시 처리했습니다.
보기 좋게 깃으로 보여드릴게요!
ErrorStatus에서 원하는 에러를 추가해준 다음, 원래 있던 코드를 삭제하고 ApiException을 이용하여 만들어주었던 코드를 입력합니다!
이 방법을 통해 팀원들끼리 오류 메시지를 다 맞춰서 높은 디테일의 프로젝트를 가져갈 수 있었습니다!
'Spring' 카테고리의 다른 글
[Spring] AOP 관점지향 프로그래밍 | 횡단 관심사 분리 (0) | 2024.09.10 |
---|---|
[Spring] 단위 테스트 | 테스트 코드 작성법 (0) | 2024.09.10 |
[Spring] JWT 인증 처리 로직 클래스 설계 (0) | 2024.08.29 |
[Spring] Filter | Filter Interface (1) | 2024.08.29 |
[Spring] 1대N 관계 | N대1 관계 | N대M | 양방향, 단방향 관계 정리 (0) | 2024.08.28 |