React와 Strapi 연동 - React, Strapi v4, MySQL을 이용한 Todolist 만들기

React, Strapi v4, MySQL을 이용한 Todolist 만들기 시리즈 

React 프로젝트에서 TodoList 프로젝트 뼈대 만들기

아래 코드를 일일히 쳐가며 만들어 가는것을 추천 드리지만, 귀찮은 작업이다 보니 아래 GitHub에 제 코드를 올려놓았으니, 다운 받으시면 됩니다.

 

GitHub - BangHyeonJun/strapi-react-mysql-front

Contribute to BangHyeonJun/strapi-react-mysql-front development by creating an account on GitHub.

github.com

자 그럼 저희가 아까 만들어 놓은 프로젝트를 VSCode를 통해 킵니다. 그러면 기본적인 프로젝트의 구조는 위와 같습니다. 이러한 프로젝트 구조의 파일들을 오른쪽과 같이 맞춰 줍니다.

src/Index.js

해당 컴포넌트는 기본 제공 템플릿에서 크게 변경하지 않았습니다. report 기능만 제거한 컴포넌트 입니다.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
	<React.StrictMode>
		<App />
	</React.StrictMode>
);

src/Index.css

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

src/App.js

할 일 컴포넌트를 호출하는 컴포넌트입니다. 또한 모든 Action 및 Fetching들은 App.js에서 관리합니다.

import React, { useState } from "react";
import TaskManager from "./components/TaskManager";

function App() {
	const [todoList, setTodoList] = useState([]);

	const fetchTodoList = async () => {};

	const changeTaskToComplete = (id) => async () => {};

	const changeTaskToUnComplete = (id) => async () => {};

	const appendTaskInToDoList = async (title) => {};

	const deleteTaskInToDoList = (id) => async () => {};

	return (
		<div className="App">
			<TaskManager
				onComplete={changeTaskToComplete}
				onUnComplete={changeTaskToUnComplete}
				onAppend={appendTaskInToDoList}
				onDelete={deleteTaskInToDoList}
				todoList={todoList}
			></TaskManager>
		</div>
	);
}

export default App;

src/App.css

/* 비어놓음 */

src/components/TaskManager.js

전반적인 할 일 목록의 컴포넌트를 호출하는 컴포넌트입니다. 또한 현재 날짜를 화면에 보여주는 역할도 수행합니다.

import React from "react";
import "./TaskManager.css";
import Tasklist from "./Tasklist";
import Taskform from "./Taskform";

const dateformatted = new Date().toLocaleDateString("ko-KR", {
	weekday: "long",
	year: "numeric",
	month: "long",
	day: "numeric",
});

const TaskManager = ({
	todoList,
	onComplete,
	onUnComplete,
	onAppend,
	onDelete,
}) => {
	return (
		<div className="taskmanager">
			<div className="header">{dateformatted} - 할 일 목록 </div>
			<Tasklist
				onComplete={onComplete}
				onUnComplete={onUnComplete}
				onDelete={onDelete}
				todoList={todoList}
			></Tasklist>
			<Taskform onAppend={onAppend}></Taskform>
		</div>
	);
};

export default TaskManager;

src/components/TaskManager.css

.taskmanager {
	display: grid;
	margin: 5%;
	height: auto;
	border-radius: 12px 8px 5px;
	grid-template-areas: "header" "content" "footer";
	grid-template-columns: 1fr;
	grid-template-rows: 0.1fr 1fr 0.1fr;
	grid-gap: 5px;
}

.header {
	grid-area: header;
	font-size: 28px;
	text-align: center;
	font-weight: bolder;
}
.task {
	grid-area: content;
}
.taskform {
	grid-area: footer;
}

.container {
	width: 100%;
	height: 50vh;
	margin: 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}

.emptyemoji {
	font-size: 6rem;
	margin-bottom: 5vh;
	color: rgba(0 0 0 / 0.3);
}

.emptytxt {
	font-size: 2rem;
	color: rgba(0 0 0 / 0.3);
}

src/components/Tasklist.js

해당 컴포넌트는 본격적으로 할 일 목록을 만들어주는 함수 입니다.

import Task from "./Task";

