Skip to content

소비 포털 Ant Design 전환 인수인계서 ​

문서 목적 ​

이 문서는 UiPlatform에서 수행한 Ant Design 전환 작업을 다음 소비 포털에 반복 적용하기 위한 기준서입니다. 소비 포털은 공통 UI 라이브러리인 portal-foundation을 사용하므로, 개별 앱에서 임시 CSS를 추가하는 방식보다 공통 Shell 계약과 Ant Design Layout 구조를 먼저 확인해야 합니다.

핵심 원칙: 메뉴 이벤트는 명시적으로 연결하고, Shell의 폭·높이·grid 영역은 구조적으로 보장하며, 소비 포털에서는 Foundation 내부 레이아웃을 CSS override로 덮지 않습니다.

현재까지 확정된 변경 사항 ​

영역결정 사항적용 위치
UI 프레임워크Ant Design 6.x 기반으로 전환portal-foundation, 소비 포털
메뉴Ant Design Menu의 onClick과 leaf item onClick을 모두 연결PortalShell, navigation adapter
Shell 구조Layout → Header + body grid → Sider + Content 구조 사용PortalShell
브랜드좌측 전체 높이 Sider의 상단에 배치하고 접힘 상태에서 함께 축소PortalSidebar
사이드바로고, 메뉴, 접기 버튼, 역할 전환을 하나의 rail로 담당PortalSidebar
콘텐츠 영역PortalMain은 grid의 두 번째 열을 명시적으로 차지PortalMain
SSRPortal/Modal 계열 컴포넌트는 client mount 이후 동작PortalDialog, PortalExploreDialog
Listdeprecated Ant Design List 대신 PortalList adapter 사용Foundation components
공개 설정build-time 환경변수보다 runtime JSON/API 우선UiPlatform runtime config

가장 중요한 장애 원인 ​

Ant Design 전환 뒤 메뉴와 오른쪽 콘텐츠가 보이지 않는 현상이 발생했습니다. 최종 원인은 이벤트 누락 하나가 아니라, Shell의 실제 computed layout이 잘못 계산된 것이었습니다.

Chrome DevTools에서 .pf-shell__body를 확인한 결과는 다음과 같았습니다.

text
display: grid
width: 248px
grid-template-columns: 248px 0px
grid-template-rows: 972px 0px

사이드바 폭만 남고 Content 열이 0px가 되었기 때문에 오른쪽 콘텐츠가 DOM에는 존재하지만 시각적으로 표시되지 않았습니다. 이 문제는 단순히 background, z-index, display를 덮는 것으로 해결하면 안 됩니다. Root Layout과 body grid가 전체 viewport 폭을 갖도록 구조에서 보장해야 합니다.

표준 Shell 레이아웃 계약 ​

다음 구조를 모든 소비 포털에서 기준으로 사용합니다.

text
PortalShell root
└── Ant Design Layout: row flex, width 100vw, min-height 100vh
    ├── PortalSidebar / Sider: 248px, 화면 전체 높이
    │   ├── 브랜드 로고와 제품명
    │   ├── 접기 버튼
    │   └── 메뉴 및 역할 전환
    └── workspace Layout: column flex, 남은 폭
        ├── PortalTopNav / Header: 검색 및 사용자 메뉴
        └── PortalMain / Content: 남은 높이

필수 레이아웃 계약은 다음과 같습니다.

요소필수 조건
Root Layoutdisplay: flex, flex-direction: column, width: 100vw, min-width: 0, min-height: 100vh
Body Layoutdisplay: grid, width: 100%, min-width: 0, grid-template-columns: 248px minmax(0, 1fr)
Sidebar첫 번째 grid 영역, 기본 폭 248px
Main Contentgrid-column: 2, min-width: 0, overflow: auto
Main Innerwidth: 100%, min-height: 100%, box-sizing: border-box

width: 248px, grid-template-columns: 248px 0px, height: 0px, visibility: hidden, opacity: 0가 발견되면 먼저 부모 Layout의 computed width와 grid track을 확인합니다.

