@yeongseoksong/framework 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,383 @@
1
+ # @yeongseoksong/framework
2
+
3
+ Mantine 9 기반 공유 UI 컴포넌트 라이브러리. Next.js App Router 환경에서 사용하도록 설계되었습니다.
4
+
5
+ ## 설치
6
+
7
+ ```bash
8
+ pnpm add @yeongseoksong/framework
9
+ ```
10
+
11
+ 피어 의존성 설치:
12
+
13
+ ```bash
14
+ pnpm add @mantine/core @mantine/hooks @mantine/carousel react react-dom next
15
+ ```
16
+
17
+ ## 설정
18
+
19
+ ### 1. MantineProvider + theme
20
+
21
+ ```tsx
22
+ // app/layout.tsx
23
+ import '@mantine/core/styles.css'
24
+ import '@mantine/carousel/styles.css'
25
+ import { MantineProvider } from '@mantine/core'
26
+ import { theme } from '@yeongseoksong/framework/ui'
27
+
28
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
29
+ return (
30
+ <html lang="ko">
31
+ <body>
32
+ <MantineProvider theme={theme}>
33
+ {children}
34
+ </MantineProvider>
35
+ </body>
36
+ </html>
37
+ )
38
+ }
39
+ ```
40
+
41
+ ### 2. 회사명 설정
42
+
43
+ `%c` 토큰을 회사명으로 치환합니다. 앱 진입점에서 한 번만 호출합니다.
44
+
45
+ ```ts
46
+ import { setCompanyName } from '@yeongseoksong/framework/util'
47
+
48
+ setCompanyName('내 회사')
49
+ ```
50
+
51
+ ## 임포트 경로
52
+
53
+ | 경로 | 내용 |
54
+ |---|---|
55
+ | `@yeongseoksong/framework/ui` | UI 컴포넌트 전체 + `theme` (`"use client"`) |
56
+ | `@yeongseoksong/framework/util` | `t()`, `setCompanyName()` |
57
+ | `@yeongseoksong/framework/types` | 공유 인터페이스 |
58
+
59
+ ---
60
+
61
+ ## 사용 예시
62
+
63
+ ### SdButton
64
+
65
+ ```tsx
66
+ import { SdButton } from '@yeongseoksong/framework/ui'
67
+
68
+ // 주요 액션
69
+ <SdButton.Primary onClick={handleSubmit}>저장</SdButton.Primary>
70
+
71
+ // 보조 액션
72
+ <SdButton.Outline onClick={handleCancel}>취소</SdButton.Outline>
73
+
74
+ // 텍스트 수준
75
+ <SdButton.Ghost>더 보기</SdButton.Ghost>
76
+
77
+ // 다크 배경 위
78
+ <SdButton.White>시작하기</SdButton.White>
79
+
80
+ // 삭제 (휴지통 아이콘 자동 포함)
81
+ <SdButton.Delete onClick={handleDelete}>삭제</SdButton.Delete>
82
+
83
+ // 취소 (X 아이콘 자동 포함)
84
+ <SdButton.Cancel onClick={handleClose}>닫기</SdButton.Cancel>
85
+ ```
86
+
87
+ ### SdText / SdTitle
88
+
89
+ ```tsx
90
+ import { SdText, SdTitle } from '@yeongseoksong/framework/ui'
91
+
92
+ <SdTitle.Display>히어로 제목</SdTitle.Display> {/* h2, 대형 */}
93
+ <SdTitle.Section>섹션 제목</SdTitle.Section> {/* h3 */}
94
+ <SdTitle.Card>카드 제목</SdTitle.Card> {/* h4 */}
95
+ <SdTitle.Sub>소제목</SdTitle.Sub> {/* h5 */}
96
+
97
+ <SdText.Strong>강조 텍스트</SdText.Strong>
98
+ <SdText.Body>본문 텍스트입니다.</SdText.Body>
99
+ <SdText.Sub>보조 설명</SdText.Sub>
100
+ <SdText.Eyebrow>LABEL</SdText.Eyebrow> {/* 대문자 레이블 */}
101
+ <SdText.Numeric>1,234</SdText.Numeric> {/* 숫자 표기 */}
102
+ <SdText.Error>필수 항목입니다.</SdText.Error>
103
+ <SdText.Hint>최대 100자까지 입력 가능합니다.</SdText.Hint>
104
+
105
+ {/* %c → setCompanyName()으로 설정한 회사명으로 치환됨 */}
106
+ <SdText.Body>%c 서비스에 오신 것을 환영합니다.</SdText.Body>
107
+ ```
108
+
109
+ ### SdTextBox
110
+
111
+ 레이블 + 제목 + 설명을 묶는 섹션 헤더 컴포넌트입니다.
112
+
113
+ ```tsx
114
+ import { SdTextBox } from '@yeongseoksong/framework/ui'
115
+
116
+ // 히어로 섹션
117
+ <SdTextBox.Hero
118
+ label="NEW"
119
+ title="더 나은 서비스를 경험하세요"
120
+ description="빠르고 안정적인 플랫폼으로 업무 효율을 높여보세요."
121
+ />
122
+
123
+ // 일반 섹션
124
+ <SdTextBox.Section
125
+ label="기능"
126
+ title="핵심 기능 소개"
127
+ description="다양한 기능을 통해 더 스마트하게 일하세요."
128
+ />
129
+
130
+ // 카드 내부
131
+ <SdTextBox.Card title="카드 제목" description="카드 설명" />
132
+ ```
133
+
134
+ ### SdTabs
135
+
136
+ ```tsx
137
+ import { SdTabs } from '@yeongseoksong/framework/ui'
138
+
139
+ // Pills 스타일
140
+ <SdTabs.Pills defaultValue="tab1">
141
+ <SdTabs.Pills.List>
142
+ <SdTabs.Pills.Tab value="tab1">소개</SdTabs.Pills.Tab>
143
+ <SdTabs.Pills.Tab value="tab2">기능</SdTabs.Pills.Tab>
144
+ </SdTabs.Pills.List>
145
+
146
+ <SdTabs.Pills.Panel value="tab1">소개 내용</SdTabs.Pills.Panel>
147
+ <SdTabs.Pills.Panel value="tab2">기능 내용</SdTabs.Pills.Panel>
148
+ </SdTabs.Pills>
149
+
150
+ // Underline 스타일
151
+ <SdTabs.Underline defaultValue="tab1">
152
+ {/* 동일한 구조 */}
153
+ </SdTabs.Underline>
154
+ ```
155
+
156
+ ### SdTable
157
+
158
+ ```tsx
159
+ import { SdTable } from '@yeongseoksong/framework/ui'
160
+
161
+ // 기본 테이블
162
+ <SdTable>
163
+ <SdTable.Thead>
164
+ <SdTable.Tr>
165
+ <SdTable.Th>이름</SdTable.Th>
166
+ <SdTable.Th>상태</SdTable.Th>
167
+ </SdTable.Tr>
168
+ </SdTable.Thead>
169
+ <SdTable.Tbody>
170
+ <SdTable.Tr>
171
+ <SdTable.Td>홍길동</SdTable.Td>
172
+ <SdTable.Td>활성</SdTable.Td>
173
+ </SdTable.Tr>
174
+ </SdTable.Tbody>
175
+ </SdTable>
176
+
177
+ // 스펙 강조 테이블 (primary 헤더)
178
+ <SdTable.Spec>
179
+ <SdTable.Spec.Thead>
180
+ <SdTable.Spec.Tr>
181
+ <SdTable.Spec.Th>항목</SdTable.Spec.Th>
182
+ <SdTable.Spec.Th>값</SdTable.Spec.Th>
183
+ </SdTable.Spec.Tr>
184
+ </SdTable.Spec.Thead>
185
+ <SdTable.Spec.Tbody>
186
+ <SdTable.Spec.Tr>
187
+ <SdTable.Spec.Td>CPU</SdTable.Spec.Td>
188
+ <SdTable.Spec.Td>8코어</SdTable.Spec.Td>
189
+ </SdTable.Spec.Tr>
190
+ </SdTable.Spec.Tbody>
191
+ </SdTable.Spec>
192
+ ```
193
+
194
+ ### SdModal
195
+
196
+ ```tsx
197
+ 'use client'
198
+ import { useDisclosure } from '@mantine/hooks'
199
+ import { SdModal, SdButton } from '@yeongseoksong/framework/ui'
200
+
201
+ export function Example() {
202
+ const [opened, { open, close }] = useDisclosure()
203
+
204
+ return (
205
+ <>
206
+ <SdButton.Primary onClick={open}>열기</SdButton.Primary>
207
+
208
+ <SdModal opened={opened} onClose={close} title="제목">
209
+ <SdModal.Body>모달 내용입니다.</SdModal.Body>
210
+ </SdModal>
211
+ </>
212
+ )
213
+ }
214
+ ```
215
+
216
+ ### SdFaq
217
+
218
+ ```tsx
219
+ import { SdFaq } from '@yeongseoksong/framework/ui'
220
+ import type { FaqItem } from '@yeongseoksong/framework/types'
221
+
222
+ const faqs: FaqItem[] = [
223
+ { question: '무료 체험이 가능한가요?', answer: '네, 14일 무료 체험을 제공합니다.' },
224
+ { question: '결제 수단은 어떻게 되나요?', answer: '카드 결제를 지원합니다.' },
225
+ ]
226
+
227
+ // 아코디언만
228
+ <SdFaq.Default items={faqs} />
229
+
230
+ // 헤더 + 아코디언
231
+ <SdFaq.WithHeader
232
+ label="FAQ"
233
+ title="자주 묻는 질문"
234
+ description="궁금한 점이 있으시면 언제든지 문의해 주세요."
235
+ items={faqs}
236
+ />
237
+ ```
238
+
239
+ ### SdPricingCard
240
+
241
+ ```tsx
242
+ import { SdPricingCard } from '@yeongseoksong/framework/ui'
243
+ import type { PricingItem } from '@yeongseoksong/framework/types'
244
+
245
+ const plans: PricingItem[] = [
246
+ {
247
+ name: '스타터',
248
+ price: '무료',
249
+ description: '소규모 팀을 위한 플랜',
250
+ features: [
251
+ { text: '프로젝트 3개', included: true },
252
+ { text: '팀원 5명', included: true },
253
+ { text: '고급 분석', included: false },
254
+ ],
255
+ ctaLabel: '무료로 시작',
256
+ },
257
+ {
258
+ name: '프로',
259
+ price: '₩29,000',
260
+ period: '월',
261
+ description: '성장하는 팀을 위한 플랜',
262
+ isPopular: true, // → SdPricingCard.Grid에서 Featured 스타일 자동 적용
263
+ features: [
264
+ { text: '프로젝트 무제한', included: true },
265
+ { text: '팀원 무제한', included: true },
266
+ { text: '고급 분석', included: true },
267
+ ],
268
+ ctaLabel: '시작하기',
269
+ },
270
+ ]
271
+
272
+ // 그리드 (isPopular 항목은 자동으로 Featured 스타일)
273
+ <SdPricingCard.Grid items={plans} onSelect={(plan) => console.log(plan)} />
274
+
275
+ // 개별 카드
276
+ <SdPricingCard.Default item={plans[0]} onSelect={handleSelect} />
277
+ <SdPricingCard.Featured item={plans[1]} onSelect={handleSelect} />
278
+ ```
279
+
280
+ ### SdClients
281
+
282
+ ```tsx
283
+ import { SdClients } from '@yeongseoksong/framework/ui'
284
+ import type { ClientItem } from '@yeongseoksong/framework/types'
285
+
286
+ const clients: ClientItem[] = [
287
+ { name: '회사 A', url: 'https://example.com', logo: '/logos/a.svg' },
288
+ { name: '회사 B', url: 'https://example.com', logo: '/logos/b.svg' },
289
+ ]
290
+
291
+ // 반응형 그리드
292
+ <SdClients.Grid items={clients} />
293
+
294
+ // 무한 마키 스크롤 (호버 시 일시정지)
295
+ <SdClients.Marquee items={clients} speed={40} />
296
+ ```
297
+
298
+ ### SdHeader / SdFooter
299
+
300
+ ```tsx
301
+ import { SdHeader, SdFooter } from '@yeongseoksong/framework/ui'
302
+ import type { NavItem, CompanyInfo } from '@yeongseoksong/framework/types'
303
+
304
+ const navItems: NavItem[] = [
305
+ { id: 1, order: 1, isShow: true, label: '소개', href: '/about' },
306
+ { id: 2, order: 2, isShow: true, label: '기능', href: '/features' },
307
+ { id: 3, order: 3, isShow: true, label: '요금제', href: '/pricing' },
308
+ ]
309
+
310
+ const company: CompanyInfo = {
311
+ name: '내 회사',
312
+ registrationNumber: '000-00-00000',
313
+ addresses: [{ label: '본사', address: '서울시 강남구', order: 1 }],
314
+ tel: '02-0000-0000',
315
+ email: 'hello@example.com',
316
+ copyrightYear: 2024,
317
+ }
318
+
319
+ <SdHeader navItems={navItems} loginFlag />
320
+ <SdFooter company={company} utilityLinks={navItems} />
321
+ ```
322
+
323
+ ### MainLayout
324
+
325
+ 헤더 + 본문 + 푸터가 포함된 전체 레이아웃입니다.
326
+
327
+ ```tsx
328
+ import { MainLayout } from '@yeongseoksong/framework/ui'
329
+
330
+ export default function Page() {
331
+ return (
332
+ <MainLayout navItems={navItems} companyInfo={company}>
333
+ <main>페이지 내용</main>
334
+ </MainLayout>
335
+ )
336
+ }
337
+ ```
338
+
339
+ ### t() — 회사명 치환
340
+
341
+ ```ts
342
+ import { t, setCompanyName } from '@yeongseoksong/framework/util'
343
+
344
+ setCompanyName('내 회사')
345
+
346
+ t('%c 서비스') // → '내 회사 서비스'
347
+ t('%c에 오신 것을 환영합니다') // → '내 회사에 오신 것을 환영합니다'
348
+ ```
349
+
350
+ ---
351
+
352
+ ## 타입
353
+
354
+ ```ts
355
+ import type {
356
+ NavItem, // 네비게이션 메뉴
357
+ HeroSlide, // 히어로 캐러셀 슬라이드
358
+ FeatureItem, // 기능 카드
359
+ TimelineEvent, // 연혁 타임라인
360
+ SolutionItem, // 솔루션 카드
361
+ StepItem, // 단계별 안내
362
+ TestimonialItem, // 고객 후기
363
+ PricingItem, // 요금제 플랜
364
+ PricingFeature, // 요금제 항목
365
+ FaqItem, // FAQ
366
+ ClientItem, // 고객사 로고
367
+ CompanyInfo, // 회사 정보 전체
368
+ CompanyAddress, // 회사 주소
369
+ } from '@yeongseoksong/framework/types'
370
+ ```
371
+
372
+ ---
373
+
374
+ ## 피어 의존성
375
+
376
+ | 패키지 | 버전 |
377
+ |---|---|
378
+ | `@mantine/core` | ^9.2.2 |
379
+ | `@mantine/hooks` | ^9.2.2 |
380
+ | `@mantine/carousel` | ^9.2.2 |
381
+ | `next` | 16.2.2 |
382
+ | `react` | 19.2.4 |
383
+ | `react-dom` | 19.2.4 |
@@ -30,10 +30,17 @@ interface NavItem extends CommonInfo {
30
30
  highlight?: boolean;
31
31
  parentId?: number;
32
32
  }