const Tasklist = ({ onComplete, onUnComplete, onDelete, todoList }) => {
	if (!todoList.length)
		return (
			<div className="container">
				<span className="emptyemoji">🫢</span>
				<strong className="emptytxt">할 일을 입력해 주세요</strong>
			</div>
		);

	return (
		<div>
			<div className="ctheader">To Do</div>

			{todoList.map(
				(v) =>
					v.attributes.complete_yn || (
						<Task
							key={v.id}
							id={v.id}
							className={"task"}
							title={v.attributes.title}
							onClick={onComplete}
							onDelete={onDelete}
						></Task>
					)
			)}

			<div className="ctheader">Completed</div>

			{todoList.map(
				(v) =>
					v.attributes.complete_yn && (
						<Task
							key={v.id}
							id={v.id}
							className={"comtask"}
							title={v.attributes.title}
							onClick={onUnComplete}
							onDelete={onDelete}
						></Task>
					)
			)}
		</div>
	);
};

export default Tasklist;

src/components/Taskform.js

할 일을 추가하는 컴포넌트 입니다. 사용자가 enter를 클릭시 할 일을 추가하는 역할을 하는 컴포넌트 입니다.

import React, { useState } from "react";
import "./Taskform.css";

const Taskform = ({ onAppend }) => {
	const [inputEntered, updateInput] = useState("");

	const enterTextlistener = (e) => {
		if (e.key === "Enter") {
			onAppend(inputEntered);
		} else {
			updateInput(() => e.target.value);
		}
	};

	return (
		<div className="taskform">
			<input
				className="ipclass"
				type="text"
				pattern=".*"
				placeholder="할 일을 추가해주세요"
				value={inputEntered}
				onChange={enterTextlistener}
				onKeyPress={enterTextlistener}
			/>
		</div>
	);
};

export default Taskform;

src/components/Taskform.css

.taskform {
	border-top: 40px;
	font-size: 18px;
	padding: 10px;
	padding-left: 20px;
	margin: 10px;
	width: auto;
	border: 1px solid rgba(0 0 0 / 0.1);
	color: black;
	font-weight: 500;
}

.ipclass {
	width: calc(100% - 22px);
	outline: none !important;
	border: 0px solid red;
	background: transparent;
	border: transparent;
	font-size: 18px;
}

.ctheader {
	color: rgba(0, 0, 0, 0.5);
	font-size: 22px;
	border-radius: 4px 20px;
	padding: 10px;
	padding-left: 20px;
	margin: 10px;
	width: auto;
	display: grid;
	font-weight: bold;
	letter-spacing: 2px;
}

src/components/Task.js

할 일을 보여주고 할 일을 완료 및 할 일로 변경하는 컴포넌트입니다. 추가로 삭제하는 기능도 포합합니다.

import React from "react";
import "./Task.css";

const Task = ({ id, title, className, onClick, onDelete }) => {
	return (
		<div className="tcontainer">
			<div className={className} onClick={onClick(id)}>
				{title}
			</div>
			<button className="delbtn" onClick={onDelete(id)}>
				삭제
			</button>
		</div>
	);
};

export default Task;

src/components/Task.css

.tcontainer {
	width: calc(100% - 20px);
	display: flex;
	justify-content: space-between;
	margin: 25px 10px 25px 10px;
}

.delbtn {
	width: 60px;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #fff;
	background-color: #dc3545;
	border-color: #dc3545;
	text-decoration: none;
	border: 1px solid transparent;
	font-weight: 400;
	cursor: pointer;
	text-align: center;
	white-space: nowrap;
	vertical-align: middle;
	user-select: none;
	font-size: 16px;
	line-height: 1.5;
	border-radius: 0.25rem;
}

.delbtn:hover {
	color: #fff;
	background-color: #c82333;
	border-color: #bd2130;
}

.task {
	border-top: 40px;
	color: black;
	font-size: 18px;
	border-radius: 0.25rem;
	padding: 10px;
	padding-left: 20px;
	width: calc(100% - 100px);
	border: 1px solid rgba(0 0 0 / 0.1);
	font-weight: 500;
	cursor: pointer;
}

