공통 응답폼?
프론트엔드 입장에서 어떤 API 테스트를 하던지 공통 응답을 받는다면, 응답 받은 내용을 좀 더 쉽게 이해할 수 있다고 생각하기 때문이다. 또한 백엔드끼리의 협업시에도 다른 API테스트를 할때도, 어디서 에러가 생긴건지도 쉽게 파악할 수 있게 하고 싶기 때문이다.
목적은 공통 응답폼으로 빠르게 응답을 이해하여 협업을 좀 더 쉽게하기 위함!
Generic으로 공통 응답폼 만들기
제네릭은 클래스, 인터페이스, 메서드를 정의할 때 타입을 매개변수화하는 방법을 제공! 즉, 타입을 필요에 따라 바꿀 수 있다는 것.
제네릭을 사용하면 클래스나 메서드를 선언할 때 구체적인 타입을 지정하지 않고, 사용할 때 타입을 결정해 코드의 재사용성을 높이고, 컴파일 시 타입 안전성을 보장!
타입의 안전성은 언제나 중요하다! 자바스크립트의 장점이자 단점인 타입의 유연하다는 점..그래서 typescript가 있지 않는가! ㅎㅎ
무튼 Generic을 활용해 공통 응답폼을 만들것이다!
절차
@Getter
@RequiredArgsConstructor
public class CommonResponse<T> {
private final Integer status;
private final String message;
private final T data;
public static <T> CommonResponse<T> success(T data) {
return new CommonResponse<>(200, "Success", data);
}
public static <T> CommonResponse<T> error(int status, String message) {
return new CommonResponse<>(status, message, null);
}
}
컨트롤러
@PostMapping("/login")
public CommonResponse<String> login(@RequestBody Login login , HttpSession session) {
return CommonResponse.success(userService.login(login,session));
}
서비스
@Transactional(readOnly = true)
public String login(Login login, HttpSession session) {
User user = userRepository.findByEmail(login.getEmail());
if(user == null || !user.isPasswordMatch(passwordEncoder,login.getPassword()))
throw new UserException(UserErrorCode.FAIL_TO_LOGIN);
session.setAttribute("user",user);
return session.getId();
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CommonException.class)
public CommonResponse<String> handleCommonException(CommonException commonException) {
return CommonResponse.error(commonException.getErrorCode(),commonException.getMessage());
}
@ExceptionHandler(UserException.class)
public CommonResponse<String> handleUserException(UserException userException) {
return CommonResponse.error(userException.getErrorCode(),userException.getErrorMessage());
}
}
예를 들어 로그인시 이메일이나 비밀번호가 틀렸다면
public String logout(HttpSession session,User user) {
if(user == null) throw new UserException(UserErrorCode.NOT_EXIST_USER);
log.info("로그아웃된 사용자 이메일 : {}", user.getEmail());
session.invalidate();
return "로그아웃 되었습니다.";
}
이런식으로 깔끔하게 데이터들을 내려준다. 물론 서버의 응답은 항상 200이다. 하지만 응답 내용은 다르다는 것! 이렇게 하는 이유는 프론트 개발자 분들이 테스트할때, 첫번째로 서버에 요청을 보내면 API가 작동하는지 여부를 확실하게 알 수 있기 때문이다. 서버에 요청이 오는 것부터 확인 되어야 다음 작업을 할 수 있다고 생각했기 때문에 공통 응답폼을 만들어서 모든 응답을 200으로 내려주고 그 안에 에러 코드의 상태와 메세지를 넣는 방식을 택했다.
사소한(?) 트러블 슈팅 - 매개변수 인식 문제 -
@ExceptionHandler(UserException.class)
public CommonResponse<String> handleUserException(UserException userException) {
return CommonResponse.error(userException.getErrorCode(),userException.getErrorMessage());
}
런타입 에러가 나면 해당 ErrorMessage가 생성되어
public static <T> CommonResponse<T> error(int status, String message) {
return new CommonResponse<>(status, message, null);
}
메세지를 매개변수로 받아서 응답하는건데, 메세지가 자꾸 null 이 생겼다. 즉 매개변수로 받지 못하는 상황이 생겼는데, getErrorMessage() 대신 순수하게 "테스트" 라고 넣고 다시 실행보니 되었다. 그 후 다시 "테스트" 삭제 후 getErrorMessage()을 넣으니 제대로 인식이 되었다. 이런 문제는 또 처음 겪어보는거라서, 신기하면서 당황했다..? 혹시라도 매개변수가 안되면 순수한 매개변수 자체를 넣어보고 다시 시도해보는 것도 어쩌면 해결책이 될수도 있을것 같다