Featured Post

LLM 코딩할 때 OpenAPI 명세를 이렇게 넣었더니 토큰도 시간도 줄었습니다

OpenAPI - API 명세 관련 이미지

요즘 개발자들 사이에서 AI 바이브 코딩이라는 말, 참 많이 들리죠. 저도 처음엔 살짝 웃었어요. “코딩을 바이브로 한다고?” 싶었거든요. 그런데 막상 GitHub Copilot, ChatGPT, Claude, Cline 같은 도구를 업무에 계속 붙여서 써보니까, 이 말이 아주 허황된 얘기는 아니더라고요. 다만 한 가지는 확실했습니다. 그냥 감으로 AI에게 일을 던지면 결과도 감으로 나오고, OpenAPI 명세 같은 설계도를 잘 쥐여주면 꽤 믿을 만한 동료처럼 움직인다는 거예요.

제가 이 글을 쓰게 된 것도 사실 별거 아닙니다. 최근에 고객사 결제 모듈을 손보면서 LLM에게 API 코드를 생성시키다가, 같은 일을 시켜도 어떤 날은 기가 막히게 잘하고 어떤 날은 엉뚱한 필드를 막 만들어내는 걸 봤거든요. 뭐랄까, 말귀를 못 알아듣는 신입에게 “대충 만들어줘”라고 해놓고 제가 화내는 느낌이랄까요. 그때 다시 들여다본 게 OpenAPI 명세를 LLM 프롬프트에 어떻게 넣을 것인가였습니다.

20년 가까이 개발을 하다 보면 새로운 도구가 나올 때마다 마음이 두 갈래로 갈립니다. “이거 또 잠깐 유행하다 사라지겠지” 싶은 마음도 있고, “그래도 써먹을 수 있으면 빨리 내 손에 붙여야지” 싶은 마음도 있어요. LLM 코딩은 후자에 가까웠습니다. 다만 조건이 있어요. AI에게 모든 걸 맡기는 게 아니라, AI가 헛소리하지 못하게 일하는 울타리를 만들어주는 것. 저는 그 울타리로 OpenAPI를 꽤 강하게 추천합니다.

LLM 코딩에서 OpenAPI 명세가 은근히 중요한 이유

처음에는 저도 그냥 이렇게 물어봤습니다. “회원 가입 REST API 만들어줘.” 아주 단순하죠. 그랬더니 AI가 정말 열심히 만들어줍니다. 문제는 너무 열심히 자기 마음대로 만든다는 거예요. endpoint 이름도 제각각이고, request body에 들어가는 필드명도 프로젝트 DB 컬럼이랑 살짝씩 어긋나고, response 구조도 팀에서 쓰는 규칙과 달랐습니다.

예를 들면 저희 쪽에서는 사용자 식별자를 userId로 쓰고 있었는데, AI는 어느 순간 id, memberId, accountId를 섞어서 쓰더라고요. 사람이 보면 “아, 같은 뜻이겠네” 하고 넘어갈 수 있지만, API 서버에서는 그런 친절함이 없습니다. 필드 하나 틀리면 프론트에서 바로 터지고, 테스트 코드에서도 줄줄이 빨간불이 들어오죠.

그때 예전에 팀에서 만들어둔 Swagger, 그러니까 OpenAPI yaml 파일이 떠올랐어요. 이미 우리가 합의한 endpoint, schema, response 구조가 다 들어 있는 문서잖아요. “이걸 AI에게 제대로 보여주면 훨씬 낫지 않을까?” 싶었습니다. 실제로 해보니 맞았습니다. 다만 통째로 넣으면 또 다른 문제가 생겨요.

OpenAPI 명세 파일이 작은 프로젝트에서는 몇백 줄이지만, 조금만 규모가 있어도 몇천 줄은 금방 넘어갑니다. 그걸 프롬프트에 그대로 붙여 넣으면 토큰이 미친 듯이 늘어나고, 모델도 정작 중요한 부분을 놓치는 경우가 생깁니다. 여행 갈 때 짐을 다 챙긴다고 이민 가방을 들고 가면 오히려 발목 잡히는 것과 비슷해요. 필요한 것만 잘 챙겨야 합니다.

