[TIL 29일 차] 좋은 웹 API 디자인이란 ~ RESTful APIs 설계와 구현

오늘의 학습

[좋은 웹 API 디자인이란]

1. API 품질의 이해

1-01. API 품질의 이해

API 품질은 단순히 잘 작동하는지를 넘어서, 얼마나 빠르게, 안정적으로, 사용하기 쉽게 동작하는 지를 평가하는 기준이다.

성능과 안정성, 사용성을 기준으로 품질을 관리한다.

  • 1-01. 성능 (Performance)
    • 성능은 API가 얼마나 빠르고 효율적으로 응답하는지를 의미
    • 대표 요소
      • 응답 시간(Response Time) : 클라이언트가 요청을 보내고 전체 응답을 수신할 때까지 걸리는 총 시간
      • 지연 시간(Latency) : 요청 이후 첫 응답 바이트가 도달하기까지 걸리는 시간
      • 처리량(Throughput) : 초당 처리 가능한 요청 수(RPS, TPS 등)
  • 1-02. 안정성 (Stability)
    • API가 예측 가능하고 지속적으로 일관된 방식으로 작동하는지를 의미
    • 대표 요소
      • 가용성(Availability) : 시스템이 정상적으로 동작하는 시간의 비율
      • 복구력(Resilience) : 장애 발생 시 자체 복구하거나 격리하는 능력
      • 장애 전파 방지(Isolation) : 특정 API나 모듈에서 발생한 장애가 전체 시스템으로 확산되지 않도록 설계
    • 안정성 확보 전략
      • 하위 호환성 유지: 기존 필드를 제거하지 않고 @Deprecated로 표시하여 유예 기간을 설정
      • 명확한 버전 관리: 새 버전 분리 운영
      • 응답 구조 고정화: 필드 순서, 존재 여부 등을 명확히 명시하여 변경으로 인한 오류 방지
    • 안정성을 해치는 변경 패턴
      • JSON 응답의 필드 순서나 이름이 예고 없이 바뀜
      • 기존 필드/파라미터 삭제
      • 기존에는 선택이던 값을 갑자기 필수로 변경
  • 1-03. 사용성 (Usability)
    • 직관적이며 개발자가 이해하고 사용하기 쉬운 정도를 의미
    • 핵심 요소: RESTful한 URI 설계, 명확한 응답 포맷, 충실한 문서화
    • 사용성 향상을 위한 문서화 전략
      • 자동화된 API 명세 생성을 위한 Swagger (OpenAPI) 도입
      • 각 API 요청/응답에 대한 샘플 제공
      • 필드 별 설명 및 제약 조건 명시 (nullable, maxLength, default 등)
    • 대표 요소
      • 일관성(Consistency) : RESTful 관점에서 명확한 기준이 있어야 함
      • 문서화(Documentation) : API 문서 자동화 및 명확한 정리
      • 에러 메시지의 명확성


1-02. API 품질을 지키기 위한 운영 포인트

1) 응답 시간 모니터링

  • AOP를 활용한 응답 시간 측정
  • 외부 모니터링 툴 활용 : Prometheus, Grafana, Spring Boot Actuator


2) 에러 처리 표준화

  • 전역 예외 처리 방식
    • @ControllerAdvice@ExceptionHandler를 조합하여 에러를 일관되게 처리


3) 사용량 체크 기능

특정 조건(예: IP 기준)을 통해 호출을 제한해서 과도한 트래픽/남용을 막는 장치

  • Redis를 사용해 분산 RateLimiter 구현 가능 (Redisson, Lua Script)
  • Bucket4j 또는 Spring Gateway Filter를 사용하면 더 정교한 제한 정책 적용 가능


1-03. 품질 관련 문서화

  • 성능 기대치 명시
    • 자주 쓰는 지표
      • 평균 응답: 보통 속도
      • p95: 100번 중 95번은 이 안에 끝남
      • p99: 100번 중 99번은 이 안에 끝남
      • 최대 처리량: 1초에 몇 건 버티나
      • 가용성: 한 달 중 정상 동작 비율
  • 제안사항 안내
    • 과도한 트래픽·오류 요청을 사전에 차단해 서버 자원을 보호.
    • 대표 제한 항목
      • 최대 본문 크기 ➡️ 413 Payload Too Large
      • Rate Limit(IP) ➡️ 429 + Retry‑After: 60
      • 필드 길이 ➡️ 400 FIELD_TOO_LONG
      • 지원 타입 ➡️ 415 Unsupported Media
  • 모범 사례 제시
    • 성공 응답만 있으면 실제 연동 단계에서 오류·재시도·백오프 로직을 구현할 근거가 부족
    • 모든 흐름을 보여 주어야 개발‧QA가 길을 잃지 않음

