Claude Code로 API 엔드포인트 생성하는 방법: 스키마에서 테스트된 엔드포인트까지 몇 분 만에

API 엔드포인트 생성: Claude Code의 가장 빠른 승리

개발자가 Claude Code를 처음 사용할 때, 가장 즉각적인 생산성 향상을 체감하는 작업이 CRUD API 엔드포인트 생성이다. 데이터베이스 스키마가 정해져 있고, 기본적인 CRUD(Create, Read, Update, Delete) 로직이 필요한 상황은 대부분의 프로젝트에서 반복적으로 발생한다. 이 작업은 패턴이 명확하고, 코드의 구조가 예측 가능하며, 실수가 발생하면 즉시 테스트로 잡아낼 수 있다. 바로 이 특성 때문에 코드 생성 도구의 효과가 극대화된다.

수동으로 하나의 리소스에 대한 CRUD 엔드포인트를 만든다면 보통 30분에서 1시간이 걸린다. 라우트 파일 생성, 컨트롤러 로직, 입력 검증, 에러 처리, 데이터베이스 쿼리, 응답 직렬화를 모두 작성해야 한다. 리소스가 10개라면 반나절이 날아간다. 대부분의 코드가 구조적으로 비슷한데도 불구하고, 매번 처음부터 작성하거나 이전 코드를 복사해서 수정하는 방식이다.

Claude Code는 이 과정을 근본적으로 바꾼다. 데이터베이스 스키마를 보여주고 “이 스키마에 맞는 CRUD 엔드포인트를 만들어줘”라고 요청하면, 프로젝트의 기존 코드 스타일과 구조를 파악하여 일관된 코드를 생성한다. 단순한 템플릿 기반 코드 생성이 아니라, 프로젝트의 맥락을 이해한 상태에서 코드를 작성하는 것이다.

이 글에서는 Claude Code로 API 엔드포인트를 효율적으로 생성하는 전체 워크플로우를 다룬다. 단순히 “이렇게 하면 된다”는 수준을 넘어, 생성된 코드의 품질을 보장하는 리뷰 체크리스트, 테스트 자동 생성, 그리고 여러 리소스로 확장하는 전략까지 포함한다.

사전 준비: CLAUDE.md로 컨텍스트 설정

왜 CLAUDE.md가 중요한가

Claude Code는 프로젝트 루트의 CLAUDE.md 파일을 자동으로 읽어 프로젝트의 맥락을 파악한다. 이 파일이 없으면 Claude Code는 프로젝트 구조를 추측해야 하고, 추측이 틀리면 생성된 코드가 기존 코드와 어울리지 않는다.

CRUD 엔드포인트 생성을 위해 CLAUDE.md에 포함해야 할 정보는 다음과 같다.

# Project: E-Commerce API

## Tech Stack
- Runtime: Node.js 20
- Framework: Express.js with TypeScript
- Database: PostgreSQL 16 with Prisma ORM
- Validation: Zod
- Testing: Vitest
- Auth: JWT with middleware in src/middleware/auth.ts

## Directory Structure

src/ routes/ # Route definitions (one file per resource) controllers/ # Business logic services/ # Database operations via Prisma middleware/ # Auth, validation, error handling schemas/ # Zod validation schemas types/ # TypeScript type definitions utils/ # Helper functions tests/ unit/ # Unit tests integration/ # API integration tests prisma/ schema.prisma # Database schema


## Conventions
- Controller functions return { data, status } objects
- Services handle all Prisma operations
- Validation schemas are defined alongside routes
- Error responses follow: { error: string, code: string, details?: any }
- Pagination uses cursor-based pagination with `cursor` and `limit` params
- All list endpoints support filtering via query params
- Use kebab-case for URL paths, camelCase for variables

이 정도의 정보가 있으면 Claude Code는 기존 코드와 일관된 스타일로 새 엔드포인트를 생성할 수 있다.

기존 코드 참조

CLAUDE.md에 모든 것을 적을 필요는 없다. 이미 잘 작성된 엔드포인트가 하나라도 있다면, 그것을 참조점으로 활용할 수 있다.

