개요
이 가이드 에서는 MERN 스택 사용하는 React 웹 애플리케이션 만드는 방법을 학습 수 있습니다. MERN 스택 MongoDB, Express, React 및 Node.js를 사용하는 웹 개발 프레임워크 이며 다음과 같은 계층으로 구성되어 있습니다.
데이터베이스 계층: MongoDB 데이터 저장 및 검색을 제공합니다.
애플리케이션 계층: Express 와 노드.js는 서버 측 로직의 중간 계층 구성합니다.
프레젠테이션 계층: React 사용자 인터페이스와 클라이언트 사이드 상호 작용을 구현합니다.
React 애플리케이션에서 MongoDB 사용하는 이유는 무엇인가요?
React 애플리케이션 데이터를 MongoDB에 저장하면 문서 데이터 모델을 사용하여 복잡한 쿼리 표현식을 빌드할 수 있습니다. document model의 유연성을 통해 중청된 데이터 구조를 저장하고 애플리케이션 디자인을 빠르게 이터레이션할 수 있습니다. MongoDB의 수평 확장 기능을 사용하여 애플리케이션을 확장할 수도 있습니다.
MongoDB 사용한 MERN 스택 동적이고 진화하는 데이터 구조가 필요한 애플리케이션을 지원합니다. 따라서 이 프레임워크 실시간 대시보드나 콘텐츠를 지속적으로 업데이트 한 페이지짜리 애플리케이션과 같은 실제 애플리케이션에 적합하게 설계되었습니다.
빠른 시작 튜토리얼
이 튜토리얼에서는 MERN 스택 사용하여 웹 애플리케이션 빌드 방법을 보여줍니다. 애플리케이션 샘플 레스토랑 데이터에 액세스하고, 데이터를 쿼리하고, 로컬에서 호스팅되는 사이트 에 결과를 표시합니다. 이 튜토리얼에는 MongoDB Atlas 에서 호스팅되는 MongoDB 클러스터 에 연결하고 데이터베이스 의 데이터에 액세스하고 표시하는 방법에 대한 지침도 포함되어 있습니다.
팁
React 없이 Node.js 드라이버 사용하여 MongoDB에 연결하려는 경우 Node.js 드라이버 시작하기 가이드 를 참조하세요.
프로젝트 설정
이 섹션의 단계에 따라 프로젝트 종속성을 설치하고, Atlas 클러스터를 만들고, 애플리케이션 디렉토리를 설정합니다.
전제 조건 확인
Quick Start 애플리케이션을 생성하려면 개발 환경에 다음 소프트웨어가 설치되어 있어야 합니다.
전제 조건 | 참고 사항 |
|---|---|
최신 LTS 또는 최신 릴리스 버전을 다운로드합니다. | |
코드 편집기 | 이 튜토리얼에서는 Visual Studio Code를 사용하지만 원하는 편집기를 사용할 수 있습니다. |
터미널 앱 및 shell | MacOS 사용자의 경우 터미널 또는 유사한 앱을 사용하세요. Windows 사용자의 경우 PowerShell을 사용하세요. |
MongoDB Atlas 클러스터 생성
MongoDB Atlas 는 MongoDB 배포서버를 호스팅하는 완전 관리형 클라우드 데이터베이스 서비스입니다. MongoDB 배포서버 없는 경우, MongoDB 시작하기 튜토리얼을 완료하여 무료로 MongoDB 클러스터 생성할 수 있습니다( 크레딧 카드 필요 없음). MongoDB 시작하기 튜토리얼에서는 이 튜토리얼에서 사용되는 sample_restaurants 데이터베이스 포함하여 샘플 데이터 세트를 클러스터 에 로드하는 방법도 보여줍니다.
MongoDB 클러스터에 연결하려면 연결 URI를 사용해야 합니다. 연결 URI를 조회 방법을 학습하려면 MongoDB 시작하기 튜토리얼의 연결 문자열 추가하기 섹션을 참조하세요.
중요
연결 string 을 안전한 위치 에 저장합니다.
package.json 파일 수정
react-quickstart/server 디렉토리 의 package.json 파일 로 이동합니다. 재사용을 위해 JavaScript 코드를 패키징하는 표준 형식인 ECMAScript 모듈을 사용하려면 "type" 필드 지정하는 기존 줄을 다음 줄로 바꾸세요.
"type": "module",
다음 명령을 실행하여 mongodb, express 및 cors 종속성을 설치합니다.
npm install mongodb express cors
이 명령은 MongoDB, Express 웹 프레임워크 및 교차 출처 리소스 공유 가능하게 하는 cors Node.js 패키지 설치합니다.
서버 구성
프로젝트 구조 및 종속성을 설정한 후 이 섹션의 단계에 따라 웹 서버 구성하고 MongoDB 에 연결합니다.
Express 웹 서버 구성
react-quickstart/server 디렉토리에서 다음 명령을 실행하여 server.js 이름의 파일을 만듭니다.
touch server.js
New-Item -ItemType File server.js
다음 코드를 파일에 붙여넣습니다.
import express from "express"; import cors from "cors"; import restaurants from "./routes/restaurant.js"; const PORT = process.env.PORT || 5050; const app = express(); app.use(cors()); app.use(express.json()); app.use("/restaurant", restaurants); // start the Express server app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });
이 파일은 백엔드의 진입점입니다. Express 서버를 시작하고, React 클라이언트가 서버를 호출할 수 있도록 CORS(출처 간 리소스 공유)를 활성화하고, /restaurant 경로에 routes/restaurant.js 에 정의된 경로를 마운트합니다. 나중 단계에서 routes/restaurant.js 파일을 생성합니다.
환경 변수 설정
server 디렉토리에서 다음 명령을 실행하여 config.env 이름의 파일을 만듭니다.
touch config.env
New-Item -ItemType File config.env
파일에 다음 변수를 추가합니다.
MONGODB_URI=<connection URI> PORT=5050
<connection URI> 자리 표시자를 이전 단계에서 저장한 연결 URI로 바꿉니다.
서버 를 데이터베이스 에 연결
server 디렉토리에서 다음 명령을 실행하여 connection.js 파일이 포함된 db 이름의 새 폴더를 만듭니다.
mkdir db touch db/connection.js
mkdir db New-Item -ItemType File db/connection.js
다음 코드를 connection.js 파일 에 붙여넣습니다.
import { MongoClient } from "mongodb"; const uri = process.env.MONGODB_URI || ""; const client = new MongoClient(uri); try { // Connect the client to the server await client.connect(); // Send a ping to confirm a successful connection await client.db("admin").command({ ping: 1 }); console.log( "Pinged your deployment. You successfully connected to MongoDB!" ); } catch(err) { console.error(err); } let db = client.db("sample_restaurants"); export default db;
이 파일은 MongoDB 배포서버에 연결하고, sample_restaurants 데이터베이스에 액세스하고, 데이터베이스 연결을 테스트합니다. 데이터베이스 연결을 db로 내보냅니다. 다음 단계에서 routes/restaurant.js 파일은 ../db/connection.js 에서 이 db 연결을 가져와 데이터베이스를 쿼리합니다.
서버 API 엔드포인트 만들기
server 디렉토리에서 다음 명령을 실행하여 restaurant.js 파일이 포함된 routes 이름의 새 폴더를 만듭니다.
mkdir routes touch routes/restaurant.js
mkdir routes New-Item -ItemType File routes/restaurant.js
다음 코드를 restaurant.js 파일 에 붙여넣습니다.
import express from "express"; import db from "../db/connection.js"; // Creates an instance of the Express router, used to define our routes const router = express.Router(); // Gets a list of all the restaurants router.get("/", async (req, res) => { let collection = await db.collection("restaurants"); let results = await collection.find({}).toArray(); res.send(results).status(200); }); // Lists restaurants that match the query filter router.get("/browse", async (req, res) => { try { let collection = await db.collection("restaurants"); let query = { borough: "Queens", name: { $regex: "Moon", $options: "i" }, }; let results = await collection.find(query).toArray(); res.send(results).status(200); } catch (err) { console.error(err); res.status(500).send("Error browsing restaurants"); } }); export default router;
이 파일은 db/connection.js에서 db 연결을 가져와 sample_restaurants 데이터베이스의 restaurants 컬렉션에 액세스하고 다음 GET 엔드포인트를 정의합니다.
/: 샘플 컬렉션 에서 모든 레스토랑을 조회합니다./browse: 쿼리 기준과 일치하는 레스토랑을 조회하며, 이름에"Moon"라는 단어가 포함된 퀸즈의 레스토랑을 필터링합니다.
클라이언트 구성
애플리케이션의 서버 설정한 후 이 섹션의 단계에 따라 React 구성하고 클라이언트 요소를 추가합니다.
React 템플릿 추가
react-quickstart 디렉토리로 이동합니다. 다음으로 Vite:를 사용하여 React 템플릿 파일을 추가하는 다음 명령을 실행합니다.
npm create vite@latest client
이 명령은 일련의 구성 질문에 응답하라는 메시지를 표시합니다. 각 질문에 대해 드롭다운 메뉴에서 다음 응답을 선택합니다.
프레임워크 선택 : React
변형 선택: JavaScript
Rolldown-vite(실험 단계)를 사용하시겠습니까?: 아니요
npm 으로 설치하고 지금 시작하시겠습니까?: 아니요
명령을 실행하면 프로젝트에 클라이언트 사이드 스캐폴딩이 포함된 client 디렉토리가 생기게 됩니다.
Tailwind CSS 설치
이 샘플 애플리케이션은 UI 포매팅에 Tailwind CSS 프레임워크를 사용합니다. client 디렉토리로 이동한 다음 다음 명령을 실행하여 Tailwind CSS를 설치합니다.
npm install tailwindcss @tailwindcss/vite
설치 후 vite.config.js 파일 로 이동합니다. 강조 표시된 줄에 표시된 대로 가져오기 문과 plugins 배열 업데이트하여 @tailwindcss/vite 플러그인을 추가합니다.
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [ react(), tailwindcss() ], })
그러린 다음 client/src/index.css 파일의 내용을 다음 가져오기 성명으로 대체합니다.
@import "tailwindcss";
React 라우터 설정
client/src/main.jsx 파일 로 이동하여 다음 코드를 붙여넣습니다.
import * as React from "react"; import * as ReactDOM from "react-dom/client"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import App from "./App"; import RestaurantList from "./components/RestaurantList"; import "./index.css"; const router = createBrowserRouter([ { path: "/", element: <App />, children: [ { path: "/", element: <RestaurantList />, }, ], }, { path: "/browse", element: <App />, children: [ { path: "/browse", element: <RestaurantList />, }, ], }, ]); ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <RouterProvider router={router} /> </React.StrictMode>, );
이 파일은 클라이언트의 진입점입니다. App 레이아웃 컴포넌트와 RestaurantList 컴포넌트를 가져와 클라이언트 사이드 라우팅을 구성하고 다음 경로를 정의합니다.
/:/restaurant/API 엔드포인트를 호출하여 모든 레스토랑을 표시하는RestaurantList구성 요소를 렌더링합니다./browse:/restaurant/browseAPI 엔드포인트를 호출하여 필터링된 레스토랑을 표시하는RestaurantList구성 요소를 렌더링합니다.
구성 요소 만들기
client 디렉토리 에서 다음 명령을 실행하여 두 개의 파일이 포함된 components 라는 새 폴더를 만듭니다.
mkdir src/components cd src/components touch Navbar.jsx RestaurantList.jsx
mkdir src/components cd src/components New-Item -ItemType File Navbar.jsx New-Item -ItemType File RestaurantList.jsx
Navbar.jsx 파일 필수 구성 요소에 연결되는 탐색 모음을 구성합니다. 이 파일 에 다음 코드를 붙여넣습니다.
import { NavLink } from "react-router-dom"; export default function Navbar() { return ( <div> <nav className="flex justify-between items-center mb-6"> <NavLink to="/"> <img alt="MongoDB logo" className="h-10 inline" src="https://d3cy9zhslanhfa.cloudfront.net/media/3800C044-6298-4575-A05D5C6B7623EE37/4B45D0EC-3482-4759-82DA37D8EA07D229/webimage-8A27671A-8A53-45DC-89D7BF8537F15A0D.png" ></img> </NavLink> </nav> </div> ); }
RestaurantList.jsx 파일 레스토랑의 보기 구성 요소로, 레스토랑 정보를 검색하고 표시합니다. server/routes/restaurant.js에 정의된 /restaurant API 엔드포인트로 요청을 보냅니다. 이 파일 에 다음 코드를 붙여넣습니다.
import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; const Restaurant = (props) => ( <tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted"> <td className="p-4 align-middle [&:has([role=checkbox])]:pr-0"> {props.restaurant.name} </td> <td className="p-4 align-middle [&:has([role=checkbox])]:pr-0"> {props.restaurant.borough} </td> <td className="p-4 align-middle [&:has([role=checkbox])]:pr-0"> {props.restaurant.cuisine} </td> </tr> ); export default function RestaurantList() { const [restaurants, setRestaurants] = useState([]); const location = useLocation(); // Fetches the restaurants from the database useEffect(() => { async function getRestaurants() { // Determines which endpoint to call based on current route const endpoint = location.pathname === "/browse" ? "http://localhost:5050/restaurant/browse" : "http://localhost:5050/restaurant/"; const response = await fetch(endpoint); if (!response.ok) { const message = `An error occurred: ${response.statusText}`; console.error(message); return; } const restaurants = await response.json(); setRestaurants(restaurants); } getRestaurants(); return; }, [location.pathname]); // Maps each restaurant on the table function restaurantList() { return restaurants.map((restaurant) => { return <Restaurant restaurant={restaurant} key={restaurant._id} />; }); } // Retrieves the dynamic title based on current route const getTitle = () => { return location.pathname === "/browse" ? 'Filtered Restaurants (Queens, containing "Moon")' : "All Restaurants"; }; // Displays the restaurants table return ( <> <h3 className="text-lg font-semibold p-4">{getTitle()}</h3> <div className="border rounded-lg overflow-hidden"> <div className="relative w-full overflow-auto"> <table className="w-full caption-bottom text-sm"> <thead className="[&_tr]:border-b"> <tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted"> <th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0"> Name </th> <th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0"> Borough </th> <th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0"> Cuisine </th> </tr> </thead> <tbody className="[&_tr:last-child]:border-0"> {restaurantList()} </tbody> </table> </div> </div> </> ); }
마지막으로 client/src/App.jsx 파일 로 이동합니다. 이 파일 기본 레이아웃 구성 요소이며 Navbar 구성 요소가 각 페이지 상단에서 하위 구성 요소 위에 렌더링되도록 합니다. 이 파일 에 다음 코드를 붙여넣습니다.
import { Outlet } from "react-router-dom"; import Navbar from "./components/Navbar"; const App = () => { return ( <div className="w-full p-6"> <Navbar /> <Outlet /> </div> ); }; export default App;
애플리케이션 실행하기
마지막으로 이 섹션의 단계에 따라 애플리케이션 실행 하고 렌더링된 레스토랑 데이터를 확인합니다.
애플리케이션 사이트 열기
이전 단계에서 조회한 http://localhost:5173/ URL 엽니다. 초기 방문 페이지에는 sample_restaurants.restaurants 컬렉션 의 모든 레스토랑 목록이 표시됩니다.

그런 다음 http://localhost:5173/browse URL 로 이동하여 name 및 borough 필드 쿼리 와 일치하는 레스토랑을 확인합니다.

빠른 시작 튜토리얼을 완료하신 것을 축하드립니다!
이 단계를 완료하면 MongoDB deployment 에 연결하고, 샘플 레스토랑 데이터에 대해 쿼리 실행하고, 검색된 결과를 렌더링하는 React 웹 애플리케이션 갖게 됩니다.
문제 해결
이 튜토리얼을 완료하는 동안 문제가 발생하면 다음 일반적인 문제와 해결책을 검토하세요.
연결 오류
서버 터미널에 Pinged your deployment. 메시지 대신 연결 오류가 인쇄되면 다음을 확인하세요.
config.env파일의MONGODB_URI값은 Atlas의 연결 문자열과 일치하며 데이터베이스 사용자 이름과 비밀번호가 포함되어 있습니다.sample_restaurants데이터 세트를 클러스터에 로드했습니다.
CORS Errors
클라이언트가 데이터를 요청할 때 브라우저 콘솔에 CORS 오류가 표시되면 경로 정의 전에 server.js 파일이 app.use(cors()) 을 호출하는지 확인합니다.
데이터 표시 안 됨
웹 페이지가 로드되지만 레스토랑이 표시되지 않은 경우 별도 터미널 Windows에서 서버와 클라이언트가 동시에 실행되고 있는지 확인합니다. React 클라이언트는 http://localhost:5050에서 서버에서 데이터를 가져오므로 데이터를 표시하려면 서버가 실행 중이어야 합니다.
포트 충돌
포트 5050 이 이미 사용 중이어서 서버가 시작되지 않는 경우 config.env 파일에서 PORT 값을 변경합니다. 서버 포트를 변경하는 경우 RestaurantList.jsx 의 엔드포인트 URL을 일치하도록 업데이트합니다. 클라이언트 포트 5173 가 사용 중인 경우 Vite는 다음 사용 가능한 포트를 자동으로 선택하고 터미널에 URL을 인쇄합니다.
추가 리소스
React, MongoDB 및 MERN 스택 에 대해 자세히 학습 다음 리소스를 참조하세요.
React 문서
Express 문서
Node.js 운전자 문서