두 개발자가 동일한 AI 도구를 사용합니다. 하나는 30초 안에 작동하는 코드를 얻고, 다른 하나는 고장난 혼란스러운 결과를 얻고 1시간을 보내서 고치는 데 사용합니다.
차이점은 도구가 아닙니다. 명령어입니다.
왜 대부분의 AI 생성 코드가 나쁜지
AI 코딩 도구는 매우 큰 컨텍스트 윈도우를 가진 패턴 매처입니다. 명확한 패턴을 주면 멋지게 완성해 줍니다. 모호함을 주면 가정을 만들어내는데 — 대부분 잘못된 가정을 만듭니다.
일반적인 실수들:
- 너무 모호함:"내 앱에 인증을 추가해줘" — AI는 당신의 스택, DB 스키마, 또는 당신의 관례를 알지 못합니다
- 너무 큼: "SaaS 대시보드를 만들어줘" — AI는 요청하지 않은 아키텍처를 발명하고 너는 한 시간을 헤맬 거야
- No context: "이 버그를 고치세요" 코드 주변 없이 — AI는 시스템을 추측
- 결정을 요청하고: AI를 사용하여 제품 또는 아키텍처 선택을 하기 대신 이미 만든 것을 구현하는 대신
수정은 다른 AI 도구가 아닙니다. 작성 방식이 다릅니다.
프레임워크: 컨텍스트 → 제약 조건 → 작업
모든 효과적인 코딩 프롬프트에는 세 부분이 있습니다.
[CONTEXT] Given this [file/system/function],
[CONSTRAINT] following these [conventions/rules],
[TASK] do [specific, scoped thing].
나쁜 프롬프트:
"삭제 버튼을 추가하세요"
좋은 프롬프트:
"
src/components/ProjectCard.tsx에 삭제 버튼을 추가하여deleteProject서버 액션을src/actions/projects.ts에서 호출하세요. 클릭 시, shadcn/ui에서 기존의AlertDialog를 사용하여 확인 대화 상자를 표시하세요. 삭제 후,revalidatePath('/dashboard')를 호출하세요. 기존의 오류 처리 패턴을 따르세요: 오류를 잡고 sonner를 사용하여 토스트를 표시하지 말고, 던지지 마세요.
동일한 요청입니다. 두 번째 것은 작성에 추가 40초가 걸립니다. 출력은 초기 단계를 수정하는 데 한 시간이 걸리는 대신 생산 준비됩니다.
코딩 프롬프트의 6가지 유형
1. 구현 프롬프트
새로운 기능을 구축하는 데 사용됩니다. 가장 일반적이면서도 가장 쉽게 잘못 이해하기 쉽습니다.
템플릿:
Add [feature] to [file/component].
It should [behavior description].
Use [specific library/pattern] — not [what to avoid].
Follow the existing pattern in [example file or function].
The return/export should look like: [description].
예시:
Add pagination to `getUserProjects` in `src/lib/queries.ts`.
It should accept `{ page: number, limit: number }` and return
`{ data, total, hasMore }`.
Use Drizzle's `.offset()` and `.limit()` — not cursor-based pagination.
Follow the same select pattern as `getUserById`: select specific
columns, never `select *`.
Type the return with a `PaginatedResult<T>` generic.
2. 버그 수정 알림
디버깅을 위해. 예상되는 동작과 실제 동작에 대한 충분한 맥락을 제공하지 않는 오류입니다.
템플릿:
[function/component] in [file] is broken.
Expected: [what should happen]
Actual: [what is happening]
Error: [exact error message if any]
Relevant context: [the failing code + what it calls]
예시:
The `createProject` Server Action in `src/actions/projects.ts`
returns { error: "Unauthorized" } even when the user is logged in.
Expected: creates the project and returns { data: project }.
Actual: always hits the requireAuth() error branch.
Error: no error message, just the unauthorized return.
requireAuth() is in `src/lib/auth.ts` — pasting it below.
The user is definitely authenticated (visible in Clerk dashboard).
3. 프롬프트 리팩토링
기존 코드를 깨뜨리지 않고 개선하기 위해. 핵심 제약 조건: 변경되어서는 안 되는 것을 지정.
템플릿:
Refactor [function/file] to [improvement goal].
Keep: [API, behavior, types that must stay identical]
Change: [what should change]
Don't: [specific things to avoid]
예시:
Refactor `UserTable.tsx` to extract the row actions into a
separate `UserTableActions` component.
Keep: the exact same props API on UserTable, same visual output,
same TypeScript types.
Change: move the action buttons and their handlers to
`UserTableActions.tsx`.
Don't: change how the parent passes data, don't add new dependencies.
4. 테스트 작성 제안
테스트 생성을 위해. 라이브러리, 커버해야 할 내용, 모킹해야 할 내용에 대해 명확하게 설명하십시오.
샘플:
Write [unit/integration] tests for [function/component] in [file].
Testing library: [Vitest/Playwright/etc.]
Cover: [list of specific scenarios]
Mock: [external dependencies to mock and why]
Don't test: [implementation details to avoid]
예시:
Write Vitest unit tests for the `createProject` Server Action
in `src/actions/projects.ts`.
Cover:
- success case (user authorized, valid input)
- validation failure (name exceeds 50 chars)
- unauthorized user (requireAuth throws)
- database error (insert fails)
Mock: `src/lib/db.ts` (mock insert to return a fake project),
`src/lib/auth.ts` (mock requireAuth).
Don't test the actual database or actual auth — only the action's logic.
5. 설명 프롬프트
기존에 모르는 코드를 이해하기 위해. 실수: 일반적인 설명을 요청함.
나쁨:
"이 코드를 설명해줘"
좋음:
"이 Drizzle 쿼리가 무엇을 하는지 설명해줘. 구체적으로:
.$onConflictDoUpdate는 무엇을 하고, 왜는target: [users.clerkId]가 필요한가요? 전체 데이터베이스에 미치는 영향에 대한 한 문장 요약을 제공하십시오."
집중된 질문은 집중된 답변을 얻습니다. "이 코드를 설명해라"는 이미 알고 있는 사항을 다루는 긴 텍스트를 얻습니다.
6. 마이그레이션 및 업그레이드 프롬프트
코드를 새 API 버전으로 업데이트하는 데 사용됩니다.
템플릿:
Migrate [file] from [old API] to [new API].
Breaking changes to handle: [list specific breaking changes]
Don't change: [function signatures your app uses]
Reference: [paste the relevant migration docs]
예시:
Migrate `src/lib/auth.ts` from Clerk v4 to Clerk v5.
Breaking changes: `auth()` is now async, `clerkMiddleware` replaces
`withClerkMiddleware`, user object shape changed.
Don't change the signatures my app uses: requireAuth() and getAuthUser().
Here are the relevant Clerk v5 migration notes:
[paste the breaking changes section from the docs]
맥락은 배수입니다
출력 품질의 가장 큰 요인은 맥락입니다. AI 도구는 당신이 주는 것만 사용할 수 있습니다.
항상 포함해야 할 것들
- 정확한 파일 경로
- 특정 함수 또는 구성 요소 이름 — 전체 파일은 아닙니다
- 따라야 할 패턴의 하나가 있습니다
- 사용 중인 라이브러리 — 스택을 알거나 가정하지 마세요
- "완료"의 모습은 무엇인지 (반환 타입, 시각적 출력, 동작)
포함하지 마세요
- 전체 코드베이스 ("여기 내 프로젝트 전체입니다")
- 관련 없는 파일 ("여기
/lib에 있는 모든 것이 있습니다") - 여러 시스템을 동시에 포함하는 요구사항
가장 좋은 맥락은 모호하지 않게 되어야 할 최소한의 맥락입니다.
팁 (Claude Code): Claude Code를 사용하면 코드를 프롬프트에 붙여넣을 필요가 없습니다. 파일 경로를 참조해도 됩니다 - "in
src/actions/projects.ts"가 충분합니다, Claude Code는 자동으로 읽습니다. 특정 코드 조각을 강조하고 싶을 때만 붙여넣기를 사용하세요.
CLAUDE.md: 영구적 컨텍스트를 무료로 제공합니다
좋은 프롬프트를 작성하는 문제는 매번 규칙을 반복해야 한다는 점입니다: "ID는 cuid2를 사용하세요, 서버 액션을 사용하고 API 라우트는 사용하지 마세요, { data } 또는 { error }을 반환하세요..."
Claude Code를 사용하면, 루트 디렉토리에 CLAUDE.md 파일에 한 번만 작성할 수 있습니다:
# CLAUDE.md
## Stack
- Next.js 15 App Router, TypeScript strict mode
- Drizzle ORM + Neon (PostgreSQL)
- Clerk auth, Zod validation, shadcn/ui
- Biome for linting and formatting
## Conventions
- cuid2 for all IDs, never auto-increment integers
- Soft-delete users (deletedAt), never hard delete
- Server Actions for mutations, not API routes
- Import env from `@/lib/env`, never `process.env` directly
- `requireAuth()` at the top of every protected action
- Return `{ data }` or `{ error: string }`, never throw from actions
- Error messages lowercase, no periods
## File structure
- Server Actions: `src/actions/[feature].ts`
- DB queries: `src/lib/queries.ts`
- Shared types: `src/types/[domain].ts`
Claude Code는 시작 시 이를 읽고 각 프롬프트에 대해 이 규칙을 자동으로 적용합니다. "cuid2를 사용하세요"라고 다시 말할 필요가 없습니다 - 그냥 그렇습니다.
작업 분해: 실제로 경험 많은 개발자 기술
큰 결과를 얻는 개발자와 좌절하는 개발자를 구분하는 단일 기술: 작업을 올바른 크기로 분해합니다.
하지 마세요:
"회원 인증 시스템을 만들어줘. 가입, 로그인, 사회적 인증, Google OAuth, 비밀번호 초기화, 이메일 검증, 세션 관리, 계정 설정 페이지를 포함해."
할 일:
- "Next.js 15 App Router에서 Clerk 설정하기" → 완료
- "
requireAuth()도우미를src/lib/auth.ts에 만들기" → 완료 - "Clerk 미들웨어를
/dashboard경로를 보호하기 위해 추가하기" → 완료 - "
(dashboard)/settings/page.tsx에서 계정 설정 페이지 만들기" → 완료
각 단계는 30초의 프롬프트로 명확하고 검증 가능한 결과가 있습니다. 결합된 결과는 큰 요청과 동일하지만, 허구된 아키텍처는 없으며, 모든 줄을 이해합니다.
규칙: 만약 프롬프트가 인간이 완전히 명세를 작성하는 데 5분 이상 걸린다면, 두 개의 프롬프트입니다.
AI에 결코 묻면 안 되는 질문들
아키텍처 결정:
"마이크로서비스를 사용해야 할까요 아니면 모노리틱을 사용해야 할까요?"
AI는 일반적인 패턴에 기반한 합리적으로 들리는 답변을 드리겠지만, 팀 규모, 트래픽, 실행 가능한 기간을 알지 못합니다. 먼저 스스로 결정한 후에 AI에게 그 결정을 구현하도록 요청하세요.
비즈니스 로직:
"사용자가 구독을 취소할 때 어떻게 되어야 하는가요?"
이것은 수익에 영향을 미치는 제품 결정입니다. 스스로 정의한 다음 구현하세요.
보안 감사:
"이 코드가 안전한가요?"
요청하면 AI는 안전한 코드를 작성할 수 있습니다. 기존 코드의 모든 취약점을 스스로 포착하지는 않습니다. 감사를 위해 전용 도구를 사용하세요.
읽지 않을 것이면:
AI 출력을 복사할 때 읽지 않고 있다면 그만두세요. 당신은 이해하지 못하는 코드를 배송하고 있습니다. 그것이 깨질 때 — 그리고 깨질 것입니다 — 당신은 어디를 찾아야 할지 몰라요.
전후: 실제 예시
기능 구현
전 (30초 작성, 45분 수정):
"프로젝트 목록에 검색 추가"
후에 (90초 걸리며 첫 번째 시도에서 작동):
"
src/app/(dashboard)/projects/page.tsx에 검색 기능을 추가하세요. 검색 상태가 새로고침 시 유지되도록 URL 검색 매개변수(?q=)를 사용해야 합니다. 결과를getUserProjects에서 필터링하고,name에 쿼리가 포함되어 있는 경우(대소문자 무시)를 필터링하세요. 디바운스된 입력을 사용하세요 —useDebounce을src/hooks/useDebounce.ts에서 300ms로 디바운스하세요. 새로운 의존성을 추가하지 마세요."
버그 수정
이전:
"제 양식이 제출되지 않습니다"
이후:
"
CreateProjectForm에서src/components/CreateProjectForm.tsx가 제출될 때 서버 액션을 호출하지 않습니다. React Hook Form의handleSubmit가 연결되어 있지만 액션 (createProject에서)src/actions/projects.ts은 절대로 발동하지 않는다 — 액션의 맨 위에서 console.log로 확인됨. 브라우저 오류는 없음. 아래 두 파일을 붙여넣음."
리팩토링
이전:
"이 파일을 정리하다"
이후:
"
src/lib/db.ts파일에는 세 개의 인라인 헬퍼 함수 (withRetry,paginate)가 있다"softDelete는 이제 6+ 곳에서 사용 중입니다. 이를src/lib/db-helpers.ts로 추출하고 모든 가져오기를 업데이트하세요. 함수 서명이나 동작을 변경하지 마세요 — 오직 위치만 변경하세요."
메타기술
가장 효과적인 프롬프팅을 배우는 방법은 나쁜 결과를 얻을 때 왜 그런지 질문하는 것입니다.
- 출력이 너무 모호하다 → 당신의 작업이 충분히 구체적이지 않았습니다
- AI는 건축을 발명했다 → 작업이 너무 크다, 나누었다
- AI는 잘못된 패턴을 사용했다 → 맥락이 부족했다, 관련 파일을 추가했다
- AI는 제품 결정을 했다 → 구현 대신 결정을 요청했다
- 코드가 스택과 일치하지 않는다 → 규약이
CLAUDE.md
모든 나쁜 출력은 프롬프트에 대한 피드백이다. 출력만 고치지 말고 프롬프트를 고칠 것.
AI 도구로 10배의 성과를 내는 개발자들은 다른 티어나 다른 모델을 사용하지 않습니다. 그들은 더 나은 프롬프트를 작성하기 위해 60초를 추가로 소비하고 있습니다. 그것이 바로 전체적인 우위이며, 매일매일 누적됩니다.
CLAUDE.md 템플릿과 Claude Code 워크플로우가 포함된 전체 가이드:
https://stacknotice.com/blog/ai-coding-prompts-senior-2026