Claude Code에게 요청할 때: "src/routes/products.ts와 같은 패턴으로 orders 리소스의 CRUD 엔드포인트를 만들어줘"

이 접근법은 CLAUDE.md가 없거나 불완전한 프로젝트에서 특히 효과적이다. Claude Code는 참조 파일의 구조, 네이밍 컨벤션, 에러 처리 방식, 응답 형식을 모두 분석하여 동일한 패턴을 적용한다.

CRUD 생성 워크플로우

단계 1: 스키마 제공

먼저 Claude Code에 데이터베이스 스키마를 제공한다. Prisma를 사용한다면 schema.prisma 파일을 직접 읽도록 하면 된다.

// Claude Code 프롬프트
"prisma/schema.prisma에서 Order 모델을 확인하고, 이 모델에 대한 완전한 CRUD API를 만들어줘.
routes, controller, service, validation schema, types를 모두 포함해줘.
기존 products 엔드포인트의 패턴을 따르되, Order의 특성(상태 관리, 결제 연동)을 반영해줘."

이 한 번의 요청으로 Claude Code는 다음 파일들을 생성한다.

  • src/routes/orders.ts — 라우트 정의
  • src/controllers/orders.controller.ts — 컨트롤러 로직
  • src/services/orders.service.ts — 데이터베이스 조작
  • src/schemas/orders.schema.ts — Zod 검증 스키마
  • src/types/orders.ts — TypeScript 타입

단계 2: 생성 결과 확인

Claude Code가 생성하는 코드의 전형적인 구조를 살펴보자. 라우트 파일을 예로 든다.

// src/routes/orders.ts
import { Router } from 'express';
import { authenticate } from '../middleware/auth';
import { validate } from '../middleware/validate';
import { OrdersController } from '../controllers/orders.controller';
import {
  createOrderSchema,
  updateOrderSchema,
  listOrdersSchema,
} from '../schemas/orders.schema';

const router = Router();
const controller = new OrdersController();

router.get('/', authenticate, validate(listOrdersSchema, 'query'), controller.list);
router.get('/:id', authenticate, controller.getById);
router.post('/', authenticate, validate(createOrderSchema), controller.create);
router.patch('/:id', authenticate, validate(updateOrderSchema), controller.update);
router.delete('/:id', authenticate, controller.delete);

export default router;

Claude Code가 기존 products 라우트의 패턴을 그대로 따르면서도, Order 리소스의 특성에 맞게 조정한 것을 확인할 수 있다. authenticate 미들웨어, validate 미들웨어의 사용 방식이 기존 코드와 일치한다.

단계 3: 비즈니스 로직 보강

기본 CRUD는 Claude Code가 잘 생성하지만, 비즈니스 로직이 복잡한 부분은 추가 지시가 필요하다. 주문의 경우 상태 전이 로직이 중요하다.

"orders.service.ts에 주문 상태 전이 로직을 추가해줘.
허용되는 상태 전이:
- pending -> confirmed, cancelled
- confirmed -> processing, cancelled
- processing -> shipped
- shipped -> delivered, returned
잘못된 상태 전이를 시도하면 InvalidStateTransitionError를 던져줘."

이런 비즈니스 규칙은 Claude Code가 자체적으로 알 수 없으므로 명시적으로 지시해야 한다. Claude Code는 지시된 규칙을 코드로 정확하게 변환하되, 에러 처리와 타입 안전성을 기존 코드 패턴에 맞춰 구현한다.

// Claude Code가 생성한 상태 전이 로직
const VALID_TRANSITIONS: Record<OrderStatus, OrderStatus[]> = {
  pending: ['confirmed', 'cancelled'],
  confirmed: ['processing', 'cancelled'],
  processing: ['shipped'],
  shipped: ['delivered', 'returned'],
  delivered: [],
  returned: [],
  cancelled: [],
};

