오늘은 비슷해 보이지만 다른 REST API 와 RESTful API의 차이점과 사용 예를 알아보려고 합니다.
REST API는 (Representational State Transfer)라는 아키텍처 스타일에 맞춰 설계된 API(Application Programming Interface)를 말합니다. 또한 클라이언트와 서버간의 상호작용을 규정하며 여러가지 제약조건을 가지고 있습니다. 예를 들어 REST는 상태를 가지지 않는 Stateless(무상태성)통신, 캐시 가능한 응답, 레이어 시스템 등을 지향합니다.
RESTful API는 REST 아키텍처 스타일을 충실히 따르는 API를 의미합니다. 즉, REST의 기본 원칙을 잘 지키면서 API가 설계된 경우 이를 RESTful API라고 부릅니다. 예를 들어 RESTful API는 데이터와 리소스를 URI를 통해 식별하고, HTTP 메서드(GET, POST, PUT, DELETE 등)를 사용하여 서버 자원에 접근하는 방식입니다.
REST API와 RESTful API는 자주 혼용되지만, 미묘한 차이가 있습니다.
REST : 웹상 자원을 이름으로 구분하고 해당 리소스의 상태를 주고 받는 모든 것을 의미합니다.
기존 웹의 기술과 HTTP 프로토콜을 그대로 활용하는 아키텍처 스타일 입니다.
RESTful : REST를 기반으로 만들어진 API이며 REST원칙을 잘 따른 서비스 API를 의미합니다.
즉 REST 원칙을 잘 따른 시스템을 RESTful하다고 표현합니다.
리소스는 명사로 표현: URI는 리소스를 나타내며, 명사로 표현해야 합니다. 동사는 사용하지 않습니다.
올바른 예: /users, /products
잘못된 예: /getAllUsers, /createProduct
URI경로에 소문자 사용하기
붙임표(-)는 가독성을 높이는데 사용하기
밑줄( _ )은 URI에 사용하지 않는다.
HTTP 메서드 활용하기 : CRUD 작업에 맞는 HTTP 메소드로 표현해야합니다.
GET: 데이터 조회
POST: 데이터 생성
PUT: 데이터 전체 수정
PATCH: 데이터 일부 수정
DELETE: 데이터 삭제
Stateless(무상태성) : 각 요청은 독립적이어야 하며, 서버는 상태를 유지하지 않아야 합니다.
응답 코드를 활용하기 : 클라이언트는 해당 요청에 대한 처리 상태를 알 수 있어야 합니다.
(에러코드 번호)
200 OK : 성공적인 요청 처리
201 Created : 리소스 생성 성공
400 Bad Request : 잘못된 요청
401 Unauthorized : 유효하지 않은 인증
404 Not Found : 리소스를 찾을 수 없음
500 Internal Server Error : 서버 에러
디자인 : 계층관계를 슬래시(/)로 표현하고, URI마지막에 슬래시를 포함하지 않습니다.
올바른 예 : /users/{id}/posts
잘못된 예 : /users/{id}/posts/
파일 확장자는 포함하지 않기
올바른 예 : http://example.com/api/photo
잘못된 예 : http://example.com/api/photo.jpg
조회시 쿼리를 활용하자 : 페이지나 필터링 정보는 쿼리 파라미터를 활용합니다.
올바른 예 : /users?page=2&count=5
리소스 간에는 연관 관계가 있는 경우에는 '/리소스명/리소스ID/관계가 있는 다른 리소스명'으로 표현
REST API의 장점:
기술 중립성(Technology Agnostic): REST는 HTTP를 기반으로 하므로, 언어나 플랫폼에 구애받지 않고 다양한 환경에서 구현할 수 있습니다.
스케일 확장성(Scalability): REST는 무상태성을 가지고 있어 각 요청이 독립적입니다. 이로 인해 서버와 클라이언트가 각각 독립적으로 확장될 수 있어 높은 확장성을 가집니다.
캐싱 가능(Cacheable): HTTP 프로토콜의 캐싱 기능을 그대로 사용할 수 있어, 응답 결과를 캐싱할 수 있습니다. 이를 통해 서버 부하를 줄이고 성능을 향상시킬 수 있습니다.
자기 설명성(Self-Descriptive): RESTful API는 자기 설명적이므로, API 문서 없이도 요청을 보내고 응답을 해석하는 것이 비교적 쉽습니다.
로스 결합성(Loose Coupling): 클라이언트와 서버가 각각 독립적으로 개발될 수 있어, 한 쪽에서 변경이 일어나더라도 다른 쪽에 큰 영향을 주지 않습니다.
상태 전이의 용이성(Stateless Interactions): 각 요청이 독립적이므로, 서버는 별도의 세션 관리를 할 필요가 없습니다. 이로 인해 서버 구현이 단순해집니다.
표준화된 통신: HTTP 표준 프로토콜을 사용하므로, 표준 HTTP 라이브러리를 이용해 쉽게 개발할 수 있습니다.
커뮤니티와 지원: REST가 널리 사용되고 있으므로, 다양한 커뮤니티와 라이브러리, 도구 등의 지원을 받을 수 있습니다.
이러한 장점들 덕분에 REST는 현재까지도 많은 웹 서비스에서 선호되는 아키텍처 스타일입니다.
REST API의 단점:
무상태성(Statelessness): REST는 무상태성을 가지고 있어 세션과 같은 상태 정보를 기억하지 않습니다. 이로 인해 각 요청마다 인증 등의 정보를 다시 보내야 할 수도 있습니다.
HTTP 메소드 제한: REST는 기본적으로 HTTP 메소드를 사용하는데, 이는 상대적으로 제한적입니다. 복잡한 작업을 수행하기 위해서는 여러 API 호출을 조합해야 할 수 있습니다.
오버페칭과 언더페칭: 클라이언트가 필요로 하는 데이터만 정확히 가져오기가 어렵습니다. 그래서 불필요한 데이터를 가져오게 되거나, 여러 번의 요청을 해야 원하는 데이터를 완전히 가져올 수 있습니다.
버전 관리: RESTful API는 버전 관리가 복잡할 수 있습니다. 새로운 버전의 API를 출시할 때, 이전 버전과의 호환성을 유지하기 위한 추가 작업이 필요할 수 있습니다.
캐싱 제한: HTTP의 캐싱 기능은 효율적이지만, 무상태성으로 인해 실시간으로 변경되는 데이터에 대한 캐싱이 어려울 수 있습니다.
트랜잭션 지원 부족: REST는 분산 트랜잭션을 지원하지 않기 때문에, 여러 리소스에 대한 동시 업데이트와 같은 복잡한 트랜잭션을 수행하기 어렵습니다.
각 단점에는 그에 대응하는 다양한 해결 방법과 패턴들이 있지만, 이러한 문제들을 완전히 피할 수는 없습니다. 따라서 개발 시에는 이러한 단점들을 잘 고려하여 설계해야 합니다.
RESTful API의 장점:
웹 인프라 활용: HTTP 프로토콜을 그대로 사용하므로, 웹 인프라를 그대로 활용할 수 있습니다. 이로 인해 부하 분산, 보안 등의 기능을 쉽게 구현할 수 있습니다.
자기 설명적: RESTful API는 자기 설명적(self-descriptive)이므로 쉽게 이해하고 사용할 수 있습니다. 이는 API 문서화에도 유리하며, 개발자간의 협업을 간편하게 만들어 줍니다.
RESTful API의 단점:
무상태성: REST는 무상태성(statelessness)을 갖으므로, 세션과 같은 상태 정보를 관리하기 어렵습니다. 이로 인해 각 요청마다 인증 정보를 전달해야 할 수도 있으며, 이는 보안에 취약할 수 있습니다.
HTTP 메소드 한정: REST는 HTTP 메소드(GET, POST, PUT, DELETE 등)에 의존하는데, 이로 인해 메소드가 제한적이어서 특정 작업을 표현하기 어렵습니다. 예를 들어, 이메일을 보내고 결과를 가져오는 것을 하나의 API 호출로 만들기가 어렵습니다.
RESTful API는 언제 사용하나요?
웹 기반 간단하거나 복잡한 서비스를 제공할때 주로 사용됩니다. 자기 설명적이고 확장성이 뛰어나
다양한 프로젝트에 적합 합니다.
RESTful API는 왜 사용하나요?
웹 인프라의 장점을 최대한 활용하면서 간결하고 이해하기 쉬운 API를 설계할 수 있기 때문입니다.
예시 코드
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Post {
private Long id;
private String title;
private String content;
}
@Service
public class PostService {
private List<Post> posts = new ArrayList<>();
private Long currentId = 1L;
public Post createPost(Post post) {
post.setId(currentId++);
posts.add(post);
return post;
}
public List<Post> getAllPosts() {
return posts;
}
public Post getPostById(Long id) {
return posts.stream().filter(post -> post.getId().equals(id)).findFirst().orElse(null);
}
public Post updatePost(Long id, Post post) {
Post existingPost = getPostById(id);
if (existingPost != null) {
existingPost.setTitle(post.getTitle());
existingPost.setContent(post.getContent());
return existingPost;
}
return null;
}
public boolean deletePost(Long id) {
return posts.removeIf(post -> post.getId().equals(id));
}
}
REST API 예시 코드
@RestController
@RequestMapping("/api")
public class RestApiController {
private final PostService postService;
public RestApiController(PostService postService) {
this.postService = postService;
}
// POST /api/createPost
@PostMapping("/createPost")
public Post createPost(@RequestBody Post post) {
return postService.createPost(post);
}
// GET /api/getAllPosts
@GetMapping("/getAllPosts")
public List<Post> getAllPosts() {
return postService.getAllPosts();
}
// GET /api/getPost/{id}
@GetMapping("/getPost/{id}")
public Post getPost(@PathVariable Long id) {
return postService.getPostById(id);
}
// PUT /api/updatePost/{id}
@PutMapping("/updatePost/{id}")
public Post updatePost(@PathVariable Long id, @RequestBody Post post) {
return postService.updatePost(id, post);
}
// DELETE /api/deletePost/{id}
@DeleteMapping("/deletePost/{id}")
public boolean deletePost(@PathVariable Long id) {
return postService.deletePost(id);
}
}
RESTful API 예시 코드
@RestController
@RequestMapping("/api/posts") // 자원 중심 URL 사용
public class RestfulApiController {
private final PostService postService;
public RestfulApiController(PostService postService) {
this.postService = postService;
}
// POST /api/posts
@PostMapping
public Post createPost(@RequestBody Post post) {
return postService.createPost(post);
}
// GET /api/posts
@GetMapping
public List<Post> getAllPosts() {
return postService.getAllPosts();
}
// GET /api/posts/{id}
@GetMapping("/{id}")
public Post getPost(@PathVariable Long id) {
return postService.getPostById(id);
}
// PUT /api/posts/{id}
@PutMapping("/{id}")
public Post updatePost(@PathVariable Long id, @RequestBody Post post) {
return postService.updatePost(id, post);
}
// DELETE /api/posts/{id}
@DeleteMapping("/{id}")
public boolean deletePost(@PathVariable Long id) {
return postService.deletePost(id);
}
}
차이점 설명
REST API:
URI 구조는 동사 중심으로 되어 있으며, 각 작업을 명시적으로 표현합니다. 예를 들어, 게시글 생성은 /api/createPost, 게시글 조회는 /api/getPost/{id}와 같이 URI에 동사를 포함하고 있습니다.
이는 API의 직관성을 떨어뜨릴 수 있으며, REST의 장점을 충분히 활용하지 못합니다.
RESTful API:
URI 구조는 자원 중심으로 되어 있으며, HTTP 메서드를 통해 작업을 정의합니다. 예를 들어, 게시글 생성은 /api/posts, 조회는 /api/posts/{id}와 같이 자원을 명확히 표현합니다.
이 접근 방식은 REST 원칙을 준수하며, API를 사용하는 개발자에게 더 많은 직관성을 제공합니다.