To-Do 하나에 웹 서비스의 뼈대·권한·세션·스키마가 모두 들어 있다
바이브코딩 강의가 예제로 To-Do 리스트를 택하는 이유는 분명하다. 기능이 단순하지만 웹 서비스가 갖춰야 할 구조가 거의 전부 담겨 있기 때문이다. 게시판, 인증, 접근 권한, 파일 저장, DB 스키마, 배포 환경 전환까지 한 프로젝트에서 순차적으로 다뤄진다. 이 글은 Claude Desktop으로 설계를 마친 뒤 Claude Code에 코드 생산만 맡기는 흐름을 전제로, To-Do 서비스를 로컬에서 시작해 AWS를 염두에 둔 구조로 설계하는 실전 순서를 정리한다
To-Do 리스트에 웹 서비스의 뼈대가 모두 들어 있다
웹 서비스 대부분은 게시판 하나와 사용자 인증 체계로 설명된다. To-Do 리스트는 이 두 가지를 가장 작은 단위로 압축한 예제다. 구조적으로는 간단하지만 기능 관점에서 꼭 익혀야 할 요소가 빠짐없이 들어간다. Next.js를 익히든, Spring Boot를 익히든, 첫 프로젝트로 가장 자주 선택되는 이유가 여기에 있다
바이브코딩도 똑같은 이유로 To-Do를 택한다. 다만 접근 방식은 다르다. 손으로 코드를 찍지 않고, 설계와 지시를 문서화한 뒤 AI가 코드를 찍게 한다. 프로젝트가 단순할수록 설계의 결함이 그대로 드러나기 때문에, 입문 단계의 설계 훈련 도구로는 오히려 더 적합하다
개발 환경은 운영 환경을 전제로 설계한다
개발 환경과 운영 환경은 구분해서 생각해야 한다. 그러나 개발 환경을 구축할 때는 운영 환경을 전제로 두어야 한다. 이 순서가 뒤집히면 로컬에서는 멀쩡히 돌던 서비스가 클라우드로 옮기는 순간 구조를 뒤집어야 하는 상황이 온다
바이브코딩 초기 단계부터 “나중에 AWS에 올릴 것”이라고 AI에 명시하는 이유가 이것이다. AWS든 다른 클라우드든, 옮길 환경을 확정해 두면 AI가 선택하는 스택과 구조가 달라진다. 이 한마디만 추가해도 프로젝트 후반에 치러야 할 리팩터링 비용이 줄어든다
설계는 3-Tier를 기본으로 둔다
소프트웨어 공학에서 가장 오래된 설계 원칙 가운데 하나가 UI, 데이터, 로직(제어)을 분리하라는 것이다. 웹 서비스에서는 이 세 가지가 프런트엔드, 데이터베이스, 백엔드 서버로 자연스럽게 매핑된
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Frontend │ ──▶ │ Backend │ ──▶ │ Database │ │ (Next.js) │ │ (Spring) │ │ (PostgreSQL) │ │ UI / View │ │ API / Logic │ │ Data │ └──────────────┘ └──────────────┘ └──────────────┘
이 구조에서 성능 이슈는 대부분 백엔드, 특히 데이터베이스 계층에서 발생한다. 프런트엔드에서 성능 문제가 생기지 않는다는 뜻은 아니지만, 서비스 전체의 병목은 데이터 계층에 몰리는 경우가 많다. 3-Tier로 분리해 두면 병목을 계층 단위로 격리해 진단할 수 있고, 계층별로 확장 전략을 따로 세울 수 있다
프런트엔드를 분리해 두는 또 다른 이유는 변화 주기가 다르기 때문이다. UI는 트렌드에 민감하고 자주 바뀐다. 컬러 팔레트 하나만 교체해도 사용자 경험이 달라진다. 핵심 로직과 UI의 결합도가 낮을수록 교체 비용이 줄어든다. 슈퍼카가 프레임과 외피를 분리해 제작하는 방식에 비유할 수 있다. 외피만 바꿔 끼울 수 있어야 변화가 빈번한 영역에서도 코어가 흔들리지 않는다
HTTP는 Stateless이므로 JWT로 세션을 흉내 낸다
HTTP는 기본적으로 Stateless 프로토콜이다. 상태라는 개념이 없다. 그러나 서비스 관점에서는 로그인 여부, 권한 유지 같은 상태가 반드시 필요하다. 앱을 껐다 켜도 로그인 상태가 이어져야 하고, 재시작 이후에도 세션이 유지되어야 한다
이 간극을 메우는 표준적인 방법이 JWT(JSON Web Token)다. 서버가 토큰을 발급해 클라이언트에 전달하고, 클라이언트가 이후의 요청에 토큰을 실어 보낸다. 서버는 토큰만으로 요청자의 상태를 판별한다. 세션 저장소를 서버가 직접 관리하지 않아도 되기 때문에, 수평 확장이 쉬운 Stateless 아키텍처와 잘 맞는다
AI에 설계를 요청할 때 “세션 관리는 어떻게 할 것이냐”는 질문을 생략하면 안 된다. “JWT 기반으로 로그인 세션을 유지한다”라고 구체적으로 명시해야 프런트엔드·백엔드 양쪽의 구현 방향이 일관되게 잡힌다
To-Do 게시물 한 줄에도 숨은 복잡도가 있다
To-Do 항목을 단순한 글 목록으로만 이해하면 놓치는 부분이 많다. “누가 이 항목을 볼 수 있는가”라는 질문을 던지는 순간 복잡도가 빠르게 올라간다
- 비로그인 사용자도 볼 수 있는가, 아니면 로그인한 사용자만인가
- 로그인했더라도 작성자 본인만 열람 가능한가, 특정 그룹에게만 공개하는가
- 항목에 첨부된 이미지나 파일은 어디에 저장되며, URL을 아는 사람이 직접 접근할 수 있는가
특히 첨부 파일은 훗날 가장 큰 문제를 만드는 지점이다. 로컬 파일 시스템에 저장하고 정적 경로로 노출하면, 게시물 접근 권한과 파일 접근 권한이 분리된다. 게시물은 보호해도 파일 URL을 아는 사람은 우회해서 파일을 내려받을 수 있다. AWS 환경으로 옮기는 순간 이 문제는 더 복잡해진다
실전에서 쓰는 해법은 서명된 URL(Signed URL) 방식이다. 파일은 S3 같은 오브젝트 스토리지에 두고, 다운로드 시점에 만료 시간이 포함된 임시 URL을 서버가 발급한다. 로컬에서 개발할 때는 비슷한 구조를 흉내 내는 로컬 스토리지 어댑터를 끼워 두었다가, 운영 환경에서 S3로 교체하는 방식이 무난하다. 이 방침은 반드시 설계 단계에서 프롬프트에 넣어야 한다. 개발이 끝난 뒤에 교체하려 들면 파일 업로드·다운로드 경로가 전부 얽혀 있다
최초 프롬프트에 스택과 제약을 못 박는
Claude Desktop에 기획 회의를 요청할 때, 기술 스택을 먼저 못 박는다. AI 추천에 맡겨도 되는 항목이 있고, 그렇지 않은 항목이 있다. To-Do 예제에서는 다음 세 가지를 고정하는 편이 낫다
| 계층 | 기술 스택 | 비고 |
|---|---|---|
| Frontend | Next.js | UI 컴포넌트 기반 구성 |
| Backend | Spring Boot (Java) | API 서버 방식 |
| Database | PostgreSQL | 스키마명은 영문 소문자 |
여기에 한 줄을 반드시 덧붙여야 한다. “아직 코드는 생성하지 마라. 설계에만 집중하라.” 이 문구가 없으면 Claude Desktop은 대화 중간부터 코드를 찍어내기 시작한다. 설계 단계에서 토큰을 소모하지 않으려면 역할을 분리해 두어야 한다. 실제 코드 생산은 Claude Code가 담당한다
프롬프트에 반드시 포함할 항목
- 기술 스택(Next.js · Spring Boot · PostgreSQL)
- 로그인/세션 방식(JWT 기반)
- 게시물 접근 제어와 파일 서명 URL 방침
- 배포 대상(AWS 또는 대안 클라우드)
- “코드 생성 금지, 설계에만 집중” 지시
UI는 컴포넌트 쇼케이스 페이지에서 먼저 검증한다
UI는 본 개발에 들어가기 전에 검증이 가능한 영역이다. AI에게 컴포넌트 라이브러리(예: shadcn/ui)를 기반으로 먼저 쇼케이스 페이지를 만들게 한다. 버튼, 인풋, 셀렉트, 모달, 테이블 같은 기본 요소를 한 페이지에 모아 놓고 눈으로 확인한다. 색상, 모서리 곡률, 여백이 마음에 들어야 본 개발로 넘어간다
AI가 쇼케이스 페이지를 먼저 언급하지 않는 경우에는 사용자가 명시적으로 요청해야 한다. “컴포넌트 기반으로 구성하고, 모든 컴포넌트를 확인할 수 있는 쇼케이스 페이지를 먼저 만들어 달라”라고 지시한다. 컴포넌트화를 해 두면 이후 UI 변경 비용이 현저하게 줄어든다
품질은 컨벤션·보안·성능 세 질문이 끌어올린다
프롬프트를 다듬는 마지막 단계에서 세 가지 키워드를 반드시 꺼낸다. 코딩 컨벤션, 보안, 성능이다. 이 셋은 AI가 스스로 우선순위를 낮게 두는 경향이 있는 주제다. 사용자가 먼저 꺼내야 답변의 구체성이 올라간다
- 코딩 컨벤션: 표기법(카멜, 스네이크), 변수 명명 규칙, 주석 언어, 린터 설정까지 구체적으로 명시한다. “내가 놓친 컨벤션이 있는지 의견을 달라”라고 덧붙이면 추가 제안이 따라온다
- 보안: “보안을 매우 신경 쓰고 있다”라는 한 문장을 추가하면 인증 토큰 보관 방식, CSRF/XSS 대응, 권한 체크 위치까지 점검 항목이 늘어난다
- 성능: “지금 단계에서 성능상 신경 써야 할 부분이 있는가”라는 질문만으로 프런트엔드·백엔드·DB별 최적화 포인트가 정리된다. To-Do처럼 작은 서비스에서도 페이지네이션, 인덱스, N+1 쿼리 같은 요소는 미리 짚고 넘어가는 편이 낫다
여기에 하나 더 추가할 만한 항목이 소프트 삭제(Soft Delete)다. 데이터 레코드를 물리적으로 지우지 않고 삭제 플래그만 세우는 방식이다. 복구 가능성, 감사 로그, 통계 정확성을 고려하면 대부분의 업무형 서비스에서 기본값으로 두는 편이 안전하다
DB 스키마는 Claude에게 실행시키지 않는다
DB는 되돌리기 가장 어려운 영역이다. Claude Code가 명령 프롬프트에서 직접 DB에 접속해 스크립트를 실행하게 두면 편리해 보이지만, 사고가 났을 때 복구 비용이 크다. 원칙은 단순하다
DB 스키마는 SQL 스크립트 생성까지만 Claude에 맡기고, 실행은 사람이 PgAdmin으로 직접 수행한다
스크립트를 생성할 때는 두 가지를 반드시 프롬프트에 넣는다. 첫째, 데이터베이스 이름과 스키마 이름을 명시한다. PostgreSQL은 스키마명을 영문 소문자로 쓰는 것이 관례다. 둘째, 스크립트가 길어지면 단계별로 나눠 달라고 요청한다. 중간에 실패해도 이전 단계까지는 살릴 수 있다
PgAdmin에서 스크립트를 실행하는 흐름은 다음과 같다
PgAdmin 실행 │ ▼ [서버 연결] ── 비밀번호 입력 │ ▼ [Databases] ▶ 우클릭 ▶ Create ▶ Database │ (Alt + Shift + N) ▼ [todolistdb 생성] │ ▼ [todolistdb] ▶ 우클릭 ▶ Query Tool │ ▼ 생성된 SQL 스크립트 붙여넣기 ▶ F5 실행 │ ▼ [스키마 · 테이블 생성 확인 (F4 새로고침)]
실행 후에는 반드시 결과를 확인한다. To-Do 예제라면 todolist_db 스키마 아래에 todos와 users 두 테이블이 생성되어 있어야 한다. 에러가 난다면 메시지를 그대로 복사해 Claude Desktop 회의 세션에 붙여 넣고 스크립트를 다시 요청한다. 이 피드백 루프가 끊기지 않도록 회의 세션을 유지하는 것이 중요하다
실습 폴더는 프런트·백 상위에서 Claude Code를 연다
폴더 구조를 설계할 때 자주 놓치는 지점이 있다. Claude Code는 프런트엔드와 백엔드의 상위 폴더에서 실행해야 한다. 두 계층을 한 세션에서 풀스택으로 다룰 것이기 때문이다
todolist-project/ ◀── Claude Code는 여기서 실행한다
├── frontend/ (Next.js)
│ ├── package.json
│ └── ...
├── backend/ (Spring Boot)
│ ├── build.gradle
│ └── ...
└── docs/
├── PRD.md
├── ROADMAP.md
├── CLAUDE.md
└── schema.sql
프런트엔드나 백엔드 폴더에서 Claude Code를 실행하면, 다른 쪽 계층을 참조할 수 없어 세션이 반쪽짜리가 된다. 풀스택으로 구현한다는 전제라면 반드시 상위 루트에서 세션을 연다
문서류는 docs/에 모아 두는 편이 이후 인수인계에 유리하다. PRD, 로드맵, CLAUDE.md 초안, DB 스크립트, 설계 회의 요약본까지 전부 여기에 쌓아 둔다. 세션이 꼬이거나 컴퓨터를 교체해야 할 때, 새 세션에 이 문서들만 읽히면 맥락을 빠르게 복원할 수 있다
정리
To-Do 리스트가 바이브코딩의 첫 프로젝트로 적합한 이유는 단순함이 아니라 압축도에 있다. 가장 작은 구조에 게시판, 인증, 권한, 파일 저장, 세션, 스키마 같은 필수 주제가 모두 들어간다. 설계 단계에서 3-Tier와 JWT, 서명 URL, 소프트 삭제 같은 표준 패턴을 프롬프트에 못 박고, UI는 쇼케이스에서 먼저 검증하며, DB 스키마는 사람이 직접 반영한다. 이 흐름을 한번 제대로 체화해 두면, 이후의 프로젝트는 같은 뼈대 위에 살을 붙이는 작업이 된다