icon

성능 최적화와 프로파일링


 TypeScript 애플리케이션의 성능 최적화는 개발 과정에서 중요한 부분입니다.

 이 절에서는 성능 측정, 분석, 그리고 최적화 기법에 대해 다룹니다.

성능 측정 및 분석 도구

 1. Chrome DevTools

  • Performance 탭 사용
    // 성능 측정 시작
    console.time('performanceTest');
     
    // 측정할 코드
    for (let i = 0; i < 1000000; i++) {
      // 작업 수행
    }
     
    // 성능 측정 종료
    console.timeEnd('performanceTest');
  • Memory 탭을 사용한 메모리 프로파일링

 2. Node.js 프로파일러

node --prof app.js
node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt

 3. 타입스크립트 컴파일러 성능

tsc --diagnostics

컴파일 최적화

 1. incremental 컴파일

{
  "compilerOptions": {
    "incremental": true
  }
}

 2. 프로젝트 참조 사용

{
  "references": [
    { "path": "./tsconfig.shared.json" },
    { "path": "./tsconfig.backend.json" },
    { "path": "./tsconfig.frontend.json" }
  ]
}

 3. 번들 크기 최적화

  • Tree shaking 활용
  • 코드 스플리팅
// 동적 임포트 사용
const module = await import('./heavyModule');

메모리 누수 탐지 및 해결

  1. 클로저 주의
function createLeak() {
  const largeArray = new Array(1000000);
  return () => console.log(largeArray.length);
}
  1. 이벤트 리스너 제거
class Component {
  private handler = () => {
    // 이벤트 처리
  };
 
  mount() {
    document.addEventListener('click', this.handler);
  }
 
  unmount() {
    document.removeEventListener('click', this.handler);
  }
}
  1. WeakMap 및 WeakSet 활용
const cache = new WeakMap();
 
function processObject(obj: object) {
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  const result = // 복잡한 계산
  cache.set(obj, result);
  return result;
}

타입 시스템을 활용한 성능 최적화

  1. 유니온 타입 최적화
type Status = 'idle' | 'loading' | 'success' | 'error';
 
function processStatus(status: Status) {
  switch (status) {
    case 'idle':
      // 처리
      break;
    case 'loading':
      // 처리
      break;
    // ... 기타 케이스
  }
}
  1. 제네릭을 활용한 불필요한 타입 변환 방지
function identity<T>(arg: T): T {
  return arg;
}

고성능 코드 작성 기법

  1. 루프 최적화
// 기존 코드
for (let i = 0; i < array.length; i++) {
  // 처리
}
 
// 최적화된 코드
const len = array.length;
for (let i = 0; i < len; i++) {
  // 처리
}
  1. 효율적인 데이터 구조 선택
// 객체 대신 Map 사용
const cache = new Map<string, number>();
 
function fibonacci(n: number): number {
  if (cache.has(n.toString())) {
    return cache.get(n.toString())!;
  }
  const result = n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
  cache.set(n.toString(), result);
  return result;
}

웹 워커와 서비스 워커 활용

  1. 웹 워커 구현
// worker.ts
const ctx: Worker = self as any;
 
ctx.addEventListener('message', (event: MessageEvent) => {
  // 복잡한 계산 수행
  const result = heavyComputation(event.data);
  ctx.postMessage(result);
});
 
// main.ts
const worker = new Worker('worker.js');
worker.onmessage = (event: MessageEvent) => {
  console.log('Result:', event.data);
};
worker.postMessage(data);
  1. 서비스 워커 구현
// sw.ts
self.addEventListener('fetch', (event: FetchEvent) => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});

고급 TypeScript 기능의 성능 영향

 1. const assertions

const config = {
  endpoint: 'https://api.example.com',
  timeout: 3000
} as const;
  • 컴파일 시간에 더 정확한 타입 추론 가능

 2. Template Literal Types

type EventName<T extends string> = `${T}Changed`;
type UserEvents = EventName<'name' | 'email'>;
// 'nameChanged' | 'emailChanged'
  • 타입 안전성 향상과 동시에 런타임 오버헤드 없음

대규모 프로젝트 성능 모니터링

  1. 지속적 통합(CI) 파이프라인에 성능 테스트 통합
  2. 성능 메트릭 모니터링 도구 사용 (예 : New Relic, Datadog)
  3. 사용자 경험 메트릭 추적 (예 : Core Web Vitals)

종합적인 Best Practices 및 체크리스트

 1. 컴파일 최적화

  • 증분 컴파일 활성화
  • 프로젝트 참조 구조 최적화
  • 불필요한 타입 체크 제거 (skipLibCheck: true)

 2. 번들 최적화

  • Tree shaking 적용
  • 코드 스플리팅 구현
  • 동적 임포트 활용

 3. 런타임 성능

  • 고성능 데이터 구조 사용 (Map, Set 등)
  • 루프 및 반복 최적화
  • 메모이제이션 기법 적용

 4. 메모리 관리

  • 주기적인 메모리 프로파일링 수행
  • 이벤트 리스너 및 타이머 적절히 해제
  • 순환 참조 방지

 5. 비동기 처리

  • Promise 체인 최적화
  • async/await 적절히 활용
  • 웹 워커 사용 고려

 6. 타입 시스템 활용

  • 유니온 타입을 통한 조건부 로직 최적화
  • const assertions 활용
  • 제네릭을 통한 재사용성 및 타입 안전성 향상

 7. 테스트 및 모니터링

  • 성능 회귀 테스트 구현
  • 프로덕션 환경 모니터링 설정
  • 사용자 중심 성능 메트릭 추적

 TypeScript 애플리케이션의 성능 최적화는 컴파일 시간부터 런타임까지 다양한 측면을 고려해야 합니다.

 컴파일러 옵션을 적절히 조정하여 빌드 시간을 단축하고, 효율적인 코드 작성 기법을 통해 런타임 성능을 개선할 수 있습니다.

 타입 시스템을 잘 활용하면 컴파일 타임에 많은 오류를 잡아낼 수 있어, 결과적으로 런타임 성능 향상에도 기여합니다.

 예를 들어, 유니온 타입과 타입 가드를 효과적으로 사용하면 런타임에 불필요한 타입 체크를 줄일 수 있습니다.

 메모리 관리는 TypeScript 애플리케이션에서도 중요한 요소입니다. 클로저, 이벤트 리스너, 그리고 큰 객체 참조 등을 주의 깊게 관리해야 합니다. WeakMap과 WeakSet 같은 도구를 활용하면 메모리 누수 위험을 줄일 수 있습니다.