이 블로그를 2년 동안 구상했다
"생각했다"는 말은 사실 정확하지 않고, 오히려 집념에 가깝다. 프런트엔드도 백엔드도 못 쓰고 임베디드 코드도 못 쓰던 시절부터 이미 있었다. 그때는 아직 전자공작을 만지작거리고 있었는데, 우연히 @ushio 의 영상과 사이트를 보게 됐고, 는 너무 재밌어 보여서 나도 갖고 싶어졌다. 그 뒤로 반년 가까이 걸려 C를 맨바닥부터 배우고 JS와 CSS도 조금 익혀서, Hexo로 정적 블로그를 만들어 Github Pages에 올렸다. 그렇게 나만의 작은 공간이 생긴 셈이다.


그러다 나중에 @Innei의 개인 사이트를 보게 되었다. 디자인은 정교했고 기능도 아주 다양해서, 한눈에 보자마자 갖고 싶어졌고 후원까지 했다. 결국 나중에는 쓰지 못했지만 돈이 아깝지는 않았다. 덕분에 React 소스 코드를 직접 들여다볼 수 있었기 때문이다. 처음 C를 봤을 때와 마찬가지로 이런 생각이 들었다. ‘어차피 코드일 뿐인데, 뭐가 그리 대단해? 언젠가는 나도 직접 하나 만들 거야.’

그건 2024년이었다. 그리고 24년, 25년, 26년을 지나 지금에 이르렀다
그때부터 멈출 수 없게 됐다.
통제 불능
처음엔 그저 @colmugx 의 Hexo 테마 Nlvi 에 라이트/다크 전환을 붙이고 싶었을 뿐이다. Dark Reader 의 JS 로 대충 하나 붙여 봤지만 보기 흉했고 마음에 들지 않았다. 그다음 CSS @media 를 배우고, JS 의 SPA 전환으로, TailwindCSS 의 .dark class 로, SSR 의 next-theme 로 옮겨 갔고, 마지막엔 Cookie 와 Head Sync 인라인 스크립트를 FOUC 깜빡임을 해결했다. 그렇게 네다섯 가지 방법을 거쳤다.

하지만 이것도 빙산의 일각일 뿐이었다. React를 접한 뒤 석기 시대의 Hexo와 순수 JavaScript에서 출발해 Next.js 13 페이지 라우터까지 갔고, 다시 옆길로 새서 Vue를 살펴보고 단일 페이지 애플리케이션 라우팅을 연구하며 index.html 대체 작동 방식을 이해하느라 프로젝트를 수도 없이 새로 만들었다. 서버를 사서 Docker, Nginx, 네트워크 결합 스토리지를 만졌고, 1Panel이 못생겼다는 이유로 Canopy 패널을 직접 만들었으며, 스토리지 배열을 연구하다 RFS를 작성했다. 보안 소켓 계층 인증서를 설정할 때는 중국 내 환경에서 말썽을 부리는 acme.sh에 시달리다 못해 Rust로 Vane과 Lazyacme라는 역방향 프록시까지 만들었다. 원래는 어느 날 밤 세 시간 만에 짠 800줄짜리 쓰레기 코드였는데, 단체 채팅방 친구들이 부추기는 바람에 넉 달 동안 개발하며 HTTP/3, 무복사 처리, 계층 모델, 흐름 엔진을 모조리 욱여넣었다. 그 뒤에는 Seam이라는 풀스택 프레임워크도 있는데, 이것도 끝없는 구덩이이니 이야기는 나중에 하자.
과잉 설계
그러다 인공지능 시대가 왔고, 불안은 또 두 배로 커졌다.
가장 아이러니한 건, 이 모든 게 원래는 블로그가 생긴 뒤에 기록하면서 만들어야 할 것들이었다는 점이다. 그런데 블로그는 , 아이디어는 점점 늘어나는데 그것들을 적어 둘 곳은 한 군데도 없었다. 한쪽에서는 마음속에서 계속 나를 몰아세웠다. Vane을 끝내겠다고 했으니 여기서 멈출 수는 없다고. 다른 한쪽에는 갈수록 멀어져만 가는 무력감이 있었다. 내가 무엇을 해야 하는지는 늘 기억하고 있었다. 다만 만들어 내지를 못했다. 곰곰이 생각해 보니, 앉아서 무언가를 써 본 지가 정말 오래되었다. 지금 돌아보면 이 모든 것이 같은 하나를 가리키고 있다 — 과잉 설계.
당시에는 사실 내가 방향을 벗어나고 있다는 생각이 들지 않았다. Vane? 어차피 블로그도 나중에는 배포해야 하니 리버스 프록시가 하나쯤은 필요하지 않겠는가. Seam? 어차피 언젠가는 풀스택으로 개발해야 하니 먼저 내가 꿈꾸던 프레임워크부터 제대로 만들어 두면 되지 않겠는가. 모든 단계에는 이유가 있었고, 모든 것은 “나중에 쓸모가 있을” 일이었다. 하지만 문제는 그 “나중”이 영원히 오지 않는다는 것이었다. 나는 단 한 번도 시작하지 않았으니까...
실패
사실 블로그를 만드는 데는 리버스 프록시도, 자체 개발 프레임워크도 필요하지 않다. 하지만 당시의 나는 그것들을 선행 조건으로 여기며, 영원히 나를 가로막으면서도 그럴듯해 보이는 이유로 삼았다. 그러나 그것들은 애초에 선행 조건이 아니었다. 진짜 선행 조건은 단 하나였다. 자리에 앉아 첫 글을 쓰는 것.
사실 블로그에 전혀 손을 대지 않았던 것은 아니다. 오히려 정반대로, 세 번이나 시도했다.

