02 - 21 ~ 02 - 22 일지
특이사항
Thunk 구조
일반적인 dispatch를 쓰는데, 여기서 경로를 createAsyncThunk한 액션 객체로 넘긴다. 그러면
dispatch - 함수 실행 - 함수 내에서 dispatch 실행이 된다. 연습해 본 바로는
1
2
3
4
5
6
7
8
9
10
11
12
13
|
export const __addNumber = createAsyncThunk(
// 첫번째 인자 : action value
"addNumber",
// 두번째 인자 : 콜백함수
(payload, thunkAPI) => {
if(payload <= 10){
setTimeout(() => {
thunkAPI.dispatch(addNumber(payload));
}, 3000);
}
}
);
|
cs |
의 구조도 가능하다. 좀더 간략하게 짠 기본 구조는
1
2
3
4
5
6
|
const __addTodo = createAsyncThunk(
"addTodo",
(payload, thunkAPI) =>{
}
)
|
cs |
이다.
텅크에서 프로미스를 다루기
현재 할 수 있는 방법은 제이슨 서버를 띄우고, 텅크 함수를 통해 API를 호출, 서버로부터 가져온 값을 Store에 dispatch하는 방법이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
export const __getTodo = createAsyncThunk(
"getTodo",
//이 안에서 서버 통신을 시도한다. 에이싱크 잊지말고
async (payload, thunkAPI) =>{
try{
const data = await axios.get("http://localhost:3001/todos");
console.log(data)
//네트워크 요청이 성공한 경우 dispatch해주는 기능
return thunkAPI.fulfillWithValue(data.data)
}
catch(error){
console.log(error);
//반대로 실패했을 때 디스패치한다.
return thunkAPI.rejectWithValue(error)
}
}
)
|
cs |
텅크 함수 구현 - 가져온 데이터를 stroe로 dispatch하기
이제부터 좀 더 복잡해지는 군. store로 diuspatch하는 것은 해봤다. 근데 그것을 텅크 안에 넣는다? 흠..... 일단 위에 있는 return thunkAPI.fulfillWithValue(data.data) 부분이 네트워크 요청 성공 시 dispatch해주는 부분이다. 그러면 이걸 받아주는 리듀서를 만들어줘야겠지?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
const todosSlice = createSlice({
name: "todos",
initialState,
reducers: {},
//생각해보니 각각의 API가 디스패치하기는 하는데, 어디로 디스패치를 하는건가?
//액스트라 리듀서에서는 pending, fulfilled, rejected에 대해 어떻게 새로운 state를 반환하는지
//구현할 수 있다.
extraReducers:{
[__getTodo.pending]: (state) => {
console.log('122121')
state.isLoading = true; // 네트워크 요청이 시작되면 로딩상태를 true로 변경합니다.
},
[__getTodo.fulfilled]: (state, action) => {
console.log('33333333')
state.isLoading = false; // 네트워크 요청이 끝났으니, false로 변경합니다.
state.todos = action.payload; // Store에 있는 todos에 서버에서 가져온 todos를 넣습니다.
},
[__getTodo.rejected]: (state, action) => {
console.log('444444444444')
state.isLoading = false; // 에러가 발생했지만, 네트워크 요청이 끝났으니, false로 변경합니다.
state.error = action.payload; // catch 된 error 객체를 state.error에 넣습니다.
},
}
});
|
cs |
엑스트라 리듀서를 이용하여 서버 연결, 실패에 대응하는 리듀서를 각각 만들어 쓸 수 있다.
커스텀 훅
인풋 개수만큼 늘어나는 useState, event handler 등이다. 보통 커스텀 훅은 src 폴더에 hooks 폴더를 생성, 여기에 보관한다.
1
2
3
4
5
6
7
8
9
10
11
12
|
const useInput = () =>{
const [value, setValue] = useState("")
const handler = (e) =>{
setValue(e.target.value)
}
return [value, handler]
}
|
cs |
이것이 기본적인 input 커스텀 훅이다. handler에서 setValue로 값을 받아오고, 그걸 감싸서 리턴시킨다, 기본적인 코드다. 이걸 본 코드에서 써먹으면 된다는 것이다.
리액트 쿼리
쿼리는 기존 미들웨어의 문제점을 보완하기 위한 장치이다. 주요 키워드는 3가지가 있는데
Query는 데이터에 대한 요청을 의미하고(액시오스의 get과 유사)
Mutation은 데이터의 변형을 의미하며(액시오스의 post, put, patch, delete와 유사)
Query invalidation은 쿼리를 무효화 시키는 것이다.(기존 가져온 쿼리는 서버에서 가져온 데이터이며, 이것이 최신이 아닐 경우가 있다. 그럴 때 기존 쿼리를 무효화 시키고 최신 쿼리를 가져와야하는데 이 때 쓰인다.)
작년까지만 해도 텅크의 점유율이 높았지만 지금은 리액트 쿼리의 점유율이 높아지고 있다.(미들웨어의 다른 종류인건가? 그러면 텅크랑 동시에 쓰지 못하는 것인가?)
설치는 yarn add react-query
기본 구조는
1
2
3
4
5
6
7
8
|
const App = () => {
return (<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
};
|
cs |
로 사용할 페이지에서
1
2
|
<QueryClientProvider client={queryClient}></QueryClientProvider>
|
cs |
로 감싸면 된다.
그 다음 사용을 원하는 리덕스 툴킷 모듈에서
1
|
const { isLoading, isError, data } = useQuery("todos", getTodos);
|
cs |
를 쓴다. 리액트 쿼리가 겁나게 편한 것이, 텅크에서 따로따로 구분하던 로딩, 에러, 데이터를 자동으로, 한 번에 생성해주는 것이다.
1
2
3
|
{data.data.filter((item) => item.isDone === !isActive).map((item) => {
return <Todo key={item.id} todo={item} isActive={isActive} />;
})}
|
cs |
그렇게 받아온 data를 한 번에 집어넣는 것이다. 위 코드는 지긋지긋한 todo를 가져오는 코드인데, 원래 data.data 부분에는 유스셀렉터로 땡겨온 todos 배열을 넣는 역할이었다.
추가 기능 구현
mutation
변형이라고 하는 기능인데
1
|
import { QueryClient, useMutation } from "react-query";
|
cs |
로 받아와서
1
2
3
4
5
6
7
8
9
10
11
|
function Input() {
const queryClient =useQueryClient();
const mutation = useMutation(addTodo, {
onSuccess: () => {
// Invalidate and refresh
// 이렇게 하면, todos라는 이름으로 만들었던 query를
// invalidate 할 수 있어요. 다른 부분에서 mutation.mutate()로 진행된다.
queryClient.invalidateQueries("todos");
},
});
|
cs |
의 구조로 진행된다. mutation 변수를 선언하고 거기에 유스뮤테이션을 집어넣은 다음, 나중에 함수에서 호출시키는 것이다.
useQuery 추가설명
유스쿼리 훅의 사용 방법의 기본 구조는
1
2
3
4
5
6
7
|
import { useQuery } from 'react-query';
import { fetchTodoList } from '../api/fetchTodoList';
function App() {
const info = useQuery('todos', fetchTodoList);
}
|
cs |
이다. 여기서 ‘todos'는 쿼리 키이다. 이것은 refetching하는데 쓰이고, 앱 전체에서 이 쿼리를 공유하는 방법으로도 쓰인다.
fetchTodolist 부분은 쿼리 함수 부분이라고 부른다. 여기선 프로미스 객체를 리턴하며 이 객체는 반드시 data를 성공시키거나 에러를 내야한다. useQuery의 결과물은 객체로 전달된다.
mutations 추가 설명
뮤테이션은 CUD에서 사용된다. useMutation은 hook이다. 함수이며 API이다. 보통 invalidQueries나 setQueryData를 같이 넣어 사용
쓰로틀링과 디바운싱
쓰로틀링은 짧은 시간 연속해서 발생하는 동일 이벤트들을 일정시간 단위로 그룹화하여 처음 또는 마지막 핸들러만 호출시키는 것
주로 쓰이는 예 : 무한 스크롤링
디바운싱은 연속해서 이벤트가 발생하면 이벤트 핸들러를 호출하지 않다가 마지막 이벤트로부터 일정 시간이 경과하면 딱 한 번 호출시키는 것, 입력값 실시간 검색이나 화면 리사이징 등에 쓰인다.
딱히 명령어가 있는 것은 아니고, SetTimeout을 쓸 때 미리 선언해둔 null 변수 timer가 있는지 없는지 체크하고 그때그때 없애는 것이다.
lodash
로대쉬는 사용 빈도가 많은 라이브러리로, 위에 했었던 과정을 아주 간략화 시킬 수 있다. lodash는 아주 간략화된 과정들을 지원하는 라이브러리인데, 여기에서 useCallback을 같이 쓰는 것이 좋다.
인증/인가
인증은 서비스를 이용할 유저가 등록된 회원인지 체크하는 절차.
인가는 특정 리소스에 접근할 수 있는 권한이 있는지 체크하는 절차
쿠키
서버가 클라이언트의 인증 상태를 기억하고 있는 것처럼 구현할 수 있는 수단
브라우저에 저장되는 텍스트 파일이며, 키 - 벨류 타입으로 저장된다.
쿠키는 별도로 조작을 가하지 않는 한 서버와 통신시 자동으로 주고 받는다.