Restful API란?

지니·2023년 5월 27일
0

며칠 전, 오랜만에 대학교 선배를 만났고 이런 대화가 오고 갔다.

"진아, Rest API가 뭐야?"
"어... 저도 정확하게 정의하기는 어렵지만... (어쩌구 저쩌구)"

그러게... Restful API가 뭘까? 1년 넘게 많은 API를 접했지만 정작 'Rest API'가 무엇인지 고민해보는 시간은 갖지 못했던 것 같다. 그래서 이번 기회에 고민해보기로 했다.








1. 보통 개발자가 아는 Rest API의 정의

REST란 Representational State Transfer의 약자로 분산 하이퍼미디어 시스템을 위한 아키텍쳐 스타일이다. 즉, 자원의 이름으로 구분하여 해당 자원의 상태를 주고받는 것이다.




a. Rest API란?

REST(Representational State Transfer) API는 요청을 보내는 API 주소만으로도 대략 어떤 요청인지 파악이 가능하도록 설계한 API다.


1)REST의 구성

  • 자원(Resource) - HTTP URI
  • 행위(Verb) - HTTP Method
  • 표현(Representations) - HTTP Message PayLoad



b. 사실 Rest API가 아니여도 기능 개발에는 문제가 없다.

사실 Restful하게 설계하지 않아도 기능이 동작하도록 구현하는 데에는 아무 문제가 없다. 아래 Spring Boot를 이용해 작성한 코드를 보자.

@RestController
@RequestMapping("/api/accounts")
@RequiredArgsConstructor
public class AccountApiController {
	@GetMapping("/duckling/{idx}")
    public int delete(@PathVariable Long idx) {
    	// 회원 탈퇴하는 로직
    	return 1;
    }
}

탈퇴할 계정의 idx를 알고, 비즈니스 로직에만 이상이 없다면 이 API는 정상적으로 동작할 것이다.




c. 그럼에도 불구하고 Rest API가 필요한 이유

하지만...

GET /api/accounts/duckling/{idx}

이렇게 API를 제시했을 때 해당 API를 사용할 다른 개발자가, 혹은 추후 유지보수 할 다른 동료 개발자가 과연 어떤 API인지 알 수 있을까? 일단 엉망진창으로 예시를 든 본인도 모르겠다.


위에서 언급한 REST 구성을 잘 이용해 API를 개선해보자.

AS-IS
GET /api/accounts/duckling/{idx}


TO-BE
DELETE /api/accounts/{idx}

훨씬 나아졌다. API만 보고도 특정 idx에 해당하는 계정을 탈퇴할 떄 사용한다는 것을 짐작할 수 있다.






2. 좀 더 깊게 알아보자

a. REST 제약조건

  • client-server
  • stateless
  • cache
  • uniform interface
  • layered system
  • code-on-demand (optional)

위 조건들은 uniform interface 빼고 웬만한 HTTP API에서도 모두 지켜질 수 있다. 그렇다면 uniform interface가 무엇인지 알아보자.




b. uniform interface

uniform interface 정의를 찾아보면 아래와 같이 나온다.

URI로 지정한 리소스에 대한 조작통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일을 말한다.

하지만 본인은 저 한 줄로는 이해하기 어려웠다. 따라서 uniform interface를 만족시키는 조건에 대입해서 생각해보려고 한다.




1) uniform interface를 만족시키는 조건

a) identifiaction of resources

  • resource가 URI로 식별되면 된다.
  • URI로 지정한 리소스에 대한 조작
    -> ex) '회원'이라는 리소스에 접근하기 위한 API를 설계할 때 localhost:8080/api/accounts 처럼 설계할 수 있다.

b) manipulation of resources through representations

  • representations 전송을 통해 resources를 조작해야 한다.
  • 여기서 representations이란 GET, POST, PUT, DELETE 등의 HTTP 메소드를 뜻한다.
  • 통일되고 한정적인 인터페이스로 수행
    • 보편적으로 등록(POST), 수정(PUT, PATCH), 삭제(DELETE), 조회(GET) 와 같이 사용한다.
    • ex) 회원 탈퇴 API 생성 시 GET localhost:8080/api/accounts/delete 처럼 명시할 수도 있겠지만 약속한 대로 DELETE localhost:8080/api/accounts 처럼 나타내면 더 깔끔하고 명확해진다.

c) self-descriptive messages

  • 메시지가 스스로 설명해야 한다.
  • (지켜지기 어렵다)

d) HATEOAS

  • application의 상태는 하이프링크를 이용해서 전이되어야 한다.
  • (지켜지기 어렵다)

사실 identifiaction of resourcesmanipulation of resources through representations는 흔히 알고있는 REST API의 특징이다. 하지만 REST API에서 uniform interface가 지켜지지 않는다고 하는 이유는 self-descriptive messagesHATEOAS 때문이다.




2) self-descriptive messages 만족하기

self-descriptive는 메시지가 스스로 설명되어야 한다 즉, 메시지의 모든 요소는 메시지만 보고 그 뜻을 알아야 한다는 것이다.


보통 API를 작성하고 response를 받을 때 아래와 같은 형식으로 받는다.
HTTP/1.1 200 OK
Content-Type: application/json
{"idx":1,"name":"윤진"}

