Skip to content

refactor: apply Vercel React best practices improvements#268

Merged
whdgur5717 merged 15 commits into
devfrom
feature/refactor-improvements
Jan 21, 2026
Merged

refactor: apply Vercel React best practices improvements#268
whdgur5717 merged 15 commits into
devfrom
feature/refactor-improvements

Conversation

@whdgur5717

@whdgur5717 whdgur5717 commented Jan 17, 2026

Copy link
Copy Markdown
Contributor

개요

https://vercel.com/blog/introducing-react-best-practices 이걸 기반으로, claude opus 4.5로 코드 리팩토링 플랜을 작성하라고 해서
refactor.md 문서를 생성했고, 해당 플랜을 바탕으로 리팩토링을 진행했습니다


변경사항

1. Radix UI 패키지 통합

기존에 @radix-ui/react-radio-group, @radix-ui/react-use-controllable-state 등 개별 패키지와 통합 radix-ui 패키지가 혼용되고 있었는데, radix-ui 통합 패키지로 일원화했습니다. 아마 AI가 작성한 것 같은데 관련해서 rule에 넣어놨습니다.

2. AuthGuard 컴포넌트 도입

기존 useAuthGuard() hook 방식보다 <AuthGuard> 로 선언적으로 표현하는 것이 더 적절한 것 같아서 개선했습니다. 로직은 그대로 유지했는데, 이부분은 기능 구현에 필요한 방향으로 개선하면 될것 같습니다.

3. useDialog hook 추가

Dialog를 열기 위해 매번 코드가 너무 장황한 것 같아서, overlay-kit 기반 useDialog hook을 만들어서 open(), openAsync() 메서드로 간단하게 Dialog를 관리하는 custom hook을 만들었습니다

혹시 디자인 관련 변경사항이 생겼을 수도 있을 것 같아서 실제 로직에 적용하진 않았습니다. 확인하고 더 추상화할 부분이 있으면 추상화해서 사용해주세요.

4. Radio 컴포넌트 타입 안전성 강화

https://tkdodo.eu/blog/building-type-safe-compound-components 이 글을 보고 한번 적용해봤습니다
스토리북에 예시 남겨놨습니다. 구현 과정에서 as를 사용하는 게 더 사용처에서 타입 활용하기가 편할것 같아서 사용했습니다

5. 이미지 blur URL 상수 추출

6개 파일에서 동일한 blur placeholder URL이 하드코딩되어 있었습니다. DEFAULT_BLUR_DATA_URL 상수로 추출해서 중복을 제거했습니다.

6. 기타

  • 전체 코드 포맷팅 적용

  • Claude /commit 명령어 추가

  • commitlint에 ai 타입 추가 - 앞으로 ai rule이나 skill 같은거 업데이트 하면 이 prefix 사용하면 될것 같습니다

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • AuthGuard 컴포넌트를 통한 개선된 인증 흐름 추가
    • useDialog 훅을 활용한 모달 대화 상자 관리 기능 추가
  • 개선사항

    • 이미지 흐리게 처리(blur) 플레이스홀더 최적화
    • Radix UI 임포트 통합으로 패키지 중복 제거
    • 라디오 그룹 컴포넌트 타입 안정성 강화
  • 문서

    • 커밋 가이드라인 문서화
    • 성능 개선 계획 및 테스트 시나리오 확대

✏️ Tip: You can customize this high-level summary in your review settings.

- Replace @radix-ui/react-use-controllable-state with radix-ui/internal
- Replace @radix-ui/react-radio-group with radix-ui
- Update CLAUDE.md with Radix UI import convention
- Add AuthGuard component with fallback and redirect options
- Refactor dashboard page to use AuthGuard wrapper
- Refactor create page to use AuthGuard wrapper
- Add shared constants/images.ts with blur placeholder URL
- Replace hardcoded blurDataURL across game components
- Add RadioGroupProps<T> and RadioItemProps<T> for type-safe values
- Add createRadioGroup<T> factory for typed radio groups
- Update reportGameDialog to use createRadioGroup<ReportReasonCode>
- Add open() for fire-and-forget dialogs
- Add openAsync() for promise-based confirmation
- Add OverlayProvider decorator for overlay-kit support
- Add UseDialogOpen and UseDialogOpenAsync story examples
- Analyzed with Vercel React best practices skill
- Add /commit command with conventional commit guidelines
- Include lint-staged failure handling instructions
@coderabbitai

