Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

axios InterCepter 사용법 #11

Open
9utty opened this issue Mar 17, 2023 · 5 comments
Open

axios InterCepter 사용법 #11

9utty opened this issue Mar 17, 2023 · 5 comments

Comments

@9utty
Copy link
Member

9utty commented Mar 17, 2023

import * as SecureStore from "expo-secure-store";
import axios from "axios";
import { useSelector } from "react-redux";
import { RootState } from "../redux/RootReducer";
import { key } from "../../config";
import { useAppDispatch } from "../redux/RootStore";
import { GlobalSlice } from "../redux/Slices/Global";

interface AuthError extends Error {
  response?: {
    status: number;
    data?: any;
  };
}

const instance = axios.create({
  baseURL: key.URL,
  timeout: 10000, // 타임아웃 설정, 10초 내에 응답이 없으면 에러 처리
});


// 인터셉터 추가(헤더에 token 붙혀서 보내기) ps - 지금 적용하면 서버하고 통신못할수도
instance.interceptors.request.use(
  async (config) => {
    const accessToken = useSelector((state: RootState) => state.global.accessToken);
    if (accessToken) {
      config.headers.Authorization = `Bearer ${accessToken}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);


instance.interceptors.response.use(
  (response) => response, // 2xx status 코드인 경우 그대로 반환
  async (error) => {
    const global = useSelector((state: RootState) => state.global);
    const dispatch = useAppDispatch();
    const originalRequest = error.config;
    if (error.response && error.response.status === 401) {
      // 만약 401 에러가 발생한 경우
      if (!originalRequest._retry) {
        // 요청을 재시도 하지 않은 경우
        originalRequest._retry = true;
        try {
          const response = await axios.post(`${URL}/auth/refresh`, {
            refreshToken: await SecureStore.getItemAsync("refreshToken"), // 시크릿저장소에서 refreshToken을 가져옴
          });
          const { accessToken, refreshToken } = response.data;
          // 새로운 accessToken, refreshToken을 받아서 저장
          dispatch(GlobalSlice.actions.addAToken(accessToken));
          await SecureStore.setItemAsync("refreshToken", refreshToken); // 시크릿저장소에 refreshToken을 저장
          // 기존 요청의 Authorization 헤더에 새로운 accessToken을 담아서 재요청
          // 유저 아이디를 받는지 모르겠음.. 받는데 어떻게 받는지 모름..
          originalRequest.headers.Authorization = `Bearer ${accessToken}`;
          return instance(originalRequest);
        } catch (e) {
          const authError = new Error("Authentication error") as AuthError;
          authError.name = "AuthError";
          authError.response = { status: 401 };
          return Promise.reject(authError);
        }
      }
    }
    return Promise.reject(error);
  }
);

export default instance;

적용한 코드 before

const handleSearch = async () => {
    try {
      const response = await axios.get("http://127.0.0.1:3000/map/search", {
        params: {
          keyword: searchText,
        },
      });
      const data = response.data;
      const newPlaces: Place[] = [];
      for (let i = 1; i <= 5; i++) {
        const place = data[`place${i}`];
        if (!place) {
          break; // 데이터가 존재하지 않으면 루프를 종료합니다.
        }
        const newPlace: Place = {
          name: place.name,
          roadAddress: place.address,
        };
        newPlaces.push(newPlace);
      }
      setPlaces([...places, ...newPlaces]);
      setModalVisible(true);
      props.setIsSelected(true);
    } catch (e) {
      console.error(e);
    }
  };

After

  const handleSearch = async () => {
    try {
      const response = await instance.get("http://127.0.0.1:3000/map/search", { // 원래 axios 써져잇는걸 만든 instance import 해서 사용하면 됨
        params: {
          keyword: searchText,
        },
      });
      const data = response.data;
      const newPlaces: Place[] = [];
      for (let i = 1; i <= 5; i++) {
        const place = data[`place${i}`];
        if (!place) {
          break; // 데이터가 존재하지 않으면 루프를 종료합니다.
        }
        const newPlace: Place = {
          name: place.name,
          roadAddress: place.address,
        };
        newPlaces.push(newPlace);
      }
      setPlaces([...places, ...newPlaces]);
      setModalVisible(true);
      props.setIsSelected(true);
    } catch (e) {
      if (axios.isAxiosError(e) && e.response && e.response.status === 401) {
        // refreshToken 재발급 실패시 로그아웃 처리하고, loginScreen으로 네비게이션해주면 됨
      } else {
        console.error(e);
      }
    }
  };
@Hello-IAN
Copy link
Collaborator

확인했습니다. 유저아이디 받는지 모르겠다고 주석다신 부분은 저희쪽에서일까요 아니면 백엔드쪽에서일까요??

@9utty
Copy link
Member Author

9utty commented Mar 17, 2023

@Hello-IAN token발급할때 uid도 같이 주는지 정확하지가 않아서

@obvoso
Copy link
Collaborator

obvoso commented Mar 17, 2023

확인했습니다

@Hello-IAN
Copy link
Collaborator

@Hello-IAN token발급할때 uid도 같이 주는지 정확하지가 않아서

백엔드 처리 로직을 못봐서 확언하기가 어렵습니더.. 보통은 액세스 토큰 안에 내용으로 들어가있고, 저희쪽에서는 크게 처리하지 않아도 되는 걸로 알고있습니당

@9utty
Copy link
Member Author

9utty commented Mar 20, 2023

전체 수정이 필요해서 전체 수정 한번 했습니다

import * as SecureStore from "expo-secure-store";
import axios from "axios";
import { useSelector } from "react-redux";
import { RootState } from "../redux/RootReducer";
import { key } from "../../config";
import { useAppDispatch } from "../redux/RootStore";
import { GlobalSlice } from "../redux/Slices/Global";
import store from "../redux/RootStore";

interface AuthError extends Error {
  response?: {
    status: number;
    data?: any;
  };
}

const instance = axios.create({
  baseURL: key.URL,
  timeout: 10000, // 타임아웃 설정, 10초 내에 응답이 없으면 에러 처리
});

// 인터셉터 추가(헤더에 token 붙혀서 보내기) ps - 지금 적용하면 서버하고 통신못할수도
instance.interceptors.request.use(
  async (config) => {
    const accessToken = store.getState().global.AccessToken;
    if (accessToken) {
      config.headers.Authorization = `Bearer ${accessToken}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  (response) => {
    return response;
  }, // 2xx status 코드인 경우 그대로 반환
  async (error) => {
    const originalRequest = error.config;
    if (error.response && error.response.status === 401) {
      // 만약 401 에러가 발생한 경우
      if (!originalRequest._retry) {
        // 요청을 재시도 하지 않은 경우
        originalRequest._retry = true;
        try {
          const acToken = store.getState().global.AccessToken;
          const response = await axios.post(`${key.URL}auth/refresh`, {
            accessToken: acToken, // 시크릿저장소에서 refreshToken을 가져옴
            refreshToken: await SecureStore.getItemAsync("refreshToken"), // 시크릿저장소에서 refreshToken을 가져옴
          });
          const { accessToken, refreshToken } = response.data;
          // 새로운 accessToken, refreshToken을 받아서 저장
          store.dispatch(GlobalSlice.actions.addAToken(accessToken));
          await SecureStore.setItemAsync("refreshToken", refreshToken); // 시크릿저장소에 refreshToken을 저장
          // 기존 요청의 Authorization 헤더에 새로운 accessToken을 담아서 재요청
          // 유저 아이디를 받는지 모르겠음.. 받는데 어떻게 받는지 모름..
          originalRequest.headers.Authorization = `Bearer ${accessToken}`;
          return instance(originalRequest);
        } catch (e) {
          const authError = new Error("Authentication error") as AuthError;
          authError.name = "AuthError";
          authError.response = { status: 401 };
          return Promise.reject(authError);
        }
      }
    }
    return Promise.reject(error);
  }
);

export default instance;

@Hello-IAN @obvoso

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants