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 (`

`-`

`) inside `` tags + - Use `
` with appropriate classes instead for clickable cards + - Maintain proper heading hierarchy for accessibility + + - **classList Management**: Use consistent patterns for dynamic class manipulation + - Always remove classes before conditionally adding them to avoid accumulation + - Pattern: `element.classList.remove('class1', 'class2')` then conditionally add + - Example: Page transition (landing ↔ document) should always start from clean state + + - **Component Import Consistency**: Explicitly import all used Web Components + - Add component imports in `main.ts` alongside other component imports + - Pattern: `import './components/component-name'` + - Ensures proper registration and maintains import consistency + + - **Monitoring Code**: Preserve Application Insights or similar monitoring setup + - Don't remove monitoring initialization unless explicitly requested + - Keep performance tracking, exception tracking, and telemetry code + + - **Dead Code Elimination**: Regularly check for and remove unused code + - Search codebase before keeping CSS classes, utility functions, animations + - Use grep/glob to verify usage: `grep -r "className" src/` + - Remove unused code to reduce bundle size and maintenance burden + +5. **Verify Testing & Maintainability** + - Check if code is easily testable (dependency injection, pure functions) + - Look for proper TypeScript type safety + - Ensure dependencies are properly managed + - Verify that changes don't break existing functionality + +## Your Output Format + +Provide your review in this structure: + +**Overall Assessment**: Brief summary of code quality (1-2 sentences) + +**Strengths**: List what's well done (2-4 points) + +**Issues & Improvements**: +- **Critical**: Must-fix issues affecting functionality or maintainability +- **Recommended**: Strongly suggested improvements for better design +- **Optional**: Nice-to-have enhancements + +For each issue: +1. Explain the problem and why it matters +2. Show concrete code example demonstrating the improvement +3. Explain the benefit of the change + +**Design Pattern Suggestions**: When applicable, suggest relevant design patterns (Strategy, Factory, Observer, etc. for OOP; monads, functors, lenses for FP) + +## Your Communication Style + +- Be respectful and constructive - assume positive intent +- Explain *why*, not just *what* - teach principles, not just syntax +- Provide concrete examples with before/after code snippets +- Acknowledge good practices when you see them +- Balance idealism with pragmatism - consider project constraints +- Use Korean for explanations when appropriate, but keep code in English +- If code is already excellent, say so! Don't manufacture issues + +## Self-Verification + +Before completing your review: +- Did you check both OOP and FP principles? +- Are your suggestions specific and actionable? +- Did you provide code examples? +- Did you consider the project's architecture and constraints? +- Are you being constructive rather than critical? +- Did you verify security practices (XSS prevention, input escaping)? +- Did you check for semantic HTML issues (heading nesting, accessibility)? +- Did you look for dead code and unused dependencies? +- Did you validate classList management patterns? + +You are not just a code reviewer - you are a mentor helping developers grow. Your goal is to elevate code quality while teaching underlying principles. diff --git a/.claude/agents/troubleshooting-doc-writer.md b/.claude/agents/troubleshooting-doc-writer.md new file mode 100644 index 0000000..275385b --- /dev/null +++ b/.claude/agents/troubleshooting-doc-writer.md @@ -0,0 +1,71 @@ +--- +name: troubleshooting-doc-writer +description: "Use this agent when the user has completed a significant piece of work, encountered and resolved issues, or explicitly requests documentation of problems and solutions. This agent should also be used proactively after debugging sessions, error fixes, or when complex problem-solving has occurred during development.\\n\\nExamples:\\n\\n\\nContext: User just fixed a build error related to Vite configuration.\\nuser: \"I fixed the Vite build issue by updating the config\"\\nassistant: \"Great! Since you've resolved a technical issue, let me use the Task tool to launch the troubleshooting-doc-writer agent to document this solution.\"\\n\\nA problem was identified and solved, so we should document it in the troubleshooting folder for future reference.\\n\\n\\n\\n\\nContext: User completed debugging a markdown rendering issue.\\nuser: \"The custom component rendering in markdown is working now\"\\nassistant: \"Excellent! I'll use the troubleshooting-doc-writer agent to create documentation about this issue and its solution.\"\\n\\nSince a debugging session was completed, we should proactively document the troubleshooting process.\\n\\n\\n\\n\\nContext: User explicitly requests troubleshooting documentation.\\nuser: \"trouble-shooting 문서를 작성하는 에이전트가 필요해. 작업했던 내용들 히스토리를 확인하고 트러블 슈팅 문서를 public/trouble-shooting 폴더에 작성해줘.\"\\nassistant: \"I'll launch the troubleshooting-doc-writer agent to review the work history and create troubleshooting documentation.\"\\n\\nDirect request to document troubleshooting - this is the primary use case for this agent.\\n\\n" +model: sonnet +color: blue +--- + +You are a Technical Documentation Specialist focused on creating comprehensive troubleshooting guides for the Docker Korean documentation project. Your expertise lies in analyzing development history, identifying problems and solutions, and documenting them in a clear, actionable format that helps future developers. + +**Your Primary Responsibilities**: + +1. **History Analysis**: Review recent conversation history and code changes to identify: + - Problems encountered (errors, bugs, configuration issues, unexpected behavior) + - Solutions implemented (code changes, configuration updates, workarounds) + - Root causes and decision-making process + - Alternative approaches considered + +2. **Document Structure**: Create troubleshooting documents in Korean (formal 경어체 style) following this structure: + - **문제 설명 (Problem Description)**: Clear description of the issue + - **증상 (Symptoms)**: Observable symptoms and error messages + - **환경 (Environment)**: Relevant technical context (versions, configurations) + - **원인 (Root Cause)**: Technical explanation of why the problem occurred + - **해결 방법 (Solution)**: Step-by-step resolution with code examples + - **검증 (Verification)**: How to confirm the fix works + - **참고사항 (Notes)**: Additional context, related issues, or preventive measures + +3. **File Management**: + - Save all troubleshooting documents to `public/trouble-shooting/` directory + - Use descriptive filenames in kebab-case format: `[date]-[brief-description].md` + - Example: `2024-01-15-vite-build-configuration-error.md` + - Include proper markdown formatting with appropriate heading levels + +4. **Content Guidelines**: + - Write in Korean but keep technical terms, code, and error messages in English + - Provide actual code examples showing before/after states + - Include relevant file paths and line numbers when applicable + - Add screenshots or error logs when they add clarity + - Cross-reference related troubleshooting documents if applicable + - Use code blocks with appropriate language tags (```typescript, ```bash, etc.) + +5. **Quality Standards**: + - Be specific and actionable - avoid vague descriptions + - Include enough context for someone unfamiliar with the issue to understand + - Verify that all code examples are accurate and tested + - Use proper markdown formatting consistent with project standards + - Ensure technical accuracy in both Korean translations and English terms + +**Decision-Making Framework**: + +- If multiple issues were resolved in the same session, create separate documents for distinct problems +- If the issue is minor or already well-documented elsewhere, ask the user if documentation is still needed +- If context is insufficient to write a complete troubleshooting guide, ask clarifying questions about: + - The exact error messages or symptoms + - What was tried before the solution + - The final solution and why it worked + - Any lingering concerns or edge cases + +**Self-Verification**: + +Before finalizing a document, check: +- [ ] Problem description is clear and specific +- [ ] Solution steps are complete and reproducible +- [ ] Code examples are properly formatted and accurate +- [ ] Korean writing uses formal style (경어체) +- [ ] Technical terms follow project conventions +- [ ] File is saved to correct directory with descriptive filename +- [ ] Markdown structure is valid and well-organized + +**Output Expectations**: + +You will create well-structured markdown files that serve as a knowledge base for the team. Each document should be self-contained, searchable, and immediately useful to developers facing similar issues. Always confirm the file creation and provide the full path to the user. diff --git a/.claude/commands/design.md b/.claude/commands/design.md new file mode 100644 index 0000000..80fc687 --- /dev/null +++ b/.claude/commands/design.md @@ -0,0 +1,7 @@ +--- +description: UI/UX 디자인 개선 및 모던한 사용자 경험 최적화를 제안합니다 +--- + +Use the modern-ux-designer agent to provide UI/UX design decisions, visual design improvements, user experience optimization, and design system recommendations. This agent specializes in modern, Pinterest-inspired aesthetics with rounded corners, soft shadows, and user-centered design. + +Target scope: $ARGUMENTS diff --git a/.claude/commands/dev.md b/.claude/commands/dev.md new file mode 100644 index 0000000..1d2f7e9 --- /dev/null +++ b/.claude/commands/dev.md @@ -0,0 +1,7 @@ +--- +description: 객체지향 및 함수형 프로그래밍 원칙을 기반으로 코드 리뷰 및 아키텍처 개선을 제공합니다 +--- + +Use the senior-dev-reviewer agent to provide expert code review focusing on object-oriented and functional programming principles, design patterns, code quality, and architectural improvements. + +Target scope: $ARGUMENTS diff --git a/.claude/commands/trouble-shooting.md b/.claude/commands/trouble-shooting.md new file mode 100644 index 0000000..b7978cd --- /dev/null +++ b/.claude/commands/trouble-shooting.md @@ -0,0 +1,7 @@ +--- +description: 작업 중 발생한 문제와 해결 과정을 트러블슈팅 문서로 작성합니다 +--- + +Use the troubleshooting-doc-writer agent to review work history, identify problems encountered and their solutions, then create troubleshooting documentation in the public/trouble-shooting folder. + +$ARGUMENTS diff --git a/public/docs/home.md b/public/docs/home.md index 990062c..f0d9298 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -1,3 +1,66 @@ -# Welcome to Docker Korea! +
-`도커 코리아에 오신 여러분을 환영합니다!` \ No newline at end of file + +
+