OpenAPI 명세를 통째로 넣지 말고, 필요한 부분만 잘라서 보여주기

제가 요즘 가장 많이 쓰는 방식은 간단합니다. 전체 OpenAPI 파일을 복사해서 붙이는 대신, 지금 만들 기능과 직접 관련 있는 pathsschema만 잘라서 보여줍니다. 이게 생각보다 효과가 좋아요. AI에게 “우리 회사 전체 조직도를 다 외워”라고 하는 대신, “이번에 같이 일할 사람은 이 세 명이야”라고 알려주는 느낌입니다.

endpoint 목록만 먼저 보여줘도 방향이 잡힙니다

회원 기능을 만들고 있다면 저는 보통 이런 식으로 먼저 줍니다. 아주 짧게요. 대신 이름은 정확하게 맞춥니다.

POST /api/users : 회원 가입
GET /api/users/{userId} : 회원 단건 조회
PUT /api/users/{userId} : 회원 정보 수정
DELETE /api/users/{userId} : 회원 삭제

이 정도만 줘도 LLM이 endpoint 네이밍을 제멋대로 바꾸는 일이 확 줄어듭니다. 예전에는 /api/member, /api/account 같은 걸 슬쩍 만들어내곤 했는데, 명세 일부를 붙여주니 훨씬 얌전해졌어요. 솔직히 이 부분에서 체감이 꽤 컸습니다.

제가 대략 측정해보니, 전체 OpenAPI 파일을 넣었을 때보다 이렇게 endpoint 목록만 넣었을 때 프롬프트 토큰은 5분의 1 수준으로 줄었습니다. 그런데 결과 품질은 오히려 좋아졌어요. 이유는 단순합니다. AI가 봐야 할 게 적고, 중요한 정보만 눈앞에 있기 때문이죠.

request와 response는 대표 샘플 하나만 있어도 꽤 충분합니다

endpoint 목록만으로 부족할 때가 있습니다. 특히 request body나 response 형식이 팀마다 다를 때 그래요. 어떤 팀은 응답을 바로 객체로 주고, 어떤 팀은 data로 감싸고, 어떤 팀은 result, message, code 같은 공통 wrapper를 쓰잖아요. 이럴 때는 대표 샘플 하나를 같이 붙입니다.

POST /api/users

Request Body
{
  "name": "홍길동",
  "email": "hong@example.com",
  "password": "plainTextPassword"
}

Response
{
  "data": {
    "userId": 123,
    "name": "홍길동",
    "email": "hong@example.com"
  },
  "message": "회원 가입이 완료되었습니다"
}

이렇게 하나만 보여줘도 AI가 “아, 이 프로젝트는 response를 data로 감싸는구나” 하고 눈치를 챕니다. 물론 100%는 아니에요. 그래도 아무것도 안 줬을 때와 비교하면 결과가 훨씬 일관됩니다. 저는 이걸 보면서, AI도 결국 좋은 예시를 먹고 자라는구나 싶었습니다.

프롬프트에는 반드시 “명세에 없는 필드는 만들지 말라”고 못 박아두기

여기서 은근히 중요한 게 있습니다. OpenAPI 명세를 넣었다고 해서 AI가 항상 그걸 철석같이 지키지는 않습니다. 가끔은 친절이 과해서 문제예요. 예를 들어 회원 가입 API를 만들라고 했더니, 명세에 없는 role, status, createdAt 같은 필드를 알아서 추가합니다. “이 정도는 필요하지 않을까요?”라는 마음으로 넣는 것 같은데, 개발 현장에서는 그게 사고의 씨앗이 되죠.

그래서 저는 프롬프트 끝에 거의 습관처럼 이 문장을 붙입니다.

위 OpenAPI 명세에 정의된 endpoint, request field, response field만 사용해주세요. 명세에 없는 custom field는 추가하지 마세요.

