client패키지에 WeatherDto의 가독성과 유지보수을 위해 생성자를 지우고 어노테이션 추가
단순 생성자 반복을 줄이기 위해 두 가지 방법을 고민했습니다.
방법 1. Record 도입
방법 2. @RequiredArgsConstructor 활용
기존 Lombok라이브러리를 활용해 어토네이션 한 줄로 간단히 해결
→ 제가 익숙한 방법을 활용하는 편이 좋을 것 같아 방법 2. 를 선택했습니다.
final키워드가 붙은 필드를 모아 생성자를 자동으로 생성해주는 어노테이션 @RequiredArgsConstructor을 적용하여 기존에 있던 생성자 코드를 대체했습니다.
개선 전 코드
@Getter
public class WeatherDto {
private final String date;
private final String weather;
public WeatherDto(String date, String weather) {
this.date = date;
this.weather = weather;
}
}
개선 후 코드
@Getter
@RequiredArgsConstructor
public class WeatherDto {
private final String date;
private final String weather;
}
불변으로 다루는 필드는 @RequiredArgsConstructor와 @Getter 조합으로 우선시 하고 나중에 좀 더 Record에 대해 공부해서 Record방법을 사용하는 방식으로 하고자 합니다.
단순히 코드가 줄어든 것이 아니라 가독성이 좋아졌고 유지보수가 크게 개선되었습니다.
전 코드에서는 필드가 늘어나면 그때마다 생성자 파라미터와 초기화 코드를 일일이 수정해야 하는 번거로움이 있었습니다. 이는 개발자의 실수로 이어질 가능성이 있고 가독성이 줄어듭니다.
코드를 바꾼 후 자동으로 생성자를 주입해주니 개발자의 실수도 줄어들고 가독성과 유지보수가 좋아졌습니다.
→ 불변 필드가 있는 요청 DTO에 생성자 삭제 후 어노테이션 추가
로그인(signin)로직을 검토하던 중, 인증 실패 상황에서 다른 타입의 예외가 발생하는 것을 발견했습니다.
InvalidRequestException 발생AuthException 발생로그인 과정에서 발생하는 인증 실패 예외들을 로그인 실패라는 비즈니스에 맞춰 일관되게 AuthException(401 Unauthorized)로 예외처리를 했습니다.
가입되지 않은 유저를 조회할 때 던지는 예외를 InvalidRequestExcption에서 AuthException으로 변경했습니다.
@Transactional(readOnly = true)
public SigninResponse signin(SigninRequest signinRequest) {
User user = userRepository.findByEmail(signinRequest.getEmail()).orElseThrow(
() -> new InvalidRequestException("가입되지 않은 유저입니다."));
if (!passwordEncoder.matches(signinRequest.getPassword(), user.getPassword())) {
throw new AuthException("잘못된 비밀번호입니다.");
}
String bearerToken = jwtUtil.createToken(user.getId(), user.getEmail(), user.getUserRole());
return new SigninResponse(bearerToken);
}
개선 후 코드
@Transactional(readOnly = true)
public SigninResponse signin(SigninRequest signinRequest) {
User user = userRepository.findByEmail(signinRequest.getEmail()).orElseThrow(
() -> new AuthException("가입되지 않은 유저입니다."));
if (!passwordEncoder.matches(signinRequest.getPassword(), user.getPassword())) {
throw new AuthException("잘못된 비밀번호입니다.");
}
String bearerToken = jwtUtil.createToken(user.getId(), user.getEmail(), user.getUserRole());
return new SigninResponse(bearerToken);
}
이 예외가 이 비즈니스 로직에 적절한 타입인가?를 고민했습니다. 나쁜 사용자가 예외를 보고 나쁜 짓을 안하게 에러 처리를 일관되게 설계하고 메시지를 "잘못된 비번입니다."라고 하기보다는 "이메일 혹은 비번이 일치하지 않습니다."와 같이 하는 쪽이 보안에 좋을 것 같다는 생각을 했습니다.
HTTP 상태 코드 401 Unauthorized로 일관 반환합니다.
전에는 이메일 오류 시 400 Bad Request / 비번 오류 시 401 Unauthorized 반환했습니다.
예외 핸들링을 InvalidRequestException와 AuthException 두개를 해야 하나 싶었지만 수정 후 AuthException 이거 하나만 하면 됩니다.
또 같은 이유로 회원가입 시에도 InvalidRequestException을 AuthException 로 수정
getComments메서드에 for문을 Stream으로 수정하여 가독성을 높였습니다.
가독성이 좋게 Stream으로 선택하였습니다.
Stream의 map과 toList()를 사용하였습니다.
개선 전 코드
@Transactional(readOnly = true)
public List<CommentResponse> getComments(long todoId) {
List<Comment> commentList = commentRepository.findByTodoIdWithUser(todoId);
List<CommentResponse> dtoList = new ArrayList<>();
for (Comment comment : commentList) {
User user = comment.getUser();
CommentResponse dto = new CommentResponse(
comment.getId(),
comment.getContents(),
new UserResponse(user.getId(), user.getEmail())
);
dtoList.add(dto);
}
return dtoList;
}
개선 후 코드
@Transactional(readOnly = true)
public List<CommentResponse> getComments(long todoId) {
List<Comment> commentList = commentRepository.findByTodoIdWithUser(todoId);
// Stream API를 통한 직관적인 데이터 변환(Map) 및 불변 리스트 반환
return commentList.stream().map(comment -> new CommentResponse(
comment.getId(),
comment.getContents(),
new UserResponse(comment.getUser().getId(), comment.getUser().getEmail())
))
.toList();
}
스트림을 사용하므로써 코드 줄 수가 줄어들었다.
코드 라인 수 및 가독성이 좋아졌다.