Skip to content

Commit

Permalink
feat: 이벤트 위임, hashRouter 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
dosilv committed Dec 17, 2024
1 parent 669a9b0 commit 78aaeed
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 67 deletions.
2 changes: 2 additions & 0 deletions src/app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "/router";
export * from "/routes";
44 changes: 44 additions & 0 deletions src/app/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Routes } from "./routes";
import userService from "../features/userService";

export const historyRouter = (path) => {
const pathToGo = interceptor(path);

history.pushState({}, "", pathToGo);

const page = Routes[pathToGo] ?? Routes[404];
const { view, init } = page();

document.querySelector("#root").innerHTML = view;
init();
};

export const hashRouter = (hash) => {
const path = hash.replace("#", "");
const pathToGo = interceptor(path);

window.location.hash = pathToGo;

const page = Routes[pathToGo] ?? Routes[404];
const { view, init } = page();

document.querySelector("#root").innerHTML = view;
init();
};

const interceptor = (path) => {
let redirectedPath;

if (path === "/profile" && !userService.isLoggedIn()) {
redirectedPath = "/login";
}

if (path === "/login" && userService.isLoggedIn()) {
redirectedPath = "/";
}

return redirectedPath ?? path;
};

export const router = (path) =>
window.location.hash ? hashRouter(path) : historyRouter(path);
8 changes: 4 additions & 4 deletions src/app/routes.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ErrorPage, LoginPage, MainPage, ProfilePage } from "../pages";

export const Routes = {
"/": MainPage,
"/profile": ProfilePage,
"/login": LoginPage,
404: ErrorPage,
"/": () => MainPage(),
"/profile": () => ProfilePage(),
"/login": () => LoginPage(),
404: () => ErrorPage(),
};
7 changes: 3 additions & 4 deletions src/features/UserService.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ class UserService {
isLoggedIn = () => !!this.getUser();

setUser = ({ username, email, bio }) => {
if (email && !this.isValidEmail(email)) {
alert("이메일 형식을 확인해주세요.");
}
localStorage.setItem(USER_KEY, JSON.stringify({ username, email, bio }));
};

getUser = () => {
console.log(JSON.parse(localStorage.getItem(USER_KEY)));
return JSON.parse(localStorage.getItem(USER_KEY));
};

clearUser = () => {
return localStorage.removeItem(USER_KEY);
};

nameRegex = /^[가-힣a-zA-Z]+$/;
isValidName = (val) => this.nameRegex.test(val);

emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
isValidEmail = (val) => this.emailRegex.test(val);
}
Expand Down
15 changes: 11 additions & 4 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { router } from "./shared/router";
import { router } from "./app/router";

const initPopListener = () => {
window.addEventListener("popstate", () => {
const currentPath = window.location.pathname;
const currentPath = window.location.hash || window.location.pathname;
router(currentPath);
});
};

const initHashChangeListener = () => {
window.addEventListener("hashchange", () => {
const currentPath = window.location.hash || window.location.pathname;
router(currentPath);
});
};

initPopListener();
initHashChangeListener();

document.addEventListener("DOMContentLoaded", () => {
console.log("✨");
const currentPath = window.location.pathname;
const currentPath = window.location.hash || window.location.pathname;
router(currentPath);
});
5 changes: 3 additions & 2 deletions src/pages/ErrorPage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { router } from "../shared/router";
import { router } from "../app/router";

