티스토리 뷰

ErrorBoundary가 뭘까

에러 바운더리는 선언적으로 에러를 처리할 수 있게 컴포넌트 형태로 사용되는 에러 헨들링 방법 중 하나입니다.

 

상세내용 참고

https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary

 

Component – React

The library for web and native user interfaces

react.dev

vue 기반의 서비스에서 저 좋은 에러 헨들링 기법을 적용하고 싶다는 강렬한 생각이 들었습니다...🔥

 

Vue에서 ErrorBoundary 구현하기

먼저 Vue에는 다행이도 에러를 감지해주는 라이프사이클 함수가 존재합니다.
이를 적극적으로 이용해 리액트에서와 같이 컴포넌트 형태로 구현이 가능해집니다.

Vue2 : errorCaptured 이용하기

먼저 ErrorBoundary와 같은 프로바이더 개념의 컴포넌트를 구현해봅니다.

 

ErrorBoundary.vue

<script>
export default {
  name: "ErrorBoundary",
  props: {
    fallback: {
      type: Object
    }
  },
  data() {
    return {
      hasError: false
    };
  },
  errorCaptured(error,compInst,errorInfo) {
    this.hasError = true;
    console.log("error: ", error);
    console.log("compInst: ", compInst);
    console.log("errorInfo: ", errorInfo);
  },
  render(createElement) {
    return this.hasError
      ? createElement(this.fallback)
      : this.$slots.default[0];
  }
};
</script>

이제는 해당 에러바운더리에 감지되었을때 리턴해줄 fallback ui를 구현해봅니다.

 

ErrorFallBack.vue

<template>
  <div id="error-page">
    <p class="text">서비스를 불러오는데 실패했습니다.</p>
      <p class="text">새로고침 버튼을 눌러주세요.</p>
      <button class="button" @click="refreshPage()">새로고침 하기</button>
  </div>
</template>

<script>
export default {
  methods: {
    refreshPage(){
      window.location.reload();
    },
  },
};
</script>

<style scoped>
... 원하는 스타일
</style>

해당 구현된 내용을 vue 앱의 최상단인 App.vue에 적용해봅니다.

 

App.vue에 ErrorBoundary 적용하기

<template>
<ErrorBoundary>
	<div>
    	...
    </div>
</ErrorBoundary>
</template>

<script>
import ErrorBoundary from './~~/ErrorBoundary.vue'
import ErrorFallBack from './~~/ErrorFallBack.vue'

