GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

요청이 짧다고 영상까지 짧아지면 안 됩니다.
빈칸은 LLM의 저작으로 채우고,
채워진 칸은 아무도 건드리지 않습니다.

 

우선 작업 결과부터 보고가겠습니다.

프롬프트

우리 토리 소개 영상 만들어줘

(애견카페 장면을 집에서 쉬는 장면으로 오해한 할루시네이션이 포함됨)

 

프롬프트

우리 토리 소개 영상 만들어줘 뛰어노는 모습 좀 나오다가 산책하고 밤에도 달리고 마지막에 하이파이브로 끝나면 딱이겠다

 

'소개 영상 만들어줘' 한 줄이면 알아서 잘 나와야 하는 것 아냐?

 

이번 함정은 파이프라인이 아니라 요청 쪽에 있었습니다. 한 줄 요청에는 구조 정보가 없습니다. 몇 블록으로, 어떤 순서로, 자막은 뭐라고, 아무것도 없죠. 지난 편까지의 파이프라인은 이 요청에 성실하게 답했습니다. 기본값 블록 하나, 소스당 한 컷씩 26초. 하지만 "소개 영상 만들어줘"라는 요청에는 소개 영상이 아니라 영상의 나열이 나왔습니다. 번역할 문장이 없으면 번역기는 구성을 못 합니다.

 

유저가 정하지 않은 것만 LLM이 저작하고,
유저가 정한 것은 LLM이 마음대로 건드리지 않는다.

 

잠시 배경 하나를 설명드리면 지난 편 이후 남았던 수동 단계 둘은 자동화로 풀었습니다. 영상마다 기계가 "무엇이 찍혔는지" 적어두는 관찰 프로필, 그리고 강아지 사진 몇 장으로 여러 마리 중 주인공을 확정하는 프레임 앵커. 덕분에 영상과 사진만 넣으면 결과가 나옵니다. 이번 편은 고정 형태의 파이프라인을 잘 만드는 파이프라인으로 다듬은 기록입니다. 한 줄 요청을 구성으로 바꾸는 저작 모드. 요청 디테일 천차만별에 대응하는 소유권 원칙. 그리고 실사용이 낸 숙제들. 결론부터 말하면 이번 구간에서 같은 문제를 네 번 고치고 나서야 규칙 하나를 얻었습니다. 프롬프트에는 지시만 적는다. 내용은 금지.

 

Two decisions, one pipeline


12. 한 줄 요청을 구성으로 - 저작 모드

편집의 구조가 요청에 없다면 어떻게 만들어야 할까요. 소재와 목적. 무엇이 찍혀 있는지(관찰 프로필)와 무엇을 원하는지(요청의 느낌)를 알면 구성은 만들 수 있습니다. 그래서 번역기 앞에 LLM을 다시 세웠습니다. 간단한 요청이 들어오면 LLM이 요청 원문과 영상별 관찰 프로필, 그리고 소재 길이를 보고 블록을 직접 구성합니다. 어떤 영상을 어떤 순서로, 몇 초씩, 자막은 뭐라고 할지 말이죠.

 

소재 길이를 입력에 넣은 건 실측 때문입니다. 길이를 모르는 LLM의 저작은 2.3초짜리 영상을 인트로와 엔딩 두 블록에 배치하는 실수를 했습니다. LLM에게도 재료의 속성은 알려줘야 합니다.

 

재미있는 결정 하나가 있습니다. LLM의 저작은 이 파이프라인 전체에서 유일하게 의도된 비결정입니다. 지금까지 "같은 입력 = 같은 출력"을 그렇게 지켜놓았지만 저작은 정답이 없는 주관 축이기 때문입니다. 같은 요청에서 매번 다른 구성이 나와도 되고, 마음에 안 들면 다시 뽑으면 됩니다(빠른 재추첨 옵션 추가). 결정론은 정답이 있는 곳의 규칙이지, 창작을 막으면 안되니까요.

 

다만 창작에도 결정론은 붙습니다. 작가가 지어낸 소재 이름은 실제 목록과 대조해 환각을 제거하고, 블록 길이와 개수는 Clamp하고, 배속은 [요청이 명시한 경우만]이라는 가드를 유지합니다. 실측 샘플로 "문틈 사이로 빼꼼! 우리 토리 등장!" 같은 자막이 나왔는데, 해당 영상의 관찰 프로필에 실제로 문틈 장면이 기록돼 있었습니다. 창작이되, 근거 있는 창작입니다.


