본문 바로가기
C.W.K.
Stream
Lesson 01 of 07 · published

리소스와 RPC — 명사와 동작이 갈리는 지점

~11 min · rest-design, resources-vs-rpc, mental-model

Level 0HTTP 입문자
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"오늘날 공개되는 'REST API' 가운데 상당수는 리소스 지향 설계와 RPC를 섞어 써. 흠잡자는 말이 아니라 설계를 정확히 읽기 위한 관찰이야. 중요한 질문은 '어느 쪽이 옳은가?'가 아니라 '무엇을 중심으로 설계했고, 그 선택이 이 도메인에 도움이 되는가?'야."

두 가지 설계 중심축

API 설계를 시작할 때 내리는 첫 결정이 뒤따르는 선택의 모양을 잡아. 이 API는 무엇을 중심으로 조직할까?

리소스 지향 설계. 사용자, 주문, 결제, 대화처럼 식별할 수 있는 대상을 중심에 둬. 각 대상은 URI로 식별되고, GET·POST·PUT·PATCH·DELETE 같은 HTTP 메서드가 그 리소스에 작용해. 읽고, 만들고, 교체하고, 일부를 바꾸고, 삭제하는 의미를 프로토콜의 공통 어휘에 실어 보내는 방식이야.

RPC(원격 프로시저 호출). createUser, processPayment, finalizeCouncil처럼 수행할 작업을 중심에 둬. 각 작업은 절차 이름과 인자를 갖고, 클라이언트는 그 절차를 호출해. 도메인의 동작이 API 표면에 직접 드러나므로 필요한 명령을 분명하게 표현하기 쉬워.

둘 다 실제 운영에서 널리 쓰여. 도메인의 성격, 소비자, 도구 생태계, 팀의 운영 방식에 맞는 중심축을 고르면 돼.

빠른 시금석 — URI 살펴보기

API의 URI 목록을 펼쳐 봐. URI 대부분이 대상을 가리키면(/users/42, /orders/abc, /payments/xyz) 리소스 지향 성격이 강해. URI 대부분이 작업을 나타내면(/createUser, /processOrder, /getPaymentStatus) RPC 성격이 강하고.

2026년에 널리 쓰이는 API를 거칠게 분류하면 이래:

  • 리소스 지향: Stripe의 많은 엔드포인트, GitHub REST, AWS S3, Kubernetes API. URI에 다루는 대상이 드러나.
  • RPC 지향: Slack Web API(chat.postMessage, users.list), 많은 gRPC 서비스, OpenAI Chat Completions(POST /v1/chat/completions). 호출하려는 작업이나 결과 생성이 전면에 나와.
  • 혼합형: 실제 API에서 가장 흔해. 명확한 대상에는 리소스 URI를 쓰고, 그 모양에 잘 맞지 않는 작업에는 동작 엔드포인트를 둬(POST /payments/{id}/refund, POST /jobs/{id}/cancel). cwkPippa도 여기에 속해.

각 모델이 빛나는 때

리소스 지향 설계가 잘 맞는 때:

  • 도메인에 사용자, 문서, 주문처럼 경계가 분명한 명사가 많을 때. CRUD 비중이 큰 관리 화면, 콘텐츠 관리, 전자상거래 카탈로그가 자연스럽게 매핑돼.
  • 캐시와 조건부 요청을 적극 활용해야 할 때. GET 중심의 읽기 흐름은 HTTP 캐시와 CDN의 장점을 살리기 좋아.
  • 브라우저, 모바일 앱, 제3자 통합처럼 클라이언트가 다양할 때. 널리 알려진 HTTP 의미를 공유하면 소비자가 익혀야 할 API 고유 규칙을 줄일 수 있어.
  • URI 체계를 예측하기 쉽게 만들고 싶을 때. /users/{id}/orders는 관계를 짐작하기 쉽지만, getOrdersForUser(userId)는 해당 절차의 계약을 따로 확인해야 해.

RPC가 잘 맞는 때:

  • 도메인의 중심이 작업일 때. AI 생성 요청, 작업 흐름 오케스트레이션, 트랜스코딩, ML 추론은 "이 입력으로 이 작업을 수행해"라고 표현하는 편이 자연스러워.
  • 클라이언트와 서버를 함께 엄격하게 통제할 때. gRPC와 Protobuf처럼 타입 계약과 코드 생성을 중심으로 한 생태계가 대표적이야.
  • 작업을 리소스 상태 변화로 표현하면 오히려 뜻이 흐려질 때. 여러 사용자에게 채널과 심각도를 지정해 알림을 보내는 작업은 억지 명사보다 명시적 절차가 읽기 쉬울 수 있어.
일관성이 순수성보다 중요해. 근거가 분명한 동작 엔드포인트를 일부 둔 리소스 지향 API는 충분히 좋은 설계야. 반대로 모든 작업을 억지로 리소스로 포장하면 API가 수수께끼가 돼. 기본 중심축을 정하고, 그 원칙을 지킬수록 의미가 더 나빠지는 지점에서만 의도적으로 벗어나.

