TIL
[Spring] Json 응답처리와 예외처리
승무_
2023. 1. 14. 13:06
공통된 포맷으로 Json 리턴하기
1. Json 포맷에 대한 Response class를 정의
@Getter
@AllArgsConstructor
public class Response<T> {
private String resultCode;
private String resultMessage;
private T result;
public static <T> Response<T> success(T result) {
return new Response<T>("SUCCESS", null, result);
}
public static Response<Void> error(String resultCode, String resultMessage) {
return new Response<Void>(resultCode, resultMessage, null);
}
}
2. controller에서, 정의한 Response class를 사용하여 return
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/join")
public Response<UserJoinResponse> join(@RequestBody UserJoinRequest request) {
return Response.success(UserJoinResponse.fromUser(userService.join(request.getName(), request.getPassword())));
}
@PostMapping("/login")
public Response<UserLoginResponse> login(@RequestBody UserLoginRequest request) {
String token = userService.login(request.getName(), request.getPassword());
return Response.success(new UserLoginResponse(token));
}
}
예외 처리
1. 에러코드를 Enum으로 정의
@Getter
@RequiredArgsConstructor
public enum ErrorCode {
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "User not founded"),
INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, "Invalid password"),
DUPLICATED_USER_NAME(HttpStatus.CONFLICT, "Duplicated user name"),
;
private final HttpStatus status;
private final String message;
}
2.
@Getter
@AllArgsConstructor
public class SimpleSnsApplicationException extends RuntimeException {
private ErrorCode errorCode;
private String message;
public SimpleSnsApplicationException(ErrorCode errorCode) {
this.errorCode = errorCode;
this.message = null;
}
@Override
public String getMessage() {
if (message == null) {
return errorCode.getMessage();
} else {
return String.format("%s. %s", errorCode.getMessage(), message);
}
}
}
3. service에서 정의한 예외처리 활용
@Service
@RequiredArgsConstructor
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder encoder;
@Value("${jwt.secret-key}")
private String secretKey;
@Value("${jwt.token.expired-time-ms}")
private Long expiredTimeMs;
@Override
public User loadUserByUsername(String userName) throws UsernameNotFoundException {
return userRepository.findByUserName(userName).map(User::fromEntity).orElseThrow(
() -> new SimpleSnsApplicationException(ErrorCode.USER_NOT_FOUND, String.format("userName is %s", userName))
);
}
public String login(String userName, String password) {
User savedUser = loadUserByUsername(userName);
if (!encoder.matches(password, savedUser.getPassword())) {
throw new SimpleSnsApplicationException(ErrorCode.INVALID_PASSWORD);
}
return JwtTokenUtils.generateAccessToken(userName, secretKey, expiredTimeMs);
}
public User join(String userName, String password) {
// check the userId not exist
userRepository.findByUserName(userName).ifPresent(it -> {
throw new SimpleSnsApplicationException(ErrorCode.DUPLICATED_USER_NAME, String.format("userName is %s", userName));
});
UserEntity savedUser = userRepository.save(UserEntity.of(userName, encoder.encode(password)));
return User.fromEntity(savedUser);
}
}
4.
@RestControllerAdvice
public class GlobalControllerAdvice {
@ExceptionHandler(SimpleSnsApplicationException.class)
public ResponseEntity<?> errorHandler(SimpleSnsApplicationException e) {
return ResponseEntity.status(e.getErrorCode().getStatus())
.body(Response.error(e.getErrorCode().name(), e.getMessage()));
}
}
@ControllerAdvice를 이용해 오류가 발생했을 때 처리할 로직을 한 곳에 모으고
@ExceptionHandler을 이용해 발생한 오류에 대한 세세한 처리
성공일 때, 응답값
{
"resultCode": "SUCEESS",
"result": {
"id": 2,
"userName": "test",
"role": "USER"
}
}
실패일 때, 응답값
{
"resultCode": "DUPLICATED_USER_NAME",
"result": null
}