@ihab_giihan/core
v0.1.36
Published
Shared package extracted from the speechear project. It contains UI components, shared styles, and common app-level utilities.
Readme
@ihab_giihan/core
Shared package extracted from the speechear project. It contains UI components, shared styles, and common app-level utilities.
개요
이 패키지는 React 기반 UI 컴포넌트와 공용 스타일을 제공합니다. 외부 프로젝트에서 사용할 때는 몇 가지 사용 규칙을 반드시 따라야 합니다 — 아래 **"외부 프로젝트에서 꼭 맞춰야 하는 사용 방식"**을 먼저 읽어주세요.
📦 외부 프로젝트 첫 도입 가이드: CONSUMING.md — 설치/peerDeps, import 경로, 미디어 리졸버 주입, 디자인시스템 vs
app/*구분, i18n 공유 방법.📚 디자인 시스템 & 개발 시스템 상세 레퍼런스: docs/design-system-reference.md — 토큰(색상·타이포·간격·그림자·z-index), 컴포넌트 47/공개 export 79, 훅 26 카탈로그.
⚠️ BREAKING (다음 메이저): 공개 경로가
@ihab_giihan/core/Example/*→@ihab_giihan/core/app/*로 변경되었습니다.
Build
- From repository root:
cd packages/core
npm install
npm run build빌드 결과는 packages/core/dist에 생성됩니다.
외부 프로젝트에서 꼭 맞춰야 하는 사용 방식
- React 인스턴스 중복 금지:
react와react-dom은 peer dependency입니다. 외부 프로젝트에 동일한 React 인스턴스가 하나만 설치되어 있어야 합니다. 문제가 의심되면npm ls react로 확인하세요. - CSS 임포트 위치와 범위: 컴포넌트 스타일(또는 토큰)이 적용되려면 적절한 시점에 CSS를 import 해야 합니다. 보통 앱 진입점(
src/main.tsx또는 Next.js의_app.tsx)에서 한 번만 import 합니다. CoreScope또는 클래스 적용: 패키지 스타일은.ihab-core-scope범위 내에서 동작하도록 작성되어 있습니다. 따라서 컴포넌트가 렌더되는 DOM을 반드시CoreScope로 감싸거나 루트 요소에ihab-core-scope클래스를 적용해야 합니다. 예:
import '@ihab_giihan/core/styles.css'
import { CoreScope } from '@ihab_giihan/core'
export default function App(){
return (
<CoreScope>
{/* @ihab_giihan/core 컴포넌트들 */}
</CoreScope>
)
}또는 CoreScope를 사용하지 않는 경우:
import '@ihab_giihan/core/styles.css'
export default function App(){
return (
<div className="ihab-core-scope">
{/* 컴포넌트들 */}
</div>
)
}CSS 사용 옵션 (권장 방식)
styles.css(권장): 전체 토큰(커스텀 프로퍼티)과 컴포넌트 스타일이 포함된 파일입니다. 토큰은:where(.ihab-core-scope){...}에 선언되므로CoreScope로 감싼 영역에서만 적용됩니다. 토큰과 컴포넌트 스타일 모두 필요하면styles.css만 import 하세요.components.css(경량): 컴포넌트별 스타일만 포함한 경량 CSS입니다. 이 파일만 import 하면 컴포넌트 룩앤필은 적용되지만 토큰(색상/간격 등)이 필요하면 별도로styles.css를 함께 import 해야 합니다.
예시:
// 전체 토큰 + 컴포넌트 스타일(권장)
import '@ihab_giihan/core/styles.css'
// 또는 컴포넌트 스타일만 (토큰은 별도)
import '@ihab_giihan/core/components.css'
import '@ihab_giihan/core/styles.css' // 토큰이 필요하면 함께 importSubpath imports
패키지는 기능별로 subpath를 제공합니다. 예:
import { CoreScope, Button } from '@ihab_giihan/core'
import { Button as CompButton } from '@ihab_giihan/core/components'
import { useMediaQuery } from '@ihab_giihan/core/hooks'
import { speak } from '@ihab_giihan/core/utils'컴포넌트 전용 엔트리를 사용할 때는 @ihab_giihan/core/components.css를 사용하여 컴포넌트 스타일만 로드할 수 있습니다.
컴포넌트 사용 예시
모든 예시는
CoreScope또는ihab-core-scope클래스 안에서 렌더링되어야 합니다.
Button
import { Button } from '@ihab_giihan/core'
// label prop 방식
<Button variant="default_main" label="저장" onClick={() => {}} />
// children 방식 (label과 동일, label이 있으면 label 우선)
<Button variant="default_main">저장</Button>
// 아이콘 포함
<Button variant="default_main" icon="save" label="저장" />
// 비활성화
<Button variant="default_main" label="처리 중" disabled />
// 토글 버튼
<Button variant="default" label="즐겨찾기" toggleable selected={isFav} onClick={toggle} />주요 variant 값: default · default_main · default_sub · default_transparent · default_transparent_gray · disabled
Loader
import { Loader } from '@ihab_giihan/core'
// 스피너 (기본)
<Loader />
<Loader size={32} label="불러오는 중..." />
// 프로그레스 바
<Loader variant="bar" progress={67} label="업로드 중..." />SearchField
import { SearchField } from '@ihab_giihan/core'
const [query, setQuery] = React.useState('')
<SearchField
value={query}
onChange={setQuery}
placeholder="훈련 검색"
suggestions={['일상 듣기', '소음 훈련', '받아쓰기']}
/>ProgressIndicator
import { ProgressIndicator } from '@ihab_giihan/core'
// max=5개 단계, 현재 index=2까지 활성화
<ProgressIndicator max={5} index={2} />Header (AppHeader)
Header는 앱 전체 레이아웃의 사이드 내비게이션입니다. 팝업 타이틀이 아닙니다.
이름 충돌 방지용 AppHeader alias도 제공됩니다.
import { Header, AppHeader } from '@ihab_giihan/core'
const navItems = [
{ key: 'home', label: '홈', icon: 'home' },
{ key: 'training', label: '훈련', icon: 'dumbbell' },
{ key: 'profile', label: '프로필', icon: 'user-round' },
]
<Header
items={navItems}
activeKey={currentKey}
onChange={(key) => navigate(key)}
onSetting={() => openSettings()}
onHelp={() => openHelp()}
onAccount={() => openAccount()}
/>SideNavFooter
import { SideNavFooter } from '@ihab_giihan/core'
<SideNavFooter
onSetting={() => openSettings()}
onHelp={() => openHelp()}
onAccount={() => openAccount()}
/>TooltipInfo / TooltipTextBox
import { TooltipInfo, TooltipTextBox } from '@ihab_giihan/core'
// 간단한 툴팁
<TooltipInfo anchorRect={btnRef.current?.getBoundingClientRect()} text="도움말 텍스트" onClose={() => {}} />
// 텍스트박스 툴팁 (글씨 크기 조절, 다크모드 지원)
<TooltipTextBox
text={['첫 번째 문장입니다.', '두 번째 문장입니다.']}
anchorRect={anchorRect}
onClose={() => setOpen(false)}
/>IconGridMenu
import { IconGridMenu } from '@ihab_giihan/core'
const categories = [
{ key: 'daily', label: '일상', icon: 'coffee' },
{ key: 'noise', label: '소음', icon: 'volume-2' },
{ key: 'story', label: '이야기', icon: 'book-open' },
]
<IconGridMenu
categories={categories}
selected={selectedKey}
onSelect={(key) => setSelectedKey(key)}
/>PaymentPlan
import { PaymentPlan, type PaymentPlanData } from '@ihab_giihan/core'
const plan: PaymentPlanData = {
id: 'pro',
title: '프로',
subtitle: '개인 집중 훈련',
quote: '가장 인기',
colorToken: 'main',
recommendedFor: ['집중 훈련이 필요한 분', '매일 꾸준히 연습하고 싶은 분'],
features: ['모든 훈련 모듈', '무제한 이용', '통계 분석'],
tiers: [
{ id: 'monthly', label: '월간', price: '9,900원', badge: '추천' },
{ id: 'yearly', label: '연간', price: '89,000원', badge: '2개월 무료' },
],
}
<PaymentPlan plan={plan} period="monthly" onSelect={(tier) => handleSelect(tier)} />NoiseTrainingPagePopup
소음 훈련 팝업입니다. trainingData.contexts에 문장 배열을 전달하세요.
import { NoiseTrainingPagePopup, type TrainingData } from '@ihab_giihan/core'
const trainingData: TrainingData = {
id: 'noise-01',
title: '카페 소음 훈련',
contexts: [
{ sentence: '오늘 날씨가 정말 좋습니다.', contextCode: 'ctx_001' },
{ sentence: '회의가 3시에 시작됩니다.', contextCode: 'ctx_002' },
],
}
<NoiseTrainingPagePopup
trainingData={trainingData}
popupTitle="소음 훈련"
onResult={(result) => console.log('점수:', result?.score)}
onClose={() => setOpen(false)}
onBack={() => goBack()}
/>
contexts는 소음 훈련 전용입니다. 일반 선택형 훈련은TrainingPagePopup+items배열을 사용하세요.
Effect_PartyPopper
훈련 완료 등 성공 이벤트에 사용할 수 있는 폭죽 애니메이션입니다.
import { Effect_PartyPopper } from '@ihab_giihan/core'
<Effect_PartyPopper
active={showConfetti}
count={60}
duration={2500}
onComplete={() => setShowConfetti(false)}
/>배포·사용 권장 사항 (Exports · 타입 · 자원 · 스코핑)
아래는 패키지를 외부 프로젝트에서 안정적으로 사용하기 위한 권장 설정과 예시입니다.
- 일관된 Exports: 소스는 명시적 named-export(
export * from './components')를 사용합니다. 소비자에서는 가능한 한 named import를 권장합니다:
// 권장 (tree-shake 가능)
import { CoreScope, Button } from '@ihab_giihan/core'
// 서브패스 사용 (필요시)
import { Button } from '@ihab_giihan/core/components'- ESM / CJS 일관성: 번들(ESM)과 CommonJS(CJS)는 같은 named export를 제공합니다. 기본 사용법은 named import 또는 네임스페이스 import입니다.
import { Button, CoreScope } from '@ihab_giihan/core'
import * as Core from '@ihab_giihan/core'기본 export는 하위 호환용 오브젝트로 제공되지만, 새 코드에서는 Core.default 같은 접근에 의존하지 말고 위 두 패턴을 권장합니다.
- 타입 선언 (d.ts) 서브패스: 각 subpath에 대한
.d.ts가dist/packages/core/src/*.d.ts에 생성됩니다.package.json의exports에는 subpath별types가 매핑되어 있어 TypeScript에서 다음처럼 인식됩니다:
import { Button } from '@ihab_giihan/core/components' // types available- 정적 자원(logo 등): 빌드 시 레포 루트
logo/디렉터리(존재하는 경우)를dist/logo/로 복사합니다. 패키지 내부 절대경로에 직접 의존하지 않도록setAssetBaseUrl()API를 제공합니다.
import { setAssetBaseUrl } from '@ihab_giihan/core'
setAssetBaseUrl('https://cdn.example.com/ihab-assets')이후 패키지 내부 로고/배경 이미지는 위 베이스 URL을 기준으로 해석됩니다. 소비자에서 로고를 사용하려면 권장 순서는:
- 앱의 퍼블릭(public) 디렉터리에 로고를 두고 호스팅
- 패키지에 포함된 로고를 직접 사용하려면(비권장)
import LogoUrl from '@ihab_giihan/core/dist/logo/Logo_speechear.svg'처럼 소비자의 번들러가 처리하도록 import
패키지 내 exports에 "./logo/*": "./dist/logo/*" 매핑을 추가했으나, 번들러/런타임 환경에 따라 서브패스 접근이 제한될 수 있으니 퍼블릭 호스팅을 우선 권장합니다.
- CSS 스코핑 (CoreScope): 이 패키지는
.ihab-core-scope네임스페이스로 스타일을 범위화합니다. 권장 사용법:
import '@ihab_giihan/core/styles.css'
import { CoreScope } from '@ihab_giihan/core'
export default function App(){
return (
<CoreScope>
{/* 컴포넌트 */}
</CoreScope>
)
}충돌 회피 팁:
전역 클래스 네임이나 토큰 이름을 피하고
ihab-core-접두어를 유지하세요.다른 라이브러리와 네임 충돌이 우려된다면, 상위 요소에 추가 클래스를 부여하고 더 구체적인 선택자를 사용하세요.
default의존 지양: 하위 호환용 default export가 존재하더라도 새 코드에서는 명시적 named import 또는import * as Core를 우선 권장합니다.긴급 폴백 export: 소비자에서 자주 찾는
TrainingPagePopupBottom,TrainingPagePopupTitle는 루트 및components엔트리에서 직접 가져올 수 있습니다.
import { TrainingPagePopupBottom, TrainingPagePopupTitle } from '@ihab_giihan/core'위 가이드들은 소비자 프로젝트의 번들러(ESBuild/Vite/Webpack) 설정과 타겟 런타임에 따라 조정될 수 있습니다.
Exports / Bundling 참고
- 패키지
package.json에는./components.css같은 subpath export가 추가되어 있어import '@ihab_giihan/core/components.css'로 불러올 수 있습니다. - 번들러가 CSS를 제거(tree-shake)하지 않도록
sideEffects: ["dist/**/*.css"]가 설정되어 있습니다. 번들러 설정에 따라 CSS를 명시적으로 import해야 합니다.
Server-side rendering (Next.js / SSR) 사용 시 주의
- 서버 사이드에서 CSS를 import 하면 빌드에 포함되어 렌더 차이가 날 수 있습니다. Next.js에서는 보통
_app.tsx에서 CSS를 import 하거나 클라이언트 전용 컴포넌트에서 import 하세요. - 클라이언트 전용 의존성(
window,document)이 필요한 컴포넌트는 클라이언트에서만 렌더링되도록 처리하세요.
예시(Next.js _app.tsx):
import '../styles/globals.css'
import '@ihab_giihan/core/styles.css'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}로컬 테스트 및 퍼블리시 가이드
- 패키지 빌드 & tarball 테스트:
cd packages/core
npm install
npm run build
npm pack
# 생성된 tarball을 테스트 프로젝트에 설치
npm install /absolute/path/to/packages/core/ihab_giihan-core-0.1.4.tgz- 퍼블리시(권장 전 검증 완료 후):
cd packages/core
npm run build
# 버전 변경 (예: patch)
npm version patch
npm publish --access public문제 해결 (Troubleshooting)
- 스타일이 적용되지 않음:
styles.css를 import했는지 확인하고,CoreScope로 감싸거나 루트에ihab-core-scope클래스를 적용했는지 확인하세요. - 토큰(커스텀 프로퍼티) 값이 보이지 않음:
styles.css가 포함되어 있으며 번들러가 해당 CSS를 제거하지 않았는지 확인하세요. (예:sideEffects설정 확인) - 아이콘/패키지 관련 오류:
lucide-react같은 peer dependency를 설치했는지 확인하세요. - 로고/이미지가 깨짐: 앱 시작 시
setAssetBaseUrl()을 호출했는지 확인하거나, 필요한logo/,images/backgrounds/자산을 호스트 앱의 public/CDN 경로에 배치하세요. - 중복된 React 인스턴스:
npm ls react로 의존성 트리를 검사하고, 외부 프로젝트가 동일한 React를 사용하도록 조정하세요.
로컬 예시 (간단한 App)
import React from 'react'
import '@ihab_giihan/core/styles.css'
import { CoreScope, MenuButton } from '@ihab_giihan/core'
export default function App(){
return (
<CoreScope>
<MenuButton item={{ key: 'home', label: 'Home' }} />
</CoreScope>
)
}문서를 더 다듬거나 추가 예시(Next.js/CRA/Vite별 설정)를 원하시면 알려주세요.
CI / 퍼블리시 자동화
패키지에는 빌드·검증용 스크립트와 GitHub Actions 워크플로가 추가되어 있습니다:
packages/core/scripts/smoke-test.js— tarball 생성 후 타입체크·SSR 확인을 수행하는 Node 스크립트packages/core/scripts/smoke-test.sh— 빌드·pack·tarball 설치·CSS 스코프 검증을 수행하는 셸 스크립트packages/core/scripts/publish-check.sh— 퍼블리시 전 빌드·pack·검증을 자동으로 수행하는 로컬 스크립트
CI 워크플로:
.github/workflows/core-ci.yml— PR/푸시 시 빌드·테스트·smoke-test를 실행합니다..github/workflows/core-publish.yml— 릴리스 또는 태그 푸시 시NPM_TOKEN을 사용해 퍼블리시합니다. 퍼블리시용 비밀 키는NPM_TOKEN으로 저장하세요.
퍼블리시 전 로컬 점검 예시:
# 패키지 루트에서
bash packages/core/scripts/publish-check.sh자동화 / AI 통합 가이드
다른 프로젝트의 자동화(리팩터 도구, CI, LLM 에이전트 등)가 이 패키지를 안전하게 다루도록 하기 위한 체크리스트와 권장 절차입니다.
핵심 점검 항목
- 빌드 명령 및 산출물 확인:
# 패키지 빌드
npm --prefix packages/core run build
# 예상 산출물 (packages/core/dist)
# - ihab-core.css (토큰 + 컴포넌트 스타일)
# - components.esm.css / components.cjs.css (컴포넌트 전용 CSS)
# - ihab-core.esm.js, ihab-core.cjs.js, ihab-core.umd.js
# - ihab-core.d.ts- CSS 스코핑 규칙: 패키지의 토큰(커스텀 프로퍼티)은
:where(.ihab-core-scope){...}에 선언됩니다. 자동화가:root를 단순히.ihab-core-scope :root로 prefix 하지 않도록 주의하세요. PostCSS transform을 수정할 때 아래 매핑을 유지해야 합니다:
// postcss.config.cjs - transform(selector)
if (selector === ':root') return ':where(.ihab-core-scope)'
if (selector === 'html' || selector === 'body' || selector === '#root') return '.ihab-core-scope'패키지 메타데이터 확인:
packages/core/package.json의exports에"./components.css"가 존재하는지 확인.sideEffects에"dist/**/*.css"가 포함되어 있어 번들러가 CSS를 제거하지 않도록 합니다.
Peer dependencies: 반드시
react,react-dom,lucide-react등의 peer deps가 소비자 프로젝트에 설치되어야 합니다.
자동화 도구(리팩터/스크립트)를 위한 규칙
- 절대/상대 경로 및 import 구문을 정규식으로 무분별하게 치환하지 마세요. 구문 손상을 초래할 수 있습니다.
- 안전 권장 방식: AST 기반 툴 사용(예:
ts-morph,jscodeshift,recast) — 문자열 기반 치환 대신 식별자/노드 수준에서 변경합니다. - 변경 전 백업 또는 커밋을 남기세요(예: git stash 또는
.bak파일 생성). - 제외 패턴(무시할 것):
dist/**,node_modules/**,**/*.bak.
문제 사례 (주의): 자동화가 잘못 적용되면 import Icon./Icon' 같은 구문 오류가 발생합니다. 이런 변경은 AST 검사 또는 TypeScript 빌드로 곧바로 발견됩니다.
CI 스모크 테스트 (권장)
- 빌드 및 tarball 검사
cd packages/core
npm run build
npm pack
tar -tzf ihab_giihan-core-*.tgz | rg 'dist/.*\.css' || (echo 'CSS 없음' && exit 1)- 로컬 설치 후 타입 체크
mkdir /tmp/core-test && cd /tmp/core-test
npm init -y
npm install react react-dom lucide-react
npm install /absolute/path/to/ihab_giihan-core-0.1.4.tgz
npx tsc --noEmit- CSS 토큰(스코프) 존재 확인 (간단 문자열 검사)
rg ":where\\(.ihab-core-scope\\)" node_modules/@ihab_giihan/core/dist/ihab-core.css || echo '토큰 스코프 누락'- 선택적 브라우저 스모크: (Playwright/Puppeteer) 짧은 E2E로
CoreScope로 감싼 컴포넌트를 렌더하고getComputedStyle로 토큰이 있는지 확인하세요.
퍼블리시 체크리스트 (자동화 가능)
npm --prefix packages/core run build실행 성공dist에 CSS/JS/d.ts 파일 존재 확인npm pack생성물에dist/*.css포함 확인- 패키지 버전 태그(
npm version patch|minor|major) 및 CHANGELOG 업데이트 npm publish --access public또는 레지스트리 업로드
자동화 관점 트러블슈팅 요령
- 빌드 실패(파싱 에러):
npm --prefix packages/core run build가 실패하면 최근 변경(diff)을 검사하세요 (git diff/ 변경된 파일에서.bak탐색). - import 구문 깨짐: 자동 리팩터가 원인인 경우, AST 기반 도구로 복구하거나
.bak에서 복사하여 수동 복원하세요. - CSS 토큰이 적용되지 않음:
packages/core/dist/ihab-core.css에:where(.ihab-core-scope)가 포함되어 있는지 확인하세요. 포함되어 있지 않다면postcss.config.cjs의 transform 규칙을 검토하세요.
위 체크리스트는 자동화 에이전트(LLM/봇)나 CI 파이프라인이 이 패키지를 안전하게 변경·검증할 수 있도록 고안되었습니다. 자동화용 추가 스크립트(예: smoke-test 스크립트, Pack 검사 스크립트)를 원하시면 만들어 드리겠습니다.