실전의 혼합형

운영 API는 흔히 최상위에 명사를 두고(/orders, /payments), 리소스 모양에 잘 맞지 않는 명령은 특정 리소스 아래의 동작 경로로 표현해(POST /orders/42/cancel, POST /payments/xyz/refund). 대상은 상위 리소스로 드러나고 작업 이름도 감추지 않아. POST를 쓴다고 해서 자동으로 비멱등인 것은 아니므로, 재시도 가능한 작업이라면 멱등성 키나 명확한 중복 처리 계약도 함께 설계해야 해.

이 방식은 리소스 지향 어휘를 유지하면서도 도메인의 실제 동작을 솔직하게 드러내. /orders/42라는 대상을 놓치지 않으면서, 취소나 환불처럼 단순한 CRUD로 설명하기 어려운 작업에도 분명한 자리를 마련하는 거야.

cwkPippa의 조합

cwkPippa는 혼합형이야. backend/routes/ 아래의 많은 경로가 리소스 지향으로 구성돼 있어. /api/conversations, /api/folders, /api/messages, /api/artifacts는 GET·POST·PUT·PATCH·DELETE가 리소스에 작용하는 형태야. 반면 POST /api/council/{id}/finalize, POST /api/council/{id}/inject, POST /api/heartbeat/cron/{id}/run-now는 기존 리소스에 명시적인 작업을 수행하는 동작 엔드포인트야. Council 마무리를 어색한 명사로 꾸미기보다 실제 도메인 동작을 드러낸 의도적인 혼합이야.

Code

리소스 지향 설계에 동작 엔드포인트를 더한 혼합형·text
# Resource-oriented 설계 — 명사 세기
GET    /users                      # resource 나열
POST   /users                      # resource 생성 (server 가 ID 할당)
GET    /users/{id}                 # resource 하나 읽기
PUT    /users/{id}                 # 교체
PATCH  /users/{id}                 # 부분 update
DELETE /users/{id}                 # 제거

GET    /users/{id}/orders          # nested resource — user 에 속한 order
POST   /users/{id}/orders          # user 아래 order 생성

# Action endpoint (실용적 하이브리드 탈출구)
POST   /orders/{id}/cancel         # action — POST + 이름 붙은 sub-path
POST   /payments/{id}/refund       # action — 같은 패턴
RPC 스타일 — 작업 이름을 URI에 두고 POST로 호출·text
# 순수 RPC 설계 — verb 세기
POST   /users.list                 # user 나열 (Slack 스타일)
POST   /users.create               # user 생성
POST   /users.get                  # 하나 읽기
POST   /users.update               # update
POST   /users.delete               # delete
POST   /orders.cancel              # action
POST   /payments.refund            # action

# 주목: 모든 URI 가 operation, 모든 method 가 POST.
# Status code 는 여전히 동작하지만 protocol 의 일곱 verb 가 하나로 붕괴.
같은 로직을 두 중심축으로 설계하기·python
# 같은 비즈니스 로직, 두 방식. 둘 다 동작; tradeoff 다름.
from fastapi import FastAPI

app = FastAPI()

# ============= Resource-oriented =============
@app.get('/orders/{oid}')
async def get_order_resource(oid: str):
    return await load_order(oid)

@app.post('/orders/{oid}/cancel', status_code=202)
async def cancel_order_resource(oid: str):
    return await cancel(oid)

# ============= RPC-스타일 ===================
@app.post('/orders.get')
async def get_order_rpc(payload: dict):
    return await load_order(payload['order_id'])

@app.post('/orders.cancel')
async def cancel_order_rpc(payload: dict):
    return await cancel(payload['order_id'])

# Resource-oriented 가 cacheable GET, conditional request, optimistic locking 공짜로 줘.
# RPC 가 더 단순한 계약 설계와 더 쉬운 코드 생성 줘. 지불하고 싶은 비용 골라.

External links

Exercise

사용해 본 API 하나를 골라. Stripe, GitHub, Slack, OpenAI, cwkPippa 가운데 선택해도 좋아. URI 열 개를 살펴보고, 각 URI가 대상 중심인지 작업 중심인지 분류해. 비율을 계산한 뒤 이 API가 주로 리소스 지향인지, 주로 RPC인지, 혼합형인지 설명해 봐. 보너스로 RPC 스타일 작업 하나를 리소스 지향 형태로 다시 설계하거나 그 반대로 바꿔 보고, 무엇을 얻고 무엇을 포기했는지 적어.
Hint
Slack의 users.listchat.postMessage는 작업 중심이어서 RPC 성격이 강해. GitHub의 /repos/{owner}/{repo}/issues는 대상을 식별하는 리소스 지향 형태야. OpenAI의 /v1/chat/completions는 결과 생성 작업이 전면에 나오는 RPC 성격이 있고, cwkPippa는 혼합형이야. 정답 하나를 찾기보다 설계의 대가와 이득을 말로 분명히 설명하는 데 집중해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 피파warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.