Post-blog-Client & Server

post-blog-client & server

Client

๊ฐ„๋‹จํ•œ ํฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑ, ์ฝ๊ธฐ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ ํ•  ์ˆ˜ ์žˆ๋Š” CRUD ๋ธ”๋กœ๊ทธ ์›น ์•ฑ์˜ client์ž…๋‹ˆ๋‹ค.

๊นƒํ—ˆ๋ธŒ ์†Œ์Šค์ฝ”๋“œ

์Šคํƒ€์ผ

styled-components๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜์˜€๊ณ 

๋ผ์šฐํ„ฐ

react-router-dom๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ
LoginPage, RegisterPage, WritePage, PostPage, PostListPage ์ด 5๊ฐœ์˜ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” HTTP ํ—ค๋”์— โ€˜last-pageโ€™๋ฅผ ๋ฐ›์•„์™€์„œ PostListPage์—์„œ ํŽ˜์ด์ง€๋„ค์ด์…˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ƒํƒœ๊ด€๋ฆฌ

React-redux๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค
Ducks ํŒจํ„ด์œผ๋กœ redux๊ด€๋ จ ํŒŒ์ผ์€ ๋ชจ๋‘ modules์— ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

Container-Presenter ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ์„ ํ•˜๋Š” ํŒŒ์ผ๋“ค์„ ๋‚˜๋ˆ ์„œ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Container Components๋Š” containers
Presentational Components๋Š” components
ํด๋”์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API ์„œ๋ฒ„์™€ ์—ฐ๋™

axios๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

Redux-saga ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ API ์š”์ฒญ๊ณผ ๊ฐ™์€ ๋น„๋™๊ธฐ์  ์ž‘์—…์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌ

๋ฐฐํฌํ•˜๋Š” ์›น์„œ๋ฒ„๋ฅผ nginx๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  API๋ฅผ ์ œ๊ณตํ•˜๋Š” post-blog-server ์„œ๋ฒ„์—์„œ koa-static์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •์  ํŒŒ์ผ์„ ์ œ๊ณตํ•˜๋„๋ก ํ†ตํ•ฉํ•ด์„œ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ทธํ›„์— Docker๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ๋œ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๋ฅผ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์—ˆ๊ณ 

Google Cloud Run์— ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์˜ฌ๋ ค์„œ ๋ฐฐํฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Young-blog ๋ฐฐํฌ๋œ ์‚ฌ์ดํŠธ์ž…๋‹ˆ๋‹ค.


page

ํšŒ์›๊ฐ€์ž…

โ€œ/registerโ€

๋กœ๊ทธ์ธ

โ€œ/loginโ€

ํฌ์ŠคํŠธ ์ž‘์„ฑ

โ€œ/writeโ€

ํฌ์ŠคํŠธ ๋ชฉ๋ก ์กฐํšŒ

โ€/โ€ ,โ€/?tagโ€ , โ€œ/@:usernameโ€

ํฌ์ŠคํŠธ ์กฐํšŒ

โ€/@:username/:postIdโ€


Environments

  • axios@0.21.1
  • immer@9.0.1
  • qs@6.10.1
  • quill@1.3.7
  • react@17.0.1
  • react-dom@17.0.1
  • react-helmet-async@1.0.9
  • react-redux@7.2.2
  • react-router-dom@5.2.0
  • react-scripts@4.0.3
  • redux@4.0.5
  • redux-actions@2.6.5
  • redux-devtools-extension@2.13.9
  • redux-saga@1.1.3
  • styled-components@5.2.1

Server

๊ฐ„๋‹จํ•œ ํฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑ, ์ฝ๊ธฐ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ ํ•  ์ˆ˜ ์žˆ๋Š” CRUD ๋ธ”๋กœ๊ทธ ์›น ์•ฑ์˜ server์ž…๋‹ˆ๋‹ค.

๊นƒํ—ˆ๋ธŒ ์†Œ์Šค์ฝ”๋“œ

REST API

Node.js์˜ koa ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ REST API๋ฅผ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

ํฌ์ŠคํŠธ ๊ด€๋ฆฌ

Joi ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ž‘์„ฑ๋œ ํฌ์ŠคํŠธ๋ฅผ ๊ฒ€์ฆํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ์š”์ฒญ์ด๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฅ˜๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

