💡REST
REST(REpresentational State Transfer)는 소프트웨어 아키텍처 스타일 중 하나로, 자원을 이름으로 구분하고 해당 자원의 상태(정보)를 주고받는 통신 방식을 나타낸다. State는 웹 애플리케이션의 상태를, Transfer는 해당 상태의 전송 방식을 의미한다.
네트워크 상에서 Client와 Server 사이의 통신 방식 중 하나이며, HTTP 프로토콜을 기반으로 하여 웹의 장점을 최대한 활용할 수 있다는 장점이 있다.
REST는 반드시 Spring에서만 사용되는 건 아니며, 다양한 플랫폼 및 언어에서 활용될 수 있다.
📌REST의 구성 요소
자원 (Resource)
자원은 소프트웨어가 관리하는 모든 것을 나타내며, 각 자원은 고유한 식별자(ID)를 가진다. 이 식별자는 서버에 의해 부여되며, 모든 자원은 서버에 존재하게 된다.
ID는 일반적으로 HTTP URI로 표현되며, 클라이언트는 이 URI를 사용하여 특정 자원을 지정하고 해당 자원에 대한 조작을 요청한다.
예로 들어, 학생 정보 자원의 URI가 '/students/student_id'일 때, 클라이언트는 '/students/427'과 같은 URI로 특정 학생의 정보를 요청할 수 있다. 이 URI에서 '427'은 특정 학생을 식별하는 고유한 ID이다.
행위 (Verb)
구분 | 업무 | 사용 예시 | 설명 |
GET | Read | GET/students/123 | 정보를 요청하는 메서드로, 지정한 자원의 표현을 가져온다. 주로 데이터의 조회에 사용된다. |
POST | Create | POST/students | 새로운 자원을 생성하는 메서드로, 클라이언트가 서버에게 새로운 자원을 제출한다. |
PUT | Update | PUT/students/123 | 지정한 자원의 전체를 업데이트하는 메서드로, 클라이언트가 변경된 자원을 전체적으로 업데이트한다. |
PATCH | Update | PATCH/students/123 | 지정한 자원의 일부를 업데이트하는 메서드로, 클라이언트가 변경된 일부 정보만을 업데이트한다. |
DELETE | Delete | DELETE/students/123 | 지정한 자원을 삭제하는 메서드로, 클라이언트가 해당 자원을 삭제 요청한다. |
HTTP 프로토콜의 메서드를 사용하여 자원에 대한 행위를 나타내며, 주로 사용되는 메서드는 GET, POST, PUT, PATCH, DELETE 등이 있다. 각 메서드는 특정한 의미와 역할을 가지며, RESTful API에서는 이를 활용하여 자원을 조작한다.
표현 (Representation)
표현은 자원의 상태를 나타내는 데이터를 의미한다. 주로 JSON 또는 XML 형식으로 나타나며, 클라이언트와 서버 간의 통신에 사용된다.
데이터 교환에서는 표현의 일관성을 유지하는 것이 중요하다. 즉, 서로 다른 클라이언트가 동일한 자원에 대해 동일한 표현을 받아들일 수 있도록 하는 것이 REST의 핵심 원칙 중 하나이다. 이를 통해 시스템의 상호 운용성이 향상되고 통신 간의 신뢰성이 확보된다.
🔎데이터 표현 형식
1. JSON (JavaScript Object Notation)
{
"id": 123,
"name": "Isaac",
"age": 25,
"courses": ["BigData", "BackEnd", "FrontEnd]
}
가벼우면서 가독성이 뛰어나며, 대부분의 프로그래밍 언어에서 파싱 및 생성이 쉽다.
키-값 쌍의 구조를 가지며, 배열을 포함하여 복잡한 데이터 표현이 가능하다.
2. XML (eXtensible Markup Language)
<student>
<id>123</id>
<name>Isaac</name>
<age>25</age>
<courses>
<course>BigData</course>
<course>BackEnd</course>
<course>FrontEnd</course>
</courses>
</student>
계층 구조로 이루어져 있어 데이터를 계층적으로 표현할 수 있다.
태그를 사용하여 데이터를 정의하며, 다양한 형식의 문서를 표현할 수 있다.
🔎URI와 URL의 차이점
- URI (Uniform Resource Identifier): 인터넷상의 자원을 식별하기 위한 문자열의 구성으로, URL을 포함하는 개념이다.
- URL (Uniform Resource Locator): 자원의 위치를 의미하며, URI 중 하나로 자원을 찾는 데 사용된다.
URI와 URL에 대해서는 위 글을 참고한다.
📌REST의 특징
Server-Client (서버-클라이언트 구조)
REST는 명확한 서버-클라이언트 구조를 가지고 있다.
이 구조에서 서버와 클라이언트는 각각 역할이 명확히 정의되어 독립적으로 작동한다.
서버와 클라이언트의 구분
서버의 역할 | 클라이언트의 역할 |
|
|
서버는 API 제공과 비즈니스 로직 처리 등을 담당하며, 클라이언트는 사용자 인증 및 세션 관리와 같은 측면에서 역할을 수행한다. 이러한 명확한 역할 분담은 시스템을 모듈화 하고 서로 간 의존성을 낮추어 유연한 구조를 형성한다.
Stateless (무상태)
무상태성은 각 클라이언트 요청이 서버에 저장된 상태를 가지지 않고, 독립적으로 처리되는 특성을 나타낸다. 이는 클라이언트가 서버에 대한 이전 상태를 계속 유지할 필요가 없다는 원칙을 기반으로 한다.
무상태성의 장점과 특징
서버는 각각의 요청을 완전히 별개의 것으로 취급하며, 이전 요청에 대한 정보나 상태를 저장하지 않는다. 따라서 서버는 클라이언트의 상태를 신경 쓰지 않으며, 클라이언트가 서버로 보내는 요청은 모든 필요한 정보를 포함하고 있어야 한다.
이로써 서버는 복잡한 세션 관리나 상태 추적을 하지 않아도 되기 때문에 서버의 부담이 줄어들고, 각 요청에 대한 응답을 빠르게 처리할 수 있게 된다. 클라이언트가 독립적으로 서비스를 이용하며, 어떠한 순서나 의존성 없이 여러 요청을 수행할 수 있다는 장점을 제공한다. 이는 시스템을 더욱 간결하고 확장 가능하도록 만든다.
Cacheable (캐시 처리 기능)
REST는 HTTP 프로토콜을 사용하므로 캐싱 기능을 적용할 수 있다.
캐시를 통해 한 번 받은 응답을 저장해 두었다가 동일한 요청 시에는 다시 서버에 요청하지 않고 캐시 된 응답을 사용함으로써 중복 요청에 대한 성능을 최적화하며, 네트워크 대역폭을 절약하고 응답 시간을 단축할 수 있다.
캐싱과 조건부 요청
HTTP 프로토콜에서는 캐싱과 조건부 요청을 통해 네트워크 성능을 최적화하고 대역폭을 절약할 수 있다. 이를 위해 서버는 리소스의 상태를 나타내는 정보를 클라이언트에게 전달하는데, 주로 Last-Modified 헤더와 E-Tag를 활용한다.
- Last-Modified 헤더: 서버는 리소스가 마지막으로 수정된 시간을 클라이언트에게 알려주는데, 이 정보는 클라이언트가 리소스의 상태를 추적하는 데 사용된다.
- E-Tag: 서버는 리소스의 고유한 식별자인 E-Tag를 생성하여 클라이언트에게 전송한다. 클라이언트는 이를 사용하여 리소스의 상태를 판단하게 된다.
클라이언트가 리소스를 요청할 때, 이전에 받은 리소스에 대한 정보를 조건으로 함께 전송한다. 이는 주로 If-Modified-Since나 If-None-Match와 같은 헤더를 사용한다. 서버는 이 조건을 검사하여 리소스가 수정되었는지를 판단하고, 수정이 없다면 304 Not Modified 상태와 함께 클라이언트의 캐시를 유지한다.
또한, Cache-Control 헤더를 통해 클라이언트나 중간 캐시가 리소스를 어떻게 캐시할지에 대한 지침을 전달할 수 있다. 이를 통해 캐시의 효율성을 높이고 적절한 캐시 전략을 수립할 수 있다. 캐싱과 조건부 요청은 HTTP 통신에서 효과적인 성능 최적화를 가능케 하며, 네트워크 부하를 최소화하여 사용자 경험을 향상시키는 핵심 메커니즘 중 하나이다.
Layered System (계층 구조)
RESTful 서비스는 계층 구조로 구성될 수 있다. 각 계층은 특정한 역할과 책임을 수행하며, 이는 단일 책임 원칙을 강조한다. 각 계층은 특정 부분에 집중하므로 전체 시스템을 모듈화 하여 유지보수를 쉽게 해 준다.
계층화된 아키텍처의 장점
클라이언트는 REST API 서버만 호출하며, 서버는 다양한 계층으로 나눠질 수 있다. 이를 통해 클라이언트는 서버의 내부 구조를 알 필요 없이 API를 사용할 수 있다. 또한, 중간에 계층을 추가하거나 변경함으로써 서비스에 다양한 기능을 덧붙일 수 있다. 보안 계층, 로드 밸런싱, 암호화 등의 기능을 필요에 따라 중간 계층에서 추가하거나 조정하여 서버와 클라이언트 간의 통신을 보다 안전하고 효율적으로 만들 수 있다.
Uniform Interface (인터페이스 일관성)
REST는 균일한 인터페이스를 제공하여 모든 웹 서비스가 공통의 디자인 원칙을 따를 수 있도록 한다. 이는 서버와 클라이언트 간의 통신을 단순하고 일관되게 만들어준다.
리소스 식별자의 일관성
모든 RESTful 웹 서비스는 균일한 리소스 식별자를 사용하여 요청된 자원을 식별한다. 각 리소스는 고유한 URI를 가지며 이를 통해 클라이언트가 특정 자원을 찾을 수 있다.
서버는 리소스를 자세히 설명하는 메타데이터를 제공하여 클라이언트가 해당 리소스를 충분히 이해하고 활용할 수 있도록 한다. 이는 클라이언트가 리소스를 수정하거나 삭제하는 데 필요한 정보를 효과적으로 전달하는 데 도움이 된다.
메타데이터를 통한 자원 설명
클라이언트는 서버로부터 받은 리소스 표현을 어떻게 처리해야 하는지에 대한 정보를 수신한다. 이를 통해 클라이언트는 리소스를 적절하게 활용하고 서버와의 상호작용을 효과적으로 수행할 수 있다.
서버는 클라이언트가 작업을 완료하는 데 필요한 다른 관련 리소스에 대한 정보를 제공하기 위해 표현에 하이퍼링크를 포함시킨다. 이는 클라이언트에게 더 많은 리소스를 동적으로 찾아 검색할 수 있는 기회를 제공한다.
Self-Descriptiveness (자체 표현)
RESTful API는 자체 표현 구조를 가지고 있어 요청 메시지만으로도 쉽게 이해할 수 있다. 이를 통해 명확한 메타데이터와 함께 클라이언트가 리소스를 효과적으로 활용할 수 있다.
서버는 클라이언트에게 리소스와 관련된 자세한 메타데이터를 제공한다. 이는 리소스의 구조, 속성, 허용된 작업 등을 명확하게 설명하며, 클라이언트는 서버의 응답을 받고 빠르게 이해할 수 있게 된다.
💡RESTful API
REST API는 REST의 특징을 기반으로 서비스 API를 구현한 것으로, 최근 대부분의 기업에서 OpenAPI나 마이크로 서비스와 같은 기술을 통해 REST API를 제공한다.
REST 원리를 따르는 시스템은 RESTful이란 용어로 지칭되며, REST API를 제공하는 웹 서비스는 RESTful 하다고 할 수 있다.
API (Application Programming Interface)
API는 컴퓨터 프로그램들이 서로 효과적으로 상호작용할 수 있도록 데이터와 기능의 집합을 제공하는 도구이다.
간단히 말해, 프로그램들 간의 소통을 원활하게 하고 서로의 기능을 활용하며 데이터를 교환할 수 있도록 해주는 인터페이스를 의미한다.
API는 소프트웨어 개발에서 중요한 역할을 하며, 여러 애플리케이션, 시스템, 서비스가 통합되어 작동할 수 있도록 한다.
RESTful API 설계 규칙
1. URI에 자원을 명시한다.
- 목록 조회: GET /api/products/gadgets
- 개별 자원 조회: GET /api/animals/wildlife/lions
- 자원 생성: POST /api/products/gadgets
- 자원 수정: PUT /api/products/gadgets/{id}
- 자원 삭제: DELETE /api/products/gadgets/{id}
2. URI 구분자는 슬래시 '/'를 사용한다.
- GET /api/categories/electronics
- GET /api/categories/electronics/products/123
3. URI의 마지막에 '/'를 포함하지 않는다.
- GET /api/categories/electronics
4. 하이픈 '-'을 사용한다.
- GET /api/categories/electronics-products
5. URI는 소문자로만 작성한다.
6. 확장자를 사용하지 않는다.
- GET /api/news (O)
- GET /api/news.json (X)
7. Query Parameter로 데이터를 검색한다.
- GET /api/products?category=electronics
8. 리소스 간의 관계 표현
- 관계 표현: GET /api/users/{userid}/devices
- 복잡한 관계 표현: GET /api/users/{userid}/preferences/devices
9. Collection과 Document 표현
- GET /api/sports/football (컬렉션: sports, 도큐먼트: football)
- GET /api/sports/football/players/10 (컬렉션: sports, players, 도큐먼트: football, 10)
HTTP 응답 상태 코드
상태 코드 | 설명 |
200 | 클라이언트의 요청을 성공적으로 처리함 |
201 | 클라이언트의 리소스 생성 요청이 성공적으로 완료됨 (POST를 통한 리소스 생성 작업 시) |
400 | 클라이언트의 요청이 부적절하거나 잘못된 경우 사용하는 응답 코드 |
401 | 클라이언트가 인증되지 않은 상태에서 보호된 리소스를 요청했을 때 사용하는 응답 코드 (로그인 하지 않은 유저가 로그인이 필요한 리소스를 요청했을 때) |
403 | 클라이언트가 요청한 리소스에 접근 권한이 없을 때 사용하는 응답 코드 (유저 인증과는 관계 없이 리소스에 접근 권한이 없는 경우) |
405 | 클라이언트가 요청한 리소스에서 지원하지 않는 Method를 사용한 경우 사용하는 응답 코드 |
301 | 클라이언트가 요청한 리소스에 대한 URI가 변경되었을 때 사용하는 응답 코드 (응답 시 Location 헤더에 변경된 URI를 포함시켜야 함) |
500 | 서버에서 처리 중에 오류가 발생한 경우 사용하는 응답 코드 |
상태코드는 클라이언트에게 서버 응답의 성공, 실패, 원인에 대한 중요한 정보를 전달한다. 이러한 코드 중, 적절한 응답의 상태코드를 사용하여 클라이언트가 문제를 식별하고 해결할 수 있도록 한다.
정확한 응답의 상태코드만으로도 많은 정보를 전달할 수가 있다. 따라서 클라이언트에게 정확한 응답을 제공하는 것은 RESTful API의 핵심이라고 할 수 있다.
REST API와 RESTful API의 차이
REST API와 RESTful API는 둘 다 웹 서비스 간에 상호작을 통해 데이터를 교환하기 위한 도구로 사용되지만, 그 설계 원칙과 특징에서 약간의 차이가 있다.
RESTful API는 REST의 설계 규칙을 잘 따르는 API를 의미하며, 주소만으로도 요청의 내용을 이해하기 쉽고 사용하기 쉽도록 설계된다.
REST API
1. 단순히 HTTP 요청을 통해 웹 서비스에 액세스 할 수 있도록 하는 인터페이스를 의미한다.
2. HTTP 프로토콜을 기반으로 하며, 주로 CRUD 작업을 수행하기 위한 URI 엔드포인트를 제공한다.
3. MVC(Model View Controller) 패턴을 따르지 않아도 되며, 특정한 구현 방식에 제약을 받지 않는다.
RESTful API
1. REST의 설계 규칙을 따르는 API를 의미하며, 주소만으로도 요청의 내용을 쉽게 이해하고 사용할 수 있도록 설계되었다.
2. HTTP 프로토콜과 해당 시맨틱을 사용하여 사람이 사용하기 쉬운 인터페이스를 만드는 아키텍처 스타일이다.
3. URI 엔드포인트의 구조, HTTP 메서드의 사용, 표현 형식 등을 통해 REST의 원칙을 적용하여 일관성 있게 디자인한다.
어떤 API를 선택해야 할까?🤔
단순한 애플리케이션 및 작은 팀에서는 RESTful API가 효과적일 수 있지만, 큰 규모의 프로젝트에서는 구현 및 유지 관리의 어려움을 고려한다면 반드시 Restful API만이 정답이 아닐 수 있다.
RESTful API의 장단점
장점
1. 단순하고 직관적인 디자인을 채택하여 개발자가 쉽게 이해하고 사용할 수 있다.
2. 클라이언트 요청에 따라 JSON, XML 등 다양한 표현 형식을 제공할 수 있어 다양한 클라이언트와 통합하기 용이하다.
3. 표준 프로토콜인 HTTP를 사용하며, 각 요청과 응답은 명확한 규칙에 따라 처리하므로 시스템 간 상호 작용이 일반적으로 더 안정적으로 이루어진다.
단점
1. 표준을 따르기 위한 엄격한 구현은 개발 및 유지 관리를 표준 REST API에 비해 어렵게 만들 수 있다.
2. 특정 구현에 따라 RESTful API는 표준 REST API에 비해 덜 안정적일 수 있다.
📌RESTful API의 활용
Model Class (User.java)
public class User {
private Long id;
private String username;
private String email;
}
Model Class를 생성하고, 필요하다면 생성자, getter, setter 등의 메서드를 구현한다.
Service Class (UserService.java)
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
private final List<User> userList = new ArrayList<>();
public List<User> getAllUsers() {
return userList;
}
public User getUserById(Long id) {
return userList.stream()
.filter(user -> user.getId().equals(id))
.findFirst()
.orElse(null);
}
public User createUser(User user) {
user.setId(generateUserId());
userList.add(user);
return user;
}
public User updateUser(Long id, User updatedUser) {
User existingUser = getUserById(id);
if (existingUser != null) {
existingUser.setUsername(updatedUser.getUsername());
existingUser.setEmail(updatedUser.getEmail());
}
return existingUser;
}
public void deleteUser(Long id) {
userList.removeIf(user -> user.getId().equals(id));
}
private Long generateUserId() {
return userList.isEmpty() ? 1 : userList.get(userList.size() - 1).getId() + 1;
}
}
Controller Class (UserController.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
return userService.updateUser(id, updatedUser);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
UserService 클래스가 비즈니스 로직을 처리하고, UserController 클래스가 RESTful 엔드포인트를 제공한다.
/api/users로 시작하는 URI에 대해 사용자 목록 조회, 특정 사용자 조회, 사용자 추가, 사용자 수정, 사용자 삭제에 대한 API를 제공한다.
REST에 대한 추가 내용은 위 글을 참고한다.
참고자료
RESTful API란 무엇인가요?, amazon, 2024.01.29.
REST API 제대로 알고 사용하기, 김동범, NHN Cloud, 2016.07.25.