조금 차갑게 들릴 수도 있는데, 이 문장이 있고 없고의 차이가 큽니다. 특히 backend entity, DTO, controller, service, test code를 한 번에 생성시킬 때는 더 그렇습니다. 명세에 없는 필드가 한 군데 들어가면, 그걸 따라 service, mapper, test fixture까지 줄줄이 오염되거든요. 나중에 지우는 게 더 귀찮습니다.

제가 자주 쓰는 프롬프트는 이런 느낌입니다.

회원 가입 API를 구현해주세요. endpoint는 POST /api/users 입니다. request body는 UserCreateRequest schema를 따르고, response는 UserResponse schema를 따릅니다. Controller, Service, DTO, 단위 테스트를 생성해주세요. OpenAPI 명세에 없는 필드는 절대 추가하지 말고, 기존 프로젝트의 response wrapper 형식을 유지해주세요.

이렇게 쓰면 결과가 꽤 안정적으로 나옵니다. 특히 “절대 추가하지 말고”라는 표현이 의외로 잘 먹힙니다. 사람한테 말할 땐 너무 강한 표현일 수 있는데, AI에게는 이 정도로 선을 그어줘야 덜 헤맵니다.

IDE 설정 파일에 OpenAPI 위치를 적어두면 매번 덜 설명해도 됩니다

요즘은 Cursor, Cline, Continue.dev 같은 도구를 쓰는 분들이 많죠. 저도 프로젝트 성격에 따라 이것저것 써보고 있는데, 공통적으로 느낀 게 있습니다. 매번 프롬프트에 같은 설명을 반복하는 건 정말 아깝습니다. 사람도 같은 말 계속하면 지치잖아요. 토큰도 지치고, 저도 지칩니다.

그래서 프로젝트 루트에 규칙 파일을 만들어둡니다. 예를 들어 .clinerules.cursorrules 같은 파일에 “이 프로젝트의 API 명세는 어디에 있고, 코드를 만들 때 뭘 지켜야 하는지”를 적어두는 식입니다.

Project rule

REST API specification file:
openapi/users-api.yaml

Code generation rule:
Follow the paths and schemas defined in the OpenAPI specification.
Do not create request or response fields that are not defined in the specification.
Keep the existing response wrapper format.
Use the current package structure when adding controller, service, dto, and test files.

이렇게 해두면 프롬프트가 훨씬 짧아집니다. “회원 수정 API 만들어줘”라고만 해도, AI assistant가 어느 정도 프로젝트 규칙을 참고해서 움직입니다. 물론 도구마다 파일을 읽는 방식이나 컨텍스트 관리 방식은 다릅니다. 그래서 처음 한두 번은 꼭 결과를 확인해야 해요. 그래도 매번 긴 설명을 붙이는 것보다는 훨씬 낫습니다.

저는 이 방식을 팀 프로젝트에 넣고 나서, 반복 설명이 꽤 줄었습니다. 특히 DTO 네이밍 규칙이나 response wrapper 규칙을 매번 말하지 않아도 되는 게 좋았어요. 별것 아닌 것 같지만 하루 종일 API 만지는 날에는 이런 작은 절약이 꽤 큽니다. 커피 한 잔 마실 정신이 생기거든요.

명세를 넣었을 때와 안 넣었을 때, 실제로 얼마나 달랐을까

제가 최근에 결제 모듈 리팩토링을 하면서 간단히 비교해본 적이 있습니다. 동일하게 “POST /api/payments endpoint를 생성해줘”라고 요청했을 때, OpenAPI 요약 명세를 넣은 경우와 넣지 않은 경우가 꽤 달랐습니다. 엄밀한 실험 논문 같은 건 아니고요. 그냥 현장에서 제가 체감하고 기록한 데이터라고 보면 됩니다.

비교 항목 OpenAPI 명세 없이 요청 요약 명세를 함께 제공
필드명 일치도 약 60%. paymentId 대신 id를 쓰거나, price 같은 임의 필드가 섞임 약 95%. amount, currency, paymentMethod 등 명세와 거의 일치
프롬프트 토큰 약 400 tokens 약 800 tokens
수동 수정 3회에서 4회 정도 손으로 고침 거의 없음. validation 정도만 다듬음
개발 시간 대략 20분 대략 8분
스트레스 생각보다 큼. “왜 또 네 맘대로야” 하는 순간이 생김 낮음. 검토에 집중할 수 있음

