어디서 돌고 있나, 왜 느린가 — 서빙 위치 표시와 채팅 지연 쪼개 보기
두 대(집 서버 + 노트북) 운영 사흘째. 오늘은 “지금 어느 기기가 받고 있지?”를 매번 터미널로 확인하던 일을 어드민 화면으로 옮기고, “Mova 채팅이 왜 이렇게 느리지?”를 단계별 시간으로 쪼개 원인을 나눠 고쳤다.
1. 어드민 홈에 서빙 서버·챗봇 경로 표시
| 내용 | |
|---|---|
| 상황 | 노트북이 꺼지거나 밖에 나가면 집 서버가 넘겨받는데, 지금 어느 쪽이 서빙 중인지 보려면 매번 두 기기에 접속해야 했다 |
| 방법 | 쿠버네티스가 파드에 “자기가 뜬 노드 이름”을 넣어 주게 했다(downward API). 어드민 API는 그 요청을 실제로 처리한 파드의 노드를 돌려주니 추측이 없다 |
| 덤 | 챗봇은 서빙 기기와 따로 움직인다(앱은 집 서버, 언어 모델은 노트북 GPU일 수 있다). 그래서 도메인 챗봇·Mova 챗봇(질문 이해·추천)이 지금 어디서 답하는지도 2초짜리 연결 확인으로 판정해 함께 보여 준다 |
| 결과 | 어드민 홈 오른쪽 위 “서빙 서버 · 집컴”, 아래 “챗봇 LLM 경로” 카드. 판정 규칙은 실제 폴백 코드와 같은 조건으로 쓰고 테스트 9개로 고정 |
같은 날 “Tailscale을 붙였는데 밖에서 GPU가 안 도는 것 같다”는 의심도 확인했다. 집 서버 운영 파드에서 노트북 GPU 경로로 실제 생성 요청을 보내고, 노트북에 모델이 GPU로 올라간 것을 확인했다 — GPU는 밖에서도 돈다. 밖에서 빠지는 건 노트북의 앱 서빙뿐이고, 그건 DB 쿼리가 매번 인터넷을 건너지 않게 일부러 정한 설계다.
2. Mova 채팅이 느렸던 이유 — 22.8초를 쪼개 보니
같은 질문이 첫 번째 22.8초, 바로 다음 4.1초. 로그 시각과 모델 서버 로그를 맞대어 보니 원인이 셋이었다.
| 단계 | 첫 요청 | 다음 요청 | 원인 |
|---|---|---|---|
| 질문 이해 (EXAONE 2.4B, 노트북 GPU) | 4.8초 | 0.8초 | 노트북 재부팅 뒤 첫 호출 — 모델 적재 |
| 검색용 임베딩 (bge-m3) | 2.2초 | 0.2초 | 임베딩 요청이 “계속 올려 둬”를 안 보내 5분 쉬면 모델이 내려갔다 |
| 추천 (EXAONE 2.4B LoRA, 집 서버 GTX 1650) | 약 14초 | 약 3초 | 처음 보는 긴 프롬프트를 읽는 시간. 두 번째는 프롬프트 캐시 덕분 |
- 임베딩은 고쳤다. 상주 설정을 같이 보내고, 실행 위치도 바꿨다. 질문 한 줄 임베딩을 재 보니 집 서버 CPU 직접 약 0.1초, 노트북 GPU를 거치면 약 0.35초 — 계산이 작으면 GPU보다 네트워크 왕복이 더 크다. 그래서 임베딩은 집 서버에 고정했다.
- 추천 단계는 숫자로만 남겼다. 직접 재 보니 1,430토큰 프롬프트 처리 4.1초 + 200토큰 생성 2.8초. 줄이는 길(노트북 GPU로 옮기기, 후보 수 줄이기)은 둘 다 구성 변경이라 다음 과제로 올렸다.
3. 첫 채팅 화면의 바깥 스크롤
채팅 영역이 “화면 − 헤더”를 꽉 채우고 그 아래 푸터가 붙어, 페이지가 푸터 높이만큼 스크롤됐다(900px 화면에 문서 979px). 채팅 페이지에서만 레이아웃 높이를 화면에 고정하고 채팅이 남은 높이를 차지하게 바꿨다. 푸터는 TMDB 출처 고지가 있어 남겼다. 1280·500px 폭에서 전송 전·로딩·답변 뒤 모두 문서 높이 = 화면 높이, 긴 대화는 목록만 안에서 스크롤되는 것을 확인했다.
4. 추천 문구는 실제로 물어보고 골랐다
입력창 아래 추천 문구를 바꾸기 전에 후보 27개를 운영 API에 하나씩 보내 답을 받아 봤다. 그럴듯해 보여도 못 하는 문구가 있었다 — “지금 극장에서 볼 만한 영화”는 엉뚱한 상영관 되묻기, “이번 주 박스오피스 순위”는 인사말, “공포는 싫고 긴장감 있는 영화”는 공포물 추천. 잘 답하는 16개만 남기고, 드러난 버그는 미결 항목(B-014)으로 옮겼다.
남긴 것
- 같은 질문이 드물게 두 번 저장되는 현상(14일 16건)을 로컬에서 경로·지연을 바꿔 가며 재현해 봤지만 재현되지 않았다. 원인을 모른 채 중복 방지 장치로 덮지 않고 관찰 사실만 기록했다(I-008).
오후 — 버그 다섯 개, 그리고 mova 랭킹
채팅 버그 5건(B-014)을 운영 로그로 원인부터 확인하고 고쳤다. 셋이 같은 유형이었다. 질문 이해 모델(2.4B)이 발화에 없는 제목을 지어냈는데(“지금 극장에서 볼 만한 영화” → 어벤져스: 엔드게임), 검증이 “카탈로그에 있는 제목인가”만 봐서 통과했다. 빠진 질문은 “사용자가 그 제목을 말했는가”였다 — 제목(또는 그 낱말)이 발화나 직전 대화에 있을 때만 쓰게 했다.
| 질문 | 전 | 후 |
|---|---|---|
| 지금 극장에서 볼 만한 영화 | 엔드게임 상영관 되묻기 | 이번 주 상영작 안내 |
| 이번 주 박스오피스 순위 | 인사말 | 이번 주 상영작 안내 |
| 공포는 싫고 긴장감 있는 영화 | 컨저링 3 포함 | 공포 장르 제외(스릴러 위주) |
| 인터스텔라 같은 영화 추천 | 인터스텔라 자신 포함 | 그래비티·마션·인셉션 |
| 기생충 평점 어때? | “별점 0(리뷰 0건)”을 지어냄 | mova 리뷰가 없으면 별점을 말하지 않음 |
랭킹의 “AI 검색 TOP”을 “mova 랭킹”으로 바꿨다. 로그인 사용자의 채팅 카드 클릭만 세던 것을, 어디서 들어오든 영화 상세를 연 것(비로그인 포함)으로 센다. 같은 사람·영화·하루는 한 번만(DB 고유 제약), 봇은 제외. 랭킹 스냅샷을 만들던 스케줄러가 노트북에서만 돌아 집 밖이면 멈추던 문제도, 조회 때 최근 7일을 바로 집계하는 방식으로 함께 없앴다.
추천 단계가 느린 이유도 숫자로 확인했다. 집 서버 GTX 1650에서 llama.cpp 옵션(flash attention·배치 크기)을 바꿔 가며 같은 프롬프트로 재 보니 이미 최적이었다(차이 3% 이내). 처음 보는 질문의 추천 단계는 4~5초가 정상치이고, 아침의 22초는 모델 콜드 로딩이 겹친 결과였다.