본문 바로가기

개발이야기

셀프호스팅 로컬 LLM 채팅 katsubee와 파일 드롭 katsubox 구축기

  • 회사에서 쓸 수 없는 AI 채팅을 로컬 Ollama 기반으로 서버에 직접 올린 것이 katsubee이고, 큰 파일을 올려 정리하는 개인용 파일 드롭이 katsubox입니다.
  • katsubee는 문서 검색, 운영 질의, 주간 보고 작성에 쓰고, katsubox는 Docker 이미지 tar를 Windows FTP로 넘기는 중간 정거장 역할을 합니다.
  • 쓰면서 드러난 개선점은 후속 글에서 하나씩 풀어 갈 예정입니다.

직접 만든 두 서비스가 생긴 배경

같은 Mac mini 한 대에서 두 개의 서비스를 운영하고 있습니다. 하나는 LLM 채팅 katsubee, 다른 하나는 파일 드롭 katsubox입니다. 둘 다 제가 직접 구축했고, Cloudflare 터널은 함께 쓰지만 레포지토리와 프로세스는 따로 나뉘어 있습니다.

시작점은 단순했습니다. 회사에서는 외부 AI 채팅을 쓸 수 없어서, 서버에 직접 올려 쓸 수 있는 채팅이 필요했습니다. 그렇게 만든 것이 katsubee입니다. 로그인하지 않아도 바로 질문할 수 있고, 로그인하면 대화와 문서가 계정에 쌓입니다. 모델은 로컬 Ollama만 사용하며, 대화와 문서의 정본은 자체 Postgres에 보관합니다.

katsubox는 용도가 다릅니다. Docker 이미지 tar처럼 덩치가 큰 파일을 브라우저에서 올리고, 폴더로 정리해 두는 파일 드롭 서비스입니다.

katsubee 채팅 홈 화면
katsubee 채팅 홈. 왼쪽에 대화 목록, 아래에 입력창과 모델 선택이 있다.

katsubee의 스택과 채팅 기능

기술 스택은 JDK 25, Spring Boot 4.1, Spring AI, PostgreSQL(pgvector)입니다. 내장 UI가 있고, 다른 서비스에서는 UI 없이 /api/v1 API와 SDK(katsubee-client)로 연결합니다. 운영 화면은 /admin에 따로 두었으며 Node 기반 BFF와 React SPA로 만들었습니다.

대화 기능

채팅은 SSE 스트리밍으로 응답이 흘러나옵니다. 모델을 직접 고르거나 자동 라우팅에 맡길 수 있고, 파일 첨부와 답변 재생성, 긴 대화를 위한 롤링 요약, 답변 피드백까지 갖췄습니다.

게스트는 IP 한도 안에서 사용하고, 계정 로그인은 아이디와 비밀번호 또는 패스키(WebAuthn)로 합니다. 로그인 세션은 마지막 요청 이후 30일 동안 유지됩니다.

생성 기능과 산출물

같은 화면에서 이미지 생성과 사진 바꾸기, 음악 생성도 됩니다. 모델이 만든 문서를 docx, pdf, xlsx, pptx로 내려받는 산출물 기능도 있습니다.

사진 편집에는 사연이 있습니다. Ollama가 입력 사진을 결과에 반영하지 않는다는 점을 실측으로 확인했고, 그래서 호스트에 별도의 편집 서버를 붙였습니다. 사진은 이 서버 밖으로 나가지 않습니다.

운영 구성

배포는 이중화로 하고, 릴리스는 불변이며 문제가 생기면 자동으로 롤백됩니다. DB는 매일 백업하고, Prometheus와 Grafana, 합성 호출 모니터링을 붙였습니다.

어드민에서는 활성 사용자(DAU, WAU, MAU)와 가입 주 코호트 리텐션, 모델별 사용량, 감사 로그를 확인합니다. 질의 로그는 작성자의 동의와 원문 열람 권한을 분리했습니다. 운영자라 해도 마음대로 대화를 들여다볼 수는 없습니다.

문서를 올리고 묻는 RAG 활용

가장 자주 쓰는 기능은 지식(RAG)입니다. 텍스트, PDF, Word, PPTX 파일을 올리면 pgvector에 임베딩이 저장됩니다. 질문을 던지면 벡터 검색과 키워드 검색 결과를 RRF로 합쳐 근거 문서를 찾습니다.

답변 아래에는 출처 칩이 붙습니다. 칩을 누르면 문서 본문이 열리고, 인용된 구간이 강조되어 보입니다. 답이 어디서 나왔는지 바로 대조할 수 있습니다.

katsubee 지식 라이브러리 화면
katsubee 지식 라이브러리. 올린 문서가 검색 대상이 된다.

지식은 계정 단위, 대화 단위, 1회 단위로 켜고 끌 수 있습니다. 대화를 프로젝트로 묶으면 해당 프로젝트에 올린 문서만 참고합니다. 문서를 외부 서비스로 보내지 않고 답을 받을 수 있다는 점이 로컬 모델을 고집하는 가장 큰 이유입니다.

