infra
Platform

모듈 맵

[Infra Ops] RESTful API 구조와 curl/Postman 테스트 실무

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 24 / 52

[Infra Ops] RESTful API 구조와 curl/Postman 테스트 실무

HTTP 메서드, 상태코드, 인증 헤더, curl 테스트, OpenAPI 문서 읽기까지 — 인프라 엔지니어가 API 연계 문제를 직접 진단하기 위한 기초 실무

🚨INCIDENT ALERT
HIGH

외부 결제 연동이 갑자기 실패했다는 긴급 알림이 들어왔습니다. 개발팀은 "API 호출이 실패하는 것 같다"고 하는데, 정확히 어디서 왜 실패하는지 모릅니다. 로그를 보니 HTTP 500이 간헐적으로 찍힙니다. 그런데 직접 API를 호출해서 확인해볼 수 있는 인프라 엔지니어가 없습니다.

개발자가 코드를 뜯어보는 동안 인프라 엔지니어가 직접 curl로 API 엔드포인트를 찔러봤다면 5분 안에 원인을 좁힐 수 있었을 것입니다. 이 모듈은 그 5분을 만드는 기초입니다.

이번 챕터에서 배울 것
  • 1HTTP 메서드(GET/POST/PUT/PATCH/DELETE)의 용도와 멱등성 차이를 설명할 수 있다
  • 2주요 상태코드(2xx/4xx/5xx)의 의미를 보고 즉시 원인 범위를 좁힐 수 있다
  • 3curl로 인증 헤더 포함 GET/POST 요청을 보내고 응답을 확인할 수 있다
  • 4jq로 JSON 응답에서 원하는 필드를 파싱할 수 있다
  • 5API 응답 시간을 측정해서 성능 이상 여부를 판단할 수 있다

HTTP 메서드와 상태코드

💡개념

HTTP 메서드 — 동작 의미와 멱등성

REST API를 처음 보면 URL보다 메서드가 더 중요합니다. 같은 /users/123이라도 GET이면 조회, DELETE면 삭제입니다. 메서드를 틀리면 원하는 동작이 일어나지 않거나, 의도치 않은 변경이 발생합니다.

REST 설계 원칙 — Stateless, Uniform Interface, Layered System 핵심 제약

메서드용도멱등성요청 바디
GET리소스 조회O없음
POST새 리소스 생성X있음
PUT리소스 전체 교체O있음
PATCH리소스 일부 수정구현에 따라 다름있음
DELETE리소스 삭제O보통 없음

멱등성이 중요한 이유: 네트워크 타임아웃 등으로 응답을 못 받았을 때 재시도해도 되는 메서드인지 판단하는 기준이 됩니다. GET, PUT, DELETE는 재시도해도 안전하지만 POST는 중복 생성이 발생할 수 있습니다.

로컬 또는 서버
# 요청 시뮬레이션 — 메서드별 curl 패턴
curl -X GET    https://api.example.com/users/123
curl -X POST   https://api.example.com/users
curl -X PUT    https://api.example.com/users/123
curl -X PATCH  https://api.example.com/users/123
curl -X DELETE https://api.example.com/users/123
💡개념

상태코드 — 범주별 의미 파악

API 장애 신고가 들어왔을 때 상태코드는 가장 먼저 확인해야 할 첫 번째 단서입니다. 코드 범주(2xx / 4xx / 5xx)만 보아도 문제가 클라이언트 요청에 있는지, 서버 내부에 있는지 즉시 구분할 수 있어 원인 추적 방향이 달라집니다. 특히 401과 403을 혼동하거나 502와 504를 같은 의미로 보면 점검 방향이 틀려지므로, 범주별 의미를 정확히 파악해두는 것이 인프라 엔지니어의 기본 역량입니다.

HTTP 상태코드 분류

API 장애 대응에서 상태코드는 첫 번째 단서입니다. 코드 범주만 봐도 문제가 클라이언트 쪽인지 서버 쪽인지 구분됩니다.

2xx — 성공

코드의미주로 쓰이는 상황
200 OK요청 성공GET, PUT, PATCH 응답
201 Created리소스 생성 성공POST 응답, Location 헤더로 새 URL 전달
204 No Content성공하지만 응답 바디 없음DELETE, 일부 PUT 응답

4xx — 클라이언트 오류 (요청 자체의 문제)

