1. Express
Express ๋
URL ๊ตฌ์กฐ
REST API
HTTP Method(CRUD)
๊ฐ์ ์ ๋ฆฌ
Express
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ผ๊ณ ํด๋ BFF(Backend for Frontend) ํน์ NextJS๋ฑ ๋ฐฑ์๋์ ๋ํ ์ง์์ด ํ์ํ๋ค. ํ ์คํธ ์ฝ๋์์ Mocking์ ํ ๋๋ ํ์ํ๋ค.
Express๋ Node.js ๊ธฐ๋ฐ์ ์๋ฒ ํ๋ ์์ํฌ. ๊ต์ฅํ ์ญ์ฌ๊ฐ ์ค๋๋ ํ๋ ์์ํฌ๋ค. NestJS, Apollo์์๋ Express๋ฅผ ์ง์ํ๋ค.
๊ฐ๋จํ ์๋ฒ ์ฑ npm ํจํค์ง ์ธํ
๊ณต์๋ฌธ์๋ JS ๊ธฐ๋ฐ์ผ๋ก ๋์์๊ธฐ ๋๋ฌธ์ TS ์ง์์ ์ํด ์ถ๊ฐ์ ์ธ ์ค์น๋ฅผ ํด์ค ๊ฒ.
ํ๋ก์ ํธ ํด๋ ๊ตฌ์ฑ
mkdir express-demo-app
cd express-demo-app
.gitignore ํ์ผ์ ํญ์ ์์ฑํด์ฃผ๊ธฐ
touch .gitignore
echo "/node_modules/" > .gitignore
ํจํค์ง ์ด๊ธฐํ
npm init -y
TypeScript
npm i -D typescript
npx tsc --init
Eslint
npm i -D eslint
npx eslint --init
# import/export syntax ์ ํ
# Node ํ๊ฒฝ ์ ํ
# Airbnb ์ ํ
ts-node
npm i -D ts-node
Express
npm i express
npm i -D @types/express
localhost:3000์ ์ฃผ์์ฐฝ์ ์ ๋ ฅํ์ ๋ ์์ ํ ํํ๋ http://localhost:3000/ ์ด๋ค. localhost๋ 127.0.0.1์ด๋ค. ์๊ธฐ ์์ ์ ๊ฐ๋ฆฌํค๋ ์ฃผ์, ๋ฃจํ๋ฐฑ ์ฃผ์๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค.
Hello World ์์
app.ts ํ์ผ ์์ฑ
res.send์ res.json์ ์ฐจ์ด์
res.send๋ argument๋ฅผ ํ๋จํ์ฌ Content-Type์ ์ก์์ค๋ค. argument๊ฐ object์ด๋ฉด ๋ด๋ถ์์๋ res.json์ ํธ์ถํ๋ค. res.json์ Content-Type์ด json์ด ์๋ ๊ฒฝ์ฐ application/json์ผ๋ก ์ธํ ํ๊ณ res.send๋ฅผ ํธ์ถํ๋ค. Content-Type์ด ๋ค๋ฅธ์ ์ด๋ผ ์๊ฐ๋๊ณ json ํ์์ ๋ฐํํด์ฃผ๋ ๊ฒฝ์ฐ๋ฅผ ๋ช ์์ ์ผ๋ก ํํํ ๋๋ res.json๋ ์ข์ ๊ฒ ๊ฐ์ผ๋, argument๋ฅผ ๋ณด๊ณ ์ ์ถฉ๋ถํ ํ๋จํ ์ ์์ผ๋ฏ๋ก ํฐ ์๋ฏธ๋ ์๋ ๊ฒ ๊ฐ๋ค.
// app.ts
// TypeScript์ด๊ธฐ ๋๋ฌธ์ ESModule(import, export)์ ์ฌ์ฉํ๋ค.
import express from 'express';
// ์๋ฒ์ ํฌํธ ๋ฒํธ๋ ํ๊ฒฝ๋ณ์๋ก ๋ง์ด ๊ด๋ฆฌํ๋ ๊ฒ ๊ฐ๋ค.
const port = 3000;
const app = express();
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
์คํ์ ts-node๋ฅผ ์ด์ฉํด์ ํ ์ ์์ผ๋, ์ฝ๋๊ฐ ์์ ๋์์ ๋๋ง๋ค ์๋ฒ๋ฅผ ์ฌ์์ํด์ฃผ๊ธฐ ์ํด nodemon์ ์ฌ์ฉํ๋ ๊ฒ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ํธํ๋ค. local์์๋ global๋ก ์ค์นํด์ ์ฌ์ฉํ๋ ๊ฒ ํธํ ๊ฒ ๊ฐ๋ค.
npm i -g nodemon
nodemon app.ts
REST API
Roy Fielding - โArchitectural Styles and the Design of Network-based Software Architecturesโ (2000)
๋๊ฐ๋ "ํ๋ฉ ์ ์ฝ ์กฐ๊ฑด" 4๊ฐ์ง๋ฅผ ๋ชจ๋ ๋ง์กฑํ์ง ์๊ณ , Resource์ HTTP Verb๋ง ๋์ ํ๋ ์์ค์ผ๋ก ์ฌ์ฉํ๋ค.
/write-post ์ ๊ฐ์ด ์์ฑํ์ง ์๊ณ
/posts ์ฒ๋ผ ์์ฑํ๋ค. (CRUD)
CRUD์ ๋ํด HTTP Method๋ฅผ ๋์ ํ๋ค. Read๋ Collection(๋ณต์)๊ณผ Item(Element)(๋จ์)๋ก ๋๋๋ค.
๊ธฐ๋ณธ ๋ฆฌ์์ค URL: /products
Read (Collection) -> GET /products => ์ํ ๋ชฉ๋ก ํ์ธ
Read (Item) -> GET /products/{id} => ํน์ ์ํ ์ ๋ณด ํ์ธ
Create (Collection Pattern ํ์ฉ) -> POST /products => ์ํ ์ถ๊ฐ (JSON ์ ๋ณด ํจ๊ป ์ ๋ฌ)
Update (Item) -> PUT ๋๋ PATCH /products/{id} => ํน์ ์ํ ์ ๋ณด ๋ณ๊ฒฝ (JSON ์ ๋ณด ํจ๊ป ์ ๋ฌ)
Delete (Item) -> DELETE /products/{id} => ํน์ ์ํ ์ญ์
GET์ request body์ ๊ฐ์ ๋ฃ์ด์ค ์ ์๋ค.
PUT์ด ๋จผ์ ๋์๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์๋ PUT์ ๋ง์ด ์ฌ์ฉํ์ง๋ง ์ด์ PATCH๋ ๋ง์ด ์ฌ์ฉํ๋ค.
PUT์ overwrite, ๋ฎ์ด์ฐ๋ ๋๋. ์์ผ๋ฉด ์ถ๊ฐํ๊ณ , ์์ผ๋ฉด ๋ฎ์ด์ด๋ค.
PATCH๋ ์ผ๋ถ๋ง ์์ ํ๋ ์ ๋ฐ์ดํธ๋ฅผ ํ๋ ๋๋.
DELETE๋ DB์ row๋ฅผ hard deleteํ์ง ์๊ณ soft delete๋ฅผ ํ ๋๋ DELETE Method๋ฅผ ์ฌ์ฉํ๋ค.
์ค์ ๋ก๋ row์ ํน์ ์ปฌ๋ผ๊ฐ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด์ง๋ง, ๋ฆฌ์์ค ์ ์ฅ์์ ๋ณผ ๋๋ ์ญ์ ๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ DELETE Method๋ฅผ ์ฌ์ฉํ๋ค.
Thinking in React ์์
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 });
});
BFF๋?
์นด์นด์คํ์ด์ง์์ BFF ์ ์ฉ๊ธฐ ์นด์นด์คํ์ด์ง์์ ๊ฒช์๋ ๋ฌธ์
์ฌ๋ฌ ํ๋ซํผ(Web, Android, iOS, ...)์ ์ง์ํ๊ฒ ๋๋ฉด์ ๊ฐ๊ฐ ํน์ ๋ฐ์ดํฐ๊ฐ ํ์ํ ์ํฉ
์ํ๋ ๋ฐ์ดํฐ ํํ์ ๋๋ฌํ๊ธฐ ์ํด ์ฌ๋ฌ API ํธ์ถ์ ์๋ต์ ์กฐ์, ํผํฉ, ์ผ์น์ํค๋ ์ํฉ
์ด๋ฐ ์ํฉ๋ค์ด ๊ฒน์ณ ํ๋ก ํธ์๋์์ ๋ณต์กํ ๊ณ์ฐ์ด๋ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํ๋ ์ํฉ
ํ๋ก ํธ์๋์์ ๋ณต์กํ ๊ณ์ฐ์ ์ํํ๋ ๊ฒฝ์ฐ ๋ ๋๋ง์ด ๋๋ ค์ง ์ ์๋ค. UI ์ค๋ ๋์์ ๋ ๋๋ง๊ณผ ๋น์ฆ๋์ค ๋ก์ง ์ํ์ด ๊ฒฝํฉ์ ๋ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ ๋๋ง์ ์ ์ ๊ฒฝํ๊ณผ ๋งค์ฐ ๋ฐ์ ํ ๊ด๋ จ์ด ์๋ค. ์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์๋ ์ด๋ ํ ๋ฐฉ๋ฒ์ด ์์๊น?
๋ค์ํ ํ๋ซํผ์ ์ง์ํด์ผ ํ๋ API๋ ํด๋ผ์ด์ธํธ๋ง๋ค ์ฌ์ฉํ์ง ์๋ ๋ถํ์ํ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋ ์ ์๋ค. ์ถ๊ฐ๋ก ์ง์ API์ ์์กดํ๋ ๊ฒฝ์ฐ์ ์๋์ ๊ฐ์ ์ด์๊ฐ ๋ฐ์ํ ์ ์๋ค.
MSA ํ๊ฒฝ์์ API ์๋ํฌ์ธํธ๊ฐ ๋ถ๋ฆฌ๋ ๋ ํ๋ก์ ์ด์
CORS
API ์ ์ฅ์์ ์ฌ๋ฌ ํ๋ซํผ๊ณผ ์คํ์ ๋ง์ถฐ์ผ ํ๋ ๋น์ฉ
ํ๋ซํผ๋ณ๋ก ๋ค๋ฅธ ์ธ์ฆ ๋ฐฉ์์ ํตํฉํ๋ ค๋ ๋ฌด๋ฆฌํ ์๋
'ํ๋ฉด์ ํ์ํ ๋ฐ์ดํฐ๋ง ๋ฐ๋' partial response๋ฅผ ํ๊ธฐ ์ด๋ ค์ด ์ด์
์ด์ ๊ฐ์ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด BFF๊ฐ ๋ฑ์ฅํ๋ค. ๋ง ๊ทธ๋๋ก ํ๋ก ํธ์๋๋ฅผ ์ํ ์ค๊ฐ ์๋ฒ๋ฅผ ๊ตฌํํ๋ ๊ฒ ํ๋์ ํ๋ก ํธ์๋์ ๋ํด ํ๋์ BFF๋ฅผ ๋์ด์ ํ๋ก ํธ์๋ ์๊ตฌ์ฌํญ์ ๋ง๊ฒ ๊ตฌํํ ์ ์๋ค. ์ฌ๋ฌ ํ๋ซํผ์ ์ง์ํ์ง ์์ ๊ฒฝ์ฐ์๋ BFF๊ฐ ์๋ฏธ ์์ ์ ์๋ค.
์นด์นด์ค ํ์ด์ง์์๋ iOS, Android, Web์ ์ง์ํ์ง๋ง, Web์์๋ง BFF๋ฅผ ์ ์ฉํ๊ณ ์๋ค. BFF์์๋ ์์ฐ์ฑ์ ๋์ด๊ธฐ ์ํด ๋ฐ์ดํฐ๋ฅผ ํตํฉํ๋ ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํ๋ค. BFF๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ ์์์ ๋งํ API ์์กด์ฑ ์ด์๋ฅผ ์ฒ๋ฆฌํด์ค ์ ์๋ค.
์นด์นด์ค ํ์ด์ง์์๋ ํด๋ผ์ด์ธํธ์์ ๋ฐ์์ผํ๋ ๋ฐ์ดํฐ์ ํ์์ด ์ฌ๋ฌ๊ฐ์ง์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์์ BFF๋ฅผ ๋์ ํ๊ฒ ๋์๋ค. ์นด์นด์ค ํ์ด์ง์ BFF ๊ตฌ์กฐ๋ NextJS, apollo server, urql, redux๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค.
ts-node๋?
JavaScript๋ฅผ ์น ๋ธ๋ผ์ฐ์ ๊ฐ ์๋๊ณณ์์ ์คํํ๊ธฐ ์ํด์๋ JavaScript Runtime์ธ Node.js๋ฅผ ์ฌ์ฉํ๋ค. TypeScript๋ฅผ ์คํํ๊ธฐ ์ํด์๋ ๋จผ์ JavaScript ์ปดํ์ผํ๋ ๊ณผ์ ์ด ํ์ํ๋ฐ ์ด๋ฅผ ์๋ตํ๊ณ TypeScript๋ก ์์ฑ๋ ํ์ผ์ ์คํํ ์ ์๊ฒ ํด์ค๋ค.
ts-node๋ Node.js๋ฅผ ์ํ TypeScript ์คํ ์์ง, REPL์ด๋ค.
JIT ์ปดํ์ผ๋ฌ๋ฅผ ์ด์ฉํ์ฌ TypeScript๋ฅผ JavaScript๋ก ๋ณํํด์ฃผ๊ณ , ํ๋ฆฌ์ปดํ์ผ์์ด ํ์ ์คํฌ๋ฆฝํธ๋ฅผ Node.js์์ ์คํํ ์ ์๊ฒ ํด์ค๋ค.
Last updated