React

Upgrade Guides

SEED 패키지 버전을 올릴 때 호환성을 확인하고 안전하게 업그레이드하는 방법을 안내합니다.

SEED React 3으로 업그레이드하는 방법을 안내합니다. 신규 기능 등 전체 변경사항은 Changelog에서 확인할 수 있습니다.

이 가이드는 SEED React 2(2.x)에서 SEED React 3으로 업그레이드하는 경우를 기준으로 작성되었습니다. 2보다 낮은 버전을 사용 중이라면 SEED React 2 가이드를 따른 뒤 이 문서로 돌아오세요.

AI로 마이그레이션하기

AI 코딩 도구(예: Claude Code)에 아래 프롬프트를 붙여넣으면 이 가이드를 읽고 프로젝트를 점검한 뒤 코드 수정이 필요한 항목을 적용합니다. 코드 블록 오른쪽 위 버튼으로 복사할 수 있습니다.

  • 화면이 달라지는 디자인 변경 항목은 AI가 직접 결정하지 않고 확인이 필요한 목록으로 정리하도록 요청합니다.
  • 코드 수정이 필요한 항목은 Step 단위로 진행하며, 같은 Step 안의 항목은 subagent로 병렬 처리하도록 요청합니다.
마이그레이션 프롬프트
이 프로젝트의 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:<id>`를 사용해줘.
     이 옵션은 기존 파일을 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/css3.0.0
@seed-design/react3.0.0
@seed-design/stackflow2.0.6기능 변경 없이 @seed-design/css peer dependency 범위에 ^3.0.0을 추가했습니다.
@seed-design/tailwind3-plugin3.0.1npm에서 게시 취소된 3.0.0을 다시 쓸 수 없어 3.0.1로 배포합니다.
@seed-design/tailwind4-theme3.0.1npm에서 게시 취소된 3.0.0을 다시 쓸 수 없어 3.0.1로 배포합니다.
@seed-design/vite-plugin2.1.1기능 변경 없이 @seed-design/css peer dependency 범위에 ^3.0.0을 추가했습니다.
@seed-design/webpack-plugin2.1.1기능 변경 없이 @seed-design/css peer dependency 범위에 ^3.0.0을 추가했습니다.
@seed-design/rsbuild-plugin2.1.1기능 변경 없이 @seed-design/css peer dependency 범위에 ^3.0.0을 추가했습니다.
@seed-design/react-popover3.0.0headless 패키지(스타일 없이 상태와 동작만 제공하는 패키지)를 직접 설치해 사용하는 경우에만 해당합니다.
@seed-design/react-drawer3.0.0headless 패키지를 직접 설치해 사용하는 경우에만 해당합니다.
@seed-design/react-scale-feedback2.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 install @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을 쓰지 않는다면 이 의존성을 제거할 수 있습니다.

아래 명령에서 프로젝트에 설치하지 않은 항목은 빼고 실행하세요.

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

--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 이름 변경에 있습니다.

  • 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 이름 변경의 @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"의 로딩 인디케이터 색은 다크 모드에서 달라집니다.

const className = "bg-bg-neutral-solid text-palette-static-white"; 
const className = "bg-bg-neutral-solid text-fg-on-neutral-solid"; 

--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)
  • 이제 긴 라벨이 더 길게 표시될 수 있습니다. 기존처럼 한 줄 말줄임이 필요한 화면에서는 너비를 직접 지정하세요.
<Badge>길이가 긴 상태 라벨</Badge> 
<Badge style={{ maxWidth: "120px" }}>길이가 긴 상태 라벨</Badge> 

3. Content Scale 적용으로 일부 컴포넌트의 DOM 구조 변경

직접 판단 필요

아래 컴포넌트에 Content Scale을 적용했습니다.

  • Content Scale은 누르는 동안 배경은 그대로 두고 콘텐츠만 줄이는 효과입니다.
  • 이를 위해 각 컴포넌트의 root 요소 안에 콘텐츠를 감싸는 박스(<span class="seed-content-scale">)가 추가되었습니다.

적용한 컴포넌트는 다음과 같습니다.

  • 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
<!-- 변경 전 -->
<button class="seed-accordion__trigger">
  <div class="seed-accordion__body">…</div>
</button>

<!-- 변경 후 -->
<button class="seed-accordion__trigger seed-scale-feedback">
  <span class="seed-content-scale">
    <div class="seed-accordion__body">…</div>
  </span>
</button>
  • 박스가 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 문서를 참고해 아래 경우를 확인하세요.
    • 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 색상 토큰 교체를 먼저 적용하면 $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를 가져오도록 수정하세요.

import { typography } from "@seed-design/css/vars/component"; 
import { vars as typography } from "@seed-design/css/vars/component/typography"; 

컴포넌트의 vars를 import하는 경우

같은 값을 디자인 토큰(@seed-design/css/vars)에서 찾아 바꾸세요. 토큰을 참조하지 않는 값(maxWidth: "7.5rem" 등)은 필요한 값을 직접 지정하세요.

import { badge } from "@seed-design/css/vars/component"; 
import { vars } from "@seed-design/css/vars"; 

const minHeight = badge.sizeMedium.enabled.root.minHeight; 
const minHeight = vars.$dimension.x5; 
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에 높이를 지정하세요.
function Body({ children }: { children: React.ReactNode }) {
  const { shouldUseBottomSheet } = useResponsiveSidePanelContext(); 

  return (
    <ResponsiveSidePanelBody minHeight="x40">
    <ResponsiveSidePanelBody>
      {children}
      <Box {...(shouldUseBottomSheet && { minHeight: "x40" })}>{children}</Box>
    </ResponsiveSidePanelBody>
  );
}
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 옵션
<BottomSheet.Root nested>{children}</BottomSheet.Root> 
<BottomSheet.Root>{children}</BottomSheet.Root> 
<ResponsiveDialog.Root bottomSheetRootProps={{ nested: true }}>{children}</ResponsiveDialog.Root> 
<ResponsiveDialog.Root>{children}</ResponsiveDialog.Root> 
<ResponsiveSidePanel.Root bottomSheetRootProps={{ nested: true }}>{children}</ResponsiveSidePanel.Root> 
<ResponsiveSidePanel.Root>{children}</ResponsiveSidePanel.Root> 
Headless 컴포넌트·타입 import 교체

수정 대상: snippet 밖에서 직접 작성한 코드 중 다음에 해당하는 코드입니다. 이 import가 다시 설치한 snippet 내부에만 있었다면 이 항목을 건너뛰세요.

  • @seed-design/react/primitive를 import하는 코드: 반드시 수정하세요. 에러로 발견 가능
  • @seed-design/react-popover를 직접 사용하는 코드: Headless Popover를 사용한 경우를 따르세요. 직접 검색 필요
  • @seed-design/react-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
DisplayItemEntryAttachmentDisplayItemEntry
DisplayItemStatusDetailsAttachmentDisplayItemStatusDetails
FileEntryAttachmentInputFileEntry
FileStatusDetailsAttachmentInputFileStatusDetails
useFileUploadContextuseAttachmentInputContext
UseFileUploadContext, UseFileUploadReturnAttachmentInputContextValue
useAttachmentDisplayContextuseAttachmentDisplayContext
UseAttachmentDisplayContext, UseAttachmentDisplayReturnAttachmentDisplayContextValue
useSideNavigationContextuseSideNavigationContext
UseSideNavigationContextSideNavigationContextValue
import type { FileEntry } from "@seed-design/react/primitive"; 
import type { AttachmentInputFileEntry } from "@seed-design/react"; 

const files: FileEntry[] = []; 
const files: AttachmentInputFileEntry[] = []; 
컴포넌트를 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.RootCheckbox.Root.PrimitiveCheckbox.RootProps → Checkbox.RootPrimitiveProps
RadioGroup.ItemRadioGroup.Item.PrimitiveRadioGroup.ItemProps → RadioGroup.ItemPrimitiveProps
RadioGroup.Root (RadioGroup.Item과 함께 사용)RadioGroupField.Root.Primitive. @seed-design/react의 RadioGroup.Root는 선택 상태를 관리하지 않는 레이아웃 컴포넌트입니다.RadioGroup.RootProps → RadioGroupField.RootPrimitiveProps
RadioGroup.Root (ListRadioItem을 감싸는 용도)List.RadioRootRadioGroup.RootProps → List.RadioRootProps
Switch.RootSwitch.Root.PrimitiveSwitch.RootProps → Switch.RootPrimitiveProps
  • ListRadioItem을 감싼 Root는 사용처의 코드이므로 snippet을 다시 설치한 뒤에도 직접 교체하세요.
  • Checkbox.HiddenInput, RadioGroup.ItemHiddenInput, Switch.HiddenInput은 이름을 유지하고 @seed-design/react에서 가져오세요.
import { Checkbox } from "@seed-design/react/primitive"; 
import { Checkbox } from "@seed-design/react"; 
import { Checkmark } from "seed-design/ui/checkbox";

function CustomCheckbox({ children, ...props }: Checkbox.RootProps) { 
function CustomCheckbox({ children, ...props }: Checkbox.RootPrimitiveProps) { 
  return (
    <Checkbox.Root {...props}> // [!code --]
    <Checkbox.Root.Primitive {...props}> // [!code ++]
      {children}
      <Checkmark />
      <Checkbox.HiddenInput />
    </Checkbox.Root> // [!code --]
    </Checkbox.Root.Primitive> 
  );
}

Chip·List와 headless 컴포넌트를 asChild로 중첩한 코드도 위 import·.Primitive 교체 후 구조를 유지할 수 있습니다. Chip.Root는 Step 2의 Chip.Root 이름 변경에서 바꾸세요.

선택적으로 구조를 간소화하려면 Chip.Toggle·Chip.RadioItem과 List.CheckItem·List.RadioItem·List.SwitchItem을 참고하세요.

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 이름 변경은 @seed-design/react의 스타일이 적용된 컴포넌트에만 해당합니다.
npm install @seed-design/react-dialog
import { Dialog } from "@seed-design/react/primitive"; 
import { Dialog } from "@seed-design/react-dialog"; 
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 install @seed-design/react-popover@latest
import { Popover } from "@seed-design/react/primitive"; 
import { Popover } from "@seed-design/react-popover"; 

<Popover.Root>
  <Popover.Trigger>열기</Popover.Trigger>
  <Popover.Positioner aria-label="추가 정보"> // [!code --]
  <Popover.Positioner> // [!code ++]
    <Popover.Content aria-label="추가 정보"> // [!code ++]
      <p>내용</p>
    </Popover.Content> // [!code ++]
  </Popover.Positioner>
</Popover.Root>;

이번 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 컴포넌트입니다. 신규 컴포넌트이므로 기존 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 install @seed-design/react-image
import { Avatar, useAvatarContext } from "@seed-design/react/primitive"; 
import { Image, useImageContext } from "@seed-design/react-image"; 
기존대체
AvatarRoot / AvatarImage / AvatarFallbackImage.Root / Image.Content / Image.Fallback
Avatar.Root / Avatar.Image / Avatar.FallbackImage.Root / Image.Content / Image.Fallback
AvatarRootProps / AvatarImageProps / AvatarFallbackPropsImage.RootProps / Image.ContentProps / Image.FallbackProps
useAvatarContext / UseAvatarContextuseImageContext / 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
import { Tabs } from "@seed-design/react/primitive"; 
import { Tabs } from "@seed-design/react-tabs"; 
deprecated 컴포넌트와 snippet 제거

수정 대상: 아래 표의 export나 삭제된 스타일 모듈·클래스·snippet을 사용하는 코드입니다. 해당 항목이 없다면 이 항목을 건너뛰세요.

에러로 발견 가능

@seed-design/react에서 아래 컴포넌트와 해당 타입·namespace export를 제거했습니다. 기존 import를 대체 컴포넌트로 바꾸고, 사용하던 prop과 배치를 새 컴포넌트의 API에 맞게 조정하세요. ActionChip과 ControlChip은 Chip 문서의 마이그레이션 예시도 참고할 수 있습니다. 제거된 컴포넌트의 문서는 최신 문서 사이트에서 삭제되었으며, 기존 사용법은 SEED React 2 문서에서 확인할 수 있습니다.

제거된 export대체 컴포넌트
ActionChip, ActionChipPropsChip.Button variant="solid"
ControlChip, ControlChipBaseProps, ControlChipPropsChip.Toggle 또는 Chip.Button
ActionSheet namespace와 ActionSheetBackdrop, ActionSheetPositioner, ActionSheetContent, ActionSheetHeader, ActionSheetRoot, ActionSheetTitle, ActionSheetDescription, ActionSheetTrigger, ActionSheetList, ActionSheetItem, ActionSheetCloseButton; 각 이름의 Props 타입SwipeableMenuSheet
ExtendedActionSheet namespace와 ExtendedActionSheetBackdrop, ExtendedActionSheetPositioner, ExtendedActionSheetContent, ExtendedActionSheetFooter, ExtendedActionSheetHeader, ExtendedActionSheetRoot, ExtendedActionSheetTitle, ExtendedActionSheetTrigger, ExtendedActionSheetList, ExtendedActionSheetGroup, ExtendedActionSheetItem, ExtendedActionSheetCloseButton; 각 이름의 Props 타입SwipeableMenuSheet
Fab, FabProps, ExtendedFab, ExtendedFabPropsContextualFloatingButton
InlineBanner namespace와 InlineBannerCloseButton, InlineBannerContent, InlineBannerDescription, InlineBannerLink, InlineBannerRoot, InlineBannerTitle; 각 이름의 Props 타입PageBanner
LinkContent, LinkContentPropsActionButton variant="ghost"
Inline, InlineProps, Columns, Column, ColumnsProps, ColumnPropsHStack
Stack, StackPropsVStack (HStack·VStack는 유지)
import { ActionChip } from "@seed-design/react"; 
import { Chip } from "@seed-design/react"; 

<ActionChip size="medium">Label</ActionChip> 
<Chip.Button size="medium" variant="solid"><Chip.Label>Label</Chip.Label></Chip.Button> 

다음 항목을 직접 참조했다면 대체 컴포넌트의 스타일로 바꾸세요. 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-sheetui:swipeable-menu-sheet
ui:extended-action-sheetui:swipeable-menu-sheet
ui:control-chipui:chip
ui:inline-bannerui:page-banner

ui:error-state는 제거된 export를 import하지 않으므로 이 변경만으로 빌드가 실패하지는 않습니다. 다만 snippet 목록에서 제거되어 더 이상 CLI로 설치할 수 없으므로, 새로 구성하는 화면에는 ui:result-section을 사용하세요.

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
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을 직접 조합하세요.
npx @seed-design/cli@latest add ui:badge --on-diff backup
import { Badge, type BadgeProps } from "@seed-design/react"; 
import { Badge, type BadgeProps } from "seed-design/ui/badge"; 

<Badge tone="positive" variant="weak" size="medium">완료</Badge>

ImageFrameBadge는 별도 export로 유지되므로 import를 바꿀 필요가 없습니다. 새 prefix와 actionProps 사용법은 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도 함께 설치됩니다.

npx @seed-design/cli@latest add ui:date-picker ui:time-picker --on-diff backup
import { DatePicker, TimePicker } from "@seed-design/react"; 
import { DatePicker } from "seed-design/ui/date-picker"; 
import { TimePicker } from "seed-design/ui/time-picker"; 
  • 다음 컴포넌트와 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 이름 변경

수정 대상: 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 업데이트에서 다시 설치한 뒤 이 항목을 건너뛰세요.

에러 없이 스타일·동작 변경

이 항목은 한 작업에서 처음부터 끝까지 순서대로 처리하세요. 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 2SEED React 3
Alert Dialogsnippetui:alert-dialog의 AlertDialogRoot 등변경 없음
Alert DialogReact namespaceDialogAlertDialog
Alert DialogReact 개별 exportDialogRoot, DialogRootProps 등AlertDialogRoot, AlertDialogRootProps 등
Alert Dialogrecipes/* 모듈 경로@seed-design/css/recipes/dialog@seed-design/css/recipes/alert-dialog
Alert Dialogrecipes/* 모듈 exportdialog, dialogVariantMap, DialogVariantProps, DialogSlotNamealertDialog, alertDialogVariantMap, AlertDialogVariantProps, AlertDialogSlotName
Alert Dialogrecipes/* CSS 파일recipes/dialog.css, recipes/dialog.layered.cssrecipes/alert-dialog.css, recipes/alert-dialog.layered.css
Alert Dialog클래스 이름.seed-dialog__*.seed-alert-dialog__*
Dialogsnippetui:dialog의 DialogRoot 등변경 없음
DialogReact namespaceContentDialogDialog
DialogReact 개별 exportContentDialogRoot, ContentDialogRootProps 등DialogRoot, DialogRootProps 등
Dialogrecipes/* 모듈 경로@seed-design/css/recipes/content-dialog@seed-design/css/recipes/dialog
Dialogrecipes/* 모듈 exportcontentDialog, contentDialogVariantMap, ContentDialogVariantProps, ContentDialogSlotNamedialog, dialogVariantMap, DialogVariantProps, DialogSlotName
Dialogrecipes/* CSS 파일recipes/content-dialog.css, recipes/content-dialog.layered.cssrecipes/dialog.css, recipes/dialog.layered.css
Dialog클래스 이름.seed-content-dialog__*.seed-dialog__*
DialogCSS 변수--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의 기본값도 바뀌었습니다.

propSEED React 2 Dialog.RootSEED React 3 AlertDialog.Root
role"dialog""alertdialog"
closeOnInteractOutsidetruefalse

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 교체에 따라 이름을 유지한 채 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 업데이트에서 다시 설치한 ui/alert-dialog.tsx, ui/dialog.tsx는 이미 SEED React 3의 이름을 사용하므로 제외하세요.

  • namespace Dialog, ContentDialog
  • 개별 export Dialog<Part>, Dialog<Part>Props, ContentDialog<Part>, ContentDialog<Part>Props. <Part>는 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하던 파일이 있습니다. 이때 겹치는 쪽의 이름은 바꾸지 마세요.

import { Dialog } from "@seed-design/react"; 
import { AlertDialog as SeedAlertDialog } from "@seed-design/react"; 

export function AlertDialog(props: AlertDialogProps) {
  return <Dialog.Root {...props} />; 
  return <SeedAlertDialog.Root {...props} />; 
}
  1. Alert Dialog를 옮기세요. Dialog를 AlertDialog로 바꾸고, 개별 export의 접두어 Dialog를 AlertDialog로 바꾸세요(DialogRoot → AlertDialogRoot, DialogRootProps → AlertDialogRootProps). JSX와 타입에서 쓰는 Dialog.Root, Dialog.RootProps 등도 함께 바꾸세요.
import { Dialog } from "@seed-design/react"; 
import { AlertDialog } from "@seed-design/react"; 
  1. 1번을 모두 마친 뒤 Dialog를 옮기세요. ContentDialog를 Dialog로 바꾸고, 개별 export의 접두어 ContentDialog를 Dialog로 바꾸세요(ContentDialogRoot → DialogRoot, ContentDialogRootProps → DialogRootProps).
import { ContentDialog } from "@seed-design/react"; 
import { Dialog } from "@seed-design/react"; 
  1. 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"로 바꾸세요.
// SEED React 2의 동작을 그대로 유지하는 경우
<Dialog.Root> // [!code --]
<AlertDialog.Root role="dialog" closeOnInteractOutside> // [!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__*로 모두 바꾸세요.
.seed-dialog__content {
  /* ... */
}
.seed-alert-dialog__content {
  /* ... */
}
  1. 1번을 모두 마친 뒤 .seed-content-dialog__*를 .seed-dialog__*로 바꾸세요.
.seed-content-dialog__content {
  /* ... */
}
.seed-dialog__content {
  /* ... */
}
  1. 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로 바꾸세요.
import { dialog } from "@seed-design/css/recipes/dialog"; 
import { alertDialog } from "@seed-design/css/recipes/alert-dialog"; 
import "@seed-design/css/recipes/dialog.css"; 
import "@seed-design/css/recipes/alert-dialog.css"; 
  1. 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로 바꾸세요.
import { contentDialog } from "@seed-design/css/recipes/content-dialog"; 
import { dialog } from "@seed-design/css/recipes/dialog"; 
import "@seed-design/css/recipes/content-dialog.css"; 
import "@seed-design/css/recipes/dialog.css"; 

작업 후 확인

타입 검사와 빌드가 통과해도 이 목록을 모두 확인하기 전에는 끝난 것이 아닙니다. 옮기지 않은 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.RootChip.Button
Chip.RootPropsChip.ButtonProps
ChipRootChipButton
ChipRootPropsChipButtonProps
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에 적용되므로, 의도한 범위가 아니라면 선택자 범위를 좁히세요.
import { wheelPickerPublic } from "@seed-design/css/recipes/wheel-picker-public"; 
import { wheelPicker } from "@seed-design/css/recipes/wheel-picker"; 
.seed-wheel-picker-public__column { 
  padding-block: var(--seed-wheel-picker-public-center-offset); 
} 
.seed-wheel-picker__column { 
  padding-block: var(--seed-wheel-picker-center-offset); 
} 
$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.layerFillvars.$color.bg.neutralMuted
bg="bg.layerFill" (style prop)bg="bg.neutralMuted"
<Box bg="bg.layerFill" /> 
<Box bg="bg.neutralMuted" /> 

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 색상 변경의 확인 목록을 먼저 만든 뒤 교체하세요. 교체한 사용처는 그 목록의 대상이 아닙니다.

  • $color.bg.neutral-inverted, $color.bg.neutral-inverted-pressed, $color.fg.neutral-inverted: @seed-design/[email protected]에서 제거될 예정입니다.
  • 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
const classes = "bg-bg-neutral-inverted active:bg-bg-neutral-inverted-pressed text-fg-neutral-inverted"; 
const classes = "bg-bg-neutral-solid active:bg-bg-neutral-solid-pressed text-fg-on-neutral-solid"; 

동작이 바뀌는 항목

기존 사용 코드를 수정할 필요는 없지만, 내부 동작 개선을 확인할 수 있는 항목입니다.

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 영역의 레이아웃이나 높이를 전제한 코드와 테스트가 있다면 확인하세요.

Last updated on

목차