웹 개발을 하면 필연적으로 만나는 REST API 에 대해 공부한 내용을 정리하는 글 입니다.
부끄럽지만 필자는 여태껏 개발하면서 REST API 에 대해 제대로 공부해본 적이 없었다.
그래서인지 Swagger 를 이용해서 만든 api 문서가 restful 하지 못하다는 피드백을 받아서
이번 기회에 공부하고 정리하는 시간을 가져보려고 한다.
컴퓨터의 기능을 실행시키거나 어떠한 응용프로그램에서 데이터를 주고 받기 위한 방법을 의미한다.
예를 들어 특정 문자열을 출력하는 문법이 자바에서는
System.out.Println("안녕하세요");
이라면 파이썬에서는
print('안녕하세요')
일 것이다. 이런식으로 특정 데이터를 출력하는 기능을 실행시키거나 혹은
브라우저 주소창에 www.naver.com
이라는 값을 입력하면 네이버 사이트가 출력되는것도
네이버 서버로부터 통신을 통해 html과 여러 데이터 값을 받았기 때문에 api라고 볼 수 있다.
자 그렇다면 REST API 란 대체 무엇일까?
REST API 는 위의 예시중에서 www.naver.com
를 브라우저에 입력해서 네이버 사이트로 이동한 것처럼
내 컴퓨터가 아닌 다른 컴퓨터를 실행시키는 api 를 보다 효율적으로 사용하기 위해 고안된 개념으로
http 통신을 통해 자원을 보다 효율적으로 관리하기 위해 사용한다.
또한, HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고,
HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.
특정 애플리케이션이 가진 데이터를 REST API 에서는 Resource 라고 한다.
가령, 우리가 사용하는 블로그 글 데이터를 생각해보면 블로그 글 테이블에는 다음과 같은 데이터가 있을 것이다.
id | title | body |
---|---|---|
1 | api 알아보기 | api는 어쩌고.. |
2 | http란? | http는 저쩌고... |
3 | Spring 개발 | 스프링으로... |
4 | rest | ... |
... | ... | ... |
위와 같은 데이터를
이 두가지로 정의를 한다.
위 테이블의 데이터를 모두 지칭하는 것으로
http://blog.com/topics
와 같이 도메인경로/(복수형의 데이터)
형식으로 표현한다.
위 테이블이 topics 라면 테이블의 모든 데이터를 지칭하게 된다.
위 테이블의 특정 데이터를 지칭하는 것으로
http://blog.com/topics/1
처럼
도메인경로/(테이블 혹은 데이터 이름)/(특정 값)
의 형식으로 표현하게 된다.
만약, 1이 id
를 의미한다면 위 테이블의 1번째 줄을 지칭하게 된다.
(Element 에서는 id
값을 식별값으로 사용하는것이 일반적이며,
이름으로 식별할 수 있다면 http://blog.com/topics/rest
처럼 단수형을 사용한다.)
즉, Element 가 모여있는 것이 Collection ,
Collection 의 하나 하나의 데이터를 Element 라고 한다.
하지만, 위에서 살펴본 URL
을 사용하는 방식으로는 데이터 식별밖에 하지 못하며 데이터를 가공하지는 못한다.
따라서 이를 해결하기 위해 REST API
에서는 CRUD
를 사용한다.
CRUD
는 Create, Read, Update, Delete 의 앞글자를 따서 만든 용어로
말 그대로 데이터를 생성(Create), 수정(Read), 갱신(Update), 삭제(Delete) 를 의미한다.
이런 작업을 REST API
에서는 Method
라고 부른다.
또한, 각각의 메서드는 HTTP Method
와 다음과 같이 매핑되어 사용된다.
- Create - Post
- Read - Get
- Update - Put/Patch
- Delete - Delete
(왼쪽이CRUD
, 오른쪽이HTTP Method
)
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
다음과 같은 JSON 을 가지고 각각의 요청을 보내서 적용시켜 보도록 하자.
POST
요청은 데이터를 생성(CREATE) 할때 사용하는 HTTP METHOD
로
가령 위의 JSON 데이터가 DB 에 있을때 서버로
'topic' 에 해당하는 데이터를 바디에 담아서
{
"title" : "API",
"body" : "API 란... 어쩌고..."
}
서버 도메인/topics
라는 경로로 POST
요청을 보내게 되면
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
},
{
"id" : 2,
"title" : "API",
"body" : "API 란... 어쩌고..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
이런식으로 데이터가 추가되게 될 것이다.
개발자 도구를 통해 네트워크 탭에서 방금 보낸 요청의 서버에서 보내준 응답 헤더를 조회 해보면
응답 코드로 201과 응답 메시지로 "created" 라는 메시지가 왔음을 확인할 수 있다.
(여기서 응답코드 201은 데이터가 성공적으로 생성되었음을 의미한다. )
즉, REST API 에서는 특정 요청을 보내게 되면, 서버에서 그 요청에 대한 응답으로 응답 코드와 응답 메시지를 Response Header(응답 헤더) 라는 곳에 담아서 보내준다.
GET
요청은 데이터를 조회 할때 사용하는 메서드이며,
여러개를 묶어서(Collection)
를 조회 하느냐 혹은 하나의 데이터(Element)
조회 하느냐 하는 두가지의 방식이 존재한다.
GET
요청에는 요청 바디에 값을 담아서 보낼 필요가 없다.
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
},
{
"id" : 2,
"title" : "API",
"body" : "API 란... 어쩌고..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
데이터베이스에 다음과 같은 데이터가 들어 있을때 topics
의 데이터를 묶어서 전부 가져오고 싶으면
서버 도메인/topics
라는 경로로 GET
요청을 보내면 된다.
그러면 Response Body에 다음과 같이 topics
데이터들이 담겨서 오게 될 것이고
응답 헤더에는 다음과 같이 상태 코드 200
과 함께 통신이 성공적으로 이루어졌다는 응답 메시지 OK
가 있을 것이다.
하나의 데이터(Element)
조회이번에는 하나의 데이터를 조회해보자.
위에서 특정 Element
를 url로 표현하는 방법이 기억나는가?
바로 도메인경로/(테이블 혹은 데이터 이름)/(특정 값)
으로 표현하면 됐었다.
서버에 요청을 보낼때도 이와 똑같은 방법으로 보내면 되므로
서버 도메인/topics/2
라는 경로로 GET
요청을 보내보자.
이 표현식은
topics
데이터중 아이디 값이 2인 것을 의미한다.
(여기서 2라는 값은 아이디일수도 있고 다른 값을 수도 있지만 서버에서 정하기 나름이므로 편의상 id 값이라고 하자.)
그러면 응답으로
응답 바디에 다음과 같은 데이터가 담겨서 올 것이고 헤더는 Collection
을 조회할 때와 똑같은 값이 담길것이다.
Update
만 특이하게도 HTTP Method 와 두개와 매칭되며 두 메서드는 각각
- PATCH - 부분 수정
- PUT - 전체 수정
을 의미한다.
또한,
PUT/PATCH
는POST
와 같이 요청 바디에 데이터를 담아서 보내야 한다.
위의 데이터에서 topics
의 id
가 2인 데이터를 수정해보자.
이 데이터의 title 만 변경하고 싶으면
{
"title" : "patch"
}
요런 식으로 변경하고 싶은 데이터를 바디에 담고PATCH
요청을 사용하면 된다.
이제, URI 표기로 서버 도메인/topics/2
라는 경로로 바디에 담은 데이터와 함께 PATCH
요청을 보내보자.
그러면 결과로
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
},
{
"id" : 2,
"title" : "patch",
"body" : "API 란... 어쩌고..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
이렇게 값이 변경된 것을 확인해 볼 수 있다.
즉,
PATCH
메서드는 바디에 넣어준 부분만 변경되고 나머지 부분은 변경되지 않는다.
PUT
은 PATCH
와 다 똑같지만 데이터가 변경되는 결과 측면에서 다르다.
위에서 보낸 PATCH
요청과 동일하게 설정하고 메서드 방식만 PUT
으로 요청을 보내보면
결과로
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
},
{
"id" : 2,
"title" : "patch"
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
다음과 같이 데이터베이스의 데이터가 변경된다.
여기서 주목해봐야 할 부분은
topics
의id
가 2인 데이터의"body" : "API 란... 어쩌고..."
라는 부분이 날아갔다는 점이다.
즉,PUT
메서드를 사용하면 바디로 넣어준 값을 덮어쓰기 때문에 수정하려는 값을 넣어준 부분과 식별자인id
를 제외하고 다른 부분이 사라진다.
DELETE 요청도 GET과 마찬가지로 바디에 값을 담을 필요없이 요청만 보내면 되며,
이렇게 두가지 방식이 존재한다.
여기서도 topics
의 id
가 2인 데이터를 삭제해 보도록 하겠다.
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
서버 도메인/topics/2
라는 경로로 DELETE
요청을 보내면 위와 같이topics
의 id
가 2인 데이터가 삭제된다.
이 경우에는 만약 topics
데이터 콜렉션을 삭제하고 싶으면
서버 도메인/topics
라는 경로로 DELETE
요청을 보내면 topics
의 데이터들이 모두 삭제된다.
하지만, 이런 삭제 요청은 위험하기 때문에 보통 서버에서 이런 요청을 수행하지 못하도록
LOCK
을 걸어두는 경우가 많다.
{
"topics": [
{
"id": 1,
"title": "REST",
"body": "REST is ..."
}
],
"comments": [
{
"id": 1,
"body": "POST is ...",
"topicId":1
},
{
"id": 2,
"body": "READ comments is ...",
"topicId":1
}
]
}
다음과 같은 데이터가 있다고 가정해보자.
그러면 이번에는 comments
들을 URI
경로 표현식으로 표현해보고 싶다.
간단하게 생각하면
도메인주소/comments
나 혹은 도메인주소/comments/1
처럼 전체 댓글 콜렉션을 지칭하거나
댓글의 아이디가 1인 엘리먼트를 지칭할수도 있다.
하지만, 만약 topics
안에 정의되어 있는 댓글을 지칭하고 싶으면 어떤식으로 하면 될까?
데이터를 잘 관찰해보면 topics
의 id
는 1이고 comments
의 topicId
는 1이다.
즉 위의 데이터의 댓글들은 id
가 1인 topic
에 종속되어 있다.
이때도 근본적인 원리는 크게 다르지 않다.
id
가 1인topic
에 종속된 모든 댓글을 지칭하고 싶으면
도메인주소/topics/1/comments
이런식으로 사용하면 되고
id
가 1인topic
에 종속되고 아이디가 2인 댓글을 지칭하고 싶으면
도메인주소/topics/1/comments/2
이런식으로 사용하면 된다.
위처럼 URI
를 사용하고 위에서 다룬 HTTP METHOD
를 적절하게 적용해서 CRUD
를 구현하면 되겠다.
즉,
REST API
는HTTP
를 이용해서 통신할 때RESOURCE
는URI
로 "표현" 하고
표현한 리소스에 적용할 "행위" 는METHOD
로
그에 따른 "결과" 는응답코드
와응답 메시지
로 적용시키는 일종의 "약속" 이라고 할 수 있다.
위에서 다룬 응답코드들 말고 HTTP
에 정의된 다른 응답코드들도 다음과 같은 것들이 있다.
REST API
에 대해 공부하기 전에 작성한 API 문서의 일부를 가져와봤다.....
공부하고 보니 킹받네...
HTTP METHOD
로 이 api 의 "행위" 를 표현할 수가 있으므로 굳이 api 경로에 create, delete 이런 불필요한 단어를 빼버렸다.
편-안
https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html
https://memostack.tistory.com/180#article-2--http-method%EC%99%80-crud