최근 회사에서 담당하고 있는 프로젝트의 UI/UX 개편과 동시에 Nuxt2 프로젝트를 Nuxt3로 마이그레이션 할 일이 생겼다.
버전 업그레이드라... 길고 긴 싸움이 될 것이라 예상한다. Nuxt부터 업그레이드를 시도하였으나, 수많은 오류를 맞이했다.
그래서 그냥 새로 만들기로 결심했다.
프로젝트 세팅부터 다시 시작했다. 라이브러리들의 사용법이 Nuxt2 버전과 Nuxt3 버전에서 다른 점이 있었다. 그 중 axios 설정에 대해 다뤄볼 것이다. Nuxt3에서도 Nuxt2와 마찬가지로 @nuxtjs/axios 모듈을 지원했다.
첫 번째 겪은 에러는 기존 타 프로젝트를 레퍼런스로 해서 /plugins/axios.js를 사용하길래 그대로 사용해 보았는데 Cannot redefine property: $axios 에러가 발생했다. 이는 nuxt.config.js에서 plugin으로 axios가 두 번 등록되어 있어서 발생하는 에러였고, nuxt.config.js 설정에는 axios 관련 설정을 삭제했다. 찾아보니 Nuxt3는 plugins 디렉토리 안에 있는 것을 자동으로 등록해준다고 한다.
두 번째, 에러는 아니지만 코드를 사용하는데 있어서 axios 사용을 위해 vue파일, js파일, pinia에서 axios를 항상 호출해야 한다는 점이었다. axios를 호출하고 싶을 때마다 import를 각 파일 형태에 따라 다르게 해줘야 한다니, 상당히 귀찮을 일이었다. 그래서 /utils/axios.js 파일을 만들었다. Nuxt3가 자동으로 임포트 해주는 것이 몇몇 디렉토리가 있다. 그 중에 /utils/.. 디렉토리 안에 있는 파일은 Auto-import가 가능한 파일들이라고 한다. 그래서 공용 axios 파일을 만들고 거기에서 필요한 서비스 로직을 처리했다.
통신 코어 작성: utils/axios.js
Nuxt 3는 프로젝트 최상단의 utils/ 폴더에 위치한 파일에서 export 한 변수들을 프로젝트 전체에서 자동으로 임포트 해준다.
이 원리를 이용해, 공용 Axios 인스턴스와 Request/Response 인터셉터 로직을 모두 utils/axios.js 으로 이동시킨다.
// utils/axios.js
import axios from 'axios';
// 💡 1. 반드시 'export const $axios' 로 내보내야 전역에서 $axios 로 사용할 수 있습니다.
export const $axios = axios.create({
baseURL: '/api', // 백엔드 API 기본 도메인
timeout: 5000,
});
// 💡 2. 요청 (Request) 인터셉터
$axios.interceptors.request.use(
(config) => {
// 토큰 셋팅 등 API 요청 전송 전에 수행할 작업
// const token = localStorage.getItem('accessToken');
// if (token) config.headers['Authorization'] = `Bearer ${token}`;
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 💡 3. 응답 (Response) 인터셉터
$axios.interceptors.response.use(
(response) => {
// 거추장스러운 axios 껍데기(status, headers 등)를 벗기고 알맹이만 화면으로 넘겨줍니다.
return response.data;
},
(error) => {
// 401 에러(토큰 만료) 등 공통 에러 처리 로직
return Promise.reject(error);
}
);
실전 사용법 (Vue & Pinia)
vue 또는 js 파일에서 import 나 useNuxtApp() 으로 axios를 꺼낼 필요 없이 바로 사용이 가능하다.
👉 Vue 컴포넌트 (script setup)에서 사용하기
<template>
<button @click="fetchData">데이터 불러오기</button>
</template>
<script setup>
// 아무것도 import 할 필요 없습니다!
const fetchData = async () => {
try {
// utils/axios.js 에서 알아서 가져옵니다.
// 인터셉터 덕분에 .data 로 꺼낼 필요 없이 곧바로 알맹이가 들어옵니다.
const res = await $axios.post('/board/list', { page: 1 });
console.log('불러온 데이터:', res);
} catch (error) {
console.error('통신 에러:', error);
}
};
</script>
👉 Pinia 스토어 (stores/xxx.js)에서 사용하기
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null
}),
actions: {
async login(payload) {
try {
// 스토어에서도 마찬가지로 그냥 $axios 로 찌르면 끝입니다!
const res = await $axios.post('/user/login', payload);
this.userInfo = res;
} catch (error) {
console.error('로그인 실패', error);
}
}
}
});
🚨 핵심 주의사항 (Troubleshooting)
이 구조를 적용하실 때 기존의 찌꺼기(?)들이 남아있으면 100% 에러가 발생할 수 있다고 한다. 이 경우 아래 3가지를 확인해보자.
- nuxt.config.js 정리: Nuxt 2 시절의 잔재인 @nuxtjs/axios 모듈이 modules 배열에 남아있다면 사젝를 해야한다. 또한 plugins: ['/plugins/axios.js'] 처럼 플러그인을 수동으로 등록해 둔 코드도 반드시 삭제해야 한다. (Nuxt 3는 중복 실행 시 Cannot redefine property 에러를 뿜어냅니다.)
- 기존 플러그인 파일 삭제: plugins/axios.js 파일이 존재한다면 이제 역할이 끝났으므로 삭제해준다.
- 캐시 초기화 (중요 ⭐️): 파일의 위치를 utils로 옮겼거나 지웠을 때, Nuxt의 자동 임포트 명단이 갱신되지 않아 $axios is not defined 에러가 발생할 수 있다. 이때는 프로젝트 폴더 내의 .nuxt 폴더를 통째로 삭제하고 다시 npm run dev 로 실행해 주면 완벽하게 해결된다.
- Just Do It -