사람이 봤을 떄는 json 타입으로 idx는 1이고 이름은 '윤진'이구나 하고 알겠지만, 기계는 이를 알지 못한다. 따라서 RESTful하지 않는 설계라고 할 수 있다. 그렇다면 어떻게 해야 만족할 수 있을까?



a) Media type 정의 (IANA)

HTTP/1.1 200 OK
Content-Type: (사용자 정의 media type)
{"idx":1,"name":"윤진"}
  1. 미디어 타입을 정의 후 각 속성의 의미가 담긴 미디어 타입 문서를 작성한다.
    (위 예제에서는 idx, name이 의미하는 것을 적는다.)
  2. IANA에 미디어 타입을 등록한다. 위에서 작성한 문서를 미디어 타입 명세로 등록한다.
  3. IAMA에 등록된 명세를 찾을 수 있으므로 메시지의 의미를 해석할 수 있게 된다.

단점: IANA에 등록해야 한다는 번거로움이 있다

HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://...>;  rel="profile"
{"idx":1,"name":"윤진"}
  1. json을 구성하는 요소들이 무엇을 의미하는지를 나타내는 문서를 작성한다.
  2. link 헤더에 명세를 확인할 수 있는 링크를 넣어 응답에 넘긴다.

단점: 클라이언트가 Link 헤더와 profile을 이해해야 하며 미디어 타입이 아닌 Link 헤더를 통해 판단하기 때문에 content negotiation할 수 없다.




3) HATEOAS 만족하기

HATEOAS는 Hypermedia As The Engine Of Application State의 약자로 애플리케이션은 Hyperlink를 이용해서 전이가 되어야 한다는 뜻이다. html처럼 말이다.


예를 들어 아래의 html에서는 a 태그를 통해 다음 상태가 결정된 것을 확인할 수 있다. 따라서 HTML은 HATEOAS를 만족한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>사용자별 소속 팀</h1>
    <a href="localhost:8080/accounts/1/teams">유저1의 소속 팀</a>
</body>
</html>

하지만 response가 아래와 같은 json 형식이라면?

HTTP/1.1 200 OK
Content-Type: application/json
{"idx":1,"name":"윤진"}

응답을 받는 시점에 response만 담기게 되고 다음 상태를 알 수 없다. 따라서 RESTful하지 못하다. 어떻게 해야 HATEOAS를 만족할 수 있을까?


단순 JSON이 아닌, HAL JSON을 이용한다.

HAL
Hypertext Application Language의 약자로 JSON, XML 코드 내의 외부 리소스에 대한 링크를 추가하기 위한 특별한 데이터 타입이다.

HTTP/1.1 200 OK
Content-Type: application/hal+json
{
  "data": { // HAL JSON의 리소스 필드
    "id": 1000,
    "name": "게시글 1",
    "content": "HAL JSON을 이용한 예시 JSON"
  },
  "_links": { // HAL JSON의 링크 필드
    "self": {
      "href": "http://localhost:8080/api/article/1000" // 현재 api 주소
    },
    ...
  }
}
  • data: 요청에 대한 응답 데이터
  • _links: 접근 가능한 추가 API

이렇게 하면 클라이언트에서 API를 호출하는 부분을 하드코딩할 필요가 없어지며 접근 가능한 추가 API가 바뀌어도 해당 API를 호출하는 쪽은 변경할 필요 또한 없어진다.






3. 느낀 점

공부하는 과정에서 uniform interface의 한 줄 정의만 봤을 때는 내가 아는 REST API랑 같은 것 같은데 왜 만족하지 못한다는 거지...? 부터 시작해서 생각이 많았다. 나름대로 정리는 해봤지만 솔직하게 말하자면 REST API에 대해 아직 완전히 이해하지는 못했다고 생각한다.

하지만 분명한 건 개발하면서 지금까지 REST API라고 알고 설계했던 것들은 전혀 RESTful하지 못했던 것 같다. 이번에 RestAPI에 대해 알아보면서 만족시킬 수 있는 방법 중 하나인 self-descriptive message를 만족하는 방법 중 하나인 link header에 명세 링크 첨부 가 가장 인상깊었던 것 같다. 프론트엔드 개발자 입장에서도 그럴 것이고 기능 개발하는 본인도 한 번씩 문서가 어딨었더라...? 하는 경우가 종종 있었는데 이를 적용한다면 그럴 일도 없고 RestAPI를 만족하는 데 한 발짝 나아갈 수 있을 것 같다.

HATEOAS는 오 그러겠네...! 싶다가도
그러면 프론트엔드 개발자 입장에서는 _links에 있는 API가 만약에 get이 아니라면 body에 들어갈 데이터 명세는 어떻게 알지? 명세를 알더라도 위에서 언급한 하드코딩할 필요가 없어진다는 장점이 여기에서도 똑같이 작용하는가? 정도의 궁금증은 남아있는데 이건 본인이 아직 지식이 부족해서 그런 것 같다.

+ 서점에서 지나가다 발견한 RESTful WEB API 웹 API를 위한 모범 전략 가이드 도 기회가 된다면 나중에 한 번 읽어보고 싶다.







References


Restful API가 리소스를 나타내는 방법
REST API란?
그런 REST API로 괜찮은가

profile
Coding Duck

0개의 댓글