redux dev tools
npm i cors
npm run dev
git clone tailwindcss frontend파일 설치
remove remote
Terminal 새로 열고
cd frontend
npm i @reduxjs/toolkit react-redux
redux/appSlice.js folder/file 생성
import { createSlice } from "@reduxjs/toolkit";
const appSlice = createSlice({
name: "appSlice",
initialState: {},
reducers: {},
extraReducers: {},
});
export default appSlice;
import { configureStore } from "@reduxjs/toolkit";
import appSlice from "./appSlice";
const store = configureStore({
reducer: {
appReducer: appSlice.reducer,
},
});
export default store;
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
cd frontend
npm i axios
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getTodos = createAsyncThunk("appSlice/getTodos", async () => {
const response = await axios.get("http://localhost:3010/todos");
console.log(response); -> 콘솔은 잘 되는지 확인하기 위함!
return response.data.todos;
});
import { createSlice } from "@reduxjs/toolkit";
import { getTodos } from "./appThunk";
const appSlice = createSlice({
name: "appSlice",
initialState: {
todos: null,
isLoading: false,
},
extraReducers: (builder) => {
builder.addCase(getTodos.pending, (state) => {
state.isLoading = true;
});
builder.addCase(getTodos.fulfilled, (state, action) => {
state.todos = action.payload;
state.isLoading = false;
});
builder.addCase(getTodos.rejected, (state) => {
state.isLoading = false;
});
},
});
export default appSlice;
import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getTodos } from "./redux/appThunk";
const App = () => {
const { todos } = useSelector((state) => state.appReducer);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTodos());
}, []);
useEffect(() => {
console.log(todos);
}, [todos]);
return <div className="bg-red-100">Hello, React!</div>;
};
export default App;
const express = require("express");
const cors = require("cors");
const todosRouter = require("./routes/todos");
const app = express();
const port = 3010;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/todos", todosRouter);
app.get("/", (req, res) => {
return res.send("Hello, Express!");
});
app.listen(port, () => {
console.log(`🚀 Server is listening on port : ${port}`);
});
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getTodos = createAsyncThunk("appSlice/getTodos", async () => {
const response = await axios.get("http://localhost:3010/todos");
return response.data.todos;
});
import { useState } from "react";
import { useDispatch } from "react-redux";
import { createTodo } from "../redux/appThunk";
const CreateTodo = () => {
const [newTodo, setNewTodo] = useState("");
const dispatch = useDispatch();
const onSubmitCreateTodo = (e) => {
e.preventDefault();
if (!newTodo) return;
dispatch(createTodo({ title: newTodo }));
};
return (
<form onSubmit={onSubmitCreateTodo}>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<input type="submit" value="생성" />
</form>
);
};
export default CreateTodo;
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getTodos = createAsyncThunk("appSlice/getTodos", async () => {
const response = await axios.get("http://localhost:3010/todos");
return response.data.todos;
});
export const createTodo = createAsyncThunk(
"appSlice/createTodo",
async ({ title }) => {
const response = await axios.post(
"http://localhost:3010/todos",
{ title },
{
headers: {
"Content-Type": "application/json",
},
}
);
return response.data.todo;
}
);
import { createSlice } from "@reduxjs/toolkit";
import { createTodo, getTodos } from "./appThunk";
const appSlice = createSlice({
name: "appSlice",
initialState: {
todos: null,
isLoading: false,
},
extraReducers: (builder) => {
builder.addCase(getTodos.pending, (state) => {
state.isLoading = true;
});
builder.addCase(getTodos.fulfilled, (state, action) => {
state.todos = action.payload;
state.isLoading = false;
});
builder.addCase(getTodos.rejected, (state) => {
state.isLoading = false;
});
builder.addCase(createTodo.pending, (state) => {
state.isLoading = true;
});
builder.addCase(createTodo.fulfilled, (state, action) => {
state.todos = [...state.todos, action.payload];
state.isLoading = false;
});
builder.addCase(createTodo.rejected, (state) => {
state.isLoading = false;
});
},
});
export default appSlice;
import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getTodos } from "./redux/appThunk";
import CreateTodo from "./components/CreateTodo";
const App = () => {
const { todos } = useSelector((state) => state.appReducer);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTodos());
}, []);
useEffect(() => {
console.log(todos);
}, [todos]);
return (
<div className="bg-red-100">
<CreateTodo />
</div>
);
};
export default App;