13. 소유권은 이진, 빈칸은 Gradient - 요청 디테일 대응

실제 요청은 한 줄만 오지 않습니다. "소개 영상 만들어줘"부터, "뛰어노는 모습 나오다가 산책하고 밤에 달리고 하이파이브로 끝" 같은 스케치, 블록별 초와 자막까지 박은 풀 스펙까지. 디테일이 천차만별입니다. 처음엔 [요청이 얼마나 구조적인가]를 점수로 재려 했습니다. 잘못된 접근이었습니다. 구조는 결국 하나로 귀결돼야 합니다. 유저가 뼈대를 줬으면 구조는 유저 것이고, 안 줬으면 LLM이 저작합니다. Gradient인 건 구조가 아니라 빈 필드의 수입니다.

요청 수준 결과
"소개 영상 만들어줘" 전체 저작 작가가 구성 창작 (4~5블록)
"뛰어놀다가 → 산책 → 밤 달리기 → 하이파이브 엔딩" 부분 저작 유저 4박자 보존 + 빈칸(자막·초·소재)만 채움
블록별 스펙 완비 저작 0호출 빈칸 없음 → 작가 미출동

 

병합은 결정론입니다. LLM의 저작에서 유저가 명시한 필드는 아예 읽지 않습니다. "덮어쓰지 않도록 주의한다"가 아니라 읽는 코드 자체가 없는 구조적 불변입니다. 대본 스킵, 복창 사고와 같은 문제들이 재발하지 않도록 원천 차단한 겁니다.

 

텍스트는 한 단계 더 엄격합니다. 요청에 자막 지시가 하나라도 있으면 자막은 통째로 유저의 소유입니다. LLM은 빈 블록의 자막조차 채우지 않습니다. 절반의 자막을 유저가 썼는데 나머지 절반을 LLM이 채우면 유저의 의도가 사라질 수 있습니다. 덤으로 잠재돼있던 버그도 하나 잡았습니다. [자막 없이]라는 요청에 [자막]이라는 단어가 포함돼 있어서 오히려 자막을 채우던 것입니다. 부정 표현이 긍정 표현보다 우선한다는 원칙으로 수정했습니다.


14. 프롬프트에는 지시만 - 같은 문제만 네 번

이번 구간에서 제일 어려웠던 문제였습니다. 자막에 요청 전문을 그대로 발화하는 사고가 났습니다("…만들어줘"까지 통째로). 1차 수정은 결정론으로 필터링 했습니다. 출력에서 요청 문자열을 지우는 필터였습니다. 그런데 이건 단순히 표면적으로 문제를 지웠습니다. 실질적 문제는 프롬프트에 있었는데 말입니다. "요청의 말투를 그대로 살려서"라는 지시가 사실상 인용의 원인이었던 겁니다. 프롬프트를 "요청은 지시문이고, 자막은 시청자에게 말하는 새 문장"으로 고치자 4회 배터리에서 복창 0건(수정 전 2/4)으로 수정되었습니다.

 

같은 문제가 또 발생했습니다. 요청에 없는 캠페인성 문구가 자막에 붙어 나왔는데 추적해 보니 프롬프트에 박아둔 페르소나(~홍보 영상 편집기)가 요청에 없는 목적을 주입하고 있었습니다. 프롬프트 6곳을 전부 중립적인 "강아지 영상"으로 고쳤습니다. 목적과 톤은 요청에서만 옵니다.

 

패턴을 정리하면 네 번 모두 같은 문제였습니다. 코드나 프롬프트에 박힌 내용(어휘, 예시 문구, 프레이밍)이 출력으로 샌 것입니다. 그래서 규칙을 정리했습니다. 프롬프트에는 지시만, 내용은 금지. 결정론은 폐기하지 않고 가드로 강등했습니다.


15. 실사용이 낸 숙제들 - 폴리싱과 권한

실사용 데모를 돌리며 자잘한 숙제들이 쏟아졌습니다. 영상이 12.2초로 짧게 나오고(LLM이 길이 필드를 안 적는 습성. 스키마에서 필수로 강제), 0.8초짜리 컷이 화면에서 번쩍하고 지나가고(표시 하한 1.5초 규칙 추가), 제목과 자막이 한 자리에 겹쳐 찍혔습니다. 자막 겹침은 8방위 텍스트 영역 시스템으로 풀었습니다. 상단, 하단, 좌우의 네 구석에 텍스트 자리를 정의하였고, 상단 행은 AI 표시 배지를 침범하지 않게 그 아래에서 시작하며, 긴 자막은 자동 개행합니다. 위치와 표시 타이밍도 소유권 원칙 그대로 유지했습니다. 유저가 명시하면("왼쪽 아래에 자막") 번역기가 받아 적고, 아니면 LLM의 연출 재량입니다.

 

