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

  • Sorting card button โœ… 2024-08-22
  • ์ƒ์„ฑ ์ˆœ ์ •๋ ฌ
  • ์ตœ์‹  ์ˆœ ์ •๋ ฌ
  • drag & drop box โœ… 2024-08-22
  • ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์‹œ ์ƒ‰์ƒ ๋ณ€๊ฒฝ(์ž”์ƒ)
  • ์นด๋“œ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ ๊ด€๋ฆฌ โœ… 2024-08-22
  • ํžˆ์Šคํ† ๋ฆฌ ์•„์ดํ…œ ์ปดํฌ๋„ŒํŠธ โœ… 2024-08-22
  • ์นด๋“œ ์ˆ˜์ • โœ… 2024-08-22

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

  • ์ด๋ฒคํŠธ ์œ„์ž„
  • pug mixin
  • templating
  • addEventListener
  • DOM API

1์ฃผ์ฐจ ํ•™์Šต์ •๋ฆฌ https://luxurious-share-af6.notion.site/1-baed698a7b3c40bbbd6b94fb1097dc42?pvs=4

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

์‚ญ์ œ ์ปดํฌ๋„ŒํŠธํ™”

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

SOL : ์‚ญ์ œ ๋ชจ๋‹ฌ mixin โ†’ ์„ฑ๊ณต

mixin deleteConfirm(target) 
    style
        | @import "/shared/deleteConfirm/css/deleteConfirm.css"
    .modalBackground
            .deleteConfirmContainer
                if target==="user"
                    p ๋ชจ๋“  ์‚ฌ์šฉ์ž ๊ธฐ๋ก์„ ์‚ญ์ œํ• ๊นŒ์š”?
                else if target==="todo"
                    p ์„ ํƒํ•œ ์นด๋“œ๋ฅผ ์‚ญ์ œํ• ๊นŒ์š”?
                .deleteConfirmContainer
                    button.cancelButton ์ทจ์†Œ
                    button.conFirmButton ์‚ญ์ œ

์‚ญ์ œ ๋ชจ๋‹ฌ์„ ์ปดํฌ๋„ŒํŠธํ™”์‹œ์ผœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์™€ ๊ฐ€์šด๋ฐ ๋“ค์–ด๊ฐ€๋Š” ํ™•์ธ ์ฐฝ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค. ์—ฌ๊ธฐ์„œ html ์ค‘๊ฐ„์— js๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์„ ํ™œ์šฉํ–ˆ๋Š”๋ฐ, if else๋ฌธ์„ ํ†ตํ•ด target์ด๋ผ๋Š” attribute์— ๋”ฐ๋ผ์„œ ๊ฐ๊ฐ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋งŒ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ–ˆ๋‹ค.

๋˜ํ•œ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ด๋ฒคํŠธ ๋“ฑ๋ก์„ ํ†ตํ•ด ํ•ด๋‹น ๋ชจ๋‹ฌ์„ ์ œ์–ดํ•˜๋ฉฐ, ์›ํ• ํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ณด์™„ํ•˜์˜€๋‹ค.

๋ฐ์ดํ„ฐ ์กฐ์ž‘ action

SSR๋กœ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๊ฐ€๋ฉด์„œ ๊ฐ€์žฅ ๊ณ ๋ฏผ์ด ๋๋˜ ๋ถ€๋ถ„์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์นด๋“œ๋‚˜ ํžˆ์Šคํ† ๋ฆฌ ๋“ฑ ์ •๋ณด๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋ฐ์ดํ„ฐ fetching api๋“ค์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์ด ๋งŽ์€๋ฐ, ์ด๋ฅผ ๋ฐ˜์˜ํ•˜๊ณ  ์ƒˆ๋กœ๊ณ ์นจํ•˜๋А๋ƒ, ์•„๋‹ˆ๋ฉด ์›ํ• ํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด์„œ ์ž„์‹œ์ ์œผ๋กœ ๋„์šฐ๋А๋ƒ์˜ ์ฐจ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

