Skip to content

Fix/#47 tts error - #48

Merged
2-dayeon merged 10 commits into
mainfrom
fix/#47-tts-error
Oct 20, 2026
Merged

2-dayeon merged 10 commits into
mainfrom
fix/#47-tts-error

Conversation

@2-dayeon

Copy link
Copy Markdown
Contributor

47

@kimwuju kimwuju left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 남깁니다. 핵심 수정 방향은 맞고 CI도 통과했습니다. ElevenLabs 전용 WebClient에만 수신 한도를 두고, 한도 초과를 재시도 불가 TtsClientException으로 바꾸고, chunked/non-chunked 회귀 테스트까지 넣은 점이 좋습니다.

머지를 막을 이슈는 없습니다. 인라인 코멘트는 모두 우선순위가 낮은 제안이에요.

  • TTS가 꺼져 있어도 한도 검증 때문에 서버 기동이 실패할 수 있음
  • ConnectionProvider dispose 누락 (기존 코드인데 새 테스트에서도 반복됨)
  • DataSize 타입, 에러 코드 구분, Boot WebClient.Builder 재사용 제안
  • 문서 범위, 기본값 중복, 생성자 정리 (nit)

@Bean("elevenLabsWebClient")
public WebClient elevenLabsWebClient(TtsProperties properties) {
TtsProperties.ElevenLabs config = properties.getElevenlabs();
Assert.isTrue(config.getMaxInMemorySize() > 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[낮음] TTS가 꺼져 있어도 이 검증 때문에 서버가 안 뜰 수 있어요

elevenLabsWebClient 빈은 tts.enabled와 관계없이 항상 생성됩니다. 그래서 TTS_ENABLED=false인 환경에서도 ELEVENLABS_MAX_IN_MEMORY_SIZE가 0 이하로 들어가면 Chat 서버 전체가 기동에 실패합니다.

잘못된 설정을 빨리 드러내려는 의도라면 그대로 두셔도 됩니다. TTS 설정 하나 때문에 면접 기능 전체가 막히는 게 부담이라면 properties.isEnabled()일 때만 검사하는 방법도 있어요.

TtsProperties.ElevenLabs config = properties.getElevenlabs();
Assert.isTrue(config.getMaxInMemorySize() > 0,
"tts.elevenlabs.max-in-memory-size는 양수여야 합니다");
ConnectionProvider provider = ConnectionProvider.builder("elevenlabs")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[낮음] ConnectionProvider가 dispose되지 않아요 (기존 코드 + 새 테스트)

ConnectionProvider를 지역 변수로 만들기 때문에 애플리케이션이 종료될 때 정리되지 않습니다. AnthropicWebClientConfig의 anthropicConnectionProvider처럼 @Bean(destroyMethod = "dispose")로 분리하면 종료 시 커넥션 풀도 함께 정리됩니다.

새 ElevenLabsTtsClientTest도 client()를 부를 때마다 elevenlabs 풀을 새로 만들고 해제하지 않아서, 테스트 수만큼 풀이 JVM에 남습니다. 기존 코드라 이 PR 범위 밖이라면 이슈로 따로 빼도 괜찮습니다.

private String outputFormat = "mp3_44100_128";
private Duration connectTimeout = Duration.ofSeconds(3);
private Duration responseTimeout = Duration.ofSeconds(20);
private int maxInMemorySize = 8 * 1024 * 1024;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[제안] int 대신 DataSize는 어떨까요?

같은 클래스의 시간 설정은 Duration이라 20s처럼 쓸 수 있는데, 크기 설정만 바이트 정수라서 8MB라고 쓰면 변환 오류로 기동이 실패합니다. Spring Boot의 spring.codec.max-in-memory-size도 DataSize를 씁니다.

private DataSize maxInMemorySize = DataSize.ofMegabytes(8);

WebClient 쪽에는 Math.toIntExact(config.getMaxInMemorySize().toBytes())로 넘기면 됩니다. 단위가 없는 8388608 같은 기존 값도 바이트로 그대로 바인딩됩니다.

));
}
return response.bodyToMono(byte[].class)
.onErrorMap(DataBufferLimitException.class, error -> new TtsClientException(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[제안] 한도 초과를 다른 실패와 구분할 수 있으면 좋겠어요

재시도하지 않게 바꾼 건 좋습니다. 이전에는 TtsClientException이 아니어서 스케줄러가 같은 합성을 2번 더 요청했죠.

다만 InterviewTtsScheduler.completeFailed에서는 이 경우도 TTS_GENERATION_FAILED로 저장됩니다. 그래서 상태나 메트릭만으로는 ElevenLabs 4xx, 빈 오디오와 구분되지 않고, 한도를 낮게 잡아 FAILED가 반복되면 WARN 로그 메시지를 직접 봐야 원인을 알 수 있어요. 예외에 에러 코드(예: TTS_AUDIO_TOO_LARGE)를 실어 스케줄러가 그대로 쓰게 하면 운영에서 바로 보입니다.

@@ -28,6 +31,7 @@ public WebClient elevenLabsWebClient(TtsProperties properties) {
return WebClient.builder()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[제안] Boot의 WebClient.Builder를 쓰면 설정이 한곳으로 모여요

여기는 정적 WebClient.builder()를 써서 Boot가 구성한 빌더(코덱 설정, HTTP 클라이언트 관측·메트릭 커스터마이저)를 거치지 않습니다. 256KiB 기본값이 그대로 적용됐던 이유이기도 하고요.

AnthropicWebClientConfig처럼 WebClient.Builder를 주입받아 clone()한 뒤 .codecs(...)로 이 클라이언트의 한도만 올리면, 다른 WebClient에는 영향 없이 메트릭도 함께 받을 수 있습니다. 이 PR에서 꼭 바꿀 필요는 없습니다.

Comment thread docs/tts-audio-buffer.md Outdated
배포 뒤 새로 준비한 면접의 긴 질문에서 오디오 READY와 재생을 확인한다.
이미 FAILED로 저장된 오디오는 이 변경만으로 자동 재생성되지 않는다.

## 녹음 업로드와 다음 질문의 구분

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[제안] 이 섹션은 TTS 수신 한도와 다른 내용이라 분리하면 좋겠어요

녹음 업로드, 답변 제출, Web STT 동작 설명은 #47을 조사하다 나온 맥락으로 보입니다. 그런데 tts-audio-buffer.md라는 파일명으로는 찾기 어렵습니다. 특히 "Web은 음성 인식 텍스트가 비어 있으면 … 제출하지 않는다"는 이 저장소 코드로 보장되지 않는 내용이라, Web 쪽이 바뀌면 문서가 조용히 틀리게 됩니다.

이슈 #47 코멘트나 PR 본문으로 옮기거나 별도 문서로 빼는 걸 제안합니다.

Comment thread docker-compose.yml
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL}
TTS_ENABLED: ${TTS_ENABLED:-false}
ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}
ELEVENLABS_MAX_IN_MEMORY_SIZE: ${ELEVENLABS_MAX_IN_MEMORY_SIZE:-8388608}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] 기본값 8388608이 세 군데에 있어요

TtsProperties(8 * 1024 * 1024), application.yml(${ELEVENLABS_MAX_IN_MEMORY_SIZE:8388608}), 여기 compose 기본값에 같은 값이 각각 하드코딩돼 있습니다. compose는 빈 문자열이 넘어가지 않도록 기본값이 필요하니 의도된 거라면 그대로 두셔도 됩니다. 기본값을 바꿀 때 세 곳을 함께 바꿔야 한다는 점만 문서에 적어 두면 좋겠어요.

return retryable;
}

public TtsClientException(String message, boolean retryable, Throwable cause) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] 새 생성자가 isRetryable() 아래에 있어서, 생성자끼리 묶어 두면 읽기 편할 것 같아요. 기존 2인자 생성자를 this(message, retryable, null)로 위임하면 필드 초기화도 한곳에 모입니다.

@danjee2009 danjee2009 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경된 ElevenLabs 응답 크기 제한과 설정 검증, 초과 응답의 비재시도 처리 및 관련 테스트를 검토했습니다. 확인된 실질적 문제는 없습니다. GitHub의 test 검사는 통과했습니다.

@2-dayeon
2-dayeon merged commit c66ce05 into main Oct 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants