Skip to content

Commit

Permalink
Merge pull request #1 from jonjour99/develop
Browse files Browse the repository at this point in the history
SPA 기본기능 완성
  • Loading branch information
jonjour99 authored Jan 10, 2025
2 parents 6df9b58 + 42f13af commit 08ca95a
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 171 deletions.
252 changes: 148 additions & 104 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"jsdom": "^25.0.1",
"lint-staged": "^15.2.11",
"prettier": "^3.4.2",
"vite": "^6.0.3",
"vite": "^6.0.7",
"vitest": "^2.1.8"
}
}
1 change: 1 addition & 0 deletions src/components/Footer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

const Footer = () => `
<footer class="bg-gray-200 p-4 text-center">
<p>&copy; 2024 항해플러스. All rights reserved.</p>
Expand Down
11 changes: 4 additions & 7 deletions src/components/Header.js
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;
18 changes: 18 additions & 0 deletions src/components/Navigation.js
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>
`;
};
14 changes: 14 additions & 0 deletions src/layouts/Layout.js
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

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ fireAnEvent node_modules/jsdom/lib/jsdom/living/helpers/events.js:18:36 ❯ SessionHistory._fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:144:7 ❯ fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:133:35 This error originated in "src/__tests__/advanced.hashRouter.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "src/__tests__/advanced.hashRouter.test.js". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ fireAnEvent node_modules/jsdom/lib/jsdom/living/helpers/events.js:18:36 ❯ SessionHistory._fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:144:7 ❯ fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:133:35 This error originated in "src/__tests__/advanced.hashRouter.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "src/__tests__/advanced.hashRouter.test.js". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ fireAnEvent node_modules/jsdom/lib/jsdom/living/helpers/events.js:18:36 ❯ SessionHistory._fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:144:7 ❯ fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:133:35 This error originated in "src/__tests__/advanced.hashRouter.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "src/__tests__/advanced.hashRouter.test.js". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ fireAnEvent node_modules/jsdom/lib/jsdom/living/helpers/events.js:18:36 ❯ SessionHistory._fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:144:7 ❯ fireEvents node_modules/jsdom/lib/jsdom/living/window/SessionHistory.js:133:35 This error originated in "src/__tests__/advanced.hashRouter.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "src/__tests__/advanced.hashRouter.test.js". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ EventTargetImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ goTo src/__tests__/advanced.test.js:27:10 This error originated in "src/__tests__/advanced.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "비로그인 사용자가 프로필 페이지에 접근시 로그인 페이지로 리다이렉트 한다.". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ EventTargetImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ goTo src/__tests__/advanced.test.js:27:10 This error originated in "src/__tests__/advanced.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "로그인된 사용자가 로그인 페이지에 접근시 메인 페이지로 리다이렉트 한다.". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ EventTargetImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ goTo src/__tests__/advanced.test.js:27:10 This error originated in "src/__tests__/advanced.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "네비게이션의 링크를 클릭에서 이벤트 전파를 막았을 때, 아무일도 일어나지 않는다.". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / advacned

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ EventTargetImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ goTo src/__tests__/advanced.test.js:27:10 This error originated in "src/__tests__/advanced.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "네비게이션의 링크를 클릭에서 이벤트 전파를 막았을 때, 아무일도 일어나지 않는다.". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 8 in src/layouts/Layout.js

View workflow job for this annotation

GitHub Actions / basic

Unhandled error

TypeError: Header is not a function ❯ Module.Layout src/layouts/Layout.js:8:9 ❯ HomePage src/pages/HomePage.js:96:10 ❯ router src/router.js:60:47 ❯ callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ EventTargetImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ EventTargetImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 ❯ dispatchEvent node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34 ❯ goTo src/__tests__/basic.test.js:28:10 This error originated in "src/__tests__/basic.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is ""/" 경로로 접근하면 홈 페이지가 렌더링된다". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.
${Navigation(currentPath)}
${content}
${Footer()}
</div>
</div>
`;
49 changes: 13 additions & 36 deletions src/main.js
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();
});
97 changes: 97 additions & 0 deletions src/pages/HomePage.js
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, "/");
};
8 changes: 4 additions & 4 deletions src/pages/LoginPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ const LoginPage = () => `
<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 max-w-md">
<h1 class="text-2xl font-bold text-center text-blue-600 mb-8">항해플러스</h1>
<form>
<form id="login-form">
<div class="mb-4">
<input type="text" placeholder="이메일 또는 전화번호" class="w-full p-2 border rounded">
<input type="text" id="username" placeholder="사용자 이름" class="w-full p-2 border rounded">
</div>
<div class="mb-6">
<input type="password" placeholder="비밀번호" class="w-full p-2 border rounded">
<input type="password" id="password" placeholder="비밀번호" class="w-full p-2 border rounded">
</div>
<button type="submit" class="w-full bg-blue-600 text-white p-2 rounded font-bold">로그인</button>
<button type="submit" id="loginButton" class="w-full bg-blue-600 text-white p-2 rounded font-bold">로그인</button>
</form>
<div class="mt-4 text-center">
<a href="#" class="text-blue-600 text-sm">비밀번호를 잊으셨나요?</a>
Expand Down
15 changes: 15 additions & 0 deletions src/pages/NotFoundPage.js
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>
`;
43 changes: 24 additions & 19 deletions src/pages/ProfilePage.js
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;
Loading

0 comments on commit 08ca95a

Please sign in to comment.