@Aspect
@Component
@RequiredArgsConstructor
public class HistoryOwnerCheckAspect {
private final HistoryRepository historyRepository;
@Before("@annotation(knu_chatbot.annotation.CheckHistoryOwner)")
public void checkHistoryOwner(JoinPoint joinPoint) {
다음엔 AOP를 구현해줘야겠죠. 일단 위에서 정의한 커스텀 어노테이션이 붙은 메서드 실행 전에 AOP가 실행될 수 있도록 @Before로 지정해줍시다.
Long memberId = findParameterValue(joinPoint, "memberId", Long.class);
Long historyId = findParameterValue(joinPoint, "historyId", Long.class);
다음엔 메서드에서 파라미터 값들을 꺼내와줬습니다. 실제 구현코드는
private <T> T findParameterValue(JoinPoint joinPoint, String parameterName, Class<T> type) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = signature.getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
if (parameterNames[i].equals(parameterName)) {
return type.cast(args[i]);
}
}
throw new KnuChatbotException("AOP: @" + signature.getMethod().getName() + " 메서드에서 '" + parameterName + "' 파라미터를 찾을 수 없습니다.", INTERNAL_SERVER_ERROR);
}
파라미터 이름들로 for문 돌려줬습니다. 생각보다 단순하죠?
그리고 그 다음은 꺼내온 값들로 원하는 로직 돌려주시면 됩니다.
@Aspect
@Component
@RequiredArgsConstructor
public class HistoryOwnerCheckAspect {
private final HistoryRepository historyRepository;
@Before("@annotation(knu_chatbot.annotation.CheckHistoryOwner)")
public void checkHistoryOwner(JoinPoint joinPoint) {
Long memberId = findParameterValue(joinPoint, "memberId", Long.class);
Long historyId = findParameterValue(joinPoint, "historyId", Long.class);
History historyCheck = findHistoryById(historyId);
Long historyOwner = historyCheck.getMember().getId();
if (!historyOwner.equals(memberId)) {
throw new KnuChatbotException("히스토리 주인이 아닙니다.", HttpStatus.FORBIDDEN);
}
}
private <T> T findParameterValue(JoinPoint joinPoint, String parameterName, Class<T> type) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = signature.getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
if (parameterNames[i].equals(parameterName)) {
return type.cast(args[i]);
}
}
throw new KnuChatbotException("AOP: @" + signature.getMethod().getName() + " 메서드에서 '" + parameterName + "' 파라미터를 찾을 수 없습니다.", INTERNAL_SERVER_ERROR);
}
public History findHistoryById(Long historyId) {
return historyRepository.findById(historyId)
.orElseThrow(() -> new KnuChatbotException("히스토리가 존재하지 않습니다.", HttpStatus.NOT_FOUND));
}
}
첨에 이 방법 생각해냈을 때는 진짜 개쩐다고 생각했는데 막상 하고나니까 간단하네요.
아무튼 공통로직이 여러곳에서 사용되면서 그 부분이 실제 동작과는 무관하면 이걸 좀 유식한 말로 Separation of Concerns(SoC)라 하던데 어쨌든 그럼 AOP로 빼보시면 좋을 것 같습니다.