2025. 11. 25. 15:12ㆍ대외활동/인공지능사관학교

해커톤 2일차는 본격적인 개발이 시작되는 날이었습니다. 전날 팀 내 역할을 정하고 사용자 흐름을 정리해 둔 덕분에 어떤 기능을 어떤 우선순위로 구현해야 할지 명확하게 파악할 수 있었습니다. 하지만 실제 코드를 작성하고 시스템을 연결하는 작업은 예상보다 훨씬 복잡했습니다. 이번 글에서는 음성 인식(STT) 연동, 메뉴 분류 로직 설계, 프론트-백엔드 연결 문제 해결, 멘토링 과정에서의 수정 작업 등 개발 단계에서 겪었던 과정을 순서대로 정리했습니다.


개발 시작 – 가장 먼저 구축해야 했던 핵심 구조
해커톤은 시간이 제한적이기 때문에 가장 중요한 기능부터 빠르게 돌아가도록 만드는 것이 핵심이었습니다.
저희 팀은 다음 3가지를 최우선 구현 목표로 설정했습니다.
- 사용자가 말한 음성을 서버로 전송
- STT(Whisper API)를 통해 음성을 텍스트로 변환
- 텍스트에서 메뉴를 정확히 추출해 UI로 반환
이 3단계가 정상적으로 구성되지 않으면 음성 주문이라는 서비스 전체가 작동할 수 없었기 때문에 무엇보다 먼저 이 기능을 안정적으로 구축해야 했습니다. 이 과정은 이전에 추천 받았던 "LLM을 활용한 AI 에이전트 개발 입문" 책을 참고해서 작성했습니다.

프론트엔드 – 녹음 기능과 음성 전송 방식 구현
프론트엔드는 사용자가 가장 먼저 마주하는 화면이기 때문에 직관성과 단순함을 최우선으로 두었습니다.
1. 녹음 인터페이스 구축
녹음 버튼을 클릭하면 MediaRecorder API가 작동해 음성을 Blob 형태로 수집하고 녹음이 종료되면 백엔드로 전송하는 구조를 먼저 만들었습니다.
여기에서 가장 신경 썼던 부분은 다음과 같습니다.
- 고령층도 바로 알아볼 수 있도록 ‘말하기 시작’ 버튼을 크게 배치
- 녹음 중이라는 것을 쉽게 알 수 있도록 상태 표시
- 음성이 서버로 전송되는 동안 로딩 메시지 제공
녹음 기능이 정상적으로 작동하는 것은 금방 확인했지만 Blob 데이터를 FastAPI로 보내는 과정에서 422(Unprocessable Entity) 오류가 발생하면서 첫 번째 난관을 만났습니다.
2. 422 오류 해결 과정
오류의 원인은 MediaRecorder가 생성하는 Blob 타입과 FastAPI의 파일 입력 모델이 서로 호환되지 않는 부분이었습니다.
백엔드에서는 UploadFile 형태를 예상하고 있었기 때문에 프론트에서 FormData로 형태를 정확히 조정하는 방식으로 문제를 해결했습니다.
이 문제를 해결했을 때 팀 전체가 한 단계 앞으로 나아간 느낌이 들었습니다. 해커톤에서는 이런 작은 오류 하나가 작업 시간을 크게 잡아먹기 때문에 빠르게 고비를 넘기는 것이 중요했습니다.

