AI Project Portfolio
LLM을 직접 올려 서비스에 붙이고, 회사 업무를 자동화해 봤습니다
Project 01
AI 영화 큐레이션 플랫폼
사용자가 챗봇에게 말을 걸면 AI 큐레이터가 영화를 추천하고 3D 전시회로 만들어 주는 팀 프로젝트.
챗봇과 LLM 연결, RAG 파이프라인, GPU 운영, OpenAI 호환 API 구축을 담당했습니다.
2025.11 ~ 2026.02
AI 서빙, RAG, GPU 운영, Public API
Qwen-14B, BGE-M3, pgvector, FastAPI, PyTorch, Docker, Prometheus, Grafana
T4의 VRAM은 16GB입니다. Qwen-14B는 fp16이면 파라미터 14B × 2바이트로 가중치만 약 28GB라 그대로는 올라가지 않습니다. 같은 카드에 BGE-M3 임베딩 모델도 함께 띄워야 했습니다.
bitsandbytes 4-bit 양자화로 올렸습니다. 가중치가 fp16의 4분의 1 수준으로 줄어, 임베딩 모델과 답변 생성 중 늘어나는 메모리를 둘 여유가 생깁니다.
BGE-M3가 PyTorch 2.6.0을 요구했습니다. 베이스 이미지를 pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime으로 바꿨습니다. T4가 CUDA 12.4를 지원하는지 먼저 확인했습니다.
PyTorch를 올리자 bitsandbytes 빌드가 깨졌습니다. C 컴파일러가 없어서였습니다. build-essential과 triton을 같이 설치해 해결했습니다.
Qwen-14B를 4-bit로 양자화해 올리고 BGE-M3 임베딩을 함께 띄웠습니다. 둘을 합쳐 T4 16GB 안에서 돌아갑니다.
PyTorch 2.6.0 + CUDA 12.4 런타임 이미지를 베이스로 쓰고, 양자화 라이브러리가 빌드되도록 build-essential과 triton을 추가했습니다.
네이버 관람평을 직접 크롤링해 영화 17,026편에서 133만 건을 모았습니다. 임베딩 모델은 공개된 평가에서 가장 좋은 점수를 받은 BGE-M3를 골라, 이 리뷰들을 벡터로 바꿔 pgvector에 저장했습니다.
적재(embedding_loader)와 검색(retrieval_service)을 나눠 구현했습니다. 요청이 오면 관련 영화를 찾아 생성 컨텍스트로 넘깁니다.
FastAPI로 만든 AI 서버에 전시회 생성(/generate)과 영화 상세(/movie-detail) 엔드포인트를 붙였습니다.
서비스 데이터는 이미 PostgreSQL에 있었습니다. 자원도 빠듯했습니다. 애플리케이션 서버 저장 공간이 부족해 DB를 GPU 서버 쪽에 둬야 할 정도였습니다.
전용 벡터 DB를 넣으면 검색은 강해집니다. 대신 배포하고 관리할 것이 하나 늘어납니다. 팀 프로젝트 규모에서는 그 이점보다 부담이 크다고 봤습니다. 이미 돌고 있는 PostgreSQL에 pgvector를 얹는 쪽을 골랐습니다.
관리할 DB는 그대로 하나입니다. 대신 적재와 검색은 모듈로 갈라 뒀습니다. 나중에 데이터가 커져 전용 엔진이 필요해지면 검색 쪽만 갈아끼우면 됩니다.
처음에는 큐레이터 말투를 LoRA로 학습시키려고 Llama 7B에 붙여 봤습니다. 어느 쪽이 나은지는 감으로 정하지 않았습니다. 같은 질문에 대한 두 응답을 어느 쪽이 무엇인지 가린 채 팀원들에게 보여주고, 말투가 끝까지 일관된 쪽에 투표하게 했습니다. 프롬프팅 쪽이 표를 더 받아 그쪽으로 정했습니다.
테마마다 시스템 프롬프트를 갈아 끼워 모델 하나로 열한 가지 말투를 냈습니다. 페르소나 요약은 매번 새로 만들면 같은 비용이 반복돼 캐싱해 두고 다시 썼습니다.
AI 서비스는 자원 소모가 널뜁니다. GPU 상태를 실시간으로 볼 수 있도록 모니터링 스택을 docker-compose에 같이 넣었습니다.
NVIDIA GPU 메트릭 수집. VRAM 사용량, GPU Utilization, 온도, 전력 소비를 Prometheus 포맷으로 노출합니다.
시스템 메트릭 수집. CPU와 RAM, 디스크 I/O도 같이 봐야 GPU 말고 다른 데서 막히는지 알 수 있습니다.
지표를 모으는 곳. 무한정 쌓이면 디스크가 차서 5GB·7일로 보존 기간을 잘라 뒀습니다.
모은 지표를 한 화면에서 봅니다. GPU 사용량, 시스템 리소스, 모델 서빙 상태를 함께 띄웠습니다. /monitor/ 경로로 서빙합니다.
4-bit로 올린 Qwen-14B는 VRAM 약 8GB
추론이 돌면 로드 직후보다 사용량이 올라감
BGE-M3를 같은 카드에 올린 상태로 16GB 안에서 운영
모델을 외부에서 쓰게 하려면 GPU 서버를 열어야 합니다. 그냥 열면 누가 얼마나 쓰는지 알 수 없고, 서버가 바로 공격 대상이 됩니다. GPU 서버는 내부망에 두고 백엔드만 통로로 뒀습니다. 인증과 사용량 집계, 과금은 전부 그 지점에서 합니다. 외부에는 OpenAI 포맷과 호환되는 API로 냈습니다.
Bearer 토큰 또는 X-API-Key 헤더로 API Key 검증. 만료·폐기 상태 확인
OpenAI messages 배열에서 사용자 프롬프트 추출. ticketId로 페르소나 테마 매핑
백엔드가 내부망(10.0.x.x:5000)의 AI 서버로 요청을 중계. 120초 타임아웃 + 에러 핸들링
AI 서버 응답을 OpenAI chat.completion 포맷으로 바꿔 반환. 토큰 사용량·비용·레이턴시를 DB에 기록
우리 규격을 새로 만들면 쓰는 쪽이 그걸 배워야 합니다. 응답을 OpenAI chat completion 포맷에 맞추면 기존 SDK에서 주소만 바꿔 붙일 수 있습니다.
한 키가 몰아 쓰면 GPU가 막혀 다른 요청까지 느려집니다. 콘솔에서 키를 발급·폐기하고 RPM·TPM·RPD 상한을 걸었습니다.
토큰과 비용, 레이턴시를 요청 단위로 남기고 일별로 모았습니다. 과금 근거이면서, 느려졌을 때 어디부터 볼지 정하는 자료가 됩니다.
Project 02
카카오 엔터프라이즈 인턴 때 만든 LLM 업무 자동화 PoC
기술문서가 업데이트되면 교육 교안에서 수정이 필요한 슬라이드를 자동으로 찾아
LLM이 수정 방향까지 제시하는 파이프라인. 기획부터 설계·구현·시연까지 전 과정을 주도했습니다.
2026.04 ~ 2026.05 (카카오 엔터프라이즈 클라우드솔루션팀)
기획, 아키텍처 설계, 구현, 시연 (전 과정)
OpenAI API, GitHub Actions, PostgreSQL, Python, Marp, SSH Tunnel
그때까지는 기술문서가 바뀌면 사람이 직접 확인하고 교안에 반영했습니다. 무엇을 자동화할지 정하기 전에 이 방식의 문제부터 적어 봤습니다.
기술문서 릴리즈 주기를 교안 담당자가 매번 직접 확인하고 따라가야 합니다.
변경 누락, 오반영, 용어 불일치 같은 실수가 생기기 쉽습니다.
릴리즈 시점과 교안 반영 사이의 시간이 길어질 수 있습니다.
어디를 어떻게 고칠지가 담당자 개인의 판단에 따라 달라집니다.
매일 자동 수집
태그 매칭으로 후보 축소
필요 여부와 수정 방향 도출
리포트 확인 후 최종 반영
사람이 매번 하던 기술문서-교안 현행화를 네 단계 파이프라인으로 바꿨습니다.
하루 1회 자동 배치로 기술문서 commit을 모읍니다. 최근 24시간을 그냥 조회하지 않고 마지막 처리 지점 이후를 읽습니다. 배치가 하루 실패해도 다음 실행에서 그 사이 변경까지 이어서 가져오기 위해서입니다.
commit 메시지만 봐서는 무엇이 바뀐 건지 알 수 없었습니다. 그래서 변경 파일 경로, frontmatter, diff 본문 세 곳에서 태그 후보를 뽑는 방식으로 바꿨습니다.
교안 슬라이드에도 같은 기준으로 태그를 붙여 두고, 겹치는 것만 후보로 좁힙니다. 여기서 좁히지 못하면 다음 단계 비용이 그대로 늘어납니다.
좁혀진 후보만 LLM에 넘겨 고칠 필요가 있는지 묻습니다. 답은 '기존/변경' 형식으로만 받습니다. 결과는 사람이 읽을 Markdown/PDF 리포트로 냅니다.
state 파일로 이미 가져온 건 다시 가져오지 않음. Actions가 실패해도 다음 실행에서 빠진 것부터 이어서 수집
기술문서가 있는 GitHub Enterprise는 사내망 안에 있어 GitHub 클라우드 러너로는 닿지 않습니다. 사내망에 연결된 PC를 self-hosted runner로 쓰고, 태그를 저장한 카카오클라우드 Managed PostgreSQL에는 SSH 터널로 보안 연결을 거쳐 접속했습니다.
개발을 모르는 담당자도 쓸 수 있게 Actions 실행·상태 조회·리포트 확인을 GUI 도구로 만듦
기술문서와 교안 전체를 LLM에 그대로 넣으면 검토 범위가 넓어지고 토큰 비용이 통제되지 않습니다.
태그로 수정 후보 슬라이드만 골라 LLM에 넘기도록 했습니다. 입력이 줄면 검토 범위도 토큰 비용도 같이 줄어듭니다.
매일 배치가 돌아도 LLM 입력은 그날 바뀐 범위만큼만 늘어납니다.
LLM이 후보 범위 밖의 슬라이드까지 판단하거나, 모호한 수정 의견을 내면 결과를 신뢰할 수 없습니다.
후보 밖 슬라이드는 아예 판단하지 못하게 막고, '기존/변경' 형식으로 나온 수정안만 결과로 받았습니다.
리포트에는 무엇을 무엇으로 바꿀지가 적힌 항목만 남습니다.
LLM이 판단한 대로 교안을 바로 고치게 만들 수도 있었습니다. 그렇게 하지 않았습니다. 사람이 마지막에 확인하는 지점을 일부러 남겨 뒀습니다.
LLM은 고칠 필요가 있는지와 어떻게 고칠지를 제안합니다. 결과는 사람이 보기 쉬운 Markdown/PDF 리포트로 나옵니다. 교안에는 손대지 않습니다. LLM이 틀려도 잘못된 교안이 나가지 않습니다.
사람이 승인한 항목만 교안에 자동 반영되게 넓힙니다. 범위는 넓히되 결정권은 사람이 그대로 갖습니다.
수정할 항목을 GitHub Issue로 만들거나 교안 수정 PR을 자동으로 올려, 평소처럼 리뷰하고 머지하는 흐름에 태웁니다.
기간을 직접 지정해 과거 변경분을 다시 돌려보는 모드를 만들었습니다. 이 모드는 상태 파일을 건드리지 않습니다. 운영 데이터를 그대로 두고 결과만 확인할 수 있습니다.
서비스 태그와 기능 태그가 함께 맞을 때만 후보로 삼는 규칙을 계속 보강해야 합니다. 후보가 넓으면 비용이 늘고, 좁으면 놓칩니다.
'기존: … / 변경: …' 형식의 구체적 수정안만 통과시키는 기준을 더 정교화하는 것이 다음 과제입니다.
Summary
Qwen-14B를 4-bit로 양자화해 임베딩 모델과 함께 16GB에 올리고, 응답 속도를 보고 생성 길이를 2048로 낮췄습니다. 말투는 블라인드 비교를 거쳐 학습 대신 프롬프팅으로 구현했습니다.
네이버 관람평 133만 건을 직접 모아 BGE-M3로 임베딩하고, pgvector로 찾은 영화를 생성 컨텍스트로 넘기는 파이프라인을 구현했습니다.
카카오 엔터프라이즈에서 사람이 매번 하던 교안 현행화를 LLM 파이프라인으로 바꾸는 PoC를 기획부터 시연까지 맡았습니다. 교안을 고치는 마지막 단계는 사람에게 남겼습니다.
인증과 과금, 키별 사용량 상한을 붙인 OpenAI 호환 API와 관리 콘솔을 만들었습니다. 다른 팀은 SDK 주소만 바꿔 쓰면 됩니다.