
이번 편이 만지는 곳: 개발의 화면(FE) / 백엔드(BE) / 데이터베이스(RDB). 빈 폴더에서 동작하는 웹앱 골격을 세우고 3-tier 구조를 코드로 확인한다.
빈 폴더 하나를 열어 두고 시작하자. 이 글을 따라오면 30분 안에 화면이 뜨고 백엔드가 응답하는 웹앱 골격이 생긴다. 중요한 건 속도가 아니라, 그 골격 안에서 3-tier가 무엇이고 화면과 서버가 어디서 연결되는지를 코드를 보며 직접 확인하는 일이다. Claude Code를 "이거 만들어 줘"만 외치는 자판기가 아니라, 왜 그렇게 만들었는지 되묻는 선생님으로 쓰는 첫 연습이기도 하다.
이번 편에서 처음 쓰는 Claude Code 기능은 세 가지다. 실행 전에 계획부터 받는 Plan Mode, 답이 아니라 판단을 묻는 "왜 그렇게 했는지 설명해", 그리고 팀 규칙을 새기는 CLAUDE.md. 프론트엔드는 Next.js와 TypeScript로 화면과 일반 데이터를 맡고, 임베딩이나 검색 같은 AI 작업은 뒤에서 FastAPI(Python) 백엔드가 맡는다. 데이터베이스는 PostgreSQL을 로컬에서 띄워 빠르게 개발하고, 운영 배포 단계에서 Supabase로 옮긴다. 앞으로 한 서비스(문서 질의응답과 스터디 관리)를 편마다 한 겹씩 키워 갈 것이다.
시작 전 준비 (한 번만) — 로컬에서 빠르게 만들고 확인하는 게 먼저다. 로컬엔 PostgreSQL(Docker로 띄우는 게 가장 빠르다)을 두고, 저장소와 CI는 GitHub, 프론트 배포는 Vercel을 쓴다. 데이터베이스를 관리형으로 옮기는 건 운영 배포 단계에서다. 그때 Supabase(관리형 PostgreSQL에 인증과 저장소까지)로 바꾼다. 임베딩이나 모델 호출 같은 AI 작업은 FastAPI(Python) 백엔드가 맡는데, 그쪽 생태계가 AI에 강해서다. 비밀키와 주소는 코드에 박지 말고 .env 파일로 뺀다. 환경 변수란 코드 밖에 따로 두는 설정값으로, 코드에 박으면 유출되기 쉽고 바꾸기도 번거롭기 때문이다. 운영에서 쓸 무료 등급의 함정 셋만 미리 알아 두자. Vercel 무료(Hobby)는 비상업적 전용이고, Supabase 무료는 7일 안 쓰면 잠들며(주기적 접속으로 깨운다), 무료엔 자동 시점 복구(PITR)가 없다.먼저 개념 셋 — 식당, 메뉴판, 손목밴드
용어를 사전식으로 옮기면(예를 들어 "표현 상태 전이") 그 번역이 또 다른 외계어가 된다. 무슨 뜻인지를 일상 비유로 먼저 잡고 가자.

