본문으로 바로가기
기술

RESTful API 설계 원칙과 실전 가이드 — 좋은 API의 조건

A
AlwaysCorp 기술팀· 기술·개발 콘텐츠 전문
||11분 읽기
#API#REST#RESTful#API설계#HTTP#백엔드#웹개발#상태코드#CRUD#인증#JWT#페이지네이션#버저닝

REST란 무엇인가

REST의 기원

REST(Representational State Transfer)는 2000년 로이 필딩(Roy Fielding)의 박사 논문에서 처음 정의된 소프트웨어 아키텍처 스타일입니다. 필딩은 HTTP/1.1 명세의 주요 저자이기도 하며, REST는 웹의 기존 인프라(HTTP, URI, 콘텐츠 타입)를 그대로 활용하는 것이 핵심 아이디어입니다.

REST의 6가지 제약 조건

  1. 클라이언트-서버 분리: UI와 데이터 저장을 분리하여 독립적 발전 가능
  2. 무상태(Stateless): 각 요청이 필요한 모든 정보를 포함, 서버에 세션 상태 저장 안 함
  3. 캐시 가능(Cacheable): 응답에 캐시 가능 여부를 명시
  4. 계층형 시스템: 클라이언트는 중간 서버(로드밸런서, CDN)의 존재를 알 필요 없음
  5. 통일된 인터페이스(Uniform Interface): 일관된 규칙으로 리소스에 접근
  6. 주문형 코드(Code on Demand): (선택) 서버가 클라이언트에 실행 가능한 코드 전송

---

URI 설계 규칙

리소스 명명 원칙

URI는 리소스(명사)를 표현하며, 행위(동사)는 HTTP 메서드로 표현합니다.

``` ✅ 올바른 URI 설계: GET /api/users → 사용자 목록 조회 GET /api/users/123 → ID 123 사용자 조회 POST /api/users → 새 사용자 생성 PUT /api/users/123 → ID 123 사용자 전체 수정 PATCH /api/users/123 → ID 123 사용자 부분 수정 DELETE /api/users/123 → ID 123 사용자 삭제

❌ 잘못된 URI 설계: GET /api/getUsers → 동사 사용 ❌ POST /api/createUser → 동사 사용 ❌ GET /api/user/delete/123 → HTTP 메서드 무시 ❌ ```

URI 설계 규칙 상세

1. 복수형 명사 사용 ``` ✅ /api/users, /api/articles, /api/categories ❌ /api/user, /api/article ```

2. 계층 관계는 중첩 경로로 표현 ``` GET /api/users/123/orders → 사용자 123의 주문 목록 GET /api/users/123/orders/456 → 사용자 123의 주문 456 ``` 단, 중첩이 3단계 이상이면 복잡해지므로 쿼리 파라미터를 고려합니다.

3. 소문자 + 하이픈 사용 ``` ✅ /api/blog-posts, /api/user-profiles ❌ /api/blogPosts, /api/blog_posts ```

4. 파일 확장자 사용 금지 ``` ✅ Accept: application/json 헤더로 응답 형식 지정 ❌ /api/users.json, /api/users.xml ```

---

HTTP 메서드의 올바른 사용

메서드별 의미와 특성

| 메서드 | 용도 | 안전 | 멱등 | 요청 Body | |--------|------|------|------|-----------| | GET | 조회 | ✅ | ✅ | 없음 | | POST | 생성 | ❌ | ❌ | 있음 | | PUT | 전체 교체 | ❌ | ✅ | 있음 | | PATCH | 부분 수정 | ❌ | ❌ | 있음 | | DELETE | 삭제 | ❌ | ✅ | 없음/있음 |

안전(Safe): 서버 상태를 변경하지 않음 멱등(Idempotent): 같은 요청을 여러 번 보내도 결과가 동일

PUT vs PATCH

``` PUT /api/users/123 → 리소스 전체를 교체. 보내지 않은 필드는 null/기본값으로 초기화

PATCH /api/users/123 → 지정한 필드만 수정. 나머지 필드는 유지 ```

실무에서는 대부분의 수정 작업이 부분 수정이므로 PATCH를 더 자주 사용합니다.

HTTP 메서드 요약 차트 — GET/POST/PUT/PATCH/DELETE 안전성과 멱등성
HTTP 메서드 요약 차트 — GET/POST/PUT/PATCH/DELETE 안전성과 멱등성

---

HTTP 상태 코드 가이드

2xx — 성공

  • 200 OK: 일반적 성공 (GET, PATCH, DELETE)
  • 201 Created: 리소스 생성 성공 (POST). Location 헤더에 새 리소스 URI 포함
  • 204 No Content: 성공했으나 응답 본문 없음 (DELETE 후)

3xx — 리다이렉션

  • 301 Moved Permanently: URI가 영구적으로 변경됨
  • 304 Not Modified: 캐시된 리소스가 아직 유효함 (조건부 GET)

4xx — 클라이언트 에러

  • 400 Bad Request: 잘못된 요청 형식, 유효성 검사 실패
  • 401 Unauthorized: 인증 필요 (토큰 없음 또는 만료)
  • 403 Forbidden: 인증됐으나 권한 부족
  • 404 Not Found: 리소스가 존재하지 않음
  • 409 Conflict: 현재 리소스 상태와 충돌 (중복 생성 등)
  • 422 Unprocessable Entity: 형식은 맞으나 의미적으로 처리 불가
  • 429 Too Many Requests: 요청 속도 제한 초과

