Packet? Header? RESTful? REST API?

콜트·2021년 1월 19일
10

라이징 프로그래머2

목록 보기
13/18

라이징 프로그래머2의 4주차 과제가 시작되었다. 이번 과제 중 일부인 REST API의 설계 및 구현을 위해 공부한 내용을 정리해보았다.

패킷, 헤더, 바디, HTTP 프로토콜 방식, XML & JSON

패킷, 헤더, 바디

데이터 덩어리, 데이터 뭉치를 패킷(packet)이라고 한다. 패킷은 일종의 소포상자 개념으로 보면 된다. 소포상자 위에는 라벨지가 있고, 거기에는 여러 정보들이 있다. 또한, 소포에는 진짜 물건이 있다. 이때, 이러한 메타데이터(어디로 보내는지, 누가 받는지 등등)가 들어있는 라벨지는 헤더에 해당하고 물건이 들어가는 공간은 바디에 해당한다고 생각하면 된다.

HTTP 프로토콜 방식, XML & JSON

HTTP 프로토콜

  • HTTP(Hypertext Transfer Protocol)는 인터넷상에서 데이터를 주고 받기 위한 서버/클라이언트 모델을 따르는 프로토콜이다.
  • 애플리케이션 레벨의 프로토콜로 TCP/IP위에서 작동한다.
  • HTTP는 어떤 종류의 데이터든지 전송할 수 있도록 설계돼 있다.
  • HTTP로 보낼 수 있는 데이터는 HTML문서, 이미지, 동영상, 오디오, 텍스트 문서 등 여러종류가 있다.
  • 하이퍼텍스트 기반으로(Hypertext) 데이터를 전송하겠다(Transfer) = 링크기반으로 데이터에 접속하겠다는 의미이다.
  • 데이터 전송의 방식에는 GET, POST, PUT, PATCH, DELETE 등이 있다.
    • GET : 데이터를 조회할 때 사용
    • POST : 데이터를 생성할 때 사용
    • ...

XML & JSON

예전에는 FORM 형태로 주로 데이터를 주고 받았다. 데이터의 양이 많지 않았기 때문이다. 하지만 시대가 변하면서 주고 받는 데이터가 많아지고 명세를 맞추기가 너무 어려워졌다. 그래서 등장한 것이 데이터 포맷(Data format - ex. XML, JSON)이다. 현재는 JSON이 대세! 더 가볍고 간편하다(태그가 필요없다). XML에서 데이터를 뽑아내려면 XML Parser가 필요하고 JSON에서 데이터를 뽑아내려면 JSON Parser가 필요한 것은 같지만 객체 단위 매핑이 더 쉽기 때문이다. 즉, 포장이 간편해서 먹기에 좋아졌다는 뜻이다.

쿼리스트링(query string)이라는 것이 있다(?를 붙이고 뒤에 찾고 싶은 데이터를 던지는 것). 주로 필터링을 할 때 사용한다.

API & Interface

Interface

좁게는 컴퓨터 및 소프트웨어 조작 방식을 말하며 넓게는 서로 다른 두 물체 사이에서 상호 간 대화하는 방법을 의미한다.

API란 서버와 클라이언트 사이에 상호작용을 할 수 있게 만들어주는 버튼과 같은 존재이다. 즉, 클라이언트와 서버라는 두 영역이 API라는 버튼으로 맞닿아 있고 그 버튼을 통해 서버와 클라이언트가 상호작용을 하기 때문이다.

하지만 버튼에 부여된 기능이 같아도 디자인이 다를 수 있는 것처럼, API의 기능은 같지만 프로토콜 방식은 다를 수 있다. 그래서 지속 가능한, 계속 사용할 수 있는 통일성 있는 API라는 REST API가 탄생하게 되었다. 결국, 규칙이라는 뜻이다. 이름을 짓는 방법론에 불과하다고 할 수도 있다. 마치 버튼 디자인에 룰과 같은 것이다.


REST API

1. What is REST?

