사실 이전에 예외 상황을 처리하는 코드를 리팩토링했다.
그러나
1. Checked 예외를 만들면 그에 따른 Handler 를 계속 추가해줘야하는 불편함이 있다.
2. 응답이 200 OK 라면 굳이 Body 에 정상 응답이라는 내용을 담아줄 필요는 없다.
3. 서버에서는 정말 필요한 응답값 (에러 상황에 필요한 메시지와 코드) 만 응답해도 좋겠다.
라는 생각을 토대로, 코드를 수정해보기로 했다!
기존 코드
기존 로그인 컨트롤러
@PostMapping
@Operation(summary = "일반 회원 로그인", description = "일반 회원용 로그인")
public ResponseEntity<Response> login(@RequestBody @Valid LoginDto loginDto) throws UserNotFoundException {
Map<Tokens, String> tokens = userService.login(loginDto);
return Response.ok(ResponseCode.OK, tokens);
}
기존 로그인 컨트롤러는
UserNotFoundException 이라는 Checked 예외를 던지고 있고
요청이 제대로 처리됐다면 요청이 정상 처리되었다는 데이터들을 반환한다.
기존 로그인 서비스
@Override
public Map<Tokens, String> login(LoginDto loginDto) throws UserNotFoundException {
String email = loginDto.getEmail();
String password = loginDto.getPassword();
User findUser = userRepository.findByEmailFetchRole(email)
.orElseThrow(() -> new UserNotFoundException(ResponseCode.USER_NOT_FOUND));
String findUserPassword = findUser.getPassword();
boolean pwdMatches = passwordEncoder.matches(password, findUserPassword);
if (!pwdMatches) {
log.error("비밀번호가 안 맞음");
throw new UserNotFoundException(ResponseCode.USER_NOT_FOUND);
}
log.info("아이디 비밀번호 일치 : {}", email);
String accessToken = tokenService.creatToken(findUser.getId(), findUser.getNickname(), findUser.getRole().getRoleName());
String refreshToken = tokenService.createRefreshToken();
Map<Tokens, String> tokenMap = new HashMap<>();
tokenMap.put(Tokens.ACCESS_TOKEN, accessToken);
tokenMap.put(Tokens.REFRESH_TOKEN, refreshToken);
return tokenMap;
}
기존 로그인 서비스에서는 유저가 없거나 비밀번호가 틀렸을 경우 내가 만든 UserNotFoundException(Checked 예외) 를 발생시킨다.
- UserNotFoundException 은 Exception 을 상속받고
- 생성자로 만들어질 때 ResponseCode 를 초기화 시킨다.
그러면 핸들러에는
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<Response> userNotFoundHandler(UserNotFoundException e) {
log.error("유저 에러 : {}", e.getResponseCode().getMessage());
e.printStackTrace();
return Response.badRequest(e.getResponseCode());
}
ResponseCode 의 메시지를 보여주고, ResponseCode 를 Response 객체에 담아준다.
그러면
@Getter
@Builder
public class Response {
private String message;
private HttpStatus status;
private ResponseCode code;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
private Object data;
public static ResponseEntity<Response> ok(ResponseCode responseCode) {
return response(HttpStatus.OK, responseCode);
}
public static ResponseEntity<Response> ok(ResponseCode responseCode, Object data) {
return responseWithData(HttpStatus.OK, data, responseCode);
}
public static ResponseEntity<Response> badRequest(ResponseCode responseCode) {
return response(HttpStatus.BAD_REQUEST, responseCode);
}
public static ResponseEntity<Response> badRequest(ResponseCode responseCode, Object data) {
return responseWithData(HttpStatus.BAD_REQUEST, data, responseCode);
}
private static ResponseEntity<Response> responseWithData(HttpStatus status, Object data, ResponseCode responseCode) {
return ResponseEntity.status(status).body(
Response.builder()
.status(status)
.message(responseCode.getMessage())
.timestamp(LocalDateTime.now())
.data(data)
.code(responseCode)
.build()
);
}
private static ResponseEntity<Response> response(HttpStatus status, ResponseCode responseCode) {
return ResponseEntity.status(status).body(
Response.builder()
.status(status)
.message(responseCode.getMessage())
.timestamp(LocalDateTime.now())
.code(responseCode)
.build()
);
}
}
이런식으로 구성된 Response 가 생기고,
message 와 code 에 ResponseCode 값들이 담겨서 반환되게 되는 것이다.
바뀐 코드
바뀐 코드는
1. 핸들러가 없음.
2. Response 객체 필요 없음.
3. ResponseCode 필요 없음.
바뀐 로그인 서비스
@Override
public Map<Tokens, String> login(LoginDto loginDto) {
String email = loginDto.getEmail();
String password = loginDto.getPassword();
User findUser = userRepository.findByEmailFetchRole(email)
.orElseThrow(() -> new BadRequestException(Exception.USER_NOT_FOUND));
String findUserPassword = findUser.getPassword();
boolean pwdMatches = passwordEncoder.matches(password, findUserPassword);
if (!pwdMatches) {
log.error("비밀번호가 안 맞음");
throw new BadRequestException(Exception.USER_NOT_FOUND);
}
log.info("아이디 비밀번호 일치 : {}", email);
String accessToken = tokenService.creatToken(findUser.getId(), findUser.getNickname(), findUser.getRole().getRoleName());
String refreshToken = tokenService.createRefreshToken();
Map<Tokens, String> tokenMap = new HashMap<>();
tokenMap.put(Tokens.ACCESS_TOKEN, accessToken);
tokenMap.put(Tokens.REFRESH_TOKEN, refreshToken);
return tokenMap;
}
UserNotFoundException(Checked) 을 BadRequestException(UnChecked) 으로 바꿔서 던져준다.
그리고 ResponseCode 대신 Exception 객체를 사용해 에러 코드를 관리하는데
Exception 객체를 자세히 보자(java 제공 Exception 클래스 아님;;)
Exception
@Getter
public enum Exception {
// 가계부
ACCOUNT_BOOK_NOT_FOUND("가계부를 찾을 수 없습니다.", 404),
RECORD_BOOK_NOT_FOUND("내역을 찾을 수 없습니다.", 404),
// 유저
USER_NOT_FOUND("유저를 찾을 수 없습니다.", 404),
// 이메일
EMAIL_NOT_FOUND("이메일을 찾을 수 없습니다.", 404),
EMAIL_CODE_UN_MATCHED("이메일 코드가 다릅니다.", 404),
// OAuth2
SNS_NOT_FOUND("존재하지 않는 SNS입니다.", 404),
OAUTH_PUBLIC_KEY_UN_MATCHED("OAuth2 공개 키가 다릅니다", 500);
private final String message;
private final int code;
Exception(String message, int code){
this.message = message;
this.code = code;
}
}
보며는 message, code 정도를 상태로 둔다.
BadRequestException 을 확인하면
public class BadRequestException extends RuntimeException{
private final int code;
public BadRequestException(Exception exception){
super(exception.getMessage());
code = exception.getCode();
}
}
Exception 객체를 받아 메시지를 RuntimeException 에 넘겨주고 상태 코드정도만 받아둔다.
그러면 예외가 발생했을 때 메시지는 우리가 만든 메시지가 뜨겠죵?
그리고 마지막으로
정상 응답이 됐을 땐, ResponseEntity<Response> 로 응답 상태 + 보내는 객체를 주는 것이 아닌
@PostMapping
@Operation(summary = "일반 회원 로그인", description = "일반 회원용 로그인")
public Map<Tokens, String> login(@RequestBody @Valid LoginDto loginDto) {
return userService.login(loginDto);
}
@PostMapping
@Operation(summary = "일반 회원 가입", description = "일반 회원 가입 완료")
public void signUp(@RequestBody @Valid UserSignUpDto userSignUpDto) {
userService.signUp(userSignUpDto);
}
응답 값이 있을 땐 객체 자체를 반환, 없을 땐 void 로 반환 값이 없게 했다.
어차피 200 OK 라면 굳이 응답 바디에 정상 응답이라는 정보를 넣어주지 않아도 될 것 같았기 때문이당. ㅎㅅㅎ/)
어쨌든 이런 식으로...리팩토링 중이라는 이야기,,,였음니다..
그래서 이렇게 핸들러와 Exception 들을 하나하나 삭제중이란 얘기~
'프로젝트 > 개인 프로젝트) 요만큼' 카테고리의 다른 글
JWT 인증 방법을 수정했다! (0) | 2024.03.15 |
---|---|
요만큼 ) 여전히 개발 진행중 (0) | 2024.03.02 |
refactor ) 요만큼 프로젝트 리팩토링을 하자 ! (0) | 2024.01.18 |
새로운 백엔드 개발자님이 바로? 그리고 JWT ... (1) | 2023.11.07 |
백엔드 개발자가 런했다.. (0) | 2023.11.06 |