.task:hover {
	background: rgba(0 0 0 / 0.1);
}

.comtask {
	border-top: 40px;
	font-size: 18px;
	border-radius: 0.25rem;
	padding: 10px;
	padding-left: 20px;
	width: calc(100% - 100px);
	text-decoration: line-through;
	background-color: rgba(0 0 0 / 0.1);
	border: 1px solid rgba(0 0 0 / 0.1);
	color: rgba(255, 255, 255, 0.9);
	font-weight: 500;
	cursor: pointer;
}

.comtask:hover {
	color: black;
	background-color: rgba(0 0 0 / 0);
	border: 1px solid rgba(0 0 0 / 0.1);
}

해당 프로젝트를 실행하면 아래와 같은 화면이 나옵니다. 또한 별다른 액션은 따로 안나타납니다. 걱정 마세요 앞으로 우리가 만들어 갈 꺼니까요

React프로젝트에서 생성한 Strapi API 사용하기

React 프로젝트에서 아래 커맨드를 입력해 줍니다.

npm install axios

그럼 Axios가 설치됩니다. 그럼 아래 소스와 같이 Axios를 사용하여 Strapi에서 만들었던 todolist Mock data를 Fetching 해보겠습니다.

src/App.js

import React, { useEffect, useState } from "react";
import TaskManager from "./components/TaskManager";
import axios from "axios"; // 추가

function App() {
	const [todoList, setTodoList] = useState([]);

	// 추가
	useEffect(() => { 
		updateTodoList();
	}, []);

	// 추가
	const updateTodoList = async () => {
		const { data } = await fetchTodoList();
		setTodoList(data);
	};

	// 추가
	const fetchTodoList = async () => {
		const { data } = await axios.get(`http://localhost:1337/api/todos`);
		return data;
	};

	const changeTaskToComplete = (id) => async () => {};

	const changeTaskToUnComplete = (id) => async () => {};

	const appendTaskInToDoList = async (title) => {};

	const deleteTaskInToDoList = (id) => async () => {};

	return (
		<div className="App">
			<TaskManager
				onComplete={changeTaskToComplete}
				onUnComplete={changeTaskToUnComplete}
				onAppend={appendTaskInToDoList}
				onDelete={deleteTaskInToDoList}
				todoList={todoList}
			></TaskManager>
		</div>
	);
}

export default App;

요렇게 추가를 하니 아래 화면이 정상적으로 나오는 것을 확인할 수 있습니다.

축하합니다!! 저희는 Strapi에서 생성한 API를 첫 사용했습니다!!

Strapi를 이용해 CRUD 생성하기

다시 Strapi로 돌아가 아래와 같이 Settings - Roles - Public 순으로 이동합니다.

그런 다음 todos 드롭박스를 열고 create, delete, update 체크박스에 체크 한 후 Save 버튼을 클릭합니다.

축하합니다! CRUD API를 전부 생성했습니다. 참고로 내 API URL이 궁금하다면 궁금한 Action에 톱니 모양 버튼을 클릭하면 확인 할 수 있습니다.

React로 만든 Todolist에서 Strapi CRUD 사용하기

자 이제 저희는 CRUD를 전부 사용하여 Todolist를 완성시킬 겁니다. 첫번째로 할 것은 할 일을 완료하고 완료한 것을 할 일로 돌리는 작업입니다. 아래와 같이 코드를 작성해 주세요

src/App.js

import React, { useEffect, useState } from "react";
import TaskManager from "./components/TaskManager";
import axios from "axios";

function App() {
	const [todoList, setTodoList] = useState([]);

	useEffect(() => {
		updateTodoList();
	}, []);

	const updateTodoList = async () => {
		const { data } = await fetchTodoList();
		setTodoList(data);
	};

	const fetchTodoList = async () => {
		const { data } = await axios.get(`http://localhost:1337/api/todos`);
		return data;
	};

	// 추가
	const changeTaskToComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: true,
			},
		});
		updateTodoList();
	};

	// 추가
	const changeTaskToUnComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: false,
			},
		});
		updateTodoList();
	};

	const appendTaskInToDoList = async (title) => {};

	const deleteTaskInToDoList = (id) => async () => {};

	return (
		<div className="App">
			<TaskManager
				onComplete={changeTaskToComplete}
				onUnComplete={changeTaskToUnComplete}
				onAppend={appendTaskInToDoList}
				onDelete={deleteTaskInToDoList}
				todoList={todoList}
			></TaskManager>
		</div>
	);
}

