Skip to content

Commit ae89e02

Browse files
authored
[Feat] 그룹 리스트 컴포넌트 UI 구현 (#138)
2 parents 447b36f + a740aae commit ae89e02

14 files changed

Lines changed: 347 additions & 7 deletions

File tree

.github/workflows/chromatic.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Chromatic
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
pull_request:
7+
branches: ['**']
8+
9+
jobs:
10+
chromatic:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Setup pnpm
19+
uses: pnpm/action-setup@v4
20+
with:
21+
version: 10.28.2
22+
run_install: false
23+
24+
- name: Setup Node
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: 22.20.0
28+
cache: pnpm
29+
30+
- name: Install
31+
run: pnpm install --frozen-lockfile
32+
33+
- name: Publish to Chromatic
34+
uses: chromaui/action@latest
35+
with:
36+
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

CLAUDE.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
@AGENTS.md
6+
57
---
68

79
## Commands
810

911
```bash
10-
npm run dev # 개발 서버
11-
npm run build # 프로덕션 빌드
12-
npm run lint # ESLint 검사
13-
npm run typecheck # TypeScript 타입 검사 (tsc --noEmit)
14-
npm run storybook # Storybook 실행 (port 6006)
12+
pnpm dev # 개발 서버
13+
pnpm build # 프로덕션 빌드
14+
pnpm lint # ESLint 검사
15+
pnpm typecheck # TypeScript 타입 검사 (tsc --noEmit)
16+
pnpm storybook # Storybook 실행 (port 6006)
1517
```
1618

17-
테스트는 Storybook Vitest 애드온으로 실행하며, 별도의 `npm test` 명령은 없다.
19+
테스트는 Storybook Vitest 애드온으로 실행하며, 별도의 `pnpm test` 명령은 없다.
1820

1921
---
2022

@@ -50,13 +52,15 @@ src/
5052
- `/ztpi-test`, `/ztpi/[ztpiTestId]` → ZTPI 성격 테스트
5153
- `/calendar` → 캘린더
5254
- `/reflection`, `/reflection/[reflectionId]` → 회고
55+
- `/groups` → 그룹 회고 (그룹 목록, 향후 `/groups/[groupId]` 상세 확장 가능)
5356
- `/characters` → 캐릭터 선택
5457
- `/profile`, `/profile/nickname` → 프로필
5558
- `/api/proxy/[...path]` → 개발용 API 프록시
5659

5760
### 상태 관리
5861

5962
- **서버 상태**: TanStack React Query v5 — staleTime 기본 1분, DevTools는 개발 모드에서만 활성화
63+
- **전역 상태**: Zustand v5
6064
- **로컬 상태**: React 훅 (`useState`, `useRef`)
6165
- 쿼리 훅은 `src/components/features/[feature]/queries/` 에 위치
6266

app/groups/page.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// TODO: 친구 리스트 컴포넌트와 통합 필요
2+
3+
'use client';
4+
5+
import { useState } from 'react';
6+
7+
import GroupList from '@/src/components/features/groups/GroupList/GroupList';
8+
import GroupTab, {
9+
type TabType,
10+
} from '@/src/components/features/groups/GroupTab/GroupTab';
11+
12+
// TODO: 실제 API 연동 필요
13+
const MOCK_FRIENDS_GROUPS = [
14+
{ id: 1, name: '친구 모임', type: 'friend' as const, image: '' },
15+
{ id: 2, name: '어떤 모임', type: 'friend' as const, image: '' },
16+
{ id: 3, name: '친구 어떤 모임', type: 'friend' as const, image: '' },
17+
];
18+
19+
const MOCK_CHARACTERS_GROUPS = [
20+
{ id: 1, name: '캐릭터 모임', type: 'character' as const, image: '' },
21+
{ id: 2, name: '어떤 모임', type: 'character' as const, image: '' },
22+
{ id: 3, name: '캐릭터 어떤 모임', type: 'character' as const, image: '' },
23+
];
24+
25+
const GroupsPage = () => {
26+
const [activeTab, setActiveTab] = useState<TabType>('friend');
27+
const [selectedId, setSelectedId] = useState(MOCK_FRIENDS_GROUPS[0]?.id ?? 0);
28+
29+
return (
30+
<div>
31+
<GroupTab activeTab={activeTab} onTabChange={setActiveTab} />
32+
<GroupList
33+
groups={
34+
activeTab === 'friend' ? MOCK_FRIENDS_GROUPS : MOCK_CHARACTERS_GROUPS
35+
}
36+
selectedId={selectedId}
37+
onSelect={setSelectedId}
38+
/>
39+
</div>
40+
);
41+
};
42+
43+
export default GroupsPage;

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"typecheck": "tsc --noEmit",
1212
"prepare": "husky",
1313
"storybook": "storybook dev -p 6006",
14-
"build-storybook": "storybook build"
14+
"build-storybook": "storybook build",
15+
"chromatic": "npx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN"
1516
},
1617
"lint-staged": {
1718
"*.{js,ts,tsx,jsx}": [
@@ -56,6 +57,7 @@
5657
"@types/react-dom": "^19",
5758
"@vitest/browser-playwright": "4.0.18",
5859
"babel-plugin-react-compiler": "1.0.0",
60+
"chromatic": "^16.10.0",
5961
"eslint": "^9",
6062
"eslint-config-next": "16.1.4",
6163
"eslint-config-prettier": "^10.1.8",

pnpm-lock.yaml

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/images/default-group.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2+
import { useState } from 'react';
3+
4+
import GroupList from './GroupList';
5+
6+
const meta = {
7+
title: 'Features/Groups/GroupList',
8+
component: GroupList,
9+
parameters: {
10+
layout: 'fullscreen',
11+
viewport: {
12+
defaultViewport: 'mobile1',
13+
},
14+
},
15+
tags: ['autodocs'],
16+
} satisfies Meta<typeof GroupList>;
17+
18+
export default meta;
19+
20+
type Story = StoryObj<typeof meta>;
21+
22+
const groups = [
23+
{ id: 1, name: '친구 많은 모임', type: 'friend' as const, image: '' },
24+
{ id: 2, name: '어떤 모임', type: 'friend' as const, image: '' },
25+
{ id: 3, name: '친구 모임', type: 'friend' as const, image: '' },
26+
{ id: 4, name: '어떤 모임', type: 'friend' as const, image: '' },
27+
{ id: 5, name: '친구 모임', type: 'friend' as const, image: '' },
28+
{ id: 6, name: '어떤 모임', type: 'friend' as const, image: '' },
29+
{ id: 7, name: '친구 모임', type: 'friend' as const, image: '' },
30+
{ id: 8, name: '어떤 모임', type: 'friend' as const, image: '' },
31+
{ id: 9, name: '친구 모임', type: 'friend' as const, image: '' },
32+
{ id: 10, name: '어떤 모임', type: 'friend' as const, image: '' },
33+
];
34+
35+
export const Default: Story = {
36+
render: () => {
37+
const [selectedId, setSelectedId] = useState(1);
38+
return (
39+
<GroupList
40+
groups={groups}
41+
selectedId={selectedId}
42+
onSelect={setSelectedId}
43+
/>
44+
);
45+
},
46+
args: { groups },
47+
};

0 commit comments

Comments
 (0)