์ฃผ์š” ์ž‘์—…

  • history์„ค๊ณ„ โœ… 2024-09-03
    • history ํ…Œ์ด๋ธ” ์„ค๊ณ„
    • action๋ณ„ history์— ๋Œ€ํ•ด ๋ ˆ์ฝ”๋“œ ์ถ”๊ฐ€ ์„ค์ •
    • ๊ฐ card ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ history ์—…๋ฐ์ดํŠธ ๋กœ์ง
  • ์ฟ ํ‚ค์™€ ์„ธ์…˜ ํ•™์Šต โœ… 2024-09-03
  • ์„œ๋ฒ„ ๋กœ๊ทธ์ธ ์ž‘์—… โœ… 2024-09-04
    • ์„œ๋ฒ„์—์„œ passport ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ๋ฐ ์„ธ์…˜๊ด€๋ฆฌ(์ง„ํ–‰์ค‘)

ํ•™์Šต ํ‚ค์›Œ๋“œ

  • XSS ๊ณต๊ฒฉ
  • sql Trigger
  • ์ฟ ํ‚ค์™€ ์„ธ์…˜

ํ•™์Šต์ •๋ฆฌ ์ข…ํ•ฉ๋ณธ

https://luxurious-share-af6.notion.site/3-1f7fcdb0591241f487bdfa6f74f1200a?pvs=4

์‹คํ–‰ ๋ฐฉ๋ฒ•

์‹คํ–‰์˜ ๊ฒฝ์šฐ

npm install
npm start

๋ฅผ ํ†ตํ•ด ์‹คํ–‰ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค!(.env ํŒŒ์ผ์˜ ๊ฒฝ์šฐ๋Š” ์Šฌ๋ž™์œผ๋กœ ์†ก๋ถ€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค)

๊ณ ๋ฏผ ๋ฐ ํ•ด๊ฒฐ๊ณผ์ •

undo, redo๋ฅผ ๊ณ ๋ คํ•œ history ์ €์žฅ ๊ด€๊ณ„

์–ด์ œ ์˜ฌ๋ฆฐ PR์—์„œ๋Š” Undo, Redo๋ฅผ ๊ณ ๋ คํ•œ history์˜ ์ €์žฅ ๊ด€๊ณ„์™€ ์ด์— ๋Œ€ํ•ด์„œ undo์™€ redo๋ฅผ ์–ด๋–ป๊ฒŒ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ธ๊ฐ€์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ ์ ์—ˆ์Šต๋‹ˆ๋‹ค.

1. mysql ์ž์ฒด์—์„œ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•œ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
	- Mysql์—์„œ ์ง€์›ํ•˜๋Š” ํŠธ๋ฆฌ๊ฑฐ(Trigger)์€ ํŠน์ • ์กฐ๊ฑด์ด ๋งŒ์กฑํ•˜๋ฉด ์ €์ ˆ๋กœ ์‹คํ–‰๋˜๋Š” ์ผ์ข…์˜ ์žฅ์น˜๋กœ, ํ•œ๋ฒˆ ์„ค์ •์„ ํ•ด๋†“์œผ๋ฉด ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ์™€ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘์„ ๊ณ„์† ๊ฐ์‹œํ•˜๋ฉด์„œ ์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ๋™์ž‘์ด ์ˆ˜ํ–‰๋˜๋Š” ์ˆœ๊ฐ„ ์‹คํ–‰๋˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค.
	- ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ธฐ๋กํ•  ๋•Œ, ํžˆ์Šคํ† ๋ฆฌ๋Š” ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€๋˜๊ธฐ ๋•Œ๋ฌธ์— undo, redo๋ฅผ ๊ณ ๋ คํ•˜๋Š” ์ž…์žฅ์—์„œ ์ด๋ฅผ ์„ค๊ณ„ํ•ด๋ณด์ž๋ฉด
		- undo, redo๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ํžˆ์Šคํ† ๋ฆฌ์—์„œ ํ•ด๋‹นํ•˜๋Š” ์œ ์ €์˜ ํžˆ์Šคํ† ๋ฆฌ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
		- ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ ์‹คํ–‰๋˜๋Š” ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ํ†ตํ•ด history์˜ undo๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
		- ๊ทธ๋Ÿฌ๋‚˜ redo๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” ์ง€๊ธˆ ์ƒ๊ฐํ•œ ์ •๋„๋กœ๋Š” history์— ๋Œ€ํ•œ History ํ…Œ์ด๋ธ”์„ ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋ฐ–์—๋Š” ์ƒ๊ฐ๋‚˜์ง€ ์•Š๋Š”๋ฐ, history ํ…Œ์ด๋ธ”์„ ๋‘ ๊ฐœ ๋‘๋Š” ๊ฒƒ์ด ์ƒ์‹์ƒ์œผ๋กœ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€ ์•Š์•„์„œ ๊ณ ๋ฏผ์ด๋‹ค.