[RESTful API 설계 원칙]

2. REST의 이해와 실제 활용

2-01. REST의 탄생 배경과 개념

정적 HTML ➡️ CGI/Perl로 동적 페이지 시작

1) 웹 확장성을 향한 고민

  • CGI의 한계
    • 요청마다 새로운 OS 프로세스 생성 ➡️ 병목 발생 ➡️ 프로세스 폭발
    • 상태(State)를 서버 세션에 저장 ➡️ 요청이 항상 동일한 서버로 가야 하는 문제 발생 ➡️ Sticky-Session(세션 고정) 필요
    • 특히 웹은 트래픽이 급격히 뛰기 때문에, 점차 수평 확장(로드밸런싱)을 전제로 한 설계가 중요해짐
  • 확장성(Scalability)
    • Vertical Scaling (수직 확장) : 한 대의 서버에 CPU, 메모리 등 자원을 추가하여 성능을 높임
      • 장점: 구조 변경 없이 빠르게 대응 가능
      • 단점: 비용 매우 높고, 물리적 한계 존재 (예: 64코어 이상 장비는 매우 비쌈)
    • Horizontal Scaling (수평 확장) : 서버 수를 늘려 로드를 분산하는 방식 (웹 서비스의 표준)
      • 장점: 트래픽 증가 시 노드 추가만으로 대응 가능
      • 단점: 세션/상태 유지 전략 필요 ➡️ (REST가 해법 제시: 무상태성이 중요)


2) HTTP와 웹의 성공 요인

  • 단순성(Simplicity)
    • 단 한 줄로 리소스를 요청할 수 있음
  • 느슨한 결합(Loose Coupling)
    • Self‑descriptive Messages
      • HTTP 메시지 하나만으로도 요청의 의도와 구조를 완전히 설명할 수 있음
    • 하위 호환성
      • 기본 HTTP 구조는 수십 년간 거의 바뀌지 않았음에도 여전히 잘 작동
  • 캐시(Cache)
    • HTTP는 응답을 그대로 재사용할 수 있도록 캐시 메커니즘(예: 캐시 제어 헤더)을 기본으로 제공
    • E-Tag + If-None-Match 헤더로 리소스 변경 여부를 빠르게 확인


2-02. REST의 주요 제약조건

모든 제약조건을 지키는 것은 이상적이지만 현실적 제약(시간, 복잡성) 때문에 Level 2가 실무 표준인 경우가 많다.

1) Client-Server - 역할 분리로 얻는 이점

REST 아키텍처의 가장 기본적인 전제로, 클라이언트와 서버의 역할을 명확히 분리하는 것

  • 서로가 관심사(Concern)를 분리함으로써, 독립적인 개발과 배포가 가능해지고, 유지보수성도 향상


2) Stateless - 무상태성으로 수평 확장

모든 요청은 독립적이다. 즉, 서버는 이전 요청 상태(Session)를 저장하지 않는다.

  • 필요한 상태는 클라이언트가 매 요청마다 전달
  • 수평 확장이 가능해짐


3) Cacheable - 네트워크 부하 감소

서버 응답에는 반드시 캐시 가능 여부를 명시

  • 정적 리소스는 max-age를 길게, 동적 리소스는 ETag + must-revalidate 조합을 사용.


4) Uniform Interface - 일관성 있는 리소스 접근

