카테고리 없음

문서 없는 API 다루기 - 엔드포인트를 더듬어 찾는 법

바뿌 2026. 10. 29. 06:02

사내 API나 직접 만든 서비스는 문서가 없는 경우가 흔합니다.
목록은 되는데 개별 조회가 안 될 때, 경로를 추측해가며 찾아야 합니다.

오늘 그 작업을 했고, 중간에 엉뚱한 결론을 내릴 뻔했습니다. 그 과정을 적습니다.

결론부터

단계 하는 일
1 응답 형태로 "누가 404를 줬는지" 구분한다
2 REST 관례대로 경로를 순서대로 찍어본다
3 쿼리 파라미터로 필드가 늘어나는지 본다
4 페이지네이션 메타를 먼저 확인한다 ← 안 하면 오판한다
5 그래도 없으면 없는 것이다. 추측을 멈춘다

1. 404를 누가 줬는지 구분한다

프록시나 게이트웨이를 거치는 구조라면, 404가 두 군데서 나올 수 있습니다.

// 프록시가 준 것 — 경로가 중계 대상이 아님
{"error":"not_found","hint":"this relay only forwards /api/xxx/*"}

// 상류 서버가 준 것 — 중계는 됐는데 서버에 경로가 없음
{"error":"not found"}

언더스코어(not_found)와 공백(not found), hint 유무가 달랐습니다.
이걸 구분하지 못하면 "프록시 설정이 잘못됐나" 하고 엉뚱한 곳을 고치게 됩니다.

프록시 로그를 볼 수 있으면 더 확실합니다.

[relay] GET /api/xxx/documents/2 -> 404     ← 상류가 404를 줬다는 뜻

상류가 응답했다는 건 중계는 정상이라는 뜻입니다.

2. 경로를 순서대로 찍어본다

REST 관례를 따르는 순서로 시도합니다.

B=https://example.com/api/resource
for p in "items/2" "items/2/content" "items/2/raw" "items/2/markdown" \
         "item/2" "detail/2" "view/2" "2"; do
  printf "  %-24s " "$p"
  curl -s "$B/$p" | head -c 60
  echo
done

식별자가 여럿이면 그것도 바꿔봅니다.

# id / slug / 사용자 지정 키
curl -s "$B/items/2"
curl -s "$B/items/my-document-slug"
curl -s "$B/items/by-key/my-stable-key"

쓰기 경로를 먼저 확인하면 힌트가 됩니다. 업로드 스크립트가 있다면 거기서
어느 경로로 POST하는지 보면 리소스 이름이 드러납니다.

grep -n "fetch(\|method:" upload.js
# 107: await fetch(`${BASE}/api/resource/items`, {
# 108:   method: 'POST',

→ POST /api/resource/items 가 있으니 GET /api/resource/items/:id 를 기대하게 됩니다.
다만 기대일 뿐, 실제로 구현돼 있다는 보장은 없습니다.

3. 쿼리 파라미터로 필드가 늘어나는지

목록 API가 요약만 준다면, 파라미터로 전체를 요청할 수 있는지 봅니다.

base=$(curl -s "$B/items" | python3 -c "
import sys,json;d=json.load(sys.stdin)
print(max(len(str(v)) for v in d['items'][0].values() if isinstance(v,str)))")
echo "기준 최장 필드: ${base}자"

for q in "include=content" "fields=content" "full=true" "expand=content" "raw=1"; do
  n=$(curl -s "$B/items?$q" | python3 -c "
import sys,json;d=json.load(sys.stdin)
print(max(len(str(v)) for v in d['items'][0].values() if isinstance(v,str)))")
  printf "  %-20s %s자%s\n" "$q" "$n" "$([ "$n" -gt "$base" ] && echo '  ← 늘었다')"
done

길이를 비교하는 게 핵심입니다. 응답이 200이어도 파라미터를 무시하고 같은 걸
돌려줄 수 있습니다. 눈으로 보면 "뭔가 왔네" 하고 넘어가게 됩니다.

⚠️ 4. 페이지네이션 — 여기서 오판했습니다

목록을 받아서 "총 20건이구나" 하고 작업을 진행했습니다.

그런데 검색을 해보니 목록에 없던 문서가 나왔습니다. 분명 목록에서 못 봤는데
검색에는 걸리는 겁니다.

응답을 다시 보니 이랬습니다.

{
  "items": [ ...20개... ],
  "total": 46,          ← 전체는 46건
  "page": 1,
  "size": 20            ← 한 번에 20건씩
}

items 길이만 세고 total 을 안 봤습니다. 실제는 46건인데 20건으로 알고
"로컬에 원본이 있는 건 2건뿐"이라는 결론까지 냈습니다. 절반만 보고 내린 판단이었습니다.

# 전체를 가져오려면
curl -s "$B/items?size=200"      # 또는 limit / per_page — 서버마다 다르다

파라미터 이름도 서버마다 다릅니다. 세 개를 시도해보니 size 만 먹혔습니다.

limit=200      → 20건 (무시됨)
per_page=200   → 20건 (무시됨)
size=200       → 46건 ✅

목록 API를 만나면 응답의 메타 필드부터 봅니다.
total, count, has_more, next, page 같은 것들입니다.
없으면 없다는 것도 확인해야 합니다.

5. 없으면 없는 것이다

경로 13종, 파라미터 12종을 시도해도 안 나오면 그 기능이 없는 것입니다.

더 추측하는 건 시간 낭비입니다. 그때는 방향을 바꿉니다.

  • 서버 코드를 본다 — 라우트 정의가 가장 확실합니다
  • 클라이언트가 쓰는 경로를 본다 — 웹 UI가 보여준다면 어딘가로 요청하고 있습니다.
    브라우저 개발자도구 네트워크 탭에 그대로 나옵니다
  • 만든 사람에게 묻는다
  • 없으면 추가한다 — 자체 서비스라면 이게 가장 깔끔합니다

저는 마지막을 택했습니다. 엔드포인트 하나를 추가하는 게, 없는 길을 계속 찾는 것보다 빨랐습니다.

정리

  • 404를 누가 줬는지 구분하세요. 프록시인지 상류인지에 따라 고칠 곳이 다릅니다
  • 쓰기 경로에서 리소스 이름 힌트를 얻을 수 있습니다
  • 파라미터 시험은 응답 길이를 비교하세요. 200이어도 무시됐을 수 있습니다
  • total 을 보세요. items 길이는 전체가 아닙니다
  • 파라미터 이름(size/limit/per_page)은 서버마다 다릅니다
  • 충분히 찾았는데 없으면 없는 겁니다. 추측을 멈추고 방향을 바꾸세요