coderabbitai Bot commented Jan 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

인증 가드 컴포넌트 추가, useDialog 훅 도입, 라디오 그룹 제네릭/팩토리 도입, 이미지 블러 상수화, Radix 임포트 경로 통일, 문서·테스트 시나리오 및 Storybook 데코레이터 추가 등 여러 UI/인프라 관련 변경 사항이 포함됩니다.

Changes

코호트 / 파일(s) 요약
인증 가드 추가 / 페이지 마이그레이션
service/app/src/entities/auth/ui/authGuard.tsx, service/app/src/app/create/page.tsx, service/app/src/app/dashboard/page.tsx
AuthGuard 컴포넌트 추가 및 기존 useAuthGuard 훅 사용 위치를 AuthGuard 컴포넌트로 교체 (페이지 래핑 방식으로 변경)
다이얼로그 훅 및 스토리
shared/design/src/components/dialog/useDialog.tsx, shared/design/src/stories/dialog.stories.tsx, shared/design/src/components/dialog/dialog.css
overlay-kit 기반 useDialog 훅 구현 및 Storybook 예제 추가, CSS 포맷 정리
라디오 그룹 제네릭/팩토리 도입
shared/design/src/components/radio/RadioGroup.tsx, shared/design/src/components/radio/RadioItem.tsx, shared/design/src/components/radio/createRadioGroup.tsx, shared/design/src/components/radio/index.tsx, service/app/src/app/games/components/reportGameDialog.tsx
RadioGroup/RadioItem 타입을 제네릭화하고 createRadioGroup 팩토리 추가; reportGameDialog에서 새 API로 리팩토링
useControllableState 임포트 경로 통일
shared/design/src/components/checkBox/index.tsx, shared/design/src/components/input/index.tsx, shared/design/src/components/radioField/index.tsx
@radix-ui/react-use-controllable-stateradix-ui/internal로 변경
이미지 블러 상수화
service/app/src/shared/constants/images.ts, service/app/src/app/_components/GameSection/GameSectionClient.tsx, service/app/src/app/dashboard/components/dashboardGameSection.tsx, service/app/src/app/games/components/gamesLibrarySection.tsx, service/app/src/entities/game/ui/gameLibraryGrid.tsx, service/app/src/entities/game/ui/gamePreview.tsx, service/app/src/widgets/components/avatarButton.tsx
DEFAULT_BLUR_DATA_URL 상수 추가 및 여러 컴포넌트에서 하드코드된 blurDataURL을 상수로 대체
Storybook 데코레이터 및 의존성
shared/design/.storybook/preview.ts, shared/design/package.json
OverlayProvider 전역 데코레이터 추가 및 overlay-kit(피어/개발) 의존성 추가
문서·테스트 시나리오 및 리팩터 계획
.claude/commands/commit.md, CLAUDE.md, refactor.md, service/app/src/app/dashboard/__tests__/dashboard-test-scenarios.md, service/app/src/app/games/__tests__/library-e2e-scenarios.md, shared/design/src/components/question/README.md
커밋 문서, Radix 권장 임포트 주의사항, 성능 리팩터 계획, E2E/대시보드 테스트 시나리오 및 Question 컴포넌트 문서 업데이트
유틸·설정·포맷 변경
commitlint.config.js, shared/design/src/scripts/applyIconsFromExport.js, shared/eslint/next.js, shared/figma-plugin/src/cli/exportIcons.ts, shared/figma-plugin/src/main.ts, shared/figma-plugin/src/utils/sanitize.ts, service/app/src/entities/game/model/useGameShareActions.ts
commitlint 타입값 변경(ruleai), 포맷/문구 정리, sanitizeForPostMessage 로직 재정렬, 의존성/콤마 및 경미한 편집

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Component
    participant AuthGuard as AuthGuard
    participant Store as Auth Store
    participant Router as Router
    Client->>AuthGuard: 렌더(children)
    AuthGuard->>Store: read authStatus
    alt authenticated
        Store-->>AuthGuard: "authenticated"
        AuthGuard->>Client: render children
    else unauthenticated
        Store-->>AuthGuard: "unauthenticated"
        AuthGuard->>Router: push(redirectTo)
        AuthGuard->>Client: show fallback / message
    else unknown
        Store-->>AuthGuard: "unknown"
        AuthGuard->>Client: show fallback
    end