RESTRepresentational State Transfer 의 약자로 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개되었다고 한다.

소프트웨어 프로그램 개발의 아키텍처의 한 형식이며, 직역하면 대표적인 상태 전달이다. 이렇게 얘기하면 한번에 와닿는 사람이 많지 않을 것이다. 그래서 쉽게 설명해보자면, 어떠한 자원을 식별하기 위해 해당 자원에 대해 고유한 URI를 부여하여 자원을 정의하고, 해당 자원을 활용하는 방법론을 의미한다고 한다.

여기서 URI와 URL은 다른 것임을 기억하자. URI는 인터넷에서 특정 자원을 나타내는 주솟값이고, URL은 인터넷에서 특정 자원이나 파일의 위치를 나타내는 주솟값이다.

그러면 대체 앞서 말한 대표적인 상태 전달이란 무엇일까? 여기서 두 가지 단어에 주목해야 한다. 대표상태 이다. 대표는 '종'에 비유해서 생각하면 쉽다. 가령 예를 들어, 포유류, 파충류 등 여러가지 종(대표)이 존재하는데, 그 중에서 포유류에 속하는 인간(자원)의 정보(상태)를 얻고 싶다고 가정하자. 여기서 인간(자원)을 대표하는 것 종의 이름은 포유류가 된다(인간은 포유류라는 종에 속하므로). 따라서 REST 아키텍처 제약 조건을 준수하면서 포유류에 속한 인간이라는 자원에 대해 고유한 URI로는 다음과 같이 표현할 수 있겠다.

포유류/인간

이처럼 자원을 이름으로 구분하고 해당 자원의 상태를 주고 받는 모든 것을 REST라고 할 수 있지만, 일반적으로 REST API라고 하면 좁은 의미로 HTTP를 통해 CRUD를 실행하는 API를 뜻한다. 웹 상에서 HTTP 프로토콜을 이용하여 URI를 통해 자원을 정의하고 HTTP Verbs(GET, POST, PUT, DELETE, PATCH)를 통해 행위를 결정하며, JSON 혹은 XML을 통해 데이터를 주고 받는다.

HTTP는 웹에서 GET, POST, PUT, DELETE 등의 메소드를 이용하여 정보를 주고 받는 프로토콜로, REST는 HTTP와 URI의 단순하고 간결한 장점을 계승한 네트워크 아키텍처이다. 따라서 다양한 요구 사항에 대응해 때로는 단순하게, 때로는 서버와 클라이언트가 서로 통신하는 리소스에 대해 복잡한 방식으로 상호작용할 수 있다.

REST가 개발된 목적은 다음과 같다.

  • 구성요소 상호작용의 규모 확장성
  • 인터페이스의 범용성
  • 구성요소의 독립적인 배포
  • 중간적 구성요소를 이용한 응답 지연 감소, 보안 강화, 레거시 시스템 캡슐화

