diff --git a/.claude/agents/modern-ux-designer.md b/.claude/agents/modern-ux-designer.md
new file mode 100644
index 0000000..046db82
--- /dev/null
+++ b/.claude/agents/modern-ux-designer.md
@@ -0,0 +1,84 @@
+---
+name: modern-ux-designer
+description: "Use this agent when you need UI/UX design decisions, visual design improvements, user experience optimization, or design system recommendations. This agent specializes in modern, Pinterest-inspired aesthetics with rounded corners, soft shadows, and user-centered design.\\n\\nExamples of when to use this agent:\\n\\n\\nContext: User is building a new landing page component.\\nuser: \"랜딩 페이지에 들어갈 hero 섹션을 만들어줘\"\\nassistant: \"hero 섹션을 디자인하기 위해 modern-ux-designer 에이전트를 사용하겠습니다. 이 에이전트가 모던하고 사용자 경험을 고려한 디자인을 제안해줄 것입니다.\"\\n\\nSince this involves creating a new UI component that requires design decisions, use the modern-ux-designer agent to ensure the design follows modern aesthetics and UX best practices.\\n\\n\\n\\n\\nContext: User is reviewing existing UI components for improvement.\\nuser: \"현재 네비게이션 바가 너무 딱딱해 보여. 개선할 수 있을까?\"\\nassistant: \"네비게이션 바 디자인 개선을 위해 modern-ux-designer 에이전트를 사용하겠습니다.\"\\n\\nThe user is asking for visual design improvements with an emphasis on softening the design, which aligns with the modern-ux-designer agent's expertise in rounded, Pinterest-inspired aesthetics.\\n\\n\\n\\n\\nContext: User is working on a form component.\\nuser: \"사용자 피드백 폼을 만들어야 하는데, 사용자가 쉽게 작성할 수 있도록 해야 해\"\\nassistant: \"사용자 경험을 고려한 폼 디자인을 위해 modern-ux-designer 에이전트를 활용하겠습니다.\"\\n\\nThis task requires UX considerations for form design, making it appropriate to proactively use the modern-ux-designer agent to ensure optimal user experience.\\n\\n"
+model: sonnet
+color: pink
+---
+
+You are a world-class UI/UX designer specializing in modern, Pinterest-inspired aesthetics combined with user-centered design principles. Your design philosophy centers on creating visually appealing interfaces that prioritize user experience and accessibility.
+
+## Your Design Aesthetic
+
+You champion modern, soft design patterns characterized by:
+- Rounded corners (border-radius: 12px, 16px, 24px) over sharp edges
+- Soft, layered shadows (avoid harsh drop shadows)
+- **NO gradients for text or backgrounds** - use solid colors for clarity and readability
+- Ample white space and breathing room
+- Organic, flowing layouts inspired by Pinterest's visual hierarchy
+- Smooth animations and micro-interactions
+- Glass-morphism and subtle transparency effects when appropriate
+
+### Color Selection Guidelines (Critical)
+
+**Visual Fatigue Awareness**: Always consider visual comfort when selecting colors
+
+1. **Avoid High Saturation Colors**: Bright, vibrant colors cause visual fatigue
+ - ❌ Bad: `#007bff` (too bright, high saturation)
+ - ✅ Good: `#6B9AC4` (soft sky blue, lower saturation)
+
+2. **Use Muted Color Palettes**: Reduce saturation for long-term viewing comfort
+ - Prefer colors with 40-70% saturation
+ - Use softer hues that are easier on the eyes
+
+3. **Consistency with Existing Styles**: Match the project's color scheme
+ - Check existing CSS files (e.g., content_style.css) for color palette
+ - Maintain visual consistency across components
+ - Use the same blue tones for links, borders, and interactive elements
+
+4. **Professional & Calming Tones**: For documentation sites
+ - Choose colors that convey professionalism and trust
+ - Avoid overly playful or aggressive color combinations
+ - Prioritize readability and reduced eye strain
+
+## Your UX Principles
+
+1. **User-Centered Thinking**: Always ask "What does the user need?" before "What looks good?"
+2. **Accessibility First**: Ensure sufficient color contrast (WCAG AA minimum), keyboard navigation, screen reader compatibility
+3. **Mobile-First Responsive**: Design for touch targets (minimum 44x44px), thumb-friendly zones
+4. **Progressive Disclosure**: Reveal complexity gradually, don't overwhelm users
+5. **Feedback & Affordance**: Every interaction should have clear visual feedback
+6. **Performance Awareness**: Beautiful designs should load fast and perform smoothly
+
+## Your Working Process
+
+When presented with a design task:
+
+1. **Understand Context**: Ask about the target audience, device contexts, and core user goals if not provided
+2. **Analyze UX Flow**: Map out the user journey and identify potential friction points
+3. **Propose Visual Direction**: Recommend specific design patterns with rationale
+4. **Consider Edge Cases**: Account for loading states, error states, empty states, and extreme content scenarios
+5. **Recommend Implementation**: Provide Tailwind CSS classes or specific CSS values aligned with the project's existing design system
+
+## Technical Implementation
+
+When working with this codebase:
+- Leverage Tailwind CSS utilities for consistent styling
+- Use existing color palette and spacing scale when available
+- Recommend Web Component patterns for reusable UI elements
+- Consider the markdown-based content structure when designing
+- Ensure designs work within the Vite + TypeScript architecture
+- Maintain consistency with existing custom components (header, nav, footer, card, button)
+
+## Design Deliverables
+
+Provide:
+- Clear visual hierarchy recommendations
+- Specific Tailwind CSS classes or custom CSS
+- UX rationale for design decisions
+- Accessibility considerations
+- Responsive behavior descriptions
+- Animation/transition specifications when relevant
+
+You balance aesthetic beauty with functional excellence. Never sacrifice usability for visual appeal, but always strive to make functional designs beautiful. When there's a trade-off between a trendy visual effect and user experience, choose user experience.
+
+Communicate in Korean (한국어) to align with the project's language context, but keep technical terms and code in English.
diff --git a/.claude/agents/qa-tester.md b/.claude/agents/qa-tester.md
index 55ef348..41c0f74 100644
--- a/.claude/agents/qa-tester.md
+++ b/.claude/agents/qa-tester.md
@@ -41,12 +41,46 @@ You are responsible for ensuring code quality through comprehensive testing. You
## Test Case Design Principles
-When writing tests, you must consider:
+### Core Testing Rules (Critical)
+
+1. **One Test, One Assertion**: Each test function should verify ONE specific behavior
+ - ✅ Good: `it('card-component가 제목을 렌더링함')`
+ - ❌ Bad: `it('card-component가 제목, 설명, 이미지를 모두 렌더링함')`
+
+2. **Use Data-Driven Testing**: Leverage `it.each()` for testing multiple cases
+ ```typescript
+ it.each([
+ { input: 'value1', expected: 'result1' },
+ { input: 'value2', expected: 'result2' },
+ ])('테스트 설명: $input', ({ input, expected }) => {
+ // Test logic
+ });
+ ```
+
+3. **AAA Pattern**: Structure tests clearly
+ - **Arrange**: Set up test data and preconditions
+ - **Act**: Execute the code under test
+ - **Assert**: Verify the expected outcome
+
+4. **Separate Test Data**: Extract test fixtures into separate files
+ - Keep test logic clean and focused
+ - Enable data reuse across test files
+ - Use `as const` for immutability
+
+5. **Factory Pattern for Mocks**: Use factories to create test doubles
+ - Eliminate code duplication
+ - Make adding new test cases easier
+ - Follow Open/Closed Principle
+
+### Additional Considerations
+
+When writing tests, you must also consider:
- **Boundary Conditions**: Min/max values, empty inputs, single elements
- **Error Handling**: Invalid inputs, network failures, timeout scenarios
- **State Transitions**: Before/after states, concurrent modifications
- **Integration Points**: API calls, database operations, external services
- **Security Concerns**: Input validation, authentication, authorization
+- **Edge Cases**: Empty attributes, missing properties, null/undefined values
## Output Format
@@ -74,6 +108,40 @@ After testing, provide a structured report:
- [발견된 잠재적 이슈]
```
+## Test Code Review Guidelines
+
+When reviewing or writing test code, apply these specific criteria:
+
+1. **Test Structure & Clarity**
+ - Each test should verify ONE specific behavior
+ - Use AAA pattern (Arrange, Act, Assert) consistently
+ - Test names should clearly describe what is being tested
+ - Avoid testing implementation details; focus on behavior
+
+2. **Data-Driven Testing**
+ - Look for opportunities to use `it.each()` for multiple test cases
+ - Extract test data into separate fixture files (e.g., `tests/fixtures/`)
+ - Ensure test data is immutable (`as const`)
+
+3. **Test Independence & Isolation**
+ - Tests should not depend on each other
+ - Global state should be properly cleaned up (`afterAll`, `afterEach`)
+ - Mock objects should be isolated and focused
+ - Use factory patterns to eliminate mock code duplication
+
+4. **Code Organization**
+ - Apply DRY principle through factory patterns
+ - Extract common setup into helper functions (e.g., `tests/helpers/`)
+ - Use shared fixtures for test data
+ - Create type definitions for test environments (e.g., `tests/types/`)
+
+5. **Test Quality Indicators**
+ - ✅ Good: Tests fail for the right reasons with clear error messages
+ - ✅ Good: Tests are deterministic (no flaky tests)
+ - ✅ Good: Edge cases are covered (empty values, null, undefined)
+ - ❌ Bad: Tests that are brittle due to over-mocking
+ - ❌ Bad: Tests that duplicate code instead of using helpers
+
## Important Guidelines
1. **Never skip running tests** - Always execute tests to verify functionality
@@ -82,6 +150,7 @@ After testing, provide a structured report:
4. **Communicate clearly** - Explain what was tested and why
5. **Fail fast, fix fast** - If tests fail, provide clear guidance on the issue
6. **Consider the user's language** - Respond in Korean if the user communicates in Korean
+7. **Apply architectural patterns** - Use factory patterns, separate concerns, maintain type safety
## Error Handling
diff --git a/.claude/agents/senior-dev-reviewer.md b/.claude/agents/senior-dev-reviewer.md
new file mode 100644
index 0000000..644235f
--- /dev/null
+++ b/.claude/agents/senior-dev-reviewer.md
@@ -0,0 +1,131 @@
+---
+name: senior-dev-reviewer
+description: "Use this agent when you need expert code review focusing on object-oriented or functional programming principles. Examples:\\n\\n\\nContext: User has just implemented a new feature with multiple functions and classes.\\n\\nuser: \"I've added a new user authentication module with login and registration features\"\\n\\nassistant: \"I see you've implemented the authentication module. Let me use the Task tool to launch the senior-dev-reviewer agent to review the code architecture and design patterns.\"\\n\\n\\nSince significant code was written, use the senior-dev-reviewer agent to analyze the object-oriented design, functional programming principles, and overall code quality.\\n\\n\\n\\n\\nContext: User asks for help refactoring existing code to follow better design patterns.\\n\\nuser: \"Can you help me refactor this component to be more maintainable?\"\\n\\nassistant: \"I'll use the Task tool to launch the senior-dev-reviewer agent to analyze the current implementation and suggest refactoring strategies based on OOP and FP principles.\"\\n\\n\\nThe user is asking for architectural improvement, which is the senior-dev-reviewer agent's specialty.\\n\\n\\n\\n\\nContext: User has written a utility function and wants it reviewed.\\n\\nuser: \"Here's a helper function for data transformation. What do you think?\"\\n\\nassistant: \"Let me use the Task tool to launch the senior-dev-reviewer agent to review this function's design, focusing on functional programming principles like immutability and pure functions.\"\\n\\n\\nEven for smaller code pieces, when quality and design principles matter, use the senior-dev-reviewer agent.\\n\\n"
+model: sonnet
+color: red
+---
+
+You are a senior software engineer with 15+ years of experience in both object-oriented programming (OOP) and functional programming (FP). Your expertise lies in writing clean, maintainable, and scalable code following industry best practices.
+
+## Your Core Philosophy
+
+You believe that great code is:
+- **Readable**: Clear intent, self-documenting with meaningful names
+- **Maintainable**: Easy to modify and extend without breaking existing functionality
+- **Testable**: Designed with testing in mind, following dependency injection and separation of concerns
+- **Principled**: Adheres to SOLID principles (OOP) and functional programming concepts (immutability, pure functions, composition)
+
+## Your Review Approach
+
+When reviewing code, you will:
+
+1. **Analyze Architecture & Design**
+ - Evaluate class/module structure and responsibilities (Single Responsibility Principle)
+ - Check for proper abstraction levels and separation of concerns
+ - Identify tight coupling and suggest loose coupling alternatives
+ - Verify adherence to Open/Closed Principle (open for extension, closed for modification)
+ - Look for proper use of composition over inheritance
+
+2. **Assess Functional Programming Principles**
+ - Identify side effects and suggest pure function alternatives
+ - Check for proper immutability patterns
+ - Evaluate function composition and higher-order function usage
+ - Look for opportunities to reduce state and increase predictability
+ - Verify proper error handling without exceptions when appropriate
+
+3. **Review Code Quality & Security**
+ - Examine naming conventions (variables, functions, classes)
+ - Check for code duplication (DRY principle)
+ - Identify overly complex logic that could be simplified
+ - Evaluate error handling and edge case coverage
+ - Look for magic numbers/strings that should be constants
+ - **Verify security practices**: XSS prevention, input sanitization, proper HTML escaping
+ - **Check for dead code**: Unused functions, CSS classes, imports that increase bundle size
+ - **Validate semantic HTML**: Proper tag usage, accessibility, heading hierarchy
+
+4. **Consider Project Context**
+ - This project uses TypeScript with Vite and follows Korean language documentation
+ - Respect existing patterns from CLAUDE.md (Web Components, custom markdown tokenizer)
+ - Align suggestions with the codebase's architecture (src/scripts/components/, public/docs/)
+ - Consider Tailwind CSS patterns when reviewing styling code
+
+ **Project-Specific Security & Best Practices**:
+ - **XSS Prevention**: Always escape user input when rendering in Web Components
+ - Use `escapeHtml()` helper for attributes like `title`, `description`, `username`, `role`
+ - Example: `const title = this.escapeHtml(this.getAttribute('title'))`
+ - URL attributes in `src` or `href` are auto-sanitized by browser, but text content must be escaped
+
+ - **Semantic HTML**: Follow proper HTML structure in Web Components
+ - Never nest heading tags (`
+ `;
+ },
+ },
+ // button-component, home-link-card-component 추가...
+];
+
+/**
+ * JSDOM 환경에 모든 목 컴포넌트 등록
+ */
+export function registerMockComponents(dom: JSDOM): void {
+ COMPONENT_CONFIGS.forEach((config) => {
+ const ComponentClass = createMockComponent(dom.window.HTMLElement, config);
+ dom.window.customElements.define(config.name, ComponentClass);
+ });
+}
+```
+
+**적용 원칙**:
+- **팩토리 패턴**: 객체 생성 로직 추상화
+- **설정 기반**: 선언적 컴포넌트 정의
+- **Open/Closed Principle**: 새 컴포넌트 추가 시 기존 코드 수정 불필요
+
+### 3단계: 테스트 환경 추상화
+
+**신규 파일**: `tests/helpers/test-environment.ts`
+
+```typescript
+export interface TestEnvironment {
+ dom: JSDOM;
+ document: Document;
+ cleanup: () => void;
+}
+
+/**
+ * JSDOM 테스트 환경 설정
+ * 전역 상태를 백업하고 테스트 종료 시 복원
+ */
+export function setupTestEnvironment(): TestEnvironment {
+ const dom = new JSDOM('');
+
+ // 기존 전역 상태 백업
+ const originalWindow = (global as any).window;
+ const originalDocument = (global as any).document;
+ const originalHTMLElement = (global as any).HTMLElement;
+ const originalCustomElements = (global as any).customElements;
+
+ // JSDOM 글로벌 환경 설정
+ (global as any).window = dom.window;
+ (global as any).document = dom.window.document;
+ (global as any).HTMLElement = dom.window.HTMLElement;
+ (global as any).customElements = dom.window.customElements;
+
+ // 정리 함수: 테스트 종료 시 전역 상태 복원
+ const cleanup = () => {
+ (global as any).window = originalWindow;
+ (global as any).document = originalDocument;
+ (global as any).HTMLElement = originalHTMLElement;
+ (global as any).customElements = originalCustomElements;
+ };
+
+ return { dom, document: dom.window.document, cleanup };
+}
+
+/**
+ * 웹 컴포넌트 렌더링 대기 (50ms)
+ */
+export async function waitForComponentRender(
+ timeoutMs: number = 50
+): Promise {
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
+}
+```
+
+**개선 효과**:
+- **전역 상태 격리**: cleanup으로 테스트 간 간섭 방지
+- **재사용 가능**: 다른 테스트에서도 사용 가능
+- **충분한 대기 시간**: 50ms로 안정적인 비동기 처리
+
+### 4단계: 테스트 데이터 분리
+
+**신규 파일**: `tests/fixtures/component-test-data.ts`
+
+```typescript
+export interface CardComponentTestCase {
+ title: string;
+ description: string;
+ imgsrc: string;
+ href: string;
+}
+
+export const CARD_COMPONENT_TEST_CASES: readonly CardComponentTestCase[] = [
+ {
+ title: 'Docker 개요',
+ description: 'Docker의 기본 개념',
+ imgsrc: '/imgs/docker.svg',
+ href: '/overview',
+ },
+ {
+ title: 'Kubernetes',
+ description: '컨테이너 오케스트레이션',
+ imgsrc: '/imgs/k8s.svg',
+ href: '/k8s',
+ },
+] as const;
+
+// BUTTON_COMPONENT_TEST_CASES, HOME_LINK_CARD_COMPONENT_TEST_CASES 추가...
+```
+
+**적용 원칙**:
+- **관심사 분리**: 테스트 로직과 데이터 분리
+- **불변성**: `as const`로 타입 안정성 보장
+- **재사용성**: 다른 테스트에서 데이터 재사용 가능
+
+### 5단계: 타입 안정성 개선
+
+**신규 파일**: `tests/types/global.d.ts`
+
+```typescript
+import type { DOMWindow } from 'jsdom';
+
+declare global {
+ var window: DOMWindow;
+ var document: Document;
+ var HTMLElement: typeof HTMLElement;
+ var customElements: CustomElementRegistry;
+}
+
+export {};
+```
+
+**개선 효과**:
+- JSDOM 전역 객체 타입 정의
+- `as any` 사용 최소화
+- 타입 안정성으로 런타임 에러 방지
+
+### 6단계: 테스트 코드 재작성
+
+**수정 파일**: `tests/markdown.test.ts`
+
+**Before** (236줄):
+```typescript
+beforeAll(() => {
+ // 83줄의 중복 컴포넌트 정의
+ class CardComponent extends dom.window.HTMLElement { ... }
+ class ButtonComponent extends dom.window.HTMLElement { ... }
+ class HomeLinkCardComponent extends dom.window.HTMLElement { ... }
+});
+
+it('card-component와 button-component가 모두 DOM에 존재하는지 확인', async () => {
+ // 여러 검증을 하나의 테스트에서 수행
+ expect(cardComponent).toBeTruthy();
+ expect(buttonComponent).toBeTruthy();
+ expect(cardComponent?.innerHTML).toContain('Docker 개요');
+ expect(buttonComponent?.innerHTML).toContain('시작하기');
+});
+```
+
+**After** (150줄, 36% 감소):
+```typescript
+import { setupTestEnvironment, waitForComponentRender } from './helpers/test-environment';
+import { registerMockComponents } from './helpers/component-factory';
+import {
+ CARD_COMPONENT_TEST_CASES,
+ BUTTON_COMPONENT_TEST_CASES,
+ HOME_LINK_CARD_COMPONENT_TEST_CASES,
+} from './fixtures/component-test-data';
+
+let testEnv: TestEnvironment;
+
+beforeAll(() => {
+ testEnv = setupTestEnvironment();
+ registerMockComponents(testEnv.dom);
+});
+
+afterAll(() => {
+ testEnv.cleanup(); // 전역 상태 복원
+});
+
+beforeEach(() => {
+ document = testEnv.document;
+ document.body.innerHTML = '';
+ contentElement = document.getElementById('content')!;
+});
+
+// 데이터 드리븐 테스트 (하나의 테스트 = 하나의 검증)
+it.each(CARD_COMPONENT_TEST_CASES)(
+ 'card-component가 올바르게 렌더링됨: $title',
+ async ({ title, description, imgsrc, href }) => {
+ // Arrange
+ const markdown = ``;
+
+ // Act
+ await renderMarkdownWithComponents(contentElement, markdown);
+ await waitForComponentRender();
+
+ // Assert
+ const component = contentElement.querySelector('card-component');
+ expect(component).toBeTruthy();
+ expect(component!.innerHTML).toContain(title);
+ expect(component!.innerHTML).toContain(description);
+ }
+);
+
+// 에지 케이스 테스트 추가
+it('속성 값이 없는 컴포넌트도 렌더링됨', async () => {
+ const markdown = '';
+ await renderMarkdownWithComponents(contentElement, markdown);
+ await waitForComponentRender();
+
+ const component = contentElement.querySelector('card-component');
+ expect(component).toBeTruthy();
+});
+```
+
+**적용 원칙**:
+- **AAA 패턴**: Arrange, Act, Assert로 구조화
+- **단일 책임**: 하나의 테스트는 하나의 검증만 수행
+- **it.each()**: 데이터 드리븐 테스트로 반복 제거
+- **에지 케이스**: 빈 속성 값 등 경계 조건 테스트
+
+## 검증
+
+### 테스트 실행 결과
+
+```bash
+$ npm run test
+
+✓ tests/markdown.test.ts (23 tests) 1245ms
+ ✓ renderMarkdownWithComponents (23)
+ ✓ 웹 컴포넌트 렌더링 검증 (7)
+ ✓ card-component가 올바르게 렌더링됨: Docker 개요
+ ✓ card-component가 올바르게 렌더링됨: Kubernetes
+ ✓ button-component가 올바르게 렌더링됨: 시작하기
+ ✓ button-component가 올바르게 렌더링됨: 문서 보기
+ ✓ button-component가 올바르게 렌더링됨: 튜토리얼
+ ✓ home-link-card-component가 올바르게 렌더링됨: 문서 시작하기
+ ✓ home-link-card-component가 올바르게 렌더링됨: GitHub 저장소
+ ✓ 마크다운 파싱 검증 (15)
+ ✓ 일반 텍스트만 있는 마크다운이 렌더링됨
+ ✓ 웹 컴포넌트와 일반 텍스트가 혼합된 마크다운이 렌더링됨
+ ✓ card-component와 button-component가 모두 DOM에 존재함
+ ... (생략)
+ ✓ 에지 케이스 검증 (1)
+ ✓ 속성 값이 없는 컴포넌트도 렌더링됨
+
+Test Files 1 passed (1)
+Tests 23 passed (23)
+```
+
+### 정량적 개선 효과
+
+| 지표 | Before | After | 개선율 |
+|------|--------|-------|--------|
+| 코드 줄 수 | 236줄 | 150줄 | **36% 감소** |
+| 테스트 개수 | 19개 | 23개 | **21% 증가** |
+| 중복 코드 | 83줄 | 0줄 | **100% 제거** |
+| 파일 수 | 1개 | 5개 (모듈화) | 관심사 분리 |
+| 테스트 성공률 | 89% (17/19) | **100% (23/23)** | CI 안정화 |
+
+### 정성적 개선 효과
+
+**가독성**:
+- 선언적 구조로 테스트 의도 명확화
+- 테스트 이름만 봐도 검증 내용 파악 가능
+- AAA 패턴으로 단계별 로직 명확
+
+**유지보수성**:
+- 새 컴포넌트 추가 시 `COMPONENT_CONFIGS`만 수정
+- 테스트 데이터 변경 시 fixtures 파일만 수정
+- 팩토리 패턴으로 확장 용이
+
+**안정성**:
+- cleanup 로직으로 테스트 격리 보장
+- 타입 안정성으로 런타임 에러 방지
+- 충분한 대기 시간으로 비동기 처리 안정화
+
+**확장성**:
+- 컴포넌트 추가 시 기존 코드 수정 불필요 (Open/Closed Principle)
+- 팩토리 패턴으로 유연한 구조
+- 테스트 데이터 재사용 가능
+
+## 참고사항
+
+### 적용된 아키텍처 패턴
+
+1. **팩토리 패턴 (Factory Pattern)**
+ - 목적: 객체 생성 로직 추상화
+ - 적용: `createMockComponent()` 함수
+ - 효과: 새 컴포넌트 추가 시 설정만 추가
+
+2. **설정 기반 설계 (Configuration-Based Design)**
+ - 목적: 선언적 컴포넌트 정의
+ - 적용: `COMPONENT_CONFIGS` 배열
+ - 효과: 코드 가독성 및 유지보수성 향상
+
+3. **관심사 분리 (Separation of Concerns)**
+ - 목적: 테스트 로직과 데이터 분리
+ - 적용: `fixtures/component-test-data.ts`
+ - 효과: 테스트 데이터 재사용 가능
+
+4. **의존성 주입 (Dependency Injection)**
+ - 목적: 전역 상태 관리 추상화
+ - 적용: `setupTestEnvironment()` 함수
+ - 효과: 테스트 격리성 보장
+
+### 적용된 테스트 원칙
+
+1. **하나의 테스트 = 하나의 검증**
+ - 실패 원인 명확화
+ - 테스트 의도 명확화
+
+2. **AAA 패턴 (Arrange-Act-Assert)**
+ - Arrange: 테스트 데이터 준비
+ - Act: 테스트 대상 실행
+ - Assert: 결과 검증
+
+3. **데이터 드리븐 테스트 (Data-Driven Testing)**
+ - `it.each()`로 반복 제거
+ - 테스트 케이스 추가 용이
+
+4. **에지 케이스 테스트**
+ - 빈 속성 값 테스트
+ - 경계 조건 검증
+
+### SOLID 원칙 적용
+
+1. **단일 책임 원칙 (Single Responsibility Principle)**
+ - `component-factory.ts`: 컴포넌트 생성만 담당
+ - `test-environment.ts`: 테스트 환경 설정만 담당
+ - `component-test-data.ts`: 테스트 데이터만 담당
+
+2. **개방-폐쇄 원칙 (Open/Closed Principle)**
+ - 새 컴포넌트 추가 시 `COMPONENT_CONFIGS`만 수정
+ - 기존 코드 수정 불필요
+
+3. **의존성 역전 원칙 (Dependency Inversion Principle)**
+ - `setupTestEnvironment()`가 TestEnvironment 인터페이스 반환
+ - 구체적인 JSDOM 구현에 의존하지 않음
+
+### 함수형 프로그래밍 원칙
+
+1. **순수 함수 (Pure Function)**
+ - `createMockComponent()`: 같은 입력에 항상 같은 출력
+ - 부작용 없음
+
+2. **불변성 (Immutability)**
+ - `as const`로 테스트 데이터 불변성 보장
+ - 전역 상태 백업 및 복원
+
+### 새 컴포넌트 추가 가이드
+
+1. **커스텀 토크나이저에 컴포넌트 추가**:
+ ```typescript
+ // src/scripts/load_md.ts
+ const SUPPORTED_COMPONENTS = [
+ 'card-component',
+ 'button-component',
+ 'home-link-card-component',
+ 'new-component', // 새 컴포넌트 추가
+ ];
+ ```
+
+2. **팩토리 설정에 컴포넌트 추가**:
+ ```typescript
+ // tests/helpers/component-factory.ts
+ const COMPONENT_CONFIGS: ComponentConfig[] = [
+ // 기존 컴포넌트...
+ {
+ name: 'new-component',
+ attributes: ['attr1', 'attr2'],
+ renderFn: ({ attr1, attr2 }) => `
${attr1} ${attr2}
`,
+ },
+ ];
+ ```
+
+3. **테스트 데이터 추가**:
+ ```typescript
+ // tests/fixtures/component-test-data.ts
+ export const NEW_COMPONENT_TEST_CASES = [
+ { attr1: 'value1', attr2: 'value2' },
+ ] as const;
+ ```
+
+4. **테스트 작성**:
+ ```typescript
+ // tests/markdown.test.ts
+ it.each(NEW_COMPONENT_TEST_CASES)(
+ 'new-component가 올바르게 렌더링됨',
+ async ({ attr1, attr2 }) => {
+ const markdown = ``;
+ await renderMarkdownWithComponents(contentElement, markdown);
+ await waitForComponentRender();
+
+ const component = contentElement.querySelector('new-component');
+ expect(component).toBeTruthy();
+ }
+ );
+ ```
+
+### 코드 리뷰에서 배운 교훈
+
+1. **중복 코드는 리팩토링의 신호**
+ - 83줄의 중복 코드를 팩토리 패턴으로 제거
+ - DRY (Don't Repeat Yourself) 원칙 준수
+
+2. **테스트는 단순해야 한다**
+ - 하나의 테스트는 하나의 검증만
+ - 복잡한 로직은 헬퍼 함수로 추상화
+
+3. **전역 상태는 위험하다**
+ - cleanup 로직으로 테스트 격리
+ - 전역 상태 백업 및 복원
+
+4. **타입 안정성은 필수다**
+ - `as any` 최소화
+ - 전역 타입 정의로 런타임 에러 방지
+
+5. **설계 원칙 준수의 중요성**
+ - SOLID 원칙 적용으로 확장 가능한 구조
+ - 함수형 프로그래밍 원칙으로 안정성 확보
+
+### 적용 가능한 프로젝트
+
+이 리팩토링 사례는 다음과 같은 프로젝트에 적용 가능합니다:
+
+- **웹 컴포넌트 기반 프로젝트**: Custom Elements API 사용
+- **JSDOM 테스트 환경**: SSR, 정적 사이트 생성기
+- **마크다운 렌더링 시스템**: marked.js, remark, unified
+- **Vitest/Jest 테스트**: 단위 테스트, 통합 테스트
+- **TypeScript 프로젝트**: 타입 안정성이 중요한 프로젝트
+
+### 관련 문서
+
+- [웹 컴포넌트 Self-Closing 태그 문제](/trouble-shooting/web-component-self-closing-tag.md)
+- [홈 페이지 렌더링 문제](/trouble-shooting/home-page-rendering.md)
+- [목차 컴포넌트 이슈](/trouble-shooting/table-of-contents-component-issue.md)
+
+### 참고 자료
+
+- [Vitest Documentation](https://vitest.dev/)
+- [JSDOM Documentation](https://github.com/jsdom/jsdom)
+- [Web Components MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
+- [marked.js Documentation](https://marked.js.org/)
+- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID)
+- [Factory Pattern](https://refactoring.guru/design-patterns/factory-method)
+
+---
+
+**작성일**: 2026-01-27
+**작성자**: Claude Sonnet 4.5 with krsy0411
+**태그**: `#testing`, `#refactoring`, `#web-components`, `#vitest`, `#jsdom`, `#architecture`, `#solid-principles`
diff --git a/public/trouble-shooting/README.md b/public/trouble-shooting/README.md
new file mode 100644
index 0000000..90dd650
--- /dev/null
+++ b/public/trouble-shooting/README.md
@@ -0,0 +1,111 @@
+# 트러블슈팅 모음
+
+Docker 한국어 문서 프로젝트를 개발하면서 마주친 문제들과 해결 과정을 정리한 문서입니다.
+
+## 📚 목록
+
+### 1. [Home 페이지 렌더링 문제](./home-page-rendering.md)
+
+**문제**: 중첩된 HTML div가 텍스트로 표시되는 문제
+
+- **원인**: 복잡한 정규식 기반 파서 로직이 중첩된 div를 올바르게 처리하지 못함
+- **해결**: marked.js의 내장 HTML 처리 기능 활용으로 파서 로직 단순화
+- **결과**: 코드 라인 수 70% 감소, 모든 HTML 구조 정상 렌더링
+
+**핵심 교훈**: 라이브러리가 제공하는 기능을 재구현하지 말고, 단순한 해결책을 우선 고려하기
+
+---
+
+### 2. [웹 컴포넌트 Self-Closing 태그 문제](./web-component-self-closing-tag.md)
+
+**문제**: contributor-component 중 첫 번째만 렌더링되고 나머지는 표시되지 않음
+
+- **원인**: marked.js가 커스텀 요소의 self-closing 태그(``)를 제대로 파싱하지 못함
+- **해결**: 명시적 닫는 태그 사용 (``)
+- **결과**: 4개의 컴포넌트 모두 정상 렌더링
+
+**핵심 교훈**: 라이브러리별 HTML 파싱 규칙 이해, 기존 코드 패턴 참조의 중요성
+
+---
+
+### 3. [TableOfContents 컴포넌트 내부 텍스트 표시 문제](./table-of-contents-component-issue.md)
+
+**문제**: 목차에 기여자 컴포넌트 내부의 username이 제목으로 표시됨
+
+- **원인**: 컴포넌트 내부에서 `
` 태그 사용, table-contents.ts가 모든 h2/h3 수집
+- **해결**: 의미론적으로 올바른 태그 사용 (`
` → `
`)
+- **결과**: 목차에 섹션 제목만 표시, 접근성 개선
+
+**핵심 교훈**: HTML 시맨틱 준수, 컴포넌트 설계 시 외부 시스템에 미치는 영향 고려
+
+---
+
+### 4. [웹 컴포넌트 테스트 코드 리팩토링 및 아키텍처 개선](./2026-01-27-test-code-refactoring-architecture.md)
+
+**문제**: CI 환경에서 마크다운 웹 컴포넌트 렌더링 테스트 실패 및 테스트 코드 품질 문제
+
+- **원인**:
+ - 커스텀 토크나이저가 일부 컴포넌트만 지원
+ - 83줄의 중복 코드, 하나의 테스트에서 여러 검증 수행
+ - 전역 상태 cleanup 로직 없음, 비동기 대기 시간 부족
+- **해결**:
+ - 커스텀 토크나이저를 모든 웹 컴포넌트 지원으로 확장
+ - 팩토리 패턴 도입으로 컴포넌트 생성 로직 추상화
+ - 테스트 환경 추상화 및 전역 상태 관리 개선
+ - 테스트 데이터 분리 및 타입 안정성 개선
+- **결과**:
+ - 코드 36% 감소 (236줄 → 150줄), 테스트 100% 통과 (23/23)
+ - 중복 코드 완전 제거, 새 컴포넌트 추가 용이
+ - 테스트 격리성 보장, 유지보수성 대폭 향상
+
+**핵심 교훈**:
+- 테스트 설계 원칙 준수 (하나의 테스트 = 하나의 검증)
+- 팩토리 패턴과 SOLID 원칙으로 확장 가능한 구조 구축
+- 전역 상태 관리와 cleanup의 중요성
+- 코드 리뷰를 통한 아키텍처 개선
+
+---
+
+## 🎯 공통 교훈
+
+### 1. 단순함의 가치
+복잡한 해결책보다 단순한 해결책이 더 안정적이고 유지보수하기 쉽습니다.
+
+### 2. 라이브러리 활용
+이미 제공되는 기능을 재구현하지 말고, 문서를 꼼꼼히 읽고 활용하세요.
+
+### 3. 웹 표준 준수
+HTML 시맨틱, 접근성, 웹 표준을 준수하면 예기치 않은 문제를 예방할 수 있습니다.
+
+### 4. 기존 코드 참조
+프로젝트 내에서 이미 잘 작동하는 패턴이 있다면, 그 패턴을 따르는 것이 가장 안전합니다.
+
+### 5. 근본 원인 파악
+표면적 증상이 아닌 근본 원인을 찾아 해결해야 재발을 방지할 수 있습니다.
+
+---
+
+## 📖 문서 구조
+
+각 트러블슈팅 문서는 다음과 같은 구조로 작성되어 있습니다:
+
+1. **문제 상황**: 무엇이 문제였는지, 어떤 증상이 나타났는지
+2. **원인 분석**: 왜 문제가 발생했는지, 근본 원인은 무엇인지
+3. **해결 방법**: 어떻게 해결했는지, 구체적인 코드와 함께
+4. **결과**: Before/After 비교, 개선된 점
+5. **교훈**: 이 경험에서 배운 점, 다음에 적용할 수 있는 지식
+
+---
+
+## 🔗 관련 링크
+
+- [프로젝트 README](../../README.md)
+- [기여 가이드](../../CONTRIBUTING.md)
+- [스타일 가이드](../../SYTLE_GUIDE.md)
+- [Claude 프로젝트 가이드](../../CLAUDE.md)
+
+---
+
+## 💡 피드백
+
+트러블슈팅 문서에 대한 피드백이나 추가하고 싶은 내용이 있다면 [이슈](https://github.com/docker-ko/docker-ko.github.io/issues)를 통해 알려주세요!
diff --git a/public/trouble-shooting/home-page-rendering.md b/public/trouble-shooting/home-page-rendering.md
new file mode 100644
index 0000000..f43493b
--- /dev/null
+++ b/public/trouble-shooting/home-page-rendering.md
@@ -0,0 +1,106 @@
+# 트러블슈팅: home.md 페이지 렌더링 문제
+
+## 문제 상황
+
+### 1. 중첩된 HTML div가 텍스트로 표시됨
+
+home.md 파일에 복잡한 HTML 구조(중첩된 div, Tailwind CSS 클래스)를 작성했지만, 브라우저에서 HTML이 파싱되지 않고 텍스트 그대로 표시되는 문제가 발생했습니다.
+
+```markdown
+
+
+
+
+
+ ...
+
+
+
+```
+
+**증상**:
+- HTML 태그가 텍스트로 그대로 화면에 출력됨
+- Tailwind CSS 스타일이 전혀 적용되지 않음
+- 레이아웃이 완전히 깨짐
+
+### 2. 기존 파서 로직의 문제점
+
+기존 `load_md.ts`의 `renderMarkdownWithComponents` 함수는 정규식으로 div 블록을 추출하려고 시도했습니다:
+
+```typescript
+// 문제가 있던 코드
+const divBlockRegex = /(
[\s\S]*?<\/div>)/gi;
+const tokens = mdText.split(divBlockRegex).filter(Boolean);
+
+for (const token of tokens) {
+ if (/^
`에서 멈춤**
+- 중첩된 div 구조를 제대로 추출하지 못함
+- 일부는 HTML로, 일부는 텍스트로 처리되어 렌더링 실패
+
+## 해결 방법
+
+### 파서 로직 단순화
+
+marked.js는 **HTML 태그를 자동으로 통과시키는** 기능이 이미 내장되어 있습니다. 복잡한 정규식으로 분리할 필요가 없었습니다.
+
+**수정된 코드** (`src/scripts/load_md.ts`):
+
+```typescript
+/**
+ * 마크다운과 HTML을 함께 렌더링
+ * marked.js는 HTML 태그(div, card-component, button-component 등)를 자동으로 통과시킴
+ * 웹 컴포넌트는 브라우저가 인식하여 자동으로 렌더링
+ */
+export async function renderMarkdownWithComponents(
+ mdText: string,
+ contentElement: HTMLElement
+) {
+ // marked.js가 마크다운 구문을 파싱하고 HTML은 그대로 통과
+ const html = await marked.parse(mdText);
+ contentElement.innerHTML = html;
+}
+```
+
+### 작동 원리
+
+1. **marked.js의 HTML 처리**: 마크다운 파서가 `
`, `` 등 모든 HTML 태그를 그대로 유지
+2. **마크다운 파싱**: `#`, `##`, `---`, 링크 등 마크다운 구문만 HTML로 변환
+3. **웹 컴포넌트 렌더링**: `customElements.define()`으로 등록된 컴포넌트는 브라우저가 자동으로 렌더링
+
+## 결과
+
+### Before (복잡한 로직)
+- ❌ 정규식으로 div 블록 추출 시도 → 중첩 처리 실패
+- ❌ 웹 컴포넌트 별도 추출 → 불필요한 복잡성
+- ❌ 토큰 단위 분리 처리 → 버그 발생
+
+### After (단순화된 로직)
+- ✅ marked.js가 모든 HTML을 자동으로 처리
+- ✅ 중첩된 div 구조 완벽 렌더링
+- ✅ 커스텀 웹 컴포넌트 정상 작동
+- ✅ 코드 라인 수 70% 감소
+- ✅ 유지보수성 대폭 향상
+
+### 성능 및 안정성
+
+- 파서 로직이 단순해져 **버그 가능성 감소**
+- marked.js의 검증된 HTML 처리 로직 활용
+- 다른 페이지(get-started, docker-concepts 등) 모두 정상 작동 확인
+
+## 교훈
+
+1. **라이브러리 기능 활용**: 이미 제공되는 기능을 재구현하지 말 것
+2. **단순함이 최고**: 복잡한 정규식보다 간단한 해결책이 더 안정적
+3. **문제의 근본 원인 파악**: 표면적 증상이 아닌 근본 원인을 찾아야 함
diff --git a/public/trouble-shooting/table-of-contents-component-issue.md b/public/trouble-shooting/table-of-contents-component-issue.md
new file mode 100644
index 0000000..d10ce6e
--- /dev/null
+++ b/public/trouble-shooting/table-of-contents-component-issue.md
@@ -0,0 +1,200 @@
+# 트러블슈팅: TableOfContents에 컴포넌트 내부 텍스트 표시 문제
+
+## 문제 상황
+
+### 목차에 기여자 이름이 표시됨
+
+home.md 페이지의 우측 TableOfContents(목차)에 **기여자 컴포넌트 내부의 username**이 제목처럼 표시되는 문제가 발생했습니다.
+
+**예상 목차**:
+```
+Table of contents
+- Docker 한국어 문서에 오신 것을 환영합니다
+- 궁금한 점이 있으신가요?
+- 기여자들
+- 시작하기
+```
+
+**실제 목차**:
+```
+Table of contents
+- Docker 한국어 문서에 오신 것을 환영합니다
+- 궁금한 점이 있으신가요?
+- 기여자들
+- krsy0411 ← 불필요한 항목
+- elecbug ← 불필요한 항목
+- g1nya2 ← 불필요한 항목
+- YoonKeumJae ← 불필요한 항목
+- 시작하기
+```
+
+**증상**:
+- 목차에 컴포넌트 내부의 텍스트가 제목으로 인식됨
+- 기여자 이름을 클릭하면 해당 카드로 스크롤 이동
+- 목차가 지저분해지고 사용성 저하
+
+## 원인 분석
+
+### 1. contributor-component 내부 구조 확인
+
+**문제가 있던 코드** (`src/scripts/components/contributor-component.ts`):
+
+```typescript
+render() {
+ const username = this.getAttribute('username') || 'Anonymous';
+ // ...
+
+ this.innerHTML = `
+
+
+```
+
+### 추가 개선사항
+
+사이즈도 함께 조정하여 시각적 계층 구조를 개선:
+- **폰트 크기**: `text-lg` (18px) → `text-base` (16px)
+- **의미**: 제목이 아닌 라벨로 적절한 크기
+
+## 결과
+
+### Before
+- ❌ 목차에 기여자 이름 4개가 제목으로 표시
+- ❌ 의미론적으로 부적절한 HTML 구조 (`
`를 라벨로 사용)
+- ❌ 목차가 길어지고 가독성 저하
+- ❌ 스크린 리더가 잘못된 구조 인식
+
+### After
+- ✅ 목차에 섹션 제목만 표시 (h2, h3 태그만)
+- ✅ 의미론적으로 올바른 HTML 구조 (`
`로 라벨 표현)
+- ✅ 목차가 깔끔하고 탐색하기 쉬움
+- ✅ 접근성 개선 (스크린 리더가 올바른 구조 인식)
+
+### 수정 후 목차
+
+```
+Table of contents
+- Docker 한국어 문서에 오신 것을 환영합니다
+- 궁금한 점이 있으신가요?
+- 기여자들
+- 시작하기
+```
+
+## 교훈
+
+### 1. HTML 시맨틱(의미론) 준수의 중요성
+
+| 태그 | 용도 | 올바른 사용 예시 |
+|------|------|------------------|
+| `