바이브코딩 To-Do 실전

도구 역할 분담이 로컬 개발의 성패를 가른다

Claude Code는 코드 생성을, IntelliJ는 빌드·실행을, SourceTree는 버전 관리를 맡는다

설계가 끝났다면 구현은 오히려 단조롭다. 그러나 단조로운 과정에서도 실수 한 번이면 DB를 다시 만들거나 포트를 죽이러 작업 관리자에 들어가야 한다. 로컬 환경에서 To-Do 서비스를 끝까지 완성하려면 Claude Code, IntelliJ, SourceTree 세 도구의 역할을 명확히 나눠야 한다. 이 글은 DB 스키마가 정의된 시점부터 백엔드·프론트엔드 구현과 GitHub 푸시까지를 하나의 사이클로 정리한다

스프링 부트 프로젝트는 start.spring.io에서 시작한다

IntelliJ 커뮤니티 버전은 스프링 부트 프로젝트를 자체적으로 생성해 주지 않는다. 유료 Ultimate 버전이 아니라면 공식 사이트인 start.spring.io에서 프로젝트를 내려받는 것이 표준 경로다. 설정값은 To-Do 예제 기준으로 다음과 같이 고정한다

항목비고
ProjectGradle – Groovy기본값 유지
LanguageJava 
Java Version21이후 JDK도 21로 맞춘다
Spring Boot기본 선택값 
Artifacttodo-listGroup/Name은 자동 채워진다
PackagingJar 
DependenciesLombok, Spring Web최소 두 개는 미리 포함

Generate 버튼을 누르면 todo-list.zip이 내려받아진다. 이 파일을 todolist-project/backend/ 폴더 안에 풀되, 폴더를 한 번 더 감싸지 않도록 주의한다. 압축 해제 시 backend/todo-list/... 형태가 되면 Claude Code가 프로젝트 루트를 잘못 인식한다. backend/ 바로 아래에 build.gradle이 보여야 한다

Claude Code는 프로젝트 루트에서 실행한다

Claude Code의 실행 위치는 반드시 backend/나 frontend/가 아니라 그 상위인 todolist-project/에서 열어야 한다. 풀스택으로 한 세션에 다루기 위함이다. Windows에서는 해당 폴더의 주소창에 cmd를 입력해 명령 프롬프트를 띄운 뒤 claude를 실행한다

todolist-project/          ◀── 여기서 `claude` 를 실행한다
├── backend/               (start.spring.io 압축 해제 결과)
│   ├── build.gradle
│   └── src/
├── frontend/              (이후 Next.js 프로젝트가 생성될 폴더)
└── docs/                  (PRD · 로드맵 · CLAUDE.md · schema.sql)

최초 실행이라면 Claude Code가 테마·계정을 물어본다. 승인 이후 /init 명령을 실행한다. 이 명령이 CLAUDE.md 파일을 생성한다. 이미 Claude Desktop 기획 회의에서 CLAUDE.md 초안을 만들어 두었다면, 그 내용을 Claude Code 프롬프트 창에 그대로 붙여 넣고 “이 내용을 CLAUDE.md에 반영해 달라”고 지시한다. 파일을 수동으로 교체할 필요는 없다. 붙여넣기 시 용량 경고가 뜨지만 무시해도 무방하다

Plan 모드에서 분석 요약을 확인하고 본 개발로 넘어간다

프롬프트를 던지기 전에 Plan 모드로 전환한다. Shift + Tab을 눌러 Plan 모드로 들어간 뒤, 백엔드용 프롬프트 MD 파일의 내용을 붙여 넣는다. Plan 모드에서는 Claude Code가 코드를 바로 찍지 않고 계획을 세워 보여 준다

분석이 끝나면 Yes를 바로 누르지 말고 Ctrl + C로 한 번 멈춘다. 그리고 이 문장을 던진다

지금까지 네가 분석한 내용을 간단히 요약해서 알려 달라.

요약에서 반드시 확인해야 할 항목이 있다

  • DTO(Data Transfer Object) 패턴을 사용하는가
  • 예외 처리 전략이 잡혀 있는가
  • 데이터 접근은 JPA 기반인가
  • 인증은 JWT 기반으로 설계되었는가
  • 현재 백엔드 설정과 CLAUDE.md 명세가 어긋나는 지점은 없는가

설정 충돌이 발견되면 Claude Code가 “어느 쪽을 따를까요?”라고 물어 온다. 특별한 이유가 없다면 Recommended 쪽을 따르는 편이 실패 확률이 낮다. 요약이 합리적이면 일반 모드로 전환한 뒤 본 개발을 시작한다

