From 10944f71089a7334fda40c6ec23335d05562cad9 Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 10:02:24 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20my,=20developer=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=ED=8C=85=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main.tsx b/src/main.tsx index a008a52..b65b3e1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,6 +5,8 @@ import "./index.css"; import App from "./App.tsx"; import { LoginPage } from "./pages/auth/LoginPage.tsx"; import { BoardPage } from "./pages/home/index.tsx"; +import { MyPage } from "./pages/my/index.tsx"; +import { DeveloperPage } from "./pages/developer/index.tsx"; const router = createBrowserRouter([ { @@ -19,6 +21,14 @@ const router = createBrowserRouter([ path: "board/:category", element: , }, + { + path: "my", + element: , + }, + { + path: "developer/:id", + element: , + }, ], }, { From 5d9b857d0636ab135697a14c51573fe33b1806ea Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 10:03:51 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20my,=20developer=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/ProfileSection.tsx | 68 ++++++++++++++++++++++++ src/pages/developer/index.tsx | 33 ++++++++++++ src/pages/my/index.tsx | 29 ++++++++++ 3 files changed, 130 insertions(+) create mode 100644 src/components/common/ProfileSection.tsx create mode 100644 src/pages/developer/index.tsx create mode 100644 src/pages/my/index.tsx diff --git a/src/components/common/ProfileSection.tsx b/src/components/common/ProfileSection.tsx new file mode 100644 index 0000000..806ecb4 --- /dev/null +++ b/src/components/common/ProfileSection.tsx @@ -0,0 +1,68 @@ +import { useRef, useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Camera } from "lucide-react"; + +interface ProfileSectionProps { + isEditable?: boolean; +} + +export const ProfileSection = ({ isEditable = false }: ProfileSectionProps) => { + const fileInputRef = useRef(null); + const [imagePreview, setImagePreview] = useState(null); + + const handleImageClick = () => { + if (!isEditable) return; + fileInputRef.current?.click(); + }; + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreview(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + return ( +
+
+
+ {imagePreview ? ( + Profile preview + ) : ( + isEditable && + )} +
+ {isEditable && ( + + )} +
+

이름

+

나이 · 포지션

+ {isEditable && ( + + )} +
+ ); +}; diff --git a/src/pages/developer/index.tsx b/src/pages/developer/index.tsx new file mode 100644 index 0000000..686268e --- /dev/null +++ b/src/pages/developer/index.tsx @@ -0,0 +1,33 @@ +import { ProfileSection } from "@/components/common/ProfileSection"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +const DEVELOPER_TABS = [ + { value: "profile", label: "프로필" }, + { value: "posts", label: "작성한 글" }, +]; + +export const DeveloperPage = () => { + return ( +
+ + + + {DEVELOPER_TABS.map((tab) => ( + + {tab.label} + + ))} + + +

프로필 정보가 여기에 표시됩니다.

+
+ +

작성한 글 목록이 여기에 표시됩니다.

+
+
+
+ ); +}; diff --git a/src/pages/my/index.tsx b/src/pages/my/index.tsx new file mode 100644 index 0000000..f575aac --- /dev/null +++ b/src/pages/my/index.tsx @@ -0,0 +1,29 @@ +import { ProfileSection } from "@/components/common/ProfileSection"; +import { ChevronRight } from "lucide-react"; + +const MENU_ITEMS = [ + { id: "posts", label: "작성한 글" }, + { id: "history", label: "활동 이력" }, + { id: "likes", label: "좋아요 한 글" }, + { id: "bookmarks", label: "북마크" }, +]; + +export const MyPage = () => { + return ( +
+ +
+
    + {MENU_ITEMS.map((item) => ( +
  • + {item.label} + +
  • + ))} +
+
+
+ ); +}; From 8eb6dd4e27c7d3046c05d849775ff6e32faf779e Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 11:07:41 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EA=B0=9C=EB=B0=9C=EC=9E=90=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=83=AD=20=EB=B0=8F=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84,=20=EC=9E=91=EC=84=B1=EA=B8=80=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - App.tsx에서 온보딩 페이지 관련 코드 제거 및 GlobalLayout로 변경 - profile.ts에 개발자 탭 상수 추가 - DeveloperPage에서 AppTabs 컴포넌트로 탭 기능 구현 - 프로필 및 작성글 컴포넌트 추가 --- src/App.tsx | 20 +-------- src/constants/profile.ts | 12 ++++++ src/pages/developer/_components/Posts.tsx | 5 +++ src/pages/developer/_components/Profile.tsx | 5 +++ src/pages/developer/index.tsx | 47 ++++++++++----------- 5 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 src/constants/profile.ts create mode 100644 src/pages/developer/_components/Posts.tsx create mode 100644 src/pages/developer/_components/Profile.tsx diff --git a/src/App.tsx b/src/App.tsx index 124eb5b..bc3affb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,8 @@ import { Outlet } from "react-router-dom"; import GlobalLayout from "./components/layout/GlobalLayout"; -import { OnboardingPage } from "./pages/onboarding/OnboardingPage"; - function App() { - const path = window.location.pathname; - - let content; - if (path === '/onboarding') { - content = ; - } else { - content = ( - <> -

brand-primary 색상 테스트

-

brand 색상 테스트

-
- 온보딩 페이지 테스트 - - ); - } - return ( - + ); diff --git a/src/constants/profile.ts b/src/constants/profile.ts new file mode 100644 index 0000000..ae5f151 --- /dev/null +++ b/src/constants/profile.ts @@ -0,0 +1,12 @@ +export const DEVELOPER_TABS = [ + { + value: "profile", + label: "프로필", + }, + { + value: "posts", + label: "작성한 글 ", + }, +] as const; + +export type DeveloperTabValue = (typeof DEVELOPER_TABS)[number]["value"]; diff --git a/src/pages/developer/_components/Posts.tsx b/src/pages/developer/_components/Posts.tsx new file mode 100644 index 0000000..9ec48d4 --- /dev/null +++ b/src/pages/developer/_components/Posts.tsx @@ -0,0 +1,5 @@ +const Posts = () => { + return

작성글

; +}; + +export default Posts; diff --git a/src/pages/developer/_components/Profile.tsx b/src/pages/developer/_components/Profile.tsx new file mode 100644 index 0000000..8442bc3 --- /dev/null +++ b/src/pages/developer/_components/Profile.tsx @@ -0,0 +1,5 @@ +const Profile = () => { + return

프로필

; +}; + +export default Profile; diff --git a/src/pages/developer/index.tsx b/src/pages/developer/index.tsx index 686268e..217a465 100644 --- a/src/pages/developer/index.tsx +++ b/src/pages/developer/index.tsx @@ -1,33 +1,32 @@ +import { useState } from "react"; +import { AppTabs } from "@/components/common/AppTabs"; import { ProfileSection } from "@/components/common/ProfileSection"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; - -const DEVELOPER_TABS = [ - { value: "profile", label: "프로필" }, - { value: "posts", label: "작성한 글" }, -]; +import type { DeveloperTabValue } from "@/constants/profile"; +import { DEVELOPER_TABS } from "@/constants/profile"; +import Posts from "./_components/Posts.tsx"; +import Profile from "./_components/Profile.tsx"; export const DeveloperPage = () => { + const [activeTab, setActiveTab] = useState("profile"); + + const handleTabChange = (value: string) => { + setActiveTab(value as DeveloperTabValue); + }; + return (
- - - {DEVELOPER_TABS.map((tab) => ( - - {tab.label} - - ))} - - -

프로필 정보가 여기에 표시됩니다.

-
- -

작성한 글 목록이 여기에 표시됩니다.

-
-
+ +
+ {activeTab === "profile" && } + {activeTab === "posts" && } +
); }; From dbbf68e21485672c364d729e9b823bd543a2a406 Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 12:14:58 +0900 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20DeveloperPage=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProfileSection을 px-4로 감싸 여백 추가 - 텍스트 색상 및 여백 조정으로 가독성 향상 --- src/pages/developer/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pages/developer/index.tsx b/src/pages/developer/index.tsx index 217a465..263c76d 100644 --- a/src/pages/developer/index.tsx +++ b/src/pages/developer/index.tsx @@ -14,8 +14,10 @@ export const DeveloperPage = () => { }; return ( -
- +
+
+ +
{ listClassName="grid-cols-2" className="w-full mt-8" /> -
+
{activeTab === "profile" && } {activeTab === "posts" && }
From 946612a5a0a79abec32b0f89a91f2eb476235a12 Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 12:22:05 +0900 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20ProfileSection=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FileReader의 결과가 문자열인지 확인하여 setImagePreview 호출 시 타입 안전성 강화 --- src/components/common/ProfileSection.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/common/ProfileSection.tsx b/src/components/common/ProfileSection.tsx index 806ecb4..c380fcd 100644 --- a/src/components/common/ProfileSection.tsx +++ b/src/components/common/ProfileSection.tsx @@ -19,8 +19,11 @@ export const ProfileSection = ({ isEditable = false }: ProfileSectionProps) => { const file = event.target.files?.[0]; if (file) { const reader = new FileReader(); + reader.onloadend = () => { - setImagePreview(reader.result as string); + if (typeof reader.result === "string") { + setImagePreview(reader.result); + } }; reader.readAsDataURL(file); } From 205188e60ff6b7ae61b2565740b612bff63caeee Mon Sep 17 00:00:00 2001 From: Monixc Date: Mon, 28 Jul 2025 12:23:13 +0900 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20DeveloperPage=20=ED=83=AD=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유효한 탭 값인지 확인하여 setActiveTab 호출 시 타입 안전성 강화 --- src/pages/developer/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/developer/index.tsx b/src/pages/developer/index.tsx index 263c76d..23e7d60 100644 --- a/src/pages/developer/index.tsx +++ b/src/pages/developer/index.tsx @@ -10,7 +10,9 @@ export const DeveloperPage = () => { const [activeTab, setActiveTab] = useState("profile"); const handleTabChange = (value: string) => { - setActiveTab(value as DeveloperTabValue); + if (DEVELOPER_TABS.some((tab) => tab.value === value)) { + setActiveTab(value as DeveloperTabValue); + } }; return ( From 9bc145b67b4297b9dda216a76af474a74bc8d5a6 Mon Sep 17 00:00:00 2001 From: Monixc Date: Thu, 31 Jul 2025 11:44:41 +0900 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A9=94=EB=89=B4=20=EA=B4=80=EB=A0=A8=20constants?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/my.ts | 8 ++++++++ src/pages/my/index.tsx | 15 +++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 src/constants/my.ts diff --git a/src/constants/my.ts b/src/constants/my.ts new file mode 100644 index 0000000..1e89c6c --- /dev/null +++ b/src/constants/my.ts @@ -0,0 +1,8 @@ +export const MENU_ITEMS = [ + { id: "posts", label: "작성한 글" }, + { id: "history", label: "활동 이력" }, + { id: "likes", label: "좋아요 한 글" }, + { id: "bookmarks", label: "북마크" }, +] as const; + +export type MenuItemId = (typeof MENU_ITEMS)[number]["id"]; diff --git a/src/pages/my/index.tsx b/src/pages/my/index.tsx index f575aac..68ac55d 100644 --- a/src/pages/my/index.tsx +++ b/src/pages/my/index.tsx @@ -1,14 +1,12 @@ import { ProfileSection } from "@/components/common/ProfileSection"; import { ChevronRight } from "lucide-react"; - -const MENU_ITEMS = [ - { id: "posts", label: "작성한 글" }, - { id: "history", label: "활동 이력" }, - { id: "likes", label: "좋아요 한 글" }, - { id: "bookmarks", label: "북마크" }, -]; +import { MENU_ITEMS, type MenuItemId } from "@/constants/my"; export const MyPage = () => { + const handleMenuItemClick = (menuId: MenuItemId) => { + console.log(`선택된 메뉴: ${menuId}`); + }; + return (
@@ -17,7 +15,8 @@ export const MyPage = () => { {MENU_ITEMS.map((item) => (
  • + onClick={() => handleMenuItemClick(item.id)} + className="flex justify-between items-center py-4 border-b border-brand-surface cursor-pointer hover:bg-brand-surface/50 transition-colors"> {item.label}
  • From 7928a201f1a7b02981e345b13150a2fd5d96b62c Mon Sep 17 00:00:00 2001 From: Monixc Date: Thu, 31 Jul 2025 16:52:08 +0900 Subject: [PATCH 8/9] =?UTF-8?q?fix:=20AppTabs=20=EB=B0=8F=20DeveloperPage?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=95=88=EC=A0=84=EC=84=B1=20=EA=B0=95?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AppTabsProps에 제네릭 타입 추가로 탭 값의 타입 안전성 향상 - DeveloperPage에서 handleTabChange 함수의 매개변수 타입을 DeveloperTabValue로 변경하여 타입 안전성 강화 --- src/components/common/AppTabs.tsx | 17 ++++++++++------- src/pages/developer/index.tsx | 6 ++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/common/AppTabs.tsx b/src/components/common/AppTabs.tsx index 337a46a..3f728c6 100644 --- a/src/components/common/AppTabs.tsx +++ b/src/components/common/AppTabs.tsx @@ -6,25 +6,28 @@ interface Tab { label: string; } -interface AppTabsProps { - tabs: readonly Tab[]; - value: string; - onValueChange: (value: string) => void; +interface AppTabsProps { + tabs: readonly (Tab & { value: T })[]; + value: T; + onValueChange: (value: T) => void; listClassName?: string; triggerClassName?: string; className?: string; } -export const AppTabs = ({ +export const AppTabs = ({ tabs, value, onValueChange, listClassName, triggerClassName, className, -}: AppTabsProps) => { +}: AppTabsProps) => { return ( - + onValueChange(value as T)} + className={cn(className)}> { const [activeTab, setActiveTab] = useState("profile"); - const handleTabChange = (value: string) => { - if (DEVELOPER_TABS.some((tab) => tab.value === value)) { - setActiveTab(value as DeveloperTabValue); - } + const handleTabChange = (value: DeveloperTabValue) => { + setActiveTab(value); }; return ( From 9771de0ef7fb84f554c6315cfab55ac3409235a7 Mon Sep 17 00:00:00 2001 From: Monixc Date: Thu, 31 Jul 2025 22:36:43 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20ProfileSection=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/{common => profile}/ProfileSection.tsx | 0 src/pages/developer/index.tsx | 2 +- src/pages/my/index.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/components/{common => profile}/ProfileSection.tsx (100%) diff --git a/src/components/common/ProfileSection.tsx b/src/components/profile/ProfileSection.tsx similarity index 100% rename from src/components/common/ProfileSection.tsx rename to src/components/profile/ProfileSection.tsx diff --git a/src/pages/developer/index.tsx b/src/pages/developer/index.tsx index c33c8c3..3e2cea8 100644 --- a/src/pages/developer/index.tsx +++ b/src/pages/developer/index.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { AppTabs } from "@/components/common/AppTabs"; -import { ProfileSection } from "@/components/common/ProfileSection"; +import { ProfileSection } from "@/components/profile/ProfileSection.tsx"; import type { DeveloperTabValue } from "@/constants/profile"; import { DEVELOPER_TABS } from "@/constants/profile"; import Posts from "./_components/Posts.tsx"; diff --git a/src/pages/my/index.tsx b/src/pages/my/index.tsx index 68ac55d..458e41a 100644 --- a/src/pages/my/index.tsx +++ b/src/pages/my/index.tsx @@ -1,4 +1,4 @@ -import { ProfileSection } from "@/components/common/ProfileSection"; +import { ProfileSection } from "@/components/profile/ProfileSection"; import { ChevronRight } from "lucide-react"; import { MENU_ITEMS, type MenuItemId } from "@/constants/my";