2. ์˜จ๋ฉ”๋ชจ๋ฆฌ์—์„œ deque๋ฅผ ํ†ตํ•œ history ์ €์žฅ
	- mysql์˜ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋”ฐ๋กœ history์— Insert๋ฅผ ํ†ตํ•ด์„œ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์ด๋‹ค.
	- deque์—์„œ 5๊ฐœ์˜ ๊ฐœ์ˆ˜์ œํ•œ์„ ๋†“๊ณ , action์ด ์ด๋ฃจ์–ด์งˆ ๋•Œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ pushํ•ด์ฃผ๋˜, ๊ฐœ์ˆ˜๋ฅผ ๋„˜์–ด๊ฐ€๋ฉด ํ•˜๋‚˜์”ฉ dequeueํ•ด์ฃผ๋ฉด์„œ ์ด์— ๋Œ€ํ•ด ์‹ค์งˆ์ ์œผ๋กœ db์— ๋ฐ˜์˜ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์ด๋‹ค.
	- ์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ•  ๊ฒฝ์šฐ, ์˜จ๋ฉ”๋ชจ๋ฆฌ์—์„œ deque๋ฅผ ํ•˜๋‚˜ ๋‘๊ณ  Undo์™€ redo์— ๋Œ€ํ•ด์„œ action๋งˆ๋‹ค ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง์„ ๋ธŒ๋ผ์šฐ์ € ์ƒ์— ๋†“๊ณ  ๊ด€๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.
	- ์ผ๋ฐ˜์ ์œผ๋กœ undo, redo๋Š” ์ƒˆ๋กœ๊ณ ์นจ์ด๋‚˜ ์ฐฝ์„ ๋‚˜๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œ ์ ‘์†ํ•  ์‹œ์— ์—†์–ด์ง€๊ฒŒ ๋˜๋ฏ€๋กœ, ์ƒˆ๋กœ๊ณ ์นจ์ด๋‚˜ ์ฐฝ ๋‹ซ๊ธฐ ๋“ฑ์˜ ํ–‰์œ„๋ฅผ ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•ด๋†“๊ณ  ์žˆ๋‹ค๊ฐ€ ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด flush๋ฅผ ํ†ตํ•ด์„œ ๋ชจ๋“  history์˜ ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜๋Š” ๋กœ์ง์œผ๋กœ ์ž‘๋™์‹œํ‚จ๋‹ค.
		- ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ฐˆ ๊ฒฝ์šฐ, history์— ๋Œ€ํ•œ ์˜ˆ์™ธ ๋กœ์ง์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•„ ๋งŒ์•ฝ ์ฐฝ์„ ๊ป์„ ์‹œ์— ์ด๋ฒคํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด history์˜ ๊ฐฑ์‹ ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์œผ๋ฏ€๋กœ ์ด๋Š” db์˜ ์†์ƒ์„ ์ผ์œผํ‚จ๋‹ค.
