2024. 10. 28. 12:15ㆍ내일배움캠프/Schedule Management
과제 제출후 받은 피드백 내용들을 프로젝트에 반영하며 기록을 남겨본다.
1. 명확한 클래스 네이밍
이 부분은 인지하고 있었으나 섬세함이 부족해 피드백을 통해 수정을 권유받았다. 과제 요구사항 중 'Open API' 사용을 반영하기 위해 사용한 RestTemplate 객체를 빈으로 등록하기 위해 Config 클래스에 RestTemplate 객체를 생성하는 메서드를 구현해 놓았다. 하지만 이는 Config 라는 클래스의 이름만으로 어떤 '구성' 을 위한 코드가 클래스에 작성되어 있을지 알기 어렵다.
그래서 아래와 같이 Config 라는 기존의 클래스명을 RestTemplateConfig 로 변경해 RestTemplate 구성과 관련된 내용이 해당 클래스에 있음을 '명시적' 으로 알 수 있도록 수정하였다.
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2. DTO 생성 관련
기존에는 Service 에서 조회/전달 받은 Entity 를 DTO 로 변환해 반환하였다. 하지만 이번 과제의 경우 Entity 에 DTO 로 변환하는 메서드를 추가했는데, 이는 옳지 않은 '의존성 방향' 이라는 피드백을 받았다. 현재 과제는 3 Layer Architecture 를 사용하고 있는데 해당 아키텍처에서는 상위 계층이 하위 계층을 참조하는 것은 허용하나 하위 계층이 상위 계층을 참조하는 것을 지양하고 있다. 그래서 Entity 에 존재하는 DTO 를 생성하는 메서드를 아래와 같이 해당 DTO 로 옮겼다.
@Getter
@Builder
public class ResponseMember {
private Long id;
private String name;
private String email;
private String createAt;
private String updateAt;
public static ResponseMember makeResponse(Member member) {
return ResponseMember.builder()
.id(member.getId())
.name(member.getName())
.email(member.getEmail())
.createAt(convertDateTimeFormat(member.getCreateAt()))
.updateAt(convertDateTimeFormat(member.getUpdateAt()))
.build();
}
}
결과적으로 Member, Schedule, Comment 클래스에 존재하는 DTO 생성 메서드를 각 DTO 클래스로 옮기게 되었다.
3. Stream 활용 관련
현재 Stream 은 대체로 각 도메인의 Service 에서 사용되고 있다. 그리고 아래와 같이 Collection 또는 Map 객체를 생성/초기화 한 후 해당 객체에 'stream(), forEach()' 를 사용해 요소를 추가하고 있다.
@Transactional(readOnly = true)
public ResponseMemberList findAll(String name, PageRequest pageRequest) {
Slice<Member> foundMembers = memberRepository.findAllByName(name, pageRequest);
List<ResponseMember> responseMembers = new ArrayList<>();
foundMembers.getContent().stream().map(ResponseMember::makeResponse).forEach(responseMembers::add);
return ResponseMemberList.builder()
.members(responseMembers)
.pageable(foundMembers.getPageable())
.build();
}
'stream()' 을 '데이터의 흐름' 이라고 하는 만큼 위의 코드는 적절하지 않다고 판단했다. 피드백에서도 "코드의 목적이 잘 파악되지 않고, 명령형 스타일의 코드로 보인다" 라고 되어있는데 아마 '데이터의 흐름' 이 명확하게 보이지 않기 때문에 이러한 피드백을 받았다고 생각하였다.
그래서 'stream(), forEach()' 를 사용하는 로직을 "이러한 데이터 흐름을 통해 컬랙션 또는 Map 객체에 저장되는 구나." 라는 생각을 할 수 있게끔 아래와 같이 로직을 변경해 주었다.
@Transactional(readOnly = true)
public ResponseMemberList findAll(String name, PageRequest pageRequest) {
Slice<Member> foundMembers = memberRepository.findAllByName(name, pageRequest);
List<ResponseMember> responseMembers = foundMembers.getContent().stream().map(ResponseMember::makeResponse).toList();
return ResponseMemberList.builder()
.members(responseMembers)
.pageable(foundMembers.getPageable())
.build();
}
Member, Schedule, Comment 도메인의 Service 에서 'stream(), forEach()' 메서드를 사용하는 로직들을 위와 같은 방식으로 수정했으며, 조회한 '날씨 정보 목록' 에서 필요한 날씨 정보를 가져오는 로직 또한 위와 같은 방식으로 로직을 수정했다.
4. AuthFilter 관련
현재 JwtUtil 이 존재함에도 AuthFilter 에서 직접적으로 토큰 정보에 접근해 이를 사용하고 있다. AuthFilter 에서는 필요한 토큰 정보를 JwtUtil 에 요청해 필요한 정보만을 전달 받는 것이 적절하다고 판단 아래와 같이 TokenInfo 라는 DTO 를 추가해 인증/인가에 필요한 '회원 ID' 및 '회원 권한' 을 담아 AuthFilter 에서 이를 사용할 수 있도록 수정하였다.
public class AuthFilter implements Filter {
private final JwtUtil jwtUtil;
private final MemberRepository memberRepository;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String httpMethod = httpServletRequest.getMethod();
String url = httpServletRequest.getRequestURI();
if (StringUtils.hasText(url)) {
if (url.matches("/member/join") || url.matches("/member/logIn")) {
chain.doFilter(request, response);
} else if (url.startsWith("/schedule") &&
(httpMethod.matches("PUT") || httpMethod.matches("DELETE"))) {
TokenInfo tokenInfo = getTokenInfoFromRequest(httpServletRequest);
if (!tokenInfo.getAuth().matches(ADMIN.getRole())) {
throw new HasNotPermissionException(HAS_NOT_PERMISSION);
}
Member member = memberRepository.findById(tokenInfo.getMemberId())
.orElseThrow(() -> new NotFoundEntityException(NOT_FOUND_MEMBER));
request.setAttribute("member", member);
chain.doFilter(request, response);
} else {
TokenInfo tokenInfo = getTokenInfoFromRequest(httpServletRequest);
Member member = memberRepository.findById(tokenInfo.getMemberId())
.orElseThrow(() -> new NotFoundEntityException(NOT_FOUND_MEMBER));
request.setAttribute("member", member);
chain.doFilter(request, response);
}
}
}
private TokenInfo getTokenInfoFromRequest(HttpServletRequest httpServletRequest) {
String token = jwtUtil.getTokenFromRequest(httpServletRequest);
jwtUtil.checkTokenValidity(token);
return jwtUtil.getPayload(token);
}
}
'내일배움캠프 > Schedule Management' 카테고리의 다른 글
[일정 관리 앱] 피드백 반영(2) (0) | 2024.10.29 |
---|---|
[일정 관리 앱] 리팩토링(5) (0) | 2024.10.16 |
[일정 관리 앱] 리팩토링(4) (0) | 2024.10.16 |
[일정 관리 앱] 리팩토링(3) (0) | 2024.10.15 |
[일정 관리 앱] 리팩토링(2) (0) | 2024.10.15 |