RESTful API 설계 간단 가이드

김준기·2025년 6월 19일

RESTful이란 무엇인가

RESTful API는 HTTP 프로토콜을 기반으로 REST 아키텍처 원칙에 맞게 설계된 API를 말한다.
자원(Resource)은 고유한 URI로 식별되며, HTTP 메서드를 통해 자원에 접근하거나 상태를 변경할 수 있다. 이때 서버는 상태를 유지하지 않는 stateless한 구조를 따른다.

REST 원칙은 무엇인가

REST는 특정한 기술이나 규격이 아니라, 웹이라는 환경에 적합한 시스템을 구성하기 위한 일련의 아키텍처 스타일이다.

REST의 6가지 제약조건을 간단히 정리하면:

  • Uniform Interface: 일관된 인터페이스 (URI, HTTP Method 등)
  • Stateless: 서버가 클라이언트 상태를 저장하지 않음
  • Cacheable: 응답을 캐시할 수 있어야 함
  • Client-Server: 클라이언트와 서버의 역할 분리
  • Layered System: 계층화된 시스템 구조
  • Code on Demand: 필요시 서버가 클라이언트에 코드 전송 (선택사항)

다시 말해, 몇 가지 원칙의 집합이며, 이 원칙들을 잘 지켜서 설계된 시스템을 RESTful하다고 표현한다.


나는 왜 REST 원칙을 쓸까 생각해봤다

이런 정의들만으로는 이해가 잘 되지 않았다.
그래서 내마음대로 요약하자면, REST 원칙은 결국 URI와 Method만으로 어떤 작업을 수행하는지 명확하게 표현하기 위한 방법이라고 생각한다.

REST는 "URL로 자원을 명시하고, HTTP Method로 자원의 상태를 조작하는 방식"을 따른다.
이 방식이 잘 지켜졌다고 판단되는 경우, RESTful하다고 표현한다.


RESTful한 설계는 어떻게 해야 할까

REST 원칙을 HTTP 상에서 적용하면, 보통 다음과 같은 식으로 표현하게 된다.

  • URL은 자원을 나타낸다.
  • Method는 동작(조회, 생성, 수정, 삭제 등)을 의미한다.
  • 상태나 오류는 HTTP 상태 코드로 표현한다.

하지만 모든 API가 이런 구조로만 표현될 수 있는 것은 아니다.

예를 들어 로그인과 로그아웃 API를 생각해보자.
보통 아래처럼 많이 설계되어 있다.


POST /api/v1/auth/login
POST /api/v1/auth/logout

하지만 이는 정확히 말하면 REST 원칙을 따른 구조는 아니다.
만약 세션 기반 인증이라면 아래처럼 표현할 수도 있다.


POST   /api/v1/sessions          // 로그인 → 세션 생성
DELETE /api/v1/sessions/current  // 로그아웃 → 현재 세션 제거

JWT를 사용할 경우는 조금 다르다. 아래와 같이 설계할 수 있다.

POST   /api/v1/tokens                    // 토큰 생성 (로그인)
PUT    /api/v1/tokens/{jti}              // 토큰 갱신 (jti: JWT ID)
DELETE /api/v1/tokens/{jti}              // 토큰 무효화 (로그아웃)

여기서 {jti}는 JWT의 고유 식별자로, 각 토큰을 구분하는 데 사용된다.

이런 방식이 더 REST스럽다고 볼 수는 있겠지만, 실제 현업에서는 /auth/login 같은 표현이 훨씬 익숙하고 이해하기 쉬운 경우가 많다.
결국 REST의 핵심은 자원의 명확한 표현보다는, 직관적이고 예측 가능한 구조에 있다고 본다.


그럼 REST스럽게 설계하려면 어떤 점을 고려해야 할까

1) URI는 명사를 사용한다

  • /users, /books, /posts/123처럼 리소스를 명사로 표현한다.
  • 동사는 Method가 담당하므로 URI에 동사를 쓰는 것은 권장되지 않는다.

2) 계층 관계는 URI로 표현한다

  • /users/123/devices처럼 소속 관계를 명확히 표현할 수 있다.

3) 소문자와 하이픈을 사용한다

  • 예: /user-profile, /book-reviews
  • 언더스코어보다 하이픈이 더 일반적으로 쓰인다.

4) URI 끝에는 슬래시(/)를 붙이지 않는다

  • REST 관점에서는 리소스와 행위 구분이 명확해야 하므로, /users//users를 다르게 취급하지 않는 것이 좋다.

5) URI에 파일 확장자를 쓰지 않는다

  • /users/123.json이나 /posts/456.xml 같은 형태는 피한다.
  • 응답 포맷은 Accept 헤더나 Content-Type으로 구분한다.
  • 단, 사용자가 업로드한 실제 파일을 서빙하는 경우(/uploads/image.jpg)는 예외해도 된다. 이 경우 파일 확장자가 파일의 실제 속성을 나타내므로 자연스럽고 직관적이다.

6) HTTP Method를 목적에 맞게 사용한다

메서드의미
GET조회
POST생성
PUT전체 수정
PATCH일부 수정
DELETE삭제
  • PUT은 전체를 교체하는 의미, PATCH는 일부 필드만 수정할 때 사용한다.

7) 필터, 정렬, 페이징은 쿼리 파라미터로 처리한다

GET /items?status=active&sort=-createdAt&page=2&size=20
  • REST에서 자원의 조회 조건은 URI가 아닌 파라미터로 표현하는 것이 일반적이다.

8) 오류 응답은 일관된 형태로 전달한다

  • HTTP 상태 코드와 함께 JSON(또는 정해진 포맷)으로 오류 정보를 제공한다.
{
  "code": "INVALID_INPUT",
  "message": "입력한 값이 올바르지 않습니다.",
  "details": {
    "email": "이메일 형식이 아닙니다."
  }
}
  • 상태 코드 예시

    • 400: 잘못된 요청
    • 401: 인증 필요
    • 403: 권한 없음
    • 404: 자원 없음
    • 409: 충돌
    • 500: 서버 오류

9) 경우에 따라 HATEOAS를 사용할 수도 있다

{
  "id": 123,
  "name": "John",
  "links": {
    "self": "/users/123",
    "orders": "/users/123/orders"
  }
}
  • 응답에 포함된 링크를 통해 클라이언트가 다음 행동을 예측할 수 있도록 도와주는 방식이다. 하지만 실무에서는 구현 복잡도에 비해 얻는 이익이 크지 않아서 잘 사용되지 않는 편이다. 대신 API 문서나 스펙으로 관계를 명시하는 경우가 더 일반적이다.

정리하면서

RESTful은 어떤 특정 기술이나 문법이 아니라, 전체적인 표현 방식과 설계 철학이다.
그래서 ‘정답’은 없고, 가능한 원칙을 잘 지키면서도 팀이나 조직에 익숙하고 예측 가능한 형태를 선택하는 것이 중요하다.

로그인처럼 REST로 표현하기 애매한 케이스도 존재한다. 이럴 땐 ‘명확하고 예측 가능하게 표현되었는가?’를 기준으로 판단하면 좋다고 생각한다.


profile
코딩 잘하고 싶은 백엔드 개발자

0개의 댓글