3. db์— action๋•Œ๋งˆ๋‹ค ์ง์ ‘ ๋„ฃ๋˜, ํ•˜๋‚˜์”ฉ ๋นผ์„œ undo, redo
	- ์ด ๊ฒฝ์šฐ, action์— ๋Œ€ํ•œ api ํ˜ธ์ถœ์„ ํ•˜๋ฉด์„œ ํ•ด๋‹น api๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์—์„œ ํžˆ์Šคํ† ๋ฆฌ ์—…๋ฐ์ดํŠธ + aciton์— ๋Œ€ํ•œ ์ˆ˜ํ–‰ ์ž์ฒด๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ธ์‹ํ•˜์—ฌ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
	- ๊ฐ๊ฐ์˜ action์— ๋Œ€ํ•œ ํžˆ์Šคํ† ๋ฆฌ์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ฟผ๋ฆฌ๋ฌธ์„ ํ†ตํ•ด ๋„ฃ์–ด์ค€๋‹ค.
	- redo์˜ ๊ฒฝ์šฐ undoํ•œ ๊ฒƒ์„ ๋นผ์„œ ์˜จ๋ฉ”๋ชจ๋ฆฌ์—์„œ stack ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ ์ด์— ๋Œ€ํ•œ ๋กœ์ง์„ ์„ค๊ณ„ํ•˜์—ฌ ๋‹ค์‹œ๊ธˆ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.
	- redo์˜ ๊ฒฝ์šฐ ๊ตณ์ด ์ƒˆ๋กœ๊ณ ์นจ์ด๋‚˜ ์ฐฝ์„ ๊ป์„ ๋•Œ, ๋ณต๊ตฌ์‹œํ‚ฌ ์˜๋ฌด๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. undo ๋˜ํ•œ db์—์„œ ๊ณ„์† ๋Œ์–ด๋‹ค๊ฐ€ ๋ณต๊ตฌํ•  ์ˆ˜๋Š” ์žˆ๊ฒ ์ง€๋งŒ์€ ๊ตณ์ด ์ด๊ฑธ ์ „๋ถ€ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•ด์ฃผ๋ฉด db์— ๋ถ€๋‹ด์ด ๊ฐˆ ์ˆ˜ ์žˆ๊ธฐ ๋–„๋ฌธ์— ๋ธŒ๋ผ์šฐ์ € ๋‚ด๋ถ€์—์„œ ๊ฐœ์ˆ˜๋ฅผ ์ •ํ•ด๋†“๊ณ  ์ด๋ฅผ ๋ง‰๋Š” ๋ฐฉ์‹์ด ๋‚˜์„ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
	- ๋‚˜๋Š” ํžˆ์Šคํ† ๋ฆฌ์™€ action์˜ ์ˆ˜ํ–‰์ด ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ํŒ๋‹จํ•ด์•ผ๋งŒ ์ •์ƒ์ ์ธ db์˜ ๊ฐฑ์‹ ์ด ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฐฉ์‹์ด ์ œ์ผ ํ•ฉ๋ฆฌ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

๊ทธ๋•Œ ์ƒ๊ฐํ•œ ๋ฐฉ์•ˆ์€ ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ์š”,

  1. mysql ์ž์ฒด์—์„œ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•œ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก
  2. ์˜จ๋ฉ”๋ชจ๋ฆฌ์—์„œ deque๋ฅผ ํ†ตํ•œ history ์ €์žฅ
  3. db์— action๋•Œ๋งˆ๋‹ค ์ง์ ‘ ๋„ฃ๋˜, ํ•˜๋‚˜์”ฉ ๋นผ์„œ undo, redo

์ฒ˜์Œ์—๋Š” ํžˆ์Šคํ† ๋ฆฌ์™€ action ์ž์ฒด๊ฐ€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ณด๋‹ค ์„ธ์‹ฌํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” 3๋ฒˆ์„ ๊ณ ๋ฏผํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. 2๋ฒˆ์˜ ๋ฐฉ์‹์—์„œ data flush์— ๋Œ€ํ•œ ์œ„ํ—˜์„ฑ์„ ์ค„์ธ ๋ฒ„์ „์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์˜จ๋ฉ”๋ชจ๋ฆฌ๋กœ history์— ๋Œ€ํ•ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ถ€๋‹ด์„ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์„ธ๋ถ€์ ์ธ history ๋‚ด์—ญ์„ ์˜จ๋ฉ”๋ชจ๋ฆฌ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ณผ์—ฐ ์•ˆ์ „ํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์— ์˜๋ฌธ์„ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋Š” history์˜ ๋‚ด์šฉ์ด ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ๋„ ํฌํ•จํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ์ ๊ณผ ๋ธŒ๋ผ์šฐ์ €์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋น„ํšจ์œจ์ ์œผ๋กœ ๋А๊ปด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ 1๋ฒˆ์˜ ๋ฐฉ์•ˆ๊ณผ 3๋ฒˆ์˜ ๋ฐฉ์•ˆ์„ ํ•ฉ์ณ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์–ด๋–จ๊นŒ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. action์ด ์ผ์–ด๋‚˜๋ฉด mysql ์ž์ฒด์—์„œ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•œ ํžˆ์Šคํ† ๋ฆฌ ๊ธฐ๋ก์„ ์—…๋ฐ์ดํŠธ ํ•˜๊ณ , ํžˆ์Šคํ† ๋ฆฌ๋ฅผ db์—์„œ ํ•˜๋‚˜์”ฉ ๋นผ์–ด ์“ฐ๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ธฐ๋กํ•  ๋•Œ, ํžˆ์Šคํ† ๋ฆฌ๋Š” ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€๋˜๋„๋ก ํ•ด๋†“๊ณ , undo, redo๊ฐ€ ๋‚˜์ค‘์— ์ถ”๊ฐ€๋  ๊ฒฝ์šฐ์—๋Š” ํ•ด๋‹น ์œ ์ €์— ํ•ด๋‹นํ•˜๋Š” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ undoํ•  ๊ฒฝ์šฐ์—๋Š” ๊ฐ€์žฅ ์ตœ์‹ ์˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ db์—์„œ ๋นผ๋‚ด์–ด ์ด์— ๋Œ€ํ•œ action์„ ๋˜๋Œ๋ฆฌ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๊ณ , redo์˜ ๊ฒฝ์šฐ์—๋Š” ์–ด์ฐจํ”ผ action์„ ๋˜๋Œ๋ฆฌ๋Š” ์ฝ”๋“œ๋ฅผ ์“ธ ๊ฒฝ์šฐ ๋˜ํ•œ action์ด card์— ์ด๋ฃจ์–ด์งˆ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋‹ค์‹œ๊ธˆ ๋นผ๋‚ด์–ด undo๋ฅผ undo ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ์ œ๊ฐ€ ์ž‘์„ฑํ•œ ํŠธ๋ฆฌ๊ฑฐ์ž…๋‹ˆ๋‹ค.(mysql workbench์—์„œ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค)