async updateStatus(id: string, newStatus: OrderStatus): Promise<Order> {
  const order = await this.prisma.order.findUniqueOrThrow({ where: { id } });
  const allowed = VALID_TRANSITIONS[order.status];

  if (!allowed.includes(newStatus)) {
    throw new InvalidStateTransitionError(
      `Cannot transition from ${order.status} to ${newStatus}`
    );
  }

  return this.prisma.order.update({
    where: { id },
    data: { status: newStatus, updatedAt: new Date() },
  });
}

리뷰 체크리스트

Claude Code가 생성한 코드를 그대로 커밋하는 것은 위험하다. 자동 생성된 코드는 다음 항목을 반드시 검토해야 한다.

보안 검토

입력 검증이 빠짐없이 적용되었는가. 모든 사용자 입력이 Zod 스키마로 검증되는지 확인한다. 특히 ID 파라미터(URL params)도 검증 대상에 포함해야 한다.

인증과 인가가 올바르게 적용되었는가. 모든 엔드포인트에 authenticate 미들웨어가 걸려 있는지, 그리고 사용자가 자신의 리소스만 접근할 수 있도록 인가 검사가 이루어지는지 확인한다.

SQL injection이나 Prisma injection 가능성이 없는가. ORM을 사용하면 대부분 안전하지만, raw query를 사용하는 부분이 있다면 파라미터화가 되어 있는지 확인한다.

민감한 정보가 응답에 노출되지 않는가. 사용자 비밀번호 해시, 내부 ID, 결제 토큰 등이 API 응답에 포함되지 않도록 select 또는 exclude를 확인한다.

데이터 무결성 검토

트랜잭션이 필요한 곳에 적용되었는가. 주문 생성 시 재고 차감과 주문 레코드 생성이 하나의 트랜잭션으로 묶여야 한다.

동시성 문제가 처리되었는가. 두 사용자가 동시에 같은 상품의 마지막 재고를 구매하면 어떻게 되는가. optimistic locking이나 pessimistic locking이 필요한지 검토한다.

삭제 정책이 명확한가. 하드 삭제(실제 삭제)인지 소프트 삭제(deletedAt 플래그)인지, 관련 데이터의 cascade 정책은 무엇인지 확인한다.

성능 검토

N+1 쿼리 문제가 없는가. 목록 조회에서 관련 데이터를 가져올 때 include/join이 적절히 사용되었는지 확인한다.

페이지네이션이 구현되었는가. 목록 엔드포인트에 cursor 또는 offset 기반 페이지네이션이 있는지 확인한다. 전체 데이터를 한 번에 반환하는 엔드포인트는 프로덕션에서 문제를 일으킨다.

인덱스가 필요한 필드가 있는가. 필터링이나 정렬에 사용되는 컬럼에 데이터베이스 인덱스가 정의되어 있는지 확인한다.

에러 처리 검토

모든 에러 경로가 일관된 형식으로 응답하는가. 404, 400, 403, 500 등 각 상황에 맞는 HTTP 상태 코드와 에러 메시지가 반환되는지 확인한다.

예상치 못한 에러가 적절히 잡히는가. 전역 에러 핸들러로 넘어가는 것은 좋지만, 의미 있는 에러(예: 중복 키 위반)는 구체적인 메시지로 변환되어야 한다.

테스트 생성

단위 테스트

"orders.service.ts에 대한 단위 테스트를 만들어줘.
Prisma client를 모킹하고, 다음 시나리오를 포함해줘:
- 주문 생성 성공
- 필수 필드 누락 시 에러
- 존재하지 않는 주문 조회 시 에러
- 상태 전이 성공
- 잘못된 상태 전이 시 에러
- 삭제 시 소프트 삭제 동작 확인"

Claude Code는 프로젝트의 테스트 프레임워크(Vitest)를 인식하고, 기존 테스트 파일의 패턴을 따라 테스트를 생성한다.

// tests/unit/orders.service.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { OrdersService } from '../../src/services/orders.service';
import { InvalidStateTransitionError } from '../../src/errors';

