주식 매도 API 설계

예썰·2026년 5월 19일

주식 매도 API 설계를 앞서 요구사항은 다음과 같다.

  • 사용자는 자신이 보유한 주식을 시장가로 매도할 수 있습니다.
  • 최대한 실제 금융 환경을 충분히 고려하여 api를 설계하세요.

AI를 활용하여 프롬프트를 작성하고 API를 해결해볼 것이다.

AI에게는 다음과 같은 상황을 고려하여 물어보았다.

  • 주식을 매도를 할때는 동시성 문제가 발생을 할 수 있다고 생각을 하였다.

  • 또한 매도를 할때 매도를 하려는 수량이 원래 가지고 있던 수량보다 많은 양을 택했을때도 문제가 생길 수 있다.

  • 그리고 장외 시간인 경우 등 매도 자체가 불가능한 상황이 있을 수가 있다.

    그래서 다음과 같이 API를 만들기 앞서 평소에 쓰던 프롬프트 설계를 사용하여 프롬프트를 작성후 지피티를 활용하여 작성을 하였다.


> 너는 “증권/금융 백엔드 시니어(API 설계 + 동시성 + 정합성 + 운영 전문가)”다.

이후 API가 다뤄야 할 도메인 상황을 설명했다.

> 사용자는 본인이 보유한 주식을 시장가로 매도한다.  
> 주문은 즉시 체결되지 않을 수 있으며, 주문 접수와 체결은 분리될 수 있다.  
> 중복 매도, 중복 잔고 감소 방지, 감사/추적 가능성, 실패 케이스를 충분히 고려해야 한다.

또한 결과물이 과제 요구사항에 맞게 나오도록 필수 항목을 지정했다.

- URL
- Method
- Request
- Response
- 상태코드
- 정책

특히 금융 API에서는 정상 응답보다 실패 상황과 정합성 처리가 중요하다고 판단해 다음 내용도 함께 요청했다.

- 보유 수량 부족
- 중복 요청
- 다중 탭 요청
- 네트워크 재시도
- 멱등성 처리
- 동시성 제어
- 부분 체결
- 거래정지 및 장외 시간
- 장애 상황 대응

처음에는 AI가 상태조회 API, 취소 API, 체결내역 조회 API까지 함께 제안했다.  
하지만 과제의 핵심은 “주식 매도 API 하나를 설계하는 것”이었기 때문에, 이후 프롬프트를 수정해 시장가 매도 주문 접수 API 하나에 집중하도록 다시 요청했다.

최종적으로는 다음과 같은 방향으로 프롬프트를 보완했다.

> 주식 매도 API는 하나만 설계한다.  
> 단, 실제 금융 환경을 고려해 주문 접수와 체결은 분리해서 설명한다.  
> 필수 항목인 URL, Method, Request, Response, 상태코드, 정책이 반드시 포함되도록 한다.

이 과정을 통해 AI의 초안을 그대로 사용하지 않고, 과제 조건에 맞게 범위를 조정하고 필요한 내용을 선별했다.

URL

/api/v1/accounts/{accountId}/orders/sell/market

Method

POST

Request

Authorization: Bearer {accessToken}
Idempotency-Key: 7b8e4c1a-5c9d-4a2b-91fd-11b7f0d2a901
Content-Type: application/json
  • Authorization: 로그인 사용자를 확인하기 위한 JWT
  • Idempotency-Key: 중복 매도 요청을 막기 위한 키
  • Content-Type: application/json

Body

{

  "instrumentId": "005930",  //종목 식별자
  "quantity": 10,  //매도 수량
  "orderType": "MARKET",  //주문 유형
  "side": "SELL"  //매도주문

}

Request Validation

검증 항목검증 내용
로그인 여부JWT가 유효한 사용자인지 확인한다.
계좌 소유 여부요청한 accountId가 로그인 사용자의 계좌인지 확인한다.
종목 검증존재하는 종목인지, 현재 거래 가능한 종목인지 확인한다.
주문 유형 검증orderType이 시장가 주문을 의미하는 MARKET인지 확인한다.
매도 방향 검증side가 매도 주문을 의미하는 SELL인지 확인한다.
수량 검증quantity가 1 이상인 정수인지 확인한다.
보유 수량 검증사용자가 해당 종목을 충분히 보유하고 있는지 확인한다.
매도 가능 수량 검증이미 다른 매도 주문에 예약된 수량을 제외하고도 매도 가능한 수량이 충분한지 확인한다.
거래 시간 검증현재 시간이 시장가 매도 주문을 접수할 수 있는 시간인지 확인한다.
거래 제한 검증거래정지, 서킷브레이커, 계좌 제한 등으로 주문이 제한되는 상황인지 확인한다.
멱등성 검증같은 Idempotency-Key로 이미 처리된 요청이 있는지 확인한다.

