#오늘 배운 것
일반 redux와 툴킷의 차이점
- == 큰 차이점은 Action Value와 Action Creator를 이제 직접 생성해주지 않고, Action Value, Action Creator, Reducer가 하나로 합쳐졌다는 점
redux를 사용하면 Redux Devtools 를 사용할 수 잇따는 장점이 있다
.현재 프로젝트의 state 상태라던가, 어떤 액션이 일어났을 때 그 액션이 무엇이고, 그것으로 인해 state가 어떻게 변경되었는지 등 리덕스를 사용하여 개발할 때 아주 편리하게 사용
Slice
기본 베이스
//createSlice API 뼈대
const counterSlice = createSlice({
name: '', // 이 모듈의 이름
initialState : {}, // 이 모듈의 초기상태 값
reducers : {}, // 이 모듈의 Reducer 로직
})
// src/redux/modules/counterSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
number: 0,
};
const counterSlice = createSlice({ //tookit의 api를 사용한 것. name, initialState, reducers필수!
//듀서의 로직이 되면서도 동시에 Action Creator가 된다. 또 Action Value 까지 함수의 이름을 따서 자동으로 만들어짐.
name: "counter",
initialState,
reducers: {
addNumber: (state, action) => {
state.number = state.number + action.payload;
},
minusNumber: (state, action) => {
state.number = state.number - action.payload;
},
},
});
// 액션크리에이터는 컴포넌트에서 사용하기 위해 export 하고
export const { addNumber, minusNumber } = counterSlice.actions;
// reducer 는 configStore에 등록하기 위해 export default 합니다.
export default counterSlice.reducer;
// src/redux/modules/config/configStore.js
import { configureStore } from "@reduxjs/toolkit";
/**
* import 해온 것은 slice.reducer 입니다.
*/
import counter from "../modules/counterSlice";
import todos from "../modules/todosSlice";
/**
* 모듈(Slice)이 여러개인 경우
* 추가할때마다 reducer 안에 각 모듈의 slice.reducer를 추가해줘야 합니다.
*
* 아래 예시는 하나의 프로젝트 안에서 counter 기능과 todos 기능이 모두 있고,
* 이것을 각각 모듈로 구현한 다음에 아래 코드로 2개의 모듈을 스토어에 연결해준 것 입니다.
*/
const store = configureStore({
//modules과 store생성을 한번에 하고 있다.객체안에 modules을 넣어주면 됨.
reducer: { counter: counter, todos: todos },
});
export default store;
json-server
설치 : yarn add json-server
시작 명령어 : yarn json-server --watch db.json --port 3001
(db.json을 db로 삼아 3001번 포트에서 서버를 시작한다는 뜻)
하나의 서버이기 때문에 react서버와 별개로 작업해야한다. 리액트도 start하고, json-server로 start 해야 함.
후에 json파일이 추가되면 이후 파일을 수정하면 된다.
//db.json
{
"todos": [
{
"id": 1,
"title": "json-server",
"content": "json-server를 배워봅시다."
}
]
}
Axios
node.js와 브라우저를 위한 Promise 기반 http 클라이언트. 서버와 통신하기 위한 방법
설치 :yarn add axios
get - 데이터 조회 (상황에 따라 요청 방법이 다르다.)
// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정을 추가할 수 있습니다.
// config는 axios 공식문서에서 확인하세요.
axios.get(url[, config]) // GET
- path variable - 정해져 있는 id를 넘겨줄때 사용. (1번 post를 get요청해라)

- query - 검색,특정 값을 찾을 때.(제목이 json이고 author가 typicode인 것을 가지고 와라.)

