[Spring Boot] 클라이언트 REST API 응답보내기
이번 글에서는 서버가 클라이언트에게 응답을 할 때 JSON 형태로 주는 것을 정리해보려 한다. 간단히 포스트맨으로 로그인 요청을 했을 때 로그인 성공, 실패의 응답을 주는 예제를 정리해보자. (HTTP 상태코드, 비밀번호 암호화, JWT에 대해서는 정리하지 않겠다.)
HTTP 상태코드는 아래링크를 참고하자.
https://developer.mozilla.org/ko/docs/Web/HTTP/Status
먼저 최근에 클라이언트에게 응답을 할 때는 아래와 같은 JSON 형태로 주는 것이 일반적이다.
{
"status": 200,
"message": "로그인 성공",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWRIjoib3VyLXNvcHQbpXkxZgFXHw"
}
}
HTTP 상태코드, 응답메세지, 클라이언트에게 보내줘야 할 data가 있다면 같이 보내주게 된다. 따라서 위와 같은 형식으로 로그인 요청이 왔을 때 응답을 주면 된다.
먼저 상태코드를 담을 클래스를 하나 만들어보자.
public class StatusCode {
public static final int OK = 200;
public static final int CREATED = 201;
public static final int NO_CONTENT = 204;
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_SERVER_ERROR = 500;
public static final int SERVICE_UNAVAILABLE = 503;
public static final int DB_ERROR = 600;
}
상태코드는 다양하게 있기 때문에 필요한 상태코드가 있다면 추가해서 사용하면 된다.
public class ResponseMessage {
public static final String LOGIN_SUCCESS = "로그인 성공";
public static final String LOGIN_FAIL = "로그인 실패";
public static final String READ_USER = "회원 정보 조회 성공";
public static final String NOT_FOUND_USER = "회원을 찾을 수 없습니다.";
public static final String CREATED_USER = "회원 가입 성공";
public static final String UPDATE_USER = "회원 정보 수정 성공";
public static final String DELETE_USER = "회원 탈퇴 성공";
public static final String INTERNAL_SERVER_ERROR = "서버 내부 에러";
public static final String DB_ERROR = "데이터베이스 에러";
}
그리고 이번에는 로그인 or 회원가입을 하였을 때 어떠한 응답메세지를 보내줄 것인지는 상황에 맞게 추가해주면 된다.
@Data
public class LoginReq {
private String name;
private String password;
}
그리고 로그인할 때 이름과 비밀번호로 받을 것이고, 로그인 정보를 담을 클래스도 하나 만들었다.
@Data
@AllArgsConstructor
@Builder
public class DefaultRes<T> {
private int statusCode;
private String responseMessage;
private T data;
public DefaultRes(final int statusCode, final String responseMessage) {
this.statusCode = statusCode;
this.responseMessage = responseMessage;
this.data = null;
}
public static<T> DefaultRes<T> res(final int statusCode, final String responseMessage) {
return res(statusCode, responseMessage, null);
}
public static<T> DefaultRes<T> res(final int statusCode, final String responseMessage, final T t) {
return DefaultRes.<T>builder()
.data(t)
.statusCode(statusCode)
.responseMessage(responseMessage)
.build();
}
}
위에서 설명했던 것처럼 상태코드, 응답메세지, 데이터로 형식을 갖춰서 클라이언트에게 응답을 해주기 위해서 DefaultRes 클래스를 만들었다. (Builder는 무엇인가?)
- ResponseEntity의 진짜 상태코드는 DefaultRes의 status에 넣어준다.
- 모든 응답메세지에 한국말로 넣어준다.
- 응답 데이터가 있다면 제네릭 타입을 이용해서 데이터에 넣어준다.
보내줄 데이터가 없다면 위의 매게변수가 2개인 생성자를 통해서 data = null로 초기화 해주는 것을 알 수 있다.
@RestController
@RequestMapping("/users")
public class TestController {
@PostMapping("login")
public ResponseEntity login(@RequestBody LoginReq loginReq) {
return new ResponseEntity(DefaultRes.res(StatusCode.OK, ResponseMessage.LOGIN_SUCCESS, loginReq), HttpStatus.OK);
}
}
위와 같이 PostMan을 이용해서 Server에게 요청을 보냈을 때 응답이 잘 오는 것을 확인할 수 있다. 실제로는 위와같이 아이디, 비밀번호를 다시 돌려주는 일은 절대로 없겠지만 예제이기 때문에 한번 해보았다.
ResponseEntity에 넣는 상태코드와 DefaultRes의 상태코드는 어떤 차이가 있을까?
- 클라이언트(Android/iOS)는 서버로부터 받은 응답 데이터를 파싱해 객체에 저장한다. (파싱형식이 다를 경우 힘들어진다)
- 상태코드에 따라 때로는 ResponseEntity가 아예 전송되지 않는 상황도 존재한다.
- 응답 메세지를 넣어주는 것을 통해 클라이언트가 에러 찾기도 편하고 상호간의 의사소통이 수월해진다.