diff --git a/src/classes/Router.js b/src/classes/Router.js new file mode 100644 index 00000000..3976ae54 --- /dev/null +++ b/src/classes/Router.js @@ -0,0 +1,26 @@ +export class Router { + constructor() { + this.routes = {}; + window.addEventListener("popstate", this.handlePopState.bind(this)); + } + + addRoute(path, handler) { + this.routes[path] = handler; + } + + navigateTo(path, replace = false) { + if (replace) history.replaceState({}, "", path); + else history.pushState({}, "", path); + this.handleRoute(path); + } + + handlePopState() { + this.handleRoute(window.location.pathname); + } + + handleRoute(path) { + const handler = this.routes[path]; + if (handler) handler(); + else this.navigateTo("/404"); + } +} diff --git a/src/classes/User.js b/src/classes/User.js new file mode 100644 index 00000000..982d3df2 --- /dev/null +++ b/src/classes/User.js @@ -0,0 +1,23 @@ +export class User { + constructor() { + this.preferences = JSON.parse(localStorage.getItem("user")) || {}; + } + + set(key, value) { + this.preferences[key] = value; + this.save(); + } + + get(key) { + return this.preferences[key]; + } + + save() { + localStorage.setItem("user", JSON.stringify(this.preferences)); + } + + clear() { + this.preferences = {}; + localStorage.removeItem("user"); + } +} diff --git a/src/components/Footer.js b/src/components/Footer.js new file mode 100644 index 00000000..ed9d133a --- /dev/null +++ b/src/components/Footer.js @@ -0,0 +1,6 @@ +export function Footer() { + return ` + `; +} diff --git a/src/components/Header.js b/src/components/Header.js new file mode 100644 index 00000000..cf96c24c --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,38 @@ +import { isLoggedIn } from "../main"; + +export function Header() { + const currentPath = window.location.pathname; + + const profileLinkClass = + currentPath === "/profile" ? "text-blue-600" : "text-gray-600"; + + const homeLinkeClass = + currentPath === "/" ? "text-blue-600 font-bold" : "text-gray-600"; + + return ` +
+

항해플러스

+
+ + `; +} diff --git a/src/main.js b/src/main.js index 755b2805..59a1b510 100644 --- a/src/main.js +++ b/src/main.js @@ -1,111 +1,53 @@ -document.querySelector('#root').innerHTML = ` -
-
-
-

항해플러스

-
- - - -
-
- - -
- -
- -
-
- 프로필 -
-

홍길동

-

5분 전

-
-
-

오늘 날씨가 정말 좋네요. 다들 좋은 하루 보내세요!

-
- - - -
-
- -
-
- 프로필 -
-

김철수

-

15분 전

-
-
-

새로운 프로젝트를 시작했어요. 열심히 코딩 중입니다!

-
- - - -
-
- -
-
- 프로필 -
-

이영희

-

30분 전

-
-
-

오늘 점심 메뉴 추천 받습니다. 뭐가 좋을까요?

-
- - - -
-
- -
-
- 프로필 -
-

박민수

-

1시간 전

-
-
-

주말에 등산 가실 분 계신가요? 함께 가요!

-
- - - -
-
- -
-
- 프로필 -
-

정수연

-

2시간 전

-
-
-

새로 나온 영화 재미있대요. 같이 보러 갈 사람?

-
- - - -
-
-
-
- - -
-
-`; +import { Router } from "./classes/Router"; +import { User } from "./classes/User"; +import { HomePage } from "./pages/HomePage"; +import { LoginPage } from "./pages/LoginPage"; +import { NotFound } from "./pages/NotFound"; +import { ProfilePage } from "./pages/ProfilePage"; + +export const router = new Router(); +export const user = new User(); +export function isLoggedIn() { + return user?.preferences?.username?.length > 0; +} + +router.addRoute("/", HomePage); +router.addRoute("/profile", ProfilePage); +router.addRoute("/login", LoginPage); +router.addRoute("/404", NotFound); + +export function handleRouting() { + const links = document.querySelectorAll("a"); + + links.forEach((link) => { + link.addEventListener("click", (e) => { + e.preventDefault(); + + if (e.target.getAttribute("href") === "/login") { + e.stopPropagation(); + return; + } + router.navigateTo(e.target.getAttribute("href")); + }); + }); +} + +export function Logout() { + const logoutEl = document.querySelector("#logout"); + if (logoutEl) + logoutEl.addEventListener("click", () => { + user.clear(); + router.navigateTo("/"); + }); +} + +document.addEventListener("DOMContentLoaded", () => { + router.handleRoute(window.location.pathname); +}); + +window.addEventListener("error", (event) => { + const errorBoundary = document.createElement("div"); + errorBoundary.id = "error-boundary"; + errorBoundary.textContent = `오류 발생! ${event.message}`; + document.body.appendChild(errorBoundary); +}); diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js new file mode 100644 index 00000000..49fa2326 --- /dev/null +++ b/src/pages/HomePage.js @@ -0,0 +1,132 @@ +import { Footer } from "../components/Footer"; +import { Header } from "../components/Header"; +import { Logout, handleRouting } from "../main"; + +export function HomePage() { + let html; + html = `
+
+
+
+ + +
+ +
+
+
+ 프로필 +
+

홍길동

+

5분 전

+
+
+

오늘 날씨가 정말 좋네요. 다들 좋은 하루 보내세요!

+
+ + + +
+
+ +
+
+ 프로필 +
+

김철수

+

15분 전

+
+
+

새로운 프로젝트를 시작했어요. 열심히 코딩 중입니다!

+
+ + + +
+
+ +
+
+ 프로필 +
+

이영희

+

30분 전

+
+
+

오늘 점심 메뉴 추천 받습니다. 뭐가 좋을까요?

+
+ + + +
+
+ +
+
+ 프로필 +
+

박민수

+

1시간 전

+
+
+

주말에 등산 가실 분 계신가요? 함께 가요!

+
+ + + +
+
+ +
+
+ 프로필 +
+

정수연

+

2시간 전

+
+
+

새로 나온 영화 재미있대요. 같이 보러 갈 사람?

+
+ + + +
+
+
+
+
+
`; + document.querySelector( + "#root" + ).innerHTML = `
+
${Header()}${html}${Footer()}
+
`; + + handleRouting(); + Logout(); +} diff --git a/src/pages/LoginPage.js b/src/pages/LoginPage.js new file mode 100644 index 00000000..d70811e3 --- /dev/null +++ b/src/pages/LoginPage.js @@ -0,0 +1,68 @@ +import { handleRouting, isLoggedIn, router, user } from "../main"; + +export function LoginPage() { + if (isLoggedIn()) { + router.navigateTo("/"); + return; + } + + const html = `
+
+

+ 항해플러스 +

+
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+
`; + document.querySelector("#root").innerHTML = html; + handleRouting(); + handleLogin(); +} + +function handleLogin() { + const loginForm = document.querySelector("#login-form"); + if (loginForm) { + loginForm.addEventListener("submit", (e) => { + e.preventDefault(); + const username = document.querySelector("#username").value; + + user.set("username", username); + user.set("email", ""); + user.set("bio", ""); + + if (isLoggedIn()) router.navigateTo("/profile"); + }); + } +} diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js new file mode 100644 index 00000000..5b3754c2 --- /dev/null +++ b/src/pages/NotFound.js @@ -0,0 +1,25 @@ +import { handleRouting } from "../main"; + +export function NotFound() { + const html = `
+
+

항해플러스

+

404

+

페이지를 찾을 수 없습니다

+

+ 요청하신 페이지가 존재하지 않거나 이동되었을 수 있습니다. +

+ + 홈으로 돌아가기 + +
+
`; + document.querySelector("#root").innerHTML = html; + handleRouting(); +} diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js new file mode 100644 index 00000000..e1477774 --- /dev/null +++ b/src/pages/ProfilePage.js @@ -0,0 +1,106 @@ +import { Footer } from "../components/Footer"; +import { Header } from "../components/Header"; +import { Logout, handleRouting, isLoggedIn, router, user } from "../main"; + +export function ProfilePage() { + if (!isLoggedIn()) { + router.navigateTo("/login", true); + return; + } + + let html = `
+
+

+ 내 프로필 +

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
`; + + document.querySelector( + "#root" + ).innerHTML = `
+
${Header()}${html}${Footer()}
+
`; + + handleRouting(); + + const userName = user.preferences?.username; + const userIntro = user.preferences?.bio; + const email = user.preferences?.email; + const userNameInputEl = document.querySelector("#username"); + const userIntroInputEl = document.querySelector("#bio"); + const emailInputEl = document.querySelector("#email"); + + userNameInputEl.value = userName; + userIntroInputEl.value = userIntro; + emailInputEl.value = email; + handleProfileUpdate(); + Logout(); +} + +function handleProfileUpdate() { + const profileForm = document.getElementById("profile-form"); + const bioInput = document.getElementById("bio"); + const usernameInput = document.getElementById("username"); + const emailInput = document.getElementById("email"); + + if (profileForm) { + profileForm.addEventListener("submit", (e) => { + e.preventDefault(); + user.set("username", usernameInput.value); + user.set("email", emailInput.value); + user.set("bio", bioInput.value); + alert("프로필이 업데이트 되었습니다"); + }); + } +}