인프라 쪽에서는 무서운 버그를 하나 잡았습니다. 전처리 산출물이 깨졌을 때 파일이 존재한다는 이유로 재사용되면서 깨진 캐시가 계속 사용되는 버그였습니다. 영상 하나의 분석이 통째로 증발했습니다. 재사용 조건에 내용 검증을 추가했습니다. TTS에서도 하나, 첫 구절 앞에 "흠" 하는 발성이 붙은 걸 Whisper 왕복과 파형으로 문제를 파악하고 해당 구절만 재합성해 지웠습니다.

 

마지막이 이번 편의 하이라이트입니다. 사진으로 지정한 주인공이 안 나오는 장면이 렌더에 들어왔습니다. 고양이만 걸어다니는 복도 컷이었죠. 문제를 추적하니 LLM이 소스를 직접 지정하면서 시스템이 이를 확실한 선택으로 취급해 Uncertain 구간임에도 영상으로 들어왔습니다. LLM의 선택은 영상 단위일 뿐인데 그것이 주인공이 없는 구간을 선택해버렸습니다. Uncertain 구간이 영상 단위 선택을 타고 그대로 들어온 겁니다.

 

수정은 권한의 분리였습니다. 유저의 프롬프트는 "그 장면을 원한다"는 보증이라 Uncertain 면제 + 소스 예약을 다 받지만, LLM의 지정은 "그 영상이 어울릴 것 같다"는 판단일 뿐이라 예약만 받았습니다. 여기에 프레임 앵커에 있는 확정된 주인공 박스로 실제 존재하는 구간인지 교차해서 클립을 뽑게 했습니다. 재렌더에서 고양이 컷은 사라졌고 모든 클립에 주인공이 있는 걸 프레임 단위로 확인했습니다.

 


맺음말

여기까지로 파이프라인이 나오는 것을 넘어 만드는 쪽까지 넘어왔습니다.

  • 저작 모드는 한 줄 요청에서 소재와 목적을 근거로 구성을 창작하고(파이프라인 유일의 의도된 비결정),
  • 소유권 원칙은 구조를 하나로 귀결시켜 유저가 정한 필드는 LLM의 저작이 아예 간섭하지 못하게 하고,
  • 프롬프트는 같은 문제 네 번 끝에 [지시만, 내용 금지]로 확립하고,
  • 권한 분리는 유저의 보증과 LLM 저작의 보증에 다른 크기의 권한을 줬습니다.

시리즈의 문장들이 이번 편에서 하나 더 늘었습니다. 정답지로 채점하고, 측정할 수 있는 건 측정에게 맡기고, 소리도 채점하고. 이것에 더해 이번엔 하나의 구조로 귀결시켜라. 구조든 텍스트든 권한이든 구조가 나뉘어지니 문제가 많아졌습니다.

 

남은 것은 저작 품질의 평가입니다. 구성이 좋은지는 골든셋으로 채점할 수 없는 주관 축이라, 지금은 재추첨 복권이 UX의 전부입니다. 저작 결과를 고객이 미리 보고 고르는 카드 UI, 그리고 이 파이프라인을 제품으로 감싸는 일. 다음 편에서 이어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

스레드보다 자원: Mac GPU(MPS)로 3.5배 빠르게 - Resource First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com파이프라인을 빠르게 만드는 첫 질문은"스레드를 몇 개 띄울까?"가 아니라"어떤 자원이 어디서 놀고

chessire.tistory.com

이전글

 

로컬 TTS(mlx-audio)로 영상 타임라인 컨트롤 - Narration First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com목소리를 얹는 순간,화면이 음성에 맞춰 늘어나고,타임라인은 음성이 컨트롤합니다. 우선 작업 결과

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

1. 강아지를 입양했다