“백엔드만 구현하라”를 프롬프트에 못 박는다

Claude Code는 방치하면 프론트엔드까지 한 번에 찍어낸다. 스콥을 명시적으로 좁히지 않으면 본인이 보기 좋은 구조로 프론트를 만들어 버리는데, 이렇게 나온 프론트는 UI 쇼케이스 단계에서 검증한 스타일과 달라질 위험이 있다. 그래서 이 한 줄을 반드시 넣는다

스콥 제한 지시

frontend/ 폴더는 건드리지 말고, backend/에 있는 스프링 부트 프로젝트만 구현해 달라.”

이 지시를 받으면 Claude Code는 API 서버 구현에 집중한다. To-Do 예제 기준으로 빌드까지 완료되는 데 5분 내외가 걸린다. 중간에 파일 접근 허용 여부를 물으면 전부 허용하면 된다

빌드와 실행은 IntelliJ에게 맡긴다

코드 생성이 끝나면 Claude Code가 “빌드와 실행까지 확인해 드릴까요?”라고 제안하는 경우가 있다. 이 제안은 거절한다. 빌드와 실행은 IntelliJ 쪽에서 수행하는 편이 이후 디버깅 동선이 훨씬 깔끔하다

IntelliJ를 실행한 뒤 backend/build.gradle 파일을 열기로 선택하면 스프링 부트 프로젝트로 자동 인식된다. JDK가 정의되지 않았다는 경고가 뜨면 Project Structure에서 Java 21로 지정한다. 공급업체는 어느 것이든 무방하지만, 향후 AWS 환경으로 옮길 것을 고려하면 Amazon Corretto 21을 선택하는 편이 일관성에 유리하다. 인터넷이 연결되어 있으면 IntelliJ가 자동으로 JDK를 다운로드·설정한다

빌드는 Build Project로 수행한다. 메인 메서드가 포함된 @SpringBootApplication 클래스가 열린 상태에서 빌드해야 한다. 상단 툴바의 Run 버튼을 무심코 누르면 빌드와 실행이 동시에 일어난다. 에러가 있는 상태에서 서버가 뜨면 원인을 찾기 까다로워지므로, 첫 실행 전까지는 빌드만 수행한다

에러 대응은 콘솔 하단 몇 줄만 Claude에 넘긴다

첫 빌드나 첫 실행에서 에러가 나는 비율은 높다. IntelliJ 콘솔에 찍힌 스택 트레이스를 전부 복사해 Claude Code에 붙여 넣을 필요는 없다. 의미 있는 정보는 예외 메시지와 그 바로 위아래 몇 줄에 몰려 있다

[IntelliJ Console]
  ...
  Caused by: org.postgresql.util.PSQLException:
      FATAL: password authentication failed for user "postgres"
      at org.postgresql.core.v3...
      at org.postgresql.core.v3...
  ▲ ── 이 근처 몇 줄만 드래그하여 복사

[Claude Code] ─▶ 붙여넣기 후
  "위 에러의 원인과 수정안을 제시해 달라."

To-Do 예제에서 자주 나오는 원인은 세 가지로 좁혀진다. DB 이름 또는 스키마명이 어긋나는 경우, 스키마명을 대문자로 쓴 경우, PostgreSQL 계정 비밀번호가 CLAUDE.md에 명시된 값과 다른 경우다. Claude Code가 원인을 잡아 코드를 수정하면, 그다음 빌드를 IntelliJ에서 다시 수행하고 결과를 Claude Code에 다시 넘기는 식으로 루프를 돈다

설정값(DB 비밀번호, 계정, 스키마명 등)이 수정되었다면 반드시 CLAUDE.md에도 반영을 요청한다. 다음 세션에서 Claude Code가 다시 어긋난 값을 참조하지 않도록 막는 장치다

public 스키마 언급이 보이면 즉시 Ctrl+C

설계 단계에서 스키마를 별도로 분리해 두었다면, 테이블은 반드시 그 스키마(예: todolist_db) 아래에 생성되어야 한다. 문제가 잘 풀리지 않을 때 Claude Code가 “public 스키마에 테이블을 생성하겠다”라며 고집을 부리는 경우가 있다. 이 문장이 콘솔에 뜨는 즉시 Ctrl + C로 중단한다

public을 피해야 하는 이유는 세 가지다. 권한 경계가 약하고(과거 모든 역할에 CREATE 권한이 열려 있었으며, 관례상 여전히 취약 구간으로 취급된다), 한 DB 서버에 여러 서비스를 격리해 공존시키기 어려우며, 백업·GRANT·pg_dump -n이 모두 스키마 단위로 이루어져 관리상 스키마 분리가 전제된다

이미 잘못 생성되었다면 DB 전체를 버릴 필요까지는 없다. 잘못된 테이블만 정리한 뒤 올바른 스키마에 다시 생성한다. 다만 초기 단계라면 JPA 엔티티 매핑과 얽혀 꼬이기 쉬우므로 DB 자체를 다시 만드는 쪽이 안전하다

-- public에 만들어진 테이블 제거
DROP TABLE public.users CASCADE;
DROP TABLE public.todos CASCADE;

-- 올바른 스키마에 재생성 (필요 시 스키마부터)
CREATE SCHEMA IF NOT EXISTS todolist_db;
SET search_path TO todolist_db, public;

재발 방지는 애플리케이션·DB 양쪽에 search_path를 고정하는 것이 가장 확실하다. Spring Boot 설정에 다음 두 줄을 넣어 두면 어떤 경로로 접속하든 기본 스키마가 고정된다

spring:
  jpa:
    properties:
      hibernate:
        default_schema: todolist_db
  datasource:
    hikari:
      connection-init-sql: SET search_path TO todolist_db, public

경고

public 스키마에 테이블이 잘못 생성되면 DB를 처음부터 다시 만들어야 한다. 스키마 분리의 목적을 훼손하기 때문에, “public 스키마”라는 문구가 보이는 순간 멈추는 것을 습관으로 둔다.

포트 8080 충돌은 netstat → taskkill 순서로 푼다

Claude Code가 임의로 실행해 버린 서버 프로세스가 남으면 IntelliJ에서 “Port 8080 already in use”가 뜬다. TCP 포트는 동일 번호를 한 프로세스만 LISTEN할 수 있다는 원칙 때문이다. 점유자를 찾아 종료하면 된다

# 1) 8080을 점유한 PID 찾기
#    -a 모든 포트, -n 숫자 주소, -o PID 표시
netstat -ano | findstr 8080

# 2) 해당 PID가 어떤 프로세스인지 확인 후 강제 종료
tasklist /FI "PID eq <PID>"
taskkill /PID <PID> /F

출력에서 상태가 LISTENING인 라인의 마지막 숫자가 PID다. TIME_WAIT 라인은 이미 닫힌 연결의 잔재이므로 잡을 필요가 없다. Mac·Linux라면 lsof -i :8080으로 확인하고 kill -9로 종료한다. 종료가 여의치 않을 때 server.port를 임시로 바꾸는 우회가 가능하지만, 프런트엔드의 API Base URL과 CORS 화이트리스트까지 함께 바꿔야 하므로 좀비 프로세스를 잡는 쪽이 일관성 면에서 깔끔하다

프론트엔드는 세션을 이어받아 한 번에 구현한다

백엔드 구현이 끝난 시점에 컨텍스트는 아직 풍부하다. DB 스키마와 API 스펙을 Claude Code가 기억하고 있기 때문에, 프론트엔드 개발은 거의 즉시 완료 단계까지 간다. 세션을 종료했다면 같은 프로젝트 루트에서 claude -c 옵션으로 실행해 이전 대화를 이어간다

# 프로젝트 루트에서
claude -c

이어진 세션에 프런트엔드 생성을 지시한다

"frontend/ 폴더에 Next.js 프로젝트를 생성하고, 방금 만든 백엔드 API 스펙에 맞춰 UI를 구현해 달라."

Claude Code는 Next.js 프로젝트 생성·의존성 설치·컴포넌트 구성을 명령 프롬프트에서 직접 수행한다. UI는 기획 단계에서 Claude Desktop으로 만들어 둔 쇼케이스 내용이 프롬프트에 이미 녹아 있기 때문에, 별도 지시 없이도 스타일이 유지된다

로컬 실행 순서는 DB → 백엔드 → 프론트다

로컬에서 서비스가 정상 기동하려면 계층별 실행 순서를 반드시 지켜야 한다

① PostgreSQL         (Windows 부팅 시 서비스로 자동 기동)
      │
      ▼
② Spring Boot API    (IntelliJ에서 Run — 8080 포트)
      │
      ▼
③ Next.js Frontend   (`npm run dev` — 3000 포트)
      │
      ▼
  브라우저:  http://localhost:3000

PostgreSQL은 설치 시점부터 시스템 서비스로 등록되므로 별도 기동이 필요 없다. IntelliJ에서 백엔드를 Run으로 실행해 localhost:8080이 살아 있는 상태에서, frontend/ 폴더로 이동해 npm run dev를 실행한다. 브라우저에서 http://localhost:3000에 접속해 로그인 화면이 뜨면 이 단계까지는 성공이다

DB에서 가입 결과를 검증한다

회원 가입을 한 뒤에는 PgAdmin에서 users 테이블을 직접 조회해 검증한다. 이메일 컬럼에 가입 주소가 찍혀 있어야 하고, 비밀번호 컬럼은 단방향 해시된 문자열로 저장되어 있어야 한다. 평문 비밀번호가 그대로 보인다면 인증 구현이 잘못된 것이므로 즉시 Claude Code에 수정을 요청한다. todos 테이블도 같은 방식으로 작성·수정·삭제가 정상 반영되는지 확인한다

컴퓨터를 재부팅한 뒤에도 같은 계정으로 로그인되고 등록한 To-Do 항목이 유지되어야 로컬 환경이 완성된 것으로 본다

GitHub 리포지터리는 프론트·백엔드를 분리해 Private으로 둔다

버전 관리는 GitHub에 두 개의 리포지터리를 만들어 시작한다. 프론트엔드와 백엔드를 하나로 묶지 않는다. 향후 배포 환경(Amplify · EC2)이 갈리고, 변경 주기도 달라지기 때문이다

리포지터리Visibility용도
todo-list-backendPrivateSpring Boot API 서버
todo-list-frontendPrivateNext.js 프런트엔드

공개 리포지터리(Public)로 두면 프로젝트 코드가 외부에 노출된다. 학습용 프로젝트라도 DB 접속 정보 패턴, JWT 시크릿 자리 같은 민감 지점이 드러날 수 있으므로 Private을 기본값으로 둔

SourceTree 워크플로우 – 최초는 Stage·Commit·Push, 이후엔 Pull이 먼저

SourceTree에서 로컬 저장소를 생성할 때는 이미 존재하는 backend/ 폴더를 경로로 지정한다. “해당 경로에 이미 파일이 있다”는 경고가 뜨지만 그대로 진행한다. 기존 프로젝트 폴더가 로컬 저장소로 등록되는 방식이다. 원격 연결은 설정 → 저장소 설정 → 원격 탭에서 Origin 이름에 origin을, URL에는 GitHub 리포지터리의 Quick Setup URL을 그대로 붙인다

최초 푸시는 다음 순서로 진행한다

  1. 전체 파일을 Stage에 올린다.
  2. 커밋 메시지를 작성하고 커밋한다. (최초 커밋은 initial commit 수준이면 충분하다)
  3. Master 브랜치 기준으로 Push를 실행한다.

두 번째 커밋부터는 Pull을 먼저 실행한다. 원격과 로컬의 차이를 미리 당겨와야 충돌을 조기에 파악할 수 있다

[최초 푸시]        Stage → Commit → Push
[이후 사이클]      Pull  → Stage  → Commit → Push

이 사이클을 Claude Code 대화 세션 안에서 대신 시켜도 동작은 한다. 다만 Git 관련 행위까지 세션에 녹으면 이후 Claude Code가 사소한 수정마다 푸시를 시도하는 경향이 생긴다. 버전 관리는 사람이 직접 제어하는 편이 리듬이 깔끔하다. 코드 리뷰를 따로 붙이고 싶다면 Claude Code 자체 서브 에이전트보다 다른 모델이나 Gemini 같은 제3의 에이전트에 맡기는 편이 피드백 관점이 겹치지 않는다

정리

로컬 환경에서 바이브코딩 To-Do를 끝내는 과정은 세 도구의 역할 분담으로 요약된다. Claude Code는 코드 생성과 에러 원인 분석을 맡고, IntelliJ는 빌드·실행·디버깅을 맡으며, SourceTree는 버전 관리를 담당한다. 각 도구가 잘하는 일을 넘기지 않을 때 전체 사이클이 가장 빠르게 돈다. 여기까지가 안정적으로 돌아가야 기능 추가, AWS 포팅 같은 다음 단계로 넘어갈 수 있다. 한 지점이라도 불안정하면 이후의 변경은 그 위에 올라서는 순간 무너진다

출처 – 인프런 [유행 말고 내공. 30년차 개발자의 실전 바이브 코딩]