Loading
sequenceDiagram
    participant User as User
    participant Hook as useDialog
    participant Overlay as Overlay Kit
    participant Dialog as Dialog Component
    User->>Hook: dialog.open(config)
    Hook->>Overlay: overlay.open(renderDialog(config))
    Overlay->>Dialog: render with buttons
    User->>Dialog: click button
    Dialog->>Hook: invoke onClick, close with result
    Hook-->>User: return result (or Promise<boolean>)
Loading
sequenceDiagram
    participant App as App Code
    participant Factory as createRadioGroup<T>
    participant Root as RadioGroup.Root
    participant Item as RadioGroup.Item
    App->>Factory: createRadioGroup<Reason>()
    Factory-->>App: { Root, Item } (typed)
    App->>Root: <RadioGroup.Root value=...>
    App->>Item: <RadioGroup.Item value={typed}>
    Item->>Root: selection change (typed)
    Root-->>App: onValueChange(typed)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🪄refactor, ✨feature

Suggested reviewers

  • kimnamheeee

Poem

🐰
제네릭 깃털로 라디오가 춤추고,
가드 문턱엔 Auth가 살며시 서네.
블러는 상수로 모여 포근히,
다이얼로그는 오버레이 위에서 노래하네. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 요약하고 있습니다. 제목은 'refactor: apply Vercel React best practices improvements'로, 전체 PR의 핵심인 Vercel React Best Practices 개선사항 적용을 정확하게 반영하고 있습니다.
Description check ✅ Passed PR description provides comprehensive overview of changes with clear sections covering overview, main improvements, and implementation details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jan 17, 2026

Copy link
Copy Markdown

Deploy preview for re-creation ready!

✅ Preview
https://re-creation-4g8i4a4o1-whdgur5717s-projects.vercel.app

Built with commit d771d8f.
This pull request is being automatically deployed with vercel-action

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In @.claude/commands/commit.md:
- Around line 9-11: Update the fenced code blocks in .claude/commands/commit.md
to include a language specifier (e.g., bash) so markdownlint passes;
specifically add "bash" after the opening fences for the blocks containing
"/commit" and "/commit --no-verify" (the code blocks shown in the diff) so they
become fenced as ```bash ... ```; ensure all similar blocks (also the ones
around lines 15-17) are updated consistently.
- Around line 21-27: The ordered list in the commit flow starts with "2."
instead of "1.", so fix the numbering to start at "1." and ensure the subsequent
lines remain sequential (change the first line "2. Checks which files are
staged..." to "1. Checks which files are staged..." and verify the rest of the
list items maintain correct order in .claude/commands/commit.md).

In `@refactor.md`:
- Around line 15-21: Update the "Tree-shaking 효율" row in the markdown table (the
table header/row containing "Tree-shaking 효율") to remove the misleading "+100%"
numeric improvement: either replace it with a qualitative label such as "최적화 완료"
or "정성적 개선 (정확한 수치 없음)", or if you have measured bundle sizes, replace the "개선율"
cell with a concrete delta like "-X KB" / "-Y MB" and include the measurement
method; ensure the table's "개선율" column value for that row is changed
accordingly.
- Around line 172-179: The project mixes individual `@radix-ui/react-`* imports
(e.g., useControllableState, RadixRadioGroup from
"@radix-ui/react-use-controllable-state" and "@radix-ui/react-radio-group") with
the bundled "radix-ui" import (e.g., Dialog), causing duplicate bundle entries;
pick one source and make all imports consistent: either change all imports to
the scoped packages (e.g., import useControllableState from
"@radix-ui/react-use-controllable-state" and RadioGroup from
"@radix-ui/react-radio-group" and replace any "radix-ui" Dialog import with the
corresponding "@radix-ui/react-dialog" export) or migrate everything to the
single "radix-ui" package equivalents, updating all files that reference
useControllableState, RadixRadioGroup, and Dialog so the module specifier is
uniform across the codebase.

