프로젝트

[Restagram] 리팩토링

SY 키키 2024. 6. 11. 01:50

오늘까지 작성한 코드를 쭉 리뷰를 해보았습니다.

 

시작 깃허브 기록은 다음과 같습니다.

https://github.com/soyeonnnb/restagram-api/tree/0593db7bf7f0ab782011318d47932a1467da6bc4

 

GitHub - soyeonnnb/restagram-api

Contribute to soyeonnnb/restagram-api development by creating an account on GitHub.

github.com

 

다음과 같은 리스트가 나왔습니다. 오늘부터 차근히 하나씩 리팩토링해보도록 하겠습니다.

 

22/26

1. ✅ jwt filer 익명필터 넣기 -> 검색해보기 .permitall() 되는지 (24.06.10)

jwt filter 내 비로그인 시 shouldNotFilter를 사용했는데, 이를 사용하지 않고 인증을 해보고 싶어졌습니다.

결국 하지는 못했습니다.ㅜㅜ


2. ✅ commonResponse 삭제하고 다시 하기  (24.06.11)

CommonResponse라는 엔티티를 이용해 데이터를 리턴해주었는데, HttpStatus를 숫자로 보내주어 이를 활용할 수 없었습니다. 따라서 ResponseEntity가 더욱 직관적인것처럼 보여 이를 삭제해주고 ApiResponse를 생성하여 사용해주었습니다.

 

3. ✅ stream 사용  (24.06.11)

이러한 코드리뷰를 받아 모든 foreach 문을 stream으로 변경해주었습니다.

그래서, 기존의 for문을

@Override
public List<AddressResponse> getSiggList(Long sidoId) {
    SidoAddress sidoAddress = sidoAddressRepository.findById(sidoId).orElseThrow(() -> new RestApiException(CommonErrorCode.ENTITY_NOT_FOUND));
    List<SiggAddress> siggAddressList = siggAddressRepository.findALlBySidoAddressOrderByName(sidoAddress);
    List<AddressResponse> addressResponseList = new ArrayList<>();
    for(SiggAddress siggAddress : siggAddressList) {
        addressResponseList.add(AddressResponse.of(siggAddress));
    }
    return addressResponseList;
}

 

stream으로 변경해주었습니다.

@Override
@Transactional(readOnly = true)
public List<AddressResponse> getSiggList(Long sidoId) {
  // 시도 엔티티를 이용해 시군구 리스트 반환
  SidoAddress sidoAddress = sidoAddressRepository.findById(sidoId).orElseThrow(
      () -> new RestApiException(AddressErrorCode.INVALID_SIDO_ID,
          "시도 ID가 유효하지 않습니다. [시도ID=" + sidoId + "]"));
  List<SiggAddress> siggAddressList = siggAddressRepository.findALlBySidoAddressOrderByName(
      sidoAddress);

  return siggAddressList.stream()
      .map(AddressResponse::of)
      .collect(Collectors.toList());
}

 

4. ✅ validation exception handler  (24.06.11)

기존 validation exception handler에 MethodArgumentNotValidException 을 받는 핸들러가 없는 것을 발견하여, 이를 추가해주었습니다. 더보기를 클릭하시면 코드 전문을 확인하실 수 있습니다.

더보기
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<List<ValidationError>>> handleMethodArgumentNotValidException(
    MethodArgumentNotValidException e) {
  List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
  List<ValidationError> errors = fieldErrors.stream()
      .map(ErrorResponse.ValidationError::of)
      .collect(Collectors.toList());

  return ResponseEntity.status(HttpStatus.BAD_REQUEST)
      .body(ApiResponse.createError("ERROR-001", errors, e.getMessage()));
}


그 결과, 기존

{
    "timestamp": "2024-06-12T08:21:19.641+00:00",
    "status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [1] in public org.springframework.http.ResponseEntity<com.restgram.global.exception.entity.ApiResponse<?>> com.restgram.domain.feed.controller.FeedController.postFeed(org.springframework.security.core.Authentication,com.restgram.domain.feed.dto.request.AddFeedRequest,java.util.List<org.springframework.web.multipart.MultipartFile>): [Field error in object 'req' on field 'storeId': rejected value [null]; codes [NotNull.req.storeId,NotNull.storeId,NotNull.java.lang.Long,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [req.storeId,storeId]; arguments []; default message [storeId]]; default message [피드 작성 가게는 필수 영역입니다.]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:148)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:224)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:178)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:206)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)\r\n\tat org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:365)\r\n\tat org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)\r\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)\r\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:58)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:189)\r\n\tat org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:175)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat com.restgram.global.jwt.filter.JwtFilter.doFilterInternal(JwtFilter.java:52)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)\r\n\tat org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:181)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)\r\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)\r\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)\r\n\tat org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)\r\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)\r\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)\r\n\tat org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)\r\n\tat org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195)\r\n\tat org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)\r\n\tat org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)\r\n\tat org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:230)\r\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)\r\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)\r\n\tat java.base/java.lang.Thread.run(Thread.java:833)\r\n",
    "message": "Validation failed for object='req'. Error count: 1",
    "errors": [
        {
            "codes": [
                "NotNull.req.storeId",
                "NotNull.storeId",
                "NotNull.java.lang.Long",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "req.storeId",
                        "storeId"
                    ],
                    "arguments": null,
                    "defaultMessage": "storeId",
                    "code": "storeId"
                }
            ],
            "defaultMessage": "피드 작성 가게는 필수 영역입니다.",
            "objectName": "req",
            "field": "storeId",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        }
    ],
    "path": "/api/v1/feed"
}

 

