Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

우당탕탕 개발일지

[Spring] API 공통 응답 본문

Spring

[Spring] API 공통 응답

YUDENG 2024. 9. 20. 20:45

스프링에서 API 응답 방식으로 보통 ResponseEntity 방식을 사용한다. ResponseEntity는 응답의 상태 코드와 데이터를 포함하지만, 각 API마다 응답 형식이 달라진다. API 공통 응답 구조는 API 개발 및 유지보수의 효율성을 높이며 클라이언트에게도 일관성 있는 응답을 전송하는 것은 중요하다.

 

API 공통 응답 패키지 구조는 다음과 같다.

 

common

  ├─constants

        ├─ ErrorCode
        └─ SuccessCode
  ├─dto

        ├─ ApiErrorResponse
        └─ ApiSuccessResponse
  ├─exceptions

        ├─ GeneralException
        └─ GlobalExceptionHandler
  └─utils

        └─ CommonResponse 

 

1. 응답 코드 작성

 

[ SuccessCode ]

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessCode {
    SUCCESS_SIGNUP(HttpStatus.OK, "회원가입 성공"),
    SUCCESS_SIGNIN(HttpStatus.OK, "로그인 성공"),
    SUCCESS_TOKEN_REFRESH(HttpStatus.OK, "AccessToken 갱신 성공"),
    SUCCESS_LOGOUT(HttpStatus.OK, "로그아웃 성공");

    private final HttpStatus status;
    private final String message;
}

 

[ ErrorCode ]

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum ErrorCode {
    JWT_VERIFICATION(HttpStatus.UNAUTHORIZED, "토큰 검증에 실패하였습니다."),
    INVALID_AUTH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 JWT 토큰입니다."),
    INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 Refresh 토큰입니다."),

    BLACKLISTED_TOKEN(HttpStatus.UNAUTHORIZED, "이미 로그아웃된 계정입니다"),

    USERNAME_ALREADY_EXIST(HttpStatus.CONFLICT, "이미 존재하는 계정입니다."),
    USERNAME_NOT_FOUND(HttpStatus.NOT_FOUND, "해당하는 사용자 계정을 찾을 수 없습니다."),
    USER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당하는 사용자를 찾을 수 없습니다."),
    PASSWORD_MISMATCH(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다."),

    INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "입력 값이 유효하지 않습니다."),
    MISSING_REQUIRED_FIELD(HttpStatus.BAD_REQUEST, "필수 입력 필드가 누락되었습니다."),

    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러입니다. 관리자에게 문의하세요.");

    private final HttpStatus status;
    private final String message;
}

 

API 요청이 성공했을 때와 실패했을 때 일관된 응답 형식을 제공하기 위한 Enum 클래스이다.

HTTP 상태 코드와 응답 메시지를 저장한다.

 

2. 기본 응답 형식 작성

 

[ CommonResponse ]

@Data
public class CommonResponse<T> {
    private final Boolean success;
    private final String message;

    protected CommonResponse(Boolean success, String message) {
        this.success = success;
        this.message = message;
    }

    public static CommonResponse res(Boolean success, SuccessCode status) {
        return new CommonResponse(success, status.getMessage());
    }
}

 

성공 여부와 상태 코드 메시지를 포함한 기본적인 응답 구조를 제공한다. 특별한 데이터가 없는 경우 사용한다.

 

3. 에러 응답 작성

 

[ ApiErrorResponse ]

@Getter
public class ApiErrorResponse extends CommonResponse<String> {

    private final HttpStatus status;

    public ApiErrorResponse(String message, HttpStatus status) {
        super(false, message);
        this.status = status;
    }

    public static ApiErrorResponse res(String message, HttpStatus status) {
        return new ApiErrorResponse(message, status);
    }
}

 

API 요청이 실패했을때, HTTP 상태 코드와 에러 메시지를 반환한다.

 

4. 성공 응답 작성

 

[ ApiSuccessResponse ]

@Getter
public class ApiSuccessResponse<T> extends CommonResponse {

    private final T data;

    private ApiSuccessResponse(String message, T data) {
        super(true, message);
        this.data = data;
    }

    public static<T> ApiSuccessResponse<T> res(SuccessCode status, T data) {
        return new ApiSuccessResponse<>(status.getMessage(), data);
    }
}

 

API 요청이 성공했을때, 데이터를 반환한다.

 

5. 예외 처리 작성

 

[ GeneralException ]

@Getter
public class GeneralException extends RuntimeException{

    private final ErrorCode errorCode;

    public GeneralException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }
}

 

 

[ GlobalExceptionHandler ]

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(GeneralException.class)
    public ApiErrorResponse handleGeneralException(GeneralException e) {
        return new ApiErrorResponse(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(UsernameNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiErrorResponse handleUsernameNotFoundException(UsernameNotFoundException e) {
        return new ApiErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(BadCredentialsException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ApiErrorResponse handleBadCredentialsException(BadCredentialsException e) {
        return new ApiErrorResponse(e.getMessage(), HttpStatus.UNAUTHORIZED);
    }
}

 

728x90