기술을 먼저 정하고 문제를 찾은 게 아닙니다.
한 마리를 데려오고 나서야
풀어야 할 문제가 보였습니다.

 AI로 무언가를 만들어보려던 참이었습니다. 도구는 손에 있는데 무엇을 풀지가 없던 흔한 상태였습니다.

 

 최근에 강아지를 입양해왔습니다. 5월 초부터 마음을 정해두고, 아내가 매일같이 보호 글을 들여다보며 인연을 찾았습니다. 그렇게 5월 24일, 강릉의 한 보호소와 연계된 임시보호자님에게 있던 아기 강아지를 데려왔습니다.

 

 이 아이의 사정은 이랬습니다. 떠돌이 어미가 낳은 새끼들 중 하나였고, 새끼들만 구조되었고 어미는 끝내 구조되지 못했다고 들었습니다. 세 마리 중 둘은 먼저 가족을 찾았고, 가장 오래 떠나지 못하고 남아 있던 아이가 우리에게 왔습니다.

 

 데려오는 과정에서 예상하지 못한 걸 봤습니다. 임시보호자와 입양 희망자 사이에서 오가는 소통, 정보, 그 사이의 불편함이 분명히 존재했습니다. 사람 손만으로는 메우기 버거운 자리였습니다.

 

 그 순간 두 가지가 겹쳤습니다. 풀 문제를 찾던 나, 그리고 눈앞에 보인 진짜 불편함. 마침 AI를 만들고 싶었던 저는 그 틈을 AI로 메울 수 있겠다 싶었습니다. 그래서 결심했습니다. 이 틈을 내 능력으로 메워보자고요.

 

 이 글은 그 결심에서 출발한 기록입니다. 그리고 다음 이야기들은 "왜 로컬인지", "왜 이렇게 도구를 쪼갰는지"를 다뤄보고자 합니다.

 

귀여운 토리


2. 영상 - 왜 전부 LLM에 맡기지 않을까?

"AI한테 영상 던지고 '이 강아지 뭐 해?' 물어보면 되잖아."
이 방법은 가장 비싸고 가장 느리게 도착하는 방법입니다.

 

 보호소 강아지의 영상들을 가지고 입양 공고 콘텐츠로 만든다고 해봅시다. 멀티모달 LLM에 영상을 통째로 밀어 넣으면 끝날 것 같습니다. 하지만 이 출발점은 비용도 속도도 가장 안 좋은 형태로 시작됩니다.

 

 여기서 짚어야 할 게 있습니다. "AI"는 한 덩어리가 아니라 파이프라인입니다. 영상 한 편을 처리하는 데에도 성격이 다른 셋이 섞입니다.

  1. 고전 알고리즘 (OpenCV) - 학습이 없습니다. 수학과 규칙으로 돕니다. 빠르고, 싸고, 결과가 일정합니다.
  2. 학습된 전용 신경망 (YOLO) - AI지만 "검출" 한 가지에 특화돼 있습니다. 프레임마다 강아지의 박스와 위치를 찾습니다.
  3. 생성형 LLM (gemma) - 의미를 판단합니다. "이 강아지는 지금 사람을 반기는 중"이라는 해석. 강력하지만 느리고, 비싸고, 가끔 틀립니다.

 만약 강아지가 화면 어디 있는지 박스 좌표를 찾는 일까지 LLM에 시키면 어떻게 될까요? 느리고, 부정확하고, 비용만 듭니다. 그건 YOLO가 훨씬 잘하고 싸게 하는 일입니다.

 

 그래서 설계의 핵심은 이렇게 정리됩니다.

 

 잘 만든 설계는 무거운 생성형 LLM을 '꼭 필요한 한 스텝'에만 씁니다. 나머지는 싸고 빠른 도구로 분리합니다.

 

 실제 영상 한 편은 이렇게 흘러갑니다.

  • 프레임 자르기·인코딩·자막 입히기 -> ffmpeg (그냥 도구)
  • 강아지가 어디 있나(박스) -> YOLO (검출)
  • 그 강아지를 영상 내내 같은 ID로 유지 -> ByteTrack (추적)
  • 마지막에 딱 한 번, "이 장면을 입양 공고용으로 어떻게 설명할까"를 저작 -> gemma (의미·생성)
  • 대본을 나레이션 음성으로 입힌다 -> mlx-audio (TTS)

 작은 비영리에서 이건 취향이 아닌 비용 절감입니다. LLM 호출 한 번이 곧 비용이고 솔로 운영에서 느린 파이프라인은 곧 굴리지 못하는 파이프라인이 됩니다. 무거운 도구를 아껴 쓰는 설계가 비영리 사업을 지속 가능하게 만드는 조건이 될 수 있습니다.

 

 이 "계층 분리"는 영상에만 해당하는 이야기가 아닙니다. 사실 이 프로젝트 전체를 관통하는 질문이고 다음 DB 이야기도 똑같은 자리에서 출발합니다.


3. DB - 왜 Postgres와 벡터DB를 같이 둘까?