delimiter $$
 
create trigger todo_submit_history
after insert on todo_cards
for each row
begin
	insert into history (username, card_id,action, column_name, title)
	values(NEW.author,new.card_id,"submit", NEW.task_column, NEW.title);
end$$
 
delimiter $$
 
create trigger todo_move_or_edit_history
after update on todo_cards
for each row
begin
	if OLD.task_column != NEW.task_column then
		insert into history(username, action, card_id,title, from_column, to_column)
        values(OLD.author, "move", old.card_id, old.title,old.task_column, new.task_column);
	else
		insert into history(username,card_id,action,title,detail)
        values(old.author,old.card_id,"edit", old.title, old.detail);
	end if;
end$$
 
delimiter $$
 
create trigger todo_delete_history
after delete on todo_cards
for each row
begin
	insert into history(username,card_id, title, detail, column_name, created_at)
    values(old.author, old.card_id, old.title,old.detail,old.task_column,old.created_at);
end$$

์ด๋Ÿฌํ•œ ๊ณ ๋ฏผ ๊ณผ์ •์—์„œ ์ œ๊ฐ€ ๋“œ๋ฆฌ๊ณ  ์‹ถ์€ ์งˆ๋ฌธ์€, ์ œ๊ฐ€ ์„ ํƒํ•œ ๋ฐฉ๋ฒ•์ด ํšจ์œจ์ ์ด๊ณ  ์•ˆ์ „ํ•œ๊ฐ€?์— ๋Œ€ํ•ด ์—ฌ์ญ™๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. undo์™€ redo๋ฅผ ๊ณ ๋ คํ•˜์—ฌ trigger๋ฅผ ์„ค๊ณ„ํ–ˆ์ง€๋งŒ, ์„œ๋ฒ„ -> db์—์„œ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด์–ด ํ•ด๋‹น action์— ๋งž์ถ˜ undo ๋กœ์ง -> ๋‹ค์‹œ ์„œ๋ฒ„๋กœ ๋ฐ˜์˜ํ•˜๋Š” ๊ณผ์ •์—์„œ db์— ๋Œ€ํ•œ ๋ถ€๋‹ด์ด ์—†๋Š”๊ฐ€?์— ๋Œ€ํ•œ ์˜๋ฌธ์ด ์ œ๋Œ€๋กœ ํ•ด์†Œ๋˜์ง€ ๋ชปํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ ํ”ผ๋“œ๋ฐฑ ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์— ๋”ฐ๋ฅธ ๋ฆฌ๋ Œ๋”๋ง ๋กœ์ง

ํด๋” ๊ตฌ์กฐ ์„ค๋ช…