describe('OrdersService', () => {
  let service: OrdersService;
  let mockPrisma: any;

  beforeEach(() => {
    mockPrisma = {
      order: {
        create: vi.fn(),
        findUnique: vi.fn(),
        findUniqueOrThrow: vi.fn(),
        findMany: vi.fn(),
        update: vi.fn(),
        count: vi.fn(),
      },
    };
    service = new OrdersService(mockPrisma);
  });

  describe('create', () => {
    it('should create an order with pending status', async () => {
      const input = {
        userId: 'user-1',
        items: [{ productId: 'prod-1', quantity: 2 }],
      };
      const expected = { id: 'order-1', status: 'pending', ...input };
      mockPrisma.order.create.mockResolvedValue(expected);

      const result = await service.create(input);

      expect(result.status).toBe('pending');
      expect(mockPrisma.order.create).toHaveBeenCalledWith({
        data: expect.objectContaining({ status: 'pending' }),
      });
    });
  });

  describe('updateStatus', () => {
    it('should allow valid state transition', async () => {
      mockPrisma.order.findUniqueOrThrow.mockResolvedValue({
        id: 'order-1',
        status: 'pending',
      });
      mockPrisma.order.update.mockResolvedValue({
        id: 'order-1',
        status: 'confirmed',
      });

      const result = await service.updateStatus('order-1', 'confirmed');
      expect(result.status).toBe('confirmed');
    });

    it('should reject invalid state transition', async () => {
      mockPrisma.order.findUniqueOrThrow.mockResolvedValue({
        id: 'order-1',
        status: 'delivered',
      });

      await expect(
        service.updateStatus('order-1', 'pending')
      ).rejects.toThrow(InvalidStateTransitionError);
    });
  });
});

통합 테스트

"orders 엔드포인트에 대한 통합 테스트를 만들어줘.
실제 테스트 데이터베이스를 사용하고, 각 테스트 전에 시드 데이터를 넣고 끝나면 정리하는 패턴으로.
인증 토큰은 테스트 유틸리티의 generateTestToken을 사용해줘."

통합 테스트는 실제 HTTP 요청을 보내고 응답을 검증한다. 데이터베이스, 미들웨어, 라우팅이 모두 올바르게 연결되었는지 확인하는 테스트다.

테스트 실행과 수정

생성된 테스트를 바로 실행한다.

"방금 생성한 테스트를 실행하고, 실패하는 테스트가 있으면 원인을 분석해서 수정해줘."

Claude Code는 테스트 실행 결과를 보고 실패 원인을 분석한 뒤, 테스트 코드나 프로덕션 코드를 수정한다. 이 반복 과정에서 실제 버그가 발견되는 경우도 흔하다.

다중 리소스 확장

패턴 확립 후 반복

첫 번째 리소스(Orders)의 CRUD가 완성되고 테스트가 통과하면, 이 패턴을 기반으로 추가 리소스를 빠르게 생성할 수 있다.

"Orders 엔드포인트와 동일한 패턴으로 다음 리소스들의 CRUD를 만들어줘:
1. Customers - 고객 관리 (이름, 이메일, 주소, 등급)
2. Products - 상품 관리 (이름, 가격, 재고, 카테고리)
3. Reviews - 리뷰 관리 (상품 ID, 고객 ID, 별점, 내용)

각 리소스별로 routes, controller, service, schema, types, 단위 테스트를 모두 생성해줘.
Reviews는 Products와 Customers에 대한 외래 키 관계를 포함해줘."

Claude Code는 첫 번째 리소스에서 확립된 패턴을 정확히 따르면서, 각 리소스의 고유한 특성을 반영한다. Products에는 재고 관리 로직이, Reviews에는 중복 리뷰 방지와 평균 별점 계산이 자동으로 포함된다.

리소스 간 관계 처리

리소스 간 관계가 있을 때는 추가 엔드포인트가 필요하다.

"다음 중첩 엔드포인트를 추가해줘:
- GET /products/:id/reviews - 특정 상품의 리뷰 목록
- GET /customers/:id/orders - 특정 고객의 주문 목록
- POST /orders/:id/items - 주문에 상품 추가

기존 라우트 파일에 추가하고, 해당 서비스에 메서드를 추가하는 방식으로."

일괄 작업

리소스가 많아지면 공통 기능을 일괄 적용할 수 있다.

"모든 리소스의 목록 엔드포인트에 다음 기능을 추가해줘:
1. 정렬: sort 쿼리 파라미터 (예: sort=createdAt:desc)
2. 필드 선택: fields 쿼리 파라미터 (예: fields=id,name,price)
3. 검색: q 쿼리 파라미터로 텍스트 검색

공통 유틸리티 함수를 만들고, 각 서비스에서 재사용하는 구조로."

이 요청 하나로 모든 목록 엔드포인트에 일관된 필터링, 정렬, 검색 기능이 추가된다. 수동으로 했다면 각 파일을 열어 하나씩 수정해야 했을 작업이다.

효율을 높이는 프롬프트 전략

구체적으로 요청한다

“API를 만들어줘”보다 “Prisma의 Order 모델에 대한 CRUD 엔드포인트를 Express.js로 만들어줘. src/routes/products.ts 패턴을 따르고, Zod로 입력 검증을 하고, cursor 기반 페이지네이션을 포함해줘”가 훨씬 좋은 결과를 낳는다.

단계를 나눈다

한 번에 모든 것을 요청하기보다, 단계를 나누는 것이 품질이 높다.

1단계: 기본 CRUD 생성 2단계: 비즈니스 로직 추가 3단계: 에러 처리 보강 4단계: 테스트 생성 5단계: 리뷰 및 수정

각 단계에서 결과를 확인하고, 문제가 있으면 바로 수정 지시를 내린다. 5단계를 한 번에 요청하면 초반의 작은 실수가 이후 단계에 누적된다.

제약 조건을 명시한다

"주의사항:
- 삭제는 소프트 삭제(deletedAt 컬럼)로 구현해줘
- 목록 조회에서 소프트 삭제된 항목은 기본적으로 제외해줘
- admin 역할만 하드 삭제를 할 수 있어
- 모든 금액은 정수(원 단위)로 처리해줘
- created_at, updated_at은 자동 관리되어야 해"

이런 제약 조건을 미리 명시하면, 나중에 수정하는 수고를 크게 줄일 수 있다.

실전에서의 주의점

Claude Code가 잘하는 것

패턴이 명확한 CRUD 로직 생성. 데이터베이스 스키마에서 타입과 검증 스키마 도출. 기존 코드 패턴을 인식하고 일관되게 적용. 테스트 케이스 시나리오 도출과 코드 생성. 에러 처리와 엣지 케이스 코드 작성.

Claude Code에 의존하면 안 되는 것

보안 요구사항의 완전성 검증. 비즈니스 로직의 정확성 판단. 성능 최적화가 필요한 복잡한 쿼리. 프로덕션 환경에서의 동시성 문제 예측. 법적 규제(개인정보보호, 결제 PCI-DSS 등) 충족 여부.

이 영역들은 개발자가 직접 검토하고 보강해야 한다. Claude Code는 도구이지 의사 결정자가 아니다. 도구가 만든 결과물을 검증하고 최종 판단하는 것은 언제나 개발자의 몫이다.

생산성 측정

Claude Code를 도입한 후 API 엔드포인트 생성 시간이 얼마나 줄었는지 측정하는 것은 의미가 있다.

일반적인 경험치를 공유하자면, 단일 리소스의 기본 CRUD는 수동 1시간에서 Claude Code 10분으로 줄어든다. 테스트 작성까지 포함하면 수동 2시간에서 Claude Code 20분이다. 10개 리소스 전체를 만드는 경우, 수동으로는 이틀이 걸릴 작업이 Claude Code로는 반나절이면 끝난다.

다만 이 수치에는 리뷰 시간이 포함되어 있지 않다. 생성된 코드를 꼼꼼히 리뷰하는 데 추가 시간이 들지만, 그래도 총 시간은 수동 작성보다 크게 단축된다.

마무리

Claude Code로 API 엔드포인트를 생성하는 워크플로우를 정리하면 다음과 같다.

CLAUDE.md에 프로젝트 컨텍스트를 충실히 기술하는 것이 성공의 전제 조건이다. 이 파일 없이는 Claude Code가 프로젝트의 관습을 추측해야 하고, 추측이 틀리면 생성된 코드를 처음부터 다시 작성하게 된다.

첫 번째 리소스에 시간을 투자한다. 패턴을 확립하는 단계이므로, 코드 구조, 에러 처리, 검증, 테스트를 완벽하게 만든다. 이후 리소스는 이 패턴을 복제하므로 훨씬 빠르다.

생성 후 반드시 리뷰한다. 보안, 데이터 무결성, 성능, 에러 처리를 체크리스트로 확인한다. 자동 생성된 코드를 맹목적으로 신뢰하는 것은 기술 부채를 쌓는 지름길이다.

테스트 생성까지 Claude Code에 맡긴다. 테스트 작성은 개발자가 가장 미루기 쉬운 작업이지만, Claude Code가 테스트를 생성하면 이 장벽이 사라진다. 테스트가 있으면 이후 리팩토링도 안전하게 진행할 수 있다.

비즈니스 로직은 명시적으로 지시한다. CRUD의 기계적 부분은 Claude Code가 자동으로 처리하지만, 상태 전이, 접근 권한, 금액 계산 같은 비즈니스 규칙은 개발자가 알려줘야 한다. 이 구분을 명확히 할수록 생성된 코드의 품질이 높아진다.

다른 도구 둘러보기

ChatGPT 사례 연구: 로펌이 계약서 검토를 자동화해 연간 2,000시간을 절감한 방법 사례 ChatGPT 사례 연구: 매출 0원 스타트업이 AI로 전체 콘텐츠 마케팅 엔진을 구축한 방법 사례 ChatGPT로 회의 준비하는 방법: 회의실에서 가장 준비된 사람이 되는 리서치 브리핑 방법 Claude로 엔터프라이즈 RAG 시스템 구축하는 방법: 직원 질문에 답하는 지식 기반 방법 Claude API로 콘텐츠 모더레이션 구축하는 방법: 확장 가능한 자동 안전 시스템 방법 Claude API 함수 호출 사용 방법: AI 에이전트 구축을 위한 Tool Use 완전 가이드 방법 ElevenLabs로 고객 서비스 IVR 구축하는 방법: 사람처럼 들리는 AI 음성 자동화 방법 ElevenLabs로 제품 데모 보이스오버 만드는 방법: SaaS 데모를 위한 전문 내레이션 방법 Gemini 멀티모달 프롬프팅 모범 사례: 이미지, 영상, 문서 분석으로 결과 얻기 모범사례 Gemini 사례 연구: 프로덕트 팀이 Deep Research로 200개 사용자 인터뷰를 3일 만에 종합한 방법 사례 Gemini 사례 연구: 부동산 회사가 Deep Research로 15개 도시 시장 분석을 수행한 방법 사례 Gemini로 코드 리뷰와 리팩토링하는 방법: AI 기반 코드 품질 개선 방법 Genspark Sparkpage 정리 모범 사례: 개인 지식 관리 시스템 구축 모범사례 Genspark으로 경쟁사 가격 분석하는 방법: AI 기반 가격 인텔리전스 방법 Grok 실시간 뉴스 분석 및 팩트체킹 모범 사례 모범사례 Grok 학술 연구 및 문헌 탐색 모범 사례: X/Twitter를 활용한 학술 인텔리전스 모범사례 Grok 콘텐츠 전략 모범 사례: 트렌딩 토픽을 정점 전에 파악하고 수요를 선점하는 콘텐츠 만들기 모범사례 Grok 사례 연구: DTC 뷰티 브랜드가 실시간 소셜 리스닝으로 제품 출시를 구한 이야기 사례 Grok 사례 연구: 제약회사가 신약 출시 중 환자 센티먼트를 추적해 FDA보다 48시간 먼저 안전 신호를 감지한 방법 사례 Grok 사례 연구: 헤지펀드가 X/Twitter 센티먼트를 대안 데이터로 활용해 연 5.9% 초과수익을 달성한 방법 사례