Claude와 Next.js로 주말 동안 SaaS를 구축한 경험 - 솔직한 분석
요약
본 글은 Claude와 Next.js를 활용하여 주말 동안 SaaS MVP를 구축한 경험을 솔직하게 분석합니다. 단순히 코드를 빠르게 작성하는 것이 아니라, 모델이 생성한 코드(diff)를 비판적으로 검토하고 실제 제품의 핵심 로직과 결함 지점을 찾아내는 과정이 중요함을 강조합니다.
핵심 포인트
- 주말 SaaS는 환상일 수 있으며, 진짜 어려움은 타이핑 속도가 아닌 '검증'에 있다.
- Claude 같은 LLM은 뼈대 구축(scaffolding)을 매우 빠르게 처리하여 개발 시간을 단축시킨다.
- 모델이 생성한 코드는 완벽해 보이지만, 실제 비즈니스 로직과 결함 지점을 찾아내는 분석적 검토가 필수적이다.
저는 Claude와 Next.js를 이용해 제 SaaS의 첫 작동 버전을 주말에 만들었습니다. 이 문장은 사실이며, 제가 지난 주말에 대해 해드릴 수 있는 가장 흥미롭지 않은 이야기이기도 합니다.
이 헤드라인 장르는 대부분 거짓입니다. '제가 했다'는 부분은 아니지만, 그 뒤에 숨겨진 의미가 거짓인 경우가 많습니다. '주말 SaaS' 게시물들은 어려운 부분이 실제 제품 출시(production)라고 환상을 팔아넘깁니다. 마치 코드를 충분히 빠르게 타이핑하기만 하면 월요일까지 사업을 갖게 될 것처럼 말이죠. 그래서 이들은 여러분에게 뼈대(scaffold), 예쁜 랜딩 페이지, Stripe 버튼 등을 보여주고, 바로 진짜 작업이 시작되는 지점에서 멈춥니다. 실제로 어려웠던 부분은 코드를 타이핑하는 것이 아니었습니다. 기계가 제가 보는 앞에서 대부분의 작업을 처리함으로써 그것을 명확하게 보여주었기 때문입니다.
그래서 이것이 다른 분석입니다. 솔직한 이야기입니다. 이 이야기는 일요일 오후에 모델이 건네준, 깔끔하고 녹색이며 완벽하게 타이핑된 하나의 diff(변경 사항)에 관한 것입니다. 그 변경 사항은 유료 고객의 돈을 조용히 갉아먹었을 것이고, 제가 발견한 작업 방식의 한 가지 변화가 그것을 잡아냈습니다. 이 변화야말로 지금 전체 제품이 된 핵심입니다. 주말은 저에게 무엇이 문제였는지 가르쳐주었습니다.
과장 없이 시간대별로 실제로 일어난 일
사람들이 클릭한 '분석' 부분은 치워두겠습니다. 왜냐하면 정말 짧기 때문입니다.
금요일 밤은 뼈대를 만드는 작업이었고, 엄청나게 빨랐습니다. create-next-app, App Router, Tailwind, Postgres 인스턴스, Provider를 통해 연결된 인증(auth), 핵심 객체에 대한 스키마 등이 있었습니다. Claude가 대부분을 작성했고 저는 방향만 제시했습니다. 예전 같았으면 토요일 내내 털고 간 작업이었던 것들—설정(config), 보일러플레이트 코드(boilerplate), 제가 같은 미들웨어(middleware)를 설정한 열네 번째 경우—이 저의 설명과 모델의 결과물이 몇 시간 만에 압축되었습니다. 이 과장된 부분은 사실입니다. 설정 문제는 해결되었습니다.
토요일은 실제 기능이었습니다. 즉, 제품을 사용하며 돈을 지불할 가치가 있는 단 하나의 기능입니다. 여기서 리듬이 바뀌었습니다. 저는 모델에게 슬라이스를 요청했고, 모델은 자신감 있고 그럴듯하며 잘 구조화된 구현체를 생성했습니다. 그리고 저는 코드를 직접 작성하기보다는 그것을 읽는 데 대부분의 시간을 보냈습니다. 'LGTM(Looks Good To Me)'이라는 의미로 검토하는 것이 아니라, 자신이 결코 확신하지 못하는 매우 빠른 주니어 개발자의 diff를 읽는 것처럼 읽었습니다. 대부분은 좋았습니다. 일부는 보기에는 좋아 보이지만 틀렸고, 이 비율이 이런 방식으로 구축하는 전체 이야기입니다.
일요일은 결제와 다듬기(polish)를 할 예정이었습니다. 하지만 이곳에 이 글이 존재하게 된 이유가 되었습니다.
완벽해 보이는 diff
일요일 오후, 저는 웹훅 핸들러(webhook handler) — 즉 결제 이벤트를 수신하고 계정을 업데이트하는 장치 — 를 요청했습니다. 모델은 제가 보고 싶어 하는 바로 그것을 돌려주었습니다. 처음부터 끝까지 타입이 지정되어 있고, 관용적이며(idiomatic), 적절한 위치에 오류 처리가 되어 있었습니다. 심지어 재시도 의미론(retry semantics)을 설명하는 주석까지 추가했는데, 이런 사려 깊은 터치가 작성자를 신뢰하게 만듭니다. 컴파일되었고, 스스로 유용하게 작성한 테스트를 통과했으며, 깔끔했습니다.
레코드를 영구 저장하기 전에 웹훅을 승인(acknowledge)했습니다.
만약 당신이 이 라인 때문에 피해를 본 적이 없다면, 그것은 아무것도 아닌 것처럼 보입니다. 핸들러는 결제 제공업체에 200 OK를 반환하고, 그 후에 데이터베이스에 레코드를 작성합니다. 99.9%의 경우 이 두 가지 일은 마이크로초 간격으로 일어나서 당신은 알아차리지 못합니다. 하지만 제공업체는 빠른 200이 아닌 모든 것에 대해 재시도하며, 쓰기 작업은 실패하거나 지연될 수 있습니다. 그리고 한 번 나쁜 밤에 승인(ack)은 나갔는데 저장(save)이 이루어지지 않는다면, 제공업체는 성공을 보고하고 다시 시도하지 않으며, 당신은 그들이 돈을 냈지만 아무 기록도 없는 고객을 갖게 됩니다. 방금 구매한 것을 사용하지 못하게 되고, 그들이 존재했다는 말조차 로그에 없습니다.
AI가 작성한 코드의 버그를 찾아낸 경험과 구조적 개선점
내 몸에 있는 이 버그를 알고 있다. 몇 년 전 다른 제품에서 배포했을 때의 것인데, AI와는 전혀 관련이 없던 깨끗한 쓰기 경로(clean write path)였고, 영속화되기 전에 승인(ack)만 하고 끝내는 방식이었다. 그 때문에 고객으로부터 '잠금 처리되었다'는 전화 통화를 아직도 생생하게 기억한다. 그래서 나는 그것을 보자마자 알아봤다. 내가 그것을 잡은 유일한 이유가 그거였다. 모델이 경고하지 않은 이유는 코드 자체에 뭔가 잘못된 것처럼 보이지 않았기 때문이다. 마치 신중하게 작업한 사람의 결과물처럼 보였으니까. 버그는 구문(syntax)에 있는 것이 아니다. 순서(ordering)에 있으며, 그 순서는 아무 문제 없이 읽힌다.
당신을 더 불편하게 만들 부분은 바로 이것이다: 두 줄의 재배치. 그것이 해결책이었다. 이번 주 가장 위험했지만 수리하기는 가장 저렴했던 차이점(diff)이다. 오직 루프 안에 있는 누군가가 아름다운 코드를 감상하는 대신 "재시도 시에는 무슨 일이 발생할까?"라고 적극적으로 질문했을 경우에만 말이다.
내가 바꾼 것 — 그리고 그것은 'AI의 출력을 더 주의 깊게 검토하라'는 것이 아니다
가장 명백한 교훈은 "모델이 작성한 것을 읽어라"일 것이다. 물론, 쓸모없는 조언이다. 나는 실제로 그것을 읽었다. 내가 직접 이 버그에 의해 한 번 불타버렸기 때문에 이것을 잡았고, 다른 누군가를 태울 다음 버그는 절대 놓치지 않을 것이다. "더 주의를 기울여라"는 시스템이 아니다. 그것은 희망이며, 그 희망은 우연히 흉터가 생긴 버그들을 넘어서 확장되지 않는다.
진짜 교훈은 구조적이며, 놀랄 만큼 단순하다:
코드를 작성하는 주체와 코드를 승인(bless)하는 주체는 같아서는 안 된다.
내가 한 모델에게 diff를 생성하게 하고 그것이 안전한지 알려달라고 요청할 때, 나는 저자 자신에게 자신의 숙제를 평가하도록 요구하는 것이다. 그들이 모든 것에 사용하는 것과 똑같은 자신감 있는 목소리로 말이다. 이 모델은 ack-before-persist 핸들러를 작성했고, 자신이 "방금 이것을 생성했다"는 내부 시각에서는 그것이 정확하다고 알려줬을 것이다. 왜냐하면 실제로 컴파일되고, 요청에 부합하며, 신중함처럼 보이기 때문이다. 자기 평가(Self-assessment)란 바로 열정적인 저자가 가장 취약한 부분이다.
그래서 저는 그렇게 하는 것을 멈췄습니다. 이제 diff를 생성하는 모델은 그것을 승인하는 주체가 아닙니다. 여기에는 '저자(author)' 검토 단계가 있는데, 이 역할의 임무는 쉽고, 싸고, 끝없이, 정말로 뛰어난 성능을 발휘하는 무언가를 생산하는 것입니다. 그리고 그 다음으로 완전히 분리된 '회의론가(skeptic)' 검토 단계가 있습니다. 이 단계의 전체 임무는 diff를 감탄하며 바라보는 것이 아니라, _그것을 깨뜨리려고 시도_하는 것입니다. "이게 좋은가요?"라는 질문에 대한 요청이 아니라, "여기에 diff가 있으니, 돈을 잃게 만드는 입력값을 찾아보세요"라는 요청을 받습니다. 이렇게 요청받았을 때, 그 핸들러(handler)에 대해 재시도 케이스(retry case)가 가장 먼저 도달하는 지점입니다. 그리고 마지막으로 '병합 버튼을 누르는 인간'—바로 저 자신—이 있습니다. 저는 이 호출권을 소유하고 있으며, 어느 검토 단계도 알 수 없는 폭발 반경(blast radius)을 볼 수 있는데, 왜냐하면 저는 잠긴 고객이 로그 라인(log line)이 아니라 전화 통화라는 것을 알고 있기 때문입니다.
저자, 회의론가, 인간. 저자는 무한한 처리량을 가지고 있으며, 저는 그것을 거의 아무것도 아닌 것으로 측정하는 법을 배웠습니다. 회의론가는 코드를 0개 생산하지만, 시스템에서 가장 가치 있는 자리입니다. 이것은 제가 어딘가서 읽었던 철학이 아닙니다. 주말 동안 제게 주어진 형태입니다. 저자가 고객 한 명을 잃게 할 수 있었지만 잘못된 것이 아무것도 없었다고 생각하는 아름다운 diff를 건네주었을 때, 그 과정에서 배운 것입니다.
주말이 실제로 사주는 것(그리고 사주지 못하는 것)
나머지에 대해서도 솔직히 말하자면: 주말은 _핵심 루프가 작동함을 증명하는 프로토타입_을 얻게 해줍니다. 비즈니스를 가져다주지는 못합니다. 일요일 밤이 되자, 저는 저의 기기에서, 저의 데이터를 가지고, 그 가치 있는 행동 하나를 끝까지 수행하는 무언가를 갖게 되었습니다. 그것은 진짜이며 어떤 가치가 있습니다. 이는 아이디어와 사람 앞에서 보여줄 수 있는 아티팩트(artifact) 사이의 차이입니다.
주말이 만들어내지 못한 것, 그리고 어떤 주말도 만들어낼 수 없는 것이 있다: 에러 상태(error states), 빈 상태(empty states), '입력이 쓰레기일 때 무슨 일이 일어나는가', 결제 관련 엣지 케이스(billing edge cases), 오용 벡터(abuse vectors), 온보딩 과정, 당신처럼 생각하지 않는 두 번째 사용자, 그리고 새벽 2시에 잠 못 이루게 하는 그 무언가. 이것은 몇 주간의 작업이 아니라, 몇 달에 걸친 것이며, 실제 제품의 _대부분_을 차지한다.
기계는 이미 쉬웠던 부분을 압축해 버리고, 어려운 부분은 예전처럼 그대로 남겨두었다 — 어쩌면 더 어려워졌는데, 이제는 틀릴 수 있는 더 많은 확신에 찬, 그럴듯한 코드가 있기 때문이다.
따라서 누군가 '주말 동안 SaaS를 구축했다'고 읽으면, '나는 주말 동안 핵심 루프의 설득력 있는 프로토타입을 만들었고, 기계가 타이핑하는 비용을 충분히 저렴하게 만들어 그 시간을 온전히 그것이 존재했는지 여부가 아니라 _올바른지_에 집중할 수 있게 했다'로 번역해야 한다. 이것이야말로 실제적인 선물이다. 제목보다 작지만 훨씬 더 유용하다.
왜 그 주말이 내가 지금 구축하는 것의 전체 아키텍처가 되었는가
일요일에 거의 실패했던 경험은 프로젝트의 각주가 아니었다. 그것 자체가 프로젝트가 되었다.
내가 만들고 있는 xenition은 에이전트 플랫폼이며, 현재 업계 전체는 생산하는 부분—얼마나 많이 배포했는지, 얼마나 깔끔한지, 얼마나 빠른지를 보고 환호하기를 원한다. 하지만 생성기는 단순히 희소성이 사라진 기술을 인간이 따라잡을 수 없는 규모로 수행하고, 그 결과물이 걸작이든 결제 정보를 처리하는 핸들러(handler)가 되든 상관없이 똑같은 목소리로 서명할 뿐이다. 따라서 제품의 전체적인 형태는 나의 주말이 가르쳐준 것이다: 저자가 자신의 출력을 절대 축복하게 두지 마라. 생성하는 저자, 저자가 만든 것을 공격하는 것이 유일한 임무인 회의론자(skeptic), 그리고 병합을 소유한 인간. 풍부한 것, 희소한 것, 그리고 그 호출에 책임지는 사람이다.
저는 그 주말에 제품 아키텍처를 찾으려고 시작한 것이 아니었습니다. Claude와 Next.js가 월요일까지 저를 얼마나 끌고 갈 수 있는지 확인해보고 싶었을 뿐입니다. 그리고 그 답은 이랬습니다. 작동하는 프로토타입까지, 그리고 성공처럼 보이는 재앙을 배포하기 직전의 '재주문-두 줄' 버그거리였습니다. 이것이 주말이 제게 보여줄 수 있었던 가장 가치 있는 것이었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기