의 결과에서, 

{
    "code": "ERROR-001",
    "message": "Validation failed for argument [1] in public org.springframework.http.ResponseEntity<com.restgram.global.exception.entity.ApiResponse<?>> com.restgram.domain.feed.controller.FeedController.postFeed(org.springframework.security.core.Authentication,com.restgram.domain.feed.dto.request.AddFeedRequest,java.util.List<org.springframework.web.multipart.MultipartFile>): [Field error in object 'req' on field 'storeId': rejected value [null]; codes [NotNull.req.storeId,NotNull.storeId,NotNull.java.lang.Long,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [req.storeId,storeId]; arguments []; default message [storeId]]; default message [피드 작성 가게는 필수 영역입니다.]] ",
    "data": [
        {
            "field": "storeId",
            "message": "피드 작성 가게는 필수 영역입니다."
        }
    ]
}

 

로 변경하여, 프론트엔드에서 더 직관적으로 확인할 수 있도록 해주었습니다.

 

5. ✅ rest api exception handler log 정확하게   (24.06.11)

이전에는 단순히 에러 코드만 보내주었습니다.

 Store store = storeRepository.findById(req.storeId())
   		.orElseThrow(() -> new RestApiException(UserErrorCode.INVALID_USER_ID));

 로그는 정확해야 한다는 말을 들어 에러 로그도 정확하게 어떤 곳에서 발생했는지 등을 추가해주었습니다.

    Store store = storeRepository.findById(req.storeId())
        .orElseThrow(() -> new RestApiException(UserErrorCode.INVALID_USER_ID,
            "사용자ID가 유효하지 않습니다. [사용자ID=" + req.storeId() + "]"));

 

6. ✅ 발생위치, 원인, 종류, 환경

기존 로그에는 요청 경로와 오류 메세지밖에 없어 오류 발생 시간과 발생 위치(클래스 및 메서드)도 추가해주었습니다.

2024-06-12T17:25:04.323+09:00  WARN 9820 --- [nio-8080-exec-1] c.r.g.e.handler.GlobalExceptionHandler   : 요청 실패 => 요청 경로: http://localhost:8080/api/v1/feed, 발생 시간: 2024-06-12T17:25:04.322496400, 발생 위치: com.restgram.domain.feed.service.impl.FeedServiceImpl.lambda$addFeed$1(FeedServiceImpl.java:90), 오류메세지: 사용자ID가 유효하지 않습니다. [사용자ID=1117]

 

7. ✅ error 코드 추가 (24.06.11)

기존에는 에러에 대해 메세지만 응답으로 보내주었습니다. 하지만 이를 프론트엔드에서 확인하기에 조건이 복잡하여, 코드를 추가하였습니다. 더보기를 누르시면 자세히 확인하실 수 있습니다.

더보기

이전에는 다음과 같이 상태 코드와 영문으로 된 로그만 전송하였습니다.

@RequiredArgsConstructor
@Getter
public enum UserErrorCode implements ErrorCode{
    USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "User not found"),
    USER_DUPLICATED(HttpStatus.BAD_REQUEST, "User already exists"),
    USER_MISMATCH(HttpStatus.BAD_REQUEST, "User mismatched"),
    PASSWORD_MISMATCH(HttpStatus.BAD_REQUEST, "Password mismatched"),
    INVALID_USER_CODE(HttpStatus.BAD_REQUEST, "User code is invalid"),
    EMAIL_DUPLICATED(HttpStatus.CONFLICT, "User Email is duplicated"),
    NICKNAME_DUPLICATED(HttpStatus.CONFLICT, "Username is duplicated")
    ;

    private final HttpStatus httpStatus;
    private final String message;

}

 하지만, 이는 가독성이 떨어질 뿐 아니라 메세지를 통해 정확한 에러를 확인하기 어렵다는 단점이 있습니다.

 

따라서, 오류코드의 형태를 맞추어주고, 메세지를 한국어로 맞추어주었습니다.

@RequiredArgsConstructor
@Getter
public enum UserErrorCode implements ErrorCode{
    INVALID_LOGIN_USER_ID(HttpStatus.BAD_REQUEST, "USER-001", "유효하지 않은 로그인 사용자 ID입니다."),
    INVALID_USER_ID(HttpStatus.BAD_REQUEST, "USER-002", "유효하지 않은 사용자 ID입니다."),
    USER_DUPLICATED(HttpStatus.BAD_REQUEST, "USER-003", "이미 존재하는 사용자입니다."),
    USER_MISMATCH(HttpStatus.BAD_REQUEST, "USER-004", "사용자가 일치하지 않습니다."),
    PASSWORD_MISMATCH(HttpStatus.BAD_REQUEST, "USER-005", "비밀번호가 일치하지 않습니다."),
    EMAIL_DUPLICATED(HttpStatus.CONFLICT, "USER-006", "중복된 유저 이메일입니다."),
    NICKNAME_DUPLICATED(HttpStatus.CONFLICT, "USER-007", "중복된 유저 닉네임입니다.")
    ;