3-tier 구조 — 식당으로 이해하기
웹 서비스는 식당과 닮았다. 손님이 보는 홀(화면, 프런트엔드), 주문을 처리하는 주방(로직, 백엔드), 재료를 보관하는 창고(저장소, 데이터베이스)로 나뉜다. 왜 나눌까? 홀 인테리어만 새로 하거나 주방 기구만 바꾸듯, 각 층을 따로 고치고 따로 배포하기 위해서다. 한 덩어리로 붙여 두면 작은 변경에도 가게 전체를 다시 손봐야 한다.
REST — 메뉴판으로 주문하기
REST(Representational State Transfer: 표현 상태 전이)는 이름만 어렵지 핵심은 단순하다. 무엇을(자원) 어떻게 할지(행동)를 정해진 규칙으로 주고받는 방식이다.
- 자원은 주소(URL)로 가리킨다. 예:
/studies(스터디 목록),/studies/3(3번 스터디). - 행동은 네 개의 동사로 표현한다. GET(보여줘), POST(새로 만들어), PUT(고쳐), DELETE(지워).
- 식당으로 치면
GET /studies는 "스터디 목록 좀 보여주세요",POST /studies는 "새 스터디 하나 만들어 주세요"다. 손님(프런트)과 주방(백엔드)이 같은 메뉴판 규칙으로 소통하니, 서로 다른 사람이 만들어도 척척 맞물린다.
인증 두 가지 — 손목밴드와 발렛 키
JWT(JSON Web Token: 토큰 기반 인증)는 놀이공원 손목밴드다. 한 번 로그인(입장)하면 위조 못 하게 도장 찍힌 밴드를 받고, 다음부터는 밴드만 보여주면 매번 신분증 검사 없이 통과한다. OAuth(Open Authorization: 제3자 위임 인증)는 "구글로 로그인"이다. 우리가 비밀번호를 직접 받지 않고, 구글에게 "이 사람 맞아요?"를 대신 확인받는다. 비밀번호를 우리가 안 가지니 그만큼 안전하다.
따라하기 1 — Plan Mode로 계획만 먼저
코드를 바로 쏟아내게 두지 말고, Plan Mode(Claude Code에서 /plan 또는 단축키로 진입)를 켠 다음 계획부터 받자. 목수에게 톱질을 시키기 전에 설계도를 먼저 그려 달라는 것과 같다.
Next.js + TypeScript 프론트엔드와 API, 로컬 PostgreSQL로
'문서 질의응답과 스터디 관리' 서비스를 시작하려고 해. 코드를 바로 쓰지 말고,
먼저 폴더 구조와 만들 순서만 계획으로 보여 줘.계획서가 나오면 같이 읽으며 묻는다. "프론트와 백엔드가 어디서 연결되지? API 주소가 코드에 박혀 있나, 환경 변수로 빠져 있나?" 보통 화면 쪽(app/)과 API 라우트(app/api/), 그리고 PostgreSQL 연결 설정으로 갈린다. 임베딩이나 검색 같은 AI 작업은 곧 FastAPI(Python) 백엔드로 떼어 낸다. 이 셋 사이에 선을 그어 보면, 프론트는 주방에 직접 들어가지 않고 정해진 창구(API 주소)로만 주문을 넘긴다. 그 주소가 코드 여기저기 박혀 있으면 나중에 서버 주소가 바뀔 때 전부 찾아 고쳐야 한다. 그래서 주소는 한곳(환경 변수)에 모아 둔다.
따라하기 2 — 계획을 승인하고 골격 생성
계획이 괜찮으면 실행을 승인한다.
이 계획대로 최소 골격을 만들어 줘. 첫 화면에 "스터디 목록"이 보이고,
백엔드에 헬스체크용 GET /api/health 하나만 있으면 돼.여기서 헬스체크란 서버가 살아 있는지 확인하는 최소한의 응답이다. 생성된 코드에 한마디 덧붙인다. 방금 만든 구조에서 프론트가 백엔드를 어떻게 호출하는지, 왜 그렇게 했는지 설명해 줘. 답이 아니라 판단을 배우는 단계다.
따라하기 3 — 모호한 한 줄의 함정
이번엔 일부러 나쁘게 지시해 본다. 이 글에서 가장 중요한 장면이다.
로그인 기능 추가해 줘.Claude Code는 JWT와 OAuth 중 하나를 임의로 골라 구현한다. 그때 멈추고 묻는다. "왜 이걸 골랐지? 우리 서비스엔 뭐가 맞나?" 둘 다 로그인이지만 역할이 다르다. JWT(손목밴드)는 우리가 직접 회원가입과 비밀번호를 받아 관리하는 방식이라 보안 책임도 우리 몫이다. OAuth("구글로 로그인")는 비밀번호 확인을 구글에 맡기니 유출 위험은 줄지만 구글 계정이 없는 사용자는 못 쓴다. 그래서 보통은 둘을 섞는다. 가입 문턱을 낮추려 "구글로 로그인"을 제공하고, 로그인 이후의 세션 유지는 손목밴드(JWT)로 한다. 방금 AI는 이 판단을 대신 해 버렸다. 우리 서비스에 맞는지는 우리가 정해 줘야 한다.
소셜 로그인은 OAuth(구글)로, 자체 세션은 JWT로 바꿔 줘.
그리고 이 결정을 CLAUDE.md에 이유와 함께 적어 줘.모호한 한 줄 지시는 AI가 대신 결정하게 만든다. 그 결정을 확인하지 못하면, 빠른 코드가 곧 빠른 부채가 된다.
따라하기 4 — CLAUDE.md 첫 작성
팀의 사규를 처음 새기는 단계다. 한 번 적어 두면 Claude Code가 매 작업에서 이 규칙을 지킨다.
이 프로젝트의 기본 규칙을 CLAUDE.md로 정리해 줘.
스택은 Next.js/TypeScript 프론트와 FastAPI(Python) 백엔드, DB는 로컬 PostgreSQL에서 운영은 Supabase로, API 주소는 환경 변수로,
인증은 OAuth와 JWT 조합, 커밋 메시지는 한 줄 요약 형식.긴 작업에서 손에 붙는 명령 몇 개도 함께 익혀 두자. /help로 전체 명령을 한 번 둘러보고, CLAUDE.md를 열어 고칠 땐 /memory를 쓴다. 대화가 길어져 앞 작업이 끼어들면 /clear로 비우고 새로 시작하며, 지금 컨텍스트가 얼마나 찼는지는 /context로 본다.
Plan Mode부터 CLAUDE.md까지, 이 흐름을 한 장으로 보면 이렇다.

