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

  • spa๋กœ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ โœ… 2024-09-04
    • ๋กœ๊ทธ์ธ ํ™”๋ฉด
    • ๋กœ๊ทธ์ธ ์—๋Ÿฌ์‹œ ์•ˆ๋‚ด๋ฌธ๊ตฌ
    • ๋กœ๊ทธ์ธ ์„ฑ๊ณต์‹œ ๋ฉ”์ธ์œผ๋กœ ์ด๋™
  • ์—๋Ÿฌ ํ•ธ๋“ค๋ง โœ… 2024-09-04
    • ๋กœ๊ทธ์ธ ์—๋Ÿฌ ์ƒํƒœ๋ณ„๋กœ ์ฒ˜๋ฆฌ
    • throw error๋ฅผ ํ†ตํ•œ ์—๋Ÿฌ ์ „ํŒŒ

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

  • ํŒจ์ŠคํฌํŠธ ๋ชจ๋“ˆ โœ… 2024-09-04
  • ๋กœ๊ทธ์ธ ๋กœ์ง โœ… 2024-09-04

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

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

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

๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ

๊ธฐ๋ณธ์ ์ธ ๋ ˆ์ด์•„์›ƒ ์ž์ฒด๋Š” ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์œผ๋กœ ๊ตฌํ˜„์„ ํ–ˆ์ง€๋งŒ, ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์œผ๋กœ ๊ตฌํ˜„์„ ํ–ˆ์—ˆ๋‹ค.

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

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๊ฒŒ ๋˜๋ฉด ๊ธฐ์กด์˜ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์ž์ฒด๊ฐ€ ๋ฌด์ƒ‰ํ•ด์งˆ ๋ฟ๋”๋Ÿฌ, ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ Pages ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์ „ํ˜€ ํ•„์š”์—†๋Š” ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ๋˜์–ด๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํ™œ์šฉํ•  ๋ฐฉ๋ฒ•์„ ๊ณ ๋ฏผํ•ด๋ณด์•˜๋‹ค.

์ฃผ์†Œ๋ฅผ ๋ฐ›์•„ ๋™์ ์œผ๋กœ ํŽ˜์ด์ง€ ๋ Œ๋”๋งํ•˜๊ธฐ

// /app/app.js
import navigation from "../shared/utils/navigation.js";
 
function entryPoint() {
  const pathName = window.location.pathname;
  console.log(pathName);
  navigation.navigate(pathName);
}
 
window.onpopstate = navigation.onPageChanged;
document.addEventListener("DOMContentLoaded", entryPoint);

๊ธฐ์กด์— mainpage๋งŒ ์žˆ๋˜ ๋•Œ๋Š” ๋ฉ”์ธ ํŽ˜์ด์ง€๋งŒ์„ ๋ Œ๋”๋ง ์‹œํ‚ค๋Š” ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ์ •๋„๋กœ app.js๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ์ถ”๊ฐ€๋จ์— ๋”ฐ๋ผ ์ด๋ฅผ pathname์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋ง ์‹œํ‚ค๋„๋ก ๋ฐฉ์‹์„ ๋ฐ”๊ฟจ๋‹ค.

app.get("/", isLoggedIn, (req, res, next) => {
  res.render("index");
});
 
app.get("/login", isNotLoggedIn, (req, res, next) => {
  res.render("index");
});

๋”ฐ๋ผ์„œ ์„œ๋ฒ„์—์„œ๋„ pathname์€ ๋‹ค๋ฅด์ง€๋งŒ, ๋ชจ๋“  ํŽ˜์ด์ง€๋“ค์ด entryPoint์—์„œ ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ spa ๋ฐฉ์‹์œผ๋กœ ๋งŒ๋“ค๋ฉด ์ข‹์€ ์ ์€ ์•„๋ฌด๋ž˜๋„ index๋งŒ์„ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ๋กœ ์žก๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ pathname์œผ๋กœ ์ ‘์†ํ–ˆ์„ ๊ฒฝ์šฐ๋Š” ์ „๋ถ€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์—์„œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋ฒ„๊ฐ€ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์€ ๊ธฐ์กด index์˜ ๋ ˆ์ด์•„์›ƒ์— ๋ถˆ๊ณผํ•˜๊ธฐ ๋•Œ๋ฌธ์— api๊นŒ์ง€ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์„œ๋ฒ„์˜ ์—ญํ• ์ด ๋ถ„๋‹ด๋˜๋Š” ํšจ๊ณผ๋ฅผ ๊ฐ€์ง„๋‹ค.

