1. 문제 상황: 11초의 대기 시간과 스레드 풀(Thread Pool) 고갈의 위기
Study-Flow 프로젝트의 핵심 기능은 사용자가 문제 이미지를 올리면 AI(Claude 3.5)가 분석하여 퀴즈와 오답노트를 만들어주는 기능입니다. 하지만 실제 테스트를 진행해 보니, AI 분석 결과를 받아오기까지 평균 11.14초라는 엄청난 지연 시간이 발생했습니다.
단순히 "사용자가 오래 기다려서 불편하다"는 UI/UX의 문제를 넘어, 백엔드 아키텍처 관점에서 이는 치명적인 시스템 장애 요인이었습니다. Spring Boot는 기본적으로 요청 1개당 1개의 Tomcat 스레드를 할당(Thread per Request)하는데, 11초 동안 스레드가 응답을 기다리며 블로킹(Blocking) 상태로 묶여있게 됩니다. 만약 여러 사용자가 동시에 AI 분석을 요청한다면 가용 스레드 풀이 순식간에 고갈되어, AI 기능과 전혀 상관없는 일반 게시판 기능까지 서버 전체가 먹통이 될 위험(장애 격리 실패)이 있었습니다.
2. 해결을 위한 고민: Local Cache vs Global Cache (Redis)
외부 Network I/O를 줄이고 응답 속도를 개선하기 위해, 동일한 프롬프트와 이미지 요청이 들어오면 AI 서버를 거치지 않고 저장된 값을 즉시 반환하는 캐싱(Caching) 전략을 도입하기로 했습니다.
이때 어떤 캐시 기술을 사용할지 두 가지를 두고 깊이 고민했습니다.
- 선택지 1: 로컬 캐시 (Caffeine Cache 등)
- 장점: 서버 메모리(RAM)를 직접 사용하므로 속도가 압도적으로 빠르고 인프라 세팅이 필요 없습니다.
- 단점: 다중 서버 환경(Scale-Out)에서는 각 서버가 캐시를 공유하지 못해 데이터 정합성 문제가 발생합니다.
- 선택지 2: 글로벌 캐시 (Redis)
- 장점: 별도의 외부 메모리 서버를 두어, 메인 서버가 여러 대로 증설되더라도 동일한 캐시 데이터를 일관성 있게 공유할 수 있습니다.
- 단점: 네트워크 통신 비용이 추가되고 관리가 필요합니다.
제 선택: Redis (Global Cache) 현재는 단일 서버로 운영 중이지만, 향후 AI 분석 트래픽이 증가하여 서버를 다중화(Scale-Out)하는 상황을 아키텍처 설계 단계부터 고려하고 싶었습니다. 또한, Redis의 강력한 TTL(Time To Live) 관리 기능을 활용하기 위해 Redis를 최종 캐시 저장소로 선택했습니다.
3. 구현 및 엣지 케이스(Edge Case) 트러블슈팅
Spring Boot의 @Cacheable 어노테이션을 활용하여 서비스 로직에 캐시를 적용했습니다. 캐시의 Key(식별자)는 사용자의 요청 내용인 프롬프트(prompt)와 이미지 파일명(filename)을 조합하여 고유하게 생성했습니다.
[적용 코드]
@Transactional
// 사용자의 프롬프트와 파일명을 조합하여 고유한 캐시 키 생성
@Cacheable(value = "aiAnalysis", key = "#prompt + (#file != null ? #file.getOriginalFilename() : 'no_file')")
public AiResponseDto analyzeImage(User user, MultipartFile file, String prompt) {
// ... 외부 WebClient 통신 로직 ...
}
추가 트러블슈팅: NullPointerException 방어 초기 구현 시, 사용자가 이미지를 첨부하지 않고 텍스트(prompt)만 보냈을 때 file.getOriginalFilename()에서 NullPointerException(NPE)이 발생하며 캐시 키 생성에 실패하는 서버 에러가 발생했습니다. 이를 해결하기 위해 SpEL(Spring Expression Language) 내부에서 삼항 연산자 (#file != null ? ... : 'no_file')를 사용하여 파일이 없을 때도 안전하게 캐싱이 동작하도록 예외 상황(Edge Case)을 방어했습니다.
4. 성능 측정 결과 (Performance Metrics)
캐싱을 적용한 후, 완전히 동일한 요청을 다시 보내 서버의 응답 시간을 측정했습니다.

- Cache Miss (최초 요청): AI Analysis Time: 11.14s
- Cache Hit (캐시 적용 후): AI Analysis Time: 0.06s
결과적으로 응답 속도를 11.14초에서 0.06초로 99.4% 획기적으로 단축시켰습니다.
5. 느낀 점
무거운 외부 API 의존성을 Redis로 캐싱하면서 응답 속도 개선은 물론, 메인 서버의 스레드 풀을 안전하게 보호하는 장애 격리(Fault Isolation)의 목적까지 달성할 수 있었습니다. 단순히 "구현했다"에 그치지 않고 성능 지표를 직접 수치화해 보며, 아키텍처 설계 시 확장성(Scale-Out)과 가용성을 왜 미리 고민해야 하는지 깊이 체감할 수 있었습니다.