Waylog Blog

API 설계 모범 사례: RESTful API부터 GraphQL까지

Backend

API Design

API(Application Programming Interface)는 소프트웨어 시스템 간의 계약입니다. 잘 설계된 API는 사용하기 쉽고, 확장하기 편하며, 오래 유지됩니다. 이 글에서는 실무에서 검증된 API 설계 원칙과 패턴들을 살펴봅니다.

1. RESTful API 설계 원칙

1.1 리소스 중심 설계

REST의 핵심은 리소스(Resource)입니다. URL은 리소스를 나타내고, HTTP 메서드로 행위를 표현합니다. "/users"는 사용자 컬렉션을, "/users/123"은 특정 사용자를 나타냅니다. "/getUser"나 "/createUser" 같은 동사 기반 URL은 피하세요.

컬렉션은 복수형, 단일 리소스는 식별자로 구분합니다. 중첩 리소스는 계층 관계를 표현합니다. "/users/123/orders"는 특정 사용자의 주문 목록을 의미합니다. 단, 중첩이 깊어지면 복잡해지므로 2단계 정도로 제한하는 것이 좋습니다.

1.2 HTTP 메서드 활용

GET은 리소스 조회에, POST는 생성에, PUT은 전체 교체에, PATCH는 부분 수정에, DELETE는 삭제에 사용합니다. 각 메서드의 의미를 정확히 따르세요.

GET과 DELETE는 멱등성(Idempotent)을 가집니다. 같은 요청을 여러 번 보내도 결과가 동일합니다. PUT도 멱등합니다. POST는 멱등하지 않습니다. 같은 요청을 두 번 보내면 리소스가 두 개 생성될 수 있습니다.

1.3 상태 코드 활용

적절한 HTTP 상태 코드를 반환하세요. 200 OK는 성공, 201 Created는 리소스 생성, 204 No Content는 성공했지만 반환할 본문이 없을 때 사용합니다. 400 Bad Request는 클라이언트 오류, 401 Unauthorized는 인증 실패, 403 Forbidden은 권한 없음, 404 Not Found는 리소스 없음, 409 Conflict는 충돌, 500 Internal Server Error는 서버 오류입니다.

상태 코드만으로 부족하면 응답 본문에 상세한 오류 정보를 포함하세요. 에러 코드, 메시지, 상세 설명, 해결 방법 등을 구조화된 형식으로 제공합니다.

2. 페이지네이션과 필터링

2.1 페이지네이션

대량의 데이터를 한 번에 반환하면 성능과 사용성 모두 저하됩니다. 오프셋 기반 페이지네이션은 page와 limit 파라미터를 사용합니다. 구현이 간단하지만, 데이터가 추가/삭제될 때 결과가 일관되지 않을 수 있습니다.

커서 기반 페이지네이션은 마지막 항목의 ID를 커서로 사용합니다. 일관된 결과를 보장하고 대용량 데이터에 효율적이지만, 구현이 복잡합니다. 다음 페이지 URL을 응답에 포함시키면 클라이언트가 페이지네이션 로직을 몰라도 됩니다.

2.2 필터링과 정렬

쿼리 파라미터로 필터 조건을 받습니다. "/users?status=active&role=admin"처럼 단순한 등호 조건부터, 범위 조건, 검색어 등 다양한 필터를 지원할 수 있습니다. 복잡한 필터는 별도의 문법(예: Ops 접미사, "/users?age_gte=18")을 정의하거나, POST 요청으로 JSON 필터를 받는 방법도 있습니다.

정렬은 sort 파라미터로 처리합니다. "/users?sort=created_at"은 오름차순, "/users?sort=-created_at"은 내림차순(마이너스 접두사)으로 하는 것이 일반적입니다.

3. 버전 관리

API는 시간이 지나면 변경됩니다. 기존 클라이언트를 깨뜨리지 않으면서 새로운 기능을 추가하려면 버전 관리가 필요합니다.

URL 경로에 버전을 포함하는 방법("/v1/users")이 가장 명확하고 널리 사용됩니다. 헤더(Accept: application/vnd.example.v1+json)를 사용하는 방법도 있지만, 테스트하기 번거롭습니다. 쿼리 파라미터("?version=1")는 캐싱과 충돌할 수 있어 권장되지 않습니다.

가능하면 하위 호환성을 유지하세요. 필드 추가는 보통 안전합니다. 필드 제거나 타입 변경은 새 버전을 만들어야 합니다. 오래된 버전은 일정 기간 유지하고, 마이그레이션 가이드를 제공한 후 종료하세요.

4. 인증과 보안

4.1 인증 방식

API Key는 가장 단순하지만 보안이 취약합니다. 헤더나 쿼리 파라미터로 전송합니다. OAuth 2.0은 표준화된 인증 프레임워크로, 액세스 토큰과 리프레시 토큰을 사용합니다. JWT(JSON Web Token)는 토큰 자체에 정보를 담아 서버에서 세션을 유지할 필요가 없습니다.

4.2 보안 모범 사례

HTTPS는 필수입니다. 요청과 응답을 암호화하여 중간자 공격을 방지합니다. Rate Limiting으로 과도한 요청을 차단하세요. 시간당 요청 수를 제한하고, 429 Too Many Requests를 반환합니다.

민감한 데이터는 응답에 포함하지 않거나 마스킹하세요. 비밀번호는 절대 반환하지 않습니다. 입력값은 항상 검증하고, SQL 인젝션, XSS 등 일반적인 공격을 방어하세요. CORS(Cross-Origin Resource Sharing) 설정도 정확히 하세요.

5. GraphQL 고려하기

REST가 유일한 선택지는 아닙니다. Facebook이 개발한 GraphQL은 클라이언트가 필요한 데이터만 정확히 요청할 수 있게 합니다.

특징RESTGraphQL
데이터 페칭고정된 구조, 오버/언더페칭 발생 가능클라이언트가 필요한 필드만 요청
엔드포인트리소스별로 다수 존재 (/users, /posts)단일 엔드포인트 (/graphql)
버전 관리URL v1, v2 등으로 분리스키마 진화(Evolution)로 단일 버전 유지
캐싱HTTP 캐싱 활용 용이별도 설정 필요 (Apollo Client 등)

스키마를 정의하면 타입 시스템의 이점을 누릴 수 있습니다. Introspection으로 API 문서가 자동 생성됩니다. BFF(Backend for Frontend) 패턴과 함께 사용하면 마이크로서비스 환경에서 강력한 힘을 발휘합니다.

6. 문서화

좋은 API도 문서가 없으면 사용하기 어렵습니다. OpenAPI(Swagger) 명세를 작성하면, 문서 사이트를 자동 생성하고, 클라이언트 SDK를 생성하고, 테스트할 수 있습니다.

예제 요청과 응답을 충분히 제공하세요. 에러 케이스도 문서화하세요. 인증 방법, rate limit, 버전 정책 등 운영 정보도 포함하세요. 코드와 문서가 일치하도록 CI에서 검증하는 것도 좋습니다.

결론

API 설계는 한 번 하고 끝나는 것이 아닙니다. 클라이언트의 요구는 변하고, 시스템은 성장합니다. 일관된 원칙을 따르되 유연하게 대응하세요. 가장 중요한 것은 API를 사용하는 개발자의 경험입니다. 그들이 쉽고 즐겁게 사용할 수 있는 API가 좋은 API입니다.