export default {
  components: {
    ErrorBoundary,
    ErrorFallBack
  },
  
  computed: {
    fallbackUI() {
      return ErrorFallBack;
    },
  }
</script>

위와 같이 적용하면 ErrorBoundary 하위에서 발생하는 에러를 감지하여 fallbackUI를 보여주고 앱이 멈추어 작동하지 않는 모습을 보여주지 않을 수 있습니다.

 

Vue3 : onErrorCaptured 이용하기

위와 마찬가지의 순서로 구현해봅니다.

 

ErrorBoundary.vue

<template>
  <Error v-if="isError"/>
  <slot v-else/>
</template>

<script setup lang="ts">
import {
  onErrorCaptured,
  ref,
} from 'vue';
import Error from './Error.vue';

const isError = ref(false);

onErrorCaptured(() => {
  isError.value = true;
});
</script>

ErrorFallBack.vue

<template>
  <div id="error-page">
    <p class="text">
      서비스를 불러오는데 실패했습니다.
    </p>
    <p class="text">
      새로고침 버튼을 눌러주세요.
    </p>
    <button
      class="button"
      @click="refreshPage()"
    >
      새로고침 하기
    </button>
  </div>
</template>

<script setup lang="ts">

const refreshPage = () => {
  window.location.reload();
};

</script>

<style lang="scss" scoped>
... 원하는 스타일
</style>

App.vue에 ErrorBoundary 적용하기

<template>
  <ErrorProvider>
    <Suspense>
      <router-view/>
    </Suspense>
  </ErrorProvider>
</template>

위처럼 적용하면 위에서 언급했듯 오류 발생시 원하는 오류화면을 보여줄 수 있게 됩니다.

 

Sentry를 곁들이자...

오류가 났을때 왜 오류가 났는지 추적하려고 로컬에서 켜서 디버깅하는 경험 많았을 것입니다.
sentry를 통해서 프론트 페이지에서 발생하는 오류를 수집하고 미리 어떠 오류인지 보고 
로컬에서 접근하여 수정할 수 있어 디버깅시간을 단축시켜줄 수 있습니다.

Sentry가 뭔데...?

Sentry는 실시간 로그 취합 및 분석 도구이자 모니터링 플랫폼입니다.
참조 👉 https://sentry.io/welcome/

 

Application Performance Monitoring & Error Tracking Software

Self-hosted and cloud-based application performance monitoring & error tracking that helps software teams see clearer, solve quicker, & learn continuously.

sentry.io

Sentry를 이용해 오류를 수집하고 분석할 수 있어 ErrorBoundary와 같이 에러 헨들링에 녹이면 아주 좋겠다는 생각이 들어 적용해보았습니다.

바로 적용해봅시다.

 

Vue 프로젝트 내 main.js에 적용하기

Sentry.init({
    Vue,
    dsn: "본인 센트리 프로젝트에서 제공하는 url",
    initialScope: (scope) => {
      // 객체 형태로 센트리 내 태그를 선택적으로 커스텀하여 넣어줄 수 있습니다.
      const customInfo = {
		key : value
      }
      scope.setTags(customInfo);
      return scope;
    },
  })

Axios Error에 Sentry 추가하기

위 설정만 가지고는 네트워크에서 발생하는 오류를 상세히 모니터링하기 어렵기 때문에 
axios의 interceptor를 사용하여 추가해줍니다.

 

Axios interceptor 추가 코드

import * as Sentry from "@sentry/vue";
import axios from "axios";

axios.interceptors.response.use(
  async (response) => {
    /*
          http status가 200인 경우
          응답 성공 직전 호출됩니다.
          .then() 으로 이어집니다.
      */

    return response;
  },
  async (error) => {
    /*
        http status가 200이 아닌 경우
        응답 에러 직전 호출됩니다.
        .catch() 으로 이어집니다.
    */

    if(error.response){

      const {data, status} = error.response
      const {method, url} = error.config
  
      // 이슈 하위 문서 내용에 정보 추가
      Sentry.setContext('Api Response Detail', {
        status,
        data
      });
		
      // 커스텀 태그 형성
      Sentry.withScope((scope)=>{
        scope.setTag('type', 'api')
        scope.setTag('api-status', status || 'no value')
        scope.setTag('api-data', data ? JSON.stringify(data) : 'no value')

        // 이슈의 레벨을 설정
        scope.setLevel('error');

        // 이슈를 그룹화 시켜줌
        scope.setFingerprint([method, status, url])

        // 센트리로 오류 전송
        Sentry.captureException(new Error('API Internal Server Error'))
      })
    } else {
      const {method, url, params, data, headers} = error.config
	
      // 이슈 하위 문서 내용에 정보 추가
      Sentry.setContext('Api Request Detail', {
        message:'네크워크 요청은 갔으나 응답이 오지 않음',
        method,
        url,
        params,
        data,
        headers
      });
	
      // 커스텀 태그 형성
      Sentry.withScope((scope)=>{
        scope.setTag('type', 'api')
        scope.setTag('api-method', method || 'no value')
        scope.setTag('api-url', url || 'no value')
        scope.setTag('api-params', params ? JSON.stringify(params) : 'no value')
        scope.setTag('api-data', data ? JSON.stringify(data) : 'no value')
        scope.setTag('api-headers', headers ? JSON.stringify(headers) : 'no value')
		
        // 이슈의 레벨을 설정
        scope.setLevel('error');

        // 이슈를 그룹화 시켜줌
        scope.setFingerprint([method, url])

        // 센트리로 오류 전송
        Sentry.captureException(new Error('API Not Found Error'))
      })
    }

    return await Promise.reject(error);
  }
);

export default axios

이로써 Sentry에서 서비스 내 js 오류 및 네트워크 오류를 전부 추적할 수 있게 되었습니다.

 

참조

https://if.kakao.com/2022/session/84

 

if(kakao)dev2022

함께 나아가는 더 나은 세상

if.kakao.com

https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary

 

Component – React

The library for web and native user interfaces

react.dev

https://vuejs.org/api/options-lifecycle.html#errorcaptured

 

Options: Lifecycle | Vue.js

VueConf Toronto - Join the premier Vue.js conference | 9-10 Nov 2023 - Toronto, CanadaView Schedule Use code VUEJS to get 15% off

vuejs.org

https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured

 

Composition API: Lifecycle Hooks | Vue.js

VueConf Toronto - Join the premier Vue.js conference | 9-10 Nov 2023 - Toronto, CanadaView Schedule Use code VUEJS to get 15% off

vuejs.org

https://engineer-mole.tistory.com/369

 

[Vue.js] Vue를 사용한다면 알아두면 좋은 Vue 패턴과 잔기술

※ 일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다. 개요 이번 포스트에서는 Vue 미경험이었던 상태에서 약 5개월

engineer-mole.tistory.com

https://docs.sentry.io/platforms/javascript/guides/vue/?original_referrer=https%3A%2F%2Fwww.google.com%2F

 

Vue

On this page, we get you up and running with Sentry's SDK. Get started using a guide listed in the right sidebar. Don't already have an account and Sentry proje

docs.sentry.io

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함