개발/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에 직접 렌더링한다.
  • loadingStoreisLoading 상태에 따라 로딩 화면을 표시하거나 숨긴다.


전역

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>