[FEAT] SVG Sprite 빌드 시스템 설정 - #50
Conversation
svg-sprite, svgo, tsx 패키지를 설치하고 icons 빌드 스크립트를 등록했습니다.
svgo.config.mjs, 스프라이트 생성 스크립트, iconNames 자동 생성 스크립트, Icon 컴포넌트를 작성하고 icons/index.ts에서 export했습니다.
SvgSprite 컴포넌트를 작성하고 layout.tsx에 인라인으로 주입했습니다. sprite.svg를 .gitignore에 추가했습니다.
sprite.svg 파일이 없을 때 readFileSync가 ENOENT를 던지는 문제를 수정했습니다. existsSync로 파일 존재 여부를 먼저 확인하고 없으면 null을 반환합니다.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
워크스루SVG Sprite 빌드 체계를 추가했습니다. 아이콘 소스에서 스프라이트와 이름 타입을 생성하고, 변경 사항SVG Sprite 빌드 시스템
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~20분 연관 가능성이 있는 PR
제안 레이블
제안 리뷰어
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/timo-web/.gitignore`:
- Around line 38-39: The .gitignore entry for the sprite asset is using a
repo-relative path instead of being relative to apps/timo-web, so it will not
ignore the intended file. Update the ignore pattern in apps/timo-web/.gitignore
to target public/sprite.svg (or /public/sprite.svg) and keep the comment/entry
aligned with the actual artifact being ignored.
In `@apps/timo-web/components/SvgSprite.tsx`:
- Around line 4-9: SvgSprite currently does synchronous filesystem checks and
reads on every render, which blocks the server render path. Move the sprite
loading logic in SvgSprite out of the render function by caching it at module
scope or using a one-time lazy initialization so existsSync and readFileSync are
not called repeatedly. Keep the component’s behavior the same by having
RootLayout still render the cached sprite content when available.
In `@packages/timo-design-system/src/icons/generate-icon-names.ts`:
- Around line 7-15: The icon name generator in generate-icon-names.ts should
make output deterministic and handle an empty source directory explicitly. Sort
the SVG-derived names before building the union so readdirSync order does not
change the generated iconNames.ts across environments, and add a names.length
=== 0 branch in the generation logic so IconName is emitted as a valid
empty/never-safe type instead of producing invalid TypeScript. Keep the fix
localized to the names mapping and content template used by the generator.
In `@packages/timo-design-system/src/icons/Icon.tsx`:
- Around line 3-4: The Icon component’s name prop is unusable because IconName
is currently never, so <Icon name="..."/> cannot type-check. Fix the upstream
icon generation in iconNames.ts so it produces a real literal union (or fails
the build when no icons are present), and ensure Icon.tsx consumes that
generated type for the name prop. Also add a generation/CI validation step so
the TypeScript artifact cannot ship with IconName = never.
In `@packages/timo-design-system/src/icons/iconNames.ts`:
- Around line 1-2: `IconName` is currently `never`, so `Icon.tsx` cannot accept
any valid icon name and `<use href="`#icon-`${name}" />` becomes unusable.
Regenerate the `IconName` type in `iconNames.ts` from the actual
`src/icons/source` icon set so it exports the real union of icon names, and
verify `Icon` continues to use that type for its `name` prop without narrowing
it to `never`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 68360a26-ad57-4d54-a735-d30805c5ba41
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
apps/timo-web/.gitignoreapps/timo-web/app/layout.tsxapps/timo-web/components/.gitkeepapps/timo-web/components/SvgSprite.tsxpackages/timo-design-system/package.jsonpackages/timo-design-system/src/icons/.gitkeeppackages/timo-design-system/src/icons/Icon.tsxpackages/timo-design-system/src/icons/generate-icon-names.tspackages/timo-design-system/src/icons/generate-sprite.tspackages/timo-design-system/src/icons/iconNames.tspackages/timo-design-system/src/icons/index.tspackages/timo-design-system/svgo.config.mjs
.gitignore 내 sprite.svg 경로가 절대 경로로 작성되어 무시되지 않던 문제를 수정했습니다.
매 렌더링마다 fs.readFileSync를 호출하던 방식을 모듈 레벨 변수로 캐싱해 첫 호출 이후 파일 I/O가 발생하지 않도록 했습니다.
아이콘 추가 순서에 따라 iconNames.ts의 union type 순서가 달라지던 문제를 수정했습니다. .sort()를 추가해 항상 알파벳 순으로 생성되도록 했습니다.
ISSUE 🔗
close #47
What is this PR? 🔍
SVG 아이콘을 Sprite 방식으로 번들링하는 빌드 시스템을 구축하고,
<Icon name="ic_close" size={24} />형태로 사용할 수 있는 컴포넌트를 추가했습니다.배경
currentColor를 거치는 불필요한 레이어가 생깁니다.sprite.svg로 합친 뒤<use href="#icon-name" />으로 참조하는 Sprite 방식을 채택해 번들 크기와 런타임 DOM 노드를 최소화했습니다.SVGR vs SVG Sprite 비교
sprite.svg1개만 변경<svg>삽입<use>참조 1개currentColor활용 가능iconNames.ts자동 생성 → TypeScript union typepnpm icons실행 필요빌드 파이프라인 (
packages/timo-design-system)svg-sprite,svgo,tsx패키지를 추가하고 3단계 빌드 스크립트를 등록했습니다.pnpm icons한 번으로 아래 세 단계가 순서대로 실행됩니다.sprite.svg는 빌드 산출물이므로.gitignore에 추가했습니다. 아이콘을 추가하거나 수정할 때마다pnpm icons를 재실행해야 합니다.SVGO 설정 (
svgo.config.mjs)removeViewBox: false,removeDimensions옵션으로 SVGO를 설정했습니다.viewBox를 제거하면 CSS로 크기 조절 시 비율이 깨지기 때문에 유지하고, 대신width/height고정 속성은 제거해 CSS로 크기를 자유롭게 제어할 수 있도록 했습니다.Icon 컴포넌트 (
src/icons/Icon.tsx)<Icon name="ic_close" size={24} />형태의 컴포넌트를 추가했습니다.<svg><use href="#icon-name" /></svg>를 매번 직접 작성하는 불편함을 줄이고,iconNames.ts의 union type을 통해 존재하지 않는 아이콘 이름을 컴파일 타임에 잡을 수 있도록 했습니다.href에#icon-{name}패턴으로 sprite의 symbol을 참조합니다.sizeprop으로width/height를 동시에 설정할 수 있고,rotateprop으로 90/180/270도 회전을 지원합니다.ariaHidden은 기본값true로 스크린리더 중복 읽기를 방지합니다.SvgSprite 컴포넌트 (
apps/timo-web/components/SvgSprite.tsx)sprite.svg를 서버에서 읽어layout.tsx에 인라인으로 주입하는 컴포넌트를 추가했습니다.<use href>cross-origin 이슈가 발생할 수 있습니다. 인라인 방식은 이 문제를 회피하고 첫 페인트 시점에 모든 symbol이 DOM에 존재함을 보장합니다.fs.readFileSync로public/sprite.svg를 읽어dangerouslySetInnerHTML로 주입합니다.sprite.svg가 없을 경우 (pnpm icons미실행 상태)null을 반환해 크래시를 방지합니다."use client"컴포넌트에서는 사용할 수 없습니다.아이콘 추가 워크플로우
새 아이콘을 추가하거나 기존 아이콘을 수정할 때는 아래 순서를 따릅니다.
To Reviewers
SvgSprite가 Server Component로 동작하면서fs를 직접 읽는 구조인데, Next.js App Router 환경에서 경계 설정이 올바른지 확인 부탁드립니다.sprite.svg는 gitignore 대상이라 로컬에서pnpm icons를 실행하지 않으면 아이콘이 렌더링되지 않습니다 (null 반환으로 크래시는 방지됩니다).iconNames.ts는 자동 생성 파일이므로 직접 수정하지 않습니다.Screenshot 📷
예시 아이콘 넣어봤을 때

Test Checklist ✔
sprite.svg미존재 시 SvgSprite null 반환 확인pnpm icons실행 후sprite.svg생성 확인<Icon name="ic_play" />렌더링 확인pnpm check-types— 미실행