하나는 "정확히 일치하는 것"을 찾고
다른 하나는 "비슷한 의미"를 찾습니다.

 

 DB를 두 개나 쓴다고 하면 과해 보입니다. "그냥 데이터베이스 하나면 되지 않나?" 하지만 이건 중복이 아니라 2번에서 본 것과 똑같은 역할 분리입니다. 두 DB는 애초에 답하는 질문이 다릅니다.

  1. PostgreSQL
    "정확히 일치하는 것"을 찾는다. 회원, 강아지 공고, 입양 신청처럼 행과 열로 떨어지는 데이터입니다. "보호 중이고, 중성화 완료이고, 3살 이하인 강아지" — 조건이 명확하고, 답도 딱 떨어집니다. 이러한 과정은 관계형 DB가 최적입니다.
  2. 벡터DB(chromadb)
    "비슷한 의미"를 찾는다. "사람을 잘 따르고 아파트에서도 키울 만한 순한 아이" 같은 질문은 다릅니다. 어느 칸에도 그렇게 적혀 있지 않습니다. 이건 의미가 가까운 데이터를 찾는 일이고 텍스트를 숫자 벡터로 바꿔(임베딩) 거리로 비교해야 됩니다. 관계형 DB의 WHERE 조건으로는 풀기 어려운 문제입니다.

 그래서 둘은 대체재가 아닙니다. 하나는 사실을 정확히 찾고, 하나는 의미를 비슷하게 찾습니다. 응대봇이 "이 조건에 맞는 강아지"(Postgres)와 "이 느낌의 강아지"(벡터DB)를 동시에 답하려면 둘 다 필요합니다.

 

 이게 2번의 영상 이야기와 똑같은 자리인 이유가 여기 있습니다. "만능 도구 하나로 다 해결한다"가 아니라 "질문의 성격에 맞는 도구를 선택한다". LLM에 전부 안 시키듯, DB도 한 종류에 전부 안 맡깁니다.

 

 다만 솔직히 둘 다 끌고 가는 건 비용입니다. 운영할 DB가 둘이 되니까요. 그래서 지금은 명확히 분리해 두되, 규모가 커지면 한쪽으로 합치는 길도 열어둡니다. PostgreSQL에 벡터 검색 확장(pgvector)을 얹어 한 DB에서 사실과 의미를 같이 다루는 선택지입니다. 초기에는 역할을 선명하게 분리하고, 규모가 커지면 운영 부담을 줄이는 쪽으로 통합하려 합니다.


4. 응대봇 - 왜 로컬이며, 왜 이 모델일까?

똑똑한 모델 하나를 고르는 문제가 아닙니다.
"무엇을 사용해야 하는가"와
"어떤 일에 어떤 모델을 쓰는가"입니다.

 

 입양 문의에 답하는 봇을 만든다고 해봅시다. 가장 쉬운 길은 외부 API(예: GPT)를 부르는 겁니다. 그런데 그 길을 택하는 순간, 1편 첫머리에서 던졌던 질문이 그대로 돌아옵니다. 무엇을 로컬에 두고 무엇을 밖에 맡길 것인가.

 

 왜 로컬(Ollama)인가. 세 가지 이유입니다.

  • 데이터. 강아지 정보, 보호 기록, 입양 신청자 문의가 매 호출마다 외부 서버로 나갑니다. 명분 사업에서 보호 동물과 신청자의 데이터를 남의 서버에 흘리는 것은 가볍지 않습니다. 로컬은 그 정보들이 내 기기 밖으로 안 흘러나갑니다.
  • 비용. 외부 API는 호출당 과금입니다. 응대봇은 많이 부를수록 가치가 큰데 많이 부를수록 그대로 토큰 값이 됩니다. 로컬 모델은 전기값 외에 호출당 비용이 0입니다.
  • 통제. 모델이 갑자기 바뀌거나 가격이 오르거나 정책으로 막히는 일에서 자유롭습니다. 예측 가능함이 곧 안정성입니다.

왜 이 모델 조합인가. 여기서도 2번의 "계층 분리"가 그대로 반복됩니다. 응대봇은 모델 하나가 아니라 역할이 다른 둘로 작동합니다.

  • bge-m3 (임베딩) — 대화하지 않습니다. 텍스트를 숫자 벡터로 바꾸는 일만 합니다. "이 강아지 설명"과 "사용자 질문"을 같은 좌표 공간에 올려 의미가 가까운지를 잴 수 있게 만듭니다. 한국어 성능이 좋아 강아지 데이터에 적합합니다.
  • gemma (생성) — 답을 만듭니다. 검색해 온 데이터를 읽고, 영상을 해석하며, 사람에게 건넬 문장으로 풀어냅니다.

