💡 발단
기존에는 HTTP code(2xx, 3xx, 4xx)와 메세지로 서버의 응답을 표현하는 것이 무조건 옳다고 생각했다.
표준을 따른다면 웹 API에서는 이 방식을 따르는게 맞다.
그런데, HTTP의 40여개 코드로는 REST API의 모든 분기를 처리하기에 너무 적다.
예를 들면 권한 처리가 있다.
정지된 유저가 요청을 할 때와, 글쓴이가 아닌 유저의 글 삭제 요청에 대한 응답 코드는 모두 403(not Authorized) 이다.
// 403 에러
{
"message" : "banned user."
}
// 403 에러
{
"message" : "not author of this content."
}
문제는, 403으로 표현할 수 있는 가짓수가 너무 적다는거.
그래서 여러가지 분기를 표현하려면 message를 길게 나열하게 된다.
그래서 나온게 JSend라고 하는 비표준 방식이다.
아예 HTTP code를 무시하자는 방식이다.
모든 응답 HTTP code를 200으로 고정하고, JSON 내에서 success, fail, error로 분류한다.
그리고 나머지 JSON body에서 상세 내용을 알려준다.
// 200
{
"status" : "fail",
"message" : "not author of this content."
}
// 200
{
"status" : "fail",
"message" : "banned user."
}
그러나 나는 JSend 방식은 HTTP 방식보다 못하면 못했지, 더 좋은 방식이 아니라고 생각했다.
❗ HTTP + JSend 방식의 status
HTTP 방식을 존중하되, 팀원간 컨벤션을 만들어서 다양한 분기를 표현하면 된다!
JSend 방식의 "status"를 더 구체적으로 적는 방법을 쓰면 된다.
// 403
{
"status" : "NOT_AUTHOR"
}
// 403
{
"status" : "BANNED_USER"
}
이렇게 하면 권한이 없다는 것(403 Not Authorized)도 표현하고, 구체적으로 어떤 권한이 없는지도 표현할 수 있다.
우린 이걸 더 응용해서, 상수 숫자로 모든 분기를 정리하기로 했다.
HTTP 방식은 표준은 지키지만 JSON body의 활용도가 떨어지고, JSend 방식은 JSON body의 활용도가 높지만 HTTP 표준에서 너무 떨어져있다.
가장 크게 느낀 불편함은 클라이언트에서 분기 처리하기가 어렵다는 것이었다.
그리고 문자열을 매번 쓰는 것도 번거롭다.
각 분기에 대해 숫자를 미리 약속해서, 서버와 클라이언트 양쪽에서 미리 상수화를 해놓으면 어떨까?
❗자체 status code 만들기
그래서 팀원들과 컨벤션을 만들어서, 우리 프로젝트만의 자체 status code를 만들기로 했다. (플럽 프로젝트 코드 참고)
// 403
{
"status_code" : 9010,
"message" : "not author of this content."
}
// 403
{
"status_code" : 9050,
"message" : "not author of this content."
}
그리고 ENUM으로 상수화했다.
필드로는 http code, 커스텀 status code, 메세지를 담았다.
public enum StatusCode {
/**
* Common
*/
INTERNAL_SERVER_ERROR(500, -1, "internal server error."),
COMMON_BAD_REQUEST(400, 9000, ""),
INVALID_INPUT_VALUE(400, 9010, "invalid input value."),
METHOD_NOT_ALLOWED(405, 9020, "method not allowed."),
HTTP_CLIENT_ERROR(400, 9030, "http client error."),
INVALID_REQUEST_PARAM(400, 9100, "invalid request param."),
NOT_FOUND_URL(404, 9110, "not found url request"),
// ...
private final int HttpCode;
private final int statusCode;
private final String message;
}
이렇게 하면 서버와 클라이언트 양측의 휴먼 에러를 최소화할 수 있다.
+ 커스텀 Exception을 만들어서 사용한다면 더 시너지를 낼 수 있다.
public Location getLocation(Long locationId) {
return locationRepository.findById(locationId)
.orElseThrow(() -> new BuzException(StatusCode.NOT_FOUND_LOCATION));
}
public class BuzException extends RuntimeException {
public StatusCode statusCode;
public BuzException(StatusCode statusCode) {
super(statusCode.getMessage());
this.statusCode=statusCode;
}
참고:
https://wildeveloperetrain.tistory.com/240
https://github.com/PLUB2022/PLUB-Server
'Back-end > 협업' 카테고리의 다른 글
[백엔드의 협업] 기획 단계의 내용들을 개발 초기에 상수화하기 (0) | 2023.09.10 |
---|