운영 질문에 쓰는 방식

런북과 배포 토폴로지 문서는 빌드할 때 함께 번들합니다. 여기에 searchRunbooks, readRunbook, appRuntimeInfo 같은 읽기 전용 도구를 연결해 운영 질문에 답하게 했습니다. 채팅 위에 인프라에 그라운딩된 도우미를 얹은 셈입니다.

katsubox: 서버에 내려놓고 FTP로 넘기는 경로

katsubox의 쓰임새는 아주 구체적입니다. 브라우저로 파일을 올리면 Mac mini의 incoming 폴더에 저장됩니다. 이 폴더를 Parallels Windows에 공유 폴더로 열어 두고, Windows의 FTP 클라이언트로 원격 서버에 올립니다.

대용량 업로드 처리

Docker 이미지 tar는 용량이 큽니다. Cloudflare 프록시는 요청 본문이 약 100MB를 넘으면 막습니다. 그래서 큰 파일은 80MB 청크로 쪼개 올리고, 서버에서 스트리밍으로 조립합니다. 다운로드도 진행률이 보이는 스트리밍 방식입니다.

셀프호스팅 로컬 LLM 채팅 katsubee와 파일 드롭 katsubox 구축기
katsubox 파일 목록. 폴더와 페이지 항목, 한 줄 툴바가 보인다. 테스트 데이터 화면이다.

화면과 기능

메인 화면은 파일 목록입니다. 폴더 옆에 글을 쓰는 페이지 항목이 같이 나열되고, 위쪽 한 줄 툴바에 새 폴더, 새 텍스트, 새 페이지, 휴지통이 있습니다. 삭제하면 바로 사라지지 않고 휴지통으로 이동하므로 복원할 수 있습니다.

업로드한 파일에는 로컬 Ollama가 프로젝트와 주제 분류를 붙여 줍니다. 읽는 범위는 파일명, 경로, 텍스트 앞 500자까지이고 외부로 나가는 데이터는 없습니다. 최근에는 파일, 폴더, 페이지에 공유 링크를 만들 수 있게 되었습니다. 방문자가 남긴 댓글은 주인이 승인해야 공개됩니다.

인증과 저장소 선택

인증은 HTTP Basic 하나뿐입니다. 사용자가 한 명이고 신뢰 경계가 Cloudflare 앞단에 있어서, 세션이나 OAuth까지 붙이는 것은 과하다고 판단했습니다.

백엔드는 FastAPI이고 저장소는 로컬 파일시스템입니다. 최종 목적지가 공유 폴더이므로 폴더 구조가 곧 메타데이터 역할을 합니다. 대용량 tar를 DB에 넣는 부담도 피할 수 있습니다. PWA 매니페스트를 넣어 두어서 Chrome이나 Edge에서 앱으로 설치해 쓸 수 있습니다.

티켓 단위 개발과 월요일 주간 보고

두 서비스는 코딩 에이전트가 티켓 단위로 일하는 하네스 위에서 개발합니다. 티켓마다 브랜치를 새로 따고, 검증 스크립트와 CI 게이트를 통과해야 머지됩니다. katsubee에는 채팅 품질 평가(eval) 게이트가 하나 더 붙어 있습니다.

매주 월요일에는 주간 보고가 생성됩니다. 그 주에 머지된 커밋과 티켓, 운영 배포 여부, 디스크와 백업 상태, 사용 중인 기술의 새 릴리스가 정리됩니다. 보고서의 원칙은 확인하지 못한 항목을 확인하지 못했다고 그대로 적는 것입니다. 이 블로그의 주간보고 글도 이 보고서를 바탕으로 합니다.

사용하며 드러난 개선점과 후속 글 계획

쓰다 보니 손볼 곳이 분명해졌습니다. katsubee에서는 지식 검색의 키워드 쪽이 자연어 질문 40개에 대해 한 건도 찾지 못한다는 사실을 발견해 고쳤습니다. 정답 청크 회수는 0/40에서 33/40으로 올랐고, 이 과정은 별도 글로 정리했습니다.

답변 품질에도 숙제가 있습니다. 방문객 SSID를 비밀번호처럼 답하는 문제가 약 절반의 확률로 남아 있습니다. 모델이나 RAG 프롬프트를 바꿀 때마다 다시 측정해야 합니다.

katsubox는 앨범과 공유 링크 메타데이터가 katsubox.db 하나에만 있어서 백업이 가장 중요합니다. 백업 스크립트와 매일 04:10에 도는 launchd 설정은 저장소에 들어왔습니다. 다만 실제로 자동 실행되는지는 아직 확인하지 못했습니다.

프런트엔드는 바닐라 JS에서 TypeScript와 React로 옮겼습니다. Preact 호환 레이어를 써서 번들 크기를 약 430KB에서 약 220KB로 줄였습니다.

이어지는 글은 이 개선점을 순서대로 다룹니다. RAG 검색 개선, 답변 품질 측정, katsubox 백업 자동화 순으로 쓸 계획입니다.