33
+ interface HeroCta {
34
+ label: string;
35
+ href: string;
36
+ variant?: 'primary' | 'secondary' | 'outline' | 'white';
37
+ icon: boolean;
38
+ }
33
39
  interface HeroSlide extends CommonInfo {
34
40
  image: string;
35
41
  title: ReactNode;
36
42
  description: string;
43
+ ctas?: HeroCta[];
37
44
  }
38
45
  interface FeatureItem extends CommonInfo {
39
46
  icon?: ReactNode;
@@ -105,4 +112,4 @@ interface SolutionItem extends CommonInfo {
105
112
  icon?: ReactNode;
106
113
  }
107
114
 
108
- export { type ClientItem, type CommonInfo, type CompanyAddress, type CompanyInfo, type FaqItem, type FeatureItem, type HeroSlide, type NavItem, type PricingFeature, type PricingItem, type SolutionItem, type StepItem, type TestimonialItem, type TimelineEvent, ceoMessage, clientItems, companyInfo, faqItems, featureItems, heroSlides, navItems, pricingItems, serviceItems, solutionItems, testimonialItems, timelineEvents };
115
+ export { type ClientItem, type CommonInfo, type CompanyAddress, type CompanyInfo, type FaqItem, type FeatureItem, type HeroCta, type HeroSlide, type NavItem, type PricingFeature, type PricingItem, type SolutionItem, type StepItem, type TestimonialItem, type TimelineEvent, ceoMessage, clientItems, companyInfo, faqItems, featureItems, heroSlides, navItems, pricingItems, serviceItems, solutionItems, testimonialItems, timelineEvents };
@@ -30,10 +30,17 @@ interface NavItem extends CommonInfo {
30
30
  highlight?: boolean;
31
31
  parentId?: number;
32
32
  }
33
+ interface HeroCta {
34
+ label: string;
35
+ href: string;
36
+ variant?: 'primary' | 'secondary' | 'outline' | 'white';
37
+ icon: boolean;
38
+ }
33
39
  interface HeroSlide extends CommonInfo {
34
40
  image: string;
35
41
  title: ReactNode;
36
42
  description: string;
43
+ ctas?: HeroCta[];
37
44
  }
38
45
  interface FeatureItem extends CommonInfo {
39
46
  icon?: ReactNode;
@@ -105,4 +112,4 @@ interface SolutionItem extends CommonInfo {
105
112
  icon?: ReactNode;
106
113
  }
107
114
 
108
- export { type ClientItem, type CommonInfo, type CompanyAddress, type CompanyInfo, type FaqItem, type FeatureItem, type HeroSlide, type NavItem, type PricingFeature, type PricingItem, type SolutionItem, type StepItem, type TestimonialItem, type TimelineEvent, ceoMessage, clientItems, companyInfo, faqItems, featureItems, heroSlides, navItems, pricingItems, serviceItems, solutionItems, testimonialItems, timelineEvents };
115
+ export { type ClientItem, type CommonInfo, type CompanyAddress, type CompanyInfo, type FaqItem, type FeatureItem, type HeroCta, type HeroSlide, type NavItem, type PricingFeature, type PricingItem, type SolutionItem, type StepItem, type TestimonialItem, type TimelineEvent, ceoMessage, clientItems, companyInfo, faqItems, featureItems, heroSlides, navItems, pricingItems, serviceItems, solutionItems, testimonialItems, timelineEvents };
@@ -36,7 +36,6 @@ __export(types_exports, {
36
36
  module.exports = __toCommonJS(types_exports);
37
37
 
38
38
  // types/example.tsx
39
- var import_jsx_runtime = require("react/jsx-runtime");
40
39
  var navItems = [
41
40
  { id: 1, order: 1, isShow: true, label: "\uC18C\uAC1C", href: "/about" },
42
41
  { id: 11, order: 1, isShow: true, label: "\uD68C\uC0AC\uC18C\uAC1C", href: "/about/company", parentId: 1 },
@@ -60,27 +59,25 @@ var heroSlides = [
60
59
  },
61
60
  {
62
61
  id: 2,
63
- order: 3,
62
+ order: 1,
64
63
  isShow: true,
65
- image: "https://images.unsplash.com/photo-1531973576160-7125cd663d86?w=1600&q=80",
66
- title: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
67
- "\uC790\uC0B0\uC744 \uD55C\uB208\uC5D0,",
68
- " ",
69
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\uC2A4\uB9C8\uD2B8 \uC790\uC0B0 \uAD00\uB9AC" })
70
- ] }),
71
- description: "\uC7A5\uBE44\xB7\uC124\uBE44\xB7IT \uC790\uC0B0\uC758 \uC218\uBA85 \uC8FC\uAE30\uB97C \uC790\uB3D9\uC73C\uB85C \uCD94\uC801\uD558\uACE0 \uC720\uC9C0\uBCF4\uC218 \uC77C\uC815\uC744 \uAD00\uB9AC\uD558\uC138\uC694."
64
+ image: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=1600&q=80",
65
+ title: "\uC5C5\uBB34\uB97C \uB354 \uC2A4\uB9C8\uD2B8\uD558\uAC8C, %c",
66
+ description: "ASM\uACFC PIMS\uB85C \uC790\uC0B0 \uAD00\uB9AC\uBD80\uD130 \uD504\uB85C\uC81D\uD2B8 \uC6B4\uC601\uAE4C\uC9C0 \u2014 \uD558\uB098\uC758 \uD50C\uB7AB\uD3FC\uC73C\uB85C \uC5F0\uACB0\uD558\uC138\uC694.",
67
+ ctas: [
68
+ { label: "\uBB34\uB8CC \uCCB4\uD5D8 \uC2DC\uC791", href: "/pricing", icon: false },
69
+ // variant 생략 → primary
70
+ { label: "\uB3C4\uC785 \uBB38\uC758\uD558\uAE30", href: "/contact", variant: "secondary", icon: true }
71
+ ]
72
72
  },
73
73
  {
74
74
  id: 2,
75
- order: 4,
75
+ order: 2,
76
76
  isShow: true,
77
- image: "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=1600&q=80",
78
- title: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
79
- "\uD504\uB85C\uC81D\uD2B8 \uB0A9\uAE30\uB97C",
80
- " ",
81
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\uC9C0\uD0A4\uB294 \uC720\uC77C\uD55C \uBC29\uBC95" })
82
- ] }),
83
- description: "WBS\xB7\uC77C\uC815\xB7\uB9AC\uC18C\uC2A4\xB7\uBE44\uC6A9\uC744 \uD55C \uD654\uBA74\uC5D0\uC11C \uAD00\uB9AC\uD558\uACE0 AI\uAC00 \uB9AC\uC2A4\uD06C\uB97C \uC0AC\uC804\uC5D0 \uAC10\uC9C0\uD569\uB2C8\uB2E4."
77
+ image: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=1600&q=80",
78
+ title: "\uC790\uC0B0\uC744 \uD55C\uB208\uC5D0, \uC2A4\uB9C8\uD2B8 \uC790\uC0B0 \uAD00\uB9AC",
79
+ description: "\uC7A5\uBE44\xB7\uC124\uBE44\xB7IT \uC790\uC0B0\uC758 \uC218\uBA85 \uC8FC\uAE30\uB97C \uC790\uB3D9\uC73C\uB85C \uCD94\uC801\uD558\uACE0 \uC720\uC9C0\uBCF4\uC218 \uC77C\uC815\uC744 \uAD00\uB9AC\uD558\uC138\uC694."
80
+ // ctaLabel 없으면 버튼 미노출
84
81
  }
