졸업 프로젝트에서 깃허브 분석본을 생성하는 api가 있다.
이 때, 다 생성된 후 응답을 주면 응답 시간이 30초 이상으로 너무 오래 걸린다.
이런 경우엔 비동기 처리를 해주는게 좋다.
특정 작업이 끝날때까지 기다리지 않고, 바로 다음 일을 진행하는 방식이다.
이전 작업을 끝낸 후 다음 작업을 진행
장점
단점
특정 작업이 끝날때까지 기다리지 않고, 바로 다음 일을 진행
장점
단점
@SpringBootApplication
@EnableJpaAuditing
@EnableAsync
public class GitTurlApplication {
public static void main(String[] args) {
SpringApplication.run(GitTurlApplication.class, args);
}
}
이 설정을 해둬야 @Async 메서드를 감지해서 비동기 처리가 가능하다.
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() { // 스레드 풀 직접 정의하는 메서드
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 항상 유지되는 최소 스레드 수
executor.setMaxPoolSize(10); // 최대 스레드 수 제한
executor.setQueueCapacity(50); // 비동기 작업 요청 대기 큐 크기
executor.setThreadNamePrefix("async-"); // 스레드 이름 prefix 지정
executor.initialize(); // 스레드 풀 초기화 메서드
return executor;
}
}
@Async에서 사용할 스레드 풀(TaskExecutor)을 직접 정의한다.
Spring은 프록시가 @Async를 호출하는데, 같은 파일 내 메서드끼리는 프록시를 안 거치므로 @Async 메서드는 사용될 서비스 메서드가 있는 파일과 분리해야한다.
따라서 ReportAsyncService를 추가로 만들었다.
@Service
@Slf4j
@RequiredArgsConstructor
public class ReportAsyncService {
private final GitLogParser gitLogParser;
private final GitAnalysisService gitAnalysisService;
private final BuildPrompt buildPrompt;
private final GptService gptService;
private final ReportRepository reportRepository;
private final ObjectMapper objectMapper;
private final GitCloneService gitCloneService;
@Async
@Transactional
public void generateReport(Long reportId, Member currentMember, ReportReqDto.Repo dto) {
log.info("비동기 실행됨: {}", Thread.currentThread().getName());
String email = currentMember.getEmail();
String gitUrl = GitRepoParser.getRepoLink(dto.getFullName());
String repoPath = gitCloneService.cloneRepository(gitUrl);
Report report = reportRepository.findById(reportId)
.orElseThrow();
report.updateGenerationStatus(GenerationStatus.PROCESSING);
reportRepository.save(report);
try {
List<GitCommit> commits = gitLogParser.getCommits(repoPath);
List<GitCommit> userCommits = commits.stream()
.filter(c -> c.getAuthorEmail().equals(email) || c.getAuthorEmail().contains(currentMember.getGithubId()))
.toList();
GitAnalysisResult result = gitAnalysisService.analyze(GitRepoParser.getRepoFullName(gitUrl), repoPath, commits, userCommits);
String prompt = buildPrompt.buildReportPrompt(result);
ReportWrapper content = gptService.analyzeGit(prompt);
if (content == null) {
throw new ReportException(ReportErrorCode.GPT_RESPONSE_NOT_FOUND);
}
String contentJson;
try {
contentJson = objectMapper.writeValueAsString(content);
report.updateContent(contentJson);
report.updateGenerationStatus(GenerationStatus.FAIL);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON 변환 실패", e);
}
} catch (Exception e) {
report.updateGenerationStatus(GenerationStatus.FAIL);
}
}
}
이렇게 비동기로 처리를 바꾸니 30초 이상에서 5초대로 빠른 응답을 할 수 있었다.