Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
10838dd
랜딩 페이지 리팩토링 (#359)
sumi-0011 Mar 25, 2026
0491668
refactor: RankingSection 상수 통합 및 prefetch 최적화 (#358)
sumi-0011 Mar 25, 2026
e68b29d
mypage 페르소나 관련 코드 리팩토링 (#360)
sumi-0011 Mar 26, 2026
1bda96c
펫 리스트 필터/정렬 툴바 및 Dialog 레이아웃 개선 (#361)
sumi-0011 Mar 26, 2026
993696e
Dialog 고정 헤더/스크롤 본문 분리 및 SelectPersonaList Compound Component 전환 (#362)
sumi-0011 Mar 26, 2026
c8a5e08
GuildPeopleList Flicking → Embla Carousel 마이그레이션 (#363)
sumi-0011 Mar 29, 2026
c7ceca5
merge conflic (#365)
sumi-0011 Mar 29, 2026
ce457d7
Merge branch 'main' into dev
sumi-0011 Mar 29, 2026
518ce60
feat: 퀴즈 관리 페이지 모바일 최적화 및 UI 개선 (#369)
sumi-0011 Mar 31, 2026
0a673dc
feat: Spring 2026 랜딩 페이지 업데이트 (#370)
sumi-0011 Apr 1, 2026
47fc23a
chore: git ignore 추가
sumi-0011 Apr 1, 2026
2078c42
Merge remote-tracking branch 'origin/main' into dev
sumi-0011 Apr 1, 2026
f0fc5c4
refactor: PandaCSS에서 Tailwind CSS로 스타일링 시스템 마이그레이션 (#356)
sumi-0011 Apr 1, 2026
2ca72da
feat: Tailwind UI 패키지 Storybook 환경 구축 (#357)
sumi-0011 Apr 1, 2026
b3bf9c4
feat: dev → main Release PR 자동 생성 워크플로우 추가 (#371)
sumi-0011 Apr 1, 2026
81f642f
feat: Tailwind 마이그레이션 및 FSD 아키텍처 리팩토링 (#373)
sumi-0011 Apr 2, 2026
348df15
UI 컴포넌트 개선: DialogV2 Size 체계 재설계 및 Tailwind 마이그레이션 (#374)
sumi-0011 Apr 2, 2026
d376b94
refactor: Dialog 리팩토링 + 캐러셀 InventoryGrid 안정성 개선 (#375)
sumi-0011 Apr 2, 2026
3d4079a
refactor: Dialog v1 → DialogV2 마이그레이션 및 ui-tailwind 패키지 전환 (#376)
sumi-0011 Apr 3, 2026
5f732be
refactor: InventoryGrid 전면 적용 및 레이아웃 높이 제한 (#377)
sumi-0011 Apr 3, 2026
e4377f8
refactor: @egjs/react-flicking → Embla Carousel 마이그레이션 (#378)
sumi-0011 Apr 3, 2026
a99dc3a
refactor: @egjs/react-flicking → Embla Carousel 마이그레이션 (#380)
sumi-0011 Apr 3, 2026
f2d68fb
refactor: PersonaList InventoryGrid 캐러셀 및 DialogV2 마이그레이션 (#379)
sumi-0011 Apr 3, 2026
2ca6221
refactor: components/ → FSD 레이어 구조 마이그레이션 (#381)
sumi-0011 Apr 3, 2026
748bb05
refactor(webview): PandaCSS 제거 및 @gitanimals/ui-tailwind 전환 (#382)
sumi-0011 Apr 6, 2026
46ea3ab
chore: legacy Remix admin 및 @gitanimals/ui-panda 패키지 제거 (#383)
sumi-0011 Apr 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
108 changes: 108 additions & 0 deletions .cursor/rules/web-fsd-architecture.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
description: apps/web 프로젝트 코드 작성 시 FSD-inspired 하이브리드 아키텍처 규칙을 적용합니다.
globs:
- apps/web/src/**/*.ts
- apps/web/src/**/*.tsx
alwaysApply: false
---

# apps/web FSD 아키텍처 규칙

## 레이어 구조와 의존성

apps/web은 FSD-inspired 하이브리드 아키텍처를 사용합니다.

```
src/
├── app/ # 라우팅 + 조합 (Next.js App Router, [locale]/_components 셸)
├── widgets/ # 독립적 대규모 UI 블록
├── features/ # 사용자 액션 기능 (필요 시 actions/에 Server Actions)
├── entities/ # 비즈니스 엔티티
├── shared/ # 범용 기반 코드
└── middleware.ts
```

의존성 규칙: `app/ → widgets/ → features/ → entities/ → shared/`

## 핵심 규칙

1. **상위 레이어는 하위 레이어만 import 가능**
- features/는 entities/와 shared/만 import
- entities/는 shared/만 import
- shared/는 외부 패키지만 import

2. **같은 레이어 슬라이스 간 import 금지**
- entities/persona/에서 entities/guild/ import 불가
- features/auction/에서 features/feedback/ import 불가

3. **`src/components/` 레거시 디렉터리는 제거됨** — 새 코드는 `shared/ui/`, `features/*/ui/`, `app/.../_components/` 등 적절한 위치에만 둡니다.

4. **슬라이스 public API(`index.ts`)는 최소로**
- 슬라이스 **밖**에서 쓰는 심볼만 barrel에서 export합니다.
- 내부 전용은 상대 경로 또는 `model/`, `actions/` 등 세그먼트 경로로 직접 import합니다.
- Server Action은 `features/{slice}/actions/*.ts`에 두고, 루트 barrel에 올리지 않아도 됩니다.

5. **entities 등 슬라이스는 필요 시 barrel 제공** (위 원칙 준수)
```typescript
// entities/persona/index.ts — 외부에 노출할 것만
export * from './ui';
export * from './model';
```

## 새 코드 배치 기준

| 코드 유형 | 배치 위치 | 예시 |
|---|---|---|
| 라우트 전용 UI | `app/[locale]/*/_components/` | 페이지별 폼, 섹션 |
| 범용 UI (도메인 무관) | `shared/ui/` | Modal, Button |
| 엔티티 UI | `entities/*/ui/` | PersonaItem, GuildCard |
| 엔티티 쿼리/로직 | `entities/*/model/` | useGetAllPersona |
| 사용자 액션 | `features/*/model/` | useRegisterProduct |
| Server Actions | `features/*/actions/` (`'use server'`) | joinGuildAction |
| API 호출 함수 | `features/*/api/` 또는 `shared/api/` | github.ts |
| 독립 대규모 블록 | `widgets/*/` | GNB |
| 유틸리티 | `shared/utils/` | image.ts, string.ts |
| 상수/설정 | `shared/config/` | env.ts, route.ts |
| hooks (범용) | `shared/hooks/` | usePagination |

## 슬라이스 내부 구조

```
features/{name}/
├── actions/ # (선택) Server Actions — `'use server'`
├── api/ # API 호출 함수
├── model/ # hooks, 상태, 비즈니스 로직
├── ui/ # UI 컴포넌트
└── index.ts # (선택) 외부에 필요한 것만 barrel export
```

## import 스타일

```typescript
// 외부 패키지
import { useQuery } from '@tanstack/react-query';
import { getAllPersona } from '@gitanimals/api';

// FSD 레이어 (절대경로 @/)
import GNB from '@/widgets/gnb/GNB';
import { AnimalCard } from '@/entities/persona';
import { getPersonaImage } from '@/shared/utils/image';

// 같은 슬라이스 내 (상대경로)
import { useGetAllPersona } from './useGetAllPersona';
```

## `@gitanimals/*` 패키지

모노레포 패키지는 외부 shared 계층으로 취급합니다.
entities, features에서 직접 import 허용:

```typescript
import { getAllPersona } from '@gitanimals/api'; // OK
import { cn } from '@gitanimals/ui-tailwind'; // OK
import { userQueries } from '@gitanimals/react-query'; // OK
```

## 상세 가이드

전체 아키텍처 가이드는 `apps/web/ARCHITECTURE.md`를 참조하세요.
44 changes: 44 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Chromatic - UI Storybook

on:
push:
branches:
- dev
paths:
- 'packages/ui/tailwind/**'

env:
DEFAULT_NODE_VERSION: '20.11.0'
PNPM_VERSION: '9.0.6'

jobs:
chromatic:
name: Publish Storybook
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ env.DEFAULT_NODE_VERSION }}
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Run Chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: packages/ui/tailwind
exitOnceUploaded: true
129 changes: 129 additions & 0 deletions .github/workflows/release-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Release PR

on:
push:
branches: [dev]

permissions:
contents: read
pull-requests: write

jobs:
release-pr:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check for changes
id: check
run: |
COMMIT_COUNT=$(git rev-list --count origin/main..origin/dev 2>/dev/null || echo "0")
echo "commit_count=$COMMIT_COUNT" >> "$GITHUB_OUTPUT"
if [ "$COMMIT_COUNT" -eq 0 ]; then
echo "No commits between main and dev. Skipping."
fi

- name: Parse commits and generate body
if: steps.check.outputs.commit_count != '0'
id: generate
run: |
COMMITS=$(git log origin/main..origin/dev --no-merges --format="%s")

FEAT=""
FIX=""
REFACTOR=""
STYLE=""
DOCS=""
CHORE=""

while IFS= read -r line; do
[ -z "$line" ] && continue

# prefix 제거하여 메시지 추출
msg=$(echo "$line" | sed 's/^[a-z]*\(([^)]*)\)\?[!]\?: *//')

case "$line" in
feat:*|feat\(*) FEAT="${FEAT}- ${msg}"$'\n' ;;
fix:*|fix\(*) FIX="${FIX}- ${msg}"$'\n' ;;
refactor:*|refactor\(*) REFACTOR="${REFACTOR}- ${msg}"$'\n' ;;
style:*|style\(*) STYLE="${STYLE}- ${msg}"$'\n' ;;
docs:*|docs\(*) DOCS="${DOCS}- ${msg}"$'\n' ;;
chore:*|chore\(*) CHORE="${CHORE}- ${msg}"$'\n' ;;
*) CHORE="${CHORE}- ${line}"$'\n' ;;
esac
done <<< "$COMMITS"

# 버전 계산: 최근 RELEASE 커밋에서 현재 버전 추출
CURRENT_VERSION=$(git log --all --oneline --grep="\[RELEASE\]" --format="%s" | head -1 | sed 's/.*v\([0-9]*\.[0-9]*\.[0-9]*\).*/\1/')
if [ -z "$CURRENT_VERSION" ]; then
CURRENT_VERSION="0.0.0"
fi

IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"

if [ -n "$FEAT" ]; then
MINOR=$((MINOR + 1))
PATCH=0
else
PATCH=$((PATCH + 1))
fi

NEXT_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"

# PR 본문 생성
BODY="## [RELEASE] $NEXT_VERSION"
NEWLINE=$'\n'

if [ -n "$FEAT" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 기능${NEWLINE}${FEAT}"
fi

if [ -n "$FIX" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 버그 수정${NEWLINE}${FIX}"
fi

if [ -n "$REFACTOR" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 리팩토링${NEWLINE}${REFACTOR}"
fi

if [ -n "$STYLE" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 스타일${NEWLINE}${STYLE}"
fi

if [ -n "$DOCS" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 문서${NEWLINE}${DOCS}"
fi

if [ -n "$CHORE" ]; then
BODY="${BODY}${NEWLINE}${NEWLINE}### 기타${NEWLINE}${CHORE}"
fi

# multiline output
{
echo "body<<BODY_EOF"
echo "$BODY"
echo "BODY_EOF"
} >> "$GITHUB_OUTPUT"

- name: Create or update PR
if: steps.check.outputs.commit_count != '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
NEXT_VERSION="${{ steps.generate.outputs.next_version }}"
TITLE="[RELEASE] $NEXT_VERSION"

# 기존 열린 PR 확인
EXISTING_PR=$(gh pr list --base main --head dev --state open --json number --jq '.[0].number // empty')

if [ -n "$EXISTING_PR" ]; then
echo "Updating existing PR #$EXISTING_PR"
gh pr edit "$EXISTING_PR" --title "$TITLE" --body "${{ steps.generate.outputs.body }}"
else
echo "Creating new PR"
gh pr create --base main --head dev --title "$TITLE" --body "${{ steps.generate.outputs.body }}"
fi
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,11 @@ apps/web/public/sw.js
# Local Claude Code instructions
apps/*/CLAUDE.local.md
CLAUDE.local.md
apps/admin-main/.env.local
apps/admin-main/docs/superpowers
.omc

docs/superpowers
.superpowers
.worktrees

Loading
Loading