sanitize-html ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—์„œ ๊ธ€์“ฐ๊ธฐ๋ฅผ ํ•  ๋•Œ ์•…์„ฑ์ฝ”๋“œ ์‚ฝ์ž… ๋ฐฉ์ง€๋ฅผ ์œ„ํ•˜์—ฌ HTML์„ ํ•„ํ„ฐ๋งํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์œ ์ € ๊ด€๋ฆฌ

jsonwebtoken ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JSON ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ  ์ฟ ํ‚ค์— ์œ ์ €์ •๋ณด๋ฅผ ์ €์žฅํ•˜์—ฌ ํšŒ์›๊ฐ€์ž… , ๋กœ๊ทธ์ธ , ๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ์— ํ•„์š”ํ•œ ํšŒ์› ์ธ์ฆ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค

mongoDB Atlas๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์—ฐ๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

mongoDB Atlas๋Š” mongodb์—์„œ ์ œ๊ณตํ•˜๋Š” ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ €์žฅ๋œ URI๋ฅผ ํ†ตํ•ด ์—ฐ๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ .env๋Š” ๋ณด์•ˆ์„ ์œ„ํ•ด git์— ์˜ฌ๋ฆฌ์ง€์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋ฐฐํฌ

โ€ป 3 / 30 ์ถ”๊ฐ€๋‚ด์šฉ
koa-static ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณดํ†ต ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋ฐฐํฌํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” nginx ์„œ๋ฒ„๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜์ง€์•Š๊ณ 

ํ˜„์žฌ ์ด API์„œ๋ฒ„์— ์ •์ ์ธ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๊ฒŒ๋” ํ†ตํ•ฉํ•˜์—ฌ ๊ตฌํ˜„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Docker๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ๋œ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๋ฅผ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์—ˆ๊ณ 

Google Cloud Run์— ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์˜ฌ๋ ค์„œ ๋ฐฐํฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Young-blog ๋ฐฐํฌ๋œ ์‚ฌ์ดํŠธ์ž…๋‹ˆ๋‹ค.


posts API

ํฌ์ŠคํŠธ ๋“ฑ๋กํ•˜๊ธฐ

  • POST /api/posts
/* Request body */
{
  "title": "์ œ๋ชฉ",
  "body": "๋‚ด์šฉ",
  "tags": ["ํƒœ๊ทธ1", "ํƒœ๊ทธ2"]
}

ํฌ์ŠคํŠธ ์ „๋ถ€๋‹ค ๊ฐ€์ ธ์˜ค๊ธฐ

  • GET /api/posts/

ํ•ด๋‹น id๊ฐ’์˜ ํฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ

  • GET /api/posts/:id

ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ํฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ

  • GET /api/posts?username=&tag=&page=

page๋งˆ๋‹ค ์ตœ๋Œ€ ํฌ์ŠคํŠธ : 10
โ€˜Last-pageโ€™ ์ •๋ณด๋Š” Request Headers์— ์žˆ์Œ.
username๊ณผ tag๋กœ ํ•ด๋‹น ํŽ˜์ด์ง€ ์ฐพ์•„์˜ฌ์ˆ˜์žˆ์Œ.

ํ•ด๋‹น id๊ฐ’์˜ ํฌ์ŠคํŠธ ์‚ญ์ œํ•˜๊ธฐ

  • DELETE /api/posts/:id

ํ•ด๋‹น id๊ฐ’์˜ ํฌ์ŠคํŠธ ์ˆ˜์ •ํ•˜๊ธฐ

  • PATCH /api/posts/:id
/* Request body */
{
  "title": "์ˆ˜์ •",
  "body": "์ˆ˜์ • ๋‚ด์šฉ",
  "tags": ["์ˆ˜์ •ํƒœ๊ทธ1", "์ˆ˜์ •ํƒœ๊ทธ2"]
}

user API

ํšŒ์›๊ฐ€์ž…

  • POST /api/auth/register
/* Request body */
{
  "username": "username",
  "password": "password"
}

๋กœ๊ทธ์ธ

  • POST /api/auth/login
/* Request body */
{
  "username": "username",
  "password": "password"
}

๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธํ•˜๊ธฐ

  • GET /api/auth/check

๋กœ๊ทธ์•„์›ƒ

  • POST /api/auth/logout

Environments

  • koa@2.13.1
  • koa-router@10.0.0
  • koa-static@5.0.0
  • koa-bodyparser@4.3.0
  • bcrypt@5.0.1
  • dotenv@8.2.0
  • esm@3.2.25
  • joi@17.4.0
  • jsonwebtoken@8.5.1
  • mongoose@5.11.19
  • sanitize-html@2.3.3