재미있는 건, 요약 명세를 넣으면 당장 프롬프트 토큰은 늘어난다는 점입니다. 그런데 전체 작업 시간을 보면 오히려 줄어요. 토큰을 조금 더 쓰더라도 수정 시간을 줄이는 게 더 이득인 경우가 많았습니다. 이건 개발자라면 다들 공감하실 거예요. 코드 한 번 잘못 생성되면, 그걸 다시 읽고 의도를 파악하고 고치는 시간이 은근히 잡아먹힙니다.

그래서 저는 토큰 절약을 무조건 “짧게 묻기”라고 생각하지 않습니다. 진짜 토큰 절약은, AI가 헛발질하지 않도록 필요한 정보를 적당히 주는 거라고 봐요. 조금 더 말하고, 훨씬 덜 고치는 방식. 이게 제 기준에서는 더 실용적입니다.

OpenAPI 파일을 AI가 직접 참고하게 만드는 방식도 써볼 만합니다

프롬프트에 명세 일부를 붙이는 방식도 좋지만, 프로젝트가 커지면 이것도 번거롭습니다. 그래서 한 단계 더 가면 AI assistant가 직접 파일을 읽게 만드는 방식이 있습니다. 예를 들어 docs/api-spec.yaml 또는 openapi/payment-api.yaml 같은 위치에 명세를 두고, 프롬프트에서 “이 파일을 참고해서 구현해줘”라고 말하는 식이죠.

다만 여기에는 조건이 있습니다. AI 도구가 실제로 파일을 읽을 수 있는 환경이어야 합니다. 단순 웹 채팅창에서는 로컬 파일을 마음대로 읽을 수 없고, IDE 기반 assistant나 MCP 환경처럼 파일 접근이 가능한 구조가 필요합니다.

    • 작은 작업은 관련 endpoint와 schema만 프롬프트에 직접 붙이는 방식이 빠릅니다.
    • 반복 작업은 규칙 파일에 OpenAPI 위치와 코딩 규칙을 적어두는 편이 편합니다.
    • 큰 프로젝트는 MCP 또는 문서 검색 구조를 붙여서 필요한 명세만 가져오게 만드는 방식이 좋습니다.

저희 부서에서도 최근에 MCP 서버를 살짝 붙여봤습니다. OpenAPI 명세와 내부 개발 문서를 연결해두고, AI assistant가 필요할 때 해당 문서를 참조하도록 만든 거죠. 처음 세팅할 때는 조금 귀찮습니다. 이건 인정해야 해요. 그런데 한 번 잡아두면 명세가 업데이트됐을 때 반영이 편하고, 프롬프트마다 긴 내용을 복붙하지 않아도 됩니다.

개인 프로젝트라면 여기까지 안 가도 됩니다. 괜히 도구만 늘리면 피곤해요. 하지만 팀 단위로 API를 계속 만들고, 명세가 자주 바뀌고, 여러 사람이 AI assistant를 같이 쓴다면 한 번쯤 고민해볼 만합니다. 특히 프론트엔드와 백엔드가 OpenAPI 하나를 기준으로 움직이는 팀이라면 효과가 더 큽니다.

제가 실제로 쓰는 OpenAPI 기반 프롬프트 패턴

말로만 하면 조금 뜬구름 같으니, 제가 실제로 자주 쓰는 패턴을 하나 적어볼게요. 프로젝트마다 바꾸긴 하지만 뼈대는 거의 비슷합니다.

작업 목표:
POST /api/payments API를 구현해주세요.

참고할 OpenAPI 요약:
POST /api/payments : 결제 요청 생성

Request Body:
{
  "orderId": 1001,
  "amount": 30000,
  "currency": "KRW",
  "paymentMethod": "CARD"
}

Response:
{
  "data": {
    "paymentId": 501,
    "orderId": 1001,
    "status": "READY"
  },
  "message": "결제 요청이 생성되었습니다"
}

