[Vue] nextTick and Lifecycle Hook

yongkini ·2024년 8월 22일
0

VueJS

목록 보기
1/1

프로젝트를 진행하다가 궁금했던 nextTick 과 vue 라이프싸이클 훅 관련 공부(Composition API 기준)

nextTick

  • nextTick은 React 때로 생각해보면, setState를 한 다음에 바로 다음 줄에 혹은 setState가 포함된 함수 내의 setState 코드 다음 부분에서 setState로 바꾼 state를 참조하면 변한값을 조회할 수 없는 것과 같은 상황을 해결한다. 하지만, 이 때, 차이점은 nextTick은 렌더링과 관련돼있다는 것이다. nextTick은 어떤 값이 업데이트되고, 리렌더링이 전부 완료됐을 때를 기다려야하는 로직이 있을 때 사용한다. 간단한 예시로 이런 경우에 쓰면된다.
    const changeStatus = async (e) => {
      e.stopPropagation();
      canSee.value = !canSee.value;
      await nextTick();
      console.log(canSee.value, divRef.value)
    }
    </script>
    
    <template>
      <button @click="changeStatus">Status</button>
      <h1 v-if="canSee" ref="divRef">{{ msg }}</h1>
    ...
    위의 케이스를 보면 changeStatus 를 통해 canSee 값을 바꿔준다. 이 때, nextTick이 없었다면, console.log(canSee.value, divRef.value) 여기서 만약에 canSee가 이전 줄에서 true로 바뀌었어도, 즉, h1 태그를 referencing 할 수 있는 상황임에도 divRef는 null 로 나온다. 즉, 리렌더링된걸 반영하지 못하고 출력한다. 하지만, nextTick을 사용해주면 divRef는 정상적으로 h1 element를 가리킨다(canSee가 true로 변했을 때). 이처럼 nextTick은 다음 렌더링이 완전히 끝날 때까지 기다리게 해주고, 그 다음에 그에 맞는 로직을 처리할 수 있도록 해준다.

Lifecycle Hooks(composition API 기준 === vuejs3기준)

  • onUpdated : 최대한 batched update를 하려는 것 같음. 하지만, 이건 내부로직을 알 수 없어서 확신할 수 없다. 뭔소린가 하면, 내가 직접 만들어봤을 때 버튼 1을 누른 다음에 handler에 버튼 2를 click() 하도록 하고, 각각의 클릭에서 리렌더링을 일으킬만한 state 변동을 줬다. 이 때, 어떤 경우에는 onUpdated 콜백이 한번 실행되고, 어떤 경우에는 두번 실행된다(대체로 한번 실행된다). 이게 무슨 의미인가하면, 리렌더링을 batch 해서 한번에 update 태우느냐, 아니냐의 문제인데, 100%는 아니지만, 최대한 한번에 태우려고 하는 것 같다. 또한, 써보니까 사실상 onUpdated 타이밍에 뭔가를 쓸일이 있을지는 모르겠다. 이 라이프싸이클에 어떤 로직을 추가하기가 애매할 것 같다는 얘기다. 오히려 앞서 말한 nextTick을 이용해서 특정 state가 변하고, 그 state에 의한 리렌더링이 일어났을 때를 캐치해서 어떤 컨트롤을 하는 일이 더 많을 것 같다.
  • onMounted & onUnMounted : 이건 자주써서 이젠 알겠다.. onMounted는 해당 컴포넌트가 실제 DOM에 attached 일 때, onUnMounted는 detached 일 때.
  • onBeforeMount & onBeforeUpdate
    • onBeforeUpdate : 이제 기억이 잘 안나는데, 옛날에 리액트에도 업데이트로 인한 리렌더링을 진하기 전에 css를 변경하고, 리렌더링하기 위해 그 중간에 싸이클을 하나더 줬던 기억이 난다. 그런 것처럼 이것도 onUpdated 직전에 싸이클을 하나 더 둔거다. 이걸 어디 이용할지는 잘모르겠지만, 떠오르는건 스크롤 값 등을 받아서 뭔가를 처리할 때 직전값을 저장하는 로직이 필요할 때 쓸수도 있을 것 같고, updated 직전에 뭔가 값에 디폴트 포매팅을 할 때도 쓸 수 있을 것 같다.
    • onBeforeMount : 이 때는 ref, computed, reactive 등의 값 등은 생성이 접근이 가능한 상태이나, 아직 DOM에 업데이트 혹은 attached 상태가 아니기 때문에 element에 걸어놓은 ref에는 접근해도 null 이나오는 상태다. 시점은 말그대로 mounted 직전에 콜백이 실행된다. (< = > onBeforeUnmout)
  • onErrorCaptured : 무턱대고(?) 아무 함수에 throw new Error()를 하면 발동할 것처럼 생긴 이것은,, 그런건 아니고, vue 관련 에러에 대해서 반응한다. 예를 들어, 라이프 싸이클 콜백 내에서, 예를 들어, onMounted 내에서 에러가 생기면 발동한다. 어차피 각각의 에러처리를 해줘야한다는 점에서 예전에 react 의 ErrorBoundary 처럼 쓰이는건 아닌 것 같고(그렇게 쓰기에는 캐치 못하는게 은근 많음), 음.. 솔직히 잘 모르겠다 쓸모를. 이번에 프로젝트를 진행할 때는 내가 일일이 에러를 처리해줬다. 401을 만나면 어떤 페이지로 (물론 하나의 페이지에 에러 코드만 props로 해줬다) 등등을 세팅해줬는데(공통 api 함수에서), 이런 방법이 있기 때문에 그리고, 각 로직마다 try, catch 문을 써서 에러를 처리해줘야하는건 JS 개발자들의 숙명이라 생각한다.. ErrorBoundary로 할 수 있는건(이것도 근데, 앞서 말했듯이 모든 에러를 컨트롤 할 수 있는게 아니면 의미가 있나 싶다), 정말 기본적인 console.error(err)와 같은 기능을 추가해놓을 수 있을 것 같다. 혹은 알 수 없는 에러에 대한 처리 및 화면을 보여줄 때 쓸 수 있을 것 같다.
    • onErrorCaptured 의 경우에는 모든 에러를 캐치하지는 못하는데,

      import { createApp } from "vue";
      import "./style.css";
      import App from "./App.vue";
      
      const app = createApp(App);
      
      app.config.errorHandler = function (err, vm, info) {
        console.log(err, vm, info);
        console.log("error occured");
      };
      
      app.mount("#app");

      app.config.errorHandler 여기서는 모든 에러를 캐치한다. 따라서 앞서 말한 기능을 위해서는 이걸 활용하는 편이 좋을 것 같다. 로그로 에러를 관리하고 싶을 때도 여길 이용하면 좋을 듯 일종의 middleware로 생각. 어차피 FE 플젝 내에는 에러 로그를 적재하기 힘드므로, 따로 에러 로그를 visualization 하는 서버가 있으면 좋을 것 같다(이거 해보고 싶다).

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글