# SEED React 3 URL: /react/updates/upgrade/v3 Source: https://github.com/daangn/seed-design/blob/dev/docs/content/react/updates/upgrade/v3.mdx SEED React 3으로 업그레이드할 때 필요한 작업을 안내합니다. SEED React 3으로 업그레이드하는 방법을 안내합니다. 신규 기능 등 전체 변경사항은 [Changelog](/react/updates/changelog)에서 확인할 수 있습니다. 이 가이드는 SEED React 2(`2.x`)에서 SEED React 3으로 업그레이드하는 경우를 기준으로 작성되었습니다. 2보다 낮은 버전을 사용 중이라면 [SEED React 2](/react/updates/upgrade/v2) 가이드를 따른 뒤 이 문서로 돌아오세요. ## AI로 마이그레이션하기 AI 코딩 도구(예: Claude Code)에 아래 프롬프트를 붙여넣으면 이 가이드를 읽고 프로젝트를 점검한 뒤 코드 수정이 필요한 항목을 적용합니다. 코드 블록 오른쪽 위 버튼으로 복사할 수 있습니다. - 화면이 달라지는 디자인 변경 항목은 AI가 직접 결정하지 않고 확인이 필요한 목록으로 정리하도록 요청합니다. - 코드 수정이 필요한 항목은 Step 단위로 진행하며, 같은 Step 안의 항목은 subagent로 병렬 처리하도록 요청합니다. ```text title="마이그레이션 프롬프트" 이 프로젝트의 SEED React를 2.x에서 3으로 업그레이드해줘. 기준 문서: https://seed-design.io/llms/react/updates/upgrade/v3.txt 세부 변경 사항은 이 문서에서 확인하고, 문서와 이 지시가 다르면 문서를 따라줘. 1. 현재 상태 확인 - package.json에서 @seed-design/* 패키지와 버전을 정리해줘. - seed-design.json의 path 아래 설치된 snippet 목록을 정리해줘. - 작업 전에 커밋되지 않은 변경이 있으면 알려주고 멈춰줘. 2. 패키지 업그레이드 - 문서의 "패키지 업그레이드" 표에서 이 프로젝트가 쓰는 패키지만 최소 버전 이상으로 한 번에 올려줘. 쓰지 않는 패키지는 새로 설치하지 마. 단, "코드 수정이 필요한 항목"이 새 패키지나 snippet 설치를 안내하면 그 안내를 따라줘. - 프로젝트의 패키지 매니저를 그대로 사용해줘. 3. 확인 목록 작성 (코드는 수정하지 마) - 문서의 "디자인 변경사항이 있는 항목"과 "동작이 바뀌는 항목"에 해당하는 사용처를 찾아 파일 위치와 확인할 내용을 적어줘. - 이 목록은 4번과 5번의 코드 수정을 시작하기 전에 만들어줘. 4. Snippet 업데이트 - 문서의 "Snippet 업데이트"에 따라 설치된 snippet을 다시 설치해줘. - snippet을 설치하거나 다시 설치할 때는 `npx @seed-design/cli@latest add --on-diff backup ui:`를 사용해줘. 이 옵션은 기존 파일을 legacy-* 파일로 옮기고 새 snippet을 쓰므로 백업만으로는 커스터마이즈가 유지되지 않아. 백업 파일과 새 파일을 비교해 필요한 사용자 정의 스타일·동작을 새 snippet에 다시 적용해줘. 5. 코드 수정 - 문서의 "코드 수정이 필요한 항목"을 Step 순서대로 진행하고, 각 항목의 "수정 대상"에 해당하는 사용처를 문서의 변경 후 코드로 바꿔줘. - 각 항목의 "수정 대상"에 해당하는 코드가 없으면 그 항목은 건너뛰어도 돼. 건너뛴 항목과 확인한 검색어는 결과 보고에 적어줘. - 같은 Step 안의 항목은 서로 의존하지 않으니 subagent로 병렬 실행해줘. subagent에는 해당 항목 섹션 전체(수정 대상, 변경 내용, 작업 전·후 확인)를 그대로 전달해줘. 다음 Step은 이전 Step의 항목이 모두 끝난 뒤 시작해줘. - 여러 항목이 같은 파일을 고쳐야 하면 그 파일은 한 subagent가 해당 항목을 모두 적용하게 해줘. - snippet 설치와 패키지 설치는 subagent에 맡기지 말고 한 번에 하나씩 직접 실행해줘. CLI가 의존성을 설치하면서 package.json과 lockfile을 수정해. - import뿐 아니라 하드코딩한 CSS 클래스와 CSS 변수도 문자열로 검색해줘. 6. 검증 - 타입 검사와 빌드를 실행하고, 실패는 문서의 해당 항목 안내에 따라 해결해줘. prop이나 타입 선언을 지워서 에러를 없애지 마. - 각 항목의 "작업 후 확인" 목록이 있으면 모두 확인해줘. - `npx @seed-design/cli@latest compat`으로 snippet이 요구하는 패키지 버전을 확인해줘. compat은 snippet 파일이 최신인지는 확인하지 않으니, "Snippet 업데이트"의 snippet을 모두 다시 설치했는지는 따로 확인해줘. 7. 결과 보고 - 바꾼 파일과 항목, 건너뛴 항목, snippet에 다시 적용한 사용자 정의와 적용하지 않은 것, 실행한 검증 결과, 3번의 확인 필요 목록, 해결하지 못한 문제를 정리해줘. ``` *** ## 패키지 업그레이드 프로젝트에서 사용하는 `@seed-design/*` 패키지를 아래 버전 이상으로 업그레이드하세요. | 패키지 | 최소 버전 | 비고 | | ----------------------------------- | ------- | -------------------------------------------------------------------------------- | | `@seed-design/css` | `3.0.0` | | | `@seed-design/react` | `3.0.0` | | | `@seed-design/stackflow` | `2.0.6` | 기능 변경 없이 `@seed-design/css` peer dependency 범위에 `^3.0.0`을 추가했습니다. | | `@seed-design/tailwind3-plugin` | `3.0.1` | npm에서 게시 취소된 `3.0.0`을 다시 쓸 수 없어 `3.0.1`로 배포합니다. | | `@seed-design/tailwind4-theme` | `3.0.1` | npm에서 게시 취소된 `3.0.0`을 다시 쓸 수 없어 `3.0.1`로 배포합니다. | | `@seed-design/vite-plugin` | `2.1.1` | 기능 변경 없이 `@seed-design/css` peer dependency 범위에 `^3.0.0`을 추가했습니다. | | `@seed-design/webpack-plugin` | `2.1.1` | 기능 변경 없이 `@seed-design/css` peer dependency 범위에 `^3.0.0`을 추가했습니다. | | `@seed-design/rsbuild-plugin` | `2.1.1` | 기능 변경 없이 `@seed-design/css` peer dependency 범위에 `^3.0.0`을 추가했습니다. | | `@seed-design/react-popover` | `3.0.0` | headless 패키지(스타일 없이 상태와 동작만 제공하는 패키지)를 직접 설치해 사용하는 경우에만 해당합니다. | | `@seed-design/react-drawer` | `3.0.0` | headless 패키지를 직접 설치해 사용하는 경우에만 해당합니다. | | `@seed-design/react-scale-feedback` | `2.0.0` | 직접 설치해 사용하는 경우에만 해당합니다. `@seed-design/css` peer dependency 범위를 `^3.0.0`으로 바꿨습니다. | - 현재 프로젝트에서 사용하는 패키지만 업그레이드하세요. 사용하지 않는 패키지를 새로 설치할 필요는 없습니다. - 위 표에서 프로젝트가 사용하는 패키지는 아래 명령에 함께 적어 한 번에 업그레이드하세요(예: `@seed-design/stackflow@latest @seed-design/vite-plugin@latest`). 표의 최소 버전보다 낮은 버전이 남아 있으면 `@seed-design/css` 3과 peer dependency 범위가 맞지 않아 설치가 실패할 수 있습니다. * npm: npm install @seed-design/react@latest @seed-design/css@latest * pnpm: pnpm add @seed-design/react@latest @seed-design/css@latest * yarn: yarn add @seed-design/react@latest @seed-design/css@latest * bun: bun add @seed-design/react@latest @seed-design/css@latest ## Snippet 업데이트 **수정 대상:** 아래 목록의 snippet을 설치한 프로젝트입니다. 해당 snippet이 없다면 이 단계를 건너뛰세요. snippet은 프로젝트에 복사된 파일이므로 패키지를 업그레이드해도 자동으로 바뀌지 않습니다. 아래 snippet은 SEED React 2에서 설치한 파일을 그대로 두면 SEED React 3에서 타입 검사나 빌드가 실패하거나 다른 컴포넌트로 렌더링되므로, 설치한 항목을 모두 다시 설치하세요. 최신 snippet에는 새 import 경로와 API가 반영되어 있습니다. - `ui:alert-dialog`, `ui:dialog`: `@seed-design/react`의 Alert Dialog·Dialog 이름 변경 - `ui:chip`: `@seed-design/react/primitive` 제거, `Chip.Root`를 `Chip.Button`으로 변경 - `ui:list`, `ui:side-navigation`, `ui:attachment-field`, `ui:attachment-field-reorderable`, `ui:attachment-display-field`, `ui:attachment-display-field-reorderable`: `@seed-design/react/primitive` 제거 - `block:side-navigation-02`: `@seed-design/react/primitive` 제거, Badge의 snippet 전환. 다시 설치하면 `ui:badge`도 함께 설치됩니다. `ui:pagination`, `ui:table-pagination`은 다시 설치하지 않아도 SEED React 3에서 동작합니다. 최신 snippet은 `usePagination`, `useTablePagination`을 `@seed-design/react-pagination` 대신 `@seed-design/react`에서 가져옵니다. 다시 설치한 뒤 다른 코드에서 `@seed-design/react-pagination`을 쓰지 않는다면 이 의존성을 제거할 수 있습니다. 아래 명령에서 프로젝트에 설치하지 않은 항목은 빼고 실행하세요. - npm: npx @seed-design/cli@latest add ui:alert-dialog ui:attachment-display-field ui:attachment-display-field-reorderable ui:attachment-field ui:attachment-field-reorderable ui:chip ui:dialog ui:list ui:pagination ui:side-navigation ui:table-pagination block:side-navigation-02 --on-diff backup - pnpm: pnpm dlx @seed-design/cli@latest add ui:alert-dialog ui:attachment-display-field ui:attachment-display-field-reorderable ui:attachment-field ui:attachment-field-reorderable ui:chip ui:dialog ui:list ui:pagination ui:side-navigation ui:table-pagination block:side-navigation-02 --on-diff backup - yarn: yarn dlx @seed-design/cli@latest add ui:alert-dialog ui:attachment-display-field ui:attachment-display-field-reorderable ui:attachment-field ui:attachment-field-reorderable ui:chip ui:dialog ui:list ui:pagination ui:side-navigation ui:table-pagination block:side-navigation-02 --on-diff backup - bun: bun x @seed-design/cli@latest add ui:alert-dialog ui:attachment-display-field ui:attachment-display-field-reorderable ui:attachment-field ui:attachment-field-reorderable ui:chip ui:dialog ui:list ui:pagination ui:side-navigation ui:table-pagination block:side-navigation-02 --on-diff backup `--on-diff`로 기존 파일과 내용이 다를 때의 처리 방식을 고를 수 있습니다. - `--on-diff backup`: 기존 파일을 같은 폴더에 `legacy-<파일명>-<타임스탬프>.tsx`로 백업한 뒤 새 파일을 씁니다. 프로젝트에 맞게 수정했던 내용은 백업 파일과 비교해 새 파일에 다시 적용하세요. 백업 파일에는 이전 import가 남아 있으므로, 변경을 옮긴 뒤 삭제하세요. - `--on-diff overwrite`: 백업 없이 기존 파일을 덮어씁니다. - `--on-diff` 생략: 파일마다 차이를 확인하고 백업·덮어쓰기·건너뛰기 중 하나를 고릅니다. 명령은 목록의 snippet이 사용하는 `ui:action-button`, `ui:loading-indicator`, `ui:progress-circle`, `ui:select`, `ui:help-bubble-tooltip`, `ui:navigation-menu`, `ui:avatar`, `ui:badge`, `ui:identity-placeholder`, `ui:menu`, `lib:format-bytes`, `lib:pagination-button`, `lib:pagination-page-item`도 함께 설치합니다. `ui:badge`는 새로 추가된 snippet이고 나머지는 SEED React 3에서 바뀌지 않았으므로, 이 파일들의 `legacy-*` 백업에서 보이는 차이는 프로젝트에서 수정한 부분이거나, 프로젝트의 snippet이 SEED React 2의 최신 snippet보다 오래된 버전이라서 생긴 차이입니다. `npx @seed-design/cli@latest compat`은 snippet이 요구하는 패키지 버전만 확인하고, 프로젝트의 snippet 파일이 최신인지는 확인하지 않습니다. 위 snippet을 모두 다시 설치했는지는 따로 확인하세요. **Chip의 `ChipBaseProps`를 import했다면 사용처도 수정하세요.** - 최신 `ui:chip`에서는 `ChipBaseProps`를 제거했습니다. 이 타입은 snippet 안에서 공통 스타일 props를 선택 상태 props와 합치는 데 썼으며, 이제 각 컴포넌트의 타입이 필요한 props를 모두 포함합니다. - 직접 작성한 코드에서 `ChipBaseProps`를 가져왔다면, 같은 snippet의 `ButtonChipProps`, `ToggleChipProps`, `RadioChipItemProps` 중 사용하는 컴포넌트에 맞는 타입으로 바꾸세요. **`ui:alert-dialog`가 있다면 반드시 다시 설치하세요.** 이름 변경의 전체 내용은 [Alert Dialog와 Dialog 이름 변경](#alert-dialog와-dialog-이름-변경)에 있습니다. - `ui:alert-dialog`: SEED React 2의 snippet은 `@seed-design/react`의 `Dialog`를 import하는데, 이 이름은 SEED React 3에서 Alert Dialog가 아닌 Dialog를 가리킵니다. - 다시 설치하지 않으면 타입 검사에서 `Property 'role' does not exist on type 'DialogRootProps'` 에러가 납니다. - 타입 검사 없이 빌드하면 snippet의 Alert Dialog가 Dialog로 렌더링됩니다. - 이 에러를 snippet의 `role` 선언이나 prop을 지워서 해결하지 마세요. - `ui:dialog`: 다시 설치하지 않으면 `Module '"@seed-design/react"' has no exported member 'ContentDialog'` 타입 에러가 납니다. `--on-diff backup`으로 두 snippet을 다시 설치하면 다음 백업 파일이 생깁니다. - `legacy-alert-dialog-<타임스탬프>.tsx`, `legacy-dialog-<타임스탬프>.tsx`: 두 snippet은 SEED React 3에서 내용이 바뀌었으므로 항상 생깁니다. - `legacy-action-button-<타임스탬프>.tsx`: 두 snippet이 import하는 `ui:action-button`도 함께 설치되며, 프로젝트의 `ui/action-button.tsx`가 최신 snippet과 다를 때만 생깁니다. 백업 파일과 새 파일을 비교할 때 다음 차이는 SEED React 3의 변경이므로 새 파일에 다시 적용하지 마세요. - `ui:alert-dialog`: `@seed-design/react`의 `Dialog`를 `AlertDialog`로 바꿨고, `AlertDialogRoot`가 `role`과 `closeOnInteractOutside`를 직접 넘기는 대신 `AlertDialog.Root`를 그대로 export합니다. 그래서 `AlertDialogRootProps`의 `role`, `closeOnInteractOutside` 선언과 `AlertDialogRoot.displayName` 지정도 없어졌습니다. - `ui:dialog`: `@seed-design/react`의 `ContentDialog`를 `Dialog`로 바꿨습니다. - `ui:action-button`: 바뀌지 않았습니다. - 줄바꿈처럼 코드 형식만 다른 부분 그 밖의 차이는 다음 중 하나일 수 있습니다. 새 snippet에 다시 반영할지 판단하고(AI 도구로 작업한다면 사용자에게 확인하게 하세요), 반영할 때는 [Alert Dialog와 Dialog 이름 변경](#alert-dialog와-dialog-이름-변경)의 `@seed-design/react` 이름 변경 규칙을 따르세요. - 프로젝트에서 수정한 부분 - 프로젝트의 snippet이 SEED React 2의 최신 snippet보다 오래된 버전이라서 생긴 차이 반영을 마친 뒤 `legacy-alert-dialog-*`, `legacy-dialog-*` 파일을 삭제하세요. 이 파일은 SEED React 2의 이름을 import하므로, 남겨 두면 `ContentDialog` 타입 에러가 나거나 Alert Dialog·Dialog 이름 변경 작업 대상에 섞입니다. ## 코드 변경 ### 디자인 변경사항이 있는 항목 화면에 보이는 결과가 달라질 수 있어 디자인 결정이 필요할 수 있는 항목입니다. #### 1. `$color.bg.neutral-solid` 색상 변경 \[직접 판단 필요] `$color.bg.neutral-solid`의 값이 바뀌었습니다. - 라이트 모드: `gray-1000` → `gray-900` - 다크 모드: `gray-300` → `gray-1000` 이 토큰을 직접 사용한 화면은 두 모드에서 배경과 전경색의 대비를 확인하고, Solid 배경 위 전경색에는 용도에 맞는 `$color.fg.on-*-solid`를 사용하세요. SEED 컴포넌트 중 `$color.bg.neutral-inverted` 대신 `$color.bg.neutral-solid`를 참조하도록 바뀐 곳은 두 토큰의 값이 같아 화면이 그대로입니다. 다만 Action Button `variant="neutralSolid"`의 로딩 인디케이터 색은 다크 모드에서 달라집니다. ```tsx const className = "bg-bg-neutral-solid text-palette-static-white"; // [!code --] const className = "bg-bg-neutral-solid text-fg-on-neutral-solid"; // [!code ++] ``` `--seed-color-bg-neutral-solid` 같은 CSS 변수나 `bg-bg-neutral-solid` 같은 Tailwind 클래스를 하드코딩해 사용했다면 값이 바뀌어도 에러로 잡히지 않습니다. 사용처를 검색해 직접 확인하세요. #### 2. Badge의 기본 최대 너비 제거 \[직접 판단 필요] Badge에 기본으로 적용되던 최대 너비를 제거했습니다. - SEED React 2의 최대 너비: `size="medium"` 7.5rem(120px), `size="large"` 6.75rem(108px) - 이제 긴 라벨이 더 길게 표시될 수 있습니다. 기존처럼 한 줄 말줄임이 필요한 화면에서는 너비를 직접 지정하세요. ```tsx 길이가 긴 상태 라벨 // [!code --] 길이가 긴 상태 라벨 // [!code ++] ``` #### 3. Content Scale 적용으로 일부 컴포넌트의 DOM 구조 변경 \[직접 판단 필요] 아래 컴포넌트에 [Content Scale](/react/components/concepts/scale-feedback#content-scale-적용하기)을 적용했습니다. - Content Scale은 누르는 동안 배경은 그대로 두고 콘텐츠만 줄이는 효과입니다. - 이를 위해 각 컴포넌트의 root 요소 안에 콘텐츠를 감싸는 박스(``)가 추가되었습니다. 적용한 컴포넌트는 다음과 같습니다. - Accordion `Trigger` - Menu `Item`, Navigation Menu `Item` - Swipeable Menu Sheet `Item`, Menu Sheet `Item` - Page Banner `Root`(root가 `button`일 때만 콘텐츠가 줄어듭니다) - Segmented Control `Item` - Check Select Box `Root`, Radio Select Box `Item` - Select `Trigger`, Select `Item` ```html ``` - 박스가 root의 flex·grid 레이아웃 값을 이어받으므로, SEED 스타일만 사용한다면 화면은 달라지지 않습니다. - snippet은 다시 설치하지 않아도 됩니다. 다만 아래에 해당하는 코드가 있다면 확인하세요. - **root의 자식을 고르는 선택자:** `.seed-accordion__trigger > *`, `:has(> svg)`나 Tailwind CSS의 `space-x-*`, `divide-*`, `*:`, `[&>svg]:` 같은 직계 자식 선택자는 이제 박스 하나만 가리킵니다. 원래 자식에 적용하려면 박스를 기준으로 선택자를 바꾸세요. 반대로 `.my-row span`, `[&_span]:`, `.my-row :first-child`처럼 태그나 순서로 후손을 고르는 선택자는 `span`인 박스에도 적용됩니다. - **root의 `::before`·`::after`:** 위치를 지정하지 않고 flex·grid 레이아웃의 항목으로 배치하던 pseudo element는 이제 박스 바깥에 놓여, 콘텐츠 끝이나 다음 행으로 밀립니다. `[&>.seed-content-scale]:before:`처럼 박스의 pseudo element로 옮기면 이전과 같은 자리에 배치됩니다. - **콘텐츠 안에 배치한 `absolute`·`fixed` 요소:** 박스가 이런 요소의 배치 기준이 됩니다. - label이나 title에 넣은 배지는 root의 padding만큼 어긋나거나, root 바깥 요소 대신 박스를 기준으로 자리를 옮기고, 누르는 동안 함께 줄어듭니다. 배지 위치는 박스를 기준으로 다시 맞추세요. - 포털 없이 렌더한 `fixed` 요소는 화면 대신 박스를 기준으로 배치됩니다. 오버레이는 포털로 렌더하세요. - **`z-index`:** 박스와 root가 stacking context가 되므로, 콘텐츠 안 요소의 `z-index`로 root 바깥 요소를 덮을 수 없습니다. Page Banner `Root`, Menu Sheet `Item`, Swipeable Menu Sheet `Item`, Segmented Control `Item`, Check Select Box `Root`, Radio Select Box `Item`은 SEED React 2에서 stacking context가 아니었으므로 특히 확인하세요. - **직접 구현한 눌림 효과:** root에 `active:scale-*`나 `:active { transform: scale(…) }`로 눌림 효과를 붙였다면 Content Scale과 겹쳐 배경과 콘텐츠가 함께 줄어듭니다. 직접 붙인 효과를 제거하세요. - **root의 DOM을 직접 다루는 코드:** - `firstElementChild`, `children` 등으로 root의 자식에 접근하던 코드(테스트 포함)는 박스를 한 단계 거쳐야 합니다. - 자식 사이의 빈 곳을 누르면 `event.target`도 root 대신 박스가 되므로, `event.target === event.currentTarget`으로 root 클릭을 가려내던 코드를 확인하세요. - root에는 크기를 담은 CSS 변수(`--seed-element-width`, `--seed-element-height`)가 inline style로 추가되어 DOM snapshot도 달라집니다. - **`asChild`로 넘긴 자식 컴포넌트:** 자식 컴포넌트가 받는 children은 박스 하나로 감싸진 상태로 전달됩니다. [Composition](/react/components/concepts/composition#3-컴포넌트는-받은-children을-렌더해야-합니다) 문서를 참고해 아래 경우를 확인하세요. - `Children.toArray`나 `Children.map`으로 children을 나눠 배치하던 컴포넌트는 요소를 하나만 받습니다. - children을 함수로 받는 컴포넌트(render prop)는 함수 대신 요소를 받아 런타임 에러가 납니다. - `input`처럼 children을 가질 수 없는 요소에 `asChild`를 쓰면 React 에러가 납니다. - children을 렌더하지 않고 props로 내용을 직접 그리는 컴포넌트는 화면은 그려지지만 축소가 적용되지 않습니다. ### 코드 수정이 필요한 항목 아래 Step을 순서대로 진행하세요. - 각 항목의 **수정 대상**에 해당하는 코드만 변경하세요. 수정 대상에 해당하는 코드가 없으면 그 항목은 건너뛰어도 됩니다. - 같은 Step 안의 항목은 서로 의존하지 않으므로 동시에 진행할 수 있습니다. AI 코딩 도구를 사용한다면 subagent로 병렬 실행하세요. 두 항목이 같은 파일을 수정해야 한다면 그 파일은 한 작업에서 함께 수정하세요. - 다음 Step은 이전 Step의 항목을 모두 마친 뒤 시작하세요. - 각 항목의 snippet·패키지 설치 명령은 병렬로 실행하지 말고 한 번에 하나씩 실행하세요. CLI는 의존성을 설치하면서 `package.json`과 lockfile을 수정합니다. - Step 1을 시작하기 전에(AI 프롬프트로 작업한다면 Snippet 업데이트 전에) [디자인 변경사항이 있는 항목](#디자인-변경사항이-있는-항목)과 [동작이 바뀌는 항목](#동작이-바뀌는-항목)의 확인 목록을 먼저 만드세요. Step 2의 [deprecated 색상 토큰 교체](#deprecated-색상-토큰-교체)를 먼저 적용하면 `$color.bg.neutral-solid` 확인 목록에 화면이 바뀌지 않는 사용처가 섞입니다. #### Step 1: import·prop 교체 대부분 타입 검사로 발견할 수 있는 항목입니다. 각 항목은 서로 다른 export·경로·prop을 다루므로 병렬로 진행할 수 있습니다. ##### 컴포넌트 vars import 교체 **수정 대상:** `@seed-design/css/vars/component` 또는 그 하위 경로에서 vars를 import하는 코드입니다. `@seed-design/css/vars/component/typography`만 사용한다면 이 항목을 건너뛰세요. \[에러로 발견 가능] `@seed-design/css/vars/component`에서는 `typography`만 제공합니다. 다른 컴포넌트의 vars는 SEED React 2에서도 SemVer 보장 대상이 아니었으므로, SEED React 3에서 export를 제거했습니다. **index에서 `typography`를 가져오는 경우** `@seed-design/css/vars/component` index를 제거했습니다. `@seed-design/css/vars/component/typography`에서 `vars`를 가져오도록 수정하세요. ```ts import { typography } from "@seed-design/css/vars/component"; // [!code --] import { vars as typography } from "@seed-design/css/vars/component/typography"; // [!code ++] ``` **컴포넌트의 vars를 import하는 경우** 같은 값을 [디자인 토큰](/foundations/color)(`@seed-design/css/vars`)에서 찾아 바꾸세요. 토큰을 참조하지 않는 값(`maxWidth: "7.5rem"` 등)은 필요한 값을 직접 지정하세요. ```ts import { badge } from "@seed-design/css/vars/component"; // [!code --] import { vars } from "@seed-design/css/vars"; // [!code ++] const minHeight = badge.sizeMedium.enabled.root.minHeight; // [!code --] const minHeight = vars.$dimension.x5; // [!code ++] ``` ##### Side Panel 본문의 높이 prop 제거 **수정 대상:** `SidePanelBody`(`SidePanel.Body`) 또는 `ResponsiveSidePanelBody`(`ResponsiveSidePanel.Body`)에 `height`, `minHeight`, `maxHeight`를 전달하는 코드입니다. 이 props를 사용하지 않는다면 이 항목을 건너뛰세요. \[에러로 발견 가능] Side Panel 본문은 항상 헤더와 푸터를 제외한 남은 높이를 채웁니다. 세 prop은 효과가 없거나, 푸터를 패널 밖으로 밀어내거나, 푸터를 패널 중간에 띄웠기 때문에 제거했습니다. - `SidePanelBody`: 세 prop을 지우세요. - `ResponsiveSidePanelBody`: 전달한 높이는 Bottom Sheet로 렌더링될 때만 효과가 있었습니다. 같은 동작이 필요하면 `useResponsiveSidePanelContext()`의 `shouldUseBottomSheet`가 `true`일 때만 본문 안의 `Box`에 높이를 지정하세요. ```tsx function Body({ children }: { children: React.ReactNode }) { const { shouldUseBottomSheet } = useResponsiveSidePanelContext(); // [!code ++] return ( {/* [!code --] */} {/* [!code ++] */} {/* [!code --] */} {children} {/* [!code ++] */} {children} ); } ``` ##### Bottom Sheet와 Drawer의 `nested` 제거 **수정 대상:** Bottom Sheet·Responsive Dialog·Responsive Side Panel·Drawer에 `nested`를 전달하는 코드입니다. `nested`를 사용하지 않는다면 이 항목을 건너뛰세요. \[에러로 발견 가능] 다음 값을 삭제하세요. 모두 동작에 영향이 없던 값이므로 대체할 옵션은 필요하지 않습니다. - `BottomSheet.Root`(`BottomSheetRoot`)의 `nested` - `ResponsiveDialog.Root`, `ResponsiveSidePanel.Root`의 `bottomSheetRootProps.nested` - `@seed-design/react-drawer`를 직접 사용한다면 `Drawer.Root`의 `nested` prop과 `useDrawer`의 `nested` 옵션 ```tsx {children} // [!code --] {children} // [!code ++] {children} // [!code --] {children} // [!code ++] {children} // [!code --] {children} // [!code ++] ``` ##### Headless 컴포넌트·타입 import 교체 **수정 대상:** snippet 밖에서 직접 작성한 코드 중 다음에 해당하는 코드입니다. 이 import가 다시 설치한 snippet 내부에만 있었다면 이 항목을 건너뛰세요. - `@seed-design/react/primitive`를 import하는 코드: 반드시 수정하세요. \[에러로 발견 가능] - `@seed-design/react-popover`를 직접 사용하는 코드: [Headless Popover를 사용한 경우](#headless-popover를-사용한-경우)를 따르세요. \[직접 검색 필요] - `@seed-design/react-avatar`를 직접 사용하는 코드: [Headless Avatar를 사용한 경우](#headless-avatar를-사용한-경우)를 따르세요. \[직접 검색 필요] `@seed-design/react/primitive`는 SEED 컴포넌트가 내부에서 사용하는 headless 컴포넌트(스타일 없이 상태와 동작만 제공하는 컴포넌트)를 제공하던 경로입니다. - 이 경로는 `@seed-design/react`의 SemVer 보장 범위에 포함되지 않아, 내부 구현 변경의 영향을 직접 받을 수 있었습니다. - SEED React 3에서는 이 경로를 제거하고, 사용자에게 필요한 API를 `@seed-design/react`에서 SemVer 보장 범위 안에서 제공합니다. - 경로를 import하면 모듈을 찾을 수 없다는 에러가 나므로 반드시 수정하세요. 독립 headless 패키지(`@seed-design/react-checkbox` 등)를 직접 사용하던 경우에도, 같은 동작을 제공하는 API가 `@seed-design/react`에 있다면 그쪽으로 옮기는 것을 권장합니다. 이 권장 사항은 선택 작업이며, 위 수정 대상의 Popover·Avatar 외에는 import를 그대로 두어도 됩니다. - headless 컴포넌트와 SEED 스타일 컴포넌트가 상태를 공유하려면 같은 React context를 사용해야 합니다. - 프로젝트에 직접 설치한 headless 패키지의 버전이 `@seed-design/react`가 사용하는 버전과 다르면, 예를 들어 Checkbox의 Control이 Root의 체크 상태를 읽지 못할 수 있습니다. - 둘 다 `@seed-design/react`에서 가져오면 같은 내부 구현을 사용하므로 이 문제를 피할 수 있습니다. HTML 요소 wrapper를 제공하는 `@seed-design/react-primitive` 패키지는 이 마이그레이션의 대상이 아닙니다. 제거된 `@seed-design/react/primitive`와는 다른 경로이며, 기존 import를 유지하세요. 아래는 기존 문서 예시의 import 교체와, headless Dialog·Popover·Avatar를 직접 사용한 코드에서 필요한 변경입니다. ###### Attachment Field·Attachment Display Field·Side Navigation의 타입과 context hook을 import한 경우 파일 목록, 업로드 상태, 내비게이션 상태를 다루는 코드에서 다음 이름을 사용했다면 표에 따라 바꾸세요. 타입이 나타내는 데이터 구조와 hook의 동작은 그대로입니다. | 기존 `@seed-design/react/primitive` export | 변경할 `@seed-design/react` export | | ----------------------------------------------------------- | ------------------------------------ | | `DisplayItemEntry` | `AttachmentDisplayItemEntry` | | `DisplayItemStatusDetails` | `AttachmentDisplayItemStatusDetails` | | `FileEntry` | `AttachmentInputFileEntry` | | `FileStatusDetails` | `AttachmentInputFileStatusDetails` | | `useFileUploadContext` | `useAttachmentInputContext` | | `UseFileUploadContext`, `UseFileUploadReturn` | `AttachmentInputContextValue` | | `useAttachmentDisplayContext` | `useAttachmentDisplayContext` | | `UseAttachmentDisplayContext`, `UseAttachmentDisplayReturn` | `AttachmentDisplayContextValue` | | `useSideNavigationContext` | `useSideNavigationContext` | | `UseSideNavigationContext` | `SideNavigationContextValue` | ```ts import type { FileEntry } from "@seed-design/react/primitive"; // [!code --] import type { AttachmentInputFileEntry } from "@seed-design/react"; // [!code ++] const files: FileEntry[] = []; // [!code --] const files: AttachmentInputFileEntry[] = []; // [!code ++] ``` ###### 컴포넌트를 import한 경우 headless Root·Item을 직접 사용했다면 import를 `@seed-design/react`로 바꾸고, 사용하는 컴포넌트와 Props 타입을 아래 표에 따라 교체하세요. 아래의 `.Primitive`와 `List.RadioRoot`는 기존 headless 컴포넌트의 선택·키보드·폼 동작을 제공하면서 SEED 레이아웃 스타일을 추가하지 않습니다. `.Primitive` 없이 `Checkbox.Root`나 `Switch.Root`로 바꾸면 SEED 스타일이 추가되어 직접 작성한 레이아웃이 달라질 수 있습니다. | 기존 `@seed-design/react/primitive` 컴포넌트 | 변경할 `@seed-design/react` 컴포넌트 | Props 타입 변경 | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | `Checkbox.Root` | `Checkbox.Root.Primitive` | `Checkbox.RootProps` → `Checkbox.RootPrimitiveProps` | | `RadioGroup.Item` | `RadioGroup.Item.Primitive` | `RadioGroup.ItemProps` → `RadioGroup.ItemPrimitiveProps` | | `RadioGroup.Root` (`RadioGroup.Item`과 함께 사용) | `RadioGroupField.Root.Primitive`. `@seed-design/react`의 `RadioGroup.Root`는 선택 상태를 관리하지 않는 레이아웃 컴포넌트입니다. | `RadioGroup.RootProps` → `RadioGroupField.RootPrimitiveProps` | | `RadioGroup.Root` (`ListRadioItem`을 감싸는 용도) | `List.RadioRoot` | `RadioGroup.RootProps` → `List.RadioRootProps` | | `Switch.Root` | `Switch.Root.Primitive` | `Switch.RootProps` → `Switch.RootPrimitiveProps` | - `ListRadioItem`을 감싼 Root는 사용처의 코드이므로 snippet을 다시 설치한 뒤에도 직접 교체하세요. - `Checkbox.HiddenInput`, `RadioGroup.ItemHiddenInput`, `Switch.HiddenInput`은 이름을 유지하고 `@seed-design/react`에서 가져오세요. ```tsx import { Checkbox } from "@seed-design/react/primitive"; // [!code --] import { Checkbox } from "@seed-design/react"; // [!code ++] import { Checkmark } from "seed-design/ui/checkbox"; function CustomCheckbox({ children, ...props }: Checkbox.RootProps) { // [!code --] function CustomCheckbox({ children, ...props }: Checkbox.RootPrimitiveProps) { // [!code ++] return ( // [!code --] // [!code ++] {children} // [!code --] // [!code ++] ); } ``` Chip·List와 headless 컴포넌트를 `asChild`로 중첩한 코드도 위 import·`.Primitive` 교체 후 구조를 유지할 수 있습니다. `Chip.Root`는 Step 2의 [`Chip.Root` 이름 변경](#chiproot-이름-변경)에서 바꾸세요. 선택적으로 구조를 간소화하려면 [Chip.Toggle·Chip.RadioItem](/react/components/chip)과 [List.CheckItem·List.RadioItem·List.SwitchItem](/react/components/list)을 참고하세요. ###### Headless Dialog를 사용한 경우 **수정 대상:** `@seed-design/react/primitive`에서 `Dialog`, `DialogRoot`, `useDialogContext` 등 headless Dialog를 가져와 직접 작성한 코드입니다. `@seed-design/react/primitive`의 Dialog는 `@seed-design/react-dialog`를 그대로 다시 export한 것이며, 이 패키지의 API는 바뀌지 않았습니다. - `@seed-design/react-dialog`를 프로젝트 의존성에 추가하고 import 경로만 바꾸세요. - 이름은 그대로 두세요. [Alert Dialog와 Dialog 이름 변경](#alert-dialog와-dialog-이름-변경)은 `@seed-design/react`의 스타일이 적용된 컴포넌트에만 해당합니다. * npm: npm install @seed-design/react-dialog * pnpm: pnpm add @seed-design/react-dialog * yarn: yarn add @seed-design/react-dialog * bun: bun add @seed-design/react-dialog ```ts import { Dialog } from "@seed-design/react/primitive"; // [!code --] import { Dialog } from "@seed-design/react-dialog"; // [!code ++] ``` ###### Headless Popover를 사용한 경우 **수정 대상:** `@seed-design/react/primitive` 또는 `@seed-design/react-popover`에서 Popover를 가져와 직접 작성한 코드입니다. Help Bubble(`@seed-design/react`의 `HelpBubble`과 `ui:help-bubble` snippet)은 이미 내부에서 새 `Popover.Content`를 사용하므로 기존 코드를 수정할 필요가 없습니다. \[직접 검색 필요] 기존 headless Popover에 접근성 속성과 포커스 관리를 담당하는 `Popover.Content`가 추가됐습니다. `@seed-design/react-popover`를 직접 사용하던 코드는 `Popover.Content`로 감싸지 않아도 타입 에러가 나지 않으므로, 사용처를 검색해 다음 두 작업을 적용하세요. 1. `@seed-design/react/primitive`에서 가져오던 Popover는 `@seed-design/react-popover`로 import를 바꾸세요. 이미 독립 패키지를 사용했다면 import를 유지하고, 패키지를 `3.0.0` 이상으로 업그레이드하세요. 2. `Popover.Positioner` 또는 `Popover.PositionerPortal` 안의 내용을 `Popover.Content`로 감싸세요. `role="dialog"`와 관련 aria 속성이 Content로 이동했으므로, 직접 지정한 `aria-label`·`aria-labelledby`·`aria-describedby`도 Content에 전달하세요. - npm: npm install @seed-design/react-popover@latest - pnpm: pnpm add @seed-design/react-popover@latest - yarn: yarn add @seed-design/react-popover@latest - bun: bun add @seed-design/react-popover@latest ```tsx import { Popover } from "@seed-design/react/primitive"; // [!code --] import { Popover } from "@seed-design/react-popover"; // [!code ++] 열기 // [!code --] // [!code ++] // [!code ++]

