API 설계
API 계약은 한 번 공개되면 내 것이 아니다. 바꾸는 비용을 먼저 계산하고 설계한다.
절차
- 호출자와 사용 사례를 먼저 적는다. 누가, 어떤 화면/작업에서, 얼마나 자주 부르는가.
호출자가 정해지지 않은 엔드포인트는 설계할 수 없다.
- 리소스와 동작을 분리한다. 명사(리소스)를 먼저 정하고, 동작을 HTTP 메서드로 표현한다.
표현이 어색하면 리소스를 잘못 잡은 것이다.
- 실패 응답을 정상 응답보다 먼저 정한다.
- 호환성 영향을 판정한다. (아래 기준)
- 계약을 문서로 고정한다. 구현이 아니라 계약이 진실이다.
리소스 모델링
- URL은 리소스를 가리키고 동작은 메서드로 표현한다:
POST /orders, GET /orders/{id}
- 동사를 URL에 넣기 전에 리소스로 바꿀 수 있는지 본다.
POST /orders/{id}/cancel 처럼
상태 전이가 자연스러운 경우에만 동사를 허용한다.
- 중첩은 소유 관계가 진짜일 때만:
/orders/{id}/items. 두 단계를 넘기지 않는다.
- 컬렉션은 복수형, 식별자는 경로에, 필터·정렬·페이징은 쿼리스트링에 둔다.
- 화면 하나를 위한 전용 엔드포인트를 만들기 전에, 그것이 재사용될지 판단한다.
상태 코드와 오류
- 200 조회/성공, 201 생성(+Location), 204 본문 없음
- 400 형식·검증 실패, 401 인증 없음, 403 권한 없음, 404 없음, 409 상태 충돌, 422 의미 검증 실패
- 429 제한 초과, 5xx 서버 결함
- 오류 응답 형식을 하나로 고정한다. 최소한: 기계가 읽는 코드, 사람이 읽는 메시지,
필드 단위 오류 목록.
- 오류 메시지에 내부 구조(스택, 쿼리, 테이블명)를 노출하지 않는다.
- 부분 성공은 200으로 감추지 않는다. 어떤 것이 실패했는지 본문에 담거나 요청을 쪼갠다.
호환성
깨는 변경(새 버전 필요)
- 필드/엔드포인트 제거, 이름 변경
- 타입 변경, 값 형식 변경
- 선택 필드를 필수로 변경
- 기본 동작 변경(기본 정렬, 기본 페이지 크기 등 호출자가 의존하는 것)
깨지 않는 변경
- 선택 필드 추가, 새 엔드포인트 추가
- 새 열거값 추가 — 단, 클라이언트가 모르는 값을 만났을 때의 동작이 계약에 있을 때만
버저닝은 필요해질 때 도입하고, 방식(URL 경로 / 헤더)을 프로젝트 전체에서 하나로 통일한다.
제거는 즉시 하지 않는다: 새 것 추가 → 사용처 이전 → 사용량 0 확인 → 제거.
반드시 정해야 하는 것
- 페이징: 방식(오프셋/커서), 기본 크기, 최대 크기. 상한 없는 목록 조회를 열지 않는다.
- 멱등성: 재시도가 안전한가. POST 계열은 멱등키를 받을지 정한다.
- 시각 표현: ISO-8601 + 타임존 기준을 고정한다.
- 인증 주체: 요청 본문의 사용자 ID를 신뢰하지 않는다. 토큰에서 얻는다.
- 정렬 기준: 목록 응답에 안정적인 정렬이 없으면 페이징이 중복/누락을 만든다.
보고 형식
엔드포인트: <메서드 경로>
호출자/용도:
요청: <파라미터·본문 + 검증 규칙>
응답: <성공 형태>
오류: <상태코드 → 상황>
호환성: <깨는 변경 여부와 이전 계획>
실제 엔드포인트 목록·필드명·도메인 규칙은 각 프로젝트의 문서에 둔다.
1---2name: api-design3description: HTTP API의 계약을 새로 설계하거나 기존 계약을 바꿀 때 사용한다. "이 엔드포인트 어떻게 설계하지", "URL을 어떻게 나눌까", "이 응답 형식 괜찮나", "이렇게 바꾸면 클라이언트가 깨지나", "버저닝 어떻게 하지" 같은 요청에 트리거된다. 리소스 모델링, 상태 코드/오류 규약, 호환성 파괴 판단, 페이징·멱등성 규칙을 다룬다. 구현 계층 배치가 목적이면 java-spring-backend를, 이미 있는 구현을 평가하는 것이면 code-review를 쓴다.4---56# API 설계78API 계약은 **한 번 공개되면 내 것이 아니다.** 바꾸는 비용을 먼저 계산하고 설계한다.910## 절차11121. **호출자와 사용 사례를 먼저 적는다.** 누가, 어떤 화면/작업에서, 얼마나 자주 부르는가.13 호출자가 정해지지 않은 엔드포인트는 설계할 수 없다.142. **리소스와 동작을 분리한다.** 명사(리소스)를 먼저 정하고, 동작을 HTTP 메서드로 표현한다.15 표현이 어색하면 리소스를 잘못 잡은 것이다.163. **실패 응답을 정상 응답보다 먼저 정한다.**174. **호환성 영향을 판정한다.** (아래 기준)185. **계약을 문서로 고정한다.** 구현이 아니라 계약이 진실이다.1920## 리소스 모델링2122- URL은 리소스를 가리키고 동작은 메서드로 표현한다: `POST /orders`, `GET /orders/{id}`23- 동사를 URL에 넣기 전에 리소스로 바꿀 수 있는지 본다. `POST /orders/{id}/cancel` 처럼24 상태 전이가 자연스러운 경우에만 동사를 허용한다.25- 중첩은 소유 관계가 진짜일 때만: `/orders/{id}/items`. 두 단계를 넘기지 않는다.26- 컬렉션은 복수형, 식별자는 경로에, 필터·정렬·페이징은 쿼리스트링에 둔다.27- 화면 하나를 위한 전용 엔드포인트를 만들기 전에, 그것이 재사용될지 판단한다.2829## 상태 코드와 오류3031- 200 조회/성공, 201 생성(+Location), 204 본문 없음32- 400 형식·검증 실패, 401 인증 없음, 403 권한 없음, 404 없음, 409 상태 충돌, 422 의미 검증 실패33- 429 제한 초과, 5xx 서버 결함34- **오류 응답 형식을 하나로 고정한다.** 최소한: 기계가 읽는 코드, 사람이 읽는 메시지,35 필드 단위 오류 목록.36- 오류 메시지에 내부 구조(스택, 쿼리, 테이블명)를 노출하지 않는다.37- 부분 성공은 200으로 감추지 않는다. 어떤 것이 실패했는지 본문에 담거나 요청을 쪼갠다.3839## 호환성4041**깨는 변경(새 버전 필요)**42- 필드/엔드포인트 제거, 이름 변경43- 타입 변경, 값 형식 변경44- 선택 필드를 필수로 변경45- 기본 동작 변경(기본 정렬, 기본 페이지 크기 등 호출자가 의존하는 것)4647**깨지 않는 변경**48- 선택 필드 추가, 새 엔드포인트 추가49- 새 열거값 추가 — 단, **클라이언트가 모르는 값을 만났을 때의 동작이 계약에 있을 때만**5051버저닝은 필요해질 때 도입하고, 방식(URL 경로 / 헤더)을 프로젝트 전체에서 하나로 통일한다.52제거는 즉시 하지 않는다: 새 것 추가 → 사용처 이전 → 사용량 0 확인 → 제거.5354## 반드시 정해야 하는 것5556- **페이징**: 방식(오프셋/커서), 기본 크기, 최대 크기. 상한 없는 목록 조회를 열지 않는다.57- **멱등성**: 재시도가 안전한가. POST 계열은 멱등키를 받을지 정한다.58- **시각 표현**: ISO-8601 + 타임존 기준을 고정한다.59- **인증 주체**: 요청 본문의 사용자 ID를 신뢰하지 않는다. 토큰에서 얻는다.60- **정렬 기준**: 목록 응답에 안정적인 정렬이 없으면 페이징이 중복/누락을 만든다.6162## 보고 형식6364```65엔드포인트: <메서드 경로>66호출자/용도:67요청: <파라미터·본문 + 검증 규칙>68응답: <성공 형태>69오류: <상태코드 → 상황>70호환성: <깨는 변경 여부와 이전 계획>71```7273실제 엔드포인트 목록·필드명·도메인 규칙은 **각 프로젝트의 문서**에 둔다.