diff --git a/src/app/group/[groupId]/pending/page.tsx b/src/app/group/[groupId]/pending/page.tsx
new file mode 100644
index 00000000..7c8d7ea1
--- /dev/null
+++ b/src/app/group/[groupId]/pending/page.tsx
@@ -0,0 +1,27 @@
+'use client';
+
+import { use } from 'react';
+
+import {
+ GroupPendingHeader,
+ GroupPendingMembers,
+ GroupPendingSummary,
+} from '@/components/pages/group';
+
+interface Props {
+ params: Promise<{ groupId: string }>;
+}
+
+const GroupPendingMembersPage = ({ params }: Props) => {
+ const { groupId } = use(params);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default GroupPendingMembersPage;
diff --git a/src/components/pages/group/group-pending-header/index.tsx b/src/components/pages/group/group-pending-header/index.tsx
new file mode 100644
index 00000000..a7d595fc
--- /dev/null
+++ b/src/components/pages/group/group-pending-header/index.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import { useRouter } from 'next/navigation';
+
+import { Icon } from '@/components/icon';
+
+export const GroupPendingHeader = () => {
+ const router = useRouter();
+
+ const handleBackClick = () => {
+ router.back();
+ };
+
+ return (
+
+
+
참여 신청
+
+ );
+};
diff --git a/src/components/pages/group/group-pending-members/index.tsx b/src/components/pages/group/group-pending-members/index.tsx
new file mode 100644
index 00000000..e04a9c4d
--- /dev/null
+++ b/src/components/pages/group/group-pending-members/index.tsx
@@ -0,0 +1,83 @@
+'use client';
+
+import { useCallback } from 'react';
+
+import { EmptyState } from '@/components/layout/empty-state';
+import { GetPendingMembersResponse } from '@/types/service/group';
+
+import { PendingMemberCard } from './pending-member-card';
+
+interface Props {
+ groupId: string;
+ pendingMembers?: GetPendingMembersResponse['pendingMembers'];
+}
+
+const MOCK_PENDING_MEMBERS: GetPendingMembersResponse['pendingMembers'] = [
+ {
+ userId: 101,
+ groupUserId: 1,
+ nickName: 'Hope Lee',
+ profileImage: null,
+ profileMessage: 'Here We Go Again :)',
+ requestMessage: '안녕하세요 함께하고 싶습니다!',
+ requestedAt: '2025-12-24T13:20:10.123456',
+ },
+ {
+ userId: 102,
+ groupUserId: 2,
+ nickName: '바다소년',
+ profileImage: null,
+ profileMessage: '물고기를 좋아합니다.',
+ requestMessage: '보드게임 정말 좋아해서 신청합니다! 앞으로 잘 부탁드려요',
+ requestedAt: '2025-12-24T13:25:15.789012',
+ },
+ {
+ userId: 103,
+ groupUserId: 3,
+ nickName: '박디자인',
+ profileImage: null,
+ profileMessage: 'UI/UX 디자이너',
+ requestMessage: null,
+ requestedAt: '2025-12-24T13:30:20.456789',
+ },
+];
+
+export const GroupPendingMembers = ({ groupId, pendingMembers = MOCK_PENDING_MEMBERS }: Props) => {
+ const handleReject = useCallback(
+ (memberId: number) => {
+ console.log('거절:', groupId, memberId);
+ },
+ [groupId],
+ );
+
+ const handleApprove = useCallback(
+ (memberId: number) => {
+ console.log('승인:', groupId, memberId);
+ },
+ [groupId],
+ );
+
+ if (pendingMembers?.length === 0) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+ {pendingMembers.map((member) => (
+ -
+ handleApprove(member.groupUserId)}
+ onReject={() => handleReject(member.groupUserId)}
+ />
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/pages/group/group-pending-members/pending-member-card/index.tsx b/src/components/pages/group/group-pending-members/pending-member-card/index.tsx
new file mode 100644
index 00000000..dd2b5d7d
--- /dev/null
+++ b/src/components/pages/group/group-pending-members/pending-member-card/index.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import Link from 'next/link';
+
+import { Button, ImageWithFallback } from '@/components/ui';
+import { GetPendingMembersResponse } from '@/types/service/group';
+
+type PendingMember = GetPendingMembersResponse['pendingMembers'][number];
+
+interface Props {
+ member: PendingMember;
+ onReject: () => void;
+ onApprove: () => void;
+}
+
+export const PendingMemberCard = ({ member, onReject, onApprove }: Props) => {
+ const profileUrl = `/profile/${member.userId}`;
+
+ return (
+
+
+
+
+
+
{member.nickName}
+ {member.profileMessage && (
+
{member.profileMessage}
+ )}
+
+
+
+ {member.requestMessage && (
+
+ {member.requestMessage}
+
+ )}
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/pages/group/group-pending-summary/index.tsx b/src/components/pages/group/group-pending-summary/index.tsx
new file mode 100644
index 00000000..b39e0c1f
--- /dev/null
+++ b/src/components/pages/group/group-pending-summary/index.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import { DEFAULT_GROUP_IMAGE } from 'constants/default-images';
+
+import { ImageWithFallback } from '@/components/ui';
+
+interface Props {
+ thumbnail?: string | null;
+ title?: string;
+ pendingCount?: number;
+}
+
+const MOCK_DATA = {
+ thumbnail: null,
+ title: '매우 긴 제목을 가진 모임입니다 이 제목은 너무 길어서 잘려야 합니다',
+ pendingCount: 5,
+};
+
+export const GroupPendingSummary = ({
+ thumbnail = MOCK_DATA.thumbnail,
+ title = MOCK_DATA.title,
+ pendingCount = MOCK_DATA.pendingCount,
+}: Props) => {
+ return (
+
+
+
+
+
+
+
{title}
+
+
+ 신청한 유저
+ {pendingCount}
+ 명
+
+
+
+ );
+};
diff --git a/src/components/pages/group/index.ts b/src/components/pages/group/index.ts
index 94287ece..e498876c 100644
--- a/src/components/pages/group/index.ts
+++ b/src/components/pages/group/index.ts
@@ -2,3 +2,6 @@ export { GroupBannerImages } from './group-banner-images';
export { GroupButtons } from './group-buttons';
export { GroupDescriptions } from './group-descriptions';
export { GroupMembers } from './group-members';
+export { GroupPendingHeader } from './group-pending-header';
+export { GroupPendingMembers } from './group-pending-members';
+export { GroupPendingSummary } from './group-pending-summary';
diff --git a/src/mock/service/group/group-mock.ts b/src/mock/service/group/group-mock.ts
deleted file mode 100644
index baf9db90..00000000
--- a/src/mock/service/group/group-mock.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Group } from '@/types/service/group';
-
-export const groupMockItem: Group[] = [
- {
- id: 1,
- title: '동탄 호수공원에서 피크닉하실 분!',
- location: '화성시',
- locationDetail: '동탄 호수공원',
- startTime: '2025-12-07T17:00:00+09:00',
- endTime: '2025-12-07T19:00:00+09:00',
- images: [
- 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
- ],
- tags: ['게임', '피크닉'],
- description: '동탄 호수공원에서 어쩌구 저쩌구',
- participantCount: 3,
- maxParticipants: 12,
- createdBy: {
- userId: 1,
- nickName: '리오넬 메시',
- 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',
- },
- createdAt: '2025-12-06T17:00:00+09:00',
- updatedAt: '2025-12-06T17:00:00+09:00',
- joinedCount: 3,
- },
-];
diff --git a/src/types/service/group.ts b/src/types/service/group.ts
index 66e06114..5b91251e 100644
--- a/src/types/service/group.ts
+++ b/src/types/service/group.ts
@@ -84,35 +84,6 @@ export interface GetMyGroupsResponse {
nextCursor: number | null;
}
-/**
-
- * 밑에 타입들은 다른 분들이 아직 다른 파일에서 사용 중이므로 그냥 제거해버리시면 안됩니다(다른 분들도 다 수정 후에 제거 예정)
- * 아직 사용 중인 파일:
- * - src/types/service/notification.ts (Notification.group)
- * - src/mock/service/group/group-mock.ts (groupMockItem)
- */
-export interface Group {
- id: number;
- title: string;
- location: string;
- locationDetail: string;
- startTime: string;
- endTime: string;
- images: string[];
- tags: string[];
- description: string;
- participantCount: number;
- maxParticipants: number;
- createdBy: {
- userId: number;
- nickName: string;
- profileImage: null | string;
- };
- createdAt: string;
- updatedAt: string;
- joinedCount: number;
-}
-
export interface PreUploadGroupImagePayload {
images: File[];
}
@@ -185,6 +156,7 @@ export interface CreateGroupResponse {
export interface GetGroupDetailsResponse {
id: number;
title: string;
+ joinPolicy: GroupV2JoinPolicy;
status: GroupV2Status;
address: {
location: string;
@@ -233,3 +205,19 @@ export interface GetGroupDetailsResponse {
export interface GroupIdParams {
groupId: string;
}
+
+// 승인 대기자 목록 조회 응답 (GET /api/v2/groups/{groupId}/attendance/pending)
+export interface GetPendingMembersResponse {
+ groupId: number;
+ joinPolicy: GroupV2JoinPolicy;
+ pendingMembers: {
+ userId: number;
+ groupUserId: number;
+ nickName: string;
+ profileImage: string | null;
+ profileMessage: string | null;
+ requestMessage: string | null;
+ requestedAt: string;
+ }[];
+ serverTime: string;
+}