개발/Vue
[VUE] API 호출 중 로딩 화면 구현
맛있는취나물
2024. 11. 20. 18:33
Vue.와 Pinia를 사용하여 API 호출 중 로딩 화면을 구현하는 방법에 대해 설명한다.
로딩 상태 관리
loadingStore.js 파일에서는 Pinia를 사용하여 로딩 상태를 관리한다.
// loadingStore.js
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useLoadingStore = defineStore('loading', () => {
// 로딩 상태를 추적하는 변수
const loadingCount = ref(0);
// 로딩 카운트 증가
const increaseLoadingCount = () => {
loadingCount.value++;
};
// 로딩 카운트 감소
const decreaseLoadingCount = () => {
if (loadingCount.value > 0) loadingCount.value--;
};
const isLoading = computed(() => loadingCount.value > 0);
return {
increaseLoadingCount,
decreaseLoadingCount,
isLoading,
};
});
- loadingCount ref를 사용하여 현재 진행 중인 로딩 작업의 수를 추적한다.
- increaseLoadingCount와 decreaseLoadingCount 함수로 로딩 카운트를 조절한다.
- isLoading computed 속성으로 현재 로딩 중인지 여부를 확인한다.
인터셉터 구현
loading.js 파일에서는 axios 인터셉터를 구현한다.
// loading.js
import { useLoadingStore } from '@/store/loadingStore'; // Pinia store import
// 요청 인터셉터 함수
export const requestInterceptor = config => {
const loadingStore = useLoadingStore();
// 요청에 로딩 상태가 설정되어 있는지 확인
if (config.loading) {
// 로딩 카운트 증가
loadingStore.increaseLoadingCount();
}
// 수정된 설정 반환
return config;
};
// 응답 인터셉터 함수
export const responseInterceptor = response => {
const loadingStore = useLoadingStore();
// 응답에 로딩 상태가 설정되어 있는지 확인
if (response.config.loading) {
// 로딩 카운트 감소
loadingStore.decreaseLoadingCount();
}
};
// 오류 인터셉터 함수
export const errorInterceptor = error => {
const loadingStore = useLoadingStore();
// 오류에 로딩 상태가 설정되어 있는지 확인
if (error.config && error.config.loading) {
// 로딩 카운트 감소
loadingStore.decreaseLoadingCount();
}
};
- requestInterceptor는 요청 시 로딩 카운트를 증가시킨다.
- responseInterceptor는 응답 수신 시 로딩 카운트를 감소시킨다.
- errorInterceptor는 오류 발생 시 로딩 카운트를 감소시킨다.
HTTP 클라이언트 설정
http.js 파일에서는 axios 인스턴스를 생성하고 인터셉터를 설정한다.
// http.js
import axios from 'axios';
import router from '@/router';
import {
requestInterceptor,
responseInterceptor,
errorInterceptor,
} from './loading';
export const http = axios.create({
baseURL: import.meta.env.VITE_DOMAIN,
loading: false, // true: 로딩화면
});
http.interceptors.request.use(
config => {
return requestInterceptor(config);
},
error => {
return Promise.reject(error);
},
);
http.interceptors.response.use(
async response => {
responseInterceptor(response);
return Promise.resolve(response.data);
},
async error => {
errorInterceptor(error);
return Promise.reject(error);
},
);
- xios 인스턴스를 생성하고 기본 설정을 한다.
- 요청 인터셉터에서 인증 토큰을 설정하고 로딩 상태를 관리한다.
- 응답 인터셉터에서 토큰 만료 등의 상황을 처리하고 로딩 상태를 업데이트한다.
- 오류 인터셉터에서 다양한 오류 상황에 대응하고 로딩 상태를 관리한다.
로딩 컴포넌트
AppLoading.vue 파일은 실제 로딩 화면을 표시하는 컴포넌트다.
// AppLoading.vue
<template>
<Teleport to="body">
<div class="loading-layout" v-if="loadingStore.isLoading">
<!-- 로딩 화면 -->
</div>
</Teleport>
</template>
<script setup>
import { useLoadingStore } from '@/store/loadingStore';
const loadingStore = useLoadingStore();
</script>
<style lang="scss" scoped>
.loading-layout {
// 클릭 방지
pointer-events: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 9999;
display: flex;
gap: 4px;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 22px;
color: white;
}
</style>
- Teleport를 사용하여 로딩 화면을 body에 직접 렌더링한다.
- loadingStore의 isLoading 상태에 따라 로딩 화면을 표시하거나 숨긴다.
전역
App.vue 파일에 import하여 전역으로 사용한다.
// App.vue
<template>
<div>
<router-view />
<AppLoading></AppLoading>
</div>
</template>
<script setup>
import AppLoading from '@/components/AppLoading.vue';
</script>
사용 예시
API 호출 시 자동으로 로딩 화면이 표시되고, 호출이 완료되면 사라지게 된다.
<script setup>
const callExample = async () => {
try {
const res = await http.get('/users', { loading: true });
console.log(res);
} catch (error) {
console.error('사용자 정보를 불러오는 데 실패했습니다:', error);
}
};
</script>