코드의미확인할 것
400 Bad Request요청 형식/파라미터 오류요청 바디 JSON 형식, 필수 파라미터
401 Unauthorized인증 실패Authorization 헤더, 토큰 만료 여부
403 Forbidden권한 없음계정 권한 설정
404 Not Found리소스 없음URL 오타, 리소스 삭제 여부
415 Unsupported Media TypeContent-Type 불일치Content-Type 헤더 누락 또는 오타
422 Unprocessable Entity유효성 검사 실패필드 형식, 값 범위
429 Too Many Requests요청 빈도 초과Rate Limit 설정, Retry-After 헤더

5xx — 서버 오류 (서버 측 문제)

코드의미확인할 것
500 Internal Server Error서버 내부 오류서버 로그 (인프라 엔지니어 불필요, 앱 로그 확인)
502 Bad Gatewayupstream 서버 비정상뒷단 서비스 상태, 네트워크 연결
503 Service Unavailable서비스 일시 과부하 또는 점검 중서버 부하, 배포 중 여부
504 Gateway Timeoutupstream 응답 지연뒷단 서비스 응답 시간, 타임아웃 설정
💡개념

요청 헤더 — 자주 쓰는 4가지

curl로 API를 호출했는데 401이 납니다. "Authorization 헤더 넣었는데요?"라고 하면 "어떤 형식으로 넣었어요?"라고 다시 묻습니다. Bearer인지 Basic인지, X-API-Key인지 서비스마다 다르고, Content-Type을 빠뜨리면 415가 납니다. 헤더 4가지의 역할을 구분해서 알아야 curl 테스트 한 번에 원인을 좁힐 수 있습니다.

헤더는 요청의 메타데이터입니다. API 인증, 데이터 형식, 클라이언트 식별 등 요청의 "문맥"을 담습니다. 인프라 엔지니어가 curl로 테스트할 때 헤더를 빠뜨리면 의미 있는 테스트가 되지 않습니다.

로컬 터미널
# 자주 쓰는 요청 헤더 4가지
Authorization: Bearer <JWT_TOKEN>   # Bearer 토큰 인증 (OAuth2)
Authorization: Basic <BASE64>       # Basic 인증 (user:pass를 base64 인코딩)
X-API-Key: <API_KEY>                # API 키 인증 (서비스마다 헤더 이름이 다를 수 있음)

Content-Type: application/json      # 요청 바디가 JSON임을 서버에 알림
Accept: application/json            # 응답을 JSON으로 받고 싶다는 클라이언트 의사 표시

Authorization 유형 비교:

유형형식주로 쓰이는 곳
BearerAuthorization: Bearer <token>REST API, OAuth2 (가장 흔함)
BasicAuthorization: Basic <base64>레거시 시스템, Jenkins, Artifactory
X-API-KeyX-API-Key: <key>외부 API 연동 (결제사, 공공 API)

curl로 API 테스트하기

1GET 요청 — 인증 헤더 포함

-s(silent)로 진행 표시를 숨기고, -H로 헤더를 추가합니다. | jq .로 응답 JSON을 들여쓰기 형태로 보기 좋게 출력합니다. jsonplaceholder는 실제 테스트용 공개 API입니다 — TOKEN 자리에 아무 문자열을 넣어도 동작합니다.

로컬 또는 서버
# 특정 필드만 추출
curl -s https://jsonplaceholder.typicode.com/users | jq '.[0].name'
curl -s https://jsonplaceholder.typicode.com/users | jq '.[].email'
curl -s https://jsonplaceholder.typicode.com/users | jq '.[0] | {id, name, email}'
curl -s -H 'Authorization: Bearer TOKEN' https://jsonplaceholder.typicode.com/users | jq
🔍실행 후 확인할 것
  • JSON이 들여쓰기된 형태로 출력됐는가 (jq 파싱 성공)
  • jq '.[0].name' 으로 첫 번째 사용자 이름만 추출됐는가
  • jq '.[].email' 로 전체 사용자 이메일 목록이 한 줄씩 출력됐는가
2POST 요청 — JSON 바디 포함

-X POST로 메서드를 지정하고, -H 'Content-Type: application/json'으로 바디 형식을 알립니다. -d '...'에 JSON 바디를 넣습니다. 실제 서비스 API는 201 Created를 반환하면서 생성된 리소스 정보를 응답합니다.

로컬 또는 서버
# 응답 상태코드와 헤더까지 확인
curl -s -o /dev/null -w "%{http_code}" \
  -X POST -H "Content-Type: application/json" \
  -d '{"name":"test","email":"test@example.com"}' \
  https://jsonplaceholder.typicode.com/users
# 출력: 201
curl -s -X POST -H 'Content-Type: application/json' -d @/tmp/body.json https://jsonplaceholder.typicode.com/users
🔍실행 후 확인할 것
  • 응답 바디에 id 필드가 포함됐는가 (서버가 새 리소스를 생성했다는 증거)
  • -w '%{http_code}' 옵션으로 201 상태코드가 반환됐는가
  • Content-Type 헤더 없이 POST를 보냈을 때와 결과 차이가 있는가
3응답 헤더 확인과 디버그

-i(include)는 응답 헤더를 바디와 함께 출력합니다. Content-Type, Date, 캐시 헤더 등을 확인할 수 있습니다. 더 상세한 디버그가 필요하면 -v(verbose)를 씁니다.

로컬 또는 서버
# 요청/응답 전체 흐름 디버그 (연결, 헤더, 바디 모두 출력)
curl -v https://jsonplaceholder.typicode.com/users/1 2>&1 | head -40

# 응답 시간 측정 (API 성능 이상 확인)
curl -s -o /dev/null -w "time_total: %{time_total}s\ntime_connect: %{time_connect}s\n" \
  https://jsonplaceholder.typicode.com/users/1
curl -i https://jsonplaceholder.typicode.com/users/1
🔍실행 후 확인할 것
  • -i 출력 첫 줄이 'HTTP/1.1 200 OK' 형태로 나왔는가
  • 응답 헤더에 Content-Type: application/json이 포함됐는가
  • -w time_total 값이 1초 이하인가 (해외 API의 경우 2-3초도 정상)
  • curl -v 출력에서 '>' 로 시작하는 줄이 요청 헤더, '<' 로 시작하는 줄이 응답 헤더임을 확인했는가
4jq로 JSON 응답 파싱

jq는 JSON 데이터를 커맨드라인에서 필터링하는 도구입니다. API 응답에서 필요한 필드만 추출하거나, 배열을 순회하거나, 조건으로 필터링할 수 있습니다.

로컬 또는 서버
# 자주 쓰는 jq 패턴
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[0]'           # 첫 번째 객체
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[].userId'     # 모든 userId 추출
curl -s https://jsonplaceholder.typicode.com/posts | jq '.[] | select(.userId == 1)'  # 필터
curl -s https://jsonplaceholder.typicode.com/posts | jq 'length'         # 배열 길이
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.address.city' # 중첩 필드
curl -s https://jsonplaceholder.typicode.com/posts | jq first
🔍실행 후 확인할 것
  • jq '[.[] | {id, title}]' 로 id와 title만 추린 새 배열이 출력됐는가
  • jq '.[] | select(.userId == 1)' 로 특정 사용자 게시물만 필터링됐는가
  • jq 'length' 로 응답 배열의 항목 수가 숫자로 출력됐는가

OpenAPI 문서 읽기

💡개념

OpenAPI(Swagger) 문서 — 실무에서 필요한 부분만

외부 결제 API 연동 담당을 맡았는데 문서 링크를 받았습니다. 문서가 100페이지가 넘습니다. 어디서부터 읽어야 할지 모르겠고, 테스트 환경과 운영 환경 URL이 다른데 어디에 있는지도 찾기 어렵습니다. OpenAPI 문서에는 일정한 구조가 있어서, 핵심 항목 6개만 찾을 줄 알면 100페이지를 다 읽지 않아도 curl 테스트를 시작할 수 있습니다.

외부 API 연동 전에 API 명세서를 읽어야 합니다. OpenAPI(구 Swagger) 문서는 표준화된 API 명세 형식입니다. 문서 전체를 다 읽을 필요는 없고, 연동에 필요한 핵심 정보만 빠르게 찾는 법을 익혀두는 것으로 충분합니다.

OpenAPI 문서에서 확인할 항목:

  1. BaseURL / Servers — API 기본 주소 (스테이징 vs 운영 URL 구분)
  2. Authentication — 인증 방식 (Bearer, API Key, OAuth)
  3. Endpoint — 메서드 + 경로 (POST /v1/payments)
  4. Parameters — 경로 파라미터, 쿼리 파라미터, 필수/선택 구분
  5. Request Body — 필드명, 타입, 필수 여부
  6. Responses — 성공(200/201) 응답 스키마, 에러 응답 형식
