pile·
프론트엔드·코인원코인원·

Change Detection 중심 Angular 최적화 방법

코인원이 쓰는 Angular 에서 Change Detection 을 최적화하는 방법을 예제로 짚어가는 글이다. 최적화 축을 실행 횟수와 한 번 실행될 때의 CPU 사용 둘로 나누고, OnPush 전략부터 Zone 밖 이벤트 바인딩, detectChanges 수동 호출, immer 를 통한 참조 유지까지 단계별로 보여준다.

핵심 포인트
  • Change Detection 은 DOM 을 업데이트하는 게 아니라 업데이트할지 체크만 하는 과정이다. 다만 그 체크가 매우 빈번해 최적화 대상이 된다.
  • 기본 전략에서는 한 컴포넌트에서 이벤트가 나면 아무 상관없는 컴포넌트들까지 전부 Change Detection 이 돈다. 형제 컴포넌트도 예외가 아니다.
  • 첫 조치는 모든 컴포넌트의 changeDetection 을 OnPush 로 바꾸는 것이다. 그러면 이벤트 발생 컴포넌트와 그 상위만 돈다.
  • Angular 의 click 바인딩은 Zone 안에서 로직을 돌려 Change Detection 을 유발한다. RxJS 의 fromEvent 로 Zone 밖에서 바인딩하면 Angular 가 이벤트 존재를 몰라 불필요한 실행이 사라진다.
  • 값이 변한 컴포넌트만 돌리는 유일한 방법은 ChangeDetectorRef 의 detectChanges 를 직접 호출하는 것이다.
상세 정리
  • 예제는 parent 와 child-one, child-two 세 컴포넌트로 구성하고 각자 Change Detection 이 돌 때 콘솔에 로그를 남기게 했다.
  • 기본 전략에서는 앱이 처음 뜰 때 컴포넌트마다 두 번씩 돈다. OnPush 로 바꾸면 한 번씩만 돈다.
  • Zone 밖 바인딩의 부작용도 짚는다. 이벤트로 Change Detection 이 안 돌기 때문에 컴포넌트 프로퍼티 값은 갱신돼 콘솔에 찍히지만 템플릿에 바인딩된 값은 그대로다.
  • 이를 위해 async pipe 를 쓴다. 템플릿에서 Observable 을 구독해 값이 올 때마다 Change Detection 을 실행시킨다.
  • 하지만 async pipe 는 현재 컴포넌트와 모든 상위 컴포넌트에 markForCheck 를 걸어 상위까지 함께 돈다. 상위가 없거나 상위가 돌아도 문제없을 때 편의상 쓰라는 결론을 낸다.
  • detectChanges 를 쓰면 값 변경 후 자기 컴포넌트만 갱신돼 부모의 Change Detection 이 돌지 않는다.
  • props 전달도 실행 조건이다. Angular 는 부모에서 받은 값이 이전 참조와 다른지 엄격 동등 비교로 판단한다.
  • 숫자 같은 primitive 는 값 전달이라 같은 0 을 계속 넘기면 자식은 돌지 않고, 값이 매번 달라지면 자식도 돈다. 여기까지는 의도대로다.
  • 문제는 Object 나 Array 다. 참조 전달이라 모양이 같아도 새로 만든 객체는 다른 것으로 판정돼 내부 값이 그대로여도 자식의 Change Detection 이 매번 돈다.
  • 해결책 두 가지를 든다. Immutable.js 나 immer 처럼 Structural Sharing 을 하는 라이브러리를 쓰거나, 다름의 기준을 엄격 동등 비교가 아닌 수동 체크로 바꾸는 것이다. 두 번째는 네이티브 DOM 을 다루는 서드파티 라이브러리에 유용하지만 복잡해 다루지 않는다.
  • immer 의 produce 는 값이 실제로 바뀌지 않으면 인자로 받은 객체를 그대로 반환한다. 참조가 같아 자식은 돌지 않고, 값이 바뀌면 새 객체를 만들어 정상적으로 돈다.
  • 두 번째 축은 Change Detection 한 번의 무게다. 템플릿에 바인딩된 메서드가 무거우면 실행될 때마다 불려 UI 스레드를 막는다.
  • 이를 보여주려고 인자만큼 for 문을 도는 heavyCalc 를 템플릿에 바인딩하고, 애니메이션이 걸린 progress 태그로 렌더링이 멈추는 것을 눈에 보이게 만들었다.
  • 서두에서 프레임워크가 알아서 해준다는 생각 때문에 성능 최적화가 방치되기 쉽다는 점을 지적하며, 가장 빠른 프론트엔드 프레임워크는 프레임워크를 쓰지 않는 것이라는 비교도 인용한다.
왜 읽나Angular 앱이 느려졌는데 원인을 못 짚고 있는 프론트엔드 개발자에게 Change Detection 이 언제 도는지와 단계별 차단 수단을 예제로 보여주는 글.
코인원
코인원 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2