Response

성공 응답

HTTP Status

201 Created

Response Body

{
"orderId": "ord-20260519-000001",
"accountId": "acc-10001",
"instrumentId": "005930",
"side": "SELL",
"orderType": "MARKET",
"requestedQuantity": 10,
"filledQuantity": 0,
"remainingQuantity": 10,
"status": "ACCEPTED",
"message": "시장가 매도 주문이 접수되었습니다.",
"riskNotice": "시장가 주문은 시장 상황에 따라 예상 가격과 실제 체결 가격이 달라질 수 있으며, 부분 체결 또는 미체결될 수 있습니다.",
"createdAt": "2026-05-19T16:20:00+09:00",
"traceId": "trc-01HX9ZK8W3Q2"
}

실패 응답

실패 응답은 다음과 같이 반환한다.

{
  "timestamp": "2026-05-19T16:20:00+09:00",
  "errorCode": "HOLDING_001",
  "message": "매도 가능 수량이 부족합니다.",
  "reason": "보유 수량 10주 중 7주가 이미 다른 매도 주문에 예약되어 있어 현재 매도 가능 수량은 3주입니다.",
  "clientAction": "주문 수량을 줄이거나 기존 미체결 매도 주문을 취소한 뒤 다시 시도하세요.",
  "retryable": false,
  "traceId": "trc-01HX9ZK8W3Q2"
}

상태코드

성공 상태코드

HTTP 상태코드 의미 사용 상황
201 Created 주문 생성 성공 시장가 매도 주문이 정상 접수됨
200 OK 기존 요청 결과 반환 같은 Idempotency-Key로 동일 요청이 재전송되어 기존 주문 결과를 반환

실패 상태코드

HTTP 상태코드 에러코드 메시지 발생 상황 클라이언트 행동
400VALID_001주문 수량은 1주 이상이어야 합니다.수량이 0 또는 음수수량 수정
400VALID_002주문 수량 형식이 올바르지 않습니다.수량이 소수 또는 문자열정수로 수정
400VALID_003주문 유형이 올바르지 않습니다.orderTypeMARKET이 아님요청값 수정
400VALID_004매도 주문만 요청할 수 있습니다.sideSELL이 아님요청값 수정
400VALID_005종목 코드 형식이 올바르지 않습니다.종목 코드 형식 오류종목 코드 수정
400IDEMPOTENCY_001Idempotency-Key가 필요합니다.멱등성 키 누락키를 포함해 재요청
400IDEMPOTENCY_002Idempotency-Key 형식이 올바르지 않습니다.UUID 형식이 아님UUID 형식으로 수정
401AUTH_001로그인이 필요합니다.JWT 없음로그인 후 재요청
401AUTH_002인증 토큰이 만료되었습니다.JWT 만료토큰 재발급 후 재요청
403AUTH_003계좌 접근 권한이 없습니다.본인 계좌가 아님올바른 계좌 선택
403ACCOUNT_001거래가 제한된 계좌입니다.휴면, 압류, 사고 등록 계좌고객센터 문의
404ACCOUNT_404계좌를 찾을 수 없습니다.존재하지 않는 계좌 ID계좌 ID 확인
404STOCK_404종목을 찾을 수 없습니다.존재하지 않는 종목종목 재검색
409HOLDING_001매도 가능 수량이 부족합니다.보유 수량 부족수량 줄이기
409HOLDING_002이미 다른 매도 주문에 예약된 수량이 있습니다.미체결 매도 주문 존재기존 주문 확인
409ORDER_001동일 종목의 매도 주문이 처리 중입니다.동일 계좌/종목 동시 매도 제한기존 주문 상태 확인
409IDEMPOTENCY_003같은 Idempotency-Key로 다른 요청이 전송되었습니다.키 재사용 + 본문 다름새 키로 재요청
409CONFLICT_001동시에 처리 중인 주문이 있어 요청을 완료할 수 없습니다.다중 탭/동시 요청잠시 후 재시도
422MARKET_001현재는 시장가 주문이 불가능한 시간입니다.장외 시간장중 다시 요청
422MARKET_002해당 종목은 거래정지 상태입니다.거래정지 종목거래 재개 후 요청
422MARKET_003서킷브레이커로 주문이 제한되었습니다.시장 급변으로 거래 제한제한 해제 후 요청
422MARKET_004시장가 주문을 지원하지 않는 종목입니다.정책상 시장가 불가지정가 주문 사용
429RATE_LIMIT_001요청이 너무 많습니다.짧은 시간 내 반복 요청일정 시간 후 재시도
500SERVER_001일시적인 서버 오류가 발생했습니다.예상하지 못한 서버 오류같은 키로 상태 확인 또는 재시도
503INFRA_001주문 처리 시스템을 일시적으로 사용할 수 없습니다.DB, Redis, 메시지 큐 장애잠시 후 재시도
504EXTERNAL_001외부 주문 시스템 응답이 지연되고 있습니다.브로커/거래소 응답 지연같은 키로 재시도 또는 상태 확인
정책은 잔고와 같이사용자의 자산과 직접 연결되기 때문에 중복 주문 방지, 보유 수량 정합성, 체결 지연, 장애 상황까지 고려하여 짜도록 설계하였다.

