@uniai-fe/uds-templates
v0.4.25
Published
UNIAI Design System; UI Templates Package
Readme
@uniai-fe/uds-templates
@uniai-fe/uds-foundation 토큰과 @uniai-fe/uds-primitives 기초 컴포넌트를 조합해,
서비스 앱에서 자주 반복되는 화면/플로우 템플릿을 제공하는 패키지입니다.
Next.js 서비스에서 primitives와 동일한 방식으로 Raw TypeScript 형태로 소비되며,
pnpm transpilePackages 설정을 통해 빌드 파이프라인에 포함됩니다.
Peer Dependencies
@uniai-fe/uds-foundation@^0.0.1(CSS 토큰 + reset)@uniai-fe/uds-primitives@^0.0.2(템플릿 내부 UI 컴포넌트)react>= 19,react-dom>= 19- Storybook/로컬 개발용으로는
@uniai-fe/uds-foundation/css를 앱 루트에서 한 번만 import 해야 합니다.
템플릿은 primitives 위에서 동작하므로,
@uniai-fe/uds-primitives가 설치되어 있지 않으면 빌드 타임에 peer dependency 경고가 발생합니다.
특징
- 화면 단위 템플릿:
- 로그인/회원가입/아이디·비밀번호 찾기 등
/auth/** - 로그인 이후 화면을 위한 모바일 페이지 프레임
/page-frame/mobile(PC는 추후/page-frame/pc)
- 로그인/회원가입/아이디·비밀번호 찾기 등
- Raw TS 배포:
package.jsonexports가src/**를 가리키며, 번들 없이 즉시 import 가능한 형태로 제공합니다.
- 디자인시스템 일관성:
- 스타일은 항상
@uniai-fe/uds-foundation/css에서 제공하는 CSS 변수(토큰)를 통해만 정의됩니다. - UI 요소는 모두
@uniai-fe/uds-primitives컴포넌트를 조합해 구성합니다.
- 스타일은 항상
- 역할 분리:
- templates는 레이아웃/플로우/상태 표현까지 담당하고,
- API 호출, 인증 토큰 관리, 라우팅, i18n 등 비즈니스 로직은 서비스 앱에서 구현합니다.
확인 완료 도구 목록
/modalModal.ProviderModal.StackProviderModal.RouteResetModal.useModalModal.AlertModal.DialogcreateAlertModalcreateDialogModalmodalStackAtomModalStateModalStatePatchModalPropsModalFooterButtonAlertTemplateOptionsDialogTemplateOptionsUseModalReturn
/weatherWeatherComponents.PageHeaderWeatherPageHeaderContainerWeatherMockProviderweatherCoordinateuseWeatherKoreauseOpenWeatherMap
/service-inquiryServiceInquiry.FormServiceInquiry.OpenButtonServiceInquiry.NavButtonServiceInquiry.useOpenServiceInquiry.useProvideContextServiceInquiry.useUserContextServiceInquiry.createModalServiceInquiryFieldModeServiceInquiryFormPropsServiceInquiryFormValuesServiceInquiryProvidedContext
/cctvCCTV.ProviderCCTV.CamList.ContainerCCTV.Video.ContainerCCTV.Video.ContentsCCTV.Video.Overlay.ContainerCCTV.Pagination.ContainerCCTV.Pagination.List.ContainerCCTV.Pagination.Button.PrevCCTV.Pagination.Button.NextCCTV.Viewer.Desktop.ContaineruseCctvCompanyDatauseCctvContextuseCctvRtcStreamgetServerCompanyListgetServerCctvTokenpostCctvRtcToken
/page-frameFrameFrame.MobileMobileFrameFrame.DesktopDesktopFrameMobileFramePropsPageFrameDesktopNavPropsSitemapDataType
현재 제공 템플릿/모듈
/auth/**- 로그인 화면 템플릿 (Login)
- 회원가입 플로우 템플릿 (Sign Up, step 기반)
- 아이디 찾기 템플릿
- 비밀번호 찾기/재설정 템플릿
/page-frame/**/page-frame/mobile: 로그인 이후 화면을 위한 모바일 프레임(header/body/footer)/page-frame/pc: 추후 확장 예정
/modal/**- ui-legacy 스택 기반 모달 Provider/Root/Container + 템플릿(
Modal.Alert,Modal.Dialog) - Storybook(
apps/design-storybook/src/stories/templates/modal)에서 Alert/Confirm 케이스를 검증한다.
- ui-legacy 스택 기반 모달 Provider/Root/Container + 템플릿(
/service-inquiry/**- 문의 입력 전용 form, 기본 원형
?open button, page-frame nav button, 커스텀 trigger용 open hook, 페이지 context 등록 hook, request context 조립 hook, 네트워크 오류 수집 hook, modal preset factory를 제공한다. - 현재 form 기본 구조는
이름,연락처,문의 유형,자세한 문의 내용4필드이며,사진 첨부는 구현 범위에서 제외한다. - submit transport, React Query mutation, Next.js route handler, 에러 피드백(
Modal.Alert)은 서비스 앱이 소유한다. - 모듈 내부 Jotai registry를 사용하므로, layout 고정 버튼 1개와 페이지별 context 등록 hook 조합으로 ready-to-use 구성이 가능하다.
stackKey는 필요할 때만 override하고,useOpen계열은 기본적으로 현재 pathname 기준${pathname}/inquiry를 사용한다.- modal description은 기본으로
"사용 중 문제가 있나요? 아래 내용을 적어 문의해 주세요."를 사용하고 특수 문구가 필요할 때만dialogOptions.description으로 덮어쓴다. - 로그인 후
farm_name,contact를 auto-fill + readonly로 보여야 할 때는formContextOptions.defaultValues와farmNameField.mode,contactField.mode를 함께 전달한다.
- 문의 입력 전용 form, 기본 원형
/weather/**- page-frame header utility에 결합되는 weather header 템플릿과 weather data hook/mock 도구를 제공한다.
/cctv/**- finder/viewer/video/pagination 조합과 rtc/company-list API helper를 제공한다.
/page-frame/**- mobile/desktop private route frame, nav/header/popup 조합을 제공한다.
각 템플릿의 상세한 범위와 의사결정은 CONTEXT-*.md 문서에서 관리합니다.
Service Inquiry 도입 흐름
- 서비스 앱이
react-hook-form으로defaultValues와onSubmit을 준비한다. - layout 고정 버튼은
ServiceInquiry.useUserContext()로 모듈 내부 registry 기반requestContext를 읽는다. - 서비스 앱은 네트워크 오류가 발생했을 때
ServiceInquiry.useNetworkError().reportNetworkError(...)만 호출하고, 모듈이 이를user_context.network_errors에 자동 병합한다. - 각 페이지/폼은
ServiceInquiry.useProvideContext({ labels, userContext })로ServiceInquiryProvidedContext를 등록한다. - 비로그인 기본 버튼은
ServiceInquiry.OpenButton, 로그인 후 page-frame 진입 버튼은ServiceInquiry.NavButton, 그 외 커스텀 버튼은ServiceInquiry.useOpen으로 모달 open을 연결한다. - modal footer confirm이
ServiceInquiry.Formsubmit 진입을 담당한다. - 실제 submit은 서비스 앱의
useMutation + Next.js route handler + Modal.Alert조합으로 처리한다.
service-inquiry는 구조와 request context까지만 제공하고, 네트워크 상태/재시도/성공·실패 피드백은 서비스 앱이 소유합니다.
회원가입 Step 구조
- Step1 — User Info (name + phone): 기본 정보 입력. PhoneInput은 인증 UI 없이 마스킹만 제공한다.
- Step2 — Verify & Agreement: 약관 동의 + EmailInput(인증요청/타이머/OneTimeCode). alert/confirm는 서비스 앱이 직접 처리한다.
- Step3 — Generate Account: username/password/confirm + password 조건 helper.
- Step4 — Complete: 승인 대기 안내. CTA 명칭은 “Complete”만 사용한다.
세부 props/스토리 구성은 docs/CONTEXT-SIGNUP.md, docs/CONTEXT-SIGNUP-FLOW.md, docs/STORYBOOK.md에서 확인하고 변경 시 세 문서를 동시에 업데이트한다.
설치 & 기본 설정(Next.js 예시)
pnpm add @uniai-fe/uds-foundation @uniai-fe/uds-primitives @uniai-fe/uds-templates1) transpilePackages 설정
// next.config.ts
const nextConfig = {
transpilePackages: [
"@uniai-fe/uds-foundation",
"@uniai-fe/uds-primitives",
"@uniai-fe/uds-templates",
],
};
export default nextConfig;2) 스타일 전역 주입
템플릿은 primitives 위에 구성되므로, 앱 루트에서 @uniai-fe/uds-templates/styles(Sass) 또는 @uniai-fe/uds-templates/css(번들 CSS) 중 하나를 한 번만 import 하면 foundation 토큰, primitives, templates 스타일이 모두 초기화됩니다.
/* app/globals.scss (Sass 사용 프로젝트) */
@use "@uniai-fe/uds-templates/styles";// app/layout.tsx (Sass 비사용 프로젝트)
import "@uniai-fe/uds-templates/css";
export default function RootLayout(props: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>{props.children}</body>
</html>
);
}간단 사용 예시
// app/(auth)/login/page.tsx
import { AuthLoginTemplate } from "@uniai-fe/uds-templates/auth";
export default function LoginPage() {
return (
<AuthLoginTemplate
text={{
title: "로그인",
description: "서비스 이용을 위해 로그인해 주세요.",
}}
logoSlot={<img src="/logo.svg" alt="서비스 로고" />}
onSubmit={async (values) => {
// 실제 로그인 API 호출은 서비스 앱에서 구현
// await login(values);
}}
/>
);
}위 예시는 개념을 설명하기 위한 형태이며, 실제 props 구조/이름은
CONTEXT-AUTH.md에서 확정·관리합니다.
modules 레포 내부(Storybook 등)에서는 개발 편의를 위해
@uniai-fe/uds-templates/styles엔트리를 import하지만, 외부 서비스/패키지는@uniai-fe/uds-templates/css엔트리만 사용해야 한다.
최근 업데이트
- Modal CSS 변수 네임스페이스를
--modal-*,--modal-alert-*,--modal-dialog-*세 축으로 통일했다. 기존--dialog-*이름을 사용하지 말고, Auth 템플릿이 Alert을 호출할 때에는 반드시Modal.Alert팩토리를 통해 렌더링한다. - Auth(회원가입/아이디 찾기/비밀번호 찾기) 템플릿은 이메일 인증 타이머 5분, alert 문구, readonly 이메일 필드, 완료 단계 헤더 제거 등 최신 플로우를 반영했다. 모듈에서
window.alert/window.confirm을 호출하지 않고, 서비스 앱이 Modal helper를 주입하는 구조를 README와CONTEXT-AUTH*.md에 명시했다.
토큰 스코프 & ThemeProvider
- templates SCSS는 모든 디자인 토큰을
:root에 선언하고, 빌드 시scripts/merge-theme-root.mjs가 토큰 블록을 단일:root { ... }로 합쳐 중복 선언을 제거합니다. - 서비스 앱은 foundation ThemeProvider가 주입하는
.uds-theme-root를 루트에 유지해야 하며, CSS import 순서는@uniai-fe/uds-foundation/css→@uniai-fe/uds-primitives/css→@uniai-fe/uds-templates/css입니다. - modules 레포(Storybook 등)는 SCSS 원본을 직접 사용하지만, 외부 프로젝트는 CSS 엔트리만 import한다는 규칙을 README/CODEX-RULES에 명시하고 있습니다.
Modal 모듈 사용법
ui-legacy에서 사용하던 모달 스택/옵션을 templates 레이어로 옮겨왔습니다. 핵심 흐름은 다음과 같습니다.
- Provider 1회 장착 — 서비스 레이아웃에서
<Modal.Provider />를 렌더합니다. - Route Reset 장착 —
<Modal.RouteReset />을 Provider와 같은 레벨에서 렌더해 경로 변경 시 스택을 초기화합니다. - 훅 사용 —
const { newModal, closeModal, hasBlockingModal } = Modal.useModal(); - 템플릿 팩토리 —
Modal.Alert,Modal.Dialog이ModalState객체를 반환하므로newModal(...)에 그대로 전달합니다.
// app/layout.tsx
import "@uniai-fe/uds-templates/styles";
import { Modal } from "@uniai-fe/uds-templates/modal";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body>
<>
{children}
<Modal.Provider />
<Modal.RouteReset />
</>
</body>
</html>
);
}// 예: Alert/Confirm 호출
import { Modal } from "@uniai-fe/uds-templates/modal";
export function ExampleActions() {
const { newModal } = Modal.useModal();
const openAlert = () =>
newModal(
Modal.Alert({
stackKey: "sample/alert",
message: "필수 항목 동의에 체크해 주세요.",
confirm: { label: "확인" },
}),
);
const openConfirm = () =>
newModal(
Modal.Dialog({
stackKey: "sample/confirm",
title: "저장하시겠어요?",
content: "입력한 내용이 저장되며 되돌릴 수 없습니다.",
confirm: {
label: "저장",
onClick: () => {
// 서비스 동작
},
},
cancel: { label: "취소" },
}),
);
return (
<>
<button onClick={openAlert}>Alert</button>
<button onClick={openConfirm}>Confirm</button>
</>
);
}- footer 버튼은 ui-legacy와 동일하게
stackKey,role,defaultOptions/linkOptions구조로 관리하며, Alert(Text)/Dialog(Solid) 규격은 templates 내부에서 보장합니다. closeOnOutsideClick?: boolean으로 overlay 클릭 닫힘을 제어할 수 있습니다. 기본값은true입니다.preventRouteChange?: boolean은 현재 열린 modal 중 route 변경 확인이 필요한 modal이 있는지 집계할 때 사용합니다. 서비스 앱은hasBlockingModal을 읽어 공통 confirm UX를 연결할 수 있습니다.- Route Reset을 누락하면 이전 라우트의 모달 스택이 그대로 남으므로, layout 수준에서 Provider와 함께 반드시 렌더합니다.
- 세부 가드레일·확장 기록은
CONTEXT-MODAL.md를 참고하고, 새 템플릿을 추가할 때 해당 문서를 선행 업데이트합니다.
Scripts
패키지 루트에서 실행 가능한 대표 명령:
pnpm module:lint— ESLint 전역 규칙 점검pnpm module:typecheck— TS 타입 검사 (tsconfig.build.json기준)pnpm module:build— lint → typecheck 순으로 배포 전 검증pnpm design-templates:dev— 타입 검사 watch 모드
모노레포 루트에서는 pnpm --filter @uniai-fe/uds-templates <command> 또는 pnpm modules:build(turbo run)으로 전체 패키지를 빌드할 수 있습니다.
문서
CONTEXT.md— templates 패키지 전체 상황/진행 현황 인덱스CONTEXT-GUIDELINES.md— templates 전역 규칙(슬롯/반응형/page-frame 적용 범위 등)CONTEXT-AUTH.md—/auth/**템플릿 범위/디자인 근거/Figma 링크CONTEXT-SIGNUP.md,CONTEXT-SIGNUP-FLOW.md— 회원가입 단계/훅/스토리 전략 및 Figma node 표CONTEXT-PAGE-FRAME.md—/page-frame/**템플릿 범위/디자인 근거FOUNDATION-USAGE-GUIDE.md— 템플릿에서 design-foundation 토큰을 사용하는 규칙PRIMITIVES-USAGE-GUIDE.md— 템플릿에서 design-primitives를 사용하는 규칙STORYBOOK.md— templates Storybook 가이드(로그인/회원가입/페이지 프레임 시나리오)