이 둘이 3번의 두 DB와 맞물려 하나의 흐름이 됩니다. 이게 RAG입니다.

  1. 사용자가 묻습니다. "사람 잘 따르고 아파트에서도 키울 만한 아이 있어요?"
  2. bge-m3가 질문을 벡터로 바꾸고 벡터DB에서 의미가 가까운 강아지를 꺼냅니다. (동시에 Postgres로 "보호중, 입양가능" 같은 사실 조건도 거릅니다.)
  3. 그렇게 추려진 진짜 데이터를 gemma에게 건네며 답을 만들게 합니다.

 RAG의 핵심은 gemma가 아는 척 지어내지 않게 만드는 겁니다. 모델의 메모리가 아니라 우리 DB의 실제 강아지 데이터를 근거로 답하니까요. 있지도 않은 강아지를 추천하는 환각은 단순 오류가 아니라 신뢰 문제입니다. RAG는 그 위험을 구조로 막습니다.

 

모델 사양의 선택. gemma는 26B 규모지만 MoE 구조라 토큰당 약 4B만 활성화됩니다. 전체는 메모리에 올라가되 속도는 작은 모델처럼 나오는 절충입니다. 거기에 4비트 양자화(q4_K_M)를 더해 메모리를 줄여 워크스테이션이 아닌 한 대의 맥 안에 들어오게 맞췄습니다. "가장 큰 모델"이 아니라 "이 기기에서 굴러가는 충분히 똑똑한 모델"을 고른 겁니다.

 

 결국 이 섹션도 같은 자리로 돌아옵니다. 강력한 모델 하나로 다 하는 게 아니라 일을 쪼개 각자에게 맞는 도구를 붙이고, 그 전부를 로컬에 두는 것입니다.


5. 프론트엔드와 백엔드 - 왜 Django며 왜 Vite에서 Remix일까?

지금 필요한 것과 나중에 필요할 것은 다릅니다.
좋은 선택은 지금을 빠르게 해결하고
나중의 선택지도 닫지 않습니다.

 

 플랫폼엔 두 축이 있습니다. 데이터를 다루는 백엔드와 화면을 그리는 프론트엔드입니다. 둘 다 선택지가 넘치는데 기준은 하나였습니다. 지금 단계에서 가장 적은 공수로 가장 멀리 가는 길.

왜 FastAPI가 아니라 Django인가.

 파이썬 백엔드라면 가벼운 FastAPI도 강력한 후보입니다. 그런데 이 프로젝트는 CRUD가 많은 플랫폼으로 예상됩니다. 강아지 공고 등록과 수정, 임보자 관리, 입양 신청 처리에 데이터를 넣고 빼는 화면이 끝없이 필요합니다.

 

 Django는 여기서 결정적인 걸 공짜로 줍니다.

  • Admin 패널 내장. 공고·신청·회원 관리 화면을 자동으로 생성해줍니다. 프론트를 한 줄도 안 짜도 데이터를 넣고 바로 운영·테스트할 수 있습니다. 솔로 운영에서 이건 몇 주를 아끼는 차이입니다.
  • ORM과 마이그레이션, 인증 기본 제공. FastAPI라면 직접 골라 붙여야 할 조각들이 처음부터 한 묶음으로 옵니다.

 무엇보다 4번까지 만든 AI 코드(Ollama, RAG, YOLO)가 전부 파이썬입니다. Django도 파이썬이라 같은 백엔드 안에서 AI와 플랫폼 로직이 함께 돕니다. 언어가 갈리지 않는다는 것은 곧 개발 속도를 빠르게 합니다.

 

 FastAPI의 가벼움이 빛나는 자리도 분명 있습니다. 다만 CRUD가 많은 플랫폼, 1인 운영이라는 이 조건에선 덜어주는 일의 양이 Django 쪽이 압도적이었습니다.

왜 한 번에 안 가고 단계적으로 Vite에서 Remix인가.