export default App;

코드를 다 입력 했다면 프로젝트에서 할 일 중 하나의 할 일을 클릭해 볼까요?

정상적으로 할 일에서 완료로 전환되는것을 확인할 수 있습니다.

자, 이제 다음은 삭제를 구현해 볼 것입니다. 아래 코드를 입력해 주세요.

src/App.js

import React, { useEffect, useState } from "react";
import TaskManager from "./components/TaskManager";
import axios from "axios";

function App() {
	const [todoList, setTodoList] = useState([]);

	useEffect(() => {
		updateTodoList();
	}, []);

	const updateTodoList = async () => {
		const { data } = await fetchTodoList();
		setTodoList(data);
	};

	const fetchTodoList = async () => {
		const { data } = await axios.get(`http://localhost:1337/api/todos`);
		return data;
	};

	const changeTaskToComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: true,
			},
		});
		updateTodoList();
	};

	const changeTaskToUnComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: false,
			},
		});
		updateTodoList();
	};

	const appendTaskInToDoList = async (title) => {};

	// 추가
	const deleteTaskInToDoList = (id) => async () => {
		await axios.delete(`http://localhost:1337/api/todos/${id}`);
		updateTodoList();
	};

	return (
		<div className="App">
			<TaskManager
				onComplete={changeTaskToComplete}
				onUnComplete={changeTaskToUnComplete}
				onAppend={appendTaskInToDoList}
				onDelete={deleteTaskInToDoList}
				todoList={todoList}
			></TaskManager>
		</div>
	);
}

export default App;

코드를 다 입력 했다면 프로젝트에서 할 일 중 하나의 할 일을 삭제해 볼까요?

와우! 삭제도 정상적으로 동작합니다. 그럼 CRUD에 마지막 Create 생성만이 남았습니다. 생성도 한번 아래 코드처럼 작성해 주세요

src/App.js

import React, { useEffect, useState } from "react";
import TaskManager from "./components/TaskManager";
import axios from "axios";

function App() {
	const [todoList, setTodoList] = useState([]);

	useEffect(() => {
		updateTodoList();
	}, []);

	const updateTodoList = async () => {
		const { data } = await fetchTodoList();
		setTodoList(data);
	};

	const fetchTodoList = async () => {
		const { data } = await axios.get(`http://localhost:1337/api/todos`);
		return data;
	};

	const changeTaskToComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: true,
			},
		});
		updateTodoList();
	};

	const changeTaskToUnComplete = (id) => async () => {
		await axios.put(`http://localhost:1337/api/todos/${id}`, {
			data: {
				complete_yn: false,
			},
		});
		updateTodoList();
	};

	// 추가
	const appendTaskInToDoList = async (title) => {
		await axios.post(`http://localhost:1337/api/todos`, {
			data: {
				title: title,
				complete_yn: false,
			},
		});
		updateTodoList();
	};

	const deleteTaskInToDoList = (id) => async () => {
		await axios.delete(`http://localhost:1337/api/todos/${id}`);
		updateTodoList();
	};

	return (
		<div className="App">
			<TaskManager
				onComplete={changeTaskToComplete}
				onUnComplete={changeTaskToUnComplete}
				onAppend={appendTaskInToDoList}
				onDelete={deleteTaskInToDoList}
				todoList={todoList}
			></TaskManager>
		</div>
	);
}

export default App;

자 코드를 작성했으면 할 일을 추가하는 항목에서 할 일을 추가해 봅시다.

세번째 할 일이 생성된 것을 확인할 수 있습니다. 이것으로 모든 CRUD가 완벽하게 동작합니다.

Strapi 안에서도 데이터가 잘 등록되어 있군요. 자 우리는 기본적인 API를 사용해서 Todolist를 만들었습니다.

이 글을 공유하기

댓글