1. REST
- Representational State Transfer의 약자로 분산 네트워크 프로그래밍의 아키텍처 스타일이다.
[REST의 특성과 규칙]
1.1 특성
- 클라이언트/서버 (client/server) : 클라이언트와 서버가 서로 독립적으로 구분되어야 하고 서로간의 의존성 때문에 확장에 문제가 되는 일이 없어야 한다.
- 상태 없음 : 통신 시에 상태가 없어야 한다. 서버는 클라이언트의 상태를 기억할 필요가 없다.
- 레이어드 아키텍처 : 서버와 클라이언트 사이에 게이트웨이, 방화벽, 프록시가 있는 것처럼 다계층 형태로 레이어를 추가하거나 수정하거나 제거할 수 있고 확장성이 있어야 한다.
- 캐시(cache) : 서버의 응답들은 캐시를 가지고 있거나 없거나 둘 중의 하나인데, 캐시를 가지고 있을 경우에는 클라이언트가 캐시를 통해서 응답을 재사용할 수 있고 이를 통해서 서버의 부하를 낮추어서 서버의 성능이 향상될 수 있다.
- 코드 온 디멘드(Code on demand) : 요청이 오면 코드를 준다는 의미.
- 통합 인터페이스 : 서버와 클라이언트 간의 상호 작용은 일관된 인터페이스들 위에서 이뤄져야 한다.
1.2 REST 인터페이스 규칙
(1) 리소스 식별
- 웹 안에서 서로 구분할 수 있는 개념으로 URI와 같은 고유 식별자를 통해 표현할 수 있다.
(2) 표현을 통한 리소스 처리
- 데이터에 대해서 표현할 때 JSON, XML, HTML 페이지와 같이 다양한 콘텐츠 유형으로 표현할 수 있다. 하지만 데이터는 변경되지 않는다.
(3) 자기 묘사 메시지
- HTTP 통신을 할 때 Http Header에 메타 데이터 정보를 추가해서 실제 데이터와는 관련 없지만 데이터에 대한 설명을 나타내는 정보를 담을 수 있다.
(4) 애플리케이션의 상태에 대한 하이퍼미디어(HATEOAS, Hypermedia As The Engine Of Application State)
- 단순히 말해서 웹은 여러 페이지들과 그 페이즈들을 이동할 수 있는 링크 정보들로 구성되어 있다. REST API를 개발할 때도 단순히 데이터만 전달하지 않고 링크 정보까지 포함한다면 좀 더 웹에 친숙한 API가 될 것이다.
2. HATEOAS를 이용한 자기주소정보 표현
REST API를 사용하는 클라이언트들은 하나의 트랜잭션 안에서 여러 API를 사용하는 경우도 있고 기본이 되는 URI은 공통으로 사용하지만 하위 파라미터나 하위 URL에 대해서는 인지하지 못하는 경우가 많다. 왜냐하면 전체 URI는 요청 파라미터에 따라서 변경될 수 있기 때문이다. 스프링에서는 HATEOAS를 이용해서 해당 결과를 얻을 수 있는 전체 URI를 반환할 수 있는 정보를 제공할 수 있다. 클라이언트는 해당 정보를 통해서 해당 결과를 얻을 수 있는 전체 URI 정보를 알 수 있기 때문에 REST API를 좀 더 올바르게 사용할 수 있다.
2.1 HATEOAS를 이용한 URI 정보 표현
일단 라이브러리를 추가해야 한다. build.gradle 파일에 다음의 내용을 추가하자.
compile("org.springframework.boot:spring-boot-starter-hateoas")
ResourceSupport 클래스를 상속 받아 클래스를 만들자. ResourceSupport 클래스는 org.springframework.hateoas 패키지에 포함된 클래스로 해당 클래스를 이용해서 리소스에 링크 정보를 포함할 수 있다. ResourceSupport 클래스는 org.springframework.hateoas 패키지에 포함된 클래스로 해당 클래스를 이용해서 리소스에 링크 정보를 포함할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import org.springframework.hateoas.ResourceSupport;
public class TodoResource extends ResourceSupport {
private String title;
public TodoResource() {
}
public TodoResource(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
|
cs |
이제 Controller 클래스에서 요청을 받으면 TodoResource 클래스 인스턴스를 반환해서 URI 정보를 함께 표시할 수 있도록 HATEOAS를 사용하는 메서드를 추가하자.
1
2
3
4
5
6
7
|
@GetMapping("/todoh")
public ResponseEntity<TodoResource> geth(@RequestParam(value = "todoTitle") String todoTitle){
TodoResource todoResource = new TodoResource(todoTitle);
todoResource.add(linkTo(methodOn(BasicController.class).geth(todoTitle)).withSelfRel());
return new ResponseEntity(todoResource, HttpStatus.OK);
}
|
cs |
todoResource 인스턴스를 생성한 후에 링크 정보 추가를 위해서 linkTo 메서드로 Controller 클래스의 geth 메서드를 매핑한다. 그 후 withSelfRel 메서드를 이용해서 URL 정보를 만들고 add 메서드로 해당 정보를 추가한다.
1
2
3
4
5
6
7
8
|
{
"title" : "hateoas",
"_links" : {
"self" : {
"href" : "http://localhost:8080/basic/todoh?todoTitle=hateoas"
}
}
}
|
cs |
그러면 위와 같이 응답 결과에 링크 정보가 포함된다. 클라이언트에서는 응답 결과를 통해서 실제 리소스의 위치가 맞는지 검증할 수 있다.
[전형적인 REST API 응답 데이터 vs HATEOAS 사용]
(1) 전형적인 REST API 응답 데이터
{
"name": "jun"
}
(2) HATEOAS 사용
{
"name": "jun",
"links": [
{
"rel": "self",
"href": "http://localhost:8080/user"
},
{
"rel": "delete",
"href": "http://localhost:8080/user/delete"
},
{
"rel": "update",
"href": "http://localhost:8080/user/update"
}
]
}
- 서버 : 현재 리소스와 연관된 링크 정보를 클라이언트에게 제공
- 클라이언트 : 연관된 링크 정보를 바탕으로 리소스에 접근
3. RestTemplate
3.1 RestTemplate 이란?
스프링 MVC 라이브러리에 포함된 클래스로 스프링 3.2 이상이면 사용할 수 있다. 스프링에서 제공하는 http 통신에 유용하게 쓸 수 있는 템플릿이며, HTTP 서버와의 통신을 단순화하고 RESTful 원칙을 지킨다. 다양한 메시지 컨버터를 이미 내장하고 있어서 JSON 응답을 Map 또는 모델 클래스로 간편하게 변환해서 사용할 수 있다.
3.2 UriComponentsBuilder 활용
REST API를 연동할 때 가장 기본이 되는 정보는 URL이다. 다양한 API의 URL을 매핑하기 위해서 문자열로 조합하다 보면 가독성이 떨어져서 유지 보수가 힘들어진다. UriComponentsBuilder를 사용하면 Builder 방식으로 uri를 만드는 데 필요한 정보들을 메서드를 이용해 만들 수 있고, 인코딩도 가능하다.
3.2.1 생성
UriComponentsBuilder의 생성자의 접근 제한자는 protected로 되어 있다. 그래서 생성자를 직접 호출해서 사용할 수 없고 newInstance 메서드를 이용해 생성한다.
UriComponentsBuilder.newInstance().sheme().method().method()
이와 같이 메서드 체이닝 방식으로 uri를 구성하는 데 필요한 정보들을 추가한다.
예를 들어서 http://movie.naver.com/movie/sdb/rank/rmovie.nhn 사이트의 정보를 uriComponentBuilder를 활용하면 구조화된 형태로 표현할 수 있다.
1
2
3
4
5
6
7
8
|
UriComponentsBuilder.newInstance()
.scheme("http") // 프로토콜
.host("movie.naver.com") // 도메인 정보
.port(80) // 포트번호
.path("/movie/sdk/rank/rmovie.nhn") // RequestMapping 어노테이션의 URL 정보에 해당하는
.build()
.encode()
.toUri();
|
cs |
3.2.2 파라미터 전달
queryParam 메서드를 사용해서 파라미터를 전달하는 uri를 만들 수 있다.
path와 build 사이에 queryParam을 추가해서 사용할 수 있고, 다수의 파라미터가 필요한 경우에는 여러 개 등록하면 된다.
1
2
3
4
5
6
7
8
9
|
UriComponentsBuilder.newInstance()
.scheme("http") // 프로토콜
.host("movie.naver.com") // 도메인 정보
.port(80) // 포트번호
.path("/movie/sdk/rank/rmovie.nhn") // RequestMapping 어노테이션의 URL 정보에 해당하는 내용
.queryParam("code", 146506) // 파라미터
.build()
.encode()
.toUri();
|
cs |
3.2.3 PathVariable이 포함된 URL 만들기
path 값에 아이디에 해당하는 uri를 {}로 감싼 뒤에 expand 메서드를 추가로 사용해서 PathVariable에 해당하는 변숫값을 전달하면 된다.
1
2
3
4
5
6
7
8
|
UriComponentsBuilder.newInstance()
.scheme("http") // 프로토콜
.host("test.book.com") // 도메인 정보
.port(80) // 포트번호
.path("/book/{bookId}") // RequestMapping 어노테이션의 URL 정보에 해당하는 내용
.build().expand(bookId) // PathVariable
.encode()
.toUri();
|
cs |
3.3 HTTP 메서드별 RestTemplate 메서드 명세
HTTP 메서드별 RestTemplate 메서드 형식
메서드명 | HTTP 호출 방식 | 반환 타입 |
getForObject | GET | 객체 |
getForEntity | GET | HttpResponseEntity |
postForObject | POST | 객체 |
postForEntity | POST | HttpResponseEntity |
delete | DELETE | 없음 |
put | PUT | 없음 |
exchange | 사용자 지정 | 사용자 지정 |
댓글