내용

// [!code ++]
; ``` 이번 headless Popover 업데이트에는 다음 동작 변경도 포함됩니다. - 열릴 때 Content로 포커스를 옮깁니다. 기존처럼 포커스를 옮기지 않으려면 Root에 `autoFocus={false}`를 지정하세요. - Popover 안에 포커스가 있는 상태에서 닫히면 포커스가 trigger로 돌아갑니다. - Popover 안에서 바깥 요소로 포커스를 옮기면 Popover가 닫히고, 포커스는 옮긴 요소에 그대로 남습니다. `closeOnInteractOutside={false}`이면 닫히지 않습니다. - Dialog·Bottom Sheet 안에서 연 Popover를 Escape 키나 바깥 영역을 눌러 닫을 때 Dialog·Bottom Sheet까지 함께 닫히던 문제를 수정했습니다. 이 문제를 피하려고 직접 추가한 이벤트 처리가 있다면 확인하세요. `@seed-design/react`의 스타일이 적용된 `Popover`와 `ui:popover` snippet은 이번 버전에 새로 추가된 [Popover](/react/components/popover) 컴포넌트입니다. 신규 컴포넌트이므로 기존 headless Popover의 교체 대상이 아닙니다. ###### Headless Avatar를 사용한 경우 **수정 대상:** `@seed-design/react/primitive` 또는 `@seed-design/react-avatar`에서 Avatar를 가져와 직접 작성한 코드입니다. `@seed-design/react`에서 가져오는 스타일이 적용된 `Avatar`는 그대로 사용할 수 있습니다. \[직접 검색 필요] `@seed-design/react-avatar`의 배포를 중단했습니다. `@seed-design/react/primitive`에서 가져온 Avatar는 모듈을 찾을 수 없다는 에러가 나지만, `@seed-design/react-avatar`를 직접 사용하던 코드는 이미 설치된 패키지가 계속 동작하므로 타입 에러가 나지 않습니다. 사용처를 검색해, 같은 이미지 로딩·fallback 기능을 제공하는 `@seed-design/react-image`를 프로젝트 의존성에 추가하고 import를 바꾸세요. - npm: npm install @seed-design/react-image - pnpm: pnpm add @seed-design/react-image - yarn: yarn add @seed-design/react-image - bun: bun add @seed-design/react-image ```ts import { Avatar, useAvatarContext } from "@seed-design/react/primitive"; // [!code --] import { Image, useImageContext } from "@seed-design/react-image"; // [!code ++] ``` | 기존 | 대체 | | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | `AvatarRoot` / `AvatarImage` / `AvatarFallback` | `Image.Root` / `Image.Content` / `Image.Fallback` | | `Avatar.Root` / `Avatar.Image` / `Avatar.Fallback` | `Image.Root` / `Image.Content` / `Image.Fallback` | | `AvatarRootProps` / `AvatarImageProps` / `AvatarFallbackProps` | `Image.RootProps` / `Image.ContentProps` / `Image.FallbackProps` | | `useAvatarContext` / `UseAvatarContext` | `useImageContext` / `UseImageContext` | | Context의 `getImageProps({ src, onLoad, onError })` | `getContentProps({ src, srcSet })`와 `setSrc(src, srcSet)`, `handleLoad()`, `handleError()` | `getImageProps`는 이미지 주소 등록과 load·error 처리를 함께 맡았지만, `getContentProps`는 `img` 요소의 props만 반환합니다. Context로 `img`를 직접 렌더한다면 `src`·`srcSet`이 바뀔 때 `setSrc(src, srcSet)`를 호출하고, `img`의 `onLoad`·`onError`에서 `handleLoad()`·`handleError()`를 호출하세요. `Image.Content`를 사용하면 이 처리가 자동으로 적용됩니다. `@seed-design/react-avatar`를 직접 사용하던 프로젝트도 같은 대체 API로 옮기세요. ###### 그 밖의 headless export를 import한 경우 `@seed-design/react/primitive`는 아래 headless 패키지의 export를 그대로 다시 export했습니다. 위에서 다루지 않은 export는 다음 기준으로 옮기세요. - `useSnackbarContext`, `UseSnackbarContext`, `CreateSnackbarOptions`, `pullToRefreshPreventPull`, `tabsCarouselPreventDrag`는 `@seed-design/react`에서 같은 이름으로 가져오세요. - 그 밖의 headless 컴포넌트와 hook은 이름을 유지하고, 아래 표의 패키지를 프로젝트 의존성에 추가한 뒤 import 경로만 바꾸세요. - `Tabs`, `Slider`, `Snackbar`, `PullToRefresh`, `NavigationMenu`, `SideNavigation`, `ProgressCircle`, `AttachmentDisplay`는 `@seed-design/react`에도 같은 이름이 있지만, SEED 스타일이 적용된 다른 컴포넌트입니다. `@seed-design/react`로 경로만 바꾸면 타입 에러 없이 스타일과 구조가 달라지므로 그렇게 하지 마세요. | headless export | 패키지 | | -------------------------------------------------------------------------------- | --------------------------------------- | | `AttachmentDisplay` 등 Attachment Display | `@seed-design/react-attachment-display` | | `Checkbox.Control`, `useCheckboxContext` 등 위에서 다루지 않은 Checkbox export | `@seed-design/react-checkbox` | | `FileUpload` 등 File Upload | `@seed-design/react-file-upload` | | `MiddleTruncate` | `@seed-design/react-middle-truncate` | | `NavigationMenu` | `@seed-design/react-navigation-menu` | | `ProgressCircle` | `@seed-design/react-progress` | | `PullToRefresh` | `@seed-design/react-pull-to-refresh` | | `RadioGroup.ItemControl`, `useRadioGroupContext` 등 위에서 다루지 않은 Radio Group export | `@seed-design/react-radio-group` | | `SideNavigation` | `@seed-design/react-side-navigation` | | `Slider` | `@seed-design/react-slider` | | `Snackbar` | `@seed-design/react-snackbar` | | `Switch.Control`, `Switch.Thumb`, `useSwitchContext` 등 위에서 다루지 않은 Switch export | `@seed-design/react-switch` | | `Tabs` | `@seed-design/react-tabs` | | `Toggle` | `@seed-design/react-toggle` | ```ts import { Tabs } from "@seed-design/react/primitive"; // [!code --] import { Tabs } from "@seed-design/react-tabs"; // [!code ++] ``` ##### deprecated 컴포넌트와 snippet 제거 **수정 대상:** 아래 표의 export나 삭제된 스타일 모듈·클래스·snippet을 사용하는 코드입니다. 해당 항목이 없다면 이 항목을 건너뛰세요. \[에러로 발견 가능] `@seed-design/react`에서 아래 컴포넌트와 해당 타입·namespace export를 제거했습니다. 기존 import를 대체 컴포넌트로 바꾸고, 사용하던 prop과 배치를 새 컴포넌트의 API에 맞게 조정하세요. ActionChip과 ControlChip은 [Chip 문서의 마이그레이션 예시](/react/components/chip#migrating-from-actionchipcontrolchip)도 참고할 수 있습니다. 제거된 컴포넌트의 문서는 최신 문서 사이트에서 삭제되었으며, 기존 사용법은 [SEED React 2 문서](https://seed-design.io/react/v2)에서 확인할 수 있습니다. | 제거된 export | 대체 컴포넌트 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | | `ActionChip`, `ActionChipProps` | [Chip.Button](/react/components/chip) `variant="solid"` | | `ControlChip`, `ControlChipBaseProps`, `ControlChipProps` | [Chip.Toggle 또는 Chip.Button](/react/components/chip) | | `ActionSheet` namespace와 `ActionSheetBackdrop`, `ActionSheetPositioner`, `ActionSheetContent`, `ActionSheetHeader`, `ActionSheetRoot`, `ActionSheetTitle`, `ActionSheetDescription`, `ActionSheetTrigger`, `ActionSheetList`, `ActionSheetItem`, `ActionSheetCloseButton`; 각 이름의 `Props` 타입 | [SwipeableMenuSheet](/react/components/swipeable-menu-sheet) | | `ExtendedActionSheet` namespace와 `ExtendedActionSheetBackdrop`, `ExtendedActionSheetPositioner`, `ExtendedActionSheetContent`, `ExtendedActionSheetFooter`, `ExtendedActionSheetHeader`, `ExtendedActionSheetRoot`, `ExtendedActionSheetTitle`, `ExtendedActionSheetTrigger`, `ExtendedActionSheetList`, `ExtendedActionSheetGroup`, `ExtendedActionSheetItem`, `ExtendedActionSheetCloseButton`; 각 이름의 `Props` 타입 | [SwipeableMenuSheet](/react/components/swipeable-menu-sheet) | | `Fab`, `FabProps`, `ExtendedFab`, `ExtendedFabProps` | [ContextualFloatingButton](/react/components/contextual-floating-button) | | `InlineBanner` namespace와 `InlineBannerCloseButton`, `InlineBannerContent`, `InlineBannerDescription`, `InlineBannerLink`, `InlineBannerRoot`, `InlineBannerTitle`; 각 이름의 `Props` 타입 | [PageBanner](/react/components/page-banner) | | `LinkContent`, `LinkContentProps` | [ActionButton](/react/components/action-button) `variant="ghost"` | | `Inline`, `InlineProps`, `Columns`, `Column`, `ColumnsProps`, `ColumnProps` | [HStack](/react/components/layout/h-stack) | | `Stack`, `StackProps` | [VStack](/react/components/layout/v-stack) (`HStack`·`VStack`는 유지) | ```tsx import { ActionChip } from "@seed-design/react"; // [!code --] import { Chip } from "@seed-design/react"; // [!code ++] Label // [!code --] Label // [!code ++] ``` 다음 항목을 직접 참조했다면 대체 컴포넌트의 스타일로 바꾸세요. CSS 클래스를 하드코딩한 사용처는 컴파일 에러로 발견되지 않으므로 문자열로 검색하세요. - 삭제된 `@seed-design/css/recipes/*` 모듈: `action-chip`, `action-sheet`, `action-sheet-item`, `control-chip`, `extended-action-sheet`, `extended-action-sheet-item`, `extended-fab`, `fab`, `inline-banner`, `link-content` - 삭제된 클래스: `.seed-action-chip`, `.seed-control-chip`, `.seed-action-sheet__*`, `.seed-action-sheet-item`, `.seed-extended-action-sheet__*`, `.seed-extended-action-sheet-item`, `.seed-fab`, `.seed-extended-fab`, `.seed-inline-banner__*`, `.seed-link-content` 기존에 설치한 다음 snippet은 제거된 export를 import하므로, 그대로 두면 빌드가 실패합니다. 대체 snippet으로 교체하세요. | 기존 snippet | 대체 snippet | | -------------------------- | ------------------------- | | `ui:action-sheet` | `ui:swipeable-menu-sheet` | | `ui:extended-action-sheet` | `ui:swipeable-menu-sheet` | | `ui:control-chip` | `ui:chip` | | `ui:inline-banner` | `ui:page-banner` | `ui:error-state`는 제거된 export를 import하지 않으므로 이 변경만으로 빌드가 실패하지는 않습니다. 다만 snippet 목록에서 제거되어 더 이상 CLI로 설치할 수 없으므로, 새로 구성하는 화면에는 `ui:result-section`을 사용하세요. - npm: npx @seed-design/cli@latest add ui:swipeable-menu-sheet --on-diff backup npx @seed-design/cli@latest add ui:chip --on-diff backup npx @seed-design/cli@latest add ui:page-banner --on-diff backup npx @seed-design/cli@latest add ui:result-section --on-diff backup - pnpm: pnpm dlx @seed-design/cli@latest add ui:swipeable-menu-sheet --on-diff backup pnpm dlx @seed-design/cli@latest add ui:chip --on-diff backup pnpm dlx @seed-design/cli@latest add ui:page-banner --on-diff backup pnpm dlx @seed-design/cli@latest add ui:result-section --on-diff backup - yarn: yarn dlx @seed-design/cli@latest add ui:swipeable-menu-sheet --on-diff backup yarn dlx @seed-design/cli@latest add ui:chip --on-diff backup yarn dlx @seed-design/cli@latest add ui:page-banner --on-diff backup yarn dlx @seed-design/cli@latest add ui:result-section --on-diff backup - bun: bun x @seed-design/cli@latest add ui:swipeable-menu-sheet --on-diff backup bun x @seed-design/cli@latest add ui:chip --on-diff backup bun x @seed-design/cli@latest add ui:page-banner --on-diff backup bun x @seed-design/cli@latest add ui:result-section --on-diff backup ##### Badge를 snippet으로 전환 **수정 대상:** `@seed-design/react`에서 `Badge` JSX 컴포넌트 또는 `BadgeProps`를 가져오는 코드입니다. `ImageFrameBadge`처럼 이름에 Badge가 들어간 다른 컴포넌트만 사용한다면 이 항목을 건너뛰세요. \[에러로 발견 가능] `@seed-design/react`의 `Badge`는 JSX 컴포넌트가 아니라 `Badge.Root`, `Badge.Prefix`, `Badge.Label`, `Badge.Action`으로 구성된 namespace로 바뀌었고, `BadgeProps` export도 제거되었습니다. `ui:badge` snippet을 설치한 뒤 import를 바꾸세요. - 기존 `tone`, `variant`, `size`와 라벨을 전달하는 `children`은 snippet에서도 그대로 사용할 수 있습니다. - snippet `Badge`는 `asChild`를 지원하지 않습니다. `asChild`를 사용했다면 `@seed-design/react`의 `Badge.Root`·`Badge.Label`을 직접 조합하세요. * npm: npx @seed-design/cli@latest add ui:badge --on-diff backup * pnpm: pnpm dlx @seed-design/cli@latest add ui:badge --on-diff backup * yarn: yarn dlx @seed-design/cli@latest add ui:badge --on-diff backup * bun: bun x @seed-design/cli@latest add ui:badge --on-diff backup ```tsx import { Badge, type BadgeProps } from "@seed-design/react"; // [!code --] import { Badge, type BadgeProps } from "seed-design/ui/badge"; // [!code ++] 완료 ``` `ImageFrameBadge`는 별도 export로 유지되므로 import를 바꿀 필요가 없습니다. 새 `prefix`와 `actionProps` 사용법은 [Badge](/react/components/badge) 문서를 참고하세요. ##### Date Picker·Time Picker를 snippet으로 전환 **수정 대상:** `@seed-design/react`의 Date Picker·Time Picker 컴포넌트와 컴포넌트 전용 props 타입을 사용하는 코드입니다. 해당 항목이 없다면 이 항목을 건너뛰세요. \[에러로 발견 가능] `ui:date-picker`, `ui:time-picker` snippet을 설치하고 컴포넌트 import를 바꾸세요. 두 snippet이 사용하는 `ui:wheel-picker`도 함께 설치됩니다. - npm: npx @seed-design/cli@latest add ui:date-picker ui:time-picker --on-diff backup - pnpm: pnpm dlx @seed-design/cli@latest add ui:date-picker ui:time-picker --on-diff backup - yarn: yarn dlx @seed-design/cli@latest add ui:date-picker ui:time-picker --on-diff backup - bun: bun x @seed-design/cli@latest add ui:date-picker ui:time-picker --on-diff backup ```tsx import { DatePicker, TimePicker } from "@seed-design/react"; // [!code --] import { DatePicker } from "seed-design/ui/date-picker"; // [!code ++] import { TimePicker } from "seed-design/ui/time-picker"; // [!code ++] ``` - 다음 컴포넌트와 props 타입은 snippet에서 가져오세요. - `seed-design/ui/date-picker`: `DatePicker`, `TwoMonthDatePicker`, `WeekDatePicker`, `ContinuousDatePicker`와 `DatePickerProps`, `TwoMonthDatePickerProps`, `WeekDatePickerProps`, `ContinuousDatePickerProps` - `seed-design/ui/time-picker`: `TimePicker`, `TimePickerProps` - `@seed-design/react`의 `DatePicker`는 이제 완성된 컴포넌트가 아니라 `DatePicker.Root`, `DatePicker.Header`, `DatePicker.Calendar`, `DatePicker.Wheel`로 구성된 namespace입니다. 완성된 UI에는 snippet을 사용하세요. - `DatePickerDate`, `DatePickerValue` 같은 값 타입, `dateOnOrAfter` 같은 제약 조건 helper, `DatePickerActions`, `DatePickerCellContentRenderProps`, `TimePickerValue`, `MinuteStep`은 `@seed-design/react`에 남아 있으므로 import를 바꾸지 마세요. #### Step 2: 이름·스타일 참조 변경 대부분 타입 에러 없이 스타일이나 동작이 바뀌는 항목입니다. Step 1의 결과에 의존하므로 Step 1을 모두 마친 뒤 진행하세요. - [Alert Dialog와 Dialog 이름 변경](#alert-dialog와-dialog-이름-변경)은 Step 1의 [Headless 컴포넌트·타입 import 교체](#headless-컴포넌트타입-import-교체)를 마친 뒤 진행해야, 남은 `@seed-design/react`의 `Dialog`가 모두 스타일이 적용된 컴포넌트로 확정됩니다. - [`Chip.Root` 이름 변경](#chiproot-이름-변경)은 Step 1의 headless 컴포넌트 교체와 deprecated 컴포넌트 제거가 같은 Chip 코드를 고치므로 그 뒤에 진행하세요. - 각 항목은 서로 다른 이름과 문자열을 다루므로 병렬로 진행할 수 있습니다. ##### Alert Dialog와 Dialog 이름 변경 **수정 대상:** snippet 밖에서 직접 작성한 코드가 `@seed-design/react`의 `Dialog`·`ContentDialog`나 그 개별 export, `@seed-design/css/recipes/dialog`·`recipes/content-dialog` 모듈, 클래스 이름 `seed-dialog__`·`seed-content-dialog__`, CSS 변수 `--content-dialog-`를 사용하는 경우입니다. `ui:alert-dialog`·`ui:dialog` snippet만 사용한다면, [Snippet 업데이트](#snippet-업데이트)에서 다시 설치한 뒤 이 항목을 건너뛰세요. \[에러 없이 스타일·동작 변경] 이 항목은 한 작업에서 처음부터 끝까지 순서대로 처리하세요. Alert Dialog와 Dialog를 나눠 병렬로 처리하면 아래 순서 규칙을 지킬 수 없습니다. **변경 내용** SEED React 2에서는 `Dialog`라는 이름이 `@seed-design/react`와 snippet에서 서로 다른 컴포넌트를 가리켰습니다. - `@seed-design/react`에서는 `Dialog`가 Alert Dialog를, `ContentDialog`가 Dialog를 가리켰습니다. `@seed-design/css`의 `recipes/*` 모듈, 클래스 이름, CSS 변수도 같은 방식으로 이름을 붙였습니다. - snippet은 Alert Dialog를 `ui:alert-dialog`(`AlertDialogRoot` 등)로, Dialog를 `ui:dialog`(`DialogRoot` 등)로 제공했습니다. SEED React 3에서는 `@seed-design/react`와 `@seed-design/css`의 이름을 snippet의 이름에 맞췄습니다. snippet의 이름은 바뀌지 않았습니다. | 컴포넌트 | 항목 | SEED React 2 | SEED React 3 | | ------------ | --------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | Alert Dialog | snippet | `ui:alert-dialog`의 `AlertDialogRoot` 등 | 변경 없음 | | Alert Dialog | React namespace | `Dialog` | `AlertDialog` | | Alert Dialog | React 개별 export | `DialogRoot`, `DialogRootProps` 등 | `AlertDialogRoot`, `AlertDialogRootProps` 등 | | Alert Dialog | `recipes/*` 모듈 경로 | `@seed-design/css/recipes/dialog` | `@seed-design/css/recipes/alert-dialog` | | Alert Dialog | `recipes/*` 모듈 export | `dialog`, `dialogVariantMap`, `DialogVariantProps`, `DialogSlotName` | `alertDialog`, `alertDialogVariantMap`, `AlertDialogVariantProps`, `AlertDialogSlotName` | | Alert Dialog | `recipes/*` CSS 파일 | `recipes/dialog.css`, `recipes/dialog.layered.css` | `recipes/alert-dialog.css`, `recipes/alert-dialog.layered.css` | | Alert Dialog | 클래스 이름 | `.seed-dialog__*` | `.seed-alert-dialog__*` | | Dialog | snippet | `ui:dialog`의 `DialogRoot` 등 | 변경 없음 | | Dialog | React namespace | `ContentDialog` | `Dialog` | | Dialog | React 개별 export | `ContentDialogRoot`, `ContentDialogRootProps` 등 | `DialogRoot`, `DialogRootProps` 등 | | Dialog | `recipes/*` 모듈 경로 | `@seed-design/css/recipes/content-dialog` | `@seed-design/css/recipes/dialog` | | Dialog | `recipes/*` 모듈 export | `contentDialog`, `contentDialogVariantMap`, `ContentDialogVariantProps`, `ContentDialogSlotName` | `dialog`, `dialogVariantMap`, `DialogVariantProps`, `DialogSlotName` | | Dialog | `recipes/*` CSS 파일 | `recipes/content-dialog.css`, `recipes/content-dialog.layered.css` | `recipes/dialog.css`, `recipes/dialog.layered.css` | | Dialog | 클래스 이름 | `.seed-content-dialog__*` | `.seed-dialog__*` | | Dialog | CSS 변수 | `--content-dialog-default-width`, `--content-dialog-default-max-width`, `--content-dialog-size-width` | `--dialog-default-width`, `--dialog-default-max-width`, `--dialog-size-width` | `AlertDialog.Root`의 기본값도 바뀌었습니다. | prop | SEED React 2 `Dialog.Root` | SEED React 3 `AlertDialog.Root` | | ------------------------ | -------------------------- | ------------------------------- | | `role` | `"dialog"` | `"alertdialog"` | | `closeOnInteractOutside` | `true` | `false` | `ui:alert-dialog` snippet의 `AlertDialogRoot`는 SEED React 2에서도 두 값을 `"alertdialog"`와 `false`로 직접 넘기고 있었습니다. 따라서 새 기본값은 snippet의 Alert Dialog와 같은 동작이며, snippet으로 사용하는 Alert Dialog의 동작은 바뀌지 않습니다. 다음은 바뀌지 않았으므로 이름 변경을 적용하지 마세요. - snippet이 export하는 이름(`AlertDialogRoot`, `AlertDialogContent`, `DialogRoot`, `DialogContent` 등)과 snippet을 import하는 코드. 특히 snippet의 `DialogRoot`, `DialogContent`에 아래 `@seed-design/react` 이름 변경을 적용하면 Dialog가 Alert Dialog로 바뀝니다. - `ResponsiveDialog`, `useResponsiveDialogContext` 등 Responsive Dialog의 React API. Dialog로 렌더링될 때 쓰는 클래스 이름은 아래 클래스 이름 규칙에 따라 바뀝니다. - `@seed-design/react/primitive`에서 import한 `Dialog`, `DialogRoot` 등. `@seed-design/react`와 경로가 비슷하지만 headless Dialog이므로, [Headless 컴포넌트·타입 import 교체](#headless-컴포넌트타입-import-교체)에 따라 이름을 유지한 채 import 경로만 바꾸세요. - `@seed-design/react-dialog`나 다른 UI 라이브러리처럼 `@seed-design/react`가 아닌 패키지에서 import한 `Dialog` - 두 컴포넌트가 함께 사용하는 CSS 변수 `--dialog-z-index` - `Dialog`(SEED React 2의 `ContentDialog`)의 기본값 **타입 검사와 빌드가 통과해도 이 항목의 작업이 끝났다는 뜻이 아닙니다.** 에러가 없는지가 아니라, 아래 절차대로 모든 사용처를 찾아 옮겼는지로 완료를 판단하세요. 다음 이름은 SEED React 3에도 그대로 있지만, Alert Dialog가 아닌 Dialog를 가리킵니다. - `@seed-design/react`의 `Dialog`와 `DialogRoot` 같은 개별 export - `@seed-design/css/recipes/dialog` 모듈과 `recipes/dialog.css` - 클래스 이름 `.seed-dialog__*` 새 `Dialog`는 SEED React 2 `Dialog`의 하위 컴포넌트(`Root`, `Trigger`, `Positioner`, `Backdrop`, `Content`, `Header`, `Title`, `Description`, `Footer`, `Action`)를 모두 제공합니다. 따라서 옮기지 않은 Alert Dialog 코드는 대부분 아무 에러 없이 Dialog의 스타일과 동작으로 렌더링됩니다. **타입 에러가 나는 곳도 에러를 없애는 것으로 끝나지 않습니다.** 에러는 다음 경우에만 나고, 모두 그 코드를 옮겨야 한다는 신호입니다. - 제거된 `ContentDialog`와 `ContentDialog`로 시작하는 개별 export, `@seed-design/css/recipes/content-dialog`로 시작하는 import 경로: 아래 절차대로 Dialog로 옮기세요. - `Dialog.Root`의 `role`·`skipAnimation` prop, `Dialog.RootProps["role"]` 같은 타입 참조, `recipes/dialog` 모듈의 `skipAnimation` variant와 slot 이름 `"action"`: 아직 옮기지 않은 Alert Dialog입니다. 다시 설치하지 않은 SEED React 2의 `ui:alert-dialog` snippet도 `role`을 선언하므로 여기에 해당합니다. - prop이나 선언을 지우면 에러는 사라지지만 그 Alert Dialog가 Dialog로 렌더링됩니다. 지우지 말고 `AlertDialog`로 옮기거나 snippet을 다시 설치하세요. **작업 전 확인** 1. `@seed-design/react`와 `@seed-design/css`가 3.0.0 이상인지 확인하세요. 새 `ui:alert-dialog`·`ui:dialog` snippet은 `@seed-design/react` 3.0.0 이상을 요구합니다. 2. 이 항목은 SEED React 2의 이름을 기준으로 한 번만 적용하세요. snippet 파일을 제외한 코드에서 `@seed-design/react`의 `AlertDialog`, `@seed-design/css/recipes/alert-dialog`, `.seed-alert-dialog__*` 중 하나라도 이미 사용하고 있다면 일부를 옮긴 상태입니다. 이때 남아 있는 `Dialog`가 아직 옮기지 않은 Alert Dialog인지, `ContentDialog`에서 옮긴 Dialog인지는 이름만으로 구분할 수 없습니다. git 기록에서 사용처마다 SEED React 2 시점의 이름을 확인하고, 확인할 수 없다면 사용자에게 확인하세요. 3. 아래 사용 방식별로 안내하는 찾을 대상을 모두 찾고, 바꾸기 전에 사용처마다 SEED React 2의 이름으로 분류하세요. - SEED React 2의 `Dialog`, `recipes/dialog`, `.seed-dialog__*`는 Alert Dialog입니다. - `ContentDialog`, `recipes/content-dialog`, `.seed-content-dialog__*`, `--content-dialog-*`는 Dialog입니다. - 찾아 바꾸기로 일괄 치환한다면 Alert Dialog를 모두 옮긴 뒤에 Dialog를 옮기세요. 순서를 바꾸면 `ContentDialog`에서 옮긴 `Dialog`까지 다시 `AlertDialog`로 바뀝니다. **`@seed-design/react`에서 `Dialog`나 `ContentDialog`를 직접 import하는 경우** 찾을 대상은 `@seed-design/react`에서 가져온 다음 이름입니다. [Snippet 업데이트](#snippet-업데이트)에서 다시 설치한 `ui/alert-dialog.tsx`, `ui/dialog.tsx`는 이미 SEED React 3의 이름을 사용하므로 제외하세요. - namespace `Dialog`, `ContentDialog` - 개별 export `Dialog`, `DialogProps`, `ContentDialog`, `ContentDialogProps`. ``는 `Root`, `Trigger`, `Positioner`, `Backdrop`, `Content`, `Header`, `Title`, `Description`, `Footer`, `Action`이며, `ContentDialog`에는 `Body`와 `CloseButton`이 더 있습니다. - 별칭 import(`import { Dialog as SeedDialog }`), type import(`import type { DialogRootProps }`), re-export(`export { Dialog } from "@seed-design/react"`), namespace import(`import * as Seed from "@seed-design/react"` 뒤의 `Seed.Dialog`), dynamic import(`import("@seed-design/react")` 결과의 `Dialog`)도 함께 찾으세요. - 테스트에서 `@seed-design/react`를 mock하는 코드도 함께 찾으세요. `vi.mock("@seed-design/react", ...)`나 `jest.mock("@seed-design/react", ...)`가 반환하는 객체의 `Dialog`, `ContentDialog` 키는 import 문이 아니므로 import만 찾으면 빠집니다. `@seed-design/react`에서 가져오는 이름만 바꾸세요. 별칭이나 프로젝트에서 감싼 컴포넌트의 이름은 바꾸지 않아도 됩니다. 바꾼 이름이 같은 파일의 다른 식별자와 겹치면 별칭으로 import하세요. 예를 들어 프로젝트에서 만든 `AlertDialog` 컴포넌트가 `@seed-design/react`의 `Dialog`를 감싸고 있었거나, 다른 라이브러리의 `Dialog`를 함께 import하던 파일이 있습니다. 이때 겹치는 쪽의 이름은 바꾸지 마세요. ```tsx import { Dialog } from "@seed-design/react"; // [!code --] import { AlertDialog as SeedAlertDialog } from "@seed-design/react"; // [!code ++] export function AlertDialog(props: AlertDialogProps) { return ; // [!code --] return ; // [!code ++] } ``` 1. Alert Dialog를 옮기세요. `Dialog`를 `AlertDialog`로 바꾸고, 개별 export의 접두어 `Dialog`를 `AlertDialog`로 바꾸세요(`DialogRoot` → `AlertDialogRoot`, `DialogRootProps` → `AlertDialogRootProps`). JSX와 타입에서 쓰는 `Dialog.Root`, `Dialog.RootProps` 등도 함께 바꾸세요. ```tsx import { Dialog } from "@seed-design/react"; // [!code --] import { AlertDialog } from "@seed-design/react"; // [!code ++] ``` 2. 1번을 모두 마친 뒤 Dialog를 옮기세요. `ContentDialog`를 `Dialog`로 바꾸고, 개별 export의 접두어 `ContentDialog`를 `Dialog`로 바꾸세요(`ContentDialogRoot` → `DialogRoot`, `ContentDialogRootProps` → `DialogRootProps`). ```tsx import { ContentDialog } from "@seed-design/react"; // [!code --] import { Dialog } from "@seed-design/react"; // [!code ++] ``` 3. 1번에서 `Dialog.Root`나 `DialogRoot`를 옮긴 `AlertDialog.Root`, `AlertDialogRoot`마다 `role`과 `closeOnInteractOutside`를 확인하세요. snippet의 `AlertDialogRoot`는 동작이 바뀌지 않았으므로 대상이 아닙니다. - 값을 직접 넘기고 있었다면 그 값이 그대로 적용됩니다. 넘긴 값이 새 기본값(`"alertdialog"`, `false`)과 같다면 지워도 됩니다. - 값을 넘기지 않았다면 새 기본값이 적용되어, `role`이 `"alertdialog"`가 되고 바깥을 눌러도 닫히지 않습니다. 새 기본값은 snippet의 Alert Dialog와 같은 동작입니다. SEED React 2의 동작을 그대로 유지해야 한다면 두 값을 명시하세요. 어느 쪽이 의도인지 코드만으로 판단할 수 없다면 사용자에게 확인하세요. - 새 기본값을 따르기로 했는데 테스트에서 `getByRole("dialog")`처럼 role로 이 Alert Dialog를 찾고 있었다면, `"alertdialog"`로 바꾸세요. ```tsx // SEED React 2의 동작을 그대로 유지하는 경우 // [!code --] // [!code ++] ``` **클래스 이름이나 CSS 변수를 직접 참조하는 경우** 찾을 대상은 `seed-dialog__`, `seed-content-dialog__`, `--content-dialog-` 문자열입니다. CSS·SCSS 파일뿐 아니라 CSS-in-JS, `className` 문자열, `querySelector`, 테스트 selector와 snapshot에서도 찾으세요. `.seed-dialog__content--skipAnimation_false`, `.seed-content-dialog__content--size_large` 같은 variant 클래스도 같은 규칙으로 바뀝니다. 1. `.seed-dialog__*`를 `.seed-alert-dialog__*`로 모두 바꾸세요. ```css /* [!code --] */ .seed-dialog__content { /* [!code --] */ /* ... */ /* [!code --] */ } /* [!code ++] */ .seed-alert-dialog__content { /* [!code ++] */ /* ... */ /* [!code ++] */ } ``` 2. 1번을 모두 마친 뒤 `.seed-content-dialog__*`를 `.seed-dialog__*`로 바꾸세요. ```css /* [!code --] */ .seed-content-dialog__content { /* [!code --] */ /* ... */ /* [!code --] */ } /* [!code ++] */ .seed-dialog__content { /* [!code ++] */ /* ... */ /* [!code ++] */ } ``` 3. CSS 변수 `--content-dialog-default-width`, `--content-dialog-default-max-width`, `--content-dialog-size-width`를 각각 `--dialog-default-width`, `--dialog-default-max-width`, `--dialog-size-width`로 바꾸세요. **`@seed-design/css/recipes/*`에서 import하는 경우** 찾을 대상은 `@seed-design/css/recipes/dialog`와 `@seed-design/css/recipes/content-dialog`로 시작하는 import 경로입니다. JS 모듈과 CSS 파일(`.css`, `.layered.css`)을 모두 찾으세요. `@seed-design/css/all.css`처럼 전체 CSS를 import하고 있다면 CSS 파일 import는 바꾸지 않아도 됩니다. 1. Alert Dialog를 옮기세요. import 경로 `recipes/dialog`를 `recipes/alert-dialog`로 바꾸고, export 이름 `dialog`, `dialogVariantMap`, `DialogVariantProps`, `DialogSlotName`을 각각 `alertDialog`, `alertDialogVariantMap`, `AlertDialogVariantProps`, `AlertDialogSlotName`으로 바꾸세요. CSS 파일 `recipes/dialog.css`, `recipes/dialog.layered.css`는 `recipes/alert-dialog.css`, `recipes/alert-dialog.layered.css`로 바꾸세요. ```ts import { dialog } from "@seed-design/css/recipes/dialog"; // [!code --] import { alertDialog } from "@seed-design/css/recipes/alert-dialog"; // [!code ++] import "@seed-design/css/recipes/dialog.css"; // [!code --] import "@seed-design/css/recipes/alert-dialog.css"; // [!code ++] ``` 2. 1번을 모두 마친 뒤 Dialog를 옮기세요. import 경로 `recipes/content-dialog`를 `recipes/dialog`로 바꾸고, export 이름 `contentDialog`, `contentDialogVariantMap`, `ContentDialogVariantProps`, `ContentDialogSlotName`을 각각 `dialog`, `dialogVariantMap`, `DialogVariantProps`, `DialogSlotName`으로 바꾸세요. CSS 파일 `recipes/content-dialog.css`, `recipes/content-dialog.layered.css`는 `recipes/dialog.css`, `recipes/dialog.layered.css`로 바꾸세요. ```ts import { contentDialog } from "@seed-design/css/recipes/content-dialog"; // [!code --] import { dialog } from "@seed-design/css/recipes/dialog"; // [!code ++] import "@seed-design/css/recipes/content-dialog.css"; // [!code --] import "@seed-design/css/recipes/dialog.css"; // [!code ++] ``` **작업 후 확인** 타입 검사와 빌드가 통과해도 이 목록을 모두 확인하기 전에는 끝난 것이 아닙니다. 옮기지 않은 Alert Dialog는 대부분 에러 없이 통과합니다. - `@seed-design/react`의 `ContentDialog`, `@seed-design/css/recipes/content-dialog`, 클래스 이름 `seed-content-dialog__`, CSS 변수 `--content-dialog-`가 코드에 남아 있지 않습니다. - `legacy-alert-dialog-*`, `legacy-dialog-*`, `legacy-action-button-*` 백업 파일이 남아 있지 않습니다. - 타입 에러를 없애려고 `role`, `skipAnimation` prop이나 `role` 선언을 지운 곳이 없습니다. 작업 중 지운 곳이 있다면 그 사용처는 Alert Dialog이므로 `AlertDialog`로 옮기고 값을 되살리세요. - 작업 전에 Alert Dialog로 분류한 사용처는 모두 `AlertDialog`, `recipes/alert-dialog`, `.seed-alert-dialog__*`를 사용합니다. snippet 파일을 제외하고, 코드에 남은 `@seed-design/react`의 `Dialog`, `recipes/dialog`, `.seed-dialog__*`는 모두 Dialog로 분류한 사용처에서 옮긴 것입니다. - snippet을 import하는 코드(`ui/alert-dialog`, `ui/dialog` 경로의 import)가 바뀌지 않았습니다. - `@seed-design/react`의 `Dialog.Root`나 `DialogRoot`에서 옮긴 `AlertDialog.Root`, `AlertDialogRoot`마다 `role`과 `closeOnInteractOutside`를 확인했습니다. ##### `Chip.Root` 이름 변경 **수정 대상:** `@seed-design/react`의 `Chip.Root`·`ChipRoot` 또는 해당 Props 타입을 사용하는 코드입니다. `seed-design/ui/chip`의 snippet 컴포넌트만 사용한다면 이 항목을 건너뛰세요. \[에러로 발견 가능] 버튼과 선택 가능한 Chip을 구분하기 위해 기존 `Chip.Root`를 `Chip.Button`으로 변경했습니다. 버튼의 동작과 스타일은 그대로입니다. | 기존 `@seed-design/react` export | 변경할 export | | ------------------------------ | ------------------ | | `Chip.Root` | `Chip.Button` | | `Chip.RootProps` | `Chip.ButtonProps` | | `ChipRoot` | `ChipButton` | | `ChipRootProps` | `ChipButtonProps` | ##### Wheel Picker 스타일 참조 변경 **수정 대상:** 다음을 직접 참조하는 스타일시트·애플리케이션 코드입니다. 해당 항목이 없다면 이 항목을 건너뛰세요. - `@seed-design/css/recipes/wheel-picker-public` 모듈, `seed-wheel-picker-public*` 클래스, `--seed-wheel-picker-public-*` CSS 변수 - Date Picker·Time Picker 안쪽 휠의 클래스: `.seed-date-picker__wheelColumns`, `__wheelItem`, `__wheelScrollFog`, `__wheelSelectionIndicator`, `.seed-time-picker__columns`, `__scrollFog`, `__selectionIndicator` - `seed-wheel-picker__*` 클래스와 `--seed-wheel-picker-*` CSS 변수 \[직접 검색 필요] 위 import, 클래스, CSS 변수는 타입 검사만으로 모두 발견할 수 없습니다. 스타일시트와 애플리케이션 코드에서 직접 검색하세요. SEED React 2에서는 공개 Wheel Picker가 `wheel-picker-public` 스타일을, Date Picker·Time Picker 안쪽 휠이 `wheel-picker` 스타일과 각 컴포넌트의 휠 클래스를 사용했습니다. SEED React 3에서는 모든 휠이 `wheel-picker` 스타일 하나를 사용합니다. - `wheel-picker-public` 이름은 `public`을 지운 `wheel-picker` 이름으로 바꾸세요. - Date Picker·Time Picker 안쪽 휠의 클래스는 제거되었습니다. 휠은 이제 Wheel Picker로 렌더링되므로 `.seed-wheel-picker__*` 클래스를 기준으로 다시 작성하세요. - `seed-wheel-picker__*` 클래스와 `--seed-wheel-picker-*` CSS 변수는 SEED React 2에서 Date Picker·Time Picker 안쪽 휠에만 적용되었지만, SEED React 3에서는 모든 Wheel Picker에 적용됩니다. `public`을 지워 옮긴 선택자도 Date Picker·Time Picker snippet 안의 Wheel Picker에 적용되므로, 의도한 범위가 아니라면 선택자 범위를 좁히세요. ```ts import { wheelPickerPublic } from "@seed-design/css/recipes/wheel-picker-public"; // [!code --] import { wheelPicker } from "@seed-design/css/recipes/wheel-picker"; // [!code ++] ``` ```css .seed-wheel-picker-public__column { /* [!code --] */ padding-block: var(--seed-wheel-picker-public-center-offset); /* [!code --] */ } /* [!code --] */ .seed-wheel-picker__column { /* [!code ++] */ padding-block: var(--seed-wheel-picker-center-offset); /* [!code ++] */ } /* [!code ++] */ ``` ##### `$color.bg.layer-fill` 제거 **수정 대상:** `$color.bg.layer-fill`과 대응하는 CSS 변수·Tailwind 클래스·`vars` 참조·style prop 값을 직접 사용하는 코드입니다. 해당 코드가 없다면 이 항목을 건너뛰세요. \[직접 검색 필요] SEED React 1.2부터 deprecated 상태였던 `$color.bg.layer-fill`을 제거했습니다. 이 토큰은 2.0.0에서 제거했다가 대체 토큰이 없어 2.1.0에서 deprecated 상태로 다시 추가했으며, 이번에 같은 값(라이트 모드 `gray-100`, 다크 모드 `gray-200`)의 `$color.bg.neutral-muted`를 추가하면서 제거했습니다. `$color.bg.neutral-muted`로 교체하세요. 교체 전후 값이 같아 화면은 바뀌지 않습니다. | 기존 | 교체 후 | | -------------------------------- | ------------------------------- | | `--seed-color-bg-layer-fill` | `--seed-color-bg-neutral-muted` | | `bg-bg-layer-fill` (Tailwind) | `bg-bg-neutral-muted` | | `vars.$color.bg.layerFill` | `vars.$color.bg.neutralMuted` | | `bg="bg.layerFill"` (style prop) | `bg="bg.neutralMuted"` | ```tsx // [!code --] // [!code ++] ``` `vars.$color.bg.layerFill`은 타입 에러로 발견되지만, CSS 변수·Tailwind 클래스와 `bg`·`background` 같은 style prop 값은 임의 문자열을 허용하므로 에러 없이 배경색만 사라집니다. `layer-fill`과 `layerFill`을 문자열로 검색해 확인하세요. ##### deprecated 색상 토큰 교체 **수정 대상:** `$color.bg.neutral-inverted`, `$color.bg.neutral-inverted-pressed`, `$color.fg.neutral-inverted`와 대응하는 CSS 변수·Tailwind 클래스를 직접 사용하는 코드입니다. 해당 코드가 없다면 이 항목을 건너뛰세요. \[직접 검색 필요] 기존 토큰을 직접 사용했다면 아래 표와 같이 교체하세요. 교체 전후 값이 같아 화면은 바뀌지 않습니다. [`$color.bg.neutral-solid` 색상 변경](#1-colorbgneutral-solid-색상-변경)의 확인 목록을 먼저 만든 뒤 교체하세요. 교체한 사용처는 그 목록의 대상이 아닙니다. - `$color.bg.neutral-inverted`, `$color.bg.neutral-inverted-pressed`, `$color.fg.neutral-inverted`: `@seed-design/css@4.0.0`에서 제거될 예정입니다. - Solid 배경 위 전경색에는 해당 배경에 맞는 `$color.fg.on-*-solid`를 사용하는 것을 권장합니다. | 기존 토큰 | 대체 토큰 | CSS 변수 변경 | | ------------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------ | | `$color.bg.neutral-inverted` | `$color.bg.neutral-solid` | `--seed-color-bg-neutral-inverted` → `--seed-color-bg-neutral-solid` | | `$color.bg.neutral-inverted-pressed` | `$color.bg.neutral-solid-pressed` | `--seed-color-bg-neutral-inverted-pressed` → `--seed-color-bg-neutral-solid-pressed` | | `$color.fg.neutral-inverted` | `$color.fg.on-neutral-solid` | `--seed-color-fg-neutral-inverted` → `--seed-color-fg-on-neutral-solid` | ```tsx const classes = "bg-bg-neutral-inverted active:bg-bg-neutral-inverted-pressed text-fg-neutral-inverted"; // [!code --] const classes = "bg-bg-neutral-solid active:bg-bg-neutral-solid-pressed text-fg-on-neutral-solid"; // [!code ++] ``` ### 동작이 바뀌는 항목 기존 사용 코드를 수정할 필요는 없지만, 내부 동작 개선을 확인할 수 있는 항목입니다. #### Help Bubble의 포커스와 닫힘 동작 Help Bubble은 새 `Popover.Content`를 내부에서 사용하므로 코드를 수정할 필요가 없습니다. 다음 동작이 바뀌었으니, 관련 우회 코드나 포커스 이동에 의존하는 동작이 있다면 확인하세요. - 열릴 때 포커스를 옮기지 않는 기본 동작(`autoFocus={false}`)은 그대로입니다. 열릴 때 Help Bubble 안으로 포커스를 옮기려면 `autoFocus`를 지정하세요. - Help Bubble 안에 포커스가 있는 상태에서 닫히면 포커스가 trigger로 돌아갑니다. - 포커스가 Help Bubble 바깥으로 이동하면 Help Bubble이 닫히고, 포커스는 옮긴 요소에 그대로 남습니다. `closeOnInteractOutside={false}`이면 닫히지 않습니다. - Bottom Sheet·Dialog 안에서 연 Help Bubble을 Escape 키나 바깥 영역을 눌러 닫을 때 Bottom Sheet·Dialog까지 함께 닫히던 문제를 수정했습니다. - `HelpBubble.Title`·`HelpBubble.Description`은 이제 Help Bubble의 접근성 이름·설명으로 연결됩니다. - `role="dialog"`와 접근성 이름·설명 속성은 Positioner가 아닌 Content 요소(`.seed-help-bubble__content`)에 붙습니다. 테스트에서 role로 Help Bubble 요소를 찾아 위치나 속성을 검사했다면 확인하세요. #### Week Date Picker의 연·월 Wheel Picker `WeekDatePicker`에서 제목을 누르면, 달력 자리 대신 제목에 붙은 popover 안에 작은(`small`) Wheel Picker가 표시됩니다. - 제목을 다시 누르는 것 외에도 바깥 영역을 누르거나, `Escape` 키를 누르거나, 포커스가 popover 밖으로 이동하면 닫히며, 이때도 고른 연·월을 반영합니다. - Week가 두 달에 걸치면 제목과 Wheel Picker는 다음 달을 기준으로 표시합니다. 다음 달이 이동 가능한 월 범위 밖이면 이전 달을 표시합니다. - Wheel Picker 영역의 레이아웃이나 높이를 전제한 코드와 테스트가 있다면 확인하세요.