From c3327e75c4d6526f0e56295d32f9db34278d1adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=9C=EC=A4=80?= Date: Mon, 30 Sep 2024 16:23:07 +0900 Subject: [PATCH 01/11] =?UTF-8?q?jsx=ED=8C=8C=EC=9D=BC=EB=A1=9C=20=20?= =?UTF-8?q?=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 24 +++++++++++++++- src/components/posts/Post.jsx | 20 ++++++++++++- src/components/posts/PostForm.jsx | 9 +++++- src/components/templates/Footer.jsx | 8 +++++- src/components/templates/Header.jsx | 8 +++++- src/components/templates/Navigation.jsx | 18 +++++++++++- src/lib/createVNode.js | 7 +++-- src/pages/HomePage.jsx | 26 ++++++++++++++++- src/pages/LoginPage.jsx | 22 ++++++++++++++- src/pages/NotFoundPage.jsx | 14 +++++++++- src/pages/ProfilePage.jsx | 37 ++++++++++++++++++++++++- 11 files changed, 181 insertions(+), 12 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 3764f6dc..587185e6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,26 @@ /** @jsx createVNode */ import{ createVNode } from "./lib"; -export const App = () => ({}); +export const App = () => { + const PageComponent = targetPage ?? NotFoundPage; + const error = globalStore.getState().error; + + return ( +
+ ${PageComponent()} + ${error ? ` + + ` : ''} +
+ ); +}; diff --git a/src/components/posts/Post.jsx b/src/components/posts/Post.jsx index 2fc9f16c..2ef456dc 100644 --- a/src/components/posts/Post.jsx +++ b/src/components/posts/Post.jsx @@ -1,4 +1,22 @@ /** @jsx createVNode */ import{ createVNode } from "../../lib"; -export const Post = () => ({}); +export const Post = () => { + return ( +
+
+ 프로필 +
+
작성자
+
시간
+
+
+

내용

+
+ + 댓글 + 공유 +
+
+ ); +}; diff --git a/src/components/posts/PostForm.jsx b/src/components/posts/PostForm.jsx index 7788cd67..8516f170 100644 --- a/src/components/posts/PostForm.jsx +++ b/src/components/posts/PostForm.jsx @@ -1,4 +1,11 @@ /** @jsx createVNode */ import{ createVNode } from "../../lib"; -export const PostForm = () => ({}); +export const PostForm = () => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/templates/Footer.jsx b/src/components/templates/Footer.jsx index c2d3a109..63f5717b 100644 --- a/src/components/templates/Footer.jsx +++ b/src/components/templates/Footer.jsx @@ -1,4 +1,10 @@ /** @jsx createVNode */ import{ createVNode } from "../../lib"; -export const Footer = () => ({}); +export const Footer = () => { + return ( + + ); +}; diff --git a/src/components/templates/Header.jsx b/src/components/templates/Header.jsx index 2d799a6a..36f482fc 100644 --- a/src/components/templates/Header.jsx +++ b/src/components/templates/Header.jsx @@ -1,4 +1,10 @@ /** @jsx createVNode */ import{ createVNode } from "../../lib"; -export const Header = () => ({}); +export const Header = () => { + return ( +
+

항해플러스

+
+ ); +}; diff --git a/src/components/templates/Navigation.jsx b/src/components/templates/Navigation.jsx index 63a020ea..909559ff 100644 --- a/src/components/templates/Navigation.jsx +++ b/src/components/templates/Navigation.jsx @@ -1,4 +1,20 @@ /** @jsx createVNode */ import{ createVNode } from "../../lib"; -export const Navigation = () => ({}); +export const Navigation = () => { + const getNavItemClass = (path) => { + const currentPath = window.location.pathname; + return currentPath === path ? 'text-blue-600 font-bold' : 'text-gray-600'; + } + + return ( + + ); +}; diff --git a/src/lib/createVNode.js b/src/lib/createVNode.js index bd0ee273..fccb5000 100644 --- a/src/lib/createVNode.js +++ b/src/lib/createVNode.js @@ -5,6 +5,9 @@ // 4. Infinity를 사용하여 모든 깊이의 배열을 평탄화하세요. export function createVNode(type, props, ...children) { - // 여기에 구현하세요 - return {} + return { + type, + props, + children: children.flat(Infinity).filter(Boolean) + } } diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index 6e61db75..dc305bff 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -1,4 +1,28 @@ /** @jsx createVNode */ +import { Header } from "../components/templates/Header"; +import { Navigation } from "../components/templates/Navigation"; +import { Footer } from '../components/templates/Footer'; + import{ createVNode } from "../lib"; -export const HomePage = () => ({}); +export const HomePage = () => { + const { loggedIn, posts } = globalStore.getState(); + + return ( +
+
+ {Header()} + {Navigation({ loggedIn })} + +
+ {loggedIn ? NotFoundPage() : ''} +
+ {posts.map(ProfilePage).join('')} +
+
+ + {Footer()} +
+
+ ); +}; diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 36ab4ccb..e2a42441 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -1,4 +1,24 @@ /** @jsx createVNode */ import{ createVNode } from "../lib"; -export const LoginPage = () => ({}); +export const LoginPage = () => { + return ( +
+
+

항해플러스

+
+ + + +
+ +
+
+ +
+
+
+ ); +}; diff --git a/src/pages/NotFoundPage.jsx b/src/pages/NotFoundPage.jsx index 09f48ef8..737d98e8 100644 --- a/src/pages/NotFoundPage.jsx +++ b/src/pages/NotFoundPage.jsx @@ -1,4 +1,16 @@ /** @jsx createVNode */ import{ createVNode } from "../lib"; -export const NotFoundPage = () => ({}); +export const NotFoundPage = () => { + return ( +
+
+

항해플러스

+

404

+

페이지를 찾을 수 없습니다

+

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

+ 홈으로 돌아가기 +
+
+ ); +}; diff --git a/src/pages/ProfilePage.jsx b/src/pages/ProfilePage.jsx index 70fb9a9e..c881ca03 100644 --- a/src/pages/ProfilePage.jsx +++ b/src/pages/ProfilePage.jsx @@ -1,4 +1,39 @@ /** @jsx createVNode */ +import { Footer } from "../components/templates/Footer"; +import { Header } from "../components/templates/Header"; import{ createVNode } from "../lib"; -export const ProfilePage = () => ({}); +export const ProfilePage = () => { + const { loggedIn, currentUser } = globalStore.getState(); + const { username = '', email = '', bio = '' } = currentUser ?? {} + + return ( +
+
+ {Header()} + {Navigation({ loggedIn })} +
+
+

내 프로필

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ {Footer()} +
+
+ ); +}; From c3e4a6825c9abef588da0ad65b759cbf154efcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=9C=EC=A4=80?= Date: Mon, 30 Sep 2024 17:03:30 +0900 Subject: [PATCH 02/11] =?UTF-8?q?createElement=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/createElement.js | 57 ++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/lib/createElement.js b/src/lib/createElement.js index e6092bf3..5f002575 100644 --- a/src/lib/createElement.js +++ b/src/lib/createElement.js @@ -1,14 +1,49 @@ // TODO: createElement 함수 구현 -// 1. vNode가 falsy면 빈 텍스트 노드를 반환합니다. -// 2. vNode가 문자열이나 숫자면 텍스트 노드를 생성하여 반환합니다. -// 3. vNode가 배열이면 DocumentFragment를 생성하고 각 자식에 대해 createElement를 재귀 호출하여 추가합니다. -// 4. vNode.type이 함수면 해당 함수를 호출하고 그 결과로 createElement를 재귀 호출합니다. -// 5. 위 경우가 아니면 실제 DOM 요소를 생성합니다: -// - vNode.type에 해당하는 요소를 생성 -// - vNode.props의 속성들을 적용 (이벤트 리스너, className, 일반 속성 등 처리) -// - vNode.children의 각 자식에 대해 createElement를 재귀 호출하여 추가 - export function createElement(vNode) { - // 여기에 구현하세요 - return {} + console.log(vNode); + + if (!vNode) { + return document.createTextNode(""); + } + + if (typeof vNode === "string" || typeof vNode === "number") { + return document.createTextNode(vNode); + } + + if (Array.isArray(vNode)) { + const fragment = document.createDocumentFragment(); + + vNode.forEach((node) => { + fragment.appendChild(createElement(node)); + }); + + return fragment; + } + + if (typeof vNode.type === 'function') { + const componentVNode = vNode.type(vNode.props || {}); + return createElement(componentVNode); + } + + const domElement = document.createElement(vNode.type); + + if (vNode.props) { + Object.keys(vNode.props).forEach((key) => { + if (key.startsWith('on')) { + const eventName = key.slice(2).toLowerCase(); + domElement.addEventListener(eventName, vNode.props[key]); + } else if (key === 'className') { + domElement.setAttribute('class', vNode.props[key]); + } else { + domElement.setAttribute(key, vNode.props[key]); + } + }); + } + + vNode.children.forEach((child) => { + domElement.appendChild(createElement(child)); + }); + + return domElement; } + From 97b2f2a027938884c1fa49a86b73349afa02f873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=9C=EC=A4=80?= Date: Mon, 30 Sep 2024 17:35:52 +0900 Subject: [PATCH 03/11] =?UTF-8?q?jsx=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 10 +++++----- src/components/templates/Navigation.jsx | 6 +++--- src/pages/HomePage.jsx | 10 ++++------ src/pages/ProfilePage.jsx | 7 +++---- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 587185e6..b572fbec 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,14 @@ /** @jsx createVNode */ -import{ createVNode } from "./lib"; +import { createVNode } from './lib'; +import { globalStore } from './stores'; -export const App = () => { +export const App = ({ targetPage }) => { const PageComponent = targetPage ?? NotFoundPage; const error = globalStore.getState().error; return (
- ${PageComponent()} - ${error ? ` + {error ? ` ); }; diff --git a/src/components/templates/Navigation.jsx b/src/components/templates/Navigation.jsx index 909559ff..5fa8a780 100644 --- a/src/components/templates/Navigation.jsx +++ b/src/components/templates/Navigation.jsx @@ -11,9 +11,9 @@ export const Navigation = () => { ); diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index dc305bff..55360407 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -2,22 +2,20 @@ import { Header } from "../components/templates/Header"; import { Navigation } from "../components/templates/Navigation"; import { Footer } from '../components/templates/Footer'; - -import{ createVNode } from "../lib"; +import { createVNode } from "../lib"; +import { globalStore } from "../stores"; +import { ProfilePage } from "./ProfilePage"; export const HomePage = () => { - const { loggedIn, posts } = globalStore.getState(); return (
{Header()} - {Navigation({ loggedIn })} + {Navigation()}
- {loggedIn ? NotFoundPage() : ''}
- {posts.map(ProfilePage).join('')}
diff --git a/src/pages/ProfilePage.jsx b/src/pages/ProfilePage.jsx index c881ca03..c751e673 100644 --- a/src/pages/ProfilePage.jsx +++ b/src/pages/ProfilePage.jsx @@ -1,17 +1,16 @@ /** @jsx createVNode */ import { Footer } from "../components/templates/Footer"; import { Header } from "../components/templates/Header"; -import{ createVNode } from "../lib"; +import { createVNode } from "../lib"; + export const ProfilePage = () => { - const { loggedIn, currentUser } = globalStore.getState(); - const { username = '', email = '', bio = '' } = currentUser ?? {} return (
{Header()} - {Navigation({ loggedIn })} + {Navigation()}

내 프로필

From 8fdc42e31297d9587638659d971671901dd7aa58 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Sep 2024 23:11:57 +0900 Subject: [PATCH 04/11] =?UTF-8?q?1=EC=A3=BC=EC=B0=A8=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EA=B3=BC=EC=A0=9C=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 18 +++++---- src/components/posts/Post.jsx | 28 ++++++------- src/components/posts/PostForm.jsx | 6 +-- src/components/templates/Footer.jsx | 2 +- src/components/templates/Header.jsx | 4 +- src/components/templates/Navigation.jsx | 19 ++++----- src/lib/createElement.js | 2 - src/main.jsx | 15 ++++++- src/pages/HomePage.jsx | 26 ++++++++---- src/pages/LoginPage.jsx | 42 +++++++++++++------ src/pages/NotFoundPage.jsx | 14 +++---- src/pages/ProfilePage.jsx | 54 ++++++++++++++++--------- 12 files changed, 144 insertions(+), 86 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index b572fbec..45b26b44 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,26 +1,28 @@ /** @jsx createVNode */ import { createVNode } from './lib'; +import { NotFoundPage } from './pages'; import { globalStore } from './stores'; export const App = ({ targetPage }) => { const PageComponent = targetPage ?? NotFoundPage; - const error = globalStore.getState().error; + const { error } = globalStore.getState(); return (
- {error ? ` -