SOL 1 : ์„œ๋ฒ„์—์„œ templatingํ•ด์„œ ๋ฐ›์€ ๋’ค ๋ Œ๋”๋ง โ†’ ์‹คํŒจ

์ค‘์š”ํ•œ ์ ์€ ์ˆ˜์ •์„ ํ•˜๊ณ  ์ €์žฅ์„ ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์€ ๊ดœ์ฐฎ์ง€๋งŒ, ์ƒˆ๋กญ๊ฒŒ ์นด๋“œ ๋“ฑ๋ก์„ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ธฐ์กด SSR ๋ฐฉ์‹์—์„œ ์ฒ˜์Œ์— ํŽ˜์ด์ง€๋ฅผ ๋กœ๋”ฉํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์™€ ํ•ด๋‹น ํŽ˜์ด์ง€์— ํ•„์š”ํ•œ ํžˆ์Šคํ† ๋ฆฌ์™€ ๋“ฑ๋กํ•œ ํ• ์ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ๋ Œ๋”๋ง์„ ์‹œ์ผœ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์ž„์‹œ์ ์œผ๋กœ ๋ณด์ด๊ฒŒ ํ•˜๊ณ  ๋‚˜์ค‘์— ์ƒˆ๋กœ๊ณ ์นจํ•  ๋•Œ๋ถ€ํ„ฐ ๋ฐ˜์˜์ด ๋˜๋А๋ƒ ํ˜น์€ ์ƒˆ๋กญ๊ฒŒ data post๋ฅผ ํ•œ ๋’ค์— ๋‹ค์‹œ๊ธˆ ์ „์ฒด์ ์ธ ํŽ˜์ด์ง€ refresh์˜ ํ•˜๋А๋ƒ์˜ ์ฐจ์ด๊ฐ€ ๋‚œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ์ด๋ฅผ ๋งจ ์ฒ˜์Œ์—๋Š” ๋ฐ˜๋Œ€๋กœ ์ƒ๊ฐํ•ด์„œ ๊ทธ๋ƒฅ ์ž„์‹œ๋กœ ๋ณด์—ฌ์ฃผ๋˜, ์„œ๋ฒ„์—์„œ templating์„ ํ•˜๊ณ  ๋ณด๋‚ด๋ฉด ๋˜์ง€ ์•Š์„๊นŒ?๋ผ๊ณ  ์ƒ๊ฐํ•ด์„œ ๋งจ ์ฒ˜์Œ์—๋Š” ์„œ๋ฒ„์˜ templating ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ–ˆ๋‹ค. pug์—๋Š” pug ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€ ํ•ด๋‹น ํŒŒ์ผ์„ ๋ Œ๋”๋ง ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. ์ด๋Ÿฌํ•œ Pug์˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๊ฐ€ ์ผ์ผ์ด ๊ท€์ฐฎ๊ฒŒ ํ…œํ”Œ๋ฆฟ์„ ๋ฌธ์ž์—ด๋กœ ํ•œ๋•€ํ•œ๋•€ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ํ•จ์ˆ˜๋งŒ ์‹คํ–‰์‹œํ‚ค๋ฉด ์•Œ์•„์„œ ๋ Œ๋”๋ง ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด๋กœ ๋‚˜์˜ค๋‹ˆ ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ข‹๊ฒ ๋‹ค ์ƒ๊ฐํ–ˆ์—ˆ๋‹ค..

// 'client' ํด๋”๋ฅผ ์ •์  ํŒŒ์ผ๋กœ ์ œ๊ณต
app.use(express.static(path.join(__dirname, "client")));
 
// Pug ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์ด ์œ„์น˜ํ•  ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
app.set("views", path.join(__dirname, "client"));
// ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ Pug๋กœ ์„ค์ •
app.set("view engine", "pug");
app.locals.basedir = path.join(__dirname, "client");
 