2. REST API의 제약 조건

  1. Client - Server (클라이언트 - 서버 형태)
    클라이언트 서버 형태의 원칙은 관심사의 명확한 분리를 말하며, 이를 지킴으로써 서버와 클라이언트의 역할을 분명히 함과 동시에 서버의 구성요소가 단순화되고 확장성이 향상되어, 여러 플랫폼을 지원할 수 있게 된다.

  2. Stateless (무상태성)
    무상태성 원칙은 서버에 클라이언트의 상태 정보를 일절 저장하지 않음을 이야기 한다. 서버는 단순히 들어오는 요청만을 처리하여 구현을 단순화 하되, 클라이언트의 모든 요청은 서버가 요청을 알아듣는 데 필요한 모든 정보를 알고 있어야 한다. 이를 테면, 어떤 메소드를 요청했는지, 여기에 필요한 정보는 무엇인지 등을 이야기 한다.

  3. Cache (캐시 기능)
    캐시 기능은 클라이언트의 요청을 캐싱한다는 이야기이다. HTTP의 기능 중 캐시 기능을 적용한 것이다.

  4. Uniform Interface (인터페이스 일관성)
    인터페이스 일관성 원칙은 URI를 가능한 지정된 리소스에 균일하고, 통일된 인터페이스를 제공해야 하는 원칙으로 아케텍처를 단순하게 분리하여 확장이 쉽도록 구현해야 한다는 원칙이다. 대표적으로는 각 Entity 별로 자원을 식별하는 방법, 혹은 HATEOAS를 이용하는 방법을 이야기 한다. HATEOAS란, 클라이언트에 응답하는 형식을 단순히 결과 데이터만 제공해주는 것이 아닌 URI를 함께 제공해야 한다는 원칙을 말한다.

  5. Layered System (계층화 시스템)
    계층화 시스템 원칙은 애플리케이션 서버가 중계 서버(Proxy, Gateway)나 로드 밸런싱, 공유 캐시 등을 이용하여 확장성 있는 시스템을 구현해야 한다는 원칙이다.

  6. Code-On-Demand (코드 온 디맨드)
    코드 온 디맨드의 원칙은 반드시 지켜야 할 필수 원칙은 아니다. 이 원칙은 클라이언트가 서버에서 JavaApplet, Javascript 실행 코드를 전달 받아 기능을 일시적으로 확장할 수 있어야 한다는 원칙이다.

단순히 무작정 HTTP의 메소드와 URI를 사용한 것이 아닌, 위의 제약 조건들을 잘 준수하면서 설계된 API를 비로소 RESTful한 API라고 표현할 수 있다.


3. REST API 설계

3-1. REST API의 구성

REST API는 HTTP의 메소드, URI로 이루어진 API 서버이므로, 필요한 것은 요청과 응답 이렇게 2가지가 존재하고, 어떠한 요청에 필요한 데이터와 그 결과 모델을 구성해야 한다.

  • 자원(Resource) - URI
  • 행위(Verb 또는 Action) - HTTP Method
  • 표현(Representations) - HTTP Message Body

3-2. REST API의 중심 규칙

REST API 설계 시 가장 중요한 것은 다음의 2가지로 요약할 수 있다.

  • 첫 번째, URI는 정보의 자원을 표현해야 한다.
  • 두 번째, 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현한다.

여기서 두 번째에 덧붙이자면, 각각의 HTTP Method 별로 의미를 생각하며 REST API를 설계하려고 하면 된다. 이게 무슨 말이냐, URI만 보고도 해당 기능의 핵심을 바로 파악할 수 있게끔 하라는 것이다.

각각의 HTTP Method 별 의미는 간단하게 다음과 같이 생각하면 된다(동사의 의미를 부여한다).

  • POST : 생성 (POST를 통해 해당 URI를 요청하면 리소스를 생성한다)
  • GET : 조회 (GET를 통해 해당 리소스를 조회한다. 리소스를 조회하고 해당 도큐먼트에 대한 자세한 정보를 가져온다)
  • PUT : 수정 (PUT를 통해 해당 리소스를 수정한다)
  • DELETE : 삭제 (DELETE를 통해 리소스를 삭제한다)

그리고 URI에는 목적어의 의미를 부여한다고 생각하면 된다.

이를 통해 CRUD (Create, Read, Update, Delete) 를 구현한다.

가령 예를 들면, '1번 유저의 친구 목록을 불러온다.'와 같은 것 말이다. 여기서는 기능의 핵심이 생성, 조회, 수정, 삭제 중에서 조회에 가장 근접하므로, GET Method(조회)를 사용하면 적당할 것이다. 이를 URI로 표현한다면 다음과 같이 표현할 수 있을 것이다.

GET /users/1/friends

여기서 위의 주소를 URI라고 한다. URL은 어떤 자원에 대한 위치를 나타내는 것이라면, URI는 문자열을 식별하기 위한 것이므로 가능한 명사를 사용해야 하며, 동사를 사용하는 것은 피해야 한다. HTTP에 대한 행위에 대해서는 이미 HTTP Method를 통해 알 수 있으며, 이를 URI에 표현하는 것은 오히려 혼동을 줄 수 있기 때문이다.