    private HttpStatus httpStatus;
    private String code;
    private String message;

    UserErrorCode(HttpStatus httpStatus, String code, String message) {
        this.httpStatus = httpStatus;
        this.code = code;
        this.message = message;
    }
}

 

8. ✅ @Data 필요없는 부분 다 삭제하기

@Data를 삭제하고 필요한 메서드만 추가해주었습니다.

 

9. ✅ entity 한번 싹 고치기  (24.06.11)

엔티티에 주석을 추가해주었습니다.

 

10. ✅ entity string 길이나 제약조건 수정하기  (24.06.11)

string length를 추가해주었습니다.

 

11. ✅ requestDTO에는 getter만 필요  (24.06.11)

requestDTO내 Getter를 제외한 어노테이션을 삭제해주었습니다.

 

12. ✅ RequestDTO 유효성 검사 다 넣기  (24.06.11)

이전에는 단순히 필드만 작성해주었습니다. 하지만 이렇게하면 유효성을 검사하지 못합니다.

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class AddFeedRequest {
    private Long storeId;
    private String content;
}

따라서, @Valid 어노테이션을 통해 유효성을 검사하도록 해주었습니다.

또한 Request의 경우 서비스 내에서 생성이 되지 않기 때문에 getter 만 두었습니다.

@Getter
public class AddFeedRequest {

  @NotNull(message = "피드 작성 가게는 필수 영역입니다.")
  @Min(value = 0, message = "가게 ID는 음수가 될 수 없습니다.")
  private Long storeId;

  @Size(max = 2000, message = "피드 내용은 최대 2000자까지 가능합니다.")
  @NotBlank(message = "피드 내용은 필수 영역입니다.")
  private String content;
}

 

13. ✅ 더티체킹하는거 save 변경하기  (24.06.11)

@Transactional을 통해 엔티티가 더티체킹이 되기 때문에, 직접 save() 함수를 호출하여 업데이트하는 경우 save() 함수 호출을 삭제해주었습니다.

 

14. ✅ service/service impl 계층 나누기 (24.06.10)

service 인터페이스와 serviceImpl 구현체의 패키지를 나누어주었습니다.

 

15. ✅ 서비스 내 서비스 트랜젝션 분리해놓기  (24.06.11)

 

16. ✅ 조회 시 treansaction readonly 달아놓기  (24.06.11)

조회 시 1차 캐시에 저장하지 않게 하기 위해 @Transactional(readOnly = true)를 추가해주었습니다.


16. ✅ 같은 계층끼리 줄바꿈

코드의 가독성을 늘리기 위해 같은 로직이 아니면 줄바꿈을 해주었습니다.

 

17. ✅ import 안쓰는거 싹 제거 (24.06.10)

 

18. ✅ ;; 이런 코드도 존재. 파일 보고 확인   (24.06.11)

오타 삭제

 

19. ✅ 주석달기  (24.06.11)

엔티티 및 서비스 내 주석을 추가하였습니다.

 

20. ✅ 출력문도 필요 없으면 다 제거 (24.06.10)

 

21.   refresh repository 어노테이션 삭제 (24.06.11)

필요없는 어노테이션을 삭제했습니다.

 

22. ✅ requestMapping에 "/api/v1" 붙이기 (24.06.10)

이전에 다음과 같은 코드리뷰를 받은 적이 있습니다.

저 또한 이에 동의하기 때문에 api에 /api/v1을 붙여주었습니다.

 

23.   dto는 한번 만들면 수정X. 그러니까 record 사용 (24.06.12)

DTO의 경우에는 한번 생성이 되면 수정되는 일이 없습니다. 따라서 더 유연한 코드 작성을 위해 DTO의 경우 class 에서 record로 변경시켜주었습니자.

자세한 내용은 다음 포스팅을 참고해주세요.🥰

https://soyeonnnb.tistory.com/126

 

[Java] Record란? & 프로젝트에 Record 적용해보기

프로젝트를 진행하며 같이 스터디하는 분께서 코드 리뷰 중에 DTO에 Record를 적용해보면 어떻겠냐는 의견을 제시해주었습니다. 그래서 이번 포스팅에서는 Record에 대해 살펴보고, 프로젝트 DTO에

soyeonnnb.tistory.com

 

24.    of랑 toEntity 이름 확인

메서드 명을 DTO -> Entity 는 toEntity, Entity/DTO -> DTO는 of 를 사용해주었습니다.

 

24. 팔로우 같은거 삭제하는거 어떤가

25. emdAddress 테이블 합치는거 정규화 테스트

 

 


시작일: 24.06.10

최근 수정: 24.06.12