export const ErrorPage = () => {
const view = `
Expand All @@ -20,7 +20,8 @@ export const ErrorPage = () => {
const init = () => {
const homeBtn = document.querySelector("#to-main");

homeBtn.addEventListener("click", () => {
homeBtn.addEventListener("click", (e) => {
e.preventDefault();
router("/");
});
};
Expand Down
22 changes: 8 additions & 14 deletions src/pages/LoginPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import userService from "../features/userService";
import { InputName } from "../shared/const";
import { router } from "../shared/router";
import { router } from "../app/router";

export const LoginPage = () => {
const view = `
Expand All @@ -9,7 +9,7 @@ export const LoginPage = () => {
<h1 class="text-2xl font-bold text-center text-blue-600 mb-8">항해플러스</h1>
<form id="login-form">
<div class="mb-4">
<input type="text" id="username" name=${InputName.ID} placeholder="이메일 또는 전화번호" class="w-full p-2 border rounded">
<input type="text" id="username" name=${InputName.USERNAME} placeholder="사용자 이름" class="w-full p-2 border rounded">
</div>
<div class="mb-6">
<input type="password" name=${InputName.PASSWORD} placeholder="비밀번호" class="w-full p-2 border rounded">
Expand All @@ -33,20 +33,14 @@ export const LoginPage = () => {
form.addEventListener("submit", (e) => {
e.preventDefault();
e.stopPropagation();
const id = form.querySelector(`input[name = ${InputName.ID}]`)?.value;
// const pw = form.querySelector(
// `input[name = ${InputName.PASSWORD}]`,
// )?.value;
const username = form.querySelector(
`input[name = ${InputName.USERNAME}]`,
)?.value;

if (!(userService.isValidName(id) || userService.isValidEmail(id))) {
return alert("이메일 또는 전화번호를 확인해주세요.");
}

if (userService.isValidName(id)) {
userService.setUser({ username: id, email: "", bio: "" });
} else if (userService.isValidEmail(id)) {
userService.setUser({ email: id, username: "", bio: "" });
if (!username) {
return alert("이름을 입력해주세요.");
}
userService.setUser({ username, email: "", bio: "" });
router("/");
});
};
Expand Down
1 change: 1 addition & 0 deletions src/pages/MainPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const MainPage = () => {
<div class="bg-gray-100 min-h-screen flex justify-center">
<div class="max-w-md w-full">
${headerView}
<main class="p-4">
<div class="mb-4 bg-white rounded-lg shadow p-4">
<textarea class="w-full p-2 border rounded" placeholder="무슨 생각을 하고 계신가요?"></textarea>
Expand Down
7 changes: 3 additions & 4 deletions src/pages/ProfilePage.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import userService from "../features/userService";
import { InputName } from "../shared/const";
import { router } from "../shared/router";
import { router } from "../app/router";
import { Footer } from "../widgets/Footer";
import { Header } from "../widgets/Header";

export const ProfilePage = () => {
const user = userService.getUser();
console.log("~ ~ ~ ~ ~ ~ ~");
console.log(user);
console.log("~ ~ ~ ~ ~ ~ ~");

const { view: headerView, init: initHeader } = Header();

const view = `
<div id="root">
<div class="bg-gray-100 min-h-screen flex justify-center">
<div class="max-w-md w-full">
${headerView}
<main class="p-4">
<div class="bg-white p-8 rounded-lg shadow-md">
<h2 class="text-2xl font-bold text-center text-blue-600 mb-8">
Expand Down
1 change: 0 additions & 1 deletion src/shared/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export const BASE_URL = window.location.origin;
export const USER_KEY = "user";

export const InputName = {
ID: "id",
PASSWORD: "password",
USERNAME: "username",
EMAIL: "email",
Expand Down
1 change: 0 additions & 1 deletion src/shared/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "const";
export * from "router";
15 changes: 0 additions & 15 deletions src/shared/router.js

This file was deleted.

34 changes: 16 additions & 18 deletions src/widgets/Header.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,53 @@
import userService from "../features/userService";
import { BASE_URL } from "../shared/const";
import { router } from "../shared/router";
import { router } from "../app/router";

export const Header = () => {
const LOGOUT = "로그아웃";
const navItemList = [
{ href: "/", title: "홈" },
{ href: "/profile", title: "프로필" },
userService.isLoggedIn()
? { href: "#", title: LOGOUT }
? { href: "/login", title: LOGOUT }
: { href: "/login", title: "로그인" },
];

const currentPath = window.location.pathname;
const isCurrNav = (path) => path === currentPath;
const navTextColor = (path) => (isCurrNav(path) ? "blue" : "gray");
const navTextColor = (path) => (isCurrNav(path) ? "blue-600" : "gray-600");
const navFontWeight = (path) => (isCurrNav(path) ? "bold" : "");

const view = `
<header class="bg-blue-600 text-white p-4 sticky top-0">
<header class="bg-blue-600 text-white p-4 sticky top-0">
<h1 class="text-2xl font-bold">항해플러스</h1>
</header>
<nav class="bg-white shadow-md p-2 sticky top-14">
<ul class="flex justify-around">
${navItemList
.map(({ href, title }) => {
return `<li><a href="${href}" id="${title === LOGOUT ? "logout" : ""}" class="text-${navTextColor(href)}-600">${title}</a></li>`;
return `<li><a href="${href}" id="${title === LOGOUT ? "logout" : ""}" class="text-${navTextColor(href)} font-${navFontWeight(href)}">${title}</a></li>`;
})
.join("")}
</ul>
</nav>
`;

const init = () => {
const navList = document.querySelectorAll("li > a");
const nav = document.querySelector("nav");

navList.forEach((el) => {
el.addEventListener("click", (e) => {
e.preventDefault();
nav.addEventListener("click", (e) => {
e.preventDefault();

if (e.target.innerHTML === LOGOUT) {
userService.clearUser();
router("/login");
}
if (e.target.innerHTML === LOGOUT) {
userService.clearUser();
}

const { href } = e.target;
if (!href) return;
const { href } = e.target;
if (!href) return;

const path = href.replace(BASE_URL, "");
const path = href.replace(BASE_URL, "");

router(path);
});
router(path);
});
};

Expand Down

0 comments on commit 78aaeed

Please sign in to comment.