๋‹ค์‹œ app.js๋กœ ๋Œ์•„์™€์„œ, pathname์— ๋”ฐ๋ผ spa๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” navigation ๊ฐ์ฒด๊ฐ€ ํ•„์ˆ˜์ ์ด์—ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด

  • pathname ๊ด€๋ฆฌ
  • ๊ธฐ์กด ๋ ˆ์ด์•„์›ƒ์„ ์žฌ์‚ฌ์šฉํ•˜๋ฉด์„œ body์˜ ๋‚ด์šฉ๋งŒ ๋‹ฌ๋ผ์ง€๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹
  • ํŽ˜์ด์ง€ ์ด๋™์— ๋”ฐ๋ฅธ ํŽ˜์ด์ง€๋ณ„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋ถ™์ด๊ธฐ/๋–ผ๊ธฐ ๋“ฑ์˜ ์กฐ๊ฑด์„ ๊ฐ–์ถฐ์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ navigation ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋Š” ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋„๋ก ํ–ˆ๋‹ค.
// /shared/utils/navigation.js
import Main from "../../pages/main/index.js";
import Login from "../../pages/login/index.js";
import EventManager from "../../feature/index.js";
 
export default (function navigation() {
  const navigator = {
    "/": Main.render,
    "/login": Login.render,
  };
  function navigate(pathname, prev) {
    document.body.innerHTML = "";
    history.pushState({ pathname: prev }, null, pathname);
    navigator[pathname]();
  }
  function onPageChanged(event) {
    document.body.innerHTML = "";
    EventManager.detachEvent(event.pathname);
    navigator[window.location.pathname]();
  }
  return {
    navigate,
    onPageChanged,
  };
})();
 

navigation ๊ฐ์ฒด๋Š” ์–ด๋””์„œ๋‚˜ ํŽ˜์ด์ง€ ์ด๋™์— ์“ฐ์—ฌ์•ผ ๋กœ์ง์ด๊ธฐ ๋•Œ๋ฌธ์— fsd ๊ณ„์ธต ์ค‘์— shared ๊ณ„์ธต์ด ์•Œ๋งž์€ ์ž๋ฆฌ์ผ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ด์„œ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค ํ•ด๋‹น navigation๊ฐ์ฒด๋Š” ๋“ฑ๋ก๋œ pathname์— ๋”ฐ๋ผ

  1. ๊ธฐ์กด ๋ ˆ์ด์•„์›ƒ์—์„œ body๋ฅผ ์ดˆ๊ธฐํ™”
  2. history.pushState ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ด์ „ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ๋’ค๋กœ๊ฐ€๊ธฐ๋ฅผ ๋ˆŒ๋ €์„ ๊ฒฝ์šฐ ์ด์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ๊ธˆ ์˜ฌ๋ฐ”๋ฅธ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋กœ์ง ๊ตฌ์„ฑ
  3. ํŽ˜์ด์ง€๋ณ„ ๋ Œ๋”๋ง ํ•จ์ˆ˜ ์‹คํ–‰ ์˜ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค. onPageChanged๋Š” ๊ธฐ์กด ํŽ˜์ด์ง€๊ฐ€ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก window์— ๋“ฑ๋กํ•ด๋†“์€ ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ์ธ๋ฐ, ์ด๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ๊ธˆ ์ดˆ๊ธฐํ™”์‹œํ‚ค๊ณ , ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ํ•ด๋‹นํ•˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋“ค์„ ์ข…ํ•ฉํ•ด๋†“์€ ๋ชจ๋“ˆ์„ head์—์„œ ๋–ผ์–ด๋ƒ„์œผ๋กœ์จ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์„ค๊ณ„ํ–ˆ๋‹ค.
// /feature/index.js
export default (function EventManager() {
  const route = {
    "/login": "/feature/login/index.js",
    "/main": "/feature/main/index.js",
  };
  function attachEvent(pathname) {
    const script = document.createElement("script");
    script.src = route[pathname];
    script.type = "module";
    document.head.appendChild(script);
  }
  function detachEvent(pathname) {
    const script = document.head.querySelector(
      `script[src="${route[pathname]}"]`
    );
    if (script) script.remove();
  }
  return { attachEvent, detachEvent };
})();
 

๋ฉ”์ธํŽ˜์ด์ง€์˜ ์ด๋ฒคํŠธ๋ฅผ ์ข…ํ•ฉํ•˜์—ฌ ๋“ฑ๋กํ•˜๊ธฐ๋งŒ ํ•ด์ฃผ๋˜ feature/index.js๋Š” ํŽ˜์ด์ง€๊ฐ€ ์ถ”๊ฐ€๋จ์— ๋”ฐ๋ผ ํŽ˜์ด์ง€๋ณ„๋กœ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด depth๋ฅผ ํ•œ๋‹จ๊ณ„ ๋Š˜๋ฆฌ๋Š” ๋Œ€์‹ ์—, ํŽ˜์ด์ง€๋ณ„๋กœ index.js๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ํ•ด๋‹น ํŽ˜์ด์ง€์— ๋“ฑ๋ก๋˜์–ด์•ผ ํ•  ์ด๋ฒคํŠธ ์œ„์ž„๊ณผ ํ•ธ๋“ค๋Ÿฌ๋“ค์„ ์ข…ํ•ฉํ•˜์—ฌ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์—ˆ์œผ๋ฉฐ, ์ด๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์„ค์ •ํ•˜์—ฌ head์˜ script๋กœ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ๋‹ค.