첫 번째 시도는 아직 Next.js 13의 Page Router를 쓰던 시절이었다. 당시 나는 단일 페이지 애플리케이션이 무엇인지 어렴풋이 아는 정도였고 SSR, SSG, ISR 같은 개념은 하나도 제대로 이해하지 못했다. 그래도 남들이 SSR이 좋다고 하니 나도 써야겠다고 생각했다. 하지만 계속 만들다 보니 이런 아키텍처가 어떤 문제를 해결하려는 것인지 전혀 이해하지 못하고 있다는 사실을 깨달았고, 결국 더 진행할 수 없었다. 그다음에는 Vue 2 기반의 순수 단일 페이지 애플리케이션으로 전환했다. 왜 Vue였냐고? 부끄러운 이야기지만 이름이 듣기 좋았고, 어차피 내게는 새로운 것이니 한번 해 보자는 마음이었다. 그것도 오래가지는 못했다. 세 번째 시도에서는 React로 돌아가 단순한 JSX부터 시작했다. TypeScript를 배우고 TSX를 익혔으며 CJS와 ESM의 차이를 이해했고, 반응형 프로그래밍에 매료되어 훗날 없이는 못 살게 될 Lucide, Framer Motion, Radix UI 같은 라이브러리도 사용하기 시작했다. 이번에는 드디어 제법 그럴듯해 보였다. 하지만 나는 또 실패했다. 본질적으로 그저 남을 따라 하고 있었기 때문이다. 남들이 쓰는 것은 무엇이든 가져다 썼고, 겉보기에는 그럴듯했지만 왜 그렇게 해야 하는지는 전혀 이해하지 못했다. 거기에 AI의 환각까지 더해지니 많은 것을 최소 기능 제품으로 작동하게 만들 수는 있었지만, 규모를 확장하려 하면 온통 함정투성이였다.

그 뒤로 React(Remix) Router의 Loader, Next.js 15 App Router, RSC도 배웠지만, 그때는 이미 일상이 너무 바빠 코드를 작성할 시간이 거의 없었고 덕분에 네 번째로 실패할 기회도 없었다. 지금 생각해 보면 내 웹사이트는 Next.js 13부터 Next.js 15까지 이어서 만들었는데, 마이그레이션을 막 끝내고 아직 공개도 하지 않은 사이 Next.js 16 beta 1이 나왔다.
놓아주자
“마음을 내려놓았다”고 말하는 건 사실 정확하지 않다. 나는 그저 지쳤을 뿐이다.

AI 시대의 속도는 너무 빠르다. 모델은 한 번씩 계속 갈아엎히고, 새로운 기술은 파도처럼 밀려온다. 나는 점점 더 불안해졌다, 멈추면 곧바로 추월당할 것 같아서. 가장 미쳐 있을 때는 하루에 열여섯 시간씩 코드를 썼고, Token 값만 하루 $1.9k USD를 태울 수 있었다 (대충 2월쯤이었던 것 같다). 몸이 거의 망가졌다. 하지만 이 모든 걸 멈추게 한 건 무슨 깨달음이 아니라 그냥 지쳤기 때문이다. 점점 조여드는 속도 속에서 마지막 지푸라기가 얹혔을 때, 나는 오히려 후련해졌고, 마음을 놓았고, ; 이래도, 꽤 괜찮다.
나는 내려놓기 시작했다. 한때 집착했던 것들을 포기하고, 적당한 선에서 멈추며, 내일 일은 내일 생각하는 법을 배웠다. Vane을 다 못 만들었다고? 일단 놔둔다. Seam은 아직 갈 길이 멀다고? 나중에 다시 하면 된다. 블로그가 완벽하지 않다고? 일단 공개한다. 완전히 Vibe Coding을 시작해, 문제가 없으면 건드리지 않고 문제가 생기면 그때 생각하기로 했다.
이상한 건, 손을 놓은 뒤에도 품질이 나빠지지 않았다는 것이다. 아마 예전의 그 프로그래밍의 나날이 헛되지 않아서일 것이다. GPT-2 시절부터 이미 AI를 쓰고 있었지만, 그때의 나는 여전히 한 줄 한 줄 성실하게 코드를 쓰고 개념 하나하나를 이해하고 있었다. 그 경험이 밑바탕이 되었다. 그 뒤의 것은 Vibe Coding이라기보다 Context Coding이었고, 있어야 할 판단력은 그대로 남아 있었다. 손을 놓았다고 해서 저급한 실수를 하거나 사고를 낸 적은 없었다.
그동안의 모든 시행착오가 지금 내가 손을 놓을 수 있는 밑바탕이 되어 주었다.
원점
최종 해법은 의외로 간단했다. 모든 것을 Cloudflare에서 운영하는 것이다. R2는 객체 스토리지, D1은 엣지 데이터베이스, KV는 캐시, Worker는 서버 측 렌더링을 담당한다. 모두 서버리스이므로 서버가 필요 없다.

