// 성공
{
"code": 200,
"success": true,
"result": {}
}
// 실패
{
"code": 400,
"message": "Username is Duplication",
"errors": {}
}
API 개발시 어떠한 처리 결과에도 동일한 포맷의 응답을 리턴하는 것은 중요하다.
- 상황에 따라 응답 데이터의 형식이 달라지면 해당 응답을 전달받는 주로 프론트 개발자가 사용하기 어려운 데이터가 될 수 있음
- 따라서 성공했을 때 그리고 실패했을 때 어떤 상황이든 응답형식을 항상 조작하기 쉽도록 json으로 일관성 있게 내려주는 게 좋다.
@Getter
public class CommonResponse<T> {
private final int code;
private final boolean success;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final T result;
@Builder
public CommonResponse(int code, boolean success, T result) {
this.code = code;
this.success = success;
this.result = result;
}
}
public class ApiUtils {
public static <T> CommonResponse<T> success(int code, T result) {
return new CommonResponse<>(code, true, result);
}
}
@GetMapping("/api/contents")
public CommonResponse<List<ContentsResponseDto>> getContents() {
return ApiUtils.success(200, ContentsService.getContents());
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ErrorResponse {
private int code;
private String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<FieldError> errors;
...
}
@ControllerAdvice
어노테이션을 이용해 GlobalExceptionHandler를 만듦 @JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ErrorCode {
// Common
INVALID_INPUT_VALUE(400, " Invalid Input Value"),
METHOD_NOT_ALLOWED(405, " Invalid Input Value"),
ENTITY_NOT_FOUND(400, " Entity Not Found"),
INTERNAL_SERVER_ERROR(500, "Server Error"),
INVALID_TYPE_VALUE(400, " Invalid Type Value"),
// 유저
HANDLE_ACCESS_DENIED(403, "로그인이 필요합니다."),
INVALID_INPUT_USERNAME(400, "닉네임을 3자 이상 입력하세요"),
NOTEQUAL_INPUT_PASSWORD(400, "비밀번호가 일치하지 않습니다"),
INVALID_PASSWORD(400, "비밀번호를 4자 이상 입력하세요"),
INVALID_USERNAME(400, "알파벳 대소문자와 숫자로만 입력하세요"),
NOT_AUTHORIZED(403, "작성자만 수정 및 삭제를 할 수 있습니다."),
USERNAME_DUPLICATION(400, "이미 등록된 아이디입니다."),
LOGIN_INPUT_INVALID(400, "로그인 정보를 다시 확인해주세요."),
NOTFOUND_USER(404, "해당 이름의 유저가 존재하지 않습니다."),
// 게시글
NOTFOUND_POST(404, "해당 게시글이 존재하지 않습니다.")
CONVERTING_FAILED(400, "파일 변환에 실패했습니다."),
;
private final String message;
private final int status;
ErrorCode(final int status, final String message) {
this.status = status;
this.message = message;
}
public String getMessage() {
return this.message;
}
public int getStatus() {
return status;
}
}
@Transactional
public void updatePost(Long id, PostUpdateRequestDto dto, MultipartFile imageFile) {
Post existingPost = exists(id);
...
postRepository.save(existingPost);
}
private Post exists(long id) {
return postRepository.findById(id).orElseThrow(() ->
new EntityNotFoundException(ErrorCode.NOTFOUND_POST));
}
@ExceptionHandler
@ControllerAdvice
@ControllerAdvice 로 모든 컨트롤러에서 발생할 예외를 정의하고, @ExceptionHandler 를 통해 발생하는 예외 마다 처리할 메소드를 정의