
작은 앱을 하나 만들어서 공개했습니다. Rimlog라는 독서 기록 앱인데, 책이나 유튜브를 보다가 마음에 남는 대목과 내 생각을 적어 두면 AI가 예전 메모 중에 이어지는 걸 찾아서 붙여 주고, 질문을 하나 던져 줍니다. 오프라인에서도 돌고, 데이터는 브라우저에만 있습니다. 서버가 없으니 회원가입도 없습니다.
앱 자체는 사실 평범합니다. 이 글을 쓰는 이유는 만든 과정이 좀 이상했기 때문입니다. 저는 이 앱의 코드를 거의 쓰지 않았습니다. 대신 한국어로 명세 두 장을 썼습니다.
리포지토리의 eux/ 폴더에 파일이 두 개 있습니다. rimlog-app.eux는 40줄 정도인데, 페이지 세 장의 구성과 동작, 로컬 스토리지에 뭘 저장할지, AI를 언제 부르고 실패하면 어떻게 할지가 전부 문장으로 적혀 있습니다. 이를테면 이런 식입니다.
saveCapture : 캡처 폼(소스명·인용·내 생각·태그) 저장 — captures 맨 앞 삽입 + 로컬 스토리지 반영 + "저장됐어요" 토스트 + 기록 탭으로 전환.
이 파일을 EstreUX라는 도구에 넣으면 코드가 나옵니다. 이 과정을 brew라고 부릅니다. 나온 코드에는 명세의 해시가 박혀서, 누가 명세를 고치고 코드를 안 고치면(반대도 마찬가지) 커밋이 막힙니다.
왜 이렇게 하냐면, AI한테 코딩을 시켜 본 분은 아실 텐데, 결과물은 남는데 그걸 만들어 낸 대화는 날아갑니다. 석 달 뒤에 "이 코드가 왜 이렇게 돼 있지?"라고 물을 곳이 없습니다. 명세를 git에 남기고 코드를 산출물 취급하면 그 질문에 답할 곳이 생깁니다. 적어도 그게 이 실험의 가설입니다.
Rimlog의 큰 틀, 그러니까 페이지 전환이나 뒤로가기, 서비스워커 같은 것들은 EstreUI가 맡습니다. 안드로이드 액티비티랑 비슷한 라이프사이클이 있어서 페이지마다 onShow, onHide 같은 훅이 옵니다. 명세 첫 장이 이쪽 코드로 나옵니다.
보라색 AI 카드 하나는 EstreUV라는 웹컴포넌트 라이브러리로 만들었습니다. 명세 둘째 장이 <ai-insight-card>라는 커스텀 엘리먼트가 되고, EstreUI 페이지 안에 끼워집니다. shadow DOM이라 격리돼 있고, 라이트 모드나 다크 모드 전환은 호스트가 CSS 변수로 흘려 넣습니다.
명세 하나에서 다른 타깃을 뽑을 수도 있습니다. 예전에 만든 끝말잇기 데모는 명세 한 장에서 세 가지 버전(EstreUI 단독, 웹컴포넌트 단독, 둘 조합)이 나왔습니다. 프레임워크를 갈아탈 일이 생겨도 명세는 그대로 두고 타깃만 바꿔 다시 뽑으면 된다는 뜻입니다.
절감은 실측을 해 봤습니다. 프레임워크에 원래 있던 숫자 키패드와 접이식 블록 컴포넌트를 명세로 되돌렸더니 사람이 유지하는 줄 수가 60에서 82퍼센트 줄었습니다. 152줄짜리 숫자 키패드 코드가 28줄 명세가 된 식입니다. 생성된 코드 자체는 원본보다 길어지기도 합니다. 줄어드는 건 사람이 읽고 고치는 쪽이고, 측정 기록도 리포에 그대로 있습니다.
라이브는 https://soliestre.github.io/Rimlog/ 입니다. 모바일 브라우저에서 열고 홈 화면에 추가하면 앱처럼 씁니다. AI 연결은 주간 탭의 설정 카드에서 고릅니다. API 키를 넣으면 실제 모델을 쓰고, 안 넣으면 태그 겹침 기반의 단순한 폴백으로 돕니다. 키는 브라우저 세션에만 있다가 사라집니다.
Pages 버전은 서버가 없어서 위처럼 폴백이나 직접 키 입력으로만 돕니다. 그래서 셀프호스트 옵션을 하나 넣었습니다. 리포에 의존성 없는 단일 파일 node 서버가 들어 있는데(이것도 명세에서 brew한 산출물입니다), 앱 서빙과 인사이트 생성을 같이 합니다. 기본 백엔드는 NVIDIA의 무료 호스팅 모델입니다. build.nvidia.com에서 카드 등록 없이 API 키를 주고, OpenAI 호환 프로토콜에 무료 구간이 분당 40회쯤이라 개인 일지 용도로는 넉넉합니다. 이미 내고 있는 구독을 재활용하는 쪽도 됩니다. claude-cli 백엔드는 요청마다 claude -p를, codex-cli는 codex exec를 부릅니다. 어느 쪽이든 키와 인증은 서버에만 있고 브라우저로는 안 나갑니다. 세팅은 README의 Self-host 절에 세 줄로 적어 뒀습니다.
리포지토리의 커밋 이력을 처음부터 보시면 스캐폴드에서 명세, 생성, 다듬는 과정까지 순서대로 남아 있습니다. 명세 포맷이 궁금하면 EstreUX 리포에 문서가 있습니다.
명세를 유지하고 코드를 산출물로 취급하는 방식이 맞는 방향인지는 저도 아직 확신이 없습니다. 다만 검증 가능한 앱 하나가 나왔고, 그 과정이 전부 리포에 남았다는 것까지는 사실입니다. 의견은 댓글이나 GitHub 이슈로 남겨 주세요.