API·REST 설계 — 백엔드 면접 (실제 기출 기반·심화)
"REST와 RESTful의 차이", "HTTP 메서드의 멱등성", "GET과 POST의 근본 차이", "REST vs GraphQL vs gRPC", "결제 중복을 어떻게 막나", "페이지네이션" — 설계 감각을 보는 단골이다. (HTTP 메서드·상태코드의 프로토콜 동작은 네트워크 편, 여기선 API 설계.)
함정형 단골: "POST는 절대 캐싱 안 된다?", "GET은 안전하니 멱등하다?"(safe ≠ idempotent), "REST = HTTP+JSON?"(HATEOAS).
1. REST·RESTful — Fielding 제약 (★ 단골)
실제 질문: "REST와 RESTful의 개념을 설명해주세요." (WeareSoft·gyoogle)
REST(Representational State Transfer) = Roy Fielding이 정의한 아키텍처 스타일. 6제약:
- 클라이언트-서버·무상태(Stateless)(각 요청 독립)·캐시 가능·균일 인터페이스(Uniform Interface)·계층화·(선택)코드 온디맨드.
- 실천: 자원을 URI로 식별·행위는 HTTP 메서드·상태는 표현(JSON).
깊이 — Richardson 성숙도: L0(RPC) → L1(자원) → L2(메서드+상태코드·실무 대부분) → L3(HATEOAS·응답에 다음 행동 링크). "진짜 RESTful"은 L3까지지만 대부분 미충족.
꼬리질문: "RESTful의 핵심 제약?" → Uniform Interface(self-descriptive·HATEOAS). / "무상태의 이점?" → 서버 확장 용이(세션 없음).
함정: ❌ "REST = HTTP + JSON" → 아키텍처 스타일. HATEOAS까지 만족해야 진짜 RESTful.
2. 멱등성·안전성 (★★ 단골)
실제 질문: "HTTP Method의 멱등성을 설명해주세요." · "GET과 POST의 근본 차이는?" (VSFe·WeareSoft)
| 메서드 | 안전(safe) | 멱등(idempotent) |
|---|---|---|
| GET·HEAD | ✓ | ✓ |
| PUT·DELETE | ✗ | ✓ |
| POST·PATCH | ✗ | ✗ |
- 안전(safe): 서버 상태를 변경하지 않음(읽기). 멱등(idempotent): 여러 번 호출해도 서버 상태가 같음. (safe 메서드 GET·HEAD·OPTIONS·TRACE는 정의상 모두 멱등 — safe ⊂ idempotent, 역은 성립 안 함.)
- 둘은 다른 개념(★): GET은 안전하면서 멱등, PUT/DELETE는 안전하지 않지만 멱등(같은 상태로 만듦), POST는 둘 다 아님(매번 새로 생성).
깊이 — DELETE 멱등 논쟁: DELETE를 두 번 호출하면 두 번째는 404일 수 있지만, 서버 상태는 "삭제됨"으로 동일 → 멱등 유지(상태코드가 달라도 멱등 정의는 서버 상태).
꼬리질문: "GET은 안전하니 멱등?" → safe ⊂ idempotent지만 별개 개념(안전하면 멱등이나 역은 아님). / "PATCH가 비멱등인 이유?" → balance += 1 같은 상대적 변경은 호출마다 누적.
함정: ❌ "safe = idempotent" → 다른 개념. ❌ "POST는 절대 캐싱 안 됨" → 조건부 캐싱 가능(Cache-Control).
3. 멱등성 키 — 결제 중복 방지 (★★ 결제 단골)
실제 질문: "결제·주문에서 중복 요청을 어떻게 막나요?"(멱등성 키) (현업 면접·결제 시스템)
POST /payments
Idempotency-Key: 9f1c-...-a3 ← 클라가 생성한 고유 키
서버는 같은 키를 한 번만 처리하고, 재시도엔 저장된 결과를 반환 → 네트워크 재시도·더블 클릭에도 결제 1회. IETF가 Idempotency-Key 헤더를 표준화 중(Stripe·Toss 사실상 표준).
꼬리질문: "키 저장은 어디에?" → DB(유니크 제약)·Redis(TTL). / "동시 요청 2개가 같은 키로?" → 유니크 제약/분산 락으로 하나만 처리·나머지는 진행 중/결과 반환. / "멱등성 키 vs PUT?" → PUT은 본질적 멱등, POST는 키로 멱등성 부여.
4. REST vs GraphQL vs gRPC (★★ 단골)
실제 질문: "REST, GraphQL, gRPC를 비교하라." · "GraphQL의 단점은?" (velog·VSFe)
| REST | GraphQL | gRPC | |
|---|---|---|---|
| 형식 | JSON·다중 엔드포인트 | 쿼리·단일 엔드포인트 | Protobuf 바이너리·HTTP/2 |
| 데이터 | 고정 응답(over/under-fetching) | 클라가 필요한 필드만 | 계약(.proto)·빠름·양방향 스트리밍 |
| 캐시 | HTTP 캐시 쉬움 | 어려움(POST 단일) | 자체 |
| 용도 | 범용 공개 API | 복잡한 프론트·다양한 클라 | MSA 서비스 간·고성능 |
깊이 — GraphQL 단점(★): ① 리졸버 N+1(각 필드가 쿼리 → DataLoader로 배치) ② 캐싱 어려움(단일 POST 엔드포인트) ③ 쿼리 복잡도 폭탄(깊은 중첩 → 깊이 제한·비용 분석).
꼬리질문: "gRPC는 언제?" → 내부 MSA 서비스 간 고성능·계약 기반(브라우저 공개 API엔 REST). / "GraphQL N+1?" → DataLoader(요청 단위 배치·캐시).
함정: ❌ "GraphQL이 항상 낫다" → 캐싱·복잡도·N+1 비용.
5. 페이지네이션 (★ 단골)
실제 질문: "Offset 기반과 Cursor 기반 페이지네이션의 차이는?" (velog @ygreenb)
-- offset: 단순하나 깊은 페이지에서 앞 행을 다 스캔 → 느림, 삽입 시 중복/누락
SELECT * FROM posts ORDER BY id DESC LIMIT 20 OFFSET 10000;
-- cursor(keyset): 마지막 본 위치 기준 → 안정·대용량·일정 성능
SELECT * FROM posts WHERE id < :lastId ORDER BY id DESC LIMIT 20;
- offset(페이지 번호 UI·깊을수록 느림·실시간 삽입 시 중복/누락) vs cursor(정렬 키 기준·무한 스크롤·대용량 안정).
꼬리질문: "커서 기반에서 임의 페이지 점프가 안 되는 이유?" → 직전 행 기준이라 N번째 페이지로 바로 못 감. / "깊은 offset이 느린 이유?" → 앞 OFFSET개를 읽고 버림.
함정: ❌ "offset이 항상 단순해서 낫다" → 대용량·실시간엔 커서.
6. 상태코드·에러 설계·rate limiting (★ 심화)
- 상태코드: 201 Created(+
Location)·204 No Content·401(인증) vs 403(인가)·409 Conflict(낙관적 락 충돌)·422 Unprocessable(검증)·429 Too Many Requests(rate limit). - 에러 포맷 일관:
{ "code", "message", "errors":[...] }또는 RFC 9457application/problem+json. - rate limiting: Token Bucket(버킷에 토큰 충전·burst 허용·가장 흔함)·Leaky Bucket·Sliding Window. 초과 시 429 +
Retry-After. - 엔티티 직접 노출 X → DTO(API 스펙 분리·
LazyInitializationException방지·보안 — Spring·JPA 편).
꼬리질문: "401 vs 403?" → 인증(누구인지 모름) vs 인가(권한 없음). / "동시 수정 충돌?" → 낙관적 락·ETag/If-Match → 409.
흔한 오답·함정 정리
- safe = idempotent → 다른 개념(GET은 둘 다, PUT/DELETE는 멱등만).
- POST는 절대 캐싱 안 됨 → 조건부 캐싱 가능.
- REST = HTTP + JSON → 아키텍처 스타일(HATEOAS).
- GraphQL이 항상 낫다 → 캐싱·N+1·복잡도 비용.
- 결제 중복은 프론트에서만 막으면 됨 → 서버 멱등성 키.
한국 면접 단골 Q&A (답변 골격)
| 질문 | 핵심 답 |
|---|---|
| REST·RESTful | 자원/메서드/무상태·Richardson L2/L3·HATEOAS |
| 멱등성·안전성 | safe(읽기) vs idempotent(상태 동일)·POST 둘 다 X |
| DELETE 멱등? | 404여도 서버 상태 동일 → 멱등 |
| 결제 중복 | Idempotency-Key·유니크/분산락 |
| REST vs GraphQL vs gRPC | 고정/필드선택/MSA 고성능 |
| GraphQL 단점 | 캐싱·리졸버 N+1·복잡도 폭탄 |
| 페이지네이션 | offset(단순·느림) vs cursor(대용량) |
| 상태코드 | 201/204·401 vs 403·409·422·429 |
| rate limiting | Token Bucket·429·Retry-After |
꼬리질문 대비 (상 난이도)
- "결제 더블 클릭?" → 멱등성 키로 1회 처리·결과 재사용.
- "PUT vs PATCH 멱등성?" → PUT 전체 교체(멱등), PATCH는 구현 따라 비멱등.
- "GraphQL N+1?" → 각 필드 리졸버가 쿼리 → DataLoader 배치.
- "API 응답에 엔티티 그대로?" → 결합·LazyInit·보안 → DTO.
- "버전 안 올리고 필드 추가?" → 호환 변경(클라는 모르는 필드 무시) → 깨는 변경만 버전.
한 줄 요약 — REST=자원(URI)+메서드+무상태(Fielding·Richardson L2/L3 HATEOAS). safe≠idempotent(POST 둘 다 X·PUT/DELETE 멱등). 결제 중복은 Idempotency-Key(유니크/분산락). REST↔GraphQL(필드 선택·리졸버 N+1·캐싱 어려움)↔gRPC(MSA·Protobuf). 페이지네이션 offset(단순·느림)↔cursor(대용량). 상태코드(201/204·401 vs 403·409·422·429)·에러 일관(RFC 9457)·rate limit(Token Bucket·429)·DTO.
(출처 — 한국 면접 기출·교차검증 2026-06: VSFe/Tech-Interview — Network·gyoogle — REST API·WeareSoft — network·velog @ygreenb — offset vs cursor · RFC 9110(멱등성·상태코드)·RFC 9457(Problem Details)·draft-ietf-httpapi-idempotency-key-header 교차검증.)