
수강중인 강의 : 코딩애플 NextJS
가보자
새로 알게 된 내용은 몇 없었다(MongoDB 업데이트, 수정 등).
알던 지식을 다시 정리해보자.
클라이언트 페이지는 다음과 같이 만들었다.
// edit/[id]/page.tsx
import { connectDB } from '@/util/db';
import { ObjectId } from 'mongodb';
import React from 'react';
const Edit = async (props: any) => {
const db = (await connectDB).db("board");
let result = await db.collection("post").findOne({ _id: new ObjectId(props.params.id) });
if (!result) {
alert('잘못된 접근입니다.');
window.location.href = '/list';
return;
}
return (
<div className>
<h4>수정 페이지</h4>
<form action="/api/post/edit" method="POST">
<label htmlFor="title">제목</label>
<input type="hidden" name="id" value={props.params.id} />
// id를 전송하기 위한 input, 유저에게 보여서는 안됨
<textarea name='title' defaultValue={result.title}></textarea>
<label htmlFor="content">내용</label>
<textarea name='content' defaultValue={result.content}></textarea>
<button type="submit">수정</button>
</form>
</div>
);
};
export default Edit;
여기서 눈여겨 볼 점은 글의 id를 담기 위한 input을 하나 더 만들어준다는 것이다. id 정보를 가지고 있어야 DB에서 올바르게 데이터를 수정할 수 있다.
<input type="hidden"> 또는 style={{display:'none'}}을 통해 사용자에게 보이지 않도록 주의하자.
다음은 서버 페이지다.
// pages/api/post/edit.tsx
import { connectDB } from "@/util/db";
import { ObjectId } from "mongodb";
import { NextApiRequest, NextApiResponse } from "next";
const edit = async (request: NextApiRequest, response: NextApiResponse) => {
if (request.method == "POST"){
const db = (await connectDB).db("board");
let result = await db.collection("post").updateOne(
{
_id: new ObjectId(request.body.id),
},
{
$set: {
title: request.body.title,
content: request.body.content,
},
},
{ writeConcern: { w: "majority" } }
);
response.status(302).redirect("/list");
} else {
response.status(400).json("잘못된 접근입니다.");
}
};
export default edit;
MongoDB에서 하나의 데이터를 수정할 때는 updateOne() 메소드를 사용한다.
기본 형식은 다음과 같다.
db.collection("테이블 이름").updateOne(
{
// 업데이트 시에 데이터를 식별할 값
},
{
$set: {
// 수정할 값과 내용
},
});
$set뿐만 아니라 $inc를 통해 값의 변경이 아닌 증가/감소 처리 또한 가능하다.
삭제 또한 비슷한 형식인데, 클라이언트에서 삭제 버튼을 누를 시, /api/post/delete로 글의 id를 담아 DELETE 요청을 보낸다고 가정해보자.
이 때 서버에서는 다음과 deleteOne 메소드를 통해 처리한다.
// api/post/delete.tsx
const Delete = async (request: NextApiRequest, response: NextApiResponse) => {
if (request.method == "DELETE"){
const db = (await connectDB).db("board");
let result = await db.collection("post").deleteOne(
{
_id: new ObjectId(request.body.id),
}
);
response.status(302).redirect("/list");
} else {
response.status(400).json("잘못된 접근입니다.");
}
};
export default Delete;
수정할 값을 담았던 updateOne과 달리, 데이터를 식별할 수 있는 값을 담아 보내면 데이터 삭제가 이루어진다.
클라이언트 컴포넌트가 서버 컴포넌트의 자식 요소로 존재할 때, 데이터 접근은 서버 컴포넌트에서 처리한 뒤, 클라이언트 컴포넌트에 props로 이를 넘겨주는 방식이다.
// 서버 컴포넌트
import { connectDB } from "@/util/db";
import Link from "next/link";
import ListItem from "./ListItem";
export default async function List() {
const db = (await connectDB).db('board');
let result = await db.collection('post').find().toArray();
return (
<div className="list-bg">
<ListItem result={result} />
// 클라이언트 컴포넌트 ListItem에 result를 넘겨줌
</div>
)
}
// ListItem.tsx, 클라이언트 컴포넌트
"use client";
const ListItem = (props: any) => {
const result = props.result;
return (
<div>
{result.map(
// ...
)}
</div>
);
};
"use client";
import { useEffect } from "react";
const ListItem = () => {
useEffect(() => {
// 서버에 부탁해서 DB 게시물 가져오는 코드 작성
// result에 저장
}, []);
return (
// ...
);
};
export default ListItem;
유의할 점은 DB에 직접 접근하는 것이 아닌, 서버에게 DB 데이터를 달라는 요청을 보낸다는 점이다. 클라이언트 컴포넌트는 클라이언트에게 노출되기 때문에, DB 접근과 같은 민감한 행위들은 금지된다.
더 궁금하다면 NextJS 독학 2차시를 참고해보자.
props를 통한 접근은 SEO에 더욱 유리하다.
useEffect가 호출되는 시점은 html이 모두 렌더링된 이후이기 때문에, 검색 엔진 봇에게 제대로 된 결과를 제공할 수 없다.
그러나 컴포넌트 구조가 복잡해질수록, props를 통해 데이터를 주고 받는 것은 Props Drilling과 같은 문제를 낳을 수 있다.
NextJS에서는 Redux와 같은 전역 상태 관리 툴이 따로 존재하는지 궁금해졌다.
또한 백엔드 지식이 없다시피 하다보니 더 상세한 서버의 데이터, 예외 처리 등에 대해 알아봐야겠다고 생각했다.