프론트는 처음부터 정답을 고르지 않았습니다. 단계마다 필요가 다르기 때문입니다.

  • MVP 단계 = Vite + React. 지금 필요한 건 빠른 출시입니다. Vite는 가볍고 빠른 개발 환경으로 React 화면을 즉시 띄웁니다. 단점도 분명합니다. 순수 SPA라 SSR이 없어 검색 노출(SEO)과 링크 미리보기가 약합니다. MVP에선 감수할 수 있는 약점입니다. 아직 공개 공고를 검색에 태울 단계가 아니니까요.
  • 프로덕트 단계 = Remix(SSR). 공개 입양 공고가 검색에 잡히고, 카톡·인스타에 링크를 붙였을 때 미리보기가 떠야 하는 순간이 옵니다. 그때 SSR이 필요해지고, Remix로 넘어갑니다. Django API 백엔드와 역할이 겹치지 않아 조합도 깔끔합니다.

 그래서 핵심은 단계 전략입니다. 지금은 발견성(SEO)을 포기하고 출시 속도를 빠르게 합니다. 나중에 발견성이 필요해지면 그때 SSR로 갈아탑니다. 처음부터 Remix로 시작해 무겁게 가는 대신 지금의 문제를 가장 빨리 풀면서 나중의 선택지도 닫지 않습니다.


맺음말

 다섯 개의 질문은 사실 하나인 듯합니다. 영상도, DB도, 응대봇도, 프론트엔드도, 백엔드도 전부 같은 자리로 수렴했습니다. 강력한 도구 하나로 다 풀지 않는다. 일을 쪼개 각자에게 맞는 도구를 붙인다. 그리고 가능한 많은 것을 로컬에 둔다는 것입니다.

 

 이건 취향이 아니었습니다. 개인이 굴리는 작은 비영리에서 무거운 것을 아껴 쓰는 설계만이 지속 가능하니까요. 가장 비싼 도구를 가장 적게 쓰는 법을 아는 것, 그것이 노하우가 됩니다.

 

도구는 이렇게 깔았고(1편), 왜 그렇게 골랐는지도 풀었습니다(2편). 다음은 이걸로 실제로 무엇을 만드는가입니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

YOLO11 해상도 함정: 골든셋으로 검증 - Measurement First AI

좋은 모델이 좋은 파이프라인을 만드는 게 아닙니다.좋은 채점 기준이,모델을 갈아끼울 수 있게 해줍니다. "AI 영상 분석이면 일단 모델부터 돌려보는 거 아냐?" 틀린 말은 아닙니다. YOLO를 붙이

chessire.tistory.com

이전글

 

맥 로컬 LLM 구축 - Local First AI

AI는 한 덩어리가 아닙니다.역할이 다른 도구들을,한 대의 맥 위에 포개는 일입니다."유기견 입양에 AI를 붙인다"고 하면, 보통 이렇게 떠올립니다."그냥 ChatGPT API 부르면 되는 거 아냐?" 틀린 말은

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

AI는 한 덩어리가 아닙니다.
역할이 다른 도구들을,
한 대의 맥 위에 포개는 일입니다.

"유기견 입양에 AI를 붙인다"고 하면, 보통 이렇게 떠올립니다.
"그냥 ChatGPT API 부르면 되는 거 아냐?"

 

틀린 말은 아닙니다. 하지만 이 출발점은 정작 중요한 걸 가립니다. 강아지 정보와 영상 같은 데이터가 매 호출마다 외부로 나가고, 호출량만큼 비용이 붙고, 모델·데이터의 통제권은 내 손을 떠납니다. 작은 비영리 명분 사업에서 이 셋은 가볍지 않은 비용입니다.

그래서 질문을 바꿨습니다.

 

"무엇을 내 손 안(로컬)에 두고, 무엇만 밖에 맡길 것인가?"

 

이 글은 그 경계를, 노트북 한 대 위에 세우는 과정입니다. 선택의 이유는 다음 편으로 미루고, 이번 편은 "무엇을 어떻게 깔았는가"만 다룹니다.


1. AI 플랫폼을 설계하다 - 한 대의 맥에 무엇이 올라가나

세울 것은 세 개의 계층입니다.

  1. Python 계층 — AI 코드(LLM·RAG·영상)와 백엔드(Django)가 같은 언어로 도는 곳.
  2. 데이터 계층 — 표 형태 데이터를 담는 PostgreSQL.
  3. Node 계층 — 화면(React)을 띄우는 프론트엔드 런타임.

핵심은, 이질적으로 보이는 이 조각들이 대부분 파이썬 하나로 묶인다는 점입니다. 강아지 공고 관리도, 응대봇도, 영상 분석도 전부 파이썬 위에서 돕니다. 그래서 환경설정의 무게중심도 자연히 파이썬에 실립니다.

AI Pipeline


2. Python 계층 - AI와 백엔드가 같은 언어로 돈다