REST의 핵심 개념으로, 모든 리소스에 일관된 방식으로 접근할 수 있도록 규정

  • 서버와 클라이언트 간의 결합도를 낮추고, 확장성과 유지보수성을 높임.

  • 4대 구성 규칙

    • Resource Identification in URIs
      • 모든 리소스는 고유한 URI로 식별해야 함
    • Manipulation via Representations
      • 리소스는 JSON, XML 같은 표현(Representation) 형태로 전달됨
    • Self-descriptive Messages
      • 요청/응답 메시지는 자체 설명력을 가져야 함. (헤더, 상태코드, 콘텐츠타입 포함)
    • Hypermedia as the Engine of Application State (HATEOAS)
      • 클라이언트가 서버로부터 받은 응답 내 링크(Hyperlink)를 통해 다음 행동을 결정
      • 적용하기 매우 어렵기 때문에 대부분 실무에서 생략됨


5) Layered System - 계층형 아키텍처

여기서 계층은 Controller–Service–Repository 같은 코드 레이어가 아니라, 주로 네트워크/배포 관점에서 요청이 거치는 중간 구성요소(중개자, intermediary) 레이어

REST 시스템은 여러 계층으로 나눌 수 있는 구조를 기본 전제로 하는데, 클라이언트는 중간 계층이 존재하더라도 최종 서버와 통신하는 것처럼 동작할 수 있음

  • 즉, 중간에 뭐가 몇 개 끼어 있든(CDN, 프록시, 게이트웨이, 로드밸런서, WAF 등) “서버랑 통신하는 것처럼” 동일한 방식(HTTP 규칙)으로 요청


2-03. 실용적인 REST

1) Richardson 성숙도 모델

REST API를 작성할 때는 몇 가지 지켜야 할 규칙

  • Level 0 : HTTP 사용 - RPC 스타일, 하나의 엔드포인트
    • 단순히 HTTP 프로토콜을 사용한 경우로, 이 경우 REST API라고 할 수는 없음.
  • Level 1: 개별 리소스와의 통신 준수 - 리소스 URI 사용
    • 모든 자원은 개별 리소스에 맞는 엔드포인트(Endpoint)를 사용
    • 요청하고 받은 자원에 대한 정보를 응답으로 전달해야 한다는 것
  • Level 2: HTTP 메서드 원칙 준수
    • CRUD에 맞게 적절한 HTTP 메서드를 사용하는 것에 중점
  • Level 3: HATEOAS 원칙 준수
    • HATEOAS(Hypertext As The Engine Of Application State)라는 하이퍼미디어 컨트롤을 적용
    • 요청은 Level 2와 동일하지만, 응답에는 리소스의 URI를 포함한 링크 요소를 삽입하여 작성

실제로 엄밀하게 3단계까지 지키기 어렵기 때문에 2단계까지만 적용해도 좋은 API 디자인이라고 볼 수 있고, 이런 경우 HTTP API라고 부름


2) 실용적 REST API 설계 시 체크리스트

1️⃣ 비즈니스 범위 파악
└ B2B / B2C / 내부 전용 / 공개 플랫폼 등
2️⃣ 클라이언트 환경 확인
└ 웹, 모바일, IoT, 대시보드 등
3️⃣ 데이터 특성 확인
└ 최신성 중요? 실시간성 필요? 일관성 얼마나 중요?
4️⃣ 보안 요건 정리
└ 인증/인가 필요 여부, 민감 정보 포함 여부
5️⃣ REST 제약조건 준수 수준 결정 ➡️ 안 봐도 됨. 무조건 Level 2!!
└ Level 1 / Level 2 / Level 3 중 선택


2-04. API-First 접근 방식

요구사항을 정리한 뒤, 프론트엔드·백엔드 구현보다 먼저 API 스펙(OpenAPI 등)을 ‘계약’으로 정의하고 공유하는 방식

  • API 명세를 일종의 ‘계약서’처럼 먼저 정의
  • 백엔드/프론트엔드가 동시에 병렬로 개발 가능
  • Swagger/OpenAPI를 활용하여 문서 자동화, 테스트, 모킹(Mock) 서버 생성 가능
    • Swagger(springdoc)은 기본적으로 구현 후 문서 자동 생성하는 코드-퍼스트
    • Spring REST Docs는 “테스트로 실제 요청/응답을 만들고 그 결과로 문서를 생성”하는 방식이라, 테스트가 선행되어야 문서가 만들어짐

3. RESTful API 설계 원칙

3-01. 리소스 중심 API 설계

  • 리소스란?
    • 고유한 URI로 식별되는 서버 측의 데이터 개체 또는 서비스 대상
    • REST 아키텍처에서는 모든 것을 리소스로 간주함

