지난 주 리뷰 반영 내용
LoginInterceptor 객체를 새로 만들지 않고 스프링이 주입하도록 수정
@Override
public void addInterceptors(InterceptorRegistry registry) {
LoginInterceptor loginInterceptor = new LoginInterceptor(jwtUtil);
registry.addInterceptor(loginInterceptor)
→
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
jwt로 요청한 사용자가 누구인지 가져오는 로직이 중복되는 것을 loginInterceptor에서 처리 후 Controller에서 @RequestAttribute 어노테이션을 통해 사용자 객체만 받도록 수정
..., @RequestHeader("Authorization") String token) {
LoggedInUser loggedInUser = jwtUtil.extractedUserFromToken(token);
→
..., @RequestAttribute(LoggedInUser loggedInUser) {
// LoginInterceptor에서 사용자를 가져오는 로직 처리
@JsonNaming 어노테이션을 사용해 클래스 레벨에서 매핑
public class GithubToken {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("scope")
private String scope;
}
→
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class GithubToken {
private String accessToken;
private String tokenType;
private String scope;
}
JPAQueryFactory를 빈으로 등록하여 새로운 객체를 생성하지 않고 스프링으로부터 주입받도록 함
/**
* CategoryRepositoryImpl.class
*/
public class CategoryRepositoryImpl implements CategoryRepositoryCustom {
private final JPAQueryFactory queryFactory;
public CategoryRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
→
/**
* CategoryRepositoryImpl.class
*/
public class CategoryRepositoryImpl implements CategoryRepositoryCustom {
private final JPAQueryFactory queryFactory;
public CategoryRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
/**
* JpaConfig.class
*/
@Configuration
public class JpaConfig {
private final EntityManager entityManager;
public JpaConfig(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
@Validated와 @Valid
@PostMapping(consumes = {"multipart/form-data"})
public ResponseEntity<CustomResponse<CreatePostResponseDto>> createPost(@Validated @ModelAttribute PostSaveDto postSaveDto, @RequestHeader("Authorization") String token) {
→
@PostMapping(consumes = {"multipart/form-data"})
public ResponseEntity<CustomResponse<CreatePostResponseDto>> createPost(@Valid @ModelAttribute PostSaveDto postSaveDto, @RequestAttribute LoggedInUser loggedInUser) {
@Valid 어노테이션은 Bean Validator를 이용해 객체의 멤버 변수에 붙어있는 어노테이션을 검증하는데 이 때 프론트 컨트롤러(디스패처 서블릿)에서 컨트롤러 메서드의 객체를 만들면서 Argument Resolver에 의해 처리되는 것같습니다. 즉, @Valid 어노테이션을 사용하면 컨트롤러에서만 동작하며 공식 문서에서는 메서드의 파라미터로 유효한 객체(값)가 전달되는 것인지 검증하는 용도로 사용하는 것을 표준으로 하고있다고 학습했습니다.
@Validated 어노테이션은 스프링 프레임워크에서 제공하는 기능으로 컨트롤러 계층 이후에 발생할 수 있는 ConstraintViolationException(save 시 유효하지 않은 객체인 경우 등)이 발생했을 때 처리하게 됩니다.
@Valid에 의한 예외는 MethodArgumentNotValidException이며, @Validated에 의한 예외는 ConstraintViolationException
조회 수 증가하는 로직의 동시성 제어 문제….
pessimistic lock을 사용하면 성능이 많이 떨어질 것으로 예상됩니다.
그래서 다른 방법을 생각한 것이
findById 이후 update query를 보내는 것입니다. 하지만 이 방식은 데이터베이스에는 반영이 되지만 최종적으로 return 하는 객체에는 반영되지 않은 상태로 보내지게 됩니다.
→
그래서 저희가 판단한 그나마 최선의 방법은
update query 이후 findById + orElseThrow()를 사용하는데 현재 문제가 발생하지는 않지만 업데이트 후 객체 유무를 검사하는 로직 흐름이 어색하다고 생각이 듭니다. 어떻게 하는 것이 좋을 지 궁금합니다.