-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
SPA 기본기능 완성
- Loading branch information
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,7 @@ | ||
import Nav from "./Nav"; | ||
|
||
const Header = () => ` | ||
<header class="bg-blue-600 text-white p-4 sticky top-0"> | ||
<h1 class="text-2xl font-bold">항해플러스</h1> | ||
</header> | ||
${Nav()} | ||
<header class="bg-blue-600 text-white p-4 sticky top-0"> | ||
<h1 class="text-2xl font-bold">항해플러스</h1> | ||
</header> | ||
`; | ||
|
||
export default Header; | ||
export default Header; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export const Navigation = (currentPath) => { | ||
const isLoggedIn = !!localStorage.getItem("user"); | ||
return ` | ||
<nav class="bg-white shadow-md p-2 sticky top-14"> | ||
<ul class="flex justify-around"> | ||
<li><a href="/" class="${currentPath === "/" ? "text-blue-600" : "text-gray-600"}">홈</a></li> | ||
<li><a href="/profile" class="${currentPath === "/profile" ? "text-blue-600" : "text-gray-600"}">프로필</a></li> | ||
<li> | ||
${ | ||
isLoggedIn | ||
? `<a href="#" id="logout" class="text-gray-600">로그아웃</a>` | ||
: `<a href="/login" class="${currentPath === "/login" ? "text-blue-600" : "text-gray-600"}">로그인</a>` | ||
} | ||
</li> | ||
</ul> | ||
</nav> | ||
`; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Header } from "../components/Header.js"; | ||
import { Navigation } from "../components/Navigation.js"; | ||
import { Footer } from "../components/Footer.js"; | ||
|
||
export const Layout = (content, currentPath = "/") => ` | ||
<div class="bg-gray-100 min-h-screen flex justify-center"> | ||
<div class="max-w-md w-full"> | ||
${Header()} | ||
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / advacnedUnhandled error
Check failure on line 8 in src/layouts/Layout.js GitHub Actions / basicUnhandled error
|
||
${Navigation(currentPath)} | ||
${content} | ||
${Footer()} | ||
</div> | ||
</div> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,22 @@ | ||
import router from "./routes/router"; | ||
import handleProfile from "./pages/handleProfile"; | ||
import handleChangeProfile from "./pages/handleChangeProfile"; | ||
import handleLogin from "./pages/handleLogin"; | ||
import hashRouter from "./routes/hashRouter"; | ||
|
||
window.addEventListener("load", () => { | ||
if (window.location.hash) { | ||
hashRouter(); | ||
} else { | ||
router(); | ||
} | ||
}); | ||
|
||
window.addEventListener("popstate", () => router()); | ||
window.addEventListener("hashchange", () => hashRouter()); | ||
import { router } from "./router.js"; | ||
|
||
// 링크 클릭 이벤트 처리 | ||
document.addEventListener("click", (e) => { | ||
if (e.target.tagName === "A") { | ||
if (e.target.matches("a")) { | ||
e.preventDefault(); | ||
if (e.target.id === "logout") { | ||
localStorage.removeItem("user"); | ||
router("/login"); | ||
} else { | ||
const path = e.target.getAttribute("href"); | ||
router(path); | ||
handleProfile(); | ||
const href = e.target.getAttribute("href"); | ||
if (href !== "#") { | ||
window.history.pushState({}, "", href); | ||
router(); | ||
} | ||
} | ||
}); | ||
|
||
document.addEventListener("submit", (e) => { | ||
e.preventDefault(); | ||
const form = e.target; | ||
const formData = new FormData(form); | ||
const data = Object.fromEntries(formData); | ||
// 브라우저 뒤로가기/앞으로가기 처리 | ||
window.addEventListener("popstate", router); | ||
|
||
// 초기 라우팅 처리 | ||
window.addEventListener("DOMContentLoaded", () => { | ||
router(); | ||
|
||
if (form.id == "login-form") { | ||
handleLogin(data); | ||
} | ||
if (form.id == "profile-form") { | ||
handleChangeProfile(data); | ||
} | ||
handleProfile(); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { Layout } from "../layouts/Layout.js"; | ||
|
||
export const HomePage = () => { | ||
const content = ` | ||
<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> | ||
<button class="mt-2 bg-blue-600 text-white px-4 py-2 rounded">게시</button> | ||
</div> | ||
<div class="space-y-4"> | ||
<div class="bg-white rounded-lg shadow p-4"> | ||
<div class="flex items-center mb-2"> | ||
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2"> | ||
<div> | ||
<p class="font-bold">홍길동</p> | ||
<p class="text-sm text-gray-500">5분 전</p> | ||
</div> | ||
</div> | ||
<p>오늘 날씨가 정말 좋네요. 다들 좋은 하루 보내세요!</p> | ||
<div class="mt-2 flex justify-between text-gray-500"> | ||
<button>좋아요</button> | ||
<button>댓글</button> | ||
<button>공유</button> | ||
</div> | ||
</div> | ||
<div class="bg-white rounded-lg shadow p-4"> | ||
<div class="flex items-center mb-2"> | ||
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2"> | ||
<div> | ||
<p class="font-bold">김철수</p> | ||
<p class="text-sm text-gray-500">15분 전</p> | ||
</div> | ||
</div> | ||
<p>새로운 프로젝트를 시작했어요. 열심히 코딩 중입니다!</p> | ||
<div class="mt-2 flex justify-between text-gray-500"> | ||
<button>좋아요</button> | ||
<button>댓글</button> | ||
<button>공유</button> | ||
</div> | ||
</div> | ||
<div class="bg-white rounded-lg shadow p-4"> | ||
<div class="flex items-center mb-2"> | ||
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2"> | ||
<div> | ||
<p class="font-bold">이영희</p> | ||
<p class="text-sm text-gray-500">30분 전</p> | ||
</div> | ||
</div> | ||
<p>오늘 점심 메뉴 추천 받습니다. 뭐가 좋을까요?</p> | ||
<div class="mt-2 flex justify-between text-gray-500"> | ||
<button>좋아요</button> | ||
<button>댓글</button> | ||
<button>공유</button> | ||
</div> | ||
</div> | ||
<div class="bg-white rounded-lg shadow p-4"> | ||
<div class="flex items-center mb-2"> | ||
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2"> | ||
<div> | ||
<p class="font-bold">박민수</p> | ||
<p class="text-sm text-gray-500">1시간 전</p> | ||
</div> | ||
</div> | ||
<p>주말에 등산 가실 분 계신가요? 함께 가요!</p> | ||
<div class="mt-2 flex justify-between text-gray-500"> | ||
<button>좋아요</button> | ||
<button>댓글</button> | ||
<button>공유</button> | ||
</div> | ||
</div> | ||
<div class="bg-white rounded-lg shadow p-4"> | ||
<div class="flex items-center mb-2"> | ||
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2"> | ||
<div> | ||
<p class="font-bold">정수연</p> | ||
<p class="text-sm text-gray-500">2시간 전</p> | ||
</div> | ||
</div> | ||
<p>새로 나온 영화 재미있대요. 같이 보러 갈 사람?</p> | ||
<div class="mt-2 flex justify-between text-gray-500"> | ||
<button>좋아요</button> | ||
<button>댓글</button> | ||
<button>공유</button> | ||
</div> | ||
</div> | ||
</div> | ||
</main> | ||
`; | ||
|
||
return Layout(content, "/"); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export const NotFoundPage = () => ` | ||
<main class="bg-gray-100 flex items-center justify-center min-h-screen"> | ||
<div class="bg-white p-8 rounded-lg shadow-md w-full text-center" style="max-width: 480px"> | ||
<h1 class="text-2xl font-bold text-blue-600 mb-4">항해플러스</h1> | ||
<p class="text-4xl font-bold text-gray-800 mb-4">404</p> | ||
<p class="text-xl text-gray-600 mb-8">페이지를 찾을 수 없습니다</p> | ||
<p class="text-gray-600 mb-8"> | ||
요청하신 페이지가 존재하지 않거나 이동되었을 수 있습니다. | ||
</p> | ||
<a href="/" class="bg-blue-600 text-white px-4 py-2 rounded font-bold"> | ||
홈으로 돌아가기 | ||
</a> | ||
</div> | ||
</main> | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,66 @@ | ||
const ProfilePage = () => ` | ||
<main class="p-4"> | ||
import { Layout } from "../layouts/Layout.js"; | ||
|
||
const ProfilePage = () => { | ||
const user = JSON.parse(localStorage.getItem("user") || "{}"); | ||
const { username = "", email = "", bio = "" } = user; | ||
|
||
const content = ` | ||
<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"> | ||
내 프로필 | ||
</h2> | ||
<form action="/" id="profile-form"> | ||
<form id="profile-form"> | ||
<div class="mb-4"> | ||
<label | ||
for="username" | ||
class="block text-gray-700 text-sm font-bold mb-2" | ||
>사용자 이름</label | ||
> | ||
>사용자 이름</label> | ||
<input | ||
type="text" | ||
id="username" | ||
name="username" | ||
value="홍길동" | ||
value="${username}" | ||
class="w-full p-2 border rounded" | ||
/> | ||
</div> | ||
<div class="mb-4"> | ||
<label | ||
for="email" | ||
class="block text-gray-700 text-sm font-bold mb-2" | ||
>이메일</label | ||
> | ||
>이메일</label> | ||
<input | ||
type="email" | ||
id="email" | ||
name="email" | ||
value="[email protected]" | ||
value="${email}" | ||
class="w-full p-2 border rounded" | ||
/> | ||
</div> | ||
<div class="mb-6"> | ||
<label | ||
for="bio" | ||
class="block text-gray-700 text-sm font-bold mb-2" | ||
>자기소개</label | ||
> | ||
>자기소개</label> | ||
<textarea | ||
id="bio" | ||
name="bio" | ||
rows="4" | ||
class="w-full p-2 border rounded" | ||
>자기소개입니다.</textarea | ||
> | ||
class="w-full p-2 border rounded"> | ||
${bio} | ||
</textarea> | ||
</div> | ||
<button | ||
type="submit" | ||
class="w-full bg-blue-600 text-white p-2 rounded font-bold" | ||
> | ||
프로필 업데이트 | ||
class="w-full bg-blue-600 text-white p-2 rounded font-bold"> | ||
프로필 업데이트 | ||
</button> | ||
</form> | ||
</div> | ||
</main> | ||
`; | ||
</main> | ||
`; | ||
return Layout(content, "/profile"); | ||
}; | ||
|
||
export default ProfilePage; |