🐾 이메일 인증을 통한 초대를 구현하고 싶었는데 팀 프로젝트이고 일정이 일주일 밖에 되지 않아 아쉽지만 간단하게 구현하기로 했다.
보드 생성자만이 초대를 할 수 있고 초대된 유저는 member로 userBoardRepository에 저장된다.
N+1
문제가 발생한다.public interface UserBoardRepository extends JpaRepository<UserBoard, Long> {
List<UserBoard> findAllByUserId(Long userId);
}
public interface UserBoardRepository extends JpaRepository<UserBoard, Long> {
@Query("SELECT ub FROM UserBoard ub JOIN FETCH ub.board WHERE ub.user.id = :userId")
List<UserBoard> findAllByUserIdWithUserFetch(Long userId);
}
처음에는 pointcut을 메서드 별로 사용했는데 너무 비효율적이라는 생각이 들었다.
그리고 상황별로 나눠서 메서드를 묶어 처리하고 싶었다.
찾아보니 커스텀 어노테이션을 통해서 메서드에 어노테이션을 추가해 일괄 처리할 수 있었다.
/*ExcludeLogging 어노테이션을 메서드에만 적용할 수 있도록 지정*/
@Target(ElementType.METHOD)
/*ExcludeLogging 어노테이션이 런타임까지 유지되도록 지정*/
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessCheck {
}
@Pointcut("@annotation(com.example.tenrello.board.aop.AccessCheck)")
private void accessCheckMethods() {
}
@Around("accessCheckMethods()")
public Object boardAccessCheck(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
User user = (User) joinPoint.getArgs()[0];
Long boardId = (Long) joinPoint.getArgs()[1];
Board board = boardService.findByBoard(boardId);
if (userBoardRepository.findByBoardIdAndUserId(boardId, user.getId()).isEmpty()) {
log.warn("초대 받지 못한 유저입니다.");
throw new BoardAccessException("초대 받지 못한 유저입니다.");
}
return joinPoint.proceed();
}