생성할 파일:
PaymentController
PaymentService
PaymentRequest
PaymentResponse
PaymentControllerTest

지켜야 할 규칙:
OpenAPI 명세에 없는 field는 추가하지 마세요.
기존 프로젝트의 package 구조를 유지해주세요.
Controller에는 business logic을 넣지 마세요.
Validation annotation을 request DTO에 추가해주세요.
테스트는 성공 케이스와 validation 실패 케이스를 포함해주세요.

이 정도면 너무 길지도 않고, AI가 일을 시작하기에 필요한 정보는 거의 들어갑니다. 특히 “생성할 파일”을 명시하는 게 중요합니다. 안 그러면 AI가 어떤 날은 controller만 만들고, 어떤 날은 service까지 만들고, 또 어떤 날은 repository를 새로 만들어버립니다. 저는 이런 변덕을 줄이고 싶어서 산출물을 딱 적어줍니다.

그리고 “Controller에는 business logic을 넣지 마세요” 같은 문장도 자주 넣습니다. LLM이 간단한 예제를 만들 때는 controller 안에 모든 로직을 몰아넣는 경우가 있거든요. 데모 코드라면 상관없지만, 회사 프로젝트에서는 나중에 다 빚으로 돌아옵니다.

OpenAPI 명세를 LLM에 줄 때 제가 보는 체크리스트

아래는 제가 실제로 작업 전에 훑어보는 체크리스트입니다. 거창한 건 아니고, 오래 삽질하다 보니 손에 붙은 것들이에요.

    • endpoint 경로가 정확한가 확인합니다. /api/user/api/users는 완전히 다른 결과를 만듭니다.
    • path variable 이름을 맞춥니다. {id}인지 {userId}인지 꼭 봅니다.
    • request body 샘플을 하나 넣습니다. 타입만 적는 것보다 실제 값 예시가 더 잘 먹힙니다.
    • response wrapper 형식을 보여줍니다. 이걸 안 보여주면 AI가 자기 스타일대로 응답을 만듭니다.
    • 명세에 없는 필드 금지 문장을 넣습니다. 정말 자주 필요합니다.
    • 생성할 파일 목록을 적습니다. 결과물 범위가 흔들리지 않습니다.
    • 테스트 조건을 같이 줍니다. 성공 케이스만 만들게 두면 나중에 다시 시켜야 합니다.

이 체크리스트를 쓰기 전에는 AI에게 한 번 시키고, 다시 고치라고 하고, 또 테스트 추가하라고 하고, 이름 바꾸라고 하는 일이 많았습니다. 지금도 물론 한 번에 완벽하진 않아요. 그래도 대화 횟수가 줄고, 제가 검토해야 할 포인트가 명확해졌습니다.

OpenAPI 명세가 있어도 사람의 판단은 여전히 필요합니다

여기서 너무 환상을 가지면 안 됩니다. OpenAPI 명세를 넣었다고 AI가 갑자기 시니어 개발자가 되는 건 아니에요. 보안 처리, 트랜잭션 범위, 장애 상황, idempotency 같은 건 여전히 사람이 봐야 합니다. 특히 결제나 인증 쪽은 더 조심해야 하고요.

제가 한 번은 결제 요청 API를 만들게 했는데, AI가 꽤 그럴듯한 코드를 뽑아줬습니다. 그런데 자세히 보니 같은 orderId로 중복 요청이 들어왔을 때 처리 정책이 없더라고요. OpenAPI 명세에는 request와 response만 있었고, 비즈니스 정책은 따로 문서에 있었거든요. AI는 자기가 본 것 안에서만 최선을 다한 겁니다. 그러니 중요한 정책은 프롬프트에 따로 넣어줘야 합니다.

제 기준에서는 OpenAPI가 API의 모양을 잡아주고, 비즈니스 규칙 문서가 행동 방식을 잡아줍니다. 둘 중 하나만 있으면 반쪽짜리예요.

