[Nuxt] SSR에서 $fetch onResponse의 Nuxt 컨텍스트 오류 해결

임승민·2025년 3월 25일
0

Nuxt

목록 보기
1/2
post-thumbnail

1. 문제

로딩 표시 최적화를 위해 로딩이 1초 이상일 때만 useLoadingStore().show()로 로딩 스피너를 표시하였다.

문제는 새로고침을 하면(SSR) Nuxt의 $fetch API에 문제가 생겨서 데이터 요청을 하지 않는다.
⬇️에러 메시지⬇️

[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.

2. 디버깅 과정

최적화 후 문제가 생겨서 이전 코드와 비교를 진행했다. 전에는 onRequest에서 스토어 초기화 및 실행을 반드시 진행하였다.

// before
onRequest({ options }) {        
 useLoadingStore().show() 🚩
 options.headers = {생략}      
},      
onResponse() {        
 useLoadingStore().hide()      
},
// after
onRequest({ options }) {        
	options.headers = {생략}
	if (timer) clearTimeout(timer)        
	timer = setTimeout(() => {          
		useLoadingStore().show() 🚩   
	}, 1000)      
},      
onResponse() {        
	if (timer) clearTimeout(timer)        
	useLoadingStore().hide()      
},

하지만 현재 코드는 요청이 1초가 지나지 않으면 스토어 초기화 및 실행을 하지 않는다. onResponse에서 초기화가 되면 문제가 생기는 것이였다.

  • useLoadingStore() 호출: 스토어 인스턴스 생성 혹은 가져옴
  • SSR) onResponse에서는 인스턴스 가져오기 가능, 생성은 불가

$fetch 외부onRequest에서 초기화하니 정상 동작하였다. 또는 runWithContext를 사용해 Nuxt 컨텍스트를 복원할 수 있다.

서버에서 사용할 필요가 없는 경우 클라이언트에서만 스토어에 접근하게 하면 된다.

onResponse에서 Nuxt 컨텍스트 사용하는 방법 4가지

  1. $fetch 외부에서 초기화
  2. onRequset에서 초기화
  3. runWithContext 사용
  4. 서버에서 렌더링 필요 없으면) import.meta.client로 분기처리

로딩은 서버에서 렌더링될 필요가 없어서 분기처리 하면 해결된다. 하지만 서버 렌더링에서 Nuxt컨텍스트가 필요하다면 1~3번 방식을 이용하면 된다.

3. 결론

onResponse에서 Nuxt 컨텍스트와 Nuxt 컴포저블 참조는 가능하나, 이를 생성하면 SSR 환경에서 오류가 발생할 수 있다.

setup(), useFetch(), useAsyncData() 등은 클라이언트와 서버에서 실행될 때 Nuxt 컨텍스트에 접근할 수 있다. 하지만 Nitro 미들웨어(onRequest, onResponse 등)에서는 SSR 환경에서 실행될 때 Nuxt 컨텍스트를 유지하지 않는다.

  • Nitro 미들웨어는 Nuxt가 아닌 Nitro 실행 컨텍스트에서 동작하기 때문에 Nuxt 컨텍스트에 접근할 수 없다.

따라서 useFetch() 내부에서 인터셉터(onRequest, onResponse…)를 사용할 경우, 클라이언트와 서버에서 동작 방식이 다르다는 것을 인지하고 개발해야 한다.

useNuxtApp은 클라이언트 및 서버 측에서 모두 사용할 수 있는(Nitro 라우트 내에서는 사용할 수 없음) Nuxt의 공유 런타임 컨텍스트에 액세스하는 방법을 제공하는 내장 컴포저블입니다(Nuxt 컨텍스트라고도 함). Vue 앱 인스턴스, 런타임 훅, 런타임 구성 변수 및 내부 상태(예: ssrContext 및 페이로드)에 액세스하는 데 도움이 됩니다.

https://nuxt.com/docs/api/composables/use-nuxt-app


const $customFetch = async (url, opts) => {
  const loadingStroe = useLoadingStore() 🚩
  let timer: NodeJS.Timeout | null = null

  return await $fetch({생략}, {
    ...opts,
    onRequest() {
		if(import.meta.client){ 🚩
		if (timer) clearTimeout(timer)
		timer = setTimeout(() => useLoadingStore().show(), 1000)}
    },
    onResponse() {
      if(import.meta.client){ 🚩
		if (timer) clearTimeout(timer)
		useLoadingStore().hide()
      }
    },
  })
}

참고

0개의 댓글