์ €๋„ ํ•˜๋‹ค๋ณด๋‹ˆ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์ง„ ํƒ“์— ๊ตฌ๋ถ„ํ•˜์‹œ๊ธฐ ํž˜๋“œ์‹ค ์ˆ˜ ์žˆ์œผ์‹ค ๊ฒƒ ๊ฐ™์•„ ๊ฐ„๋‹จํžˆ ๋ง์”€๋“œ๋ฆฌ๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ๋งŽ์ด ํ™œ์šฉํ•˜๊ณ ์ž ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์— ๋Œ€ํ•ด์„œ fsd ํŒจํ„ด์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ํด๋”๋ณ„๋กœ ๋‚˜๋ˆˆ ๋’ค์— ๋‹ค์‹œ๊ธˆ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ api๋‚˜ ์Šคํƒ€์ผ์‹œํŠธ ๋“ฑ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐ€๊นŒ์šด ์ž๋ฆฌ์— ๋ฐฐ์น˜ํ–ˆ๊ณ , ์ด๋ฒคํŠธ ์œ„์ž„์„ ํ†ตํ•œ ๋กœ์ง์€ feature์— ํฐ ์ปดํฌ๋„ŒํŠธ(widget)๋ณ„๋กœ ๊ตฌ๋ถ„ํ•œ ๋’ค index๋ฅผ ํ†ตํ•ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ „์ฒด์ ์ธ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆviews
 โ”ฃ ๐Ÿ“‚app
 โ”ƒ โ”ฃ ๐Ÿ“œapp.css
 โ”ƒ โ”ฃ ๐Ÿ“œapp.js
 โ”ƒ โ”— ๐Ÿ“œapp.scss
 โ”ฃ ๐Ÿ“‚asset
 โ”ฃ ๐Ÿ“‚entities
 โ”ƒ โ”ฃ ๐Ÿ“‚addTodo
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚template
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”ฃ ๐Ÿ“‚card
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚component
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚template
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”ฃ ๐Ÿ“‚history
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚script
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚ui
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”ฃ ๐Ÿ“‚historyItem
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚ui
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”— ๐Ÿ“‚section
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚component
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ฃ ๐Ÿ“‚feature
 โ”ƒ โ”ฃ ๐Ÿ“‚header
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œhistoryHandler.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œindex.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œrendering.js
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œsortHandler.js
 โ”ƒ โ”ฃ ๐Ÿ“‚mainsection
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œaddCardHandler.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œdeleteCardHandler.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œeditCardHandler.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œindex.js
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œmoveCardHandler.js
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œrendering.js
 โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ฃ ๐Ÿ“‚pages
 โ”ƒ โ”— ๐Ÿ“‚main
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚css
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚ui
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ฃ ๐Ÿ“‚shared
 โ”ƒ โ”ฃ ๐Ÿ“‚deleteConfirm
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚component
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚css
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”ฃ ๐Ÿ“‚fab
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”ฃ ๐Ÿ“‚utils
 โ”ฃ ๐Ÿ“‚widgets
 โ”ƒ โ”ฃ ๐Ÿ“‚header
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚component
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”ƒ โ”— ๐Ÿ“‚mainsection
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚style
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚ui
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œindex.js
 โ”— ๐Ÿ“œindex.pug

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ๊ฐ์˜ section๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” mainsection์˜ ๊ฒฝ์šฐ, /widgets/mainsection/index.js์—์„œ ํ•ด๋‹น ํ•˜๋Š” ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์˜์กด ๊ด€๊ณ„๋กœ ๋ฌถ์–ด ๋ Œ๋”๋ง ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

// /widgets/mainsection/index.js
import section from "/entities/section/index.js";
import pug from "/shared/utils/pugCompile.js";
 
function Mainsection() {
  const template = `style
  | @import url('/widgets/mainsection/style/mainsection.css')
main#sections`;
 
  function render() {
    const mainsection = pug.compileHTML(template);
    document.getElementById("html__body--pageContainer").innerHTML +=
      mainsection;
    section.render();
  }
 
  return {
    render,
  };
}
 
const mainsection = new Mainsection();
export default mainsection;
 

๊ทธ๋ ‡๊ธฐ ๋–„๋ฌธ์— mainsection์„ ๋ Œ๋”๋ง์‹œํ‚ค๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š” ๊ธฐ์กด์— pug ํŒŒ์ผ์„ ์ปดํŒŒ์ผํ•˜์—ฌ ๋ฐ˜ํ™˜๋œ HTML element๋“ค์„ DOM Api๋ฅผ ํ†ตํ•ด ์ง์ ‘ ์ง€์ •ํ•œ ์œ„์น˜์— ๊ทธ๋ ค๊ฐ‘๋‹ˆ๋‹ค.

// entities/section/index.js
import fetch from "./api/index.js";
import card from "/entities/card/index.js";
import addTodo from "/entities/addTodo/index.js";
 
