들어가며
우리 서비스 handDoc 에서는 의사의 음성을 텍스트로 변환해야하는 부분이 존재한다. 그리고 STT(Speech-To-Text)를 Naver CLOVA Speech Recognition(CSR)을 통해 구현하기로 하였다.
STT (Speech-To-Text)
사람의 음성을 인식해 텍스트로 변환하는 기술
NAVER CSR (CLOVA Speech Recognition)
네이버 클라우드 플랫폼에서 제공하는 음성 인식 API이다. 한국어 환경에 최적화 되어있다.
- 한국어에 특화된 음성 인식 모델
- 실시간 스트리밍/비동기 처리 모두 지원
- 긴 오디오 파일도 처리 가능
- 화자 분리 (Speaker Diarization) 옵션 제공
사용 방법
1. 네이버 클라우드 플랫폼에 가입한다. https://www.ncloud.com/

2. 콘솔을 눌러보면 콘솔 접근이 불가능한 계정이라고 나온다. 결제 수단을 등록하지 않아서 그렇다. 일단 먼저 결제 수단을 등록해준다.
3. [마이페이지] > [결제 수단 관리] 에 들어가서 결제 수단을 등록한다.
4. 좌측에 [Services] > AI.NAVER API 에 들어가서 [Application 등록] 을 눌러준다.

5. 애플리케이션 이름을 작성하고 등록 버튼을 눌러주면 된다.
아 근데, 아래 문서에 엄청 잘 적혀 있다.. !! 그냥 쭉 따라가면 문제 없을 듯~하다 !
https://guide.ncloud-docs.com/docs/naveropenapiv3-application#Application%EB%93%B1%EB%A1%9D
구현
https://api.ncloud-docs.com/docs/ai-naver-clovaspeechrecognition
CLOVA Speech Recognition (CSR) 개요
api.ncloud-docs.com
이 문서를 참고하면, 쉽게 개발이 가능하다.
0) WebClientConfig 작성
일단 외부 API 호출을 위해, WebClientConfig을 작성했다.
1) NaverCsrApiClient
Client라는 이름을 가진 클래스는 외부 시스템, 외부 API, 서드파티 서비스와의 통신을 담당할 때 사용한다.
NaverCsrClient는 네이버 CLOVA STT API라는 외부 시스템에 요청을 보내고 결과를 받아온다.
@Component
@RequiredArgsConstructor
public class NaverCsrApiClient {
private final WebClient webClient;
@Value("${naver.clova.api-key-id}")
private String apiKeyId;
@Value("${naver.clova.api-key}")
private String apiKey;
private static final String DEFAULT_LANG = "Kor";
public String transcribe(byte[] audioBytes){
return webClient.post()
.uri(uri -> uri.scheme("https")
.host("naveropenapi.apigw.ntruss.com")
.path("/recog/v1/stt")
.queryParam("lang", DEFAULT_LANG)
.build())
.header("X-NCP-APIGW-API-KEY-ID", apiKeyId)
.header("X-NCP-APIGW-API-KEY", apiKey)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.bodyValue(audioBytes)
.retrieve()
.onStatus(s -> s.is4xxClientError() || s.is5xxServerError(),
resp -> resp.bodyToMono(String.class)
.defaultIfEmpty("")
.flatMap(msg-> Mono.error(new IllegalStateException("Clova CSR Error: " + msg))))
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(20))
.block();
}
}
2) Controller
@RestController
@RequiredArgsConstructor
@Tag(name = "Speech", description = "음성 관련 API")
@RequestMapping("/api/v1/stt")
public class SpeechController {
private final NaverCsrClient naverCsrClient;
@PostMapping(value = "", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "음성을 텍스트로 변환하는 API", description = "음성 파일을 업로드하여 호출합니다.")
public ResponseEntity<String> stt(@RequestParam("file") MultipartFile file) throws Exception{
String result = naverCsrClient.transcribe(file.getBytes());
return ResponseEntity.ok(result);
}
}
마무리

https://github.com/3-NoPainNoGain/BE/pull/8
pr 링크를 마지막으로 이만... !!
'프로젝트 > handDoc' 카테고리의 다른 글
| [AI] 수어 동작 인식 모델 만들기 - CNN과 LSTM (0) | 2025.10.01 |
|---|---|
| [AI] 구음장애 환자 발화 데이터셋을 활용한 Whisper 모델 파인튜닝 (0) | 2025.09.21 |
| [Spring] WebRTC 정리 - 시그널링 서버와 STUN/TURN 서버 (0) | 2025.09.08 |
| [Spring] MongoDB Atlas 설정하기 (0) | 2025.08.04 |
| [Spring] MongoDB 도입하기 with Docker & MongoDB Compass (0) | 2025.08.04 |