// src/App.js
import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.
const App = () => {
const [todos, setTodos] = useState(null);
// axios를 통해서 get 요청을 하는 함수를 생성합니다.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리합니다.
const fetchTodos = async () => { //promise기반이기 때문에 비동기를 기반으로 작성한다.
//json서버에 있는 todo데이터를 가져와 settodo해 todo에 넣겠다.
const { data } = await axios.get("http://localhost:3001/todos");
setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set 합니다.
};
// 생성한 함수를 컴포넌트가 mount 됐을 떄 실행하기 위해 useEffect를 사용합니다.
useEffect(() => {
// effect 구문에 생성한 함수를 넣어 실행합니다.
fetchTodos(); //get를 이용해 todo데이터를 받아와 usestate를 이용해 업데이트 한다.
}, []);
// data fetching이 정상적으로 되었는지 콘솔을 통해 확인합니다.
console.log(todos); // App.js:16
return <div>App</div>;
};
export default App;
POST - 서버에 데이터를 추가할 때 사용.백엔드쪽은 클라이언트의 데이터를 body형태로 서버에 보내고자 할 때 사용
예시 (화면에 인풋과 버튼이 있고, 인풋에 어떤 값을 넣고 버튼을 클릭했을 때 onSubmitHandler 이 실행됩니다. ==>onSubmitHandler 함수의 목적은 todo를 body에 담아 서버로 POST 요청을 보내는 것 입니다.)
★추가한 todo를 todos에 반영되지 않아 리렌더링 되지 않았다. => setTodos([...todos, todo]) 를 추가하면 적용 된다.
// src/App.jsx
import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.
const App = () => {
// 새롭게 생성하는 todo를 관리하는 state
const [todo, setTodo] = useState({
title: "",
});
//★
const [todos, setTodos] = useState(null);
//★
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:3001/todos");
setTodos(data);
};
//★
const onSubmitHandler = async(todo) => {
//1. 이때 todos는 [{투두하나}]임
await axios.post("http://localhost:3001/todos", todo); // 이때 서버에 있는 todos도 [{투두하나}]임
// 근데 여기서 서버 요청이 끝나고 서버는 [{투두가},{두개임}]
setTodos([...todos, todo]) 2. <-- 만약 이게 없다면, go to useEffect
//4. 새로고침해서 진짜 현재 서버 데이터를 받아오기전에 상태를 똑같이 동기시켜줌
//5. 어떻게보면 유저한테 서버에서 새로 받아온것처럼 속이는거지
};
//★
useEffect(() => {
fetchTodos(); //3. 새로고침해서 여기를 다시 실행해줘야 서버값이 새로 들어옴 e.g) [{투두가},{두개임}]
}, []);
return (
//★
<>
<form
onSubmit={(e) => {
// 👇 submit했을 때 브라우저의 새로고침을 방지합니다.
e.preventDefault();
onSubmitHandler(todo);
}}
>
<input
type="text"
onChange={(ev) => {
const { value } = ev.target;
setTodo({
...todo,
title: value,
});
}}
/>
<button>추가하기</button>
</form>
<div>
{todos?.map((todo) => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
</>
//★
);
};
export default App;
★네트워크탭 확인하기
1.headers 2.payload 3.response
delete
axios.delete(url[, config]) // DELETE
const onClickDeleteButtonHandler = (todoId) => {
axios.delete(`http://localhost:3001/todos/${todoId}`);
};
<buttontype="button" onClick={() => onClickDeleteButtonHandler(todo.id)}>
삭제하기
</button>
patch
axios.patch(url[, data[, config]]) // PATCH
// patch에서 사용할 id, 수정값의 state를 추가
const [targetId, setTargetId] = useState(null);
const [editTodo, setEditTodo] = useState({
title: "",
});
// 수정버튼 이벤트 핸들러 추가 👇
const onClickEditButtonHandler = (todoId, edit) => {
axios.patch(`http://localhost:3001/todos/${todoId}`, edit);
};
{/* 👇 수정기능에 필요한 id, 수정값 input2개와 수정하기 버튼을 추가 */}
<div>
<input
type="text"
placeholder="수정하고싶은 Todo ID"
onChange={(ev) => {
setTargetId(ev.target.value);
}}
/>
<input
type="text"
placeholder="수정값 입력"
onChange={(ev) => {
setEditTodo({
...editTodo,
title: ev.target.value,
});
}}
/>
<button
// type='button' 을 추가해야 form의 영향에서 벗어남
type="button"
onClick={() => onClickEditButtonHandler(targetId, editTodo)}
>
수정하기
</button>
</div>
미들웨어 - 리덕스에서 dispatch를 하면 action 이 리듀서로 전달이 되고, 리듀서는 새로운 state를 반환
thunk
thunk를 사용하면 우리가 dispatch를 할때 객체가 아닌 함수를 dispatch 할 수 있게 해줍니다. 즉 dispatch(객체) 가 아니라 dispatch(함수)를 할 수 있게 되는 것
dispatch(함수) → 함수실행 → 함수안에서 dispatch(객체)
- 우리의 첫 thunk 함수 만들기
- extraReducer에 thunk 등록하기
- dispatch(thunk 함수) 하기
// thunk 함수는 createAsyncThunk 라는 툴킷 API를 사용해서 생성합니다.
// __가 함수 이름에 붙는 이유는 이 함수가 thunk 함수라는 것을 표시하기 위한
// 개인의 convention 입니다. 함수의 이름은 본인이 편한 이름으로 명명하세요.
export const __addNumber = createAsyncThunk(
"ADD_NUMBER_WAIT",
(arg, thunkAPI)=>{},
);
#후발대
ex) 네이버 안의 수많은 페이지가 있는데 라우팅이 많이 필요한데
라우터 안에 컴포넌트가 페이지에 들어간다!!많을 경우 디렉토리가 필요하다
redux폴더 config, modules(return되는것이 reduxcer가 나온다)pages안의 수많은 기능들
우리의 playground는 app.jsx라고 생각하면 된다.
라우터.js(라우터들의 집합)에서 react-router-dom을 설치하자!
react와 redux 차이 : redux는 state를 관리하는 방법이 더 간편해짐
- 투두리스트 해석
redux폴더의 configStore가 store를 내보내면 index.js에 store를 연결시킨다.
modules안의 todo.js는 reducer.이 안에 핵심 내용이 다 들어있는 셈.
reducers =>state와 그것을 관리하는 방법==action(type,payload를 객체로 보유)이 같이 들어있는 것.
그래서 actionCreate는 action을 만들어주는 함수.각 컴포넌트의 import에서 사용

이 state들에 대한 action을 진행해달라
action item을 만든 이유 : 2개를 묶을 메타 데이터...?오류를 방지하기위해

todos의 state
useSelector : state를 가지고 옴.
dispatch : state값을 변경할 수 있는 action을 호출하는 방법.
navigate : 모달을 띄울 때 /${todo.id}를 가져와 띄운다.
여러개의 기능을 하나로 합쳐준 것 == 툴킷
thunk가 툴킷의 내장되어 있어 비동기적인 부분을 수행한다.
1pending 2.fullfiled 3.rejected
useEffect 컴포넌트가 렌더링 될 때 무조건 실행하는 함수.
thunk

action을 만든다 ==외부에서 action을 호출할 수 있도록 actionCreate를 만들어준다.

비동기적인 요소만 들어간다.