컴알못 블로그 About

자바스크립트 센트리는 어떻게 동작할까?

내가 일하고 있는 팀에선 완벽한 코드를 작성하는 것을 목표로 하지 않는다. 대신, 신속하게 프로덕트를 만들고 에러가 발생하면 빠르게 수정하는 것을 목표로 한다. 에러는 당연히 발생할 수 있다고 여긴다. 하지만 에러가 빠르게 수정되지 않는 건 문제로 여긴다. 따라서 에러를 즉각적으로 탐지하는 게 아주 중요하다.

센트리는 프로그램에서 발생하는 의도치 않은 에러를 쉽게 탐지할 수 있도록 도와주는 서비스다. 센트리를 이용하면 여러가지 규칙에 따라 에러 발생 알림을 받아볼 수 있다. 예를 들어 ‘똑같은 에러를 1시간동안 50명 이상의 유저가 겪었다’를 규칙으로 정해두면 이메일이나 슬랙으로 에러에 대한 상세한 정보를 받아볼 수 있다.

센트리를 사용하면서 내부 구현이 궁금한 부분들이 조금씩 생겨났다. 이 글에선 직접 코드를 뜯어보며 의문점들을 해소해보고자 한다.


의문점들

센트리를 사용하면서 아래와 같은 점들이 궁금했다.

  • 센트리는 어떻게 모든 에러를 기록할까?
  • 오프라인 상태에서도 에러를 기록하는 것 같은데 어떻게 구현되어 있을까?
  • 센트리는 왜 이렇게 번들 사이즈가 클까?

센트리는 오픈소스로 관리되어 있어서 코드를 읽어보니 쉽게 궁금증들을 해소할 수 있었다.


센트리는 어떻게 모든 에러를 기록할까?

  1. GlobalEventHandlers.onerror를 통해 런타임 에러와 문법 오류를 모두 기록한다. 다만 Promise의 Rejection은 에러로 구분되진 않기 때문에 GlobalEventHandlers.onerror 이벤트 리스너가 호출되지 않는다. 따라서 onnhandledrejection를 통해 핸들링되지 않은 Promise Rejection을 감지하고 기록한다. (관련 코드 1, 관련 코드 2)
  2. 디버깅에 중요한 네이티브 API(History, Console, DOM 등)를 몽키패칭하여 로그를 기록하는 코드를 심는다. 에러가 발생하면 onnhandledrejection 핸들러에서 기록된 로그와 해당 에러를 서버에 전송한다. (관련 코드)

나는 Error 객체를 몽키패칭해서 구현했을 것이라고 생각했는데 onerror와 onnhandledrejection 이벤트라는 아주 좋은 방법이 있었다.


오프라인 상태에서도 에러를 기록하는 것 같은데 어떻게 구현되어 있을까?

  1. 오프라인 상태일 때 발생한 이벤트를 디바이스 로컬 데이터 저장소에 캐싱한다 (해당 로직)
  2. 온라인으로 돌아오면 캐싱된 이벤트들을 모두 서버에 전송한다 (해당 로직)
  3. 센트리가 초기화 될 때 마다 캐싱된 이벤트들을 모두 서버에 전송한다 (해당 로직)

어느정도 예상한 방식대로 구현되어 있었다. localStorage를 썼을 것이라고 예상했지만 실제론 IndexedDB를 사용했다. 아마도 제한적인 스토어 크기 때문에 상대적으로 안정적인 IndexedDB를 활용한 것 같다. 이전에 IndexedDB 사용을 고려했는데 복잡한 API 떄문에 사용하지 않은 경험이 있다. 센트리는 localForage 라이브러리를 사용한다. IndexedDB를 보다 고수준 API로 제공하는 라이브러리인데, 꽤 괜찮아 보여서 앞으로 IndexedDB를 사용할 일이 생기면 활용해보아야겠다.


센트리는 왜 이렇게 번들 사이즈가 클까?

센트리의 번들 사이즈는 76 KB (링크)

  1. 주요 네이티브 API들을 몽키패칭하는 방식으로 구현되어 있다보니 절대적인 코드의 양이 많다
  2. 트리 쉐이킹(Tree Shaking)이 잘 동작하지 않는다
  3. 라이브러리에 자체적인 폴리필이 포함되어있다 (관련 코드)

번들 사이즈 문제는 센트리 팀에서도 이미 인지하고 있다. 관련 이슈에서 다음 메이저 업데이트에선 높은 우선순위로 번들 사이즈 문제를 해결하려고 한다는 코멘트를 남겼다. 하지만 센트리는 번들 사이즈가 거대함에도 불구하고 에러 탐지를 위해 꼭 필요하다.

번들의 크기와 웹페이지 로드 속도는 비례하기 때문에 유저 경험을 위해 번들 크기를 최소화하는 것이 중요하다. 따라서 토스에선 이 문제를 해결하기 위해 최초 로딩엔 센트리를 포함시키지 않고, 최초 로딩이 끝난 이후 센트리를 불러오는 지연된 로드(Lazy Load) 방식을 활용하여 초기 로딩 속도를 최적화했다.

comments powered by Disqus