브랜드와 레일 배치 기준 ​

로고와 제품명은 좌측 전체 높이 Sider의 상단에 배치합니다. 이 구조에서 Sider는 단순한 메뉴 영역이 아니라 하나의 navigation rail입니다. Rail을 접으면 로고, 제품명, 접기 버튼, 메뉴가 동일한 접힘 상태를 공유해야 합니다.

tsx
<Sider>
  <BrandMark />
  <strong>{productName}</strong>
  <CollapseButton />
  <Menu items={items} />
</Sider>
<Layout>
  <Header>
    <Search />
    <AccountMenu />
  </Header>
  <Content />
</Layout>

제품명은 Foundation의 productName, 로고는 productMark를 사용합니다. 소비 포털이 별도 로고를 제공하더라도 PortalShell의 topNavStart 슬롯을 우선 사용하고, Shell 내부에서 동일 브랜드를 다시 그리지 않도록 합니다.

메뉴 이벤트 체크리스트 ​

Ant Design Menu로 변환하는 adapter에서는 다음 두 경로를 모두 검증해야 합니다.

경로검증 내용
Leaf item각 item에 key와 onClick 또는 상위 Menu의 key handler가 존재하는지 확인
Group child그룹 하위 item의 id가 Ant Design key로 보존되는지 확인
Shell callback선택 시 onSelect, onNavigate, 소비 앱의 state setter가 호출되는지 확인
Active state소비 앱의 activeNavigationId가 선택된 key와 동일한지 확인
Mobilemobile sidebar에서도 동일한 선택 callback이 호출되고 닫히는지 확인

메뉴 클릭 테스트는 단순히 DOM click 성공 여부만 보지 말고, 클릭 전후의 active id와 콘텐츠 제목을 검증해야 합니다.

소비 포털 적용 순서 ​

첫째, 소비 포털의 portal-foundation 버전과 실제 설치된 tarball 또는 registry package의 버전을 확인합니다. lockfile의 integrity와 패키지 내용이 일치하지 않으면 배포 빌드가 실패하거나 이전 bundle이 서비스될 수 있습니다.

둘째, PortalShell에 전달하는 navigationItems, activeNavigationId, mainHeader, topNavStart, topNavEnd, userProfile을 확인합니다. 특히 소비 앱이 메뉴 id를 자체 tab id로 변환하는 경우 id 변환 표를 먼저 작성해야 합니다.

셋째, 소비 앱의 레거시 Shell CSS를 제거합니다. .pf-shell__body, .pf-shell-main, .ant-layout, .ant-layout-content에 대한 display, width, height, position, overflow override는 Foundation 계약과 충돌할 수 있습니다.

넷째, 데스크톱과 모바일에서 실제 브라우저 computed style을 확인합니다. React DOM에 콘텐츠가 존재한다는 사실만으로 렌더링 성공을 판단하지 않습니다.

다섯째, 배포 후 HTML이 참조하는 JS/CSS asset hash를 로컬 build 결과와 비교합니다. Git commit이 최신이어도 관리형 클라우드 또는 CDN이 이전 asset을 반환하면 검증 결과가 달라집니다.

검증 명령 ​

bash
pnpm install --frozen-lockfile --prefer-offline
pnpm run check
pnpm run test
pnpm run build
git diff --check

배포 asset 확인:

bash
curl -sS https://<consumer-portal>/ -o /tmp/portal.html
# HTML에서 /assets/index-*.js와 /assets/index-*.css 추출
curl -sS https://<consumer-portal>/assets/index-<hash>.js -o /tmp/portal.js
curl -sS https://<consumer-portal>/assets/index-<hash>.css -o /tmp/portal.css

브라우저 검증 기준 ​

최소한 다음 시나리오를 수행합니다.

