첫 웹사이트와의 아픈 작별: "컨텍스트 없는 커밋은 없다"던 그 경고를 나는 끝내 외면했다.
2026. 8. 22.
첫 웹사이트와의 아픈 작별: "컨텍스트 없는 커밋은 없다"던 그 경고를 나는 끝내 외면했다.
도메인 갱신 알림이 왔다. 그리고 나는 망설임 없이, 이젠 그만 보내주기로 결정했다.
threadkeeper.io. 2025년 여름, 커뮤니티 칼리지 AI 캠프에서 ccc-ai-pdf-project 같은 어설픈 파일명으로 파이썬 코드를 끄적이며 "초보 파이썬 프로젝트"라고 README에 적어두던 시절이 있었다. 그 후 약 6주 뒤, 2025년 8월에 이 도메인을 구매했다. 그때만 해도 야심만만했다. 도메인부터 블로그, CLI, 심지어 거창한 '선언문(manifesto)'까지 내 손으로 직접 만들고 배포하겠다는 포부를 담았다. 제가 실무에서 수많은 초기 프로젝트를 지켜본 결과, 처음의 열정이 아무리 뜨거워도 이렇게 쉽게 잊히고 방치되는 경우가 허다하더군요.
서비스 만료 직전, 나는 마지막으로 한 번 더 사이트를 돌아봤다. 감상에 젖어들 시간은 길지 않았다. 길어야 5분? 그 후 나는 이 사이트의 소스 코드가 어디에 있을지 찾아보려 애썼고, 곧 깨달았다. 세상 어디에도 존재하지 않는다는 사실을.
사이트는 Spaceship이라는 플랫폼 위에 있었다. 브라우저에서 직접 빌드했고, 단 한 번도 버전 관리 시스템에 넣지 않았다. 레포를 클론할 수도, 로컬 폴더도, 백업도 없었다. 세상에 존재하는 내 첫 웹사이트의 유일한 사본은, 만료까지 겨우 나흘 남은 서버 위에서 돌아가는 그 자체뿐이었다.
정작 이 사이트 페이지 상단에는, 선명한 시안색 글씨로 이렇게 적혀 있었다. "Don't commit without context." (컨텍스트 없는 커밋은 없다.)
아이러니하게도, 나는 이 사이트를 단 한 번도 커밋하지 않았다.
내 웹사이트의 소스 코드가 사라졌다니
향수에 젖을 틈도 없었다. 첫 번째 임무는 ‘복원’이 아닌, ‘추출’이었다.
서비스가 완전히 종료되기 전, 나는 살아있는 서버에서 8개의 페이지와 모든 자산을 끌어모았다. 랜딩 페이지, 블로그, 3개의 포스트, 닥터 칼로(Dr. Kahlo) 페이지, 그리고 AWS 해커톤을 위해 만들었던 아리아드네 클루(Ariadne Clew) 요약 앱까지. 총 19개의 파일이었다. sitemap.xml 파일에는 4개의 페이지만 있다고 적혀 있었는데, 이 숫자가 2025년의 내가 얼마나 내 사이트맵을 신뢰하지 않았는지 잘 보여준다. 나머지는 링크를 따라가며 겨우 찾아냈다.
이제 그 아카이브는 공개되어 있다. 각 원본 파일마다 SHA-256 해시를 부여해서, 누구든 복구 과정에서 변조된 부분이 없음을 확인할 수 있다.
earlgreyhot1701d.github.io/threadkeeper-archive
이제야 커밋되었다. 1년이나 늦게 말이다.
dom_js.js? 심지어 나는 눈 하나 깜빡하지 않았다
내 코드를 겨우 읽어볼 수 있게 되었을 때, 가장 먼저 눈에 띈 것은 바로 이것이었다.
아리아드네 클루 앱에는 7개의 JavaScript 모듈이 있었다. 그중 2개는 스네이크 케이스에 _js라는 접미사를 달고 있었다: api_js.js, dom_js.js, main_js.js. 다른 4개는 카멜 케이스였고 접미사는 없었다: utils.js, theme.js, exportMarkdown.js, dragDrop.js.
하나의 폴더 안에 두 가지 네이밍 컨벤션. 총 7개의 파일.
잠깐 dom_js.js를 살펴보자. 이 파일명은 "dom, js, 점 js"라고 읽힌다. 접미사가 확장자를 다시 반복하고 있는 것이다. 2025년의 내가 그랬던 방식을 정확히 기억한다. 아마도 채팅 창에서 dom_js라고 이름 붙은 코드 블록을 복사한 다음, 그 라벨에 .js를 덧붙여 저장하고 "작동하니까 됐지 뭐" 하고 넘어갔을 것이다.
이 파일명은 일종의 '영수증'과 같다. 프로젝트 내부에서 새로 만들어진 것이 아니라, 대화나 스니펫에서 복사되어 붙여넣기 식으로 생겨났다는 기록을 고스란히 담고 있다.
단 한 줄의 코드가 페이지의 모든 버튼을 마비시켰다
main_js.js 파일은 6개의 import 문으로 시작한다. 그중 세 번째 줄은 다음과 같았다.
import { getElement, getValue, ... } from './dom.js';
문제는 서버에 배포된 실제 파일명이 dom_js.js였다는 점이다.
ES 모듈은 실행에 앞서 전체 임포트 그래프를 먼저 해석한다. 단 하나의 파일이라도 찾을 수 없으면, 해당 모듈은 아예 실행되지 않는다. 다른 다섯 개의 임포트가 성공적으로 해석되었더라도 부분 실행이나 폴백(fallback) 같은 건 없었다.
이 페이지의 모든 이벤트 리스너는 main_js.js 안에 들어 있었다. 폼 제출, 복사 버튼, 내보내기 버튼, 테마 토글, 드래그 앤 드롭, 초기 UI 상태까지. 그 어떤 것도 작동하지 않았다.
페이지는 완벽하게 렌더링되었지만, 아무것도 할 수 없는 껍데기에 불과했던 것이다.

