코딩애플 강의를 통해 배운 NextJS를 정리한 글입니다.
2025년 4월 05일
Part 2 : 게시판 프로젝트
// database.js
import { MongoClient } from "mongodb";
const url =
"mongodb+srv://admin:fkaus123@cluster0.zrch4.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0";
const options = { useNewUrlParser: true };
let connectDB;
if (process.env.NODE_ENV === "development") {
if (!global._mongo) {
global._mongo = new MongoClient(url, options).connect();
}
connectDB = global._mongo;
} else {
connectDB = new MongoClient(url, options).connect();
}
export { connectDB };
// page.js
import { connectDB } from "@/util/database";
export default async function Home() {
const client = await connectDB;
const db = client.db("forum");
let result = await db.collection("post").find().toArray();
console.log(result);
return (
<div>
<div>a</div>
</div>
);
}
// Next.js 15버전
export default async function Detail({ params }) {
const { id } = await params;
return()
}
// 14이하
export default async function Page({ params }) {
const id = params.id;
return <p>{id}</p>;
}
"use client";
import { useRouter } from "next/navigation";
export default function DetailLink() {
let router = useRouter();
return (
<button
onClick={() => {
router.push("/");
}}
></button>
);
}
// test.js
export default function handler(요청, 응답) {
if (요청.method == "POST") {
return 응답.status(200).json("처리완료");
}
}
import { connectDB } from "@/util/database";
export default async function handler(요청, 응답) {
if (요청.method == "POST") {
const db = (await connectDB).db("forum");
let result = await db.collection("post").insertOne(요청.body);
return 응답.status(200).redirect("/list");
}
}
await db
.collection("post")
.updateOne({ _id: new ObjectId(요청.body.id) }, { $set: 바꿀꺼 });
return 없이 응답.status(302).redirect("/list");
// list.js
onClick={() => {
fetch("/api/post/delete", {
method: "DELETE",
body: item._id,
})
.then((r) => {
if (r.status == 200) {
return r.json();
} else {
//서버가 에러코드전송시 실행할코드
}
})
.then((result) => {
//성공시 실행할코드
})
.catch((error) => {
//인터넷문제 등으로 실패시 실행할코드
console.log(error);
});
}}
// delete.js
import { connectDB } from "@/util/database";
import { ObjectId } from "mongodb";
export default async function handler(요청, 응답) {
if (요청.method == "DELETE") {
const db = (await connectDB).db("forum");
let result = await db
.collection("post")
.deleteOne({ _id: new ObjectId(요청.body) });
return 응답.status(200).json("삭제됨");
}
}
fetch("/api/test?name=kim&age=20");
fetch("/api/test/ㅇㅁㄴㅇㅁ");
// api test
export default function handler(요청, 응답) {
console.log(요청.qurery); // ㅇㅁㄴㅇㅁ
}
//directory
pages / api / test / [작명.js];
export const dynamic = "force-dynamic";
export const dynamic = "force-static";
fetch("/URL", { cache: "force-cache" });
fetch("/URL", { cache: "no-store" });
// 그 외
fetch("/URL", { next: { revalidate: 60 } });
export const revalidate = 60;
// 설치
npm install next-auth
// 디렉토리
pages/api/auth/[...nextauth].js
// nextauth.js
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
export const authOptions = {
providers: [
GithubProvider({
clientId: "Ov23limmnbY0Aqd6DvNU",
clientSecret: "8c39afc500e87b2191bf5197c3b28b1ba895a7ec",
}),
],
secret: "0630",
};
export default NextAuth(authOptions);
// 로그인
"use client";
import { signIn } from "next-auth/react";
export default function LoginBtn() {
return (
<button
onClick={() => {
signIn();
}}
>
로그인
</button>
);
}
// 데이터
import { authOptions } from "@/pages/api/auth/[...nextauth]";
let session = await getServerSession(authOptions);
console.log(session);
// nextauth.js
import { connectDB } from "@/util/database";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
export const authOptions = {
providers: [
GithubProvider({
clientId: "Ov23limmnbY0Aqd6DvNU",
clientSecret: "8c39afc500e87b2191bf5197c3b28b1ba895a7ec",
}),
],
secret: "0630",
adapter: MongoDBAdapter(connectDB),
};
export default NextAuth(authOptions);
// api.js
import { connectDB } from "@/util/database";
import { getServerSession } from "next-auth";
import { authOptions } from "@/pages/api/auth/[...nextauth]";
export default async function handler(요청, 응답) {
let session = await getServerSession(요청, 응답, authOptions);
if (session) {
요청.body.author = session.user.email;
}
if (요청.method == "POST") {
const db = (await connectDB).db("forum");
let result = await db.collection("post").insertOne(요청.body);
return 응답.status(200).redirect("/list");
}
}
import { connectDB } from "@/util/database";
import bcrypt from "bcrypt";
export default async function handler(요청, 응답) {
if (요청.method == "POST") {
let hash = await bcrypt.hash(요청.body.password, 10);
요청.body.password = hash;
let db = (await connectDB).db("forum");
await db.collection("user_cred").insertOne(요청.body);
응답.status(200).json("성공");
}
}
import { connectDB } from "@/util/database";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcrypt";
export const authOptions = {
providers: [
GithubProvider({
clientId: "Ov23limmnbY0Aqd6DvNU",
clientSecret: "8c39afc500e87b2191bf5197c3b28b1ba895a7ec",
}),
CredentialsProvider({
//1. 로그인페이지 폼 자동생성해주는 코드
name: "credentials",
credentials: {
email: { label: "email", type: "text" },
password: { label: "password", type: "password" },
},
//2. 로그인요청시 실행되는코드
//직접 DB에서 아이디,비번 비교하고
//아이디,비번 맞으면 return 결과, 틀리면 return null 해야함
async authorize(credentials) {
let db = (await connectDB).db("forum");
let user = await db
.collection("user_cred")
.findOne({ email: credentials.email });
if (!user) {
console.log("해당 이메일은 없음");
return null;
}
const pwcheck = await bcrypt.compare(
credentials.password,
user.password
);
if (!pwcheck) {
console.log("비번틀림");
return null;
}
return user;
},
}),
],
//3. jwt 써놔야 잘됩니다 + jwt 만료일설정
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, //30일
},
callbacks: {
//4. jwt 만들 때 실행되는 코드
//user변수는 DB의 유저정보담겨있고 token.user에 뭐 저장하면 jwt에 들어갑니다.
jwt: async ({ token, user }) => {
if (user) {
token.user = {};
token.user.name = user.name;
token.user.email = user.email;
}
return token;
},
//5. 유저 세션이 조회될 때 마다 실행되는 코드
session: async ({ session, token }) => {
session.user = token.user;
return session;
},
},
secret: "0630",
adapter: MongoDBAdapter(connectDB),
};
export default NextAuth(authOptions);
localStorage.setItem("자료이름", "값");
localStorage.getItem("자료이름");
localStorage.removeItem("자료이름");
document.cookie = "쿠키이름=값";
document.cookie = "쿠키이름=값; max-age=3600";
import { cookies } from "next/headers";
export default function 서버컴포넌트() {
let result = cookies().get("쿠키이름");
console.log(result);
}
<body
className={`${geistSans.variable} ${geistMono.variable} ${
res !== undefined && res?.value === "dark" ? "dark-mode" : ""
}`}
/>;
("use client");
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default function Darkmode() {
let router = useRouter();
useEffect(() => {
let 쿠키값 = ("; " + document.cookie).split(`; mode=`).pop().split(";")[0];
if (쿠키값 == "") {
document.cookie = "mode=light; max-age=" + 3600 * 24 * 400;
}
});
return (
<span
onClick={() => {
let 쿠키값 = ("; " + document.cookie)
.split(`; mode=`)
.pop()
.split(";")[0];
if (쿠키값 == "light") {
document.cookie = "mode=dark; max-age=" + 3600 * 24 * 400;
router.refresh();
} else {
document.cookie = "mode=light; max-age=" + 3600 * 24 * 400;
router.refresh();
}
}}
>
{" "}
🌙{" "}
</span>
);
}
import { getToken } from "next-auth/jwt";
import { NextResponse } from "next/server";
export async function middleware(request) {
const session = await getToken({ req: request });
if (request.nextUrl.pathname.startsWith("/write")) {
const session = await getToken({ req: request });
console.log("세션", session);
if (session == null) {
return NextResponse.redirect(new URL("/api/auth/signin", request.url));
}
}
if (request.nextUrl.pathname === "/list") {
console.log(request.headers.get());
return NextResponse.next();
}
request.cookies.get("쿠키이름"); //출력
request.cookies.has("쿠키이름"); //존재확인
request.cookies.delete("쿠키이름"); //삭제
const response = NextResponse.next();
response.cookies.set({
name: "mode",
value: "dark",
maxAge: 3600,
httpOnly: true,
});
return response;
}
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: true,
},
};
export default nextConfig;
import { connectDB } from "@/util/database";
import { revalidatePath } from "next/cache"; //페이지 상단에 추가
export default async function Write2() {
//DB에서 데이터 뽑아서 보여주기
const db = (await connectDB).db("forum");
let result = await db.collection("post_test").find().toArray();
async function handleSubmit(formData) {
"use server";
const db = (await connectDB).db("forum");
await db
.collection("post_test")
.insertOne({ title: formData.get("post1") });
revalidatePath("/write2");
}
return (
<form action={handleSubmit}>
<input type="text" name="post1" />
<button type="submit">Submit</button>
{result ? result.map((a) => <p>글제목 : {a.title}</p>) : null}
</form>
);
}