소스: https://github.com/braverokmc79/nextjs-pages-router-sample
https://nextjs-pages-router-meetup.netlify.app/
477. 몽고 DB 로 작업하기
1 MongoDB Atlas 계정 생성 및 클러스터 설정
- MongoDB Atlas 홈페이지 방문 및 무료 체험 시작: MongoDB Atlas 웹사이트에서 무료 체험 버튼을 클릭하여 계정을 만듭니다.
- 클러스터 생성:
- 기본 설정을 변경하지 않고 진행.
- Free Tier (M0 Sandbox) 선택.
- AWS 및 기본 영역 선택.
- Network Access 설정:
- IP 주소 추가 버튼 클릭.
- 현재 IP 주소 추가.
- Database Access 설정:
- 읽기 및 쓰기 권한을 가진 사용자를 생성
2. Next.js 프로젝트에서 MongoDB와의 연결 설정
- MongoDB 드라이버 설치:
npm install mongodb
3.MongoDB 클러스터와 연결 설정:
- MongoClient를 사용하여 MongoDB 클러스터에 연결.
- 연결 문자열에는 MongoDB Atlas에서 제공한 URI를 사용.
- 연결 시 사용자 이름과 비밀번호 입력.
const { MongoClient } = require('mongodb'); async function connectToDatabase() { const client = new MongoClient('your_connection_string_here'); try { await client.connect(); console.log('Connected to database'); const db = client.db('meetups'); const meetupsCollection = db.collection('meetups'); return { client, meetupsCollection }; } catch (error) { console.error('Connection failed', error); throw error; } }
3. 데이터베이스에 데이터 삽입
- 데이터 삽입 함수 작성:
- 데이터베이스에 문서를 삽입하기 위해 insertOne 메서드 사용.
async function insertMeetup(data) { const { client, meetupsCollection } = await connectToDatabase(); try { const result = await meetupsCollection.insertOne(data); console.log('Meetup inserted:', result.insertedId); } finally { await client.close(); } }
5. API 경로 설정 및 데이터 삽입 API 구현
- API 경로 파일 생성:
- Node.js Express 서버 또는 Next.js API 경로를 사용하여 데이터 삽입 API 작성.
import { MongoClient } from 'mongodb'; async function handler(req, res) { if (req.method === 'POST') { const data = req.body; const client = new MongoClient('your_connection_string_here'); await client.connect(); const db = client.db('meetups'); const meetupsCollection = db.collection('meetups'); const result = await meetupsCollection.insertOne(data); await client.close(); res.status(201).json({ message: 'Meetup inserted', result }); } } export default handler;
6. 클라이언트에서 API 호출
- API 호출 코드 작성:
- 클라이언트 측에서 데이터를 제출하여 API 경로로 요청을 보냄.
async function submitMeetup(meetupData) { const response = await fetch('/api/new-meetup', { method: 'POST', body: JSON.stringify(meetupData), headers: { 'Content-Type': 'application/json', }, }); const data = await response.json(); console.log(data); }
7.요약
MongoDB Atlas 클러스터를 생성하고 next.js 애플리케이션에서 MongoDB 드라이버를 사용하여 클러스터에 연결한 후,
API 경로를 통해 데이터를 삽입할 수 있습니다.
클라이언트 측에서는 API 경로로 요청을 보내어 데이터를 삽입합니다.
이 과정은 MongoDB Atlas 설정, 네트워크 접근 및 데이터베이스 접근 설정,
next.js 프로젝트에서 MongoDB 드라이버 설치 및 사용, 데이터 삽입 함수 작성,
그리고 API 경로 설정 및 클라이언트에서 API 호출로 구성됩니다.
478. API 경로로 Http 요청 보내기
1.Meetup 프로젝트
1)components/meetups/NewMeetupForm.js
import { useRef } from 'react'; import Card from '../ui/Card'; import classes from './NewMeetupForm.module.css'; function NewMeetupForm(props) { const titleInputRef = useRef(); const imageInputRef = useRef(); const addressInputRef = useRef(); const descriptionInputRef = useRef(); function submitHandler(event) { event.preventDefault(); const enteredTitle = titleInputRef.current.value; const enteredImage = imageInputRef.current.value; const enteredAddress = addressInputRef.current.value; const enteredDescription = descriptionInputRef.current.value; const meetupData = { title: enteredTitle, image: enteredImage, address: enteredAddress, description: enteredDescription, }; props.onAddMeetup(meetupData); } return ( <Card> <form className={classes.form} onSubmit={submitHandler}> <div className={classes.control}> <label htmlFor='title'>Meetup 제목</label> <input type='text' required id='title' ref={titleInputRef} /> </div> <div className={classes.control}> <label htmlFor='image'>Meetup 이미지</label> <input type='url' required id='image' ref={imageInputRef} /> </div> <div className={classes.control}> <label htmlFor='address'>주소</label> <input type='text' required id='address' ref={addressInputRef} /> </div> <div className={classes.control}> <label htmlFor='description'>설명</label> <textarea id='description' required rows='5' ref={descriptionInputRef} ></textarea> </div> <div className={classes.actions}> <button>Meetup 추가</button> </div> </form> </Card> ); } export default NewMeetupForm;
2)pages/new-meetup/index.js
import NewMeetupForm from "@/components/meetups/NewMeetupForm"; import { useRouter } from "next/router"; function NewMeetupPage() { const router=useRouter(); async function addMeetupHandler(enteredMeetupData) { const response=await fetch('/api/new-meetup',{ method: 'POST', body: JSON.stringify(enteredMeetupData), headers: { 'Content-Type': 'application/json' } }); const data=await response.json(); console.log(data); router.push("/"); } return ( <> <NewMeetupForm onAddMeetup={addMeetupHandler} /> </> ); } export default NewMeetupPage;
3)api/new-meetup.js
import { MongoClient } from "mongodb"; // /api/new-meetup // POST /api/new-meetup async function handler(req, res) { if (req.method === "POST") { const data = req.body; const clinet = await MongoClient.connect( "mongodb+srv://아이디:비번@mongo-macaronics.y37mjuf.mongodb.net/meetups?retryWrites=true&w=majority&appName=mongo-macaronics", { useNewUrlParser: true } ); const db = clinet.db(); const meetupsCollection = db.collection("meetups"); const result=await meetupsCollection.insertOne(data); console.log(result); clinet.close(); res.status(201).json({message:'Meetup is created successfully'}); } } export default handler;
479. 데이터베이스에서 데이터 가져오기
pages/api/db/ConnMongoDB.js
// pages/api/db/ConnMongoDB.js import { MongoClient } from "mongodb"; export async function ConnMongoDB(){ const clinet = await MongoClient.connect( "mongodb+srv://아이디:비번@mongo-macaronics.y37mjuf.mongodb.net/meetups?retryWrites=true&w=majority&appName=mongo-macaronics", { useNewUrlParser: true } ); const db = clinet.db(); const meetupsCollection = db.collection("meetups"); return {meetupsCollection, clinet }; }
pages/index.js
import MeetupList from "@/components/meetups/MeetupList"; import { ConnMongoDB } from "./api/db/ConnMongoDB"; const DUMMY_MEETUPS = [ { id: "m1", title: "A first meetup", image: "https://cdn.pixabay.com/photo/2024/05/11/06/47/tropical-8754092_1280.jpg", address: "Some address 1", description: "This is a first meetup", }, { id: "m2", title: "A second meetup", image: "https://cdn.pixabay.com/photo/2024/04/08/19/54/coffee-8684315_1280.jpg", address: "Some address 2", description: "This is a second meetup", }, ]; function HomePage(props) { return <MeetupList meetups={props.meetups} />; } export async function getStaticProps() { const { meetupsCollection, clinet } = await ConnMongoDB(); const meetups = await meetupsCollection.find().toArray(); clinet.close(); return { props: { meetups: meetups.map(meetup=>({ title:meetup.title, address:meetup.address, //description:meetup.description, image:meetup.image, id:meetup._id.toString() })), }, revalidate: 1, }; } export default HomePage;
- ConnMongoDB 함수로 MongoDB에 연결합니다.
- meetupsCollection.find().toArray()를 사용하여 모든 meetup 데이터를 배열로 가져옵니다.
- MongoDB에서 가져온 meetups 데이터를 필요한 속성만 추출하여 props로 설정합니다.
- revalidate는 1초마다 페이지를 새로 고침하여 최신 데이터를 유지합니다.
전체 동작 방식
- Next.js는 getStaticProps 함수를 실행하여 빌드 시 또는 페이지 요청 시 MongoDB에서 데이터를 가져옵니다.
- 가져온 데이터를 HomePage 컴포넌트의 props로 전달합니다.
- HomePage 컴포넌트는 MeetupList 컴포넌트를 사용하여 meetups 데이터를 렌더링합니다.
- revalidate 옵션으로 1초마다 데이터를 새로 고침하여 최신 상태를 유지합니다.
480. Meetup 세 정보 데이터 가져오기 및 페이지 준비하기
pages/[meetupId]/index.js
import MeetupDetail from "@/components/meetups/MeetupDetail"; import { useRouter } from "next/router"; import { ConnMongoDB } from "../api/db/ConnMongoDB"; import { ObjectId } from "mongodb"; function MeetUpDeailPage(props) { const router = useRouter(); if (router.isFallback) { return <p>Loading...</p>; } const { id, image, title, address, description}= props.meetupData; return ( <MeetupDetail id={id} title={title} image={image} address={address} description={description} /> ); } export async function getStaticPaths() { const {meetupsCollection, client } =await ConnMongoDB(); const meetups=await meetupsCollection.find({}, { _id: 1}).toArray(); client.close(); return { fallback: false, paths: meetups.map(meetup => ({params :{meetupId : meetup._id.toString() }})) } // return { // fallback: false, // paths: [ // { // params: { // meetupId: "m1", // }, // }, // { // params: { // meetupId: "m2", // }, // }, // ], //}; } export async function getStaticProps(context) { const meetupId = context.params.meetupId; const {meetupsCollection, client } =await ConnMongoDB(); const selectedMeetup = await meetupsCollection.findOne({ _id: new ObjectId(meetupId) }); client.close(); return { props:{ meetupData: { id:selectedMeetup._id.toString(), title:selectedMeetup.title, image:selectedMeetup.image, address:selectedMeetup.address, description:selectedMeetup.description, }, revalidate: 1, } } // return { // props: { // meetupData: { // title: "A second meetup", // image: // "https://cdn.pixabay.com/photo/2024/04/08/19/54/coffee-8684315_1280.jpg", // address: "Some address 2", // description: "This is a second meetup", // id: meetupId, // }, // revalidate: 1, // }, // }; } export default MeetUpDeailPage;
구성 요소 및 기능 설명
1. MeetupDetail 컴포넌트
- Meetup의 세부 정보를 표시하는 컴포넌트입니다. @/components/meetups/MeetupDetail 경로에서 가져옵니다.
import MeetupDetail from "@/components/meetups/MeetupDetail";
2. useRouter 훅
- Next.js의 useRouter 훅을 사용하여 라우팅과 관련된 기능을 제공합니다.
- router.isFallback을 사용하여 빌드 시 경로가 아직 생성되지 않은 경우 로딩 상태를 처리합니다.
import { useRouter } from "next/router";
3. ConnMongoDB 함수
- MongoDB에 연결하는 함수입니다. ../api/db/ConnMongoDB 경로에서 가져옵니다.
import { ConnMongoDB } from "../api/db/ConnMongoDB";
4. ObjectId 클래스
- MongoDB에서 문서의 _id 필드를 다루기 위해 mongodb 패키지에서 ObjectId를 가져옵니다.
import { ObjectId } from "mongodb";
5. MeetUpDeailPage 컴포넌트
- 특정 meetup의 세부 정보를 렌더링하는 페이지 컴포넌트입니다.
- router.isFallback을 사용하여 로딩 상태를 처리합니다.
function MeetUpDeailPage(props) { const router = useRouter(); if (router.isFallback) { return <p>Loading...</p>; } const { id, image, title, address, description } = props.meetupData; return ( <MeetupDetail id={id} title={title} image={image} address={address} description={description} /> ); }
6. getStaticPaths 함수
- Next.js의 getStaticPaths를 사용하여 정적 생성할 경로를 정의합니다.
- MongoDB에서 모든 meetup의 _id를 가져와 paths를 생성합니다.
export async function getStaticPaths() { const { meetupsCollection, client } = await ConnMongoDB(); const meetups = await meetupsCollection.find({}, { _id: 1 }).toArray(); client.close(); return { fallback: false, paths: meetups.map(meetup => ({ params: { meetupId: meetup._id.toString() } })) }; }
- fallback: false로 설정하면, 지정된 경로 외의 다른 경로는 404 페이지를 표시합니다.
- 모든 meetup의 _id를 가져와 params 객체로 변환하여 paths를 생성합니다.
7. getStaticProps 함수
- Next.js의 getStaticProps를 사용하여 빌드 시 meetup 데이터를 가져와 props로 전달합니다.
- context.params를 통해 동적 경로 매개변수를 가져옵니다.
export async function getStaticProps(context) { const meetupId = context.params.meetupId; const { meetupsCollection, client } = await ConnMongoDB(); const selectedMeetup = await meetupsCollection.findOne({ _id: new ObjectId(meetupId) }); client.close(); return { props: { meetupData: { id: selectedMeetup._id.toString(), title: selectedMeetup.title, image: selectedMeetup.image, address: selectedMeetup.address, description: selectedMeetup.description, }, revalidate: 1, } }; }
- context.params.meetupId를 사용하여 특정 meetup의 ID를 가져옵니다.
- MongoDB에서 해당 ID를 가진 meetup 데이터를 찾고, 필요한 정보를 props로 전달합니다.
- revalidate를 통해 1초마다 페이지를 다시 생성하여 최신 데이터를 유지합니다.
전체 동작 방식
정적 경로 생성 (getStaticPaths):
- MongoDB에서 모든 meetup의 ID를 가져와 paths를 생성합니다.
- fallback: false로 설정하여 지정된 경로 외의 다른 경로는 404 페이지를 표시합니다.
정적 데이터 생성 (getStaticProps):
- context.params.meetupId를 사용하여 특정 meetup의 데이터를 MongoDB에서 가져옵니다.
- 가져온 데이터를 props로 설정하여 MeetUpDeailPage 컴포넌트에 전달합니다.
- 1초마다 페이지를 다시 생성하여 최신 데이터를 유지합니다.
페이지 렌더링 (MeetUpDeailPage):
- useRouter를 사용하여 라우팅과 로딩 상태를 처리합니다.
- props로 전달받은 데이터를 MeetupDetail 컴포넌트에 전달하여 meetup의 세부 정보를 렌더링합니다.
이를 통해 사용자는 특정 meetup의 세부 정보를 볼 수 있으며, 데이터는 MongoDB에서 가져와 최신 상태를 유지합니다.
481. "head" 메타 데이터 추가하기
pages/[meetupId]/index.js
return ( <Fragment> <Head> <title>{title}</title> <meta name="description" content={description} /> </Head> <MeetupDetail id={id} title={title} image={image} address={address} description={description} /> </Fragment> );
1. next/head 모듈 임포트
먼저 next/head 모듈을 페이지 컴포넌트에 임포트합니다.
import Head from 'next/head';
2. Head 컴포넌트 사용
Head 컴포넌트를 사용하여 메타데이터를 추가합니다. 예를 들어, 제목과 메타 태그를 추가하는 방법은 다음과 같습니다.
import Head from 'next/head'; function HomePage() { return ( <> <Head> <title>My Awesome Meetup</title> <meta name="description" content="This is a description of my awesome meetup page." /> <meta property="og:title" content="My Awesome Meetup" /> <meta property="og:description" content="This is a description of my awesome meetup page." /> <meta property="og:image" content="/images/meetup-image.jpg" /> <meta property="og:url" content="https://myawesomewebsite.com/meetup" /> <meta name="twitter:card" content="summary_large_image" /> <link rel="icon" href="/favicon.ico" /> </Head> <main> {/* 페이지 내용 */} </main> </> ); } export default HomePage;
설명
- Head 컴포넌트: Head 컴포넌트 안에 모든 메타데이터 태그를 추가합니다.
- <title> 태그: 페이지의 제목을 설정합니다.
- 메타 태그들:
- <meta name="description" ... />: 페이지 설명을 설정합니다.
- <meta property="og:title" ... />: Open Graph 제목을 설정합니다 (소셜 미디어 공유 시).
- <meta property="og:description" ... />: Open Graph 설명을 설정합니다.
- <meta property="og:image" ... />: Open Graph 이미지 URL을 설정합니다.
- <meta property="og:url" ... />: Open Graph URL을 설정합니다.
- <meta name="twitter:card" ... />: Twitter 카드 타입을 설정합니다.
- 파비콘: <link rel="icon" href="/favicon.ico" />를 사용하여 파비콘을 설정합니다.
이렇게 하면 해당 페이지를 렌더링할 때, Next.js가 <head> 섹션에 지정한 메타데이터를 포함하게 됩니다.
동적 메타데이터
동적 메타데이터를 설정하려면 getStaticProps 또는 getServerSideProps를 사용하여 데이터를 가져온 후, 해당 데이터를 Head 컴포넌트에 사용합니다.
import Head from 'next/head'; import { ConnMongoDB } from './api/db/ConnMongoDB'; function MeetupDetailPage({ meetupData }) { return ( <> <Head> <title>{meetupData.title}</title> <meta name="description" content={meetupData.description} /> <meta property="og:title" content={meetupData.title} /> <meta property="og:description" content={meetupData.description} /> <meta property="og:image" content={meetupData.image} /> <meta property="og:url" content={`https://myawesomewebsite.com/meetup/${meetupData.id}`} /> <meta name="twitter:card" content="summary_large_image" /> </Head> <main> {/* 페이지 내용 */} </main> </> ); } export async function getStaticProps(context) { const meetupId = context.params.meetupId; const { meetupsCollection, client } = await ConnMongoDB(); const selectedMeetup = await meetupsCollection.findOne({ _id: new ObjectId(meetupId) }); client.close(); return { props: { meetupData: { id: selectedMeetup._id.toString(), title: selectedMeetup.title, image: selectedMeetup.image, address: selectedMeetup.address, description: selectedMeetup.description, }, }, revalidate: 1, }; } export default MeetupDetailPage;
이렇게 하면 동적으로 데이터를 가져와 <head> 섹션에 반영할 수 있습니다.
482. Next.js 프로젝트 배포하기
1. Vercel 계정 생성 및 로그인
- Vercel 웹사이트로 이동합니다.
- 계정이 없으면 "Sign Up" 버튼을 클릭하여 가입합니다.
- GitHub, GitLab, Bitbucket 계정으로 가입 및 로그인할 수 있습니다.
- 이미 계정이 있다면 "Login" 버튼을 클릭하여 로그인합니다.
2. Next.js 프로젝트 준비
Next.js 프로젝트가 없는 경우, 다음 명령어로 새로운 프로젝트를 생성할 수 있습니다:
npx create-next-app my-next-app cd my-next-app
3. Git 저장소에 프로젝트 푸시
- Git 저장소를 생성합니다 (GitHub, GitLab 또는 Bitbucket에서).
- 로컬 프로젝트 디렉토리에서 Git 초기화 및 원격 저장소에 프로젝트를 푸시합니다.
git init git add . git commit -m "Initial commit" git remote add origin <your-repository-url> git push -u origin main
4. Vercel에서 새로운 프로젝트 생성
- Vercel 대시보드에서 "New Project" 버튼을 클릭합니다.
- Git 제공자(GitHub, GitLab, Bitbucket)와 연결하여 배포할 저장소를 선택합니다.
5. 프로젝트 임포트
- 배포할 저장소를 선택한 후 "Import" 버튼을 클릭합니다.
- Vercel이 자동으로 프로젝트 설정을 감지합니다. 기본적으로 설정된 값을 사용할 수 있습니다.
6. 빌드 설정 확인
Vercel은 Next.js 프로젝트를 자동으로 인식하고 기본 빌드 설정을 적용합니다.
- Build Command: next build
- Output Directory: .next
특정 설정이 필요한 경우 이 단계에서 수정할 수 있습니다.
7. 환경 변수 설정 (필요한 경우)
프로젝트에 필요한 환경 변수가 있다면 Vercel 대시보드에서 설정할 수 있습니다.
- Vercel 대시보드에서 배포된 프로젝트를 선택합니다.
- "Settings" 탭으로 이동합니다.
- "Environment Variables" 섹션에서 환경 변수를 추가합니다.
8. 배포 트리거
- 프로젝트를 Vercel에 연결하고 저장소에 푸시하면 자동으로 배포가 트리거됩니다.
- 모든 푸시마다 Vercel이 프로젝트를 다시 빌드하고 배포합니다.
git add . git commit -m "Update project" git push origin main
9. 배포 확인
- 배포가 완료되면 Vercel 대시보드에서 프로젝트의 URL을 확인할 수 있습니다.
- 제공된 URL을 방문하여 Next.js 애플리케이션이 올바르게 작동하는지 확인합니다.
10. 추가 설정 및 관리
- 도메인 연결: Vercel 대시보드에서 사용자 정의 도메인을 프로젝트에 연결할 수 있습니다.
- "Domains" 탭에서 도메인을 추가하고 설정을 완료합니다.
- 배포 미리보기: PR을 생성하면 Vercel이 자동으로 미리보기 URL을 생성하여 변경 사항을 확인할 수 있습니다.
- 프로젝트 설정: "Settings" 탭에서 프로젝트의 기타 설정을 관리할 수 있습니다.
도메인 설정
- Vercel 대시보드에서 프로젝트를 선택합니다.
- "Settings" 탭으로 이동하여 "Domains" 섹션으로 스크롤합니다.
- "Add Domain" 버튼을 클릭하고 사용자 정의 도메인을 입력합니다.
- 도메인 제공자의 DNS 설정에서 Vercel이 제공하는 CNAME 또는 A 레코드를 추가합니다.
지속적인 배포
Vercel은 기본적으로 지속적인 배포를 지원합니다. 코드 변경 사항을 Git 저장소에 푸시하면 Vercel이 자동으로 빌드 및 배포를 트리거합니다.
git add . git commit -m "Update project" git push origin main
이 과정을 통해 Next.js 애플리케이션을 Vercel을 통해 쉽게 배포하고 관리할 수 있습니다.
Vercel의 강력한 기능과 간편한 사용법 덕분에 빠르게 프로덕션 수준의 웹사이트를 운영할 수 있습니다.
댓글 ( 0)
댓글 남기기