명사를 사용할 때도 단수보다는 복수를 사용하는 것이 좋다. 왜냐하면, API를 설계할 때, 어떠한 자원을 가져오게 되는데 그것은 보통 DB 등에서 가져오게 되고, 그들은 여러 개의 데이터로 존재하기 때문이다. 따라서 복수형을 사용함으로써 컬렉션이라는 것을 명확하게 알려줄 수 있고, 그 중에서 특정 데이터를 가져온다면 해당 데이터를 단수로 표현하는 것으로써 더욱 깔끔한 API가 될 것이다.

GET /users/1/friends/1 -> 1번 유저의 1번 친구

그러나 피해야 하는 명사들도 있다. 이를 테면, 주문 현황을 나타내는 orders 등의 명사는 가능한 사용하지 않는 것이 좋다. 왜냐하면, SQL 쿼리문에서 order는 정렬의 키워드로써 사용하기 때문에 이를 같이 사용하면 혼동을 줄 수 있기 때문이다. 따라서 SQL 질의문 등에 존재하는 키워드들의 사용은 자제하는 것이 좋다.

위와 같이 URI는 자원을 표현하는 데에 집중하고 행위에 대한 정의는 HTTP Method를 통해 하는 것이 REST API를 설계하는 중심 규칙이다.

3-3. URI 설계 시 주의할 점

  1. 슬래시 구분자(/)는 계층 관계를 나타내는 데 사용한다.
http://restapi.example.com/houses/apartments
http://restapi.example.com/animals/mammals/whales
  1. URI 마지막 문자로 슬래시(/)를 포함하지 않는다.
    URI에 포함되는 모든 글자는 리소스의 유일한 식별자로 사용되어야 하며 URI가 다르다는 것은 리소스가 다르다는 것이고, 그 말은 곧 리소스가 다르면 URI도 달라져야 한다는 뜻이다. REST API는 분명한 URI를 만들어 통신을 해야 하기 때문에 혼동을 주지 않도록 URI 경로의 마지막에는 슬래시(/)를 사용하지 않는다.
http://restapi.example.com/houses/apartments/ (X)
http://restapi.example.com/houses/apartments  (0)
  1. 하이픈(-)은 URI 가독성을 높이는데 사용한다.
    URI를 쉽게 읽고 해석하기 위해, 불가피하게 긴 URI경로를 사용하게 된다면 하이픈을 사용해 가독성을 높일 수 있다.

  2. 밑줄(_)은 URI에 사용하지 않는다.
    글꼴에 따라 다르긴 하지만 밑줄은 보기 어렵거나 밑줄 때문에 문자가 가려지기도 한다. 이런 문제를 피하기 위해 밑줄 대신 하이픈(-)을 사용하는 것이 좋다(가독성).

  3. URI 경로에는 소문자가 적합하다.
    URI 경로에 대문자 사용은 피하도록 해야 한다. 대소문자에 따라 다른 리소스로 인식하게 되기 때문이다. RFC3986(URI 문법 형식)은 URI 스키마와 호스트를 제외하고는 대소문자를 구별하도록 규정하고 있기 때문이다.

  4. 파일 확장자는 URI에 포함시키지 않는다.

http://restapi.example.com/members/soccer/345/photo.jpg (X)

REST API에서는 메시지 바디 내용의 포맷을 나타내기 위한 파일 확장자를 URI 안에 포함시키지 않는다. Accept header를 사용하도록 하자.

GET / members/soccer/345/photo HTTP/1.1 Host: restapi.example.com Accept: image/jpg

3-4. 리소스 간의 관계를 표현하는 방법

REST 리소스 간에는 연관 관계가 있을 수 있고, 이런 경우 다음과 같은 표현방법으로 사용한다.

/리소스명/리소스 ID/관계가 있는 다른 리소스명

ex)    GET : /users/{userid}/devices (일반적으로 소유 ‘has’의 관계를 표현할 때)