1) 계층형 리소스 설계

  • 부모-자식 리소스 모델링
    • 리소스 간 관계가 상위-하위(부모-자식) 구조를 이룰 때, URI에도 이를 명확히 표현하는 것이 REST 설계 원칙
    • 계층형 URI가 필요한 조건
      • 하위 리소스가 상위 리소스의 컨텍스트에 종속되어 있을 때 사용
  • 독립 URI 병행 전략
    • 하위 리소스가 전역적으로 의미를 가지거나, 여러 화면에서 재사용된다면 계층 URI + 전역 URI를 병행하는 경우


2) Query Parameter vs Path Variable

  • Path Variable
    • 고유 리소스의 식별자
  • Query Parameter
    • 검색, 필터링, 정렬, 페이징 등 옵션성 조건


3) 리소스 식별자 설계 전략

  • 리소스 식별자(Resource Identifier): RESTful API에서 각 리소스를 고유하게 식별하는 값
    • URI 안에서 주로 Path Variable 형태로 나타냄
  • 식별자 유형
    • 숫자 기반(PK)
    • UUID
    • 자연키(Slug, Username)
    • Hash ID, Base62, Base64URL
    • 실제 서비스에서는 내부 식별자(PK)와 외부 노출 식별자를 구분하여 관리하는 것이 일반적
  • 실무 설계 전략
    • 내부/외부 ID 분리
    • 불변 식별자 사용
    • 정렬 고려 시 Snowflake ID
    • 예측 불가능성 확보
    • 노출 ID는 검색 가능하게 설계


3-02. HTTP 프로토콜의 효과적인 활용

1) 상태 코드의 적절한 사용

성공 응답에 2xx, 리다이렉트에 3xx, 클라이언트 오류에 4xx, 서버 오류에 5xx


2) HTTP 헤더 활용

  • Content‑Type / Accept
    • Content‑Type: 요청 본문 타입
    • Accept: 클라이언트가 원하는 응답 타입
  • Authorization
    • 인증 토큰 전달
    • HTTPS 필수 ➡️ 토큰 탈취 방지
  • Cache‑Control / ETag
    • 캐싱 고급 활용
    • ETag: "..." : 응답의 버전 식별자
    • Cache-Control: max-age=3600, public : 응답을 최대 3600초(1시간) 동안 캐시 가능하고, 모든 사용자(public)가 공유 가능


3-03. API 응답 설계

1) 일관된 응답 구조

  • 중요성
    • 응답 구조가 일관되지 않으면 클라이언트 개발자는 API를 사용할 때마다 응답을 해석하는 방식이 달라져 생산성과 유지보수성이 떨어짐
    • 성공과 실패 응답에 대한 명확하고 일관된 응답 포맷이 필요
  • Swagger 문서나 RestDocs에도 이 공통 응답 구조를 반영하면 API 문서의 신뢰도가 올라감


2) 성공/실패 응답 형식

  • 성공 응답의 일관성 확보
    • 모든 성공 응답은 "success": true와 함께 data 필드를 포함
    • 예외적으로 단순 성공 메시지(API 생성 등)에도 data는 항상 존재해야
    • 회원가입처럼 단순 ID만 응답하더라도 data를 생략하면 안 됨 ➡️ 항상 JSON 구조는 동일하게 유지
  • 실패 응답의 일관성 확보
    • 모든 실패 응답은 "success": false와 함께 error 필드를 포함해야 하며 datanull로 설정


3) 에러 처리 원칙

  • HTTP 상태 코드와 내부 에러 코드 분리
    • 회원가입 성공:
      • HTTP 상태 코드: 201 Created
      • 내부 에러 코드: 없음
    • 로그인 실패:
      • HTTP 상태 코드: 401 Unauthorized
      • 내부 에러 코드: AUTH_FAILED
    • 필수값 누락:
      • HTTP 상태 코드: 400 Bad Request
      • 내부 에러 코드: VALIDATION_ERROR
    • 서버 장애:
      • HTTP 상태 코드: 500 Internal Server Error
      • 내부 에러 코드: INTERNAL_ERROR
  • @ExceptionHandler 적용

Categories:

Updated:

Leave a comment