In `@shared/design/src/components/input/index.tsx`:
- Line 5: Update the import of useControllableState to use the public Radix
package: replace the current import source "radix-ui/internal" with "radix-ui"
in the module that imports useControllableState (the import statement
referencing useControllableState in
shared/design/src/components/input/index.tsx) so the code relies on the
supported public API.

In `@shared/design/src/components/question/README.md`:
- Around line 293-301: The fenced code blocks in
shared/design/src/components/question/README.md (the blocks showing the
COMPONENT_SET "question" tree and the bullet list) are missing a language
specifier and trigger markdownlint MD040; update each triple-backtick fence for
those code blocks (including the block around COMPONENT_SET "question" and the
subsequent bullet list block referenced at lines ~293 and ~331-335) to include a
language token such as text (i.e., change ``` to ```text) so the linter warning
is resolved and the blocks are explicitly annotated.

In `@shared/design/src/components/radio/createRadioGroup.tsx`:
- Around line 9-18: Update the JSDoc example to match the actual API returned by
createRadioGroup: replace ReportRadio.RadioGroup with ReportRadio.Root and
ReportRadio.RadioItem with ReportRadio.Item, keeping the generic type parameter
(createRadioGroup<"SPAM" | "ABUSE">()) and the onValueChange callback signature
intact so the example shows v as "SPAM" | "ABUSE".

In `@shared/figma-plugin/src/utils/sanitize.ts`:
- Around line 21-30: The current sanitizeForPostMessage recursion can overflow
on circular structures and loses data for special iterables; update
sanitizeForPostMessage to track visited objects using a WeakSet (or a stack) to
detect circular references and return a safe placeholder like "[Circular]" when
encountered, and add explicit handling for Array, Map, and Set (serialize Arrays
by mapping elements through sanitizeForPostMessage, serialize Maps to an array
of [key,value] pairs or plain object, and convert Sets to arrays) while
preserving the existing Date and RegExp branches; ensure the WeakSet is created
once at the top-level call and passed through recursive calls or use an inner
helper that accepts the visited set so all nested calls (including the loop in
the object branch that currently uses Object.entries) consult it.
♻️ Duplicate comments (2)
shared/design/src/components/radioField/index.tsx (1)

3-3: import 경로 변경 일관성 유지됨

input/index.tsx와 동일한 radix-ui/internal import 경로 변경입니다. 컴포넌트 전반에 걸쳐 일관성 있게 적용되었습니다. 내부 API 안정성에 대한 검증은 이전 파일의 리뷰 코멘트를 참조하세요.

shared/design/src/components/checkBox/index.tsx (1)

3-3: import 경로 변경 일관성 유지됨

디자인 시스템 컴포넌트 전반에 걸쳐 동일한 패턴으로 Radix UI 통합 패키지 import가 적용되었습니다.

🧹 Nitpick comments (8)
shared/figma-plugin/src/utils/sanitize.ts (2)

2-8: isMixed 헬퍼를 함수 외부로 추출하는 것을 고려해 주세요.

현재 헬퍼 함수가 sanitizeForPostMessage 호출마다 새로 생성됩니다. 재귀 호출이 많은 깊은 객체의 경우 불필요한 오버헤드가 발생할 수 있습니다.

