Rest API (Representational State Transfer)
REST를 기반으로 만들어진 API
- 디바이스의 종류에 상관없이 공통으로 데이터 처리할 수 있는 방식
- 하나의 URI는 하나의 고유한 Resource를 대표하도록 설계된다는 개념
- Resource를 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것
- 사용자가 URI(Resource)로 요청 HTTP Method(POST, GET, PUT, DELETE)을 보낼 때
- 사용자가 필요로 하는 결과(데이터 - URI에 대한 CRUD, Representation of Resource) 리턴
Rest API 구성요소
Resource
: URI(ex) user)
서버는 Unique한 ID를 가지는 Resource를 가짐
클라이언트는 이러한 Resource에 요청
Collection : Resource의 집합(ex) users)-
Method
: 서버에 요청을 보내기 위한 방식(GET, POST, PUT, PATCH, DELETE)
CRUD 연산 중에서 처리를 위한 연산에 맞는 Method를 사용하여 서버에 요청을 보내야 함1) GET : 서버로부터 정보를 가져올 때 사용(Read)
2) POST : 리소스를 생성할 때 사용(Create)
3) PUT : 리소스를 수정할 때 사용(Update)
4) DELETE : 리소스를 제거할 때 사용(Delete)참고) Idempotent : 여러 번 수행해도 결과가 같은 경우를 의미
Representation
: 자원에 대한 행위의 내용 = 요청에 대한 Body(형태는 json, xml, text, rss 등)
RESTful
REST의 원리를 따르는 시스템
REST라는 아키텍쳐개념을 그대로 적용해서 웹서버를 구성하고 웹서비스 하는 것
- 웹서버가 RESTful 하다 = REST API 이용할 수 있다
= 웹서버에 구성된 주소(URI)만 알고 있다면 어떤 클라이언트라도 해당 웹서버를 이용할 수 있음 -
REST를 사용했다 하여 모두가 RESTful 하지는 않음
REST API의 설계 규칙을 명확하게 지킨 시스템만이 RESTful 하다고 말할 수 있음
REST 규칙(디자인)
- URI
- 명사 사용
- Non-Resource URL(특정 작업을하고 응답으로 아무것도 하지 않는 경우)에는 동사 사용가능
- 슬래시로 계층 관계 표현
- 소문자로만 구성
- 가독성이 떨어지는 경우 하이픈( _ 대신 - ) 사용
- Path Variable에는 카멜케이스(userId) 사용
- 파일확장자 포함x
- ex) 나쁜예
[GET] /users/1/profile.png
- 클라이언트가 허용할 수 있는 파일 형식 정보가 있는 Accept header를 사용
[GET] ttaehee.github.io/users/1/profile-img Accept: image/jpg
- ex) 나쁜예
- 명사 사용
-
HTTP Method로 표현
[PUT] /posts/1
- 적절한 상태값 응답
-
JSON property에는 카멜케이스 사용
{ userId: "1" userName: "Covenant" }
-
API 버전을 위해서 서수를 사용
https://kapi.kakao.com/v2/user/me
REST의 특징
1) Uniform Interface(일관된 인터페이스)
Resource(URI)에 대한 요청을 통일되고 한정적으로 수행하는 아키텍처 스타일을 의미
- 요청을 하는 Client가 플랫폼(Android, Ios, Jsp 등)에 무관
- 특정 언어나 기술에 종속받지 않음 의미
=> Rest API는 HTTP 사용하는 모든 플랫폼에서 요청가능 + Loosely Coupling(느슨한 결함) 형태 가짐
2) Stateless(무상태성)
클라이언트의 컨텍스트를 서버쪽에 유지 하지 않음 의미
(Server는 각각의 요청을 별개의 것으로 인식하고 처리해야함, 이전 요청이 다음 요청에 연관되어서는 안됨)
= HTTP Session과 같은 컨텍스트 저장소에 상태 정보를 저장하지 않는 형태
=> 서버의 처리방식에 일관성 부여, 서버의 부담을 줄이기 위함
=> Rest API는 서비스의 자유도가 높음(각 API 서버는 들어오는 요청만을 들어오는 메시지로만 처리하면 됨)
서버에서 불필요한 정보를 관리하지 않으므로(컨텍스트 정보를 신경쓸 필요가 없기 때문에) 구현이 단순
3) Cacheable(캐시가능)
- Rest API는 HTTP라는 기존의 웹표준을 그대로 사용 -> 웹의 기존 인프라 그대로 활용가능 -> Rest API에서도 캐싱 가능
- HTTP 프로토콜 표준에서 사용하는 Last-Modified Tag 또는 E-Tag를 이용하여 캐싱 구현가능
=> 대량의 요청 효율적으로 처리가능
(REST 컴포넌트가 위치한 서버에 트랜잭션 발생x -> 전체 응답시간, 성능, 서버의 자원 사용률 비약적으로 향상)
Client가 HTTP GET을 Last-Modified 값과 같이 보냄
-> 컨텐츠 변화가 없으면 REST 컴포넌트는 304 Not Modified 리턴
-> Client는 자체 캐시에 저장된 값을 사용
4) Self-Descriptiveness(자체 표현)
REST API 자체가 매우 쉬워서 API 요청메시지 자체만 보고도 API를 이해할 수 있는 자체 표현 구조로 되어있음
- Resource와 Method로 어떤 메서드에 무슨 행위를 하는지를 알 수 있음
- Message format 역시 JSON을 이용해서 직관적으로 이해가 가능한 구조
HTTP POST, http://localhost:8080/board
{
"board":{
"title":"제목",
"content":"내용"
}
}
localhost:8080/board 로 게시글의 제목, 내용 전달하고 있음
board라는 데이터를 추가(POST)하는 요청임을 쉽게 파악
5) Client-Server Architecture (클라이언트-서버 구조)
- Client : 자원을 요청하는 쪽, 사용자 인증, 컨텍스트(세션, 로그인 정보) 등을 직접 관리하는 역할
- Server : Rest API에서 자원을 가지고 있는 쪽, API 제공하는 역할
=> 역할을 확실히 구분시킴으로써 개발 관점에서 클라이언트와 서버에서 개발해야 할 내용들이 명확하게 되고 서로의 개발에 있어서 의존성이 줄어들게 됨
6) Layered System(계층 구조)
- 클라이언트 입장에서는 REST API 서버만 호출(서버와 직접 통신하는지, 중간 서버와 통신하는지 알 수 없음)
- 서버는 다중 계층으로 구성될 수 있음
- 순수 비즈니스 로직을 수행하는 API 서버와 그 앞단에 사용자 인증(Authentication), 암호화(SSL), 로드밸런싱 등을 하는 계층을 추가해서 구조를 변경할 수 있음
- Proxy, Gateway와 같은 네트워크 기반의 중간매체를 사용할 수 있게 해줌
Spring에서 Rest API 사용
- 1- @RestController
- 클래스 레벨에 선언
- 붙은 클래스의 모든 메서드는 자동으로 @ResponseBody가 적용
- 2- @Controller (이것만 붙어있으면 리턴 String 값의 html 파일을 찾아서 리턴) + @ResponseBody
-
스프링의 message converter에 의해 데이터 자체를 리턴
= Rest 구조로 받을 수 있게 json 형태로 변환시켜줌@ResponseBody @GetMapping("/helloworld") public String helloWorld() { return "helloworld"; }
- @ResponseBody로 인해 helloworld라는 view가 아닌 문자열 그대로 반환
- vo 객체도 반환 가능 -> json형태로 반환
-
- Spring의 REST 관련 Annotation
- @RestController : Controller가 REST 방식을 처리하기 위한 것임을 명시
- @ResponseBody : 일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도
- @PathVariable : URL 경로에 있는 값을 파라미터로 추출하려고 할 때 사용
- @RequestBody : JSON 데이터를 원하는 타입으로 바인딩 처리
- @CrossOrigin : Ajax의 크로스 도메인 문제를 해결
Response 내려주는 방법
@RestController
@RequestMapping("/api")
public class ApiController{
1) text 형태
@GetMapping("/text")
public String text(@RequestParam String account){
return account;
}
-
[GET] localhost:8080/api/text?account=user1
2) json 형태
(User.java DTO 생성 후)
@PostMapping("/json")
public User json(@RequestBody User user){
return user;
}
-
object mapper가 json(text) <-> object 변환을 처리
json(request) -> object mapper -> object
-> method
-> object -> object mapper -> json(response) -
[POST] localhost:8080/api/json
=>
3) ResponseEntity 형태 : 사용자가 직접 응답형태를 수정
언제나 200이 아닌 경우에 맞는 HTTP Status 전달
(Response를 내려줄 때 HTTP Status를 지정해주기)
@PutMapping("/put")
public ResponseEntity<User> put(@RequestBody User user){
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
-
[PUT] localhost:8080/api/put
=>
4) @Controller에서 JSON 사용
- @ResponseBody : @ResponseBody 어노테이션을 통해 @Controller도 Json 형태로 데이터 반환가능
- User.java field : int는 기본 0이 셋팅 되므로 Integer로 변경
@Controller
public class PageController {
@ResponseBody
@GetMapping("/user")
public User user() {
User user = new User();
user.setName("hello")
user.setAddress("경기도");
return user;
}
}
- null 값은 제외하고 싶을 시 User.java에서
- @JsonInclude(JsonInclude.Include.NON_NULL) : Null을 포함시키지 않음
- @JsonInclude(JsonInclude.Include.NON_EMPTY) : 빈값을 포함시키지 않음
Reference
https://sidepower.tistory.com/408
https://mangkyu.tistory.com/46
https://pronist.dev/146
https://rimkongs.tistory.com/221
https://bcho.tistory.com/953
https://100100e.tistory.com/393
https://sharplee7.tistory.com/49
https://covenant.tistory.com/241
https://dreaming5developer.tistory.com/243