만약에 관계명이 복잡하다면 이를 서브 리소스에 명시적으로 표현하는 방법이 있다. 예를 들어 사용자가 '좋아하는' 디바이스 목록을 표현해야 할 경우 다음과 같은 형태로 사용될 수 있다.

GET : /users/{userid}/likes/devices (관계명이 애매하거나 구체적 표현이 필요할 때)

3-5. 자원을 표현하는 Collection과 Document

Collection과 Document에 대해 알면 URI 설계가 한 층 더 쉬워진다. Document는 단순히 문서 또는 객체로, 컬렉션은 문서들의 집합, 객체들의 집합이라고 생각하면 이해하기 쉬울 것이다. Collection과 Document는 모두 리소스라고 표현할 수 있으며 URI에 표현된다.

http:// restapi.example.com/sports/soccer

위 URI를 보면 sports라는 Collection과 soccor라는 Document로 표현되고 있다고 생각하면 된다. 좀 더 예를 들면,

http:// restapi.example.com/sports/soccer/players/13

sports, players Collection과 soccor, 13(13번인 선수)를 의미하는 Document로 URI가 이루어지게 된다. 여기서 중요한 점은 Collection은 복수로 사용하고 있다는 점이다. 좀 더 직관적인 REST API를 위해서는 Collection과 Document를 사용할 때 단수 복수도 지켜준다면 더 이해하기 쉬운 URI를 설계할 수 있을 것이다.


4. HTTP 응답 상태 코드

잘 설계된 REST API는 URI만 잘 설계된 것이 아닌 그 리소스에 대한 응답을 잘 내어주는 것까지 포함되어야 한다. 정확한 응답의 상태코드만으로도 많은 정보를 전달할 수가 있기 때문에 응답의 상태코드 값을 명확히 돌려주는 것은 생각보다 중요한 일이 될 수도 있다. 따라서 200이나 4XX관련 특정 코드 정도만 사용하고 있다면 처리 상태에 대한 좀 더 명확한 상태코드 값을 사용할 수 있기를 권장하는 바이다. 여기서는 대표적인 몇 가지만 정리하도록 하겠다.

상태 코드를 이용함으로써 클라이언트가 분기처리를 할 수 있게 도움을 줄 수 있다. 만약 에러마다 후속처리가 달라진다면 상태 코드 또한 달라져야 할 것이다.

  • 2XX
상태코드-
200클라이언트의 요청을 정상적으로 수행함
201클라이언트가 어떠한 리소스 생성을 요청, 해당 리소스가 성공적으로 생성됨(POST를 통한 리소스 생성 작업 시)
  • 4XX
상태코드-
400클라이언트의 요청이 부적절할 경우 사용하는 응답 코드
401클라이언트가 인증되지 않은 상태에서 보호된 리소스를 요청했을 때 사용하는 응답 코드
(로그인 하지 않은 유저가 로그인 했을 때, 요청 가능한 리소스를 요청했을 때)
403유저 인증상태와 관계 없이 응답하고 싶지 않은 리소스를 클라이언트가 요청했을 때 사용하는 응답 코드
(403 보다는 400이나 404를 사용할 것을 권고, 403 자체가 리소스가 존재한다는 뜻이기 때문에)
405클라이언트가 요청한 리소스에서는 사용 불가능한 Method를 이용했을 경우 사용하는 응답 코드
  • 3XX, 5XX
상태코드-
301클라이언트가 요청한 리소스에 대한 URI가 변경 되었을 때 사용하는 응답 코드
(응답 시 Location header에 변경된 URI를 적어 줘야 한다)
500서버에 문제가 있을 경우 사용하는 응답 코드

참고자료


profile
개발 블로그이지만 꼭 개발 이야기만 쓰라는 법은 없으니, 그냥 쓰고 싶은 내용이면 뭐든 쓰려고 합니다. 코드는 깃허브에다 작성할 수도 있으니까요.

0개의 댓글