프론트엔드 개발자가 클로드로 풀스택 개발을 해봤습니다.
서론
6개월 전만 해도 저는 평범한 프론트엔드 개발자였습니다. React, TypeScript로 UI를 만들고, 백엔드는 “API를 호출하는 쪽”에서만 경험했습니다. 서버를 어떻게 구성하는지, 인프라를 어떻게 다루는지, 깊게 알 필요를 느끼지 못했습니다.
그런데 문득 서비스를 혼자 만들어보고 싶었습니다. 프론트엔드·백엔드·모바일 앱·랜딩 페이지까지 전부요. 팀은 없었고, 외주를 맡길 여유도 없었습니다.
그래서 Claude Code를 사용해보기로 결심했습니다.
처음엔 단순하게 생각했습니다. “백엔드는 Claude한테 짜라고 하면 되겠지.” 그런데 원하는 대로 흘러가지 않았습니다. 모르는 코드를 그냥 붙여넣었더니 왜 안 되는지 파악도 못했고, 컨텍스트 없이 시킨 작업은 방향이 엇나갔습니다. 프로젝트를 처음부터 다시 구성하는 일도 있었습니다.
여러 시행착오 끝에, 돌들의 숲이라는 서비스를 만들었습니다. 그 과정에서 AI를 어떻게 써야 하는지, 프론트엔드 너머의 영역을 어떻게 배워가는지를 직접 익혔습니다.
이 글은 그 경험을 정리한 이야기입니다.
본론
기술 스택 및 개발 환경 셋팅
어떤 서비스를 만들었나
돌들의 숲은 익명 P2P 힐링 커뮤니티입니다. 고민이나 소원(원석)을 올리면 모르는 누군가가 위로와 응원(조약돌)을 남기는 서비스입니다.
작은 프로젝트처럼 들리지만, 익명 커뮤니티라는 특성상 모더레이션 시스템, 포인트 경제, 실시간 알림 등 갖춰야 할 기능이 많았습니다. 앱도 여러 개였습니다. 모든 소스 코드를 하나의 레포에서 관리해야 Claude가 프로젝트 전체를 파악하고 작업할 수 있다고 판단해서, Turborepo 기반 모노레포로 구성했습니다.
forest-of-stones/
├── apps/
│ ├── backend/ # GraphQL API (Cloudflare Workers)
│ ├── frontend/ # React 웹앱
│ ├── native/ # React Native + Expo
│ ├── landing/ # Astro 랜딩 페이지
│ └── admin/ # 관리자 패널
└── packages/
├── graphql-schema/ # 공유 GraphQL 스키마
├── shared-config/ # Biome 설정
├── shared-constants/ # 공유 상수
└── typescript-config/ # 공유 TypeScript 설정기술 선택 — 처음엔 이름만 알았던 것들
| 영역 | 기술 | 선택 이유 |
|---|---|---|
| 백엔드 | Cloudflare Workers + Hono + GraphQL Yoga + Pothos + Prisma D1 | 엣지 컴퓨팅, 운영 비용 최소화 |
| 프론트엔드 | React 19 + TanStack Router + Relay + Tailwind CSS 4 | 타입 안전성, GraphQL과의 궁합 |
| 모바일 | React Native + Expo (WebView 래핑) | TypeScript, 빠른 출시 |
| 랜딩 | Astro | 정적 생성, 간단한 배포 |
| 공통 | Biome + Vitest + TypeScript 6 + Bun | 린터·포매터 일원화 |
프론트엔드를 빼면 이 스택 대부분은 이름만 아는 수준이었습니다. “Cloudflare Workers가 좋다더라”는 건 들었지만, 왜 일반 Node.js 서버와 다른지, Durable Objects가 뭔지, D1이 왜 SQLite 기반인지는 몰랐습니다.
Claude에게 “1인 개발로 운영 부담 없이 백엔드를 만들려면 어떤 스택이 좋을까”를 물어보면서 구체적인 이유와 트레이드오프를 같이 이해했습니다. 그냥 추천을 받은 게 아니라, 왜 이 선택이 맞는지를 대화하면서 납득했고, 덕분에 나중에 문제가 생겼을 때 어디를 봐야 하는지 알 수 있었습니다.
스크립트로 만든 자동화 생태계
Claude가 코드를 빠르게 짜줘도, 그 코드를 배포하고 스토어에 올리는 데 매번 30분씩 쓰면 결국 병목은 사람에게 남습니다. 1인 개발에서 반복 작업이 쌓이면 개발할 시간이 줄어듭니다.
그래서 반복되는 작업들을 하나씩 스크립트로 만들었습니다. Claude와 함께 TypeScript CLI로 작성했고, 지금은 이 스크립트들을 적극 활용하고 있습니다.
배포 — 명령어 하나로 끝내기
배포는 생각보다 손이 많이 가는 작업입니다. 타입 체크, 환경변수 동기화, 빌드, Cloudflare 배포, 버전 태그, 팀(=나 혼자지만) 알림까지. 이걸 매번 순서대로 직접 하다 보면 실수가 생기고, 실수를 막으려면 체크리스트가 필요해집니다.
지금은 단일 앱 배포, 통합 배포, 다중 앱 순차 릴리즈 세 가지 상황에 맞는 커맨드가 있습니다. 어떤 경로를 써도 타입 체크 통과, CHANGELOG 기반 버전 관리, git 태그 생성, Discord 알림까지 자동으로 처리됩니다. 버전이 충돌하면 다음 가용 버전을 알아서 찾고, 오류가 나면 변경한 파일을 자동으로 원복합니다.
main 브랜치에 직접 커밋하면 경고와 함께 막히도록 git 훅도 걸어뒀습니다. 혼자 일하다 보면 브랜치 규율이 흐트러지기 쉬운데, 자동화로 강제하는 편이 낫습니다.
앱스토어 스크린샷 — 수작업 제거
모바일 앱을 스토어에 올릴 때마다 스크린샷이 필요합니다. iOS는 iPhone, iPad 11인치, iPad 13인치, Android는 Phone, 7인치, 10인치 태블릿 — 플랫폼마다 해상도와 규격이 다릅니다. 업데이트할 때마다 수작업으로 맞추면 UI 하나 바꿀 때도 스크린샷 세트를 전부 다시 만들어야 합니다.
스크린샷 생성도 스크립트로 만들었습니다. 원본 캡처 이미지에 디바이스 프레임과 홍보 문구를 자동으로 합성해서, 각 플랫폼·사이즈별 PNG를 한 번에 만들어줍니다. UI가 바뀌어도 원본 스크린샷만 새로 찍으면 전체 세트가 나옵니다.
모바일 개발 편의 — 작은 마찰 제거
모노레포 구조에서 Expo가 모바일 앱을 빌드할 때, 워크스페이스 의존성을 올바르게 인식하려면 루트에서 먼저 의존성을 설치해야 합니다. 이걸 빌드 전에 자동으로 처리하는 스크립트가 없으면 CI 빌드가 실패합니다. 한 번 겪고 나서 자동화했습니다.
로컬 개발에서도 작은 마찰이 있었습니다. 모바일 앱이 WebView로 로컬 프론트 서버에 접속할 때 IP를 직접 찾아서 설정해야 했는데, 이것도 네트워크 IP를 자동 감지하는 스크립트로 해결했습니다. 사소해 보이지만, 매일 개발 시작할 때 반복되는 마찰은 없애는 게 맞습니다.
코드 품질
Husky git hook으로 커밋 전에 Biome를 자동 실행해서 코드 스타일 정리와 에러 감지를 처리합니다.
Claude와 일하기
Claude Code를 켜고 쓰면 그냥 대화 상대입니다. 잘 쓰려면 Claude가 프로젝트를 이해하도록 환경을 만들고, 효과적으로 대화하는 방법을 찾아야 합니다.
환경 셋팅 — CLAUDE.md와 .claude/ 디렉토리
Claude Code는 프로젝트 루트의 CLAUDE.md를 매 대화 시작마다 자동으로 읽습니다. 새 팀원에게 주는 온보딩 문서와 같습니다. 저는 두 계층으로 나눴습니다.
seonhamlabs/CLAUDE.md ← 조직 전체 공통 규칙
seonhamlabs/forest-of-stones/CLAUDE.md ← 이 프로젝트 특화 규칙# 지침
- 공통 지침: 상위 seonhamlabs/CLAUDE.md 준수
- 모든 프로젝트 문서는 로컬 위키에서 관리 (기능 명세, 데이터 모델, 배포 가이드)
- 프로젝트 특성: 하이브리드 앱 (React Native Expo + React 웹뷰)
- FE, BE, Native 영역 모두 작업 가능
- 작업 완료 후 bun run build, bun run lint 성공 확인 필수한 가지 원칙이 있습니다. CLAUDE.md는 짧게. 너무 길어지면 Claude가 중요한 규칙을 무시합니다. 각 줄마다 *“이 내용 없으면 Claude가 실수를 할까?”*를 물어보고, 아니라면 지웁니다.
넣어야 할 것은 Claude가 코드만 봐서는 알 수 없는 것들입니다. 빌드·테스트 명령어, 프로젝트 특화 규칙, 문서 위치, 주요 아키텍처 결정 같은 것들이요. 반대로 Claude가 코드를 읽으면 스스로 파악할 수 있는 것, 표준 관행, 자주 바뀌는 정보, 긴 튜토리얼은 넣지 않습니다. 파일이 길어질수록 정작 중요한 내용이 묻힙니다.
.claude/skills/에는 반복 작업을 슬래시 커맨드로 만들어뒀습니다. 새 GraphQL 뮤테이션을 추가할 때의 절차를 skill로 정의해두면 매번 같은 설명을 쓰지 않아도 됩니다. .claude/agents/에는 용도별 서브에이전트를 정의했습니다. 보안 코드 검토 전용 에이전트를 따로 띄우면 메인 컨텍스트를 오염시키지 않고 집중적인 검토가 가능합니다.
탐색 → 계획 → 구현 — 모르는 영역일수록 더 중요하다
백엔드나 인프라 작업을 처음 할 때, Claude에게 바로 “구현해줘”를 시키면 대부분 방향이 엇나갔습니다. 제가 뭘 원하는지 정확히 모르는 상태에서 요청했으니 당연한 결과였습니다.
시행착오 끝에 정착한 방식은, 제가 먼저 이해하고 나서 Claude에게 구현을 맡기는 순서입니다.
- 탐색 — 제가 Claude에게 묻는 단계입니다. “Cloudflare Workers에서 상태를 유지하려면 어떻게 해야 해?”, “D1이 일반 Prisma랑 다른 점이 뭐야?” 같은 질문으로 먼저 개념을 잡습니다. Claude Code의 Plan Mode를 쓰면 Claude가 코드베이스를 파악하면서 설명해주되, 코드는 수정하지 않습니다.
- 계획 — 이해한 내용을 바탕으로 제가 구현 방향을 결정합니다. 어떤 파일을 건드릴지, 어떤 순서로 할지. 이 판단은 Claude가 아닌 제가 합니다.
- 구현 — 방향이 정해진 뒤에 Claude에게 실제 코드 작성을 맡깁니다.
- 검증 — 빌드, 테스트, 타입 체크로 결과를 확인합니다. 이것도 Claude가 직접 실행하도록 요청합니다.
이 순서가 중요한 이유는 단순히 코드를 얻는 게 목적이 아니기 때문입니다. 탐색 단계에서 왜 그렇게 만들어야 하는지를 먼저 이해하면, Claude가 생성한 코드를 검토할 수 있고, 나중에 문제가 생겼을 때 어디를 봐야 하는지도 압니다. Cloudflare Workers에서 긴 연결에 Durable Objects가 필요한 이유, D1이 SQLite 기반이라 Prisma 일부 기능이 다르게 동작하는 이유 — 이런 것들을 탐색 단계에서 이해했습니다.
오타 수정이나 로그 한 줄 추가 같은 작업은 이 과정이 불필요합니다. 처음 다루는 기술이거나, 여러 파일에 걸친 작업이거나, 범위가 불명확할 때 특히 효과적입니다.
모르는 코드도 리뷰할 수 있다
백엔드를 처음 다룰 때 가장 불안했던 건 “Claude가 짜준 코드가 맞는지 모른다”는 점이었습니다. 그냥 믿고 쓰거나, 무조건 의심하거나 — 둘 다 좋지 않습니다.
방법을 찾았습니다. 코드를 짜달라고 하기 전에 왜 이렇게 구현해야 하는지를 먼저 물어봤습니다. “Pothos에서 Prisma 모델을 어떻게 연결하는 게 맞아?”를 먼저 이해하고, 그다음에 “그럼 이걸 구현해줘”로 넘어갔습니다. 코드가 나왔을 때 이미 의도를 알고 있으니 검토가 가능했습니다.
지금은 GraphQL 리졸버를 보고 “이 N+1 문제 생기겠다”를 알아챕니다. Prisma 쿼리를 보고 인덱스 걸어야 할 자리를 찾습니다. 반년 전엔 못 했던 일입니다.
컨텍스트 관리
Claude의 성능은 컨텍스트 윈도우가 가득 찰수록 떨어집니다. 오래된 실패 시도, 관련 없는 파일 내용, 지나간 대화가 쌓이면 중요한 지시를 놓치기 시작합니다.
실천하는 습관들입니다.
- 관련 없는 작업 사이에
/clear: 인증 기능 끝내고 알림 기능 시작할 때 컨텍스트를 비웁니다. - 같은 문제를 두 번 수정했다면 새로 시작: 반복 수정이 쌓이면 실패한 접근들로 컨텍스트가 오염됩니다.
/clear하고 배운 내용을 담아 더 나은 프롬프트로 다시 시작하는 게 빠릅니다. - 코드베이스 조사는 서브에이전트로: “인증 시스템 전체 분석해줘”를 시키면 수십 개 파일을 읽으면서 컨텍스트를 소진합니다. 서브에이전트를 띄워서 요약만 받습니다.
작은 단위로, 검증 기준과 함께
“백엔드를 만들어줘”는 잘 안 됩니다. 결과물이 너무 커서 검토하기 어렵고, 이해도 안 됩니다. 범위가 좁고 레퍼런스가 명확할수록, 검증 기준이 있을수록 결과가 좋습니다.
이 Prisma 스키마를 기반으로 `createStone` 뮤테이션 리졸버를 Pothos로 작성해줘.
관련 타입은 @./src/types/stone.ts에 있어.
모든 필드는 nullable이 아니면 non-null로 지정해줘.
작성 후 bun run type-check 실행해서 통과하는지 확인해줘.검증 기준을 같이 주면 Claude가 스스로 확인합니다. 처음엔 번거롭게 느껴졌는데, 이게 쌓이다 보면 테스트 케이스를 자연스럽게 생각하는 습관이 생깁니다.
기존 코드를 레퍼런스로 주는 것도 효과적입니다. Relay 프래그먼트, Pothos 타입 빌더, TanStack Router 라우트 파일은 패턴이 반복됩니다. 처음 한 번 함께 작성한 뒤, 이후엔 “이 파일처럼 만들어줘”로 일관성을 유지했습니다. 반복하다 보면 그 구조를 자연스럽게 외우게 됩니다.
보안과 인프라는 반드시 직접 검토하기
인증 로직, 권한 체크, 입력 검증은 Claude가 초안을 잡아줘도 직접 읽고 검토합니다. JWT 검증 흐름이 맞는지, Rate Limiting이 제대로 걸리는지, 이건 코드를 이해하지 않으면 확인이 안 됩니다.
Cloudflare Workers나 EAS 빌드 설정도 로컬에서 됐다고 실제 환경에서 되는 게 아닙니다. 엣지 환경에서 다르게 동작하거나, iOS 빌드에서만 터지는 경우를 몇 번 겪었습니다. 이런 경험이 쌓이면서 어디를 믿어도 되고, 어디를 직접 확인해야 하는지 감이 생겼습니다. 확인할 수 있게 됐다는 것 자체가 성장이기도 합니다.
결론
6개월 전 저는 wrangler.toml이 뭔지 몰랐습니다. Cloudflare Workers가 일반 서버와 왜 다른지도 몰랐습니다.
이제는 완벽하지는 않지만, 코드를 보고 왜 이렇게 작성되었는지 이해할 수 있고, 문제가 생겼을 때 어디를 봐야 하는지 알게 되었습니다.
이 변화는 그냥 생긴 것이 아닙니다. Claude가 생성한 코드를 그냥 사용했다면, 아무것도 배우지 못했을 겁니다. “왜 이렇게 하는지”를 먼저 묻고, 이해한 다음 쓰고, 결과를 직접 검토하는 과정에서 많은 것을 배웠습니다.
아직 풀스택 개발자라고 말하기엔 이르지만, 그래도 6개월 전보다는 분명히 더 넓은 시야로 코드를 볼 수 있게 됐습니다.