7분 만에 MERN stack으로 풀스택 TodoApp 웹서비스 만들기
지난번 시간에는 프론트엔드 기본 - Next.js 로 간단한 TodoApp 웹사이트 만들기와 백엔드 기본 - Python FastAPI로 TodoApp API 만들기를 각각 시도 해보았는데요 오늘은 이 두가지를 합친 형태인 Full Stack TodoApp 웹서비스를 만들어 보겠습니다. 참고로 ChatGPT에서는 결과값이 짤리는 경우가 있어 오늘은 OpenAI Playground를 사용하여 진행 해 보았습니다.
먼저 ChatGPT에게 아래와 같이 Full Stack 개발자가 되어 달라고 하고 간단한 Full Stack TodoApp을 만들어달라고 요청 합니다. 원하는 기술 스택과 요구 사항을 구체적으로 작성 하는게 좋지만 오늘은 간단하게 요청만 해보겠습니다.
act as an experienced full stack developer.
I’m trying to build a full-stack simple todo app (db, api server, web page), give me step by step instruction with code example
아래와 같이 답변을 구체적으로 만들어줍니다. 참고로 답변 내용에서는 MERN 스택을 예시로 제안 해주었지만 ChatGPT특성상 다른 결과가 나올수 있는 부분은 참고 부탁 드립니다. 그럼 간단한 Todo App 웹서비스를 MERN 스택(MongoDB, Express, React 및 Node.js)을 사용하여 작성하겠습니다. 이해하기 쉽도록 아래 단계별로 나누어 설명 합니다.
1. 프로젝트 구조 설정:
프로젝트용 새 폴더를 만들고 터미널을 열어 프로젝트 디렉토리로 이동한 다음 다음 명령을 실행합니다.
npx create-react-app todo-app
cd todo-app
mkdir server
이렇게 하면 'todo-app' 폴더에 새로운 React 앱이 생성되고 백엔드용 'server' 폴더가 생성됩니다.
2. 백엔드 설정:
'server' 폴더로 이동한 다음 다음 명령을 실행합니다.
npm init -y
npm install express mongoose cors dotenv
npm install nodemon --save-dev
이렇게 하면 필요한 패키지를 사용하여 기본 Node.js 백엔드가 설정됩니다.
3. 'server' 폴더에 .env 파일을 생성하고 다음 내용을 추가합니다.
PORT=5000
MONGODB_URI=mongodb://localhost:27017/todo-db
원격 데이터베이스를 사용하는 경우 'MONGODB_URI'를 자신의 MongoDB 연결 문자열로 바꿉니다.
4. 'server' 폴더에 'server.js' 파일을 생성하고 다음 코드를 추가합니다.
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
app.use(express.json());
app.use(cors());
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
});
const db = mongoose.connection;
db.on('error', (error) => console.error(error));
db.once('open', () => console.log('Connected to the database'));
const todoSchema = new mongoose.Schema({
task: String,
completed: Boolean,
});
const Todo = mongoose.model('Todo', todoSchema);
app.get('/todos', async (req, res) => {
try {
const todos = await Todo.find();
res.json(todos);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.post('/todos', async (req, res) => {
const todo = new Todo({
task: req.body.task,
completed: req.body.completed,
});
try {
const newTodo = await todo.save();
res.status(201).json(newTodo);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
app.delete('/todos/:id', async (req, res) => {
try {
await Todo.findByIdAndDelete(req.params.id);
res.json({ message: 'Todo deleted successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.patch('/todos/:id', async (req, res) => {
try {
const todo = await Todo.findById(req.params.id);
todo.completed = !todo.completed;
const updatedTodo = await todo.save();
res.json(updatedTodo);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.listen(process.env.PORT, () =>
console.log(`Server running on port ${process.env.PORT}`)
);
이 코드는 할 일 항목을 가져오고, 생성하고, 삭제하고, 업데이트하는 네 개의 엔드포인트를 사용하여 기본 API를 설정합니다.
5. 프론트엔드 설정:
'todo-app' 폴더로 이동한 다음 다음 명령을 실행합니다.
npm install axios
이렇게 하면 axios가 설치되며, 프론트엔드에서 API 호출을 수행하는 데 사용합니다.
6. 'todo-app' 폴더의 'src/App.js' 내용을 다음 코드로 바꿉니다.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
const response = await axios.get('http://localhost:5000/todos');
setTodos(response.data);
};
const addTodo = async () => {
if (inputValue.trim()) {
await axios.post('http://localhost:5000/todos', {
task: inputValue,
completed: false,
});
setInputValue('');
fetchTodos();
}
};
const deleteTodo = async (id) => {
await axios.delete(`http://localhost:5000/todos/${id}`);
fetchTodos();
};
const toggleComplete = async (id) => {
await axios.patch(`http://localhost:5000/todos/${id}`);
fetchTodos();
};
return (
<div>
<h1>할 일 목록</h1>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={addTodo}>할 일 추가</button>
<ul>
{todos.map((todo) => (
<li key={todo._id}>
<span
onClick={() => toggleComplete(todo._id)}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.task}
</span>
<button onClick={() => deleteTodo(todo._id)}>삭제</button>
</li>
))}
</ul>
</div>
);
}
export default App;
이 코드는 할 일 항목을 가져오고, 생성하고, 삭제하고, 업데이트하는 간단한 React 앱을 설정합니다.
7. 애플리케이션 실행:
두 개의 별도 터미널을 연 후 첫 번째 터미널에서 'server' 폴더로 이동하여 다음 명령을 실행합니다.
npm run start
참고로 서버 코드의 경우 뒤에서 설명 하겠지만 실행시 오류가 발생하며 가이드 대로 해결도 할수 있습니다. 그리고 정상 동작을 위해서는 MongoDB가 설치되어 있어야 합니다.
두 번째 터미널에서 'todo-app' 폴더로 이동한 다음 다음 명령을 실행합니다.
npm start
이제 할 일 앱이 http://localhost:3000
에서 실행됩니다.
이것으로 기본적인 할 일 앱을 MERN 스택으로 구축 완료했습니다. 이번에는 기본 요구 사항을 따로 지정을 안했더니 기본적인 TodoApp 이 만들어졌는데요. 필요에 따라 간단한 디자인과 기능을 개선할 수 도 있습니다.
8. 후속작업
8.1 폴더 구조 확인
다 완성이 되었으면 지금까지 작성한 코드의 구조도 확인 해봅니다.
folder 구조를 보여줘
그럼 폴더 구조를 보여주고 아래와 같이 폴더에 대한 설명 까지 잘 해줍니다.
프로젝트의 폴더 구조는 다음과 같습니다:
todo-app/
├── server/
│ ├── .env
│ ├── package.json
│ ├── package-lock.json
│ └── server.js
├── public/
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src/
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── .gitignore
├── package.json
├── package-lock.json
└── README.md
프로젝트의 주요 부분은 다음과 같습니다:
server/
폴더: 백엔드 코드가 포함되어 있습니다. 이 폴더에는 .env 파일, package.json 파일 및 server.js 파일이 포함되어 있습니다.public/
폴더: 정적 파일 및 index.html 파일이 포함되어 있습니다.src/
폴더: React 프론트엔드 코드가 포함되어 있습니다. 주요 파일은 App.js입니다.package.json
파일: 프로젝트에 대한 메타데이터와 의존성이 포함되어 있습니다. 서버 및 클라이언트에 각각 별도의 package.json 파일이 있습니다.
8.2 MongoDB 설치
프롬프트에 DB라고 넣었지만 DB 설치 까지 포함 시켜주지는 못했습니다. 그래서 간단하게 Docker로 localhost에 MongoDB를 설치 하는 방법을 알려달라고 요청 해보았습니다.
how to install mongodb on localhost by using docker
그럼 아래와 같이 Docker를 사용하여 MongoDB를 설치하는 방법을 알려줍니다.
Docker를 사용하여 MongoDB를 설치하려면 다음 단계를 따르십시오.
Docker 설치: Docker를 아직 설치하지 않은 경우 공식 웹 사이트에서 다운로드하여 설치합니다. https://www.docker.com/products/docker-desktop 터미널/명령 프롬프트를 열고 다음 명령을 실행하여 Docker가 제대로 설치되었는지 확인합니다.
공식 MongoDB Docker 이미지를 가져옵니다.
docker pull mongo
호스트 시스템에 MongoDB 데이터를 저장할 새 디렉토리를 생성합니다(/path/to/your/mongodb/data를 원하는 경로로 대체).
mkdir -p /path/to/your/mongodb/data
MongoDB 컨테이너를 실행하여 호스트의 데이터 디렉터리를 컨테이너의 데이터 디렉터리에 매핑하고 사용자 지정 포트를 설정합니다(필요한 경우 27017을 원하는 포트 번호로 대체).
docker run -d -p 27017:27017 -v /path/to/your/mongodb/data:/data/db --name mongodb mongo
MongoDB 컨테이너가 실행 중인지 확인합니다.
docker ps
실행 중인 컨테이너 목록에 mongodb 컨테이너가 표시되어야 합니다.
이제 Docker를 사용하여 MongoDB를 설치하고 시작했습니다. 호스트의 IP 주소와 지정된 포트 번호를 사용하여 MongoDB 인스턴스에 연결할 수 있습니다.
8.3 서버 실행 오류 확인
직접 실행을 해보셨으면 아시겠지만 ChatGPT가 만들어준 코드에서 오류가 발생 합니다. 하지만 오류 까지 ChatGPT의 도움을 받아 해결 할 수 있습니다.
저는 아래와 같이 오류가 발생 했고 오류 내용을 복사 하여 ChatGPT에게 확인을 요청 해봤습니다.
when i tried to run express server, there is a following error: MongoParseError: option usefindandmodify is not supported
아래와 같이 ChatGPT의 답변을 받고 오류를 해결 할 수 있었습니다.
useFindAndModify
옵션은 최신 버전의 MongoDB에서 더 이상 지원되지 않는 것 같습니다. 이에 대한 해결책은 해당 옵션을 삭제하거나 더 이상 사용되지 않는 메서드 대신 새로운 메서드를 사용하는 것입니다.
useFindAndModify
옵션을 삭제하여 이 문제를 해결할 수 있습니다. server.js
파일에서 다음과 같이 mongoose.connect
메소드를 업데이트하세요.
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
또는, 해당 기능을 사용하는 코드에서 findByIdAndUpdate
및 findByIdAndRemove
메서드 대신 findOneAndUpdate
및 findOneAndRemove
메서드를 사용하세요.
예를 들어, 할 일을 삭제하는 엔드포인트를 업데이트하려면 다음과 같이 변경할 수 있습니다:
app.delete('/todos/:id', async (req, res) => {
try {
await Todo.findOneAndRemove({ _id: req.params.id });
res.json({ message: 'Todo deleted successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
이렇게 하면 MongoParseError: option usefindandmodify is not supported
오류를 해결할 수 있습니다.
9. 마무리
이번에는 MERN 스택을 사용하여 간단한 할 일 앱을 만들어 보았습니다. 물론 중간에 오류가 발생 했지만 ChatGPT의 도움을 받아 해결 할 수 있었습니다. DB 구성하는 부분은 누락이 되어 아쉬운 부분이 있었지만 다음 시간에 다뤄볼 예정인 요구 사항을 잘 전달하는 방법을 통해 누락 되는 부분은 충분히 해결이 가능할거라고 생각이 됩니다.
참고로 완성된 소스 코드는 GitHub 링크에서 확인 하실수 있습니다.