개요
RESTful 에 대한 워낙 좋은 글들이 많아 굳이 작성할 필요는 없는 글이지만 개인적인 정리를 위해 작성한다.
Spring MVC 를 활용하여 RESTful 개념으로 적당히 Controller 을 구현하고 구글링을 통해 여러 글들을 접하면서
내가 정리한 RESTful 의 장점은 크게 3가지라고 생각한다.
장점
- 읽기 쉽다. 가장 중요하다고 생각. 읽히기 쉬운 인터페이스를 가지면 개발간에 오해 없이 빠른 개발 진행이 가능
- HTTP 스펙을 최대한 활용하기 때문에, HTTP 기반이면 뭐든 사용 가능
- (사실 두번째 장점의 연장성인데) 여러 플랫폼 / 여러 클라이언트를 대응할 수 있음
RESTful 의 규칙
로이필딩이 정의한 다음의 요건을 만족해야 RESTful 하다고 말할 수 있다.
- 클라이언트 서버 구조(Client / Server Architectural Style)
- 무상태(Stateless): 단, 인증/권한을 위한 내용은 실용적인 측면에서 예외가 가능함 캐시 처리 가능(Cacheable)
- 계층화(Layered System)
- 균일 인터페이스(Uniform Interface)
- Code on Demand(optional)
그 중 가장 중요한 규칙이라고 할 수 있는 균일 인터페이스(Uniform Interface)를 지키기 위한 중요한 2가지 요소를 적어보면 다음과 같다.
- 리소스의 식별
- 리소스의 조작
그 중 리소스의 식별 의 측면에서 RESTful 특징의 일부를 기술하고자 한다.
리소스의 식별
리소스는 반드시 유일한 식별자를 가져야 한다. 따라서 웹 API의 경우 고유의 URI을 가진다.
또한 URI 만을 통해서 해당 자원의 의미를 충분히 표현할 수 있어야 한다.
다만, 동일한 URI 라도 HTTP method 에 따라 리소스에 관련한 작업 - 조회, 등록, 수정, 삭제를 구분하기 에 URI 자체에는 CRUD 성격의 표현이 들어가선 안 된다.
이를 지키기 위한 간단한 디자인 팁은 다음과 같다.
- 리소스 표현(URI)에서 동사를 제거하자
- 리소스 간의 관계를 표현하자
- snake-case 를 사용하자. 하이픈 활용
- 확장자를 URI 에 기술하지 말자
1) 리소스 표현(URI) 에서 동사를 제거하자
나쁜 예 / 좋은 예를 제시하여 내용 설명을 대체한다.
1 | ex) 나쁜 예 |
1 | ex) 좋은 예 |
2) 리소스 간의 관계를 표현하자
보통 리소스는 리소스 독립적이 아니라 리소스 간의 관계를 가지게 된다.
REST 에서는 이를 컬렉션(Collection) 과 도큐먼트(Document) 라는 개념으로 설명하고 있는데 굳이 그런 설명이 없더라도 직관적으로 이해가 가능하다. 다음 예시 URI 를 보자
1 | GET /users/{id}/dogs/ |
URI 를 읽어보면 특정 user 가 소유한 (또는 연관된) dogs 의 리스트를 말하는 것임을 직관적으로 알 수 있다.
내가 URI 에 표현하고자 하는 자원이 복수인지 단수인지를 명확히 구분하여 복수 자원인 경우에는 s 를 꼭 붙여줘야 RESTful 한 URI 설계가 된다.
3) snake-case 를 사용하자. 하이픈 활용
URI 로 자원을 표현하고 싶을 때 공백을 두어 단어간의 의미를 명확히 구분하기를 원할 때가 있다.
예를 들어보면, 현재 시스템에서 유 효한 상태의 user 를 얻고자 할 때 URI 설계를 다음과 같이 할 수도 있다.
1 | ex) GET /valid user |
그러나 공백이 들어가는 경우 URI 에서는 %20 으로 표현되는데 보기가 안 좋다. 그에 따라 보통 밑줄( _ ) 이나 하이픈 ( - ), 또는 CamelCase 방식으로 작성한다.
여기서는 하이픈 사용을 권장한다.
밑줄을 쓸 경우 링크 표현 시 밑줄이 가려보이는 이슈가 있고, CamelCase 방식은 대소문자가 섞이는 이슈가 있다.
도메인과 달리 URI 레벨에서는 대소문자가 구분된다. 따라서 대소문자를 섞어서 쓸 경우 예상치 못한 이슈가 발생하므로 소문자 통일을 권장한다.
4) 확장자를 URI 에 추가하지 말자
URI 는 리소스의 표현이고 이는 플랫폼이나 클라이언트의 요청 자원 형태에 독립적이어야 한다.
따라서 동일한 자원에 대해 .txt / .csv 등의 확장자는 붙이지 말아야 한다.
1 | 확장자가 포함된 URI |
예시에서는 group 아이디 14번이 가지는 users 의 리스트를 표현하는데 각각의 응답 형태가 txt 와 csv 임을 알 수 있다. 하지만 리소스 자체는 group 아이디 14번이 가지는 user 리스트이므로 둘 간의 차이는 없다.
클라이언트가 원하는 리소스 형태에 따라 대응하고 싶다면 HTTP header 에서 Accept 를 활용하여 미디어 타입에 따른 대응이 가능하도록 구현하면 된다.
1 | Accept header 활용 |
Accept 에 따라 서버에서 각각 적절한 응답 형식을 리턴하도록 구현하면 URI 고유의 의미를 지키면서 각 클라이언트 요청이 원하는 형 태의 데이터 리턴이 가능하다.
또한 Spring MVC 에서는 Accept 에 따라 메서드 분기가 가능하도록 이미 Controller 의 메서드에 produces / consumes 를 제공하고 있다.
1 | // Spring MVC 의 Controller 에서 produces / consumes 활용 |
stackoverflow springmvc requestmapping article 를 꼭 읽어보시길 바람
리소스의 식별과 관련하여 지금까지 제시한 규칙을 따른다면 RESTful 한 구현에 있어서 URI 설계 쪽에는 큰 이슈가 없을 것이다.
제시된 4가지 팁 이외에 고민해볼만한 한가지 사항을 더 추가하면 다음과 같다.
URI 의 버전 정보를 어떻게 표현할 것인가?
사실 API의 버전에 상관없이 고유한 URI의 측면에서 생각한다면 URI 에 /v1,/v2 등을 기술하여 관리하는 것은 RESTful 하지 않다.
이상적이라면 HTTP 의 header 안에 버전 정보를 기술하여 넣는 것이 맞다.
실제로 github 같은 경우 다음과 같이 자체적인 header 를 사용하여 resource 형태와 versioning 을 표현한다.
1 | application/vnd.github+json; version=1.0 |
header 안에 versioning 을 표현하는 아래와 같은 예시도 있다.
1 | curl https://api.stripe.com/v1/charges \ |
허나 best practice for api versioning 와 링크도 있듯이, header 안에 versioning 을 넣는 이상적인 방안 뿐만 아니라 URI 에 /v1, /v2 를 넣는 방식에 대한 논의도 많다. 브라우저에서 직접 /v1, /v2 등을 입력하여 접근하기 좋고, 구현하는 쪽이나 사용하는 쪽 둘 다 편하다는 장점을 생각한다면 URI 에 /v1 등을 사용하는 것이 꼭 나쁘다고는 생각하지 않아도 될 듯 하다.
참고 링크
- https://spoqa.github.io/2012/02/27/rest-introduction.html
- https://spoqa.github.io/2013/06/11/more-restful-interface.html
- http://blog.remotty.com/blog/2014/01/28/lets-study-rest/
- http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
- https://stripe.com/docs/api#versioning
- https://stackoverflow.com/questions/389169/best-practices-for-api-versioning
- https://beyondj2ee.wordpress.com/2013/03/21/%EB%8B%B9%EC%8B%A0%EC%9D%98-api%EA%B0%80-restful-%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-5%EA%B0%80%EC%A7%80-%EC%A6%9D%EA%B1%B0/
- http://www.hanbit.co.kr/store/books/look.php?p_code=E9835405237 - 이 책의 경우, 목차만 봐도 RESTful 을 다 이해할 것만 같다.