백엔드 – 음성 인식(STT)과 메뉴 분류 로직 구축
프론트에서 음성이 안정적으로 전송되자 백엔드에서는 다음 기능을 구현해야 했습니다.
- Whisper API로 음성을 텍스트로 변환
- 변환된 텍스트에서 메뉴 이름을 추출
- 필요 시 옵션(사이즈·뜨겁게/차갑게 등)을 자동 판단
1) STT(Whisper API) 연동
Whisper는 정확도가 높아서 고령층의 발음이나 말투에도 비교적 안정적으로 인식하는 편이었습니다. 하지만 처음에는 다음 문제가 발생했습니다.
- 음성 길이가 길면 STT 응답 속도가 느려짐
- 배포 환경에서 파일 경로가 달라져 오류 발생
- 지나치게 긴 문장은 필요 없는 문맥까지 포함하여 분류 성능 저하
이 문제를 해결하기 위해 백엔드에서 다음과 같은 전략을 사용했습니다.
- 음성 길이에 따라 ‘짧은 모드’와 ‘일반 모드’로 분기
- Whisper 반환 텍스트에서 불필요한 감탄사 제거
- 문장 길이를 줄인 후 분류 모델로 전달
이 과정을 거친 후 텍스트 인식 품질이 눈에 띄게 안정되었습니다.
메뉴 분류 – 규칙 기반 + 간단 모델 방식
텍스트가 정제되면 이제 그 문장 안에서 메뉴를 찾아내야 했습니다.
저희 팀은 해커톤 시간 안에 모델을 복잡하게 만들기 어렵기 때문에 다음과 같은 혼합 방식을 사용했습니다.
- 메뉴 이름이 포함되어 있으면 최우선 매칭
- 문장에 없는 경우 유사어 리스트 기반 매칭
- 옵션 정보는 규칙 기반으로 추출:
- “따뜻한”, “뜨겁게” → Hot
- “차갑게”, “시원하게” → Ice
- “큰 사이즈”, “라지”, “L” → Large
이 방식은 단순하지만 안정적이었고 해커톤 환경에서 빠르게 구현하기에 적합했습니다.
UI와 API 연결 – 예상보다 복잡했던 문제
두 부분이 완성되었기 때문에 이제는 프론트에서 말한 문장 → 백엔드가 분류 → 프론트에서 주문 결과 표시 흐름만 연결하면 되는 단계였습니다.
하지만 이 과정에서 또 한 번 문제가 발생했습니다.
- API 응답이 프론트엔드에서 비동기적으로 처리되지 않아 UI 렌더링이 지연
- 녹음 버튼이 재작동하지 않는 현상 발생
- 옵션 확인 팝업이 두 번 호출되는 문제
- 주문 확인 화면에서 메뉴가 undefined로 뜨는 버그
이 문제들은 대부분 “비동기 처리와 상태 관리 구조의 충돌” 때문이었습니다.
해결 방법
- 응답을 받기 전까지 버튼을 비활성화하는 방식 적용
- useEffect 구조 정리
- 상태 값이 초기화되는 조건 명확히 구분
- API 응답 형식을 통일
이 변경만으로 전체 흐름이 훨씬 안정적으로 작동했습니다.
멘토링 시간 – 서비스 완성도를 높이는 질문들
해커톤의 중요한 요소 중 하나가 멘토링이었습니다.
멘토님들은 기술보다는 사용성, 흐름, 서비스 목적에 대한 본질적인 질문을 많이 던지셨습니다.
가장 기억에 남는 질문은 다음과 같았습니다.
- “고령층이 실제로 이 위치에서 말하기 버튼을 찾을 수 있을까요?”
- “메뉴를 두 번 반복해서 말할 경우 어떻게 처리할까요?”
- “추천 메뉴 기능이 있으면 더 이해하기 쉽지 않을까요?”
- “음성 인식 실패 시 사용자에게 어떻게 안내할 건가요?”
이 질문들을 듣고 서비스가 단순히 기술적으로 작동하는 것뿐 아니라 사용자가 실제로 사용할 수 있는지가 더 중요하다는 점을 깨달았습니다.
멘토링 이후 다음과 같은 수정이 즉시 반영되었습니다.
- 버튼을 화면 가장 하단 중앙에 크게 배치
- 오류 발생 시 음성 안내로 “잘 듣지 못했습니다. 다시 말씀해주세요.” 문구 추가
- 추천 메뉴 3개를 자동 표시
- 메뉴 인식 실패 시 주문 키워드 기반 힌트 제공
이 수정들은 전체 사용자 흐름을 훨씬 부드럽게 만들어 주었습니다.
2일차 마무리 – 기능은 완성에 가까워지고 팀워크는 더 단단해졌다
긴 하루였습니다.
오전에는 녹음 기능과 STT 연결에서 막혀 있었고 오후에는 분류 로직과 UI 연결에서 다시 문제가 발생했습니다. 하지만 서로 역할을 넘어 필요한 부분을 도와가며 해결해 나간 덕분에 결국 2일차가 끝날 때에는 음성 → 텍스트 → 메뉴 분류 → UI 표시까지 모든 기본 기능이 작동하는 상태가 되었습니다.
물론 완성된 것은 아니었고 발표 당일에 맞춰 디자인 디테일을 손보거나 오류 처리 방식을 조금 더 다듬어야 했지만 가장 중요한 구조는 모두 갖춰진 상태였습니다. 그래서 이날은 해커톤의 취지에 맞게 거의 밤을 새워 개발을 완성했습니다.
다음 글에서는 최종 발표 준비 과정, 데모 영상 구성, 심사위원 피드백, 발표 당일 느꼈던 점 등을 포함해 3회차 후기를 정리하겠습니다.
아이디어 취합과 개발 과정에서 지친 마음은 중간 중간에 간식도 먹고 바다도 보면서 풀었습니다




'대외활동 > 인공지능사관학교' 카테고리의 다른 글
| [AI Crew][3회차] 음성 기반 주문 키오스크 해커톤 발표기 – 최종 발표와 느낀 점 및 확장 가능성 (1) | 2025.11.25 |
|---|---|
| [AI Crew][1회차] 광주 인공지능 사관학교 해커톤 준비기 – 음성 기반 주문 키오스크 주제 선택 (0) | 2025.11.25 |
| [AI Crew]인공지능사관학교 동문 네트워크 행사 후기|기업 채용부터 퍼컬 진단·기타 연주·뷔페 (0) | 2025.11.24 |
| [AI Crew]AI 음성 기반 민원 안내 시스템 | 프로젝트 방향 전환과 개발 과정 정리 (0) | 2025.11.24 |
| [TIL]정보처리기사 OSI 7계층 정리- 2025.11.04 (0) | 2025.11.04 |