시나리오기대 결과
기본 메뉴 클릭active 메뉴가 바뀌고 오른쪽 콘텐츠 제목이 바뀜
Portal Foundation Preview 클릭Preview 화면이 표시됨
Public Runtime Settings 클릭설정 화면이 표시됨
사이드바 접기메뉴가 축소되고 Content 폭이 남은 공간으로 유지됨
브라우저 새로고침동일한 active route 또는 초기 화면이 정상 표시됨
모바일 메뉴 열기overlay와 mobile Sider가 표시되고 선택 후 닫힘
테마 변경콘텐츠와 Header의 foreground/background가 함께 변경됨

실패 시 진단 순서 ​

  1. DOM 존재 여부를 확인합니다. 접근성 트리에 콘텐츠가 없으면 React 조건 분기나 state를 확인합니다.
  2. Root width를 확인합니다. Root가 viewport 전체를 차지하지 않으면 body의 minmax(0, 1fr)가 0px가 될 수 있습니다.
  3. Body grid track을 확인합니다. grid-template-columns의 두 번째 값이 0px인지 확인합니다.
  4. Content width/height를 확인합니다. width, height, min-width, min-height, overflow를 확인합니다.
  5. Visibility를 확인합니다. display, visibility, opacity, color, background-color를 확인합니다.
  6. Asset version을 확인합니다. staging이 최신 commit의 JS/CSS hash를 반환하는지 확인합니다.
  7. 그 후에만 코드 수정을 합니다. 임시 !important override는 원인 확인 전 사용하지 않습니다.

금지할 접근 ​

접근문제
소비 앱에 .pf-shell__body { display: grid !important } 추가Foundation과 소비 앱의 책임 경계가 무너짐
Content가 안 보일 때 흰색 배경만 변경0px grid track 문제를 해결하지 못함
vendor tarball을 수정하고 lockfile을 갱신하지 않음관리형 클라우드 frozen install에서 integrity 오류 발생
배포 성공 로그만 보고 검증 완료 처리이전 asset cache가 남아 있을 수 있음
DOM 텍스트 추출만으로 시각 검증 완료 처리width/height/visibility 문제를 놓칠 수 있음
제품 로고를 Header와 Sider에 동시에 렌더링Ant Design shell에서 브랜드 위치가 어긋남

변경 파일 및 참고 위치 ​

파일역할
vendor/hohyun94-portal-foundation-<ver>.tgzUiPlatform이 사용하는 Foundation package. 버전 기준은 package.json dependencies 다(현재 0.6.156)
client/src/pages/Home.tsx소비 앱 메뉴와 콘텐츠 state 연결
client/src/runtimeConfig.tsruntime JSON/API 설정 로더
client/public/config/ui-center.json정적 fallback runtime 설정
client/src/components/RuntimeConfigManager.tsx관리자용 공개 설정 편집 UI
drizzle/schema.tsMySQL runtime config schema
drizzle/schema.pg.tsPostgreSQL runtime config schema

인수인계 시 확인할 정보 ​

다음 소비 포털을 전환할 때 아래 정보를 문서에 기록합니다.

항목기록 내용
포털 이름소비 포털의 서비스명과 저장소
Foundation 버전package 버전, registry 또는 tarball 출처
메뉴 id 표기존 route/tab id와 Ant Design key의 대응
브랜드Header에 사용할 productName과 productMark
runtime 설정JSON fallback, API endpoint, 관리자 저장 위치
인증로그인 callback과 초기 loading 조건
배포build command, start command, asset cache 정책
검증 결과check/test/build 및 브라우저 시나리오 결과
잔여 위험모바일, SSR, theme, Modal focus 등 미확인 항목

현재 UiPlatform의 기준 커밋 ​

현재까지의 주요 기준 커밋은 다음과 같습니다.

커밋내용
2e61097Portal Foundation Ant Design migration과 SSR/List 개선 병합
d684198CSS patch의 재현성 개선
5b624a6PortalMain Content 영역 명시
ce7da7aAnt Design shell grid 구조 명시
cb937b3Root와 body full-width 보장

다음 소비 포털은 cb937b3 이후의 Foundation 적용 결과를 기준으로 시작하고, 좌우 우선 레이아웃과 Sider rail 브랜드 배치를 함께 적용해야 합니다.

References ​