Axios의 interceptor를 왜 써야 하는가 ?
-> api 호출할 때마다 비효율적으로 axios 인스턴스를 생성하고, axios 마다 동일한 content-type, token와 같은 헤더값을 처리하고 TIMEOUT 시간, base url등에 대한 중복 처리 들이 있기 때문에..
-> 유지보수에 최악 !
(아래 예시 코드들은 로그인 성공시 유저의 데이터를 받아오는 api통신을 뜯어보았다!)
axios를 생성하고, 중복 로직과 데이터 요청&응답에 대한 전/후처리 그리고 오류처리까지 지정하여 인스턴스를 생성해주자 !
1-1. api.ts(AxiosInstanceCreator를 사용하여 interceptor내부를 설정 )
const instance =axios.create({
baseUrl: 데이터를 요청할 기본 주소,
timeout : 1000 (변경 가능)
};
// axios request처리
instance.interceptors.request.use(
function (config) {
config.headers["Content-Type"] = "application/json; charset=utf-8";
config.headers["Authorization"] = " 토큰 값";
return config;
},
function (error) {
console.log(error);
return Promise.reject(error);
}
);
AxiosInstanceCreator를 사용하여 interceptor내부를 설정해준다! 🔼 함수형은 조금 다른 문법 !
interceptor 내부에서 로그인 정보를 확인하는 로직을 넣어준다.
프로젝트의 구조에 따라 헤더나 브라우저 저장소관련 로직에서 검증하고 토큰을 설정해주는 부분이 들어간다!
이렇게 인스턴스를 만들어준 후 , axios를 통해 받아온 데이터를 리덕스와 사가를 통해 데이터를 받아올 수 있도록 폴더구조를 짜보자 !
auth.api.ts -> api 주소별 함수를 정의해놓는 파일
auth.model.ts -> reducer의 state들의 형식타입
auth.reducer.ts -> auth관련 리듀서들의 모음 - enum의 액션타입을 활용한다.
auth.saga.ts -> 사가함수(제너레이터)의 모음 - auth.api.ts에서 만든 api를 활용한다
auth.slice.ts -> createSlice를 통해 만든 authReducer를 .acition으로 내보내고, initialState(초깃값)을 설정해주는 파일
1-2. api.ts ) 만든 인스턴스를 통해 URL을 넣어주고 사용할 api를 생성한다.
공통으로 사용하는 api주소를 인스턴스로 생성해주고 !
구현하고자 하는 기능별 api를 생성해준다. 그렇다면 이렇게 만들어진 api들이 어디서 어떻게 쓰여서 Redux-saga로 받아올수 있느냐..!
2. auth.saga.ts 에서 기능별 필요한 Saga들을 만든다! 즉, api를 요청하고 각 상황별 액션을 제너레이터 함수 형태로 만들어서 내보내준다!
// 로그인 호출 Saga 생성
function* controlLoginSaga(action : 사가가 호출될때 들어오는 액션, 파라미터값){
try{
// call : 주어진 함수를 실행한다
const login : 서버에서 뱉어주는 로그인 응답값 타입 = yield call(authApi.fetchLogin, action.payload);
// 데이터를 받아서 원하는 액션 분기처리 해주는곳
if(login.statusCode === 1000) {~~~~}
localStorage.setItem ("ROLE", login.accessRole)
..
} catch(error){
//에러코드를 분석하여 다른 액션을 put 해줄수도 있다
// put : 특정 액션을 dispatch한다
yield put(AuthSlice.loginFail())
//AuthSlice의 loginFail은 바로 리듀서...!!!
}
//로그인코드 받아오는 Saga생성
//비밀번호 업데이트 Saga생성
..
//만든 Saga들을 쓸수 있도록 내보내준다
export funcion* authSaga(){
yield takeLatest(AuthSlice.loginRequest, controlLoginSaga);
// 리듀서파일(AuthSlice)의 loginRequest라는 리듀서가 호출이 되면 controlLoginSaga를 실행시킨다는뜻.
// takeLatest는 AuthSlice.loginRequest에 대하여 기존에 진행 중이던 작업이 있다면 취소 처리하고
// 가장 마지막으로 실행된 작업에 대해서만 controlLoginSaga를 수행한다.
(제너레이터 문법과 기본적인 리듀서, dispatch, 구독의 개념은 필히 알고 있어야 사가 생성 및 문법 사용이 가능하다..!)
이렇게 각 리듀서가 호출될때마다 실행시켜줄 사가들을 사가 파일에서 내보내준다!
3. auth.reducer.ts 에서는 리듀서들을 만들어준다! 리듀서는 액션을 감지하여 state값을 변경한다.
export const authSlice = {
//로그인 요청 리듀서
loginRequest : {
// 초기 state값을 지정해놔야한다
reducer: (state : AuthState) =>{
// 지정해논 여러개의 타입중 하나로 바꾸어주는 로직
state.isLogin = LoginStatus.LOGOUT },
//콜백을 사용하여 리듀서가 실행되기 전에 사용자 페이로드를 지정할수 있는 prepare기능
prepare : (loginData)=>({payload : logindata})
}
,
// 로그인 성공 리듀서
loginSuccess:(state: AuthState, action: PayloadAction<>)=>{
state.decodedToken = action.payload
// action의 payload는 받아서 들어오는값..!!
}
...
//만들어진 리듀서들을 사가에서 선택해서 쓰고있다!
4. auth.slice.ts에서는 만들어진 사가들의 루트라고 할수 있다. 초깃값을 지정해주고, 하나의 리듀서로서 내보내준다.
//초기 타입지정 해주는 부분
const initialState: AuthState = {
isLogin: LoginStatus.INIT,
decodedToken: null,
account: {
passwordCheck: false,
isMask: true,
isLoading: false,
validateResult: {
nowPassword: false,
newPassword: false
}
},
error: '',
loading: false,
salesManagers: [],
managementManagers: []
};
//리듀서로 지정해주는 부분
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: authReducer
});
//리듀서 모음 조각
export const {
loginRequest,
loginSuccess,
...
} = authSlice.actions;
//리듀서 내보내줘서 사용할수 있는 부분
export default authSlice.reducer;
이렇게 리듀서를 내보내주면 ..!!
dispatch를 사용하여 리듀서를 호출해서 사가를 통해 api 통신을 하고,
state에 담겨진 값을 useSelector를 통해 꺼내와서 쓸수 있다..!
Saga과 axios를 이용하여 비동기 통신 처리를 해줄때,
무조건 요청하고 한번에 받았던 axios 사용과 달리
통신이 이루어지는 단계별로 saga에서 호출을 단계적으로 처리해줄수 있어서 좋았다.
프로젝트 수행을 위해 겉핥기 식으로 수행하며 알아가 보았지만, 제대로 파헤치며 사가 작동원리와 활용법에 대해 더 익혀야겠다!