이전 글에 이어서 클린코드 11장 시스템에서 다룬 관심사 분리를 적용합니다.
이전 글에서는 인증된 사용자 정보 추출에 대한 관심사를 분리했습니다.
이번 글에서는 비즈니스 로직 수행에 대한 로그 처리를 분리합니다.
public void createFcmToken(String token, long userId) {
log.info("start createFcmToken by token: [{}], userId: [{}]", token, userId);
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(CommonCode.NONEXISTENT_USER));
// 중복 데이터 저장되지 않도록
if (!fcmTokenRepository.existsByUserIdAndToken(userId, token)) {
log.info("FCM 토큰 생성 by userId: [{}], token: [{}]", userId, token);
fcmTokenRepository.save(new FcmToken(user, token));
}
}
@Transactional
public void deleteFcmToken(long userId) {
log.info("start deleteFcmToken by userId: [{}]", userId);
fcmTokenRepository.deleteAllByUserId(userId);
}
public void updateUser(String nickname, long userId) {
log.info("start updateUser by nickname: [{}], userId: [{}]", nickname, userId);
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(CommonCode.NONEXISTENT_USER));
if (userRepository.existsByNickname(nickname)) {
throw new CustomException(CommonCode.ALREADY_EXIST_NICKNAME);
}
user.updateNickname(nickname);
userRepository.save(user);
}
“서비스 로직 시작 로깅” 이라는 관심사를 분리한다면?
→ 사소한 변경에도 로그를 직접 수정하지 않아도 될 것입니다.
→ 로깅 동작이 자동화되어 개발자는 더이상 시작 로깅을 신경쓰지 않고, 비즈니스 로직에 집중할 수 있을 것입니다.
@Aspect
@Slf4j
@Component
public class LogAspect {
@Pointcut("execution(* com.and20roid.backend.service.*.*(..))")
public void service() {
}
@Around("service()")
public Object loggingBefore(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String logMessage = buildLogMessage(methodName, joinPoint.getArgs(),
(MethodSignature) joinPoint.getSignature());
log.info(logMessage);
return joinPoint.proceed();
}
private String buildLogMessage(String methodName, Object[] args, MethodSignature signature) {
StringBuilder sb = new StringBuilder();
appendMethodName(sb, methodName);
appendParameters(sb, signature, args);
return sb.toString();
}
private void appendMethodName(StringBuilder sb, String methodName) {
sb.append("start ").append(methodName).append(" by ");
}
private void appendParameters(StringBuilder sb, MethodSignature signature, Object[] args) {
String[] parameterNames = signature.getParameterNames();
if (args.length > 0) {
appendArguments(sb, parameterNames, args);
} else {
sb.append("no arguments");
}
}
private void appendArguments(StringBuilder sb, String[] parameterNames, Object[] args) {
for (int i = 0; i < args.length; i++) {
sb.append(parameterNames[i]).append(": [");
appendArgument(sb, args[i]);
sb.append("], ");
}
// remove comma and space
sb.setLength(sb.length() - 2);
}
private void appendArgument(StringBuilder sb, Object arg) {
if (arg instanceof MultipartFile) {
appendMultipartFile(sb, (MultipartFile) arg);
} else {
sb.append(arg);
}
}
private void appendMultipartFile(StringBuilder sb, MultipartFile file) {
sb.append(file.getName()).append(": [").append(file.getOriginalFilename()).append("]");
}
}
public void createFcmToken(String token, long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(CommonCode.NONEXISTENT_USER));
// 중복 데이터 저장되지 않도록
if (!fcmTokenRepository.existsByUserIdAndToken(userId, token)) {
log.info("FCM 토큰 생성 by userId: [{}], token: [{}]", userId, token);
fcmTokenRepository.save(new FcmToken(user, token));
}
}
@Transactional
public void deleteFcmToken(long userId) {
fcmTokenRepository.deleteAllByUserId(userId);
}
public void updateUser(String nickname, long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(CommonCode.NONEXISTENT_USER));
if (userRepository.existsByNickname(nickname)) {
throw new CustomException(CommonCode.ALREADY_EXIST_NICKNAME);
}
user.updateNickname(nickname);
userRepository.save(user);
}