Vue.js 3 & NodeJS/사내교육 - 점심 메뉴

[16] Frontend 개발 - Login 관리, Axios Interceptor

carrotweb 2026. 8. 5. 18:29
728x90
반응형

Axios Interceptor (엑시오스 인터셉터)를 사용하여 Access Token 재발급(갱신)

Axios Interceptor (엑시오스 인터셉터)
(https://axios-http.com/kr/docs/interceptors)

Axios 요청 전이나 Axios 응답 후 처리는 Axios Interceptor (엑시오스 인터셉터)을 사용하여 가로챌 수 있습니다.

 

Axios Interceptor (엑시오스 인터셉터) 기본 구조입니다.

// 요청 인터셉터
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    // 요청이 전달되기 전에 처리
    return config;
  }, function (error) {
    // Do something with request error
    // 요청 오류에 대한 처리
    return Promise.reject(error);
  });

// 응답 인터셉터
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // 응답 상태코드가 2xx 범위안에 있으면 트리거됩니다.
    // Do something with response data
    // 응답 데이터로 처리
    return response;
  }, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // 응답 상태코드가 2xx 범위를 벗어나면 트리거됩니다.
    // Do something with response error
    // 응답 오류에 대한 처리
    return Promise.reject(error);
  });

 

1. src에 apis 디렉터리를 생성하고 axiosInterceptor.js 파일을 생성합니다.

import axios from 'axios';

const axiosInstance = axios.create();

// 요청 인터셉터
axiosInstance.interceptors.request.use(function (config) {
    console.log("axios.interceptors.request");
    return config;
  }, function (error) {
    console.log("axios.interceptors.request.error");
    console.log(error);
    return Promise.reject(error);
  });

// 응답 인터셉터
axiosInstance.interceptors.response.use(function (response) {
    console.log("axios.interceptors.response");
    return response;
  }, function (error) {
    console.log("axios.interceptors.response.error");
    console.log(error);
    return Promise.reject(error);
  });

  export default axiosInstance;

 

2. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)

 

3. src\apis\axiosInterceptor.js 파일을 수정합니다.

import axios from 'axios';
import { useLoginStore } from '@/stores/useLoginStore';

export const createAxiosInstance = () => {
    const loginStore = useLoginStore();
    
    const axiosInstance = axios.create();

    // 요청 인터셉터
    axiosInstance.interceptors.request.use(async function (config) {
        console.log("axios.interceptors.request");
        console.log(loginStore.access_token);
        return config;
    }, function (error) {
        console.log("axios.interceptors.request.error");
        console.log(error);
        return Promise.reject(error);
    });

    // 응답 인터셉터
    axiosInstance.interceptors.response.use(function (response) {
        console.log("axios.interceptors.response");
        return response;
    }, function (error) {
        console.log("axios.interceptors.response.error");
        console.log(error);
        return Promise.reject(error);
    });

    return axiosInstance;
};

Store를 사용하기 위해서 함수로 변경해야 합니다.

 

4. src\views\LunchMenu\LunchMenuListView.vue 파일을 수정합니다.

<script>
:
//import axios from 'axios';
import { createAxiosInstance } from '@/apis/axiosInterceptor';
:

export default {
    name : 'LunchMenuListView',
    setup() {
        const router = useRouter();
        const route = useRoute();
        :

        async function getLunchMenuList() {
            console.log(loginStore.access_token);
            :

            try {
                const res = await createAxiosInstance().get("http://127.0.0.1:9000/api/v1/lunchmenus/", {
                    headers: {
                        "Authorization" : "Bearer " + loginStore.access_token
                    }
                });
                console.log(res);
                lunchMenuList.value = res.data.data;
            } catch(err) {
                console.log(err);
            }
            :

 

5. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)

 

 

Axios Interceptor (엑시오스 인터셉터) 사용 - Axios 요청 전 적용

 

Access Token의 만료 확인 시점

요청하기 전에 Access Token이 만료되었는지 확인하기 위해서는 Axios Interceptor (엑시오스 인터셉터)을 사용합니다.

 

Access Token의 payload에 있는 exp(expiration, 만료 시간)를 확인하여 만료되었는지 확인 후 Refresh Token를 사용하여 Access Token를 재발급합니다. → 매번 Access Token를 확인

 

 

1. src\apis\axiosInterceptor.js 파일을 수정합니다.

:
import { useRouter } from 'vue-router';

export const createAxiosInstance = () => {
    :
    const router = useRouter();
    :

    // 요청 인터셉터
    axiosInstance.interceptors.request.use(async function (config) {
        console.log("axios.interceptors.request");
        console.log(loginStore.access_token);
        if (loginStore.isAccessTokenExpire()) {
            console.log("Access Token이 만료되었습니다.");
            try {
                await loginStore.doRefreshToken();
            } catch(err) {
                console.log(err);
                alert("인증이 만료되었습니다.\n다시 로그인 해주시기 바랍니다.");
                router.replace({name : 'Login'});
            }
        }
        return config;
    }, function (error) {
        console.log("axios.interceptors.request.error");
        console.log(error);
        return Promise.reject(error);
    });
    :

 

2. src\views\LunchMenu\LunchMenuListView.vue 파일을 수정합니다.

<script>
:
import { useRouter } from 'vue-router';
:

export default {
    name : 'LunchMenuListView',
    setup() {
        :
        // const route = useRoute();
        :

        async function getLunchMenuList() {
            console.log(loginStore.access_token);
            /*
            if (loginStore.isAccessTokenExpire()) {
                console.log("Access Token이 만료되었습니다.");
                try {
                    await loginStore.doRefreshToken();
                } catch(err) {
                    console.log(err);
                    alert("인증이 만료되었습니다.\n다시 로그인 해주시기 바랍니다.");
                    router.replace({path: '/login', query: { returnUrl: route.fullPath }});
                    return;
                }
            }
            */

            :

useRoute를 삭제하고 Access Token 만료 처리를 부분을 주석처리하거나 삭제합니다.

 

3. src\views\Account\LoginView.vue 파일을 수정합니다.

<script>
:

export default {
    name : 'LoginView',
    setup() {
        :
        function loginClick() {
            :

            const memberInfo = { id: state.memberId, password: state.memberPassword };
            loginStore.doLogin(memberInfo).then(() => {
                const returnUrl = route.query.returnUrl;
                if (returnUrl == null || returnUrl == "") {
                    router.replace({name : 'home'});
                } else {
                    router.replace({path : returnUrl});
                }
            }).catch((err) => {
                state.errorMessage = err.message;
            });
        }
        :

returnUrl이 없으면 홈으로 이동되게 처리합니다.

 

4. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)  Access Token 만료 (10분 후) Access  Token 재발급(갱신)

 

5. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)  Access Token 만료 (15분 후) → Refresh Token 만료 → 재발급(갱신) 불가 → 로그인 → 홈

 

 

Axios Interceptor (엑시오스 인터셉터) 사용 - Axios 응답 후 적용

 

Access Token의 만료 확인 시점

요청 후 응답에서 Access Token이 만료되었는지 확인하기 위해서는 Axios Interceptor (엑시오스 인터셉터)을 사용합니다.

 

요청 후 응답에서 Access Token이 만료되었다는 응답 코드(401, Unauthorized)와 응답 메시지(“토큰이 만료되었습니다.”)가 오면 Refresh Token를 사용하여 Access Token를 재발급하고 다시 요청을 보냅니다. → 요청에 대한 응답이 실패일 경우만 처리

 

 

1. src\apis\axiosInterceptor.js 파일을 수정합니다.

:

export const createAxiosInstance = () => {
    :

    // 요청 인터셉터
    axiosInstance.interceptors.request.use(async function (config) {
        console.log("axios.interceptors.request");
        console.log(loginStore.access_token);

        config.headers['Content-Type'] = 'application/json';
        config.headers['Authorization'] = `Bearer ${loginStore.access_token}`;

        /*
        if (loginStore.isAccessTokenExpire()) {
            console.log("Access Token이 만료되었습니다.");
            try {
                await loginStore.doRefreshToken();
            } catch(err) {
                console.log(err);
                alert("인증이 만료되었습니다.\n다시 로그인 해주시기 바랍니다.");
                router.replace({name : 'Login'});
            }
        }
        */

        return config;
    }, function (error) {
        :

Request Header에 Authorization를 추가하고 Access Token 만료 처리 부분을 주석처리하거나 삭제합니다.

 

 

2. src\views\LunchMenu\LunchMenuListView.vue 파일을 수정합니다.

<script>
:
//import { useLoginStore } from '@/stores/useLoginStore';
:

export default {
    name : 'LunchMenuListView',
    setup() {
        :
        //const loginStore = useLoginStore();
        :

        async function getLunchMenuList() {
            //console.log(loginStore.access_token);

            try {
            	/*
                const res = await createAxiosInstance().get("http://127.0.0.1:9000/api/v1/lunchmenus/", {
                    headers: {
                        "Authorization" : "Bearer " + loginStore.access_token
                    }
                });
                */
                const res = await createAxiosInstance().get("http://127.0.0.1:9000/api/v1/lunchmenus/");
                console.log(res);
                lunchMenuList.value = res.data.data;
            } catch(err) {
                console.log(err);
            }
        }
        :

Header를 삭제합니다.

 

3. src\apis\axiosInterceptor.js 파일을 수정합니다.

:

export const createAxiosInstance = () => {
    :

    // 응답 인터셉터
    axiosInstance.interceptors.response.use(function (response) {
        console.log("axios.interceptors.response");
        return response;
    }, async function (error) {
        console.log("axios.interceptors.response.error");
        console.log(error);
        var axiosRequest = error.config;
        if (error.response && !axiosRequest._retry) {
            axiosRequest._retry = true;
            // 응답코드가 401이면 처리합니다.
            if (error.response.status == 401) {
                if (loginStore.isAccessTokenExpire()) {
                    console.log("Access Token이 만료되었습니다.");
                    try {
                        // Access Token 재발급 요청
                        await loginStore.doRefreshToken();

                        // 재발급된 Access Token으로 header를 수정합니다.
                        axiosRequest.headers = {
                            'Content-Type': 'application/json',
                            Authorization: `Bearer ${loginStore.access_token}`
                          };
                        // 다시 요청(request)합니다.
                        const response = await axios.request(axiosRequest);
                        return response;
                    } catch(err) {
                        console.log(err);
                        alert("인증이 만료되었습니다.\n다시 로그인 해주시기 바랍니다.");
                        router.replace({name : 'Login'});
                    }
                }
            }
        }
        return Promise.reject(error);
    });
    :

재요청이 실패하면 계속 반복되기 때문에 반복을 방지하기 위해서 요청 정보(config)에 _retry를 사용하여 한 번만 처리되게 합니다.

응답 상태코드가 401이고 Access Token 만료되어 있으면 Access Token를 재발급하고 요청 정보(config)를 사용하여 다시 요청합니다.

 

4. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)  Access Token 만료 (10분 후)  Access  Token 재발급(갱신)

 

5. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list)  Access Token 만료 (15분 후) → Refresh Token 만료 → 재발급(갱신) 불가 → 로그인 → 홈

728x90
반응형