Google Lighthouse 오픈소스 기여

Category
OpenSource
Status
Published
Tags
OpenSource
Google
Lighthouse
Description
Published
Slug

기여 대상 프로젝트

  • 규모: 29.3k Stars, 전 세계 수백만 개발자 사용
  • 역할: 웹 성능 측정 및 최적화 도구
  • 해결한 이슈: #14521 (2022년 11월부터 오픈)
 

발생하던 이슈

Lighthouse의 non-composited-animations audit에서 CSS 커스텀 프로퍼티(--variable)와
일반 CSS 프로퍼티가 동일한 오류 메시지로 혼재되어 표시되는 UX 문제
GitHubGitHubadd new UIString for non-composited-animations due to custom properties · Issue #14521 · GoogleChrome/lighthouse
 

문제 상황

기존 Lighthouse의 non-composited-animations 감사에서는
CSS 커스텀 속성(CSS variables, --로 시작)과 일반 CSS 속성을 구분하지 않고
동일한 오류 메시지로 표시했습니다.
/* 문제가 되는 애니메이션 예시 */ @keyframes problematic { from { --swing-y: 0; /* 커스텀 속성 - 컴포지터에서 지원 안됨 */ height: 100px; /* 일반 속성 - 레이아웃 변경 유발 */ } to { --swing-y: 10px; height: 200px; } }
기존 출력:
❌ Unsupported CSS Properties: --swing-y, height
개선된 출력:
❌ Unsupported CSS Properties: height ❌ Custom CSS properties cannot be animated on the compositor: --swing-y
 

문제 분석

  1. 사용자 혼란: 커스텀 프로퍼티와 일반 프로퍼티는 실패 원인이 다름
  1. 기술적 차이:
      • 일반 CSS: 특정 조건에서 컴포지터 지원 가능
      • 커스텀 프로퍼티: 구조적으로 컴포지터에서 작동 불가
  1. 개발자 경험: 문제 해결을 위한 명확한 방향성 부족
→ 개발자 입장: "--swing-y는 뭐가 다른거지? 왜 color랑 같이 나오지?" 이라는 의문이 들 수 있음
 

Lighthouse Architecture 분석

lighthouse/ ├── core/ │ ├── audits/ │ │ └── non-composited-animations.js # 수정 대상 │ └── test/audits/ │ └── non-composited-animations-test.js # 테스트 추가 ├── shared/ └── report/
 

핵심 파일 구조 분석

non-composited-animations.js 구조

// 1. 국제화 문자열 정의 영역 const UIStrings = { title: '...', description: '...', unsupportedCSSProperty: '...', # 기존 unsupportedCustomCSSProperty: '...' # 새로 추가 }; // 2. 실패 사유 매핑 테이블 const ACTIONABLE_FAILURE_REASONS = [ { flag: 1 << 13, text: UIStrings.unsupportedCSSProperty }, { flag: 1 << 11, text: UIStrings.transformDependsBoxSize }, // ... 기타 사유들 ]; // 3. 핵심 로직 함수 function getActionableFailureReasons(failureCode, unsupportedProperties) { // 이 함수를 주요 개선 대상으로 선정 } // 4. 메인 감사 클래스 class NonCompositedAnimations extends Audit { static get meta() { /* 메타데이터 정의 */ } static async audit(artifacts) { /* 감사 실행 로직 */ } }

구현한 솔루션

1. 국제화 문자열 확장

새로운 UI 문자열을 추가하여 커스텀 속성에 특화된 메시지 정의
unsupportedCustomCSSProperty: `{propertyCount, plural, =1 {Custom CSS properties cannot be animated on the compositor: {properties}} other {Custom CSS properties cannot be animated on the compositor: {properties}} }`,
기술적 고려사항
  • ICU MessageFormat 활용으로 단수/복수 처리
  • 다국어 지원을 위한 구조적 설계
  • Google의 UI 가이드라인 준수
 

2. 속성 분류 알고리즘

function getActionableFailureReasons(failureCode, unsupportedProperties) { return ACTIONABLE_FAILURE_REASONS .filter(reason => failureCode & reason.flag) // 비트마스킹으로 해당 사유 필터링 .map(reason => { if (reason.text === UIStrings.unsupportedCSSProperty) { // 분류 로직 const customProperties = new Set(); const nonCustomProperties = new Set(); for (const property of unsupportedProperties) { if (property.startsWith('--')) { // CSS 커스텀 속성 식별 customProperties.add(property); } else { nonCustomProperties.add(property); } } const reasons = []; // 각 유형별 별도 메시지 생성 if (nonCustomProperties.size > 0) { reasons.push(str_(UIStrings.unsupportedCSSProperty, { propertyCount: nonCustomProperties.size, properties: Array.from(nonCustomProperties).join(', '), })); } if (customProperties.size > 0) { reasons.push(str_(UIStrings.unsupportedCustomCSSProperty, { propertyCount: customProperties.size, properties: Array.from(customProperties).join(', '), })); } return reasons; } return str_(reason.text); }) .flat(); // 중첩 배열 평면화 }
알고리즘 특징
  • O(n) 시간 복잡도: 효율적인 단일 루프 처리
  • Set 자료구조 활용: 중복 제거 및 빠른 검색
  • 함수형 프로그래밍: map/filter/flat을 활용한 선언적 코드
  • 확장성: 새로운 속성 유형 추가 시 쉽게 확장 가능
 

3. 데이터 플로우 설계

Chrome DevTools Protocol ↓ TraceElements Artifact ↓ NonCompositedAnimations.audit() ↓ getActionableFailureReasons() // 개선된 함수 ↓ 분류된 메시지 출력 ↓ Lighthouse Report

테스트 아키텍처

describe('Custom CSS Properties Separation', () => { // 시나리오 1: 혼합 속성 처리 it('separates custom CSS properties from regular properties', async () => { const artifacts = createTestArtifacts({ unsupportedProperties: ['--swing-y', '--rotation', 'color', 'height'] }); const result = await NonCompositedAnimationsAudit.audit(artifacts); // 검증: 2개의 별도 메시지 생성 expect(subItems).toHaveLength(2); expect(failureReasons[0]).toBeDisplayString('Unsupported CSS Properties: color, height'); expect(failureReasons[1]).toBeDisplayString('Custom CSS properties cannot be animated on the compositor: --swing-y, --rotation'); }); // 시나리오 2: 커스텀 속성만 it('handles animations with only custom CSS properties', async () => { // 커스텀 속성만 있을 때 적절한 메시지 표시 검증 }); // 시나리오 3: 일반 속성만 it('handles animations with only regular CSS properties', async () => { // 기존 동작 유지 검증 }); });
 
  • 경계값 테스트: 각 속성 유형별 독립적 처리 검증
  • 회귀 테스트: 기존 기능이 손상되지 않음을 보장
  • 통합 테스트: 실제 Lighthouse 환경과 동일한 조건에서 검증

 

Chrome Compositor 이해

// 실패 사유 비트마스킹 시스템 활용 const ACTIONABLE_FAILURE_REASONS = [ { flag: 1 << 13, text: UIStrings.unsupportedCSSProperty }, // ↑ 8192 (비트 13번째) - 지원되지 않는 CSS 속성 ]; // 비트 연산으로 실패 사유 확인 if (failureCode & reason.flag) { // 해당 사유로 인한 컴포지터 실패 }
기술적 통찰
  • 비트마스킹 패턴: Chrome 내부 실패 사유를 효율적으로 인코딩/디코딩
  • 성능 최적화: O(1) 시간 복잡도로 실패 사유 확인
  • 메모리 효율성: 단일 정수로 여러 실패 사유 표현
 

국제화 시스템 활용

// ICU MessageFormat 활용한 복수형 처리 const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); // 사용 예시 str_(UIStrings.unsupportedCustomCSSProperty, { propertyCount: properties.length, // 자동 단수/복수 선택 properties: properties.join(', ') // 동적 속성 목록 });

 

결과 비교

Before (기존)

사용자가 보는 메시지: "Unsupported CSS Properties: --custom-var, background-color"

After (고친 후)

사용자가 보는 메시지: 1. "Unsupported CSS Properties: background-color" 2. "Custom CSS properties cannot be animated on the compositor: --custom-var" "커스텀 변수는 원래 GPU에서 안되는구나, background-color는 다른 이유구나"