이 답이 아이러니하냐고? 너무나도 아이러니하다. 이 “간단한” 종착점에 도달하기까지 한참을 빙 돌아왔기 때문이다. 한때는 직접 호스팅할 계획이어서 서버를 관리하고, Docker와 씨름하고, 컨테이너를 관리해야 했다. 그래서 관리 패널로 Canopy를 만들고, 인증서 관리를 위해 Lazycert를 만들고, 역방향 프록시로 Vane을 만들고, 리소스 모니터링 계측을 위해 Twig를 만들었다. 고작 형편없는 서버 한 대를 떠받들려고 수많은 프로젝트를 만든 셈이다.
스토리지도 마찬가지였다. 객체 스토리지가 비싸다고 생각해 내 서버의 하드디스크를 쓰려 했고, 그래서 데이터를 원자적으로 중복 제거해 단일 파일로 합치는 VFS인 RFS를 만들었다. 심지어 1980~90년대의 레지스트리 개념까지 되살린 뒤, 마지막에는 FUSE를 통해 마운트했다. 결과는 어땠을까? 더 많은 시간을 들여 성능도 별로인 바퀴를 다시 발명했고, 서버는 1년 동안 놀고만 있었다. 완전히 밑지는 장사였다.

결국 한참을 돌아 다시 서버리스로 돌아왔다. Vercel의 개념은 좋지만 나는 Next.js를 좋아하지 않는다. 게다가 Vercel에는 마법 같은 요소가 너무 많고, 미들웨어를 미리 컴파일해 엣지에서 실행하는 설계도 결국 구조를 단절시키며 CVE까지 몇 건 발생시켰다. 이럴 바에는 차라리 모든 것을 오리진 서버에서 함께 실행하는 편이 낫다. 반면 Cloudflare Workers는 아주 좋다. 10 MB 미만의 결과물을 WASM으로 컴파일해 실제 비즈니스 로직을 엣지에서 실행할 수 있게 함으로써 또 다른 극단에 도달했다. 바로 이것이 내가 원하던 것이다. 돈이 많이 든다는 점 외에는 단점이 없다. 하지만 따지고 보면 그것도 단점은 아니다. 결국 절약한 시간이 곧 돈이기 때문이다.
적을수록 좋다
이 말은 수도 없이 들었지만, 그 의미를 진정으로 이해하기까지 2년이 걸렸다.
엔지니어링의 본질로 돌아가자. 「Make it function, make it correct, then make it exceptional.」 이제야 제대로 된 길에 들어서서 우선 작동하게 만들었다. 지금 블로그가 허술한가? 허술하다. 불완전한가? 분명 불완전하다. 그래도 없는 것보다는 낫다. 공개한 것도 하나 없는 채로 “어쩌면 필요할지도 모르는” 것만 계속 만들고, 눈에 보이지도 않는 완벽함을 좇는다면 결국 아무것도 내놓지 못한다. 이것은 타협이 아니라 엔지니어링상의 선택이다. 어쨌든 나는 이제 내려놓게 됐다. 그래서 내려놓았다. 한 가지 세부 사항에 집착하지 않게 되니 오히려 문밖으로 나왔다. 휴대폰 사진첩에는 한동안 야외 사진이 없었기에 길가 풍경을 무심코 한 장 찍었다—정말 오랫동안 밖에 나가지 않았으니까 (

지금까지 돌아서 온 길을 되돌아보면 후회할까? 후회하지 않는다. 어떤 일은 직접 해 보지 않으면 어쩌면 영원히 이해하지 못할 수도 있다. 더구나 인공지능 시대인 지금은 내가 당시에 걸었던 옛길을 다시 가고 싶어도 사실상 기회가 없다. 너무 많은 작업 흐름이 이미 바뀌었기 때문이다. 그리고 이 프로젝트는 Taki로, 코딩 전부를 인공지능으로 했다. 하지만 그렇다고 장기적으로 유지·보수할 수 없다거나 인간미가 없다는 뜻은 아니다. 사실 인공지능을 사용했는지는 중요하지 않다. 게다가 내가 좋아하는 것은 코드 작성 자체가 아니라 무언가를 만들어 내는 일이다...