로컬 또는 서버
# Swagger UI가 없는 경우 — OpenAPI YAML/JSON 파일 직접 확인
# 실제 연동 전 curl로 직접 검증

# ① 인증 방식 확인 후 토큰 발급
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"client_id":"ID","client_secret":"SECRET"}' \
  https://auth.example.com/token | jq .access_token

# ② 발급받은 토큰으로 실제 API 호출
TOKEN=$(curl -s -X POST ... | jq -r .access_token)
curl -s -H "Authorization: Bearer $TOKEN" https://api.example.com/v1/resource

트러블슈팅

원인: Bearer 토큰이 만료됐거나 Authorization 헤더 형식이 잘못됐습니다. 인프라 환경에서는 서버 시간 차이로 토큰이 일찍 만료되는 경우도 있습니다.

로컬 터미널
# ① 토큰 내용 확인 (JWT는 Base64 디코딩 가능)
TOKEN="eyJhbGci..."
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .exp
# exp 값을 date 명령으로 변환해 만료 시각 확인
date -d @<exp값>

# ② Authorization 헤더 형식 확인 (Bearer 앞에 공백 주의)
curl -v -H "Authorization: Bearer $TOKEN" https://api.example.com/resource 2>&1 | grep "Authorization"

# ③ 토큰 갱신 (OAuth2 refresh_token 방식)
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"grant_type":"refresh_token","refresh_token":"REFRESH_TOKEN"}' \
  https://auth.example.com/token

# ④ 서버 시간 동기화 확인 (토큰 유효성 시간 비교)
timedatectl status | grep "Local time"

원인: 요청에 Content-Type: application/json 헤더가 없거나, 잘못된 형식이 지정됐습니다. curl로 테스트할 때 가장 자주 빠뜨리는 헤더입니다.

로컬 또는 서버
# ① 현재 보내는 헤더 확인 (-v로 요청 헤더 출력)
curl -v -X POST -d '{"key":"value"}' https://api.example.com/resource 2>&1 | grep ">"
# Content-Type 줄이 없으면 헤더 누락

# ② Content-Type 헤더 추가
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"key":"value"}' \
  https://api.example.com/resource

# ③ API가 application/x-www-form-urlencoded를 요구하는 경우
curl -s -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "key=value&key2=value2" \
  https://api.example.com/resource
💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

외부 API 장애 신고가 들어왔을 때, 인프라 엔지니어가 직접 curl로 해당 엔드포인트를 찔러보는 것이 가장 빠른 1차 진단입니다.

로컬 또는 서버
# 외부 API 장애 1차 진단 루틴
# 1. 기본 연결 확인 (DNS, 네트워크)
curl -s -o /dev/null -w "%{http_code}" https://api.paymentgateway.com/health

# 2. 인증 포함 실제 요청 테스트
curl -s -H "X-API-Key: $API_KEY" \
  https://api.paymentgateway.com/v1/payments/test \
  | jq '{status: .status, message: .message}'

# 3. 응답 시간 측정 (SLA 확인)
curl -s -o /dev/null \
  -w "connect: %{time_connect}s | total: %{time_total}s\n" \
  https://api.paymentgateway.com/v1/payments/test

# 4. 상태코드별 판단:
# 200/201 → API 정상, 앱 로직 문제일 가능성
# 401/403 → 인증 정보 만료 또는 IP 화이트리스트 문제
# 500/502 → 외부 API 서버 이상, 담당사에 연락
# timeout → 네트워크 구간 문제, 방화벽 확인

이 루틴을 외울 필요는 없습니다. curl 옵션 몇 가지만 익혀두면 API 연계 문제의 70%는 코드를 보기 전에 범위를 좁힐 수 있습니다. 다음 모듈에서는 이 API 호출 앞에 위치하는 API Gateway 구조와 Webhook 연계 패턴을 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

HTTP 401과 403의 차이로 올바른 것은?

Q2

API 호출 시 Content-Type: application/json 헤더가 필요한 이유는?

Q3

curl로 POST 요청을 보낼 때 요청 바디를 포함하는 옵션은?

Q4

REST API에서 멱등성(idempotent)이 보장되는 메서드를 모두 고른 것은?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 60
[Infra Ops] 써드파티 API와 공공 인프라 연계 실무
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점