Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.5",
"axios": "^1.7.2",
"next": "14.2.4",
"react": "^18",
"react-dom": "^18",
Expand Down
2,309 changes: 937 additions & 1,372 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions src/hooks/useFetchDashboards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useQuery } from 'react-query';
import { useDispatch, useSelector } from 'react-redux';

import { getDashboardsList } from '@/services/getService';
import { setDashboards } from '@/store/reducers/dashboardsSlice';

const fetchDashboards = async () => {
const response = await getDashboardsList();

if (response.status !== 200) {
throw new Error('Failed to fetch dashboards');
}

return response.data;
};

export const useFetchDashboards = () => {
const dispatch = useDispatch();

return useQuery('dashboards', () => fetchDashboards(), {
onSuccess: (data) => {
dispatch(setDashboards({ dashboards: data.dashboards, totalCount: data.totalCount }));
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 핸들링은 사용하는 분이 여기서 추가하면 될까요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞습니다! onError 콜백함수를 추가하시면 돼요!

onError: (error: Error) => {
      console.error('Error fetching dashboards:', error.message);

});
};
58 changes: 15 additions & 43 deletions src/hooks/useFetchData.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,18 @@
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { RootState } from "@/store/store";

const useFetchData = <T>(url: string) => {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);

const accessToken = useSelector((state: RootState) => state.user.accessToken);

useEffect(() => {
const fetchData = async () => {
try {
const headers: HeadersInit = {};

if (accessToken) {
headers["Authorization"] = `Bearer ${accessToken}`; // 로그인 상태인 경우, 전역상태의 accessToken을 가져와 인증 요청을 할 수 있음
}

const response = await fetch(url, { headers });

if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const result: T = await response.json();
setData(result);
} catch (error) {
if (error instanceof Error) {
setError(error.message);
} else {
setError("An unknown error occurred");
}
} finally {
setIsLoading(false);
}
};

fetchData();
}, [url, accessToken]);

return { data, isLoading, error };
import { useQuery, QueryKey } from 'react-query';

const useFetchData = <T>(queryKey: QueryKey, getService: () => Promise<{ data: T }>) => {
const fetchData = async () => {
const response = await getService();
return response.data;
};

return useQuery<T, Error>({
queryKey, // 캐시 키를 임의로 지정할 수 있음
queryFn: fetchData, // 비동기 getService 함수 (services/ 에 정의)
Copy link
Contributor

@un0211 un0211 Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'data'로 하면 모두 key가 'data'가 되는 것 아닌가요??
useFetchData 함수는 어떤 경우 사용될까요?? Redux로 관리되지 않는 data fetch에 사용되나요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 Redux로 관리되지 않는 데이터 조회 요청을 할 때 쓰시면 됩니다!
캐시키는 제가 잘못 생각했네요 ㅎㅎ.. 캐시키도 파라미터에 추가해서 자유롭게 바꿀수 있도록 수정할게요!

onError: (error: Error) => {
console.error('Error fetching data:', error);
},
});
};

export default useFetchData;
19 changes: 19 additions & 0 deletions src/services/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from 'axios';

import { store } from '@/store/store';

const instance = axios.create({
baseURL: 'https://sp-taskify-api.vercel.app/15',
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지윤님 api에 teamId가 {기수}-{팀} 으로 되어야해서 6-15를 사용해야 할 것 같아요 🫡

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 맞아요 스웨거에는 기수-팀으로 되어있는데 실제로 테스트해보면 15만 적어야 통신이 되더라구요!
요건 스웨거에서 잘못 알려준것 같아요


instance.interceptors.request.use((config) => {
const state = store.getState();
const { accessToken } = state.user; // 전역 상태에 저장된 accessToken을 가져와 인증요청에 사용
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}

return config;
});

export default instance;
15 changes: 15 additions & 0 deletions src/services/getService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import instance from './axios';

// 컬럼 목록 조회
export async function getColumnsList(id: string) {
return await instance.get(`/columns?dashboardId=${id}`);
}

// 대시보드 목록 조회
export async function getDashboardsList(
navigationMethod: 'infiniteScroll' | 'pagination' = 'infiniteScroll', // navigationMethod는 'infiniteScroll' 또는 'pagination'만 가능. 기본값은 'infiniteScroll'
page: number = 1, // 기본값 1
size: number = 10, // 기본값 10
) {
return await instance.get(`/dashboards?navigationMethod=${navigationMethod}&page=${page}&size=${size}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page, size는 number type인 게 더 좋을 것 같아요!
navigationMethod는 타입을 'infiniteScroll', 'pagination' 둘만 받도록 하는 게 어떨까요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 넵!! 이 부분도 수정할게요

}
28 changes: 28 additions & 0 deletions src/store/reducers/dashboardsSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

import { Dashboard } from '@/types/Dashboard.interface';

interface DashboardsState {
dashboards: Dashboard[];
totalCount: number;
}

const initialState: DashboardsState = {
dashboards: [],
totalCount: 0,
};

const dashboardsSlice = createSlice({
name: 'dashboards',
initialState,
reducers: {
setDashboards(state, action: PayloadAction<{ dashboards: Dashboard[]; totalCount: number }>) {
state.dashboards = action.payload.dashboards;
state.totalCount = action.payload.totalCount;
},
},
});

export const { setDashboards } = dashboardsSlice.actions;

export default dashboardsSlice.reducer;
5 changes: 2 additions & 3 deletions src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

import dashboardsReducer from './reducers/dashboardsSlice';
import userReducer from './reducers/userSlice';

const persistConfig = {
key: 'root',
storage,
};

const persistedReducer = persistReducer(persistConfig, userReducer);

export const store = configureStore({
reducer: {
user: persistedReducer,
dashboards: dashboardsReducer,
},
devTools: process.env.NODE_ENV !== 'production',
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const persistor = persistStore(store);
15 changes: 15 additions & 0 deletions src/types/Dashboard.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface Dashboard {
id: number;
title: string;
color: string;
userId: number;
createdAt: string;
updatedAt: string;
createdByMe: boolean;
}

export interface DashboardsResponse {
dashboards: Dashboard[];
totalCount: number;
cursorId: number | null;
}