정책

주문 접수/체결 분리

API 성공은 체결 완료가 아니라 접수 완료를 의미한다.

시장가 위험 고지

실제 체결 가격은 접수 시점에 확정되지 않는다.

보유 수량 예약

주문 접수 시 보유 수량을 바로 차감하지 않고 예약한다.

멱등성

같은 주문 요청이 중복 처리되지 않도록 Idempotency-Key를 사용한다.

동시성 제어

동일 계좌/종목 동시 매도 요청을 락과 트랜잭션으로 제어한다.

거래 가능 시간

정규장 장중에만 시장가 매도를 허용한다.

부분체결

일부만 체결될 수 있으며 잔여 수량은 계속 관리한다.

취소 정책

이미 체결된 수량은 취소할 수 없고 잔여 수량만 취소 가능하다.

재시도

같은 주문 재시도는 반드시 같은 멱등성 키를 사용한다.

Rate Limit

과도한 주문 요청을 제한한다.

감사 로그

주문 요청과 상태 변경 이력을 추적 가능하게 남긴다.

장애 대응

외부 시스템 지연과 내부 장애를 고려해 상태를 관리한다.

AI 사용 로그

AI가 잘해준 점은 생각보다 더욱 더 실패 상황을 자세히 설계해준 부분이다. 또한 내가 고려하지 못했던 거래정지 상태라던지 다영한 상황에 대해 고려하여 실패코드를 작성을 한 것 같다.
AI를 활용하면서 API를 작성하는 과정에서 처음에는 주문 매도를 위해 조회를 해야하는 다른 로직들 API까지 작성을 해주었다. 이 부분을 제외하고 매도 부분만 집중하고 싶어서 매도 부분 로직에만 집중을 하도록 프롬프트를 수정을 하였다.

또한 처음에는 단순히 요구 사항만 적고 요청을 했지만 추후에는 맨 위에 적은것과 같이 좀 더 구체화하여 프롬프트를 작성하였다.

초기 버전은 아래와 같다.


초기:

사용자는 자신이 보유한 주식을 시장가로 매도할 수 있습니다.
최대한 실제 금융 환경을 고려하여 API를 설계해주세요.

필수로 포함해야 하는 내용은 다음과 같습니다.

1. URL
2. Method
3. Request
4. Response
5. 실패 응답
 - 인증 실패
 - 계좌 권한 실패
 - 보유 수량 부족
 - 존재하지 않는 종목
 - 거래 정지 종목
 - 시장가 주문 불가능 시간
 - 중복 요청
 - 동시성 문제
 - 외부 시스템 오류
6. 상태 코드
7. 이 API를 사용할 때 고려해야 할 정책
8. 동시성 이슈

이외에도 AI가 작성해준 실패응답에는 타임스탬프가 없어 추가를 하였다.

0개의 댓글