간단한 서버를 만들고, custom hook 을 사용하여 서버에서 데이터를 받아올 수 있다.
Express와 REST API에 대해 알아보자
Express는 굉장히 오래된 Node.js 프레임워크이다.
Next.js처럼 각광받는 프레임워크 뒤에는 기본적으로 Express가 지원할 정도로 표준적이다.
백엔드에 대한 이해없이 프론트만 작업하는 것은 거의 불가능하다. (BFF, Next.js, 테스트에서 모킹)
다음과 같은 상황에는 백엔드를 간단하게 정적이더라도 구성해서 진행할 수 있다.
mkdir express-demo-app
cd mkdir express-demo-app
touch .gitignore
echo "/node_modules/" > .gitignore
npm init -y
npm i -D typescript
npx tsc --init
초기화 설정 할 때 JavaScript modules(import/export)
선택한다.
npm i -D eslint
npx eslint --init
Node는 원래 CommonJs 구현을 기반으로 하기 때문에, CommontJS를 선택해야 한다.
하지만 타입스크립트를 사용할 때는 자바스크립트 모듈(import/export)을 사용할 수 있다.
타입스크립트가 ES6이상의 자바스크립트 문법을 지원하기 때문이다.!
module.exports = {
env: {
browser: true,
es2021: true,
jest: true, // 추가
},
extends: [
'plugin:react/recommended',
'xo',
'plugin:react/jsx-runtime', // 추가
],
overrides: [
{
extends: [
'xo-typescript',
],
files: [
'*.ts',
'*.tsx',
],
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
tsconfigRootDir: __dirname, // 추가 (파일 경로를 제대로 잡기 위해서)
},
plugins: [
'react',
],
rules: {
'import/no-unresolved': 'off', // 추가
},
};
npm i -D ts-node
- ts-node를 쓰는 이유는?
- 타입스크립트로 작업할 때 코드를 바로 실행할 수 있다.
- 따라서 타입스크립트를 자바스크립로 수동 컴파일 하지 않아도 된다.
- 이게 어떻게 가능할까?
- ts-node가 내부적으로 코드를 컴파일하여 실행 가능한 자바스크립트로 변환한다.
npm i express
npm i -D @types/express
타입스크립트에서 사용하기 때문에 types까지 설치한다.
import express from 'express';
const port = 300;
const app = express();
app.get('/', (req, res) => {
res.send('Hello');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
})
localhost:3000
은 사실http://localhost:3000/
이다.
그래서/
로 요청을 할 수 있는 것이다.
import express from 'express';
const port = 3000;
const app = express();
app.get('/', (req, res) => {
res.send('Hello');
});
app.get('/products', (req, res) => {
const products = [
{
category: 'Fruits', price: '$1', stocked: true, name: 'Apple',
},
{
category: 'Fruits', price: '$1', stocked: true, name: 'Dragonfruit',
},
{
category: 'Fruits', price: '$2', stocked: false, name: 'Passionfruit',
},
{
category: 'Vegetables', price: '$2', stocked: true, name: 'Spinach',
},
{
category: 'Vegetables', price: '$4', stocked: false, name: 'Pumpkin',
},
{
category: 'Vegetables', price: '$1', stocked: true, name: 'Peas',
},
];
res.send({products});
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
res.send({products});
이렇게 send 안에 자바스크립트 Object를 넣으면 자동으로 JSON으로 바뀐다.res.send(JSON.stringipy({products}));
하지 않아도 된다.
res.send({products});
말고res.send(products);
이렇게 해도 된다. JSON으로 배열을 주는 게 되기 때문이다.하지만
res.send({product})
잡은 이유는res.send({products, pages: {currentPage: 1, totalPages: 10}});
처럼 메타 정보를 같이 주고 싶을 때 Object 형태로 주기 때문에 그냥 그렇게 잡는 것이다.
코드를 수정하면 서버가 재실행이 된다. (app.ts 뿐만 아니라 프로젝트 내에 모든 코드에 대해 감시를 한다.)
npm i -D nodemon
원래는 코드를 수정할 때마다 서버를 직접 재실행 해야 한다.
nodemon은 ts-node에 대한 의존성이 있다.
그래서 ts-node 설치가 되어 있지 않으면 오류가 난다.
nodemon은 개발 모드에서만 사용한다. (실 서비스에는 사용x)
서버에 올라가 있는 파일을 수정했는데, 서버가 내려가면 안되기 때문이다.
근데 서버에 올라가 있는 파일을 고치는 것 자체도 이상하긴하다.
# nodemon을 설치하지 않았다면?
npx ts-node app.ts
# nodemon을 설치했다면?
npx nodemon app.ts
curl localhost:3000/products
더 예쁘게 볼 수 있다. ⬇️
http localhost:3000/products
"scripts": {
"lint": "eslint --fix .",
"start": "nodemon app.ts"
},
원래 REST하게 하려면 필딩 제약 조건
4가지를 다 만족해야 한다.
하지만 실제로는 4가지를 모두 만족하지 않고, Resource와 HTTP Verb만 도입하는 수준으로 사용한다.
우리는 어떤 URL에 대해 하나의 Resource를 사용한다.
# 이 주소로 접속하면 뭔가 된다.
/write-post (X)
# 리소스에 대해서 우리가 뭔가를 한다
/post (O)
여기서 뭔가
는 CRUD를 의미한다.
CRUD에 대해서는 HTTP Method가 있다. 이걸 이용하면 된다.
예를 들어보자.
기본 리소스 URL: /products
Read는 얻을 수 있는 게 Collection(복수)과 Item(단수)로 나뉜다.
1. Read(Collection): GET /products → 상품 목록 확인
2. Read(Item): GET /products/{id} → 특정 상품 정보 확인
Collection Pattern은 기본 리소스 URL을 사용하여 컬렉션에 있는 데이터를 가리키거나 새로운 리소스를 컬렉션에 추가하는 방법을 제공한다.
예를 들어, POST /produts
는 새로운 리소스를 컬렉션에 추가하는 것이다.
GET /products/{id}
는 컬렉션 내부에 존재하는 개별 리소스들을 가져오는 것이다.
기본 리소스 URL /products
를 이용한 걸 확인할 수 있다.
그래서 Collection Pattern은 일관성 있는 API 디자인을 만들 수 있는 것이다.
3. Create(Collection Pattern 활용): POST /products → 상품 추가 (JSON 정보 함께 전달)
업데이트는 PUT 또는 PATCH를 쓴다.
PUT은 없는 것들에 대해 접근을 하는 경우에는 만들어주고, 아니면 전체를 overwrite(덮어쓰기) 한다.
반면 PATCH는 overwrite이 아닌 일부만 변경한다.
옛날에는 PUT을 많이 썼다고 한다. 왜냐하면 PATCH가 HTTP 스펙에 나중에 들어왔기 때문이다.
그리고 최근에 PATCH를 많이 쓰는 또 다른 이유는특정한 상품 정보를 변경
처럼 전체가 아닌 일부만 업데이트 되는 게 맞기 때문이다.
그리고 PUT, PATCH, DELETE는 기본 브라우저에서 처리할 수 없다. (Form은 GET, POST만 가능하니까) 그래서 이건 Fetch API를 이용해서 처리할 수 있다.
4. Update(Item): PUT 또는 PATCH /products/{id} → 특정 상품 정보 변경 (JSON 정보 함께 전달)
5. Delete(Item): DELETE /products/{id} → 특정 상품 삭제
클라이언트에서 쓰기 위한 Fetch API에 대해 알아보자.