function Section() {
  const template = `style
    | @import url('/entities/section/style/section.css')
section(id=section.classify)
    .main__div--topbar
        .section__div--titleWrapper
            span.titleWrapper__span--title #{section.classify}
            span.titleWrapper__span--count #{section.cards.length}
        .section__div--buttonWrapper
            img(src="/asset/plus.svg", alt="add", data-action="add", data-target=section.classify)
            img(src="/asset/close.svg", alt="delete")
    ul.todoList`;
 
  async function render() {
    const { data, path } = await fetch();
    console.log(data);
    const compiledFunction = window.pug.compile(template, {
      basedir: path,
    });
    document.getElementById("sections").innerHTML = "";
    data.forEach((section) => {
      const html = compiledFunction({ section: section }); // ๋ Œ๋”๋ง๋œ HTML
      document.getElementById("sections").innerHTML += html;
      addTodo.render(section.classify);
      card.render(document.getElementById(section.classify), section.cards);
    });
  }
 
  return {
    render,
  };
}
 
const section = new Section();
export default section;
 

์ด๋ ‡๊ฒŒ ๋ Œ๋”๋ง ์ค‘๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ fetchingํ•ด์™€์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์„ค์ •ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„์— ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์นด๋“œ ์ถ”๊ฐ€๋‚˜ ์‚ญ์ œ์™€ ๊ฐ™์€ ํ–‰์œ„ ๋˜ํ•œ ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ•ด๋‹น section ์ „์ฒด๋ฅผ ์œ„ ํ•จ์ˆ˜์˜ render() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ ๋ฆฌ๋ Œ๋”๋งํ•˜๋Š” ๋ฐฉ์‹์„ ๊ตฌ์ƒํ•˜์—ฌ ๊ตฌํ˜„ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ œ๊ฐ€ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ๊ฐ๊ฐ์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ json ํ˜•์‹์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ณ , ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ templating ๋ฐ painting ์ž‘์—…์„ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•œ๋‹ค๋Š” ์ ์—์„œ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์ด ์–ด๋А์ •๋„ ๊ตฌํ˜„๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•จ์— ์žˆ์–ด์„œ ๊ณ ๋ฏผ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ๋ Œ๋”๋ง์˜ ๊ฒฝ์šฐ DOM API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‹ค์‹œ๊ธˆ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ๊ทธ๋ ค๋‚ด๋Š” ๋ฐฉ์‹ ์ž์ฒด๊ฐ€ ๋„ˆ๋ฌด rawํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

DOM ์ž‘์—…์˜ ๊ฒฝ์šฐ ๊ณ ๋น„์šฉ์ธ๋ฐ ๋ฐ˜ํ•ด ์ด๋ฅผ ๊ณ„์† ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ ์ด ์ด๋ฃจ์–ด์งˆ ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ๊ธˆ http ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๋ Œ๋”๋ง ๋‹จ์œ„์ธ ์ „์ฒด column์— ๋Œ€ํ•ด ๋ฆฌ๋ Œ๋”๋งํ•˜๋Š”๊ฒƒ์ด ์ž์› ๋‚ญ๋น„๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋น„์šฉ์„ ์•„๋ผ๊ณ ์ž ์นด๋“œ ์ถ”๊ฐ€, ์‚ญ์ œ์™€ ๊ฐ™์€ ๊ฒฝ์šฐ ๋ฐ›์•„์˜จ json ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ํ•˜๋‚˜๋งŒ์˜ ์นด๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” softํ•œ ๋ฐฉ์‹์˜ ์ถ”๊ฐ€๋‚˜ ์‚ญ์ œ์˜ ๊ฒฝ์šฐ ๋™์‹œ ์ ‘๊ทผํ•ด์„œ ์ˆ˜์ •์ด๋‚˜ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๊ฐ™์€ ๊ณ„์ •์˜ column์˜ ์ž‘์—…์— ๋Œ€ํ•ด ๋ฐ˜์˜์‚ฌํ•ญ์„ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ ์—์„œ ์ „์ฒด์ ์ธ column์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ getํ•ด์™€์„œ ๋ฆฌ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ด๋ผ๋Š” ์ƒ๊ฐ ๋˜ํ•œ ๋“ค์–ด ์–ด๋–ค ๋ฐฉ์‹์ด ๋” ๋งž์„๊นŒ์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์ด ๊ณ„์† ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๋ฉ˜ํ† ๋‹˜์˜ ๊ณ ๊ฒฌ์„ ์—ฌ์ญ™๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค!