본문 바로가기
Frontend/Nuxt3

[Nuxt3] useNuxtApp() 함수의 상호작용과 runWithContext()의 역할

by 만발한매화 2024. 10. 9.

Nuxt3 에서 script setup에 선언된 변수와 함수는 컴포넌트의 setup 함수 scope에서 실행된다.

이는 해당 컴포넌트가 렌더링될 때 처음 한번만 실행되는 영영이다.

 

따라서, 아래와같인 script setup 최상단에서 useNuxtApp()을 호출하는 것은 올바른 방식이다.

해당 시점에 컴포넌트 렌더링중으로 nuxtApp을 사용할 수 있다.

<script setup>
const nuxtApp = useNuxtApp()

</script>

 

하지만, 비동기 타이머 콜백이 실행되는 시점에는 컨텍스트가 달라진다.

 

컨텍스트란?

특정 시점에서의 Nuxt 어플리케이션 상태와 환경정보를 의미한다.

에를들어, 컴포넌트 실행시 상태, 전역 설정, 플러그인, Nuxt 앱 관련 객체등의 정보이다.

  • 어플리케이션 상태정보 : Nuxt 어플리케이션이 현재 어디에서 실행(SSR, CSR)되는지 관련 정보를 포함한다.
  • Nuxt 관련 전역 객체 : Nuxt앱은 useNuxtApp()과 같은 함수로 전역객체인 nuxtApp에 접근하게 해준다.
    이 객체는 플로그인, 미들웨어, 전역 유틸리티, 상태 관리 기능 등을 제공하는 주요 컨텍스트이다.
  • 비동기 작업에서 환경 정보 : 비동기 작업은 실행 시점의 Nuxt 상태에 의존한다.
    예를 들어, 서버에서 요청이 처리되고 있거나 페이지 전환이 이루어지고 있는 경우에 비동기 작업은 다른 컨텍스트에서 실행될 수 있다.
    이 때, 비동기 작업에서 Nuxt 컨텍스트 손실시 Nuxt 관련 기능을 제대로 사용할 수 없다.

 

비동기함수에서 컨텍스트를 사용하면

비동기함수에서 컨텍스트를 사용하는 경우, 해당 함수 실행시점의 Nuxt 어플리케이션의 컨텍스트는 변경되거나 유효하지 않게된다.

아래의 예시와 같이 타이머나 api 호출과 같은 비동기 작업 완려시점에 Nuxt 앱의 상태와 컨텍스트가 달라져 필요한 정보를 제대로 가져오지 못하게된다.

<script setup>
setTimeout(() => {
  const nuxtApp = useNuxtApp()
});

</script>

 

script setup scope의 타이머가 완료된 후, setTimeout 콜백이 실행되기에 Nuxt에서 제공하는 useNuxtApp() 컨텍스트는 이미 바뀌었을 것이다. 따라서 해당 코드는 문제가 있다.

 

비동기함수에서 올바른 컨텍스트 사용법

setTimeout 사용시 올바른 비동기 처리는 다음과 같이 할 수 있다.

runWithContext 함수로 Nuxt 앱 컨텍스트를 보존한다. 

이는 비동기 코드 내에서 Nuxt 기능을 안전하게 사용할 수 있도록 보장해준다. 

<script setup>
const nuxtApp = useNuxtApp()

setTimeout(() => {
 nuxtApp.runWithContext(() => {})
});

</script>

 

runWithContext 컨텍스트 유지 방식

Nuxt 내부의 Reactiveness 시스템과 nuxt 컨텍스트 관리를 통해 비동기 작업중에도 nuxt 전역 상태와 설정에 접근할 수 있게 보장한다.

 

Vue3는 반응형 시스템으로 컴포넌트 상태를 추적하며 변경 사항을 자동으로 감지한다.

Nuxt3 역시 이를 기반으로 SSR, 페이지 전환등의 상황에서 전역상태를 관리한다.

 

runWithContext()는 이러한 이러한 전역상태 관리 시스템에서 현재 nuxt 어플리케이션이 어느 단계에서 실행 중인지 보존하고 콜백이 실행되는 동안 해당 상태를 유지한다. 

현재 실행중인 컨텍스트를 캡쳐한 후, 비동기 콜백 실행시점에 원 컨텍스트를 다시 활성화시킨다.

const nuxtApp = useNuxtApp();

setTimeout(() => {
  // 일반적인 비동기 코드에서는 Nuxt의 컨텍스트를 잃어버릴 수 있음
  nuxtApp.runWithContext(() => {
    // 이 블록 안에서는 Nuxt 앱의 컨텍스트가 보존됨
    console.log(nuxtApp.ssrContext); // SSR 컨텍스트에 안전하게 접근 가능
  });
}, 1000);

 

Nuxt 캡쳐(Capture)

캡쳐라는 용어는 주로 클로저(closure) 개념과 관련되어 있으며, 비동기 함수나 이벤트 핸들러에서 외부 스코프에 있는 변수를 저장하고 나중에 사용할 수 있도록 하는 것을 의미한다.

클로저(closure는 JavaScript에서 함수가 선언될 당시 환경(스코프)의 변수기억하고, 그 함수가 나중에 호출될 때도 그 변수를 참조할 수 있게 해주는 기능이다. 클로저를 통해 비동기 함수나 콜백 함수에서 외부의 변수나 상태를 "캡쳐"해서 사용할 수 있다.

function createCounter() {
  let count = 0; // 외부 스코프에 있는 변수
  return function() {
    count++; // 클로저: 이 함수는 외부 스코프의 'count' 변수를 캡쳐함
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 1
counter(); // 2

 

이 예시에서 createCounter() 내부의 count 변수는 함수가 호출된 이후에도 계속 기억되고, 반환된 함수가 실행될 때마다 증가한다.

이것이 클로저의 전형적인 예로, 여기서 count는 캡쳐된 변수이다.

function createCounter() {
  let count = 0; // 외부 스코프에 있는 변수
  count++; // 해당 시점을 기억하기에 1만 노출
  return function() {
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 1
counter(); // 1

 

Nuxt와 같은 비동기 환경에서, 특정 시점의 Nuxt 앱 컨텍스트 또는 전역 상태 캡쳐하는 것은 비동기 작업이 끝난 후에도 그 컨텍스트에 안전하게 접근할 수 있도록 하는 것을 의미한다.

const nuxtApp = useNuxtApp(); // 외부 스코프에서 캡쳐된 'nuxtApp'

setTimeout(() => {
  // 비동기 작업이 실행될 때도 'nuxtApp'은 여전히 사용 가능 (클로저로 인해 캡쳐됨)
  nuxtApp.runWithContext(() => {
    console.log(nuxtApp.ssrContext); // 비동기적으로 실행된 후에도 컨텍스트에 접근 가능
  });
}, 1000);