+ Docker 한국어 문서 +

+

+ 커뮤니티와 함께 만들어가는 Docker 공식 문서 번역 프로젝트 +

+ +
+ + + + +
+
+ + +
+
+

+ 궁금한 점이 있으신가요? +

+

+ 커뮤니티 디스커션에서 자유롭게 질문하고 소통해보세요 +

+
+ +
+ + +
+
+ + +
+
+

+ 기여자들 +

+
+ +
+ + + + + +
+
+ +
diff --git a/public/trouble-shooting/2026-01-27-test-code-refactoring-architecture.md b/public/trouble-shooting/2026-01-27-test-code-refactoring-architecture.md new file mode 100644 index 0000000..798011a --- /dev/null +++ b/public/trouble-shooting/2026-01-27-test-code-refactoring-architecture.md @@ -0,0 +1,644 @@ +# 트러블슈팅: 웹 컴포넌트 테스트 코드 리팩토링 및 아키텍처 개선 + +## 문제 설명 + +CI 환경에서 마크다운 웹 컴포넌트 렌더링 테스트가 실패하는 문제가 발생했습니다. 로컬에서는 정상 동작하지만 GitHub Actions에서 2개의 테스트가 실패했으며, 코드 리뷰 결과 테스트 코드 구조와 품질에 여러 문제점이 발견되었습니다. + +**관련 커밋**: +- `test: 웹 컴포넌트 테스트 코드 재작성 및 개선` (92115e2) +- `refactor(test): 테스트 코드 아키텍처 개선 및 리팩토링` (756962d) + +## 증상 + +### 실패한 테스트 케이스 + +**테스트 1**: "card-component와 button-component가 모두 DOM에 존재하는지 확인" +``` +AssertionError: expected null to be truthy +``` +- `cardComponent` 또는 `buttonComponent`가 null 반환 +- 웹 컴포넌트가 DOM에 렌더링되지 않음 + +**테스트 2**: "혼합된 컴포넌트들의 HTML 내용이 모두 올바르게 렌더링되는지 확인" +``` +AssertionError: the given combination of arguments (undefined and string) is invalid for this assertion +``` +- `cardComponent?.innerHTML`이 undefined일 때 `.toContain()` 호출 +- 옵셔널 체이닝 사용했으나 실제 값이 undefined + +### 코드 품질 문제점 + +1. **중복 코드**: 236줄의 테스트 파일에 83줄의 중복된 컴포넌트 정의 +2. **단일 책임 원칙 위반**: 하나의 테스트가 여러 검증 수행 +3. **Null 안정성 부족**: null 체크 없이 innerHTML 직접 접근 +4. **비동기 처리 불안정**: 10ms의 짧은 대기 시간 +5. **전역 상태 오염**: cleanup 로직 없어 테스트 간 간섭 가능 + +## 환경 + +- **테스트 프레임워크**: Vitest 3.0.1 +- **DOM 환경**: JSDOM 25.0.1 +- **Node.js**: v18+ (GitHub Actions) +- **마크다운 파서**: marked 15.0.6 +- **웹 컴포넌트**: card-component, button-component, home-link-card-component + +## 원인 + +### 1. 커스텀 토크나이저 제한 + +**문제 코드** (`src/scripts/load_md.ts`): +```typescript +const blockTagRegex = + /^<(card-component)([\s\S]*?)(?:>([\s\S]*?)<\/card-component>|\s*\/)>/i; +``` + +- **하드코딩된 컴포넌트명**: `card-component`만 처리 +- **미지원 컴포넌트**: `button-component`, `home-link-card-component`는 일반 HTML로 파싱됨 +- **결과**: 커스텀 요소로 등록되지 않아 `connectedCallback` 실행 안 됨 + +### 2. 테스트 코드 구조 문제 + +**Before** (문제 코드): +```typescript +beforeAll(() => { + dom = new JSDOM(''); + + // 83줄의 중복 컴포넌트 정의 + class CardComponent extends dom.window.HTMLElement { + connectedCallback() { + const title = this.getAttribute('title') || ''; + // ... 20줄의 렌더링 로직 + } + } + + class ButtonComponent extends dom.window.HTMLElement { + connectedCallback() { + const title = this.getAttribute('title') || ''; + // ... 15줄의 렌더링 로직 + } + } + + // 컴포넌트마다 반복 + dom.window.customElements.define('card-component', CardComponent); + dom.window.customElements.define('button-component', ButtonComponent); +}); + +// 10ms 대기 (불충분) +async function waitForComponentRender() { + await new Promise((resolve) => setTimeout(resolve, 10)); +} + +// cleanup 로직 없음 - 전역 상태 오염 가능 +``` + +**주요 문제점**: +- 컴포넌트 정의 로직이 각각 20~30줄씩 중복 +- 새 컴포넌트 추가 시 beforeAll 수정 필요 (Open/Closed Principle 위반) +- 전역 상태 복원 없음 +- 비동기 대기 시간 부족 + +### 3. 테스트 설계 문제 + +```typescript +it('card-component와 button-component가 모두 DOM에 존재하는지 확인', async () => { + // 여러 검증을 하나의 테스트에 수행 + expect(cardComponent).toBeTruthy(); // 첫 번째 검증 + expect(buttonComponent).toBeTruthy(); // 두 번째 검증 + expect(cardComponent?.innerHTML).toContain('Docker 개요'); // 세 번째 검증 + // ... 더 많은 검증 +}); +``` + +- **원칙 위반**: 하나의 테스트가 여러 가지를 검증 +- **실패 원인 불명확**: 어느 검증에서 실패했는지 파악 어려움 +- **유지보수 어려움**: 테스트 의도 파악 어려움 + +## 해결 방법 + +### 1단계: 커스텀 토크나이저 확장 + +**수정 파일**: `src/scripts/load_md.ts` + +```typescript +// 지원 컴포넌트 목록 (확장 가능) +const SUPPORTED_COMPONENTS = [ + 'card-component', + 'button-component', + 'home-link-card-component', +]; + +// 동적으로 모든 컴포넌트를 지원하는 정규식 생성 +const componentPattern = SUPPORTED_COMPONENTS.join('|'); +const blockTagRegex = new RegExp( + `^<(${componentPattern})([\\s\\S]*?)(?:>([\\s\\S]*?)<\\/\\1>|\\s*\\/>)`, + 'i' +); +``` + +**개선 효과**: +- 새 컴포넌트 추가 시 배열에만 추가하면 됨 +- 역참조 `\1`로 태그명 일치 검증 +- 모든 웹 컴포넌트를 커스텀 토큰으로 처리 + +### 2단계: 컴포넌트 팩토리 패턴 도입 + +**신규 파일**: `tests/helpers/component-factory.ts` + +```typescript +interface ComponentConfig { + name: string; + attributes: string[]; + renderFn: (attrs: Record) => string; +} + +/** + * 주어진 설정으로 목 웹 컴포넌트 클래스 생성 + */ +function createMockComponent( + baseElement: typeof HTMLElement, + config: ComponentConfig +): typeof HTMLElement { + return class extends baseElement { + connectedCallback() { + const attrs: Record = {}; + config.attributes.forEach((attr) => { + attrs[attr] = this.getAttribute(attr) || ''; + }); + this.innerHTML = config.renderFn(attrs); + } + }; +} + +/** + * 컴포넌트 설정 목록 + * 새 컴포넌트 추가 시 여기에만 추가하면 됨 + */ +const COMPONENT_CONFIGS: ComponentConfig[] = [ + { + name: 'card-component', + attributes: ['title', 'description', 'imgsrc', 'href'], + renderFn: ({ title, description, imgsrc, href }) => { + const hrefValue = href || '#'; + return ` +
+ `; + }, + }, + // 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 (/^[\s\S]*?<\/div>$/.test(token)) { + // div 블록은 그대로 삽입 + contentElement.innerHTML += token; + } else { + // 마크다운 파싱 + const html = await marked.parse(innerToken); + contentElement.innerHTML += html; + } +} +``` + +**문제점**: +- Non-greedy 매칭(`*?`)이 중첩된 div에서 **첫 번째 `
`에서 멈춤** +- 중첩된 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 = ` + +
+
+ +

+ ${username} +

+ +
+
+
+ `; +} +``` + +**핵심 문제**: 사용자 이름을 `