띄워서 확인 — 에이전트가 한 일을 검증한다
화면이 떴다고 일이 끝난 게 아니다. 에이전트는 골격을 빠르게 세우는 동안 인증 방식이나 API 주소를 어디 둘지 같은 결정을 슬쩍 대신 내린다. 그래서 확인은 "돌아가는가"가 아니라 "에이전트가 무엇을 나 대신 정했는가"를 본다. 시킨 결과를 검증하는 방법은 늘 같은 세 가지다. 무엇을 만들었는지 코드를 직접 열어 보고, 미심쩍은 곳은 왜 그렇게 했는지 설명해로 되묻고, 백엔드를 일부러 꺼서 안 되는 경우에 어떻게 반응하는지 깨뜨려 본다.
프론트와 백엔드를 실행하는 방법을 알려 주고, 한 번에 띄우는 스크립트도 만들어 줘.브라우저로 접속해 "스터디 목록" 화면이 뜨는지, GET /api/health가 응답하는지 본 다음 아래를 점검한다.
- 프론트가 백엔드를 부르는 API 주소가 환경 변수로 빠져 있는가? 코드에 박혀 있으면 고친다.
- 백엔드가 죽으면 화면에 에러가 보이는가, 아니면 빈 화면인가?
- 방금 만든 코드를 Claude Code가 "왜 그렇게 했는지" 설명할 수 있는가?
한 겹 더 — 스터디 CRUD 기초
스터디 항목을 추가/조회하는 REST API(GET, POST)와 간단한 입력 폼을 붙여 줘.
입력값이 비어 있으면 막아 줘.빈 값으로 등록을 시도하면 막히는지(검증), 새 항목이 목록에 보이는지 확인한다. 여기까지가 CRUD(Create/Read/Update/Delete: 생성/조회/수정/삭제)의 가장 작은 절반이다. 그리고 git(코드의 변경 이력을 시점별로 저장하는 도구)으로 첫 커밋을 남긴다. git add . && git commit -m "환경과 뼈대 + 스터디 CRUD 기초"
지금은 데이터가 임시 상태로만 떠 있다. 다음 편에서는 진짜 데이터베이스를 붙이고, 에이전트가 그 데이터베이스를 망가뜨려도 되돌리는 법을 배운다. 오늘 만든 CLAUDE.md가 그 규칙의 출발점이 된다.