85
82
  ];
86
83
  var featureItems = [
@@ -1,5 +1,4 @@
1
1
  // types/example.tsx
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
2
  var navItems = [
4
3
  { id: 1, order: 1, isShow: true, label: "\uC18C\uAC1C", href: "/about" },
5
4
  { id: 11, order: 1, isShow: true, label: "\uD68C\uC0AC\uC18C\uAC1C", href: "/about/company", parentId: 1 },
@@ -23,27 +22,25 @@ var heroSlides = [
23
22
  },
24
23
  {
25
24
  id: 2,
26
- order: 3,
25
+ order: 1,
27
26
  isShow: true,
28
- image: "https://images.unsplash.com/photo-1531973576160-7125cd663d86?w=1600&q=80",
29
- title: /* @__PURE__ */ jsxs(Fragment, { children: [
30
- "\uC790\uC0B0\uC744 \uD55C\uB208\uC5D0,",
31
- " ",
32
- /* @__PURE__ */ jsx("span", { children: "\uC2A4\uB9C8\uD2B8 \uC790\uC0B0 \uAD00\uB9AC" })
33
- ] }),
34
- description: "\uC7A5\uBE44\xB7\uC124\uBE44\xB7IT \uC790\uC0B0\uC758 \uC218\uBA85 \uC8FC\uAE30\uB97C \uC790\uB3D9\uC73C\uB85C \uCD94\uC801\uD558\uACE0 \uC720\uC9C0\uBCF4\uC218 \uC77C\uC815\uC744 \uAD00\uB9AC\uD558\uC138\uC694."
27
+ image: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=1600&q=80",
28
+ title: "\uC5C5\uBB34\uB97C \uB354 \uC2A4\uB9C8\uD2B8\uD558\uAC8C, %c",
29
+ description: "ASM\uACFC PIMS\uB85C \uC790\uC0B0 \uAD00\uB9AC\uBD80\uD130 \uD504\uB85C\uC81D\uD2B8 \uC6B4\uC601\uAE4C\uC9C0 \u2014 \uD558\uB098\uC758 \uD50C\uB7AB\uD3FC\uC73C\uB85C \uC5F0\uACB0\uD558\uC138\uC694.",
30
+ ctas: [
31
+ { label: "\uBB34\uB8CC \uCCB4\uD5D8 \uC2DC\uC791", href: "/pricing", icon: false },
32
+ // variant 생략 → primary
33
+ { label: "\uB3C4\uC785 \uBB38\uC758\uD558\uAE30", href: "/contact", variant: "secondary", icon: true }
34
+ ]
35
35
  },
36
36
  {
37
37
  id: 2,
38
- order: 4,
38
+ order: 2,
39
39
  isShow: true,
40
- image: "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=1600&q=80",
41
- title: /* @__PURE__ */ jsxs(Fragment, { children: [
42
- "\uD504\uB85C\uC81D\uD2B8 \uB0A9\uAE30\uB97C",
43
- " ",
44
- /* @__PURE__ */ jsx("span", { children: "\uC9C0\uD0A4\uB294 \uC720\uC77C\uD55C \uBC29\uBC95" })
45
- ] }),
46
- description: "WBS\xB7\uC77C\uC815\xB7\uB9AC\uC18C\uC2A4\xB7\uBE44\uC6A9\uC744 \uD55C \uD654\uBA74\uC5D0\uC11C \uAD00\uB9AC\uD558\uACE0 AI\uAC00 \uB9AC\uC2A4\uD06C\uB97C \uC0AC\uC804\uC5D0 \uAC10\uC9C0\uD569\uB2C8\uB2E4."
40
+ image: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=1600&q=80",
41
+ title: "\uC790\uC0B0\uC744 \uD55C\uB208\uC5D0, \uC2A4\uB9C8\uD2B8 \uC790\uC0B0 \uAD00\uB9AC",
42
+ description: "\uC7A5\uBE44\xB7\uC124\uBE44\xB7IT \uC790\uC0B0\uC758 \uC218\uBA85 \uC8FC\uAE30\uB97C \uC790\uB3D9\uC73C\uB85C \uCD94\uC801\uD558\uACE0 \uC720\uC9C0\uBCF4\uC218 \uC77C\uC815\uC744 \uAD00\uB9AC\uD558\uC138\uC694."
43
+ // ctaLabel 없으면 버튼 미노출
47
44
  }
48
45
  ];
49
46
  var featureItems = [