// '/' ๊ฒฝ๋กœ๋กœ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌ
app.get("/", (req, res, next) => {
  console.log("refresh");
  const filePath = path.join(__dirname, "mockups");
  const todoData = JSON.parse(readFileSync(`${filePath}/todo.json`, "utf-8"));
  const historyData = JSON.parse(
    readFileSync(`${filePath}/history.json`, "utf-8")
  );
  // home.pug ํ…œํ”Œ๋ฆฟ์„ ๋ Œ๋”๋งํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์— ์ „์†ก
  res.render("app", { todoData: todoData, historyData: historyData.history });
});

ํ•˜์ง€๋งŒ ๋‚ด ์„œ๋ฒ„์˜ ์„ธํŒ… ์ž์ฒด๊ฐ€ ๋ฌธ์ œ์˜€๋‹ค. ๋‚˜๋Š” SSR ๋ฐฉ์‹์œผ๋กœ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ํŽ˜์ด์ง€ ์ž์ฒด๋ฅผ ๋ Œ๋”๋งํ•ด์„œ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ์‹์„ ํƒํ–ˆ๋‹ค. ํ•ด๋‹น ๋ฐฉ์‹๋งŒ์„ ์ƒ๊ฐํ•˜๋ฉด์„œ ํด๋”๊ตฌ์กฐ๋ฅผ ์งฐ๋Š”๋ฐ, ๋‚˜์ค‘์— ์ด๋ฅผ ์ปดํŒŒ์ผํ•œ ๊ฒƒ์„ templatingํ•ด์„œ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด์„œ๋Š” Res.render ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๋ฐ, ํ•ด๋‹น ๋ฉ”์„œ๋“œ์—์„œ๋Š” ์œ„์—์„œ ์„ค์ •ํ•œ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋” ๊นŠ๊ฒŒ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ธ์ž๋กœ ์ค„ ์ˆ˜ ์—†์—ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์ด ๋ถ€๋ถ„์ด ์™œ ์ด๋Ÿฌ๋Š”์ง€ ์ž˜ ๋ชฐ๋ž๊ณ , ์ด๊ฑฐ๋•Œ๋ฌธ์— ์‹œ๊ฐ„์ด ๋งŽ์ด ๊ฑธ๋ฆฐ ๊ฒƒ ๊ฐ™๋‹ค.

SOL2: ์‚ฌ์ „ ์ปดํŒŒ์ผ์„ ํ†ตํ•œ ๋ฌธ์ž์—ด ๋ณด๋‚ด๊ธฐ โ†’ ์‹คํŒจ์ค‘

  const compiled = pug.compileFile(
    path.join(__dirname, "client", "shared", "card", "component", "card.pug"),
    {
      basedir: path.join(__dirname, "client"),
    }
  );
  console.log(
    "๋ญ์—ฌ",
    compiled({
      title: "์ž„์‹œ",
      detail: "์ž„์‹œ๋””ํ…Œ์ผ",
      author: "๋ฏผํ˜•",
      date: "2023-10-24 11:00",
      id: "temp-1",
    })
  );

์ด์— compileFile์„ ํ†ตํ•ด pug ํŒŒ์ผ์„ ์ปดํŒŒ์ผ ํ•ด์„œ ๋ฌธ์ž์—ด๋งŒ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ›์•„ ์ถœ๋ ฅํ•˜๋Š” ๋ฐฉ์‹์„ ์ƒ๊ฐํ•ด๋ณด์•˜๋Š”๋ฐ, ๊ธฐ์กด์˜ ์นด๋“œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ attiribute๋ฅผ ๋ฐ›์•„์„œ์ธ์ง€ You should not have pug tags with multiple attributes. ์—๋Ÿฌ๊ฐ€ ๋–ด๋‹ค. ์‹œ๊ฐ„์ด ๋‚จ์„ ๋•Œ ์ด ๊ตฌ์กฐ๋ฅผ ๋ณด๋‹ค ํ•™์Šตํ•ด์„œ ์•Œ๋งž์„ ํ…œํ”Œ๋ฆฟ ํ˜•ํƒœ๋กœ ์ œ์ž‘ํ•œ ๋’ค ์ ์šฉํ•  ์˜ˆ์ •์ด๋‹ค.