이 부분은 20년 전이나 지금이나 크게 다르지 않습니다. 좋은 설계 문서가 있으면 개발이 편하고, 없으면 말로 때우다가 언젠가 꼬입니다. AI 시대가 됐다고 해서 이 기본기가 사라지진 않더라고요. 오히려 더 중요해진 느낌입니다. AI는 문서를 정말 빨리 소비하니까요.

이 방식이 특히 잘 맞는 개발자들

이 글을 읽는 분이 RESTful API 기반 백엔드 개발자라면 바로 써먹을 수 있을 겁니다. 특히 Spring Boot, NestJS, Express, FastAPI 같은 프레임워크로 API 서버를 만들고 있다면 효과가 빨리 느껴질 거예요. 프론트엔드와 API 계약을 맞추는 일이 잦은 팀에도 잘 맞습니다.

LLM 코딩을 이제 막 업무에 붙여보려는 분들에게도 좋습니다. 처음부터 거창하게 MCP를 붙이고 자동화 파이프라인을 만들 필요는 없어요. 작은 OpenAPI 조각 하나를 프롬프트에 넣는 것부터 시작하면 됩니다. 정말 그 정도로도 결과가 달라집니다.

개인적으로는 이런 분들께 특히 추천하고 싶습니다.

    • AI에게 코드를 시키면 매번 필드명이 달라져서 짜증났던 분
    • OpenAPI 명세는 있는데 문서용으로만 묵혀두고 있던 팀
    • 토큰 비용 때문에 프롬프트를 너무 짧게 쓰다가 결과 품질이 떨어졌던 분
    • API controller, DTO, test code를 반복해서 만드는 백엔드 개발자
    • 프론트엔드와 백엔드 사이의 API 계약을 안정적으로 맞추고 싶은 팀

제가 지금 갖고 있는 생각

AI 코딩 도구를 쓰다 보면 가끔 묘한 기분이 듭니다. 내가 코드를 짜는 건지, 코드를 짜는 동료를 옆에서 코칭하는 건지 헷갈릴 때가 있어요. 그런데 그럴수록 더 분명해지는 게 있습니다. 좋은 결과를 얻으려면 좋은 지시가 필요하고, 좋은 지시는 좋은 설계에서 나온다는 겁니다.

OpenAPI 명세는 예전부터 있던 익숙한 도구입니다. 새롭지도 않고, 화려하지도 않아요. 그런데 LLM 코딩 시대에 와서 다시 빛을 보는 느낌입니다. AI에게 “대충 만들어줘”라고 말하는 대신, “우리는 이런 계약으로 일하고 있어. 이 안에서 만들어줘”라고 말할 수 있게 해주니까요.

저는 요즘 새로운 API를 만들기 전에 OpenAPI yaml을 먼저 한 줄이라도 적습니다. 완벽한 문서가 아니어도 괜찮습니다. endpoint, request, response 정도만 있어도 AI가 훨씬 덜 헤맵니다. 그리고 저도 덜 헤맵니다. 사실 이게 제일 중요하죠. 도구는 결국 내가 편하려고 쓰는 거니까요.

LLM을 개발 업무에 쓰고 있는데 결과가 들쭉날쭉해서 답답했다면, 오늘은 거창한 프롬프트 기법보다 OpenAPI 명세를 작게 잘라서 프롬프트에 넣는 방식부터 한번 해보시면 좋겠습니다. 토큰도 아끼고, 수정 시간도 줄고, 무엇보다 AI와 일하는 감각이 조금 달라질 거예요. “아, 얘도 설계도를 줘야 일을 잘하는구나” 하고요.

이 글은 AI 코딩을 실무에 붙여보고 싶은 백엔드 개발자, OpenAPI 문서를 더 실용적으로 쓰고 싶은 팀 리더, 그리고 토큰 비용과 코드 품질 사이에서 매번 고민하는 분들이 읽으면 특히 도움이 될 겁니다. 제 경험상, AI 시대에도 오래된 개발 습관 중 좋은 것들은 살아남습니다. 명세를 먼저 잡고 코드를 짜는 습관, 그건 아직도 꽤 단단한 무기입니다.

댓글