맥 기본 파이썬은 버전이 낡아 개발용으로는 권장되지 않습니다. brew로 최신을 깔고, 프로젝트 전용 가상환경(venv)을 만듭니다.

brew install python
python3 --version          # 3.12 / 3.13 나오면 OK
which python3              # /opt/homebrew/bin/python3 이면 brew 거 잡힌 것

# 프로젝트 전용 가상환경
python3 -m venv ~/ai/venv
source ~/ai/venv/bin/activate   # 프롬프트에 (venv) 뜨면 활성
pip install -U pip

그 위에 세 묶음을 올립니다. LLM·RAG 스택, 영상 파이프라인, 그리고 백엔드(Django).

# LLM·RAG — 로컬 모델 클라이언트 + 벡터DB
pip install ollama chromadb

# 영상 파이프라인
brew install ffmpeg                            # 영상 자르기·인코딩·자막 입히기
pip install opencv-python                      # 프레임 추출
pip install ultralytics                        # YOLO(검출) + ByteTrack(추적)
pip install mlx-audio soundfile sounddevice    # TTS — 대본을 나레이션 음성으로 (애플 실리콘 최적)

# 백엔드 — Django + REST + DB 드라이버
pip install django djangorestframework django-cors-headers psycopg2-binary
django-admin startproject config .        # 현재 폴더에 프로젝트 생성
python manage.py runserver                # http://127.0.0.1:8000 뜨면 OK

로컬 LLM 본체인 Ollama는 코드 라이브러리가 아니라 독립 서버라 brew로 깝니다. 위에서 깐 ollama(pip)는 그 서버를 파이썬에서 부르는 리모컨이고, 지금 까는 본체가 TV입니다.

brew install ollama
brew services start ollama        # 재부팅해도 자동으로 뜨는 백그라운드 서비스

ollama pull bge-m3                 # 임베딩 모델 (작고 빠름, 첫 성공)
ollama pull gemma4:26b-a4b-it-q4_K_M   # 멀티모달 본체 (비전+텍스트)

ollama run gemma4:26b-a4b-it-q4_K_M "안녕, 한국어 돼?"   # 응답 나오면 /bye

이 한 덩어리만으로 "텍스트를 벡터로 바꾸고(bge-m3), 의미가 비슷한 데이터를 꺼내(chromadb), 그걸 보고 답하는(gemma)" 응대봇의 뼈대가 한 대의 맥 안에 들어옵니다.


3. PostgreSQL - 구조화 데이터의 자리

회원, 강아지 공고, 입양 신청처럼 행과 열로 떨어지는 데이터는 관계형 DB에 둡니다.

brew install postgresql
brew services start postgresql    # ollama처럼 백그라운드 서비스로

조금 전 깐 chromadb와는 역할이 다릅니다. postgres는 구조화 데이터, chromadb는 의미 검색용 벡터입니다. 대체재가 아니라 함께 씁니다. (왜 둘 다 필요한지는 다음 편에서.)


4. Node 계층 - 화면을 띄우다

프론트엔드(React)를 돌릴 런타임입니다.

brew install node          # node + npm 같이 들어옴
node --version             # 20 이상

npm create vite@latest     # React 선택 (MVP용 SPA)

MVP는 가볍고 빠른 Vite + React로 갑니다. 검색 노출(SEO)이 필요한 프로덕트 단계에서 SSR 프레임워크로 넘어갈 여지를 남겨두지만, 그 갈림길의 이야기도 다음 편 몫입니다. (참고로 create-react-app은 폐기되어 쓰지 않습니다.)


맺음말

여기까지가 골격입니다.

  • Python 하나에 AI와 백엔드를 포개고,
  • Postgres로 구조화 데이터를 받치고,
  • Node로 화면을 띄운다.

겉으로는 그저 brew install과 pip install의 나열처럼 보입니다. 하지만 이 배치에는 일관된 선택이 하나 깔려 있습니다. 가능한 많은 것을 외부 API가 아니라 내 손 안에 두는 것. 왜 굳이 로컬인지, 왜 FastAPI가 아니라 Django인지, 그리고 영상 분석을 왜 전부 LLM에 맡기지 않는지 — 이 환경을 이렇게 세운 이유는 다음 편에서 풀어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

로컬 LLM 아키텍처 - Local First AI

1. 강아지를 입양했다기술을 먼저 정하고 문제를 찾은 게 아닙니다.한 마리를 데려오고 나서야풀어야 할 문제가 보였습니다. AI로 무언가를 만들어보려던 참이었습니다. 도구는 손에 있는데 무

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com