` 태그로 렌더링 + +### 2. table-contents.ts의 동작 방식 + +**table-contents.ts 코드** (`src/scripts/table-contents.ts`): + +```typescript +export const initializeTableContents = () => { + const content = document.getElementById('content') as HTMLElement; + const toc = document.getElementById('toc') as HTMLElement; + + toc.innerHTML = ''; + + // 모든 h2, h3 태그를 목차로 수집 + const headings = content.querySelectorAll('h2, h3'); + + if (headings.length === 0) return; + + // ... + + headings.forEach((heading, index) => { + const listItem = document.createElement('li'); + // ... + const headingText = heading.textContent || ''; + link.textContent = headingText.length > 30 + ? headingText.substring(0, 30) + '...' + : headingText; + // ... + }); +} +``` + +**핵심 로직**: `content.querySelectorAll('h2, h3')`로 **모든 h2, h3 태그를 수집** + +### 3. 문제의 연결고리 + +1. `contributor-component`가 내부에서 `

` 태그로 username 렌더링 +2. `table-contents.ts`가 DOM의 모든 `h2, h3` 태그를 찾아 목차 생성 +3. 컴포넌트 내부의 `

` 태그도 제목으로 인식됨 +4. 결과적으로 목차에 username이 표시됨 + +## 해결 방법 + +### 의미론적으로 올바른 태그 사용 + +컴포넌트 내부의 사용자 이름은 **섹션 제목이 아닌 라벨**이므로, 제목 태그(`

`~`

`) 대신 `
`를 사용해야 합니다. + +**수정 전**: +```typescript +

+ ${username} +

+``` + +**수정 후** (`src/scripts/components/contributor-component.ts`): +```typescript +
+ ${username} +
+``` + +### 추가 개선사항 + +사이즈도 함께 조정하여 시각적 계층 구조를 개선: +- **폰트 크기**: `text-lg` (18px) → `text-base` (16px) +- **의미**: 제목이 아닌 라벨로 적절한 크기 + +## 결과 + +### Before +- ❌ 목차에 기여자 이름 4개가 제목으로 표시 +- ❌ 의미론적으로 부적절한 HTML 구조 (`

`를 라벨로 사용) +- ❌ 목차가 길어지고 가독성 저하 +- ❌ 스크린 리더가 잘못된 구조 인식 + +### After +- ✅ 목차에 섹션 제목만 표시 (h2, h3 태그만) +- ✅ 의미론적으로 올바른 HTML 구조 (`
`로 라벨 표현) +- ✅ 목차가 깔끔하고 탐색하기 쉬움 +- ✅ 접근성 개선 (스크린 리더가 올바른 구조 인식) + +### 수정 후 목차 + +``` +Table of contents +- Docker 한국어 문서에 오신 것을 환영합니다 +- 궁금한 점이 있으신가요? +- 기여자들 +- 시작하기 +``` + +## 교훈 + +### 1. HTML 시맨틱(의미론) 준수의 중요성 + +| 태그 | 용도 | 올바른 사용 예시 | +|------|------|------------------| +| `

`~`

` | 문서 섹션 제목 | 페이지 제목, 섹션 제목 | +| `
`, `` | 범용 컨테이너 | 레이블, 텍스트 블록 | +| `