Open daadaadaah opened 3 weeks ago
@Service
public class LogDirectSaveGoogleSheetsService implements LogService {
private final LogGoogleSheetsRepository logGoogleSheetsRepository;
private final NotificationService notificationService;
private final LogHelper logHelper;
private final UnSavedLogFileFactory unSavedLogFileFactory;
private final Stack<LogSaveResult> logSaveResultStack = new Stack<>();
@Autowired
public LogDirectSaveGoogleSheetsService(
LogGoogleSheetsRepository logGoogleSheetsRepository,
NotificationService notificationService,
LogHelper logHelper,
UnSavedLogFileFactory unSavedLogFileFactory
) {
this.logGoogleSheetsRepository = logGoogleSheetsRepository;
this.notificationService = notificationService;
this.logHelper = logHelper;
this.unSavedLogFileFactory = unSavedLogFileFactory;
}
/**
* saveJobLog는 Job 관련된 로그를 저장하는 메서드이다.
*
* < @Async("logSaveTaskExecutor") 추가한 이유 >
* - 비즈니스 로직인 Github 작업과 로그 작업을 디커플링 시키기 위해
* -
*
*
*/
// TODO : 디스코드로! Job, Step, Task 모두 보내면, 디스코드 시끄러울 것 같은데, 이거 어떻게 할지 고민해보기
@Async("logSaveHandlerExecutor") // TODO : 로그 저장 실패시, 비동기 예외 처리 어떻게 할 것인지 + 트랜잭션 처리
public void saveJobLog(SaveJobLogDto saveJobLogDto) {
BatchProcessType batchProcessType = BatchProcessType.JOB;
UUID jobId = saveJobLogDto.jobId();
long jobDetailLogId = saveJobLogDto.endTime();
JobDetailLog jobDetailLog = JobDetailLog.of(
jobDetailLogId,
logHelper.getEnvironment(),
saveJobLogDto,
logHelper.getCreatedAt(saveJobLogDto.startTime())
);
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
jobId,
null,
logHelper.getEnvironment(),
batchProcessType,
saveJobLogDto.methodName(),
saveJobLogDto.status(),
saveJobLogDto.statusReason(),
jobDetailLogId,
saveJobLogDto.endTime() - saveJobLogDto.startTime(),
logHelper.getCreatedAt(saveJobLogDto.startTime())
);
try {
logGoogleSheetsRepository.saveJobLogsWithTx(jobDetailLog, executionTimeLog);
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.SUCCESS,
createLogsSaveSuccessMessage(batchProcessType, jobId),
new ArrayList<>()
));
} catch (Exception exception) {
List<UnSavedLogFile> unSavedLogFiles = createUnSavedJobLogsFiles(jobDetailLog, executionTimeLog);
String jobDetailLogFileName = unSavedLogFiles.get(0).fileName();
String jobExecutionTimeLogFileName = unSavedLogFiles.get(1).fileName();
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.FAILURE,
createLogsSaveFailureMessage(
batchProcessType,
exception,
jobDetailLogFileName,
jobExecutionTimeLogFileName
),
unSavedLogFiles
));
}
// TODO : 배치
notifyBatchProcessTotalResult();
}
private void notifyBatchProcessTotalResult() {
List<UnSavedLogFile> logFiles = new ArrayList<>();
StringBuilder currentMessage = new StringBuilder("");
while (!logSaveResultStack.isEmpty()) {
LogSaveResult logSaveResult = logSaveResultStack.pop();
String newMessage = logSaveResult.message();
if(
currentMessage.length() + newMessage.length() > MAX_DISCORD_MESSAGE_LENGTH ||
logFiles.size() + logSaveResult.unSavedLogFiles().size() > MAX_DISCORD_FILE_COUNT
) {
notificationService.sendMessageWithFiles(currentMessage.toString(), logFiles);
currentMessage = new StringBuilder(newMessage);
logFiles = new ArrayList<>(logSaveResult.unSavedLogFiles());
} else {
currentMessage.append("\n").append(newMessage);
logFiles.addAll(logSaveResult.unSavedLogFiles());
}
}
// 마지막으로 남은 메시지를 전송합니다.
if (currentMessage.length() > 0) {
notificationService.sendMessageWithFiles(currentMessage.toString(), logFiles);
}
}
private List<UnSavedLogFile> createUnSavedJobLogsFiles(JobDetailLog jobDetailLog, ExecutionTimeLog executionTimeLog) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String githubApiDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(jobDetailLog.getClass().getSimpleName() +"_"+ jobDetailLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiDetailLogFileName, jobDetailLog));
String githubApiExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(executionTimeLog.getClass().getSimpleName() +"_"+ executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiExecutionTimeLogFileName, executionTimeLog));
return unSavedLogFiles;
}
@Async("logSaveHandlerExecutor")
public void saveStepLog(SaveStepLogDto saveStepLogDto) {
BatchProcessType batchProcessType = BatchProcessType.STEP;
UUID stepId = saveStepLogDto.stepId();
long stepDetailLogId = saveStepLogDto.endTime();
StepDetailLog stepDetailLog = StepDetailLog.of(
stepDetailLogId,
logHelper.getEnvironment(),
saveStepLogDto,
logHelper.getCreatedAt(saveStepLogDto.endTime())
);
long timeTaken = saveStepLogDto.endTime() - saveStepLogDto.startTime();
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
saveStepLogDto.stepId(),
saveStepLogDto.jobId(),
logHelper.getEnvironment(),
batchProcessType,
saveStepLogDto.methodName(),
saveStepLogDto.status(),
saveStepLogDto.statusReason(),
stepDetailLogId,
timeTaken,
logHelper.getCreatedAt(saveStepLogDto.endTime())
);
try {
logGoogleSheetsRepository.saveStepLogsWithTx(stepDetailLog, executionTimeLog);
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.SUCCESS,
createLogsSaveSuccessMessage(batchProcessType, stepId),
new ArrayList<>()
));
} catch (Exception exception) {
List<UnSavedLogFile> unSavedLogFiles = createUnSavedStepLogsFiles(stepDetailLog, executionTimeLog);
String stepDetailLogFileName = unSavedLogFiles.get(0).fileName();
String stepExecutionTimeLogFileName = unSavedLogFiles.get(1).fileName();
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.FAILURE,
createLogsSaveFailureMessage(
batchProcessType,
exception,
stepDetailLogFileName,
stepExecutionTimeLogFileName
),
unSavedLogFiles
));
}
}
private List<UnSavedLogFile> createUnSavedStepLogsFiles(StepDetailLog stepDetailLog, ExecutionTimeLog executionTimeLog) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String githubApiDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(stepDetailLog.getClass().getSimpleName() +"_"+ stepDetailLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiDetailLogFileName, stepDetailLog));
String githubApiExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(executionTimeLog.getClass().getSimpleName() +"_"+ executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiExecutionTimeLogFileName, executionTimeLog));
return unSavedLogFiles;
}
@Async("logSaveHandlerExecutor")
public void saveTaskLog(SaveTaskLogDto saveTaskLogDto) {
BatchProcessType batchProcessType = BatchProcessType.TASK;
UUID taskId = saveTaskLogDto.taskId();
long taskDetailLogId = saveTaskLogDto.endTime();
long timeTaken = saveTaskLogDto.endTime() - saveTaskLogDto.startTime();
GithubApiLog githubApiLog = new GithubApiLog(
taskDetailLogId,
logHelper.getEnvironment(),
saveTaskLogDto.batchProcessName(),
saveTaskLogDto.httpMethod(),
saveTaskLogDto.url(),
saveTaskLogDto.requestHeaders(),
saveTaskLogDto.requestBody(),
saveTaskLogDto.responseStatusCode(),
saveTaskLogDto.responseHeaders(),
saveTaskLogDto.responseBody(),
timeTaken,
logHelper.getCreatedAt(saveTaskLogDto.endTime())
);
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
taskId,
saveTaskLogDto.stepId(),
logHelper.getEnvironment(),
batchProcessType,
saveTaskLogDto.batchProcessName(),
saveTaskLogDto.status(),
saveTaskLogDto.statusReason(),
taskDetailLogId,
timeTaken,
logHelper.getCreatedAt(saveTaskLogDto.endTime())
);
try {
logGoogleSheetsRepository.saveGithubApiLogsWithTx(githubApiLog, executionTimeLog);
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.SUCCESS,
createLogsSaveSuccessMessage(batchProcessType, taskId),
new ArrayList<>()
));
} catch (Exception exception) {
List<UnSavedLogFile> unSavedLogFiles = createUnSavedGithubApiLogFiles(githubApiLog, executionTimeLog);
String githubApiDetailLogFileName = unSavedLogFiles.get(0).fileName();
String githubApiExecutionTimeLogFileName = unSavedLogFiles.get(1).fileName();
logSaveResultStack.add(new LogSaveResult(
LogSaveResultType.FAILURE,
createLogsSaveFailureMessage(
batchProcessType,
exception,
githubApiDetailLogFileName,
githubApiExecutionTimeLogFileName
),
unSavedLogFiles
));
}
}
private List<UnSavedLogFile> createUnSavedGithubApiLogFiles(GithubApiLog githubApiLog, ExecutionTimeLog executionTimeLog) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String githubApiDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(githubApiLog.getClass().getSimpleName() +"_"+ githubApiLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiDetailLogFileName, githubApiLog));
String githubApiExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(executionTimeLog.getClass().getSimpleName() +"_"+ executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(githubApiExecutionTimeLogFileName, executionTimeLog));
return unSavedLogFiles;
}
}
@Service
public class LogDirectSaveGoogleSheetsService implements LogService {
private final LogGoogleSheetsRepository logGoogleSheetsRepository;
private final LogHelper logHelper;
private final LogSaveDiscordNotificationFacade logSaveDiscordNotificationFacade;
@Autowired
public LogDirectSaveGoogleSheetsService(
LogGoogleSheetsRepository logGoogleSheetsRepository,
LogHelper logHelper,
LogSaveDiscordNotificationFacade logSaveDiscordNotificationFacade
) {
this.logGoogleSheetsRepository = logGoogleSheetsRepository;
this.logHelper = logHelper;
this.logSaveDiscordNotificationFacade = logSaveDiscordNotificationFacade;
}
/**
* saveJobLog는 Job 관련된 로그를 저장하는 메서드이다.
*
* < @Async("logSaveTaskExecutor") 추가한 이유 >
* - 비즈니스 로직인 Github 작업과 로그 작업을 디커플링 시키기 위해
* -
*
*
*/
// TODO : 디스코드로! Job, Step, Task 모두 보내면, 디스코드 시끄러울 것 같은데, 이거 어떻게 할지 고민해보기
@Async("logSaveHandlerExecutor") // TODO : 로그 저장 실패시, 비동기 예외 처리 어떻게 할 것인지 + 트랜잭션 처리
public void saveJobLog(SaveJobLogDto saveJobLogDto) {
BatchProcessType batchProcessType = BatchProcessType.JOB;
UUID jobId = saveJobLogDto.jobId();
long jobDetailLogId = saveJobLogDto.endTime();
JobDetailLog jobDetailLog = JobDetailLog.of(
jobDetailLogId,
logHelper.getEnvironment(),
saveJobLogDto,
logHelper.getCreatedAt(saveJobLogDto.startTime())
);
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
jobId,
null,
logHelper.getEnvironment(),
batchProcessType,
saveJobLogDto.methodName(),
saveJobLogDto.status(),
saveJobLogDto.statusReason(),
jobDetailLogId,
saveJobLogDto.endTime() - saveJobLogDto.startTime(),
logHelper.getCreatedAt(saveJobLogDto.startTime())
);
try {
logGoogleSheetsRepository.saveJobLogsWithTx(jobDetailLog, executionTimeLog);
logSaveDiscordNotificationFacade.stackBatchProcessLogSaveSuccessResult(batchProcessType, jobId);
} catch (Exception exception) {
logSaveDiscordNotificationFacade.stackJobLogSaveFailureResult(exception, jobDetailLog, executionTimeLog);
}
logSaveDiscordNotificationFacade.sendBatchProcessResultsNotification();
}
@Async("logSaveHandlerExecutor")
public void saveStepLog(SaveStepLogDto saveStepLogDto) {
BatchProcessType batchProcessType = BatchProcessType.STEP;
UUID stepId = saveStepLogDto.stepId();
long stepDetailLogId = saveStepLogDto.endTime();
StepDetailLog stepDetailLog = StepDetailLog.of(
stepDetailLogId,
logHelper.getEnvironment(),
saveStepLogDto,
logHelper.getCreatedAt(saveStepLogDto.endTime())
);
long timeTaken = saveStepLogDto.endTime() - saveStepLogDto.startTime();
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
saveStepLogDto.stepId(),
saveStepLogDto.jobId(),
logHelper.getEnvironment(),
batchProcessType,
saveStepLogDto.methodName(),
saveStepLogDto.status(),
saveStepLogDto.statusReason(),
stepDetailLogId,
timeTaken,
logHelper.getCreatedAt(saveStepLogDto.endTime())
);
try {
logGoogleSheetsRepository.saveStepLogsWithTx(stepDetailLog, executionTimeLog);
logSaveDiscordNotificationFacade.stackBatchProcessLogSaveSuccessResult(batchProcessType, stepId);
} catch (Exception exception) {
logSaveDiscordNotificationFacade.stackStepLogSaveFailureResult(exception, stepDetailLog, executionTimeLog);
}
}
@Async("logSaveHandlerExecutor")
public void saveTaskLog(SaveTaskLogDto saveTaskLogDto) {
BatchProcessType batchProcessType = BatchProcessType.TASK;
UUID taskId = saveTaskLogDto.taskId();
long taskDetailLogId = saveTaskLogDto.endTime();
long timeTaken = saveTaskLogDto.endTime() - saveTaskLogDto.startTime();
GithubApiLog githubApiLog = new GithubApiLog(
taskDetailLogId,
logHelper.getEnvironment(),
saveTaskLogDto.batchProcessName(),
saveTaskLogDto.httpMethod(),
saveTaskLogDto.url(),
saveTaskLogDto.requestHeaders(),
saveTaskLogDto.requestBody(),
saveTaskLogDto.responseStatusCode(),
saveTaskLogDto.responseHeaders(),
saveTaskLogDto.responseBody(),
timeTaken,
logHelper.getCreatedAt(saveTaskLogDto.endTime())
);
ExecutionTimeLog executionTimeLog = ExecutionTimeLog.of(
taskId,
saveTaskLogDto.stepId(),
logHelper.getEnvironment(),
batchProcessType,
saveTaskLogDto.batchProcessName(),
saveTaskLogDto.status(),
saveTaskLogDto.statusReason(),
taskDetailLogId,
timeTaken,
logHelper.getCreatedAt(saveTaskLogDto.endTime())
);
try {
logGoogleSheetsRepository.saveGithubApiLogsWithTx(githubApiLog, executionTimeLog);
logSaveDiscordNotificationFacade.stackBatchProcessLogSaveSuccessResult(batchProcessType, taskId);
} catch (Exception exception) {
logSaveDiscordNotificationFacade.stackTaskLogSaveFailureResult(exception, githubApiLog, executionTimeLog);
}
}
}
@Service
public class LogSaveDiscordNotificationFacade {
private NotificationService notificationService;
private final UnSavedLogFileFactory unSavedLogFileFactory;
private final Stack<LogSaveResult> logSaveResultStack = new Stack<>();
@Autowired
public LogSaveDiscordNotificationFacade(
NotificationService notificationService,
UnSavedLogFileFactory unSavedLogFileFactory
) {
this.notificationService = notificationService;
this.unSavedLogFileFactory = unSavedLogFileFactory;
}
public void sendBatchProcessResultsNotification() {
List<UnSavedLogFile> logFiles = new ArrayList<>();
StringBuilder currentMessage = new StringBuilder("");
while (!logSaveResultStack.isEmpty()) {
LogSaveResult logSaveResult = logSaveResultStack.pop();
String newMessage = logSaveResult.message();
if (
currentMessage.length() + newMessage.length() > MAX_DISCORD_MESSAGE_LENGTH
|| logFiles.size() + logSaveResult.unSavedLogFiles().size() > MAX_DISCORD_FILE_COUNT
) {
notificationService.sendMessageWithFiles(currentMessage.toString(), logFiles);
currentMessage = new StringBuilder(newMessage);
logFiles = new ArrayList<>(logSaveResult.unSavedLogFiles());
} else {
currentMessage.append("\n").append(newMessage);
logFiles.addAll(logSaveResult.unSavedLogFiles());
}
}
if (currentMessage.length() > 0) {
notificationService.sendMessageWithFiles(currentMessage.toString(), logFiles);
}
}
public void stackBatchProcessLogSaveSuccessResult(
BatchProcessType batchProcessType,
UUID batchProcessId
) {
logSaveResultStack.add(new LogSaveResult(
createLogsSaveSuccessMessage(batchProcessType, batchProcessId),
new ArrayList<>()
));
}
public void stackJobLogSaveFailureResult(
Exception exception,
JobDetailLog jobDetailLog,
ExecutionTimeLog executionTimeLog
) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String jobDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
jobDetailLog.getClass().getSimpleName() + "_" + jobDetailLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobDetailLogFileName, jobDetailLog));
String jobExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
executionTimeLog.getClass().getSimpleName() + "_" + executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobExecutionTimeLogFileName, executionTimeLog));
logSaveResultStack.add(new LogSaveResult(
createLogsSaveFailureMessage(
BatchProcessType.JOB,
exception,
jobDetailLogFileName,
jobExecutionTimeLogFileName
),
unSavedLogFiles
));
}
public void stackStepLogSaveFailureResult(
Exception exception,
StepDetailLog stepDetailLog,
ExecutionTimeLog executionTimeLog
) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String jobDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
stepDetailLog.getClass().getSimpleName() + "_" + stepDetailLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobDetailLogFileName, stepDetailLog));
String jobExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
executionTimeLog.getClass().getSimpleName() + "_" + executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobExecutionTimeLogFileName, executionTimeLog));
logSaveResultStack.add(new LogSaveResult(
createLogsSaveFailureMessage(
BatchProcessType.STEP,
exception,
jobDetailLogFileName,
jobExecutionTimeLogFileName
),
unSavedLogFiles
));
}
public void stackTaskLogSaveFailureResult(
Exception exception,
GithubApiLog githubApiLog,
ExecutionTimeLog executionTimeLog
) {
List<UnSavedLogFile> unSavedLogFiles = new ArrayList<>();
String jobDetailLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
githubApiLog.getClass().getSimpleName() + "_" + githubApiLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobDetailLogFileName, githubApiLog));
String jobExecutionTimeLogFileName = unSavedLogFileFactory.createFileNameWithExtension(
executionTimeLog.getClass().getSimpleName() + "_" + executionTimeLog.id());
unSavedLogFiles.add(new UnSavedLogFile(jobExecutionTimeLogFileName, executionTimeLog));
logSaveResultStack.add(new LogSaveResult(
createLogsSaveFailureMessage(
BatchProcessType.TASK,
exception,
jobDetailLogFileName,
jobExecutionTimeLogFileName
),
unSavedLogFiles
));
}
}
배경 : 기존 방식(매 로그 저장시마다 전송) 문제 파악
다음 그림처럼 배치 프로세스별(Job, Step, Task) 로그 데이터가 저장될 때마다 디스코드로 알림을 전송하는 방식이었습니다.
그런데, 이 알림이
주간 이슈 배치 Close 작업
의 경우, (최대 스터디 인원인 10명인 상황 가정)1분도 안되는 시간에 22번의 알림이 전송되면서 디스코드 채널이 과도하게 시끄러워지는 문제가 발생했습니다. 따라서, 이를 개선시키고 싶었습니다개선 과정
1. 개선책(Stack을 활용한 배치 전송)
(1) Stack 선택한 이유
1) 로그 데이터 저장 알림 메시지의 특징
2) 각 자료구조의 특징
3) Stack이 적합한 이유
(2) 배치 전송으로 인한 단점
2. 추가 보완점 개선
(1) 단점 보완 1 : 유효성 검사와 사전 전송 방식으로 메시지 및 파일 제한 초과 문제 해결
1) 문제
2) 해결책
3) 구현 코드
4) 결과
(2) 단점 보완 2 : 추가적인 서비스 클래스 도입으로 로직 단순화
1) 배경
LogDirectSaveGoogleSheetsService
클래스의 경우, 개선 작업 전에는 Google Sheets에 데이터를 저장한 후, Discord로 알림을 전송하는 기능만 수행했었습니다.LogDirectSaveGoogleSheetsService
클래스가 다음과 같이 책임이 과중되어서, 코드가 복잡해지는 문제가 있었습니다.2) 개선 방안 : 스프링 이벤트(Spring Event) 활용 vs 추가적인 서비스 클래스 도입
2. 유연한 확장성: 새로운 기능이나 서비스가 필요할 때, 기존 코드를 수정하지 않고 새로운 이벤트를 추가하거나 기존 이벤트에 대한 리스너를 추가함으로써 유연하게 확장할 수 있습니다.
3. 관심사 분리로 유지보수성 향상
2. 코드 이해 용이: 서비스 클래스가 명확한 역할을 갖고 있어, 코드의 이해도가 높아지고 디버깅이 상대적으로 쉬워집니다.
3. 복잡한 이벤트 시스템 학습 없이 사용 가능: 기존 객체 지향 설계 원칙을 쉽게 적용할 수 있습니다.
2. 디버깅 어려움: 이벤트의 발생 및 처리 흐름을 추적하고 이해하는 데 어려움이 있을 수 있습니다.
3. 학습 곡선: 스프링 이벤트 시스템에 대한 이해와 설정에 일정한 학습 곡선이 있을 수 있습니다.
2. 비동기 처리 어려움: 비동기 처리를 직접 구현해야 하며, 이는 코드 복잡성을 증가시킬 수 있습니다.
3. 이벤트 기반의 유연성 부족: 스프링 이벤트에 비해 기능 확장에 있어 유연성이 떨어질 수 있습니다.
3) 최종 개선책 선택
최종적으로
LogSaveNotificationService
라는 추가적인 서비스 클래스를 도입하기로 결정했습니다. 이유는 다음과 같습니다.LogSaveNotificationService
와 같은 추가적인 서비스 클래스를 도입함으로써, 특정 기능에 대한 책임을 명확히 하고, 코드의 가독성과 유지보수성을 높일 수 있었습니다. 또한, 클래스 간의 의존성을 명확히 하여, 시스템의 복잡도를 줄이고, 디버깅을 더 용이하게 할 수 있었습니다.결론: 스프링 이벤트는 이벤트 기반 비동기 처리가 필요한 상황에서는 유용하지만, 현재의 요구 사항과 상황에서는 동기 작업으로 충분히 책임을 분리할 수 있는 만큼, 코드의 복잡도를 줄이고 관리하기 쉬운 구조를 위해 추가적인 서비스 클래스를 도입하는 것이 더 적합하다고 판단하였습니다.
4) 최종 구현
성과
로그 저장 기능
과로그 저장 결과 저장 및 알림 전송 기능(-> 체크 필요)
을 분리함으로써 코드의 복잡성을 줄이고, 관리하기 쉽게 유지보수성이 향상되었습니다.