♻️ 제안된 수정
+const isMixed = (v: unknown) => {
+  try {
+    return typeof figma !== "undefined" && v === figma.mixed
+  } catch {
+    return false
+  }
+}
+
 export function sanitizeForPostMessage(value: unknown): unknown {
-  const isMixed = (v: unknown) => {
-    try {
-      return typeof figma !== "undefined" && v === figma.mixed
-    } catch {
-      return false
-    }
-  }

32-32: 함수 값 처리 누락.

typeof value === "function"인 경우 현재 그대로 반환되지만, postMessage는 함수를 직렬화할 수 없어 DataCloneError가 발생합니다. 명시적으로 처리하는 것을 권장합니다.

♻️ 함수 처리 추가
+  if (typeof value === "function") return "[Function]"
+
   return value
service/app/src/app/games/__tests__/library-e2e-scenarios.md (1)

27-28: 문서 형식에 관한 선택적 제안

Markdownlint가 테스트 케이스 이름에 강조(bold) 대신 제목(heading)을 사용하라고 경고합니다 (MD036). 현재 형식도 가독성이 좋지만, 린터 경고를 해결하려면 ##### TC-301-01: 홈 버튼 클릭 형식으로 변경할 수 있습니다.

다만, 이는 선택 사항이며 현재 형식도 테스트 문서로서 충분히 명확합니다.

shared/design/package.json (1)

23-25: overlay-kit 버전 불일치 확인

peerDependencies에서 ^1.0.0을, devDependencies에서 ^1.8.6을 요구하고 있습니다. 일반적으로 peerDependency 범위는 실제 테스트된 버전을 반영해야 합니다. ^1.8.0 이상으로 peerDependency를 업데이트하는 것이 적절할 수 있습니다.

🔧 버전 범위 조정 제안
  "peerDependencies": {
-    "overlay-kit": "^1.0.0"
+    "overlay-kit": "^1.8.0"
  },

Also applies to: 44-44

service/app/src/entities/game/ui/gameLibraryGrid.tsx (1)

17-32: 인터페이스에 사용되지 않는 props가 있습니다.

isDashboard, onCreateGame, onEditGame, onShareGame, onDeleteGame, onCopyLinkGame props가 인터페이스에 정의되어 있지만 컴포넌트에서 사용되지 않습니다. 의도적으로 남겨둔 것이 아니라면 정리를 고려해 주세요.

.claude/commands/commit.md (1)

70-77: 커밋 메시지 예시 코드 블록에 언어 지정자를 추가해 주세요.

일관성을 위해 언어 지정자를 추가하세요 (예: text 또는 생략).

service/app/src/entities/auth/ui/authGuard.tsx (1)

33-35: alert() 대신 토스트 알림을 고려해 보세요.

브라우저 alert()는 블로킹 동작으로 UX가 좋지 않습니다. 향후 개선 시 토스트 알림 라이브러리 사용을 고려해 주세요.

shared/design/src/components/radio/RadioGroup.tsx (1)

8-23: 제네릭 함수 시그니처가 필요합니다.

RadioGroupProps<T>에서 제네릭을 정의했으나 컴포넌트 함수가 비제네릭이므로 타입 매개변수를 전달할 수 없습니다. TypeScript에서 제네릭 JSX 컴포넌트는 함수 선언부에 제네릭을 포함해야 타입 추론과 명시가 가능합니다.

♻️ 제안 수정
-export function RadioGroup({ className, ...props }: RadioGroupProps) {
+export function RadioGroup<T extends string = string>({
+  className,
+  ...props
+}: RadioGroupProps<T>) {
   return (
     <RadixRadioGroup.Root
       className={cn("flex flex-col gap-24", className)}
       {...props}
     />
   )
 }

Comment on lines +9 to +11
```
/commit
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

코드 블록에 언어 지정자가 누락되었습니다.

markdownlint에서 지적한 대로 펜스드 코드 블록에 언어를 지정해 주세요 (예: bash 또는 shell).

📝 수정 제안
-```
+```bash
 /commit

```diff
-```
+```bash
 /commit --no-verify
</details>


Also applies to: 15-17

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @.claude/commands/commit.md around lines 9 - 11, Update the fenced code
blocks in .claude/commands/commit.md to include a language specifier (e.g.,
bash) so markdownlint passes; specifically add "bash" after the opening fences
for the blocks containing "/commit" and "/commit --no-verify" (the code blocks
shown in the diff) so they become fenced as bash ... ; ensure all similar
blocks (also the ones around lines 15-17) are updated consistently.


</details>

<!-- fingerprinting:phantom:poseidon:ocelot -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread .claude/commands/commit.md Outdated
Comment thread refactor.md
Comment on lines +15 to +21
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
| **Store 조회 복잡도** | O(n) | O(1) | -99% |
| **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 |
| **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 |
| **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

개선율 수치 표현을 명확히 해주세요.

Line 17의 "Tree-shaking 효율" 개선율 "+100%"는 오해의 소지가 있습니다. Tree-shaking 효율은 정량적으로 측정 가능한 퍼센트 지표가 아닙니다.

"번들 크기 감소" 또는 "최적화 완료" 같은 정성적 표현이나, 실제 번들 크기를 측정한 후 구체적인 KB/MB 감소량을 표시하는 것이 더 정확합니다.

제안하는 수정안
 | 지표 | 현재 | 개선 후 | 개선율 |
 |------|------|---------|--------|
-| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | 최적화 완료 |

또는 실제 번들 크기를 측정한 경우:

-| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **번들 크기 (해당 파일들)** | 측정 필요 | 측정 필요 | -X KB |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
| **Store 조회 복잡도** | O(n) | O(1) | -99% |
| **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 |
| **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 |
| **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 |
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | 최적화 완료 |
| **Store 조회 복잡도** | O(n) | O(1) | -99% |
| **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 |
| **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 |
| **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 |
🤖 Prompt for AI Agents
In `@refactor.md` around lines 15 - 21, Update the "Tree-shaking 효율" row in the
markdown table (the table header/row containing "Tree-shaking 효율") to remove the
misleading "+100%" numeric improvement: either replace it with a qualitative
label such as "최적화 완료" or "정성적 개선 (정확한 수치 없음)", or if you have measured bundle
sizes, replace the "개선율" cell with a concrete delta like "-X KB" / "-Y MB" and
include the measurement method; ensure the table's "개선율" column value for that
row is changed accordingly.

Comment thread refactor.md
Comment on lines +172 to +179
```tsx
// ❌ 개별 @radix-ui/* 패키지와 통합 radix-ui 패키지 혼용
import { useControllableState } from "@radix-ui/react-use-controllable-state"
import * as RadixRadioGroup from "@radix-ui/react-radio-group"

// 다른 파일에서는
import { Dialog } from "radix-ui"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

official unified Radix UI package name npm "radix-ui"

💡 Result:

Yes — the official unified package is published as radix-ui. Install with: npm install radix-ui. [1][2]

  • Individual packages still exist under the @radix-ui scope (e.g. @radix-ui/react-dialog). [1]

Sources:

  1. Radix Primitives docs (installation shows npm install radix-ui). [radix-ui.com/docs]. [1]
  2. npm package page for radix-ui. [npmjs.com/package/radix-ui]. [2]

Radix UI 패키지 혼용으로 인한 번들 중복 제거 필요

172-179번 줄에서 개별 @radix-ui/react-* 패키지와 통합 radix-ui 패키지를 혼용하고 있습니다. 이는 동일한 컴포넌트가 서로 다른 패키지 소스에서 중복으로 번들에 포함되어 번들 크기 증가를 초래합니다. 한 가지 패키지 소스로 통일하세요.

🤖 Prompt for AI Agents
In `@refactor.md` around lines 172 - 179, The project mixes individual
`@radix-ui/react-`* imports (e.g., useControllableState, RadixRadioGroup from
"@radix-ui/react-use-controllable-state" and "@radix-ui/react-radio-group") with
the bundled "radix-ui" import (e.g., Dialog), causing duplicate bundle entries;
pick one source and make all imports consistent: either change all imports to
the scoped packages (e.g., import useControllableState from
"@radix-ui/react-use-controllable-state" and RadioGroup from
"@radix-ui/react-radio-group" and replace any "radix-ui" Dialog import with the
corresponding "@radix-ui/react-dialog" export) or migrate everything to the
single "radix-ui" package equivalents, updating all files that reference
useControllableState, RadixRadioGroup, and Dialog so the module specifier is
uniform across the codebase.

import { useControllableState } from "@radix-ui/react-use-controllable-state"
import { cva } from "class-variance-authority"
import { Form } from "radix-ui"
import { useControllableState } from "radix-ui/internal"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

radix-ui unified package useControllableState import path v1.4.2

💡 Result:

You can import useControllableState either from the unified package or the standalone package:

  • From the unified radix-ui package:
    import { useControllableState } from "radix-ui"; [1]

  • Or from the standalone package:
    import { useControllableState } from "@radix-ui/react-use-controllable-state"; [2]

Sources: radix-ui dist/internal export (unified package) and Radix docs / npm listing. [1][2]


🌐 Web query:

radix-ui/internal exposed API documentation

💡 Result:

Short answer: Radix does not publish a stable "radix-ui/internal" public API — their internal helpers/packages are intentionally undocumented for public consumption. If you need to inspect them, look at the monorepo source (internal/ packages and the @radix-ui/* packages) on GitHub or the npm packages (they are labeled “internal utility, not intended for public usage”). See examples: Radix site overview, the @radix-ui/primitive/readme, and related GitHub discussion. [1][2][3]

Sources

  • Radix homepage / Primitives docs. [1]
  • @radix-ui/primitive (npm) — README: “This is an internal utility, not intended for public usage.” [2]
  • rad ix-ui/primitives GitHub (issues and source showing internal-only patterns). [3]

If you want, I can:

  • link to the specific internal folder/files in the GitHub repo and list notable internal exports, or
  • search a specific internal package (e.g., @radix-ui/primitive or @radix-ui/react-primitive) and extract its TypeScript types for you. Which would you prefer?

🌐 Web query:

radix-ui react-use-controllable-state unified package migration

💡 Result:

Short guide — migrating from @radix-ui/react-use-controllable-state to the unified radix-ui package

  1. Install the mono package:

    • npm install radix-ui (or pnpm/yarn). [1][2]
  2. Update imports

    • Replace any imports from the scoped package with named imports from "radix-ui".
      Example:
      • before: import { useControllableState } from "@radix-ui/react-use-controllable-state";
      • after: import { useControllableState } from "radix-ui";
    • Likewise change other @radix-ui/react-* imports (e.g. @radix-ui/react-dialog → import { Dialog } from "radix-ui"). [1][2][3]
  3. Automate the refactor (optional)

    • Use a codemod/sed/ts-morph script or tools like shadcn's migrate command which can rewrite @radix-ui/react-* imports to radix-ui across a project. Test after running. [4]
  4. Test & tidy

    • Run your test suite and dev build. Check for type/peer-dep issues; if you hit dependency conflicts (rare but possible), consider pinning offending packages or using package-manager overrides. [2][4]

Notes

  • The unified entrypoint is tree-shakeable (only used exports are bundled). [1]
  • If you used internal/unofficial Radix internals, verify exported APIs match; run a quick search for any non-public imports. [3]

Sources: Radix docs and releases (radix-ui package + release notes) and community migration notes. [1][2][4]

References
[1] Radix Primitives docs — installing/importing radix-ui.
[2] Radix releases — announcement of radix-ui mono package (Jan 22, 2025).
[3] npm: @radix-ui/react-use-controllable-state package page.
[4] shadcn/ui changelog & migration command (example automating import changes).


radix-ui/internal은 지원되지 않는 내부 API 경로입니다. "radix-ui"에서 직접 가져와야 합니다.

Radix UI는 radix-ui/internal 경로를 공식 공개 API로 발행하지 않습니다. 내부 유틸리티는 명시적으로 공개 용도가 아닙니다. 통합 패키지의 공식 권장 방식은 다음과 같습니다:

import { useControllableState } from "radix-ui";

현재 코드를 수정하여 "radix-ui/internal" 대신 "radix-ui"에서 가져오세요.

🤖 Prompt for AI Agents
In `@shared/design/src/components/input/index.tsx` at line 5, Update the import of
useControllableState to use the public Radix package: replace the current import
source "radix-ui/internal" with "radix-ui" in the module that imports
useControllableState (the import statement referencing useControllableState in
shared/design/src/components/input/index.tsx) so the code relies on the
supported public API.

Comment on lines 293 to 301
```
COMPONENT_SET "question"
├── COMPONENT "state=selected, image=true" (350×118px)
├── COMPONENT "state=selected, image=false" (350×118px)
├── COMPONENT "state=selected, image=false" (350×118px)
├── COMPONENT "state=error, image=true" (350×118px)
├── COMPONENT "state=error, image=false" (350×118px)
├── COMPONENT "state=default, image=true" (350×118px)
└── COMPONENT "state=default, image=false" (350×118px)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

펜스 코드 블록에 언어 지정이 누락되었습니다.
markdownlint(MD040) 경고가 발생하니 text 등으로 명시해 주세요.

📝 제안 수정
-```
+```text
 COMPONENT_SET "question"
 ├── COMPONENT "state=selected, image=true"   (350×118px)
 ├── COMPONENT "state=selected, image=false"  (350×118px)
 ├── COMPONENT "state=error, image=true"      (350×118px)
 ├── COMPONENT "state=error, image=false"     (350×118px)
 ├── COMPONENT "state=default, image=true"    (350×118px)
 └── COMPONENT "state=default, image=false"   (350×118px)

```diff
-```
+```text
 - 6개의 고정 배리언트
 - state와 image 조합으로만 사용 가능
 - 내부 구조 변경 불가
</details>


Also applies to: 331-335

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

293-293: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @shared/design/src/components/question/README.md around lines 293 - 301, The
fenced code blocks in shared/design/src/components/question/README.md (the
blocks showing the COMPONENT_SET "question" tree and the bullet list) are
missing a language specifier and trigger markdownlint MD040; update each
triple-backtick fence for those code blocks (including the block around
COMPONENT_SET "question" and the subsequent bullet list block referenced at
lines ~293 and ~331-335) to include a language token such as text (i.e., change
totext) so the linter warning is resolved and the blocks are explicitly
annotated.


</details>

<!-- fingerprinting:phantom:poseidon:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread shared/design/src/components/radio/createRadioGroup.tsx
Comment on lines +21 to +30
if (typeof value === "object") {
if (value instanceof Date) return value.toISOString()
if (value instanceof RegExp) return value.toString()

const out: Record<string, unknown> = {}
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = sanitizeForPostMessage(v)
}

if (typeof value === "object") {
if (value instanceof Date) return value.toISOString()
if (value instanceof RegExp) return value.toString()

const out: Record<string, unknown> = {}
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = sanitizeForPostMessage(v)
}
return out
}

return value
} No newline at end of file
return out
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

순환 참조 시 스택 오버플로우 위험이 있습니다.

재귀 호출에서 순환 참조를 감지하지 않아 const obj = {}; obj.self = obj;와 같은 경우 무한 재귀로 인해 스택 오버플로우가 발생할 수 있습니다. 또한 Map, Set 등의 특수 객체는 Object.entries()로 빈 배열을 반환하여 데이터 손실이 발생합니다.

🔒️ 순환 참조 감지 추가 제안
-export function sanitizeForPostMessage(value: unknown): unknown {
+export function sanitizeForPostMessage(
+  value: unknown,
+  seen: WeakSet<object> = new WeakSet()
+): unknown {
   // ... existing early returns ...

   if (typeof value === "object") {
+    if (seen.has(value)) return "[Circular]"
+    seen.add(value)
+
     if (value instanceof Date) return value.toISOString()
     if (value instanceof RegExp) return value.toString()
+    if (value instanceof Map) return sanitizeForPostMessage(Object.fromEntries(value), seen)
+    if (value instanceof Set) return sanitizeForPostMessage([...value], seen)

     const out: Record<string, unknown> = {}
     for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
-      out[k] = sanitizeForPostMessage(v)
+      out[k] = sanitizeForPostMessage(v, seen)
     }
     return out
   }
🤖 Prompt for AI Agents
In `@shared/figma-plugin/src/utils/sanitize.ts` around lines 21 - 30, The current
sanitizeForPostMessage recursion can overflow on circular structures and loses
data for special iterables; update sanitizeForPostMessage to track visited
objects using a WeakSet (or a stack) to detect circular references and return a
safe placeholder like "[Circular]" when encountered, and add explicit handling
for Array, Map, and Set (serialize Arrays by mapping elements through
sanitizeForPostMessage, serialize Maps to an array of [key,value] pairs or plain
object, and convert Sets to arrays) while preserving the existing Date and
RegExp branches; ensure the WeakSet is created once at the top-level call and
passed through recursive calls or use an inner helper that accepts the visited
set so all nested calls (including the loop in the object branch that currently
uses Object.entries) consult it.

@whdgur5717 whdgur5717 self-assigned this Jan 17, 2026
@whdgur5717 whdgur5717 added 📃docs 문서 추가 또는 변경 🧹cleanup 기능 변경 없이 코드만 깔끔하게 수정 labels Jan 17, 2026
@whdgur5717 whdgur5717 merged commit 5243fe8 into dev Jan 21, 2026
2 checks passed
@whdgur5717 whdgur5717 deleted the feature/refactor-improvements branch January 21, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🧹cleanup 기능 변경 없이 코드만 깔끔하게 수정 📃docs 문서 추가 또는 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants