Compare commits

...

2 Commits

Author SHA1 Message Date
junh_eee(이준희) 3269298ba3 기초정보/기체관리 toolkit 적용중 8 months ago
junh_eee(이준희) 33cb319d96 redux-toolkit 초기셋팅 8 months ago
  1. 13
      package.json
  2. 31
      src/app/test/drone.tsx
  3. 11
      src/app/test/page.tsx
  4. 25
      src/redux/features/basis/drone/droneSlice.ts
  5. 71
      src/redux/features/basis/drone/droneState.ts
  6. 146
      src/redux/features/basis/drone/droneThunk.ts
  7. 7
      src/redux/hooks.ts
  8. 10
      src/redux/provider.tsx
  9. 11
      src/redux/rootReducer.ts
  10. 9
      src/redux/store.ts
  11. 27
      src/utils/axiosInterceptor.ts
  12. 20
      src/utils/cookie.ts
  13. 51
      src/utils/jwtToken.ts

13
package.json

@ -9,16 +9,23 @@
"lint": "next lint"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.7",
"axios": "^1.6.0",
"js-cookie": "^3.0.0",
"jwt-decode": "^3.1.2",
"next": "13.5.6",
"qs": "^6.11.2",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"react-redux": "^8.1.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/qs": "^6.9.11",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "13.5.6"
"eslint-config-next": "13.5.6",
"typescript": "^5"
}
}

31
src/app/test/drone.tsx

@ -0,0 +1,31 @@
import { getDroneList } from '@/redux/features/basis/drone/droneThunk';
import { useAppSelector } from '@/redux/hooks';
import { AppDispatch } from '@/redux/store';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
export default function Drone() {
const dispatch = useDispatch<AppDispatch>();
const droneList = useAppSelector(state => state.drone.list);
useEffect(() => {
console.log(droneList, '---list');
}, [droneList]);
const handler = () => {
dispatch(
getDroneList({
groupId: 'C807F9',
page: 1
})
);
};
return (
<div>
main
<div style={{ background: '#ff00ff', width: '100px' }} onClick={handler}>
Redux-toolkit
</div>
</div>
);
}

11
src/app/test/page.tsx

@ -0,0 +1,11 @@
'use client';
import Drone from './drone';
export default function Home() {
return (
<>
<Drone />
</>
);
}

25
src/redux/features/basis/drone/droneSlice.ts

@ -0,0 +1,25 @@
import { createSlice } from '@reduxjs/toolkit';
import { InitDroneState } from './droneState';
import { getDroneList, getIdntfList } from './droneThunk';
const droneSlice = createSlice({
name: 'droneSlice',
initialState: InitDroneState,
reducers: {},
extraReducers: builder => {
builder.addCase(getDroneList.fulfilled, (state, action) => {
const { data, count } = action.payload;
state.list = data.items;
state.total = data.total;
state.count = count;
});
builder.addCase(getIdntfList.fulfilled, (state, action) => {
const { data, count } = action.payload;
state.listIdntf = data;
state.idntfCount = count;
state.isRefreshIdntf = false;
});
}
});
export const droneReducer = droneSlice.reducer;

71
src/redux/features/basis/drone/droneState.ts

@ -0,0 +1,71 @@
export const InitDroneState = {
list: undefined,
count: 0,
detail: undefined,
searchParams: '',
selectData: undefined,
listIdntf: undefined,
idntfCount: 0,
isRefreshIdntf: false,
page: 1,
total: 0
};
export interface DroneState {
list: DroneData[] | undefined;
count: number | 0;
detail: DroneData | undefined;
searchParams: string | '';
selectData: SelectData | undefined;
listIdntf: IdntfData[] | undefined;
idntfCount: number | 0;
isRefreshIdntf: boolean | false;
page: number | 1;
total: number | 0;
}
export interface SelectData {
groupId: string;
groupNm: string;
}
export interface DroneData {
groupId: string;
arcrftSno: number;
arcrftHght: number;
arcrftLngth: number;
arcrftModelNm: string;
arcrftTypeCd: string;
arcrftWdth: number;
arcrftWght: number;
cameraYn: string;
createDt: Date;
createUserId: string;
insrncYn: string;
prdctCmpnNm: string;
prdctDate: Date;
prdctNum: string;
takeoffWght: number;
updateDt: Date;
updateUserId: string;
imageUrl: string;
wghtTypeCd: string;
idntfNum: string;
newIdntfNum: string;
idntfTypeCd: string;
ownerSno: number;
ownerNm: string;
hpno: string;
telno: string;
useYn: string;
file: FileList;
}
export interface IdntfData {
idntfNum: string;
newIdntfNum: string;
isSave: boolean;
updateDt: Date;
createDt: Date;
cstmrSno: number;
}

146
src/redux/features/basis/drone/droneThunk.ts

@ -0,0 +1,146 @@
import axios from '../../../../utils/axiosInterceptor';
import qs from 'qs';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { DroneData } from './droneState';
// 드론 목록 조회
export const getDroneList = createAsyncThunk(
'basis/getDroneList',
async (data: any) => {
try {
const queryString = qs.stringify(data, {
addQueryPrefix: true,
arrayFormat: 'repeat'
});
const res = await axios.get(`api/bas/dron/list${queryString}`);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 드론 상세 조회
export const getDroneDetail = createAsyncThunk(
'basis/getDroneDetail',
async (id: number) => {
try {
const res = await axios.get(`api/bas/dron/detail/${id}`);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 드론 생성
export const createDrone = createAsyncThunk(
'basis/createDrone',
async (data: DroneData) => {
try {
const res = await axios.post('api/bas/dron/create', data);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 드론 수정
export const updateDrone = createAsyncThunk(
'basis/updateDrone',
async (data: DroneData) => {
try {
const res = await axios.put('api/bas/dron/update', data);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 드론 삭제
export const deleteDrone = createAsyncThunk(
'basis/deleteDrone',
async (id: number) => {
try {
const res = await axios.delete(`api/bas/dron/delete/${id}`);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 식별장치 목록 조회
export const getIdntfList = createAsyncThunk(
'basis/getIdntfList',
async (id: number) => {
try {
const res = await axios.get(`api/bas/dron/idntf/list/${id}`);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 식별장치 수정
export const updateIdntf = createAsyncThunk(
'basis/updateIdntf',
async (item: { id: number; data: any[] }) => {
try {
const res = await axios.put(`/api/bas/dron/idntf/update/${item.id}`, {
idntfNum: item.data[0].idntfNum,
ownerNm: item.data[0].ownerNm,
hpno: item.data[0].hpno
});
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 식별장치 삭제
export const deleteIdntf = createAsyncThunk(
'basis/deleteIdntf',
async (id: string) => {
try {
const res = await axios.delete(`api/bas/dron/idntf/delete/${id}`);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 식별장치 생성
export const createIdntf = createAsyncThunk(
'basis/createIdntf',
async (item: { data: any[]; arcrftSno: number }) => {
try {
const res = await axios.post('api/bas/dron/idntf/create', item);
return res.data;
} catch (error) {
console.error(error);
}
}
);
// 파일 업로드
export const fileUpload = createAsyncThunk(
'basis/fileUpload',
async (file: any) => {
try {
let form = new FormData();
form.append('file', file);
const res = await axios.post('api/file/upload', form);
return res.data;
} catch (error) {
console.error(error);
}
}
);

7
src/redux/hooks.ts

@ -0,0 +1,7 @@
import { AppDispatch, store } from './store';
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
type RootState = ReturnType<typeof store.getState>;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

10
src/redux/provider.tsx

@ -0,0 +1,10 @@
'use client';
import { store } from './store';
import { Provider } from 'react-redux';
type Props = {
children: React.ReactNode;
};
export default function ReduxProvider({ children }: Props) {
return <Provider store={store}>{children}</Provider>;
}

11
src/redux/rootReducer.ts

@ -0,0 +1,11 @@
import { combineReducers } from '@reduxjs/toolkit';
import { droneReducer } from './features/basis/drone/droneSlice';
const rootReducer = (state: any, action: any) => {
const combineReducer = combineReducers({
drone: droneReducer
});
return combineReducer(state, action);
};
export default rootReducer;

9
src/redux/store.ts

@ -0,0 +1,9 @@
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';
export const store = configureStore({
reducer: rootReducer
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

27
src/utils/axiosInterceptor.ts

@ -0,0 +1,27 @@
import axios, { AxiosInstance } from 'axios';
import { getAccessToken } from './jwtToken';
import { store } from '@/redux/store';
const axiosInstance: AxiosInstance = axios.create({
baseURL: 'http://121.190.193.50:6081/'
});
axiosInstance.interceptors.request.use(
async config => {
config.headers['Authorization'] =
'palnet eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwYWxuZXQiLCJjc3RtclNubyI6MiwiZXhwIjoxNzA4MzA2Nzg4LCJ1c2VySWQiOiJwYWxuZXQiLCJpYXQiOjE3MDY0OTIzODh9.X5S5oF5ZWcTK6FwjpVAfRwffuwLeCHUssn1OEJbsSFJDzi-Vp_DdnfgIvWAM1ry97VmNfFsT9bpX8XhE4dRnWg';
// let accessToken = await getAccessToken();
// config.headers['Authorization'] = accessToken;
// // alert(JSON.stringify(config.headers));
// // store.dispatch({
// // // type: GLOBAL_LOADING
// // });
return config;
},
error => {
return Promise.reject(error);
}
);
export default axiosInstance;

20
src/utils/cookie.ts

@ -0,0 +1,20 @@
import Cookies, { CookieAttributes, CookiesStatic } from 'js-cookie';
const CookieStorageBuilder = (cookies: CookiesStatic) => ({
setCookie: (key: string, value: string, options?: CookieAttributes) =>
cookies.set(key, value, {
path: '/',
secure: process.env.NODE_ENV === 'production' ? false : false,
sameSite: 'Lax',
...options
}),
getCookie: (key: string) => cookies.get(key),
removeCookie: (key: string) => cookies.remove(key)
});
export const cookieStorage = CookieStorageBuilder(Cookies);
const COOKIE_BASE_NAME = 'palnet';
export const COOKIE_ACCESS_TOKEN = `${COOKIE_BASE_NAME}_accs`;
export const COOKIE_REFRESH_TOKEN = `${COOKIE_BASE_NAME}_rfrs`;

51
src/utils/jwtToken.ts

@ -0,0 +1,51 @@
import decode from 'jwt-decode';
import {
cookieStorage,
COOKIE_ACCESS_TOKEN,
COOKIE_REFRESH_TOKEN
} from './cookie';
const JWT_FLEFIX = 'palnet ';
export const checkTokenExpired = (token?: string) => {
return true;
// const decodedToken = decode<LoginData>(token as string);
// if (parseInt(decodedToken.exp) < dayjs().unix()) {
// return true;
// }
// return false;
};
export const getAccessToken = async () => {
let token = cookieStorage.getCookie(COOKIE_ACCESS_TOKEN);
if (!token || token == undefined || token == 'undefined') {
return '';
}
// if (checkTokenExpired(token)) {
// //freshtoken 으로 재발급 진행.
// const refreshToken = getRefreshToken();
// //const isRefresh = store.getState()?.authState?.isRefresh;
// if (refreshToken) {
// // if (!store.getState()?.authState?.isRefresh) {
// const decodeToken = decode<LoginData>(token as string);
// const cstmrSno: number = decodeToken.cstmrSno;
// const { data, errorCode } = await refreshTokenAPI(cstmrSno, refreshToken);
// if (errorCode === '-101') {
// cookieStorage.setCookie(COOKIE_REFRESH_TOKEN, '');
// }
// //REFHRESTH 토큰 넣기
// cookieStorage.setCookie(COOKIE_ACCESS_TOKEN, data.accessToken);
// cookieStorage.setCookie(COOKIE_REFRESH_TOKEN, data.refreshToken);
// token = data.refreshToken;
// // }
// }
// }
return JWT_FLEFIX + token;
};
Loading…
Cancel
Save