[ 살펴보기 ] API - Rest API
![[ 살펴보기 ] API - Rest API](https://cdn.hashnode.com/res/hashnode/image/upload/v1732548065581/ce623ddc-2eed-4dfd-bcf9-75199bc96fc1.jpeg)
API ( Application Programming Interface )는 특정 software와 통신하여 원하는 resource를 요청하거나 특정한 동작을 요구하기 위해 정해진 일종의 규칙이다. 여기서 resource란 image, css, javascript와 같은 file이 될 수도 있고 json, text format의 data가 될 수도 있다.
특정 service에 resource를 요청하거나 특정한 동작을 요구할 때 해당 service가 제공하는 API ( Application Programming Interface )에 따라 원하는 동작이나 resource에 대한 요청을 한다.
Web application을 제작할 때 흔히 사용되는 API 구조 중 하나가 Restful( Representational State Transfer ) APIs이며 Rest APIs라고도 부른다. 그리고 Rest APIs 설계를 위한 일종의 원칙은 다음과 같다.
Client-Server Architecture
Resource를 요청하는 client와 resource를 제공하는 server는 분리한다. Client는 http request를 통해서만 server와 통신하고 server 역시 client request에 대한 response를 통해서 client와 통신한다.
Uniform Interface
Server가 제공하는 resource는 각각 해당 resource를 구분할 수 있는 URI( uniform resource identifier )를 통해 표현한다. 예를 들어 user list resource를 나타내는 URI는 다음과 같이 표현할 수 있다.
https://my-application.com/users
또는 user id가 10인 user는 다음과 같이 표현할 수 있다.
https://my-application.com/users/10
그리고 각각의 URI와 연관된 resource를 표현하기 위해 users, orders와 같은 복수형 명사( a plural noun )를 사용하며 아래와 같이 resource에 대한 행위를 나타내는 단어가 포함되어선 안된다.
https://my-application.com/getUsers ( X )
Resource에 대한 행위는 URI에 포함하는 것이 아닌 client에서 request를 보낼 때 http method를 통해 설정한다. 흔히 사용하는 http method는 다음과 같다.
GET : 특정 resource를 read할 때 사용한다.
POST : 새로운 resource를 생성할 때 사용한다.
PUT, PATCH : 기존 resource를 수정할 때 사용한다.
DELETE : 기존 resource를 삭제할 때 사용한다.
Stateless
모든 client-server communication은 서로 독립적이며 이전의 상태를 유지하지 않는다. 만약 총 세 번의 request-response가 발생했다면 server가 두 번째 request를 처리할 때 첫 번째 request에 대한 상태를 기억하고 있지 않고 세 번째 request를 처리할 때 역시 두 번째 request에 대한 상태를 기억하고 있지 않는다. 그러므로 각각의 request는 원하는 작업에 필요한 데이터를 모두 포함하고 있어야 한다.
Cacheability
Server는 client가 요청한 resource가 client side에서 cache할 수 있는지 여부에 대한 정보를 response에 함께 포함하여 전달한다.
Layered System
Proxies, load balancer등을 application server 앞단에 두어 server를 계층적 구조로 구축할 수 있으며 client는 server의 계층 구조와는 무관하게 application server가 제공하는 URI를 통해 resource를 요청한다.
Code on Demand
RestAPI는 보통 json data과 같은 static resource를 response로 전달하지만 필요시 server가 client side에서 실행 가능한 script를 전달함으로서 client에서 특정 기능을 수행할 수 있다.
Best Practices
Rest API 설계 시 일반적으로 best practices로 여겨지는 사항은 다음과 같다.
Resource의 표현은 명사를 사용 : 위에서도 언급되었지만 URI가 표현하는 resource는 복수형 명사( a plural noun )를 이용하여 표현한다. 예를 들어 상품 주문 ( order ) 목록은 다음과 같이 표현한다.
https://my-application.com/orders적절한 status code사용 : Client request에 대한 response 결과에 맞는 적절한 status code를 사용한다. 예를 들어 post request를 통해 resource가 성공적으로 생성되었다면 201 status code를 만약 resource를 요청하는 client가 resource에 대한 권한이 없다면 401 status code를 response의 status code로 설정하여 전달한다.
API versioning : 새로운 api가 기존 api에 의존하는 client에 대한 호환성을 깨드리지 않을 수 있도록 API versioning을 적용한다. 예를 들어 다음은 1 version API의 order list와 2 version API의 order list의 resource를 나타내는 URI이다.
https://my-application.com/v1/orders https://my-application.com/v2/ordersfilter, pagination 적용 : 지나치게 많은 데이터를 response로 전달하려고 하면 API server에 부하를 줄 수 있다. pagination과 filter등을 적용하여 response로 전달하는 data가 지나치게 커지지 않게 유지한다. 예를 들어 order list에 대한 pagination을 적용할 때는 다음과 같이 query param을 URL에 추가할 수 있다.
https://my-application.com/v1/orders?offset=0&limit=10
Examples
이제 Rest API를 구현한 간단한 예제를 살펴보자. 예제는 NodeJS Hono framework를 사용하여 작성되었으며 member data resource를 조회, 추가, 수정, 삭제하는 기능을 제공한다.
Member list를 조회하는 endpoint의 경우 pagination을 위해 limit, offset을 query parameter로 전달 받아 사용한다. 그리고 특정 member의 order list data를 조회하기 위해 /members/:id/orders uri를 제공하고 있다.
import "dotenv/config";
import { serve } from "@hono/node-server";
import { Hono } from "hono";
const app = new Hono();
app.get(`/v1/members`, async (c) => {
const { limit, offset } = c.req.query();
...
});
app.get(`/v1/members/:id`, async (c) => {
const { id } = c.req.param();
...
});
app.get(`/v1/members/:id/orders`, async (c) => {
const { id } = c.req.param();
const { limit, offset } = c.req.query();
...
});
app.post(`/v1/members`, async (c) => {
...
});
app.patch(`/v1/members/:id`, async (c) => {
const { id } = c.req.param();
...
});
app.delete(`/v1/members/:id`, async (c) => {
const { id } = c.req.param();
...
});
const port = 3000;
serve({
fetch: app.fetch,
port,
});
![[ 살펴보기 ] RDB - Relationships](https://cdn.hashnode.com/res/hashnode/image/upload/v1739711556668/48dc9e84-a621-42aa-9c9f-5fc5c436f0ec.jpeg)
![[ 살펴보기 ] MySQL - Data types](https://cdn.hashnode.com/res/hashnode/image/upload/v1739593589113/530f8704-4d27-42c9-a451-bb5c63150b99.jpeg)
![[ 살펴보기 ] TypeORM - Transactions, Migration](https://cdn.hashnode.com/res/hashnode/image/upload/v1739106042581/980b8133-61d4-406a-a026-65be9c28eace.jpeg)
![[ 살펴보기 ] TypeORM - Relations](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666874402/b688bd0b-b6bb-4f43-87d8-c1b46b59f1b7.jpeg)
![[ 살펴보기 ] TypeORM - Basics](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666803591/bef5df17-7dc7-4123-ae55-004d5042df39.jpeg)