5xx — 서버 에러

  • 500 Internal Server Error: 서버 내부 오류 (예상치 못한 예외)
  • 502 Bad Gateway: 업스트림 서버 응답 오류
  • 503 Service Unavailable: 서버 점검 또는 과부하
REST API 상태 코드 분류표 — 2xx/3xx/4xx/5xx 주요 코드 정리
REST API 상태 코드 분류표 — 2xx/3xx/4xx/5xx 주요 코드 정리

---

에러 응답 표준화

일관된 에러 응답 형식

```json { "error": { "code": "VALIDATION_ERROR", "message": "요청 데이터가 유효하지 않습니다.", "details": [ { "field": "email", "message": "유효한 이메일 주소를 입력해주세요." }, { "field": "age", "message": "나이는 0 이상이어야 합니다." } ] } } ```

에러 응답 설계 원칙

  1. 기계가 읽을 수 있는 코드: `VALIDATION_ERROR`, `NOT_FOUND` 등 열거형 코드
  2. 사람이 읽을 수 있는 메시지: 개발자가 이해할 수 있는 설명
  3. 필드별 상세 정보: 유효성 검사 실패 시 어떤 필드가 왜 실패했는지
  4. 보안 주의: 내부 스택 트레이스, DB 스키마 정보를 에러에 노출하지 않기

---

페이지네이션

오프셋 기반 페이지네이션

``` GET /api/articles?page=2&limit=20 ```

응답: ```json { "data": [...], "pagination": { "page": 2, "limit": 20, "total": 156, "totalPages": 8 } } ```

장점: 구현 간단, 임의 페이지 접근 가능 단점: 데이터 추가/삭제 시 누락·중복 발생 가능, 큰 오프셋에서 성능 저하

커서 기반 페이지네이션

``` GET /api/articles?cursor=eyJpZCI6MTAwfQ&limit=20 ```

응답: ```json { "data": [...], "pagination": { "nextCursor": "eyJpZCI6MTIwfQ", "hasMore": true } } ```

장점: 실시간 데이터에서 누락·중복 없음, 일관된 성능 단점: "N번째 페이지로 이동" 불가, 구현 복잡도 증가

실무 권장: 관리자 패널(표 형태) → 오프셋, 타임라인·피드(무한 스크롤) → 커서

---

API 버저닝

버저닝 전략 비교

1. URI 버저닝 (가장 흔함) ``` /api/v1/users /api/v2/users ``` 장점: 직관적, 브라우저에서 바로 테스트 가능 단점: URI가 변경되므로 REST 순수주의자가 반대

2. 헤더 버저닝 ``` GET /api/users Accept: application/vnd.myapp.v2+json ``` 장점: URI 깔끔 단점: 테스트가 어렵고, 캐싱 설정 복잡

3. 쿼리 파라미터 버저닝 ``` GET /api/users?version=2 ```

실무 권장: 대부분의 대규모 API(Google, GitHub, Stripe)가 URI 버저닝을 사용합니다. 단순하고 명확하다는 것이 가장 큰 장점입니다.

---

인증과 보안

JWT 기반 인증

현재 REST API에서 가장 널리 사용되는 인증 방식입니다.

``` Authorization: Bearer eyJhbGciOiJIUzI1NiIs... ```

Access Token + Refresh Token 패턴:

  • Access Token: 짧은 만료 시간(15분~1시간), API 요청 시 사용
  • Refresh Token: 긴 만료 시간(7~30일), Access Token 갱신 시 사용
  • Refresh Token은 httpOnly 쿠키에 저장하여 XSS 방어

API 보안 체크리스트

  1. HTTPS 필수: 모든 API 통신은 TLS 암호화
  2. Rate Limiting: IP/사용자별 요청 속도 제한 (429 응답)
  3. 입력 검증: 서버 측에서 모든 입력 데이터 검증 (클라이언트 검증만으로 불충분)
  4. CORS 설정: 허용된 Origin만 API 접근 가능
  5. SQL Injection 방지: ORM 사용 또는 파라미터화된 쿼리
  6. 민감 데이터 마스킹: 로그에 비밀번호, 토큰 등 기록 금지

---

좋은 API 설계의 최종 체크리스트

  • [ ] URI는 리소스(명사)를 표현하고, 행위는 HTTP 메서드로 표현
  • [ ] 적절한 HTTP 상태 코드 사용 (200만 반환하지 않기)
  • [ ] 일관된 에러 응답 형식 (code + message + details)
  • [ ] 페이지네이션 제공 (대량 데이터 응답 방지)
  • [ ] 버저닝 전략 적용
  • [ ] HTTPS + 인증 + Rate Limiting 보안 적용
  • [ ] API 문서화 (OpenAPI/Swagger) 제공
좋은 API는 예측 가능하고, 일관적이며, 자기 설명적입니다. 개발자가 문서를 최소한으로 참고하면서도 직관적으로 사용할 수 있어야 합니다.
A

AlwaysCorp 기술팀

기술·개발 콘텐츠 전문

얼웨이즈 블로그에서 유용한 정보와 인사이트를 공유합니다.