위 스크린샷은 내 웹사이트가 만료되기 며칠 전, 내 브라우저에서 직접 찍은 것이다. 왼쪽에는 완벽하게 렌더링된 페이지, 오른쪽에는 단 하나의 붉은 에러 라인.
콘솔은 실패한 한 가지 요소만 보여줄 뿐이지만, 전체 트레이스는 이렇다.

8개 중 7개는 정상적으로 해석되었지만, 4가지 독립적인 확인 결과, 핵심 모듈은 전혀 실행되지 않았다.
이런 상태로 최소 11개월 동안 방치되어 있었다. 2025년 9월 블로그 포스트에서 이 앱으로 링크했던 시점을 기준으로 한 것이니, 실제로는 더 길었을 수도 있다. 배포 타임스탬프? 당연히 남아있지 않았다.
이 버그가 작동하지 않는 모습을 직접 확인해 볼 수 있다. 아카이브는 이 버그를 배포 당시의 모습 그대로 보존하고 있다.
오류가 있는 버전 열기, F12를 눌러 네트워크 탭을 확인해 보라. 8개 요청 중 scripts/dom.js만 404를 반환한다. 그리고 수정된 버전을 열면 상태 라인이 "Ready to generate recap."으로 정상적으로 바뀌는 것을 볼 수 있을 것이다.
PR을 보내서 수정하지 말아 달라. 의도적으로 보존해 둔 것이다.
이 부분에 대해서는 조심스럽게 이야기하고 싶다. 이 이야기를 쉽게 풀어내면 "나는 코딩을 못했다"가 되겠지만, 그게 전부는 아니다. 당시 스택으로는 이런 문제를 잡아낼 수 있는 게 아무것도 없었다. 빌드 스텝도 없었고, 번들러도 없었다. 임포트 해석 기능이 있는 린터도, TypeScript도, 테스트도 전무했다. 이들 중 하나라도 있었다면, 서버에 도달하기 전에 누락된 임포트에 대해 큰 소리로 실패를 알렸을 것이다. 제가 실무에서 아무리 작은 프로젝트라도 CI/CD 파이프라인에 린터와 기본 테스트를 포함시키려는 이유가 바로 여기에 있습니다. 이런 사소한 실수가 배포 환경까지 넘어가지 않게 방지하는 최소한의 안전망이니까요.
빌드 스텝 없는 CDN 기반의 바닐라 JavaScript를 사용했지만, 작은 프로젝트라면 지금도 그 선택을 옹호할 것이다. 하지만 여기에는 '세금'이 따른다. 바로 임포트 그래프를 자동으로 검증해 주는 도구가 없다는 점이다. 당시에는 이 '세금'의 존재를 알지 못했다. 그래서 11개월 동안 눈치채지 못한 채 그 대가를 치렀던 것이다.
첫 AI에게 가짜 박사 학위를 수여했다
내 웹사이트에는 '닥터 칼로(Dr. Kahlo)'라는 페이지가 통째로 있었다. 내 코드를 리뷰해 주도록 만든 커스텀 GPT였다. 나는 그녀에게 배경 스토리를 부여했다. 2025년 페이지에서 가져온 원문 그대로다.
그녀의 가상의 이력서에는 30년간의 FAANG급 경험, 코드 품질 박사 학위, 그리고 Python, JavaScript, JSON, React에 대한 모국어 수준의 유창함이 포함되어 있습니다. 그녀의 미학은 프리다 칼로와 포매터의 만남입니다. 그녀의 사운드트랙은 릴라 다운스입니다.
그리고 한 단락 뒤에는 이렇게 적혀 있었다.
그녀는 아첨하지 않습니다. 그녀는 환각을 일으키지 않습니다. 그녀는 수술적 정직함으로 리뷰합니다.
나는 이 글을 2025년 8월에 썼다. 이 문장을 지금 있는 그대로 남겨두겠다.
참고로, 닥터 칼로는 여전히 활동 중이다. 그녀는 자신을 소개했던 웹사이트보다 오래 살아남았다. AI 코드 어시스턴트와의 2026년 버전 관계가 궁금하다면, Breaking Build 글을 참조해 달라. 거기서 Kiro와 Claude는 내가 요구한 것을 정확히 주었지만, 그것은 내가 원했던 것이 아니었다.
방금 막 첫 결과물을 내놓았다면
오늘 당장, 당신의 결과물을 레포에 넣어라. 코드가 엉망일지라도, 아니, 특히 코드가 엉망일수록 더더욱 그렇게 해야 한다.
버전 관리 기록 때문도 아니고, 채용 담당자가 볼까 봐도 아니다. 호스팅은 임대하는 것이지만, 레포는 온전히 당신의 것이기 때문이다. 언젠가 당신은 과거의 당신이 무슨 생각을 했었는지 돌아보고 싶어질 테고, 그때 "내가 돈 내는 걸 멈춘 서버에 있었어"라는 말은 당신의 작업물에 대해 해야 할 가장 슬픈 문장일 것이다.
이것이 모든 교훈이며, 당신에게 드는 비용은 약 90초면 충분하다.
아직 그 의미를 말할 수 없는 이유
그 옛날 사이트를 다시 살펴보는 일은 나에게 묘한 감정을 불러일으켰다. 시간의 거리가 느껴졌다. 1년 전의 내 글을 읽으면서, 그 목소리는 완벽하게 알아볼 수 있지만 결과물 자체에는 얼굴을 찌푸리게 되는 이상한 경험이었다.
그리고 나는 '내가 얼마나 발전했는가'에 대한 회고록을 쓰려고 앉았다가 멈췄다. 왜냐하면, 아무런 근거 없이 내 성장에 대해 자신감 넘치는 주장을 늘어놓으려는 자신을 발견했기 때문이다.
이는 내가 연구를 통해 피하고자 했던 바로 그 지점이었다.
Clew Chronicles는 2025년 7월부터 2026년 8월까지 13개월간 나의 AI-보조 빌딩 과정을 기록한 n=1 종단 연구이다. 총 57개의 레포지토리가 포함되어 있다. 데이터는 8월 9일에 수집을 마쳤고, 가설들은 이미 등록되어 동결되었다. 10가지 가설 중 두 가지는 본질적으로 "내가 무엇이 중요하다고 기억하는지는 아마 틀렸을 것"과 "데이터가 나를 반박하도록 허용해야 한다"이다.
이 57개 레포 중 49개는 아직 분류되지 않은 채 스프레드시트에 앉아 나를 기다리고 있다. 이 단계는 '인간 코딩' 작업이다. 규칙상 어떤 모델도 이 작업을 건드릴 수 없다.
그러니 아직 무엇이 변했는지 이야기하지 않겠다. 데이터가 준비되면 그때 알려줄 것이다. 그리고 내가 틀렸다면, 그 또한 인정할 것이다.
실타래는 계속된다
ThreadKeeper는 AWS 해커톤 중에 '아리아드네 클루(Ariadne Clew)'로 이름을 바꿨다. 프로젝트가 단순히 '무엇을 했는지'를 설명하는 이름을 넘어섰기 때문이었다. '클루(clew)'는 아리아드네가 테세우스에게 미궁을 빠져나올 길을 찾도록 준 실타래를 의미한다. 그렇게 Ariadne Clew가 되었고, 이어서 'Clew'라는 이름이 붙은 일련의 도구들로 확장되었다.
이 도메인은 이번 주에 사라진다. 하지만 그 아이디어는 그 어느 때보다도 거대해졌다.
오래된 푸터에는 "Flag planted August 26, 2025"라고 적혀 있었다. 기념일을 계획했던 건 아니지만, 원본 ThreadKeeper 레포의 첫 커밋 날짜는 2025년 8월 20일이었고, 나는 2026년 8월 20일에 그 사이트를 다시 찾았다. 우연치 않게 딱 12개월 만이었다.
나는 그 당시 그 작업물을 정말 자랑스러워했다. 론칭 포스트에는 "작동하는 스냅샷, 인사이트, 그리고 기능적인 TypeScript CLI와 함께 ThreadKeeper를 공식 론칭했습니다. 예쓰!"라고 썼다. 나는 여전히 그 시절의 '그녀'가 좋다. 그녀는 자신이 무엇을 모르는지 전혀 몰랐지만, 어쨌든 결과물을 세상에 내놓았다.
깃발은 다시 세워졌다. 데이터와 함께 다시 보고하겠다.
여기 처음이라면 잠시 참고: 나는 법정에서 코딩 세계로 넘어왔다. 배심원 서비스에서 AI 빌더로, 독학으로 배우며 공개적으로 학습하는 중이다. 나는 지시하고, 에이전트가 생성하며, 나는 검증하고 결정한다. Clew Suite와 몇몇 시민 기술 도구들을 만들고 있다. 이 모든 글은 그런 관점에서 쓰인 것이다.
AI Assisted. Human Approved. Powered by NLP.
원문: https://dev.to/earlgreyhot1701d/my-first-website-said-dont-commit-without-context-i-never-committed-it-at-all-5d57 수집일: 2026-08-22 00:30:03