);
};
diff --git a/src/app/(user)/profile/[userId]/page.tsx b/src/app/(user)/profile/[userId]/page.tsx
index 7ae84f47..2184aeaa 100644
--- a/src/app/(user)/profile/[userId]/page.tsx
+++ b/src/app/(user)/profile/[userId]/page.tsx
@@ -2,7 +2,8 @@
import { use } from 'react';
-import { useFollowUser, useGetUser, useUnfollowUser } from '@/hooks/use-user';
+import { ProfileInfo } from '@/components/pages/profile';
+import { useGetUser } from '@/hooks/use-user';
interface Props {
params: Promise<{ userId: string }>;
@@ -12,27 +13,13 @@ const ProfilePage = ({ params }: Props) => {
const { userId: id } = use(params);
const userId = Number(id);
- const { data } = useGetUser({ userId });
- const { mutate: followUser } = useFollowUser({ followeeId: userId });
+ const { data: user } = useGetUser({ userId });
- const { mutate: unfollowUser } = useUnfollowUser({ followeeId: userId });
-
- const handleFollowClick = () => {
- followUser();
- };
-
- const handleUnfollowClick = () => {
- unfollowUser();
- };
+ if (!user) return null;
return (
-
-
{data?.id}
-
{data?.nickName}
-
{data?.mbti}
-
-
-
+
);
};
diff --git a/src/components/pages/profile/index.ts b/src/components/pages/profile/index.ts
new file mode 100644
index 00000000..fea446ef
--- /dev/null
+++ b/src/components/pages/profile/index.ts
@@ -0,0 +1,6 @@
+export { ProfileCard } from './profile-card';
+export { ProfileDescription } from './profile-description';
+export { ProfileDescriptionBadge } from './profile-description-badge';
+export { ProfileFollowsBadge } from './profile-follows-badge';
+export { ProfileInfo } from './profile-info';
+export { ProfileSetting } from './profile-setting';
diff --git a/src/components/pages/profile/profile-card/index.tsx b/src/components/pages/profile/profile-card/index.tsx
new file mode 100644
index 00000000..55a2c669
--- /dev/null
+++ b/src/components/pages/profile/profile-card/index.tsx
@@ -0,0 +1,20 @@
+import Image from 'next/image';
+
+import { User } from '@/types/service/user';
+
+interface Props {
+ user: User;
+}
+
+export const ProfileCard = ({ user }: Props) => {
+ const { profileImage, nickName, profileMessage } = user;
+ return (
+
+
+
+
+
{nickName}
+
{profileMessage}
+
+ );
+};
diff --git a/src/components/pages/profile/profile-description-badge/index.tsx b/src/components/pages/profile/profile-description-badge/index.tsx
new file mode 100644
index 00000000..acad823c
--- /dev/null
+++ b/src/components/pages/profile/profile-description-badge/index.tsx
@@ -0,0 +1,25 @@
+import { Icon, IconId } from '@/components/icon';
+
+export interface ProfileDescriptionBadgeProps {
+ label: string;
+ iconId: IconId;
+ value: string;
+}
+
+interface Props {
+ badgeItems: ProfileDescriptionBadgeProps;
+}
+
+export const ProfileDescriptionBadge = ({ badgeItems }: Props) => {
+ return (
+
+
+
+
+
+ {badgeItems.label}
+ {badgeItems.value}
+
+
+ );
+};
diff --git a/src/components/pages/profile/profile-description/index.tsx b/src/components/pages/profile/profile-description/index.tsx
new file mode 100644
index 00000000..ce88a0d3
--- /dev/null
+++ b/src/components/pages/profile/profile-description/index.tsx
@@ -0,0 +1,51 @@
+import { User } from '@/types/service/user';
+
+import {
+ ProfileDescriptionBadge,
+ ProfileDescriptionBadgeProps,
+} from '../profile-description-badge';
+
+const formatISO = (dateString: string) => {
+ const date = new Date(dateString);
+ const y = date.getFullYear();
+ const m = String(date.getMonth() + 1).padStart(2, '0');
+ const d = String(date.getDate()).padStart(2, '0');
+ return `${y}. ${m}. ${d}`;
+};
+
+interface Props {
+ user: User;
+}
+
+export const ProfileDescription = ({ user }: Props) => {
+ const listMap: ProfileDescriptionBadgeProps[] = [
+ {
+ label: 'MBTI',
+ iconId: 'symbol',
+ value: user.mbti,
+ },
+ {
+ label: '가입 일자',
+ iconId: 'calendar-2',
+ value: formatISO(user.createdAt),
+ },
+ {
+ label: '모임 참여',
+ iconId: 'users-2',
+ value: `${user.joinedCount}회`,
+ },
+ {
+ label: '모임 생성',
+ iconId: 'map-pin-2',
+ value: `${user.createdCount}회`,
+ },
+ ];
+
+ return (
+
+ {listMap.map((item) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/pages/profile/profile-follows-badge/index.tsx b/src/components/pages/profile/profile-follows-badge/index.tsx
new file mode 100644
index 00000000..17e9d8f7
--- /dev/null
+++ b/src/components/pages/profile/profile-follows-badge/index.tsx
@@ -0,0 +1,36 @@
+import { Fragment } from 'react/jsx-runtime';
+
+import { User } from '@/types/service/user';
+
+interface Props {
+ user: User;
+}
+
+export const ProfileFollowsBadge = ({ user }: Props) => {
+ const listMap = [
+ {
+ label: '팔로워',
+ value: user.followersCount,
+ },
+ {
+ label: '팔로잉',
+ value: user.followeesCount,
+ },
+ ];
+
+ const listLength = listMap.length;
+
+ return (
+
+ {listMap.map((item, index) => (
+
+
+ {item.value.toLocaleString()}
+ {item.label.toLocaleString()}
+
+ {index < listLength - 1 && }
+
+ ))}
+
+ );
+};
diff --git a/src/components/pages/profile/profile-info/index.tsx b/src/components/pages/profile/profile-info/index.tsx
new file mode 100644
index 00000000..1596779a
--- /dev/null
+++ b/src/components/pages/profile/profile-info/index.tsx
@@ -0,0 +1,21 @@
+import { Button } from '@/components/ui';
+import { User } from '@/types/service/user';
+
+import { ProfileCard } from '../profile-card';
+import { ProfileDescription } from '../profile-description';
+import { ProfileFollowsBadge } from '../profile-follows-badge';
+
+interface Props {
+ user: User;
+}
+
+export const ProfileInfo = ({ user }: Props) => {
+ return (
+
+ );
+};
diff --git a/src/components/pages/profile/profile-setting/index.tsx b/src/components/pages/profile/profile-setting/index.tsx
new file mode 100644
index 00000000..a647b0b4
--- /dev/null
+++ b/src/components/pages/profile/profile-setting/index.tsx
@@ -0,0 +1,26 @@
+'use client';
+import { useState } from 'react';
+
+import { User } from '@/types/service/user';
+
+import { ProfileActionButton, ProfileToggleButton } from './profile-setting-button';
+
+interface Props {
+ user: User;
+}
+
+export const ProfileSetting = ({ user }: Props) => {
+ console.log(user);
+ // useState 로직은 추후 삭제 예정
+ const [isOn, setIsOn] = useState(false);
+
+ return (
+
+ setIsOn((prev) => !prev)}>
+ 알림 받기
+
+ console.log('로그아웃')}>로그아웃
+ console.log('회원탈퇴')}>회원탈퇴
+
+ );
+};
diff --git a/src/components/pages/profile/profile-setting/profile-setting-button/index.tsx b/src/components/pages/profile/profile-setting/profile-setting-button/index.tsx
new file mode 100644
index 00000000..b323fbe3
--- /dev/null
+++ b/src/components/pages/profile/profile-setting/profile-setting-button/index.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import React, { ButtonHTMLAttributes } from 'react';
+
+import * as m from 'motion/react-m';
+
+interface ToggleButtonProps extends Omit
{
+ value?: boolean;
+}
+export const ProfileToggleButton = ({ children, value = false, ...props }: ToggleButtonProps) => {
+ return (
+
+ );
+};
+
+type ActionButtonProps = ButtonProps;
+
+export const ProfileActionButton = ({ children, ...props }: ActionButtonProps) => {
+ return ;
+};
+
+interface ButtonProps extends ButtonHTMLAttributes {
+ children: React.ReactNode;
+}
+
+const Button = ({ children, ...props }: ButtonProps) => {
+ return (
+
+ );
+};
+
+interface ToggleUIProps {
+ value?: boolean;
+}
+
+const ToggleUI = ({ value = false }: ToggleUIProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/mock/service/user/users-mock.ts b/src/mock/service/user/users-mock.ts
index 57e745c3..8f416c5e 100644
--- a/src/mock/service/user/users-mock.ts
+++ b/src/mock/service/user/users-mock.ts
@@ -5,30 +5,48 @@ export const mockUserItems: User[] = [
id: 1,
email: 'test@example.com',
nickName: '리오넬 메시',
- profileImage: 'https://cdn.myapp.com/user/profile/123.png',
- notification_enabled: '1',
+ profileImage:
+ 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
+ notification_enabled: true,
mbti: 'ISTJ',
phoneNumber: '010-1234-5678',
profileMessage: 'Zzz...',
+ followeesCount: 102356,
+ followersCount: 104,
+ createdAt: '2025-12-07T17:00:00+09:00',
+ joinedCount: 5,
+ createdCount: 3,
},
{
id: 2,
email: 'test@example.com',
nickName: '크리스티아누 호날두',
- profileImage: 'https://cdn.myapp.com/user/profile/123.png',
- notification_enabled: '2',
+ profileImage:
+ 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
+ notification_enabled: false,
mbti: 'ENFP',
phoneNumber: '010-1234-5678',
profileMessage: '안녕하세요',
+ followeesCount: 7056512,
+ followersCount: 134,
+ createdAt: '2025-08-03T17:00:00+09:00',
+ joinedCount: 5,
+ createdCount: 3,
},
{
id: 3,
email: 'test@example.com',
nickName: '페르난도 토레스',
- profileImage: 'https://cdn.myapp.com/user/profile/123.png',
- notification_enabled: '3',
+ profileImage:
+ 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
+ notification_enabled: true,
mbti: 'ESFJ',
phoneNumber: '010-1234-5678',
profileMessage: '반갑습니다',
+ followeesCount: 15,
+ followersCount: 12,
+ createdAt: '2025-11-03T17:00:00+09:00',
+ joinedCount: 2,
+ createdCount: 1,
},
];
diff --git a/src/styles/layout.css b/src/styles/layout.css
index 264dd69a..b0560128 100644
--- a/src/styles/layout.css
+++ b/src/styles/layout.css
@@ -25,3 +25,7 @@
@utility flex-col-between {
@apply flex flex-col items-center justify-between;
}
+
+@utility shadow-card {
+ box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.08);
+}
diff --git a/src/types/service/user.ts b/src/types/service/user.ts
index ad4230d3..2fe7616a 100644
--- a/src/types/service/user.ts
+++ b/src/types/service/user.ts
@@ -3,10 +3,16 @@ export interface User {
email: string;
nickName: string;
profileImage: string;
- notification_enabled: string;
+ notification_enabled: boolean;
mbti: string;
- phoneNumber: string;
+ phoneNumber: string; // 삭제
profileMessage: string;
+ // 아래는 추가되어야 할 것들
+ followeesCount: number;
+ followersCount: number;
+ createdAt: string;
+ joinedCount: number;
+ createdCount: number;
}
export interface GetUserPayload {