refactor: apply Vercel React best practices improvements#268
Conversation
- 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
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Walkthrough인증 가드 컴포넌트 추가, useDialog 훅 도입, 라디오 그룹 제네릭/팩토리 도입, 이미지 블러 상수화, Radix 임포트 경로 통일, 문서·테스트 시나리오 및 Storybook 데코레이터 추가 등 여러 UI/인프라 관련 변경 사항이 포함됩니다. Changes
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
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>)
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)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ 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. Comment |
|
Deploy preview for re-creation ready! ✅ Preview Built with commit d771d8f. |
There was a problem hiding this comment.
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/internalimport 경로 변경입니다. 컴포넌트 전반에 걸쳐 일관성 있게 적용되었습니다. 내부 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 valueservice/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,onCopyLinkGameprops가 인터페이스에 정의되어 있지만 컴포넌트에서 사용되지 않습니다. 의도적으로 남겨둔 것이 아니라면 정리를 고려해 주세요..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} /> ) }
| ``` | ||
| /commit | ||
| ``` |
There was a problem hiding this comment.
코드 블록에 언어 지정자가 누락되었습니다.
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 -->
| | 지표 | 현재 | 개선 후 | 개선율 | | ||
| |------|------|---------|--------| | ||
| | **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% | | ||
| | **Store 조회 복잡도** | O(n) | O(1) | -99% | | ||
| | **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 | | ||
| | **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 | | ||
| | **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 | |
There was a problem hiding this comment.
개선율 수치 표현을 명확히 해주세요.
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.
| | 지표 | 현재 | 개선 후 | 개선율 | | |
| |------|------|---------|--------| | |
| | **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.
| ```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" | ||
| ``` |
There was a problem hiding this comment.
🧩 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-uiscope (e.g.@radix-ui/react-dialog). [1]
Sources:
- Radix Primitives docs (installation shows npm install radix-ui). [radix-ui.com/docs]. [1]
- 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" |
There was a problem hiding this comment.
🧩 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/primitiveor@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
-
Install the mono package:
- npm install radix-ui (or pnpm/yarn). [1][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]
- Replace any imports from the scoped package with named imports from "radix-ui".
-
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]
- Use a codemod/sed/ts-morph script or tools like shadcn's migrate command which can rewrite
-
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.
| ``` | ||
| 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) | ||
| ``` |
There was a problem hiding this comment.
펜스 코드 블록에 언어 지정이 누락되었습니다.
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 -->
| 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 | ||
| } |
There was a problem hiding this comment.
순환 참조 시 스택 오버플로우 위험이 있습니다.
재귀 호출에서 순환 참조를 감지하지 않아 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.
개요
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 기반
useDialoghook을 만들어서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
릴리스 노트
새로운 기능
개선사항
문서
✏️ Tip: You can customize this high-level summary in your review settings.