Bolt 프롬프트 엔지니어링 베스트 프랙티스: 프로덕션 레디 코드를 위한 완벽 가이드
Bolt 프롬프트 엔지니어링으로 프로덕션 레디 코드 만들기
Bolt는 AI 기반 풀스택 개발 도구로, 프롬프트 하나로 완성된 웹 애플리케이션을 생성합니다. 하지만 무작정 프롬프트를 입력하면 재생성 사이클이 반복되어 시간과 토큰을 낭비하게 됩니다. 이 가이드에서는 컴포넌트 구조 계획, 점진적 기능 프롬프팅, 의존성 관리, 에러 핸들링 패턴을 체계적으로 다룹니다.
1단계: 컴포넌트 구조 사전 계획
Bolt에 코드를 요청하기 전에 컴포넌트 트리를 먼저 정의하세요. 구조를 명시하면 AI가 일관된 아키텍처를 유지합니다.
구조 정의 프롬프트 예시
React + TypeScript 프로젝트를 생성해줘. 다음 컴포넌트 구조를 따라줘:
src/
├── components/
│ ├── layout/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ └── Footer.tsx
│ ├── ui/
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ └── Modal.tsx
│ └── features/
│ ├── Dashboard.tsx
│ └── UserProfile.tsx
├── hooks/
│ ├── useAuth.ts
│ └── useFetch.ts
├── types/
│ └── index.ts
└── utils/
└── api.ts
각 컴포넌트는 Props 인터페이스를 별도로 정의하고,
children prop은 React.ReactNode 타입을 사용해줘.
이렇게 디렉토리 트리를 ASCII로 명시하면 Bolt가 파일 구조를 정확히 따릅니다.
2단계: 점진적 기능 프롬프팅 (Incremental Prompting)
한 번에 모든 기능을 요청하면 코드 품질이 떨어집니다. 기능별로 나누어 순차적으로 추가하세요.
권장 순서
- 스캐폴딩: 레이아웃과 라우팅만 먼저 구성- 데이터 레이어: API 연동과 상태 관리 추가- UI 인터랙션: 폼 검증, 모달, 알림 기능 추가- 에러 핸들링: 경계 에러, 로딩 상태, 폴백 UI 추가- 최적화: 성능 개선과 접근성 처리
각 단계별 프롬프트 예시
# 1단계 프롬프트
“Header, Sidebar, Footer 레이아웃을 구성하고
react-router-dom v6으로 /, /dashboard, /profile 라우팅을 설정해줘.
Sidebar에는 네비게이션 링크를 포함해줘.”
2단계 프롬프트
“useFetch 커스텀 훅을 만들어줘.
- baseURL은 환경변수 VITE_API_URL을 사용
- loading, error, data 상태를 반환
- AbortController로 cleanup 처리
- 제네릭 타입 지원”
3단계 프롬프트
“Dashboard 페이지에 데이터 테이블을 추가해줘.
- 정렬, 페이지네이션 기능 포함
- 로딩 시 Skeleton UI 표시
빈 데이터일 때 EmptyState 컴포넌트 표시”
3단계: 의존성 관리 프롬프트 패턴
Bolt에서 패키지 충돌은 가장 흔한 재생성 원인입니다. 의존성을 명시적으로 지정하세요.
의존성 명시 프롬프트
“다음 패키지를 정확한 버전으로 설치해줘:
- react: ^18.2.0
- react-router-dom: ^6.20.0
- axios: ^1.6.0
- zustand: ^4.4.0
- tailwindcss: ^3.4.0
@types 패키지도 함께 설치하고,
tsconfig.json에 strict 모드를 활성화해줘.
path alias로 @/를 src/에 매핑해줘.”
환경 변수 설정
// .env 파일 생성 프롬프트
"프로젝트 루트에 .env 파일을 만들고 다음 변수를 설정해줘:
VITE_API_URL=https://api.example.com/v1
VITE_API_KEY=YOUR_API_KEY
VITE_APP_NAME=MyApp
.env.example 파일도 함께 생성하고
.gitignore에 .env를 추가해줘."
4단계: 에러 핸들링 패턴
에러 핸들링을 프롬프트에 포함하면 재생성 횟수를 크게 줄일 수 있습니다.
글로벌 에러 바운더리 프롬프트
“ErrorBoundary 컴포넌트를 만들어줘:
- componentDidCatch로 에러 로깅
- fallback UI에 ‘다시 시도’ 버튼 포함
- 개발 모드에서는 에러 스택 표시
- 프로덕션에서는 사용자 친화적 메시지만 표시
App.tsx의 최상위에 래핑해줘.”
// 생성되는 코드 예시
import { Component, ErrorInfo, ReactNode } from ‘react’;
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error(‘ErrorBoundary caught:’, error, info);
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
문제가 발생했습니다
<button onClick={() => this.setState({ hasError: false })}>
다시 시도
);
}
return this.props.children;
}
}
API 에러 핸들링 프롬프트
"axios 인터셉터를 설정해줘:
- 401 에러 시 자동 로그아웃 및 /login으로 리다이렉트
- 429 에러 시 exponential backoff로 재시도 (최대 3회)
- 500 에러 시 토스트 알림 표시
- 네트워크 에러 시 오프라인 상태 배너 표시"
재생성 사이클을 최소화하는 핵심 전략
| 전략 | 설명 | 효과 |
|---|---|---|
| 구조 선정의 | 파일 트리를 ASCII로 먼저 명시 | 구조 불일치 재생성 70% 감소 |
| 단일 책임 프롬프트 | 한 프롬프트에 하나의 기능만 요청 | 코드 품질 향상, 디버깅 용이 |
| 버전 고정 | 패키지 버전을 명시적으로 지정 | 의존성 충돌 제거 |
| 타입 명시 | TypeScript 인터페이스를 프롬프트에 포함 | 타입 에러 재생성 80% 감소 |
| 컨텍스트 유지 | 이전 대화 참조하여 일관성 유지 | 컴포넌트 간 불일치 방지 |
"모든 컴포넌트는 named export를 사용하고, CSS는 Tailwind utility class만 사용해줘."- **Lock 파일 고정**: 프롬프트에 "package-lock.json 또는 pnpm-lock.yaml을 변경하지 마"를 추가하면 의존성 트리가 안정됩니다.- **Diff 기반 수정 요청**: 전체 재생성 대신 "Header.tsx의 23번째 줄에서 flex-row를 flex-col로 변경해줘"처럼 구체적 위치를 지정하세요.- **테스트 우선 프롬프팅**: "먼저 useFetch 훅의 테스트를 작성하고, 그 테스트를 통과하는 구현을 만들어줘"처럼 TDD 방식으로 요청하면 품질이 높아집니다.- **프롬프트 체이닝**: 복잡한 기능은 3-4개의 연속 프롬프트로 분할하고, 각 단계에서 결과를 확인한 뒤 다음으로 넘어가세요.
## Troubleshooting: 자주 발생하는 문제 해결
문제: 패키지 설치 실패 또는 버전 충돌
**증상**: Could not resolve dependency 또는 ERESOLVE 에러가 발생합니다.
**해결**: 프롬프트에 정확한 버전을 명시하세요. "react 18과 호환되는 버전으로 모든 패키지를 설치해줘. --legacy-peer-deps 플래그는 사용하지 마."
문제: 컴포넌트 간 Props 타입 불일치
증상: TypeScript 에러가 여러 파일에서 동시에 발생합니다.
해결: “types/index.ts에 정의된 인터페이스를 기준으로 모든 컴포넌트의 Props를 다시 정리해줘. 새로운 타입을 만들지 말고 기존 타입을 import해서 사용해.”
문제: 이전 코드가 덮어쓰기됨
증상: 새 기능을 추가했더니 이전에 작성한 코드가 사라집니다.
해결: “기존 코드는 유지하고, Dashboard.tsx의 return문 안에 차트 섹션만 추가해줘. 다른 부분은 절대 수정하지 마.”처럼 보존 범위를 명확히 지정하세요.
문제: 환경 변수를 인식하지 못함
증상: import.meta.env.VITE_API_URL이 undefined를 반환합니다.
해결: Vite 프로젝트에서는 반드시 VITE_ 접두사를 사용해야 합니다. 프롬프트에 “환경 변수 이름은 반드시 VITE_ 접두사로 시작해줘”를 포함하세요.
자주 묻는 질문 (FAQ)
Q1: Bolt에서 한 번의 프롬프트로 전체 앱을 생성하는 것과 단계별로 나누는 것 중 어떤 게 나은가요?
단계별 점진적 프롬프팅이 훨씬 효과적입니다. 한 번에 전체 앱을 요청하면 컴포넌트 간 의존성 충돌, 타입 불일치, 불완전한 에러 핸들링이 발생할 확률이 높습니다. 레이아웃 → 데이터 레이어 → UI → 에러 처리 → 최적화 순서로 5단계에 걸쳐 구축하면 각 단계에서 코드를 검증할 수 있어 재생성 사이클이 크게 줄어듭니다.
Q2: Bolt가 생성한 코드에서 TypeScript 에러가 반복적으로 발생합니다. 어떻게 해결하나요?
types/index.ts에 공유 인터페이스를 먼저 정의하는 프롬프트를 작성하세요. 이후 모든 컴포넌트 프롬프트에 “types/index.ts에서 정의한 타입을 import해서 사용해줘”를 추가합니다. 또한 tsconfig.json의 strict 모드를 처음부터 활성화하면 AI가 더 엄격한 타입 코드를 생성합니다.
Q3: Bolt 프로젝트에서 Git 워크플로우를 어떻게 관리하는 게 좋나요?
각 프롬프트 단계를 하나의 커밋으로 취급하세요. 레이아웃 완성 후 커밋, 데이터 레이어 추가 후 커밋하는 식으로 관리하면 문제 발생 시 특정 단계로 롤백할 수 있습니다. Bolt의 히스토리 기능과 함께 사용하면 프롬프트별 변경 사항을 정확히 추적할 수 있습니다.