📝 서버리스란 ?
- 서버리스(Serverless) 아키텍쳐는 서버 인프라를 생성(프로비저닝)하거나 관리하지 않고 어플리케이션을 배포할 수 있는 아키텍처입니다.
- 이번 포스팅에서는 Next.js 14버전에서 서버리스 아키텍쳐를 이용하여 API 라우팅을 구현해보는 과정을 담아보도록 하겠습니다.
- Next.js 14버전에는 App router와 Page router가 있는데, 그 중에서 App router 방식을 적용할 예정입니다.
- 사실 Next.js 14 버전이 나온지 얼마되지 않았고, 특히 App router 방법에 대한 내용은 공식문서 말고는 참고할만한 레퍼런스가 적었기 때문에, 잘못된 정보가 있다면 댓글로 피드백 해주시면 감사하겠습니다!
🚀 Next.js 에서 API 라우팅을 위한 서버리스 함수를 생성하는 방법
// 경로: app/api/route.ts
import { NextResponse } from "next/server";
export function GET() {
return NextResponse.json({ message: "hello world" }, { status: 200 });
}
- app/api/hello.ts에 위와같은 코드를 작성후 http://localhost:3000/api 엔드포인트로 접속을 합니다.
- 위와 같이 아주 간단하게 구현할 수 있습니다.
- next.js에서 API route는 클라이언트 측(javascript)가 아닌 서버에서 실행되는 함수(컴포넌트) 이며, app/api/ 폴더 내의 파일들은 자동으로 서버에서 실행이 가능한 API 엔드포인트가 됩니다.
- API 라우트에서는 GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS 메서드를 사용할 수 있으며, NextRequest와 NextResponse 객체를 사용하여 HTTP 요청을 처리하고 응답할 수 있습니다.
- 또한, API 라우트에서 데이터베이스와 연결하고, 해당 모듈을 API 라우트에 가져와서 사용하는 것도 가능합니다.
- 다음은 실제 프로젝트에서 MongoDBAtlas와 연결하여 사용한 예시를 보여드리겠습니다.
🔍️ API route에서 DB 연결하기
// Mongoose 라이브러리를 사용하여 MongoDB 데이터베이스에 연결하는 모듈
import type _mongoose from "mongoose";
import { connect } from "mongoose";
// 전역 네임스페이스에 mongoose 객체를 포함
declare global {
var mongoose: {
promise: ReturnType<typeof connect> | null;
conn: typeof _mongoose | null;
};
}
// 환경 변수에서 MongoDB URI를 가져오기
const { NEXT_PUBLIC_MONGODB_URI } = process.env;
// MongoDB URI가 정의되어 있지 않으면 에러 처리
if (!NEXT_PUBLIC_MONGODB_URI) {
throw new Error(
"Please define the MONGODB_URI environment variable inside .env.local"
);
}
// 전역 네임스페이스에 캐시된 mongoose 객체가 있는지 확인
let cached = global.mongoose;
// 캐시된 mongoose 객체가 없다면 전역 네임스페이스에 초기화
if (!cached) {
global.mongoose = { conn: null, promise: null };
cached = { conn: null, promise: null };
}
// MongoDB 데이터베이스에 연결하기 위한 함수
async function dbConnect() {
if (cached.conn) {
return cached.conn;
}
// 연결 중인 상태가 아니라면 새로운 연결을 시작
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
// 연결을 시작하고 프로미스를 저장
cached.promise = connect(NEXT_PUBLIC_MONGODB_URI!, opts).then(
(mongoose) => {
return mongoose;
}
);
}
try {
// 연결 프로미스가 해결되기를 기다리고 연결을 저장
cached.conn = await cached.promise;
} catch (e) {
// 연결 중 오류가 발생하면 프로미스를 재설정하고 오류 발생
cached.promise = null;
throw e;
}
// MongoDB 연결을 반환
return cached.conn;
}
export default dbConnect;
- 이 함수 dbConnect는 MongoDB 데이터베이스에 대한 연결을 설정하는 역할을 합니다.
- 먼저 캐시된 연결이 있는지 확인하고 있다면 반환하고, 그렇지 않으면 Mongoose의 connect 함수를 사용하여 새로운 연결을 시작합니다.
// DB를 연결하고, HTTP POST 요청을 처리하는 함수
import { NextRequest, NextResponse } from "next/server";
import dbConnect from "../db-connect";
import Data from "./model";
const BAD_REQUEST = 400;
const INTERNAL_SERVER_ERROR = 500;
//요청 데이터의 유효성을 검사하는 함수
function isValidData(data: any): boolean {
return (
data &&
typeof data.name === "string" &&
typeof data.contact === "string" &&
typeof data.company === "string" &&
typeof data.item === "string" &&
typeof data.address === "string" &&
typeof data.agreedToTerms === "boolean"
);
}
export async function POST(request: Request) {
await dbConnect();
try {
const body = await request.json();
// 요청 데이터의 유효성을 검사
if (!isValidData(body)) {
return NextResponse.json(
{ error: "Bad Request - Invalid Data" },
{ status: BAD_REQUEST }
);
}
const createdData = await Data.create(body);
return NextResponse.json(createdData, { status: 201 });
} catch (error) {
console.error("Error creating example:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: INTERNAL_SERVER_ERROR }
);
}
}
- dbConnect() 함수를 실행하여, MongoDBAtlas 와 연결하고 POST 요청을 처리합니다.
- 클라이언트에서 받은 요청데이터의 유효성검사를 한 뒤, const createdData = await Data.create(body); 부분을 통해 요청 데이터를 실제 DB에 저장하게 됩니다.
- 그러면 여기서 import한 Data의 model.ts 파일을 살펴보도록 하겠습니다.
import type { Model } from "mongoose";
import mongoose, { model } from "mongoose";
interface IData {
name: string;
contact: string;
company: string;
item: string;
address: string;
addressDetail: string;
agreedToTerms: Boolean;
isDone: Boolean;
date: Date;
}
const DataSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please provide a name"],
maxlength: [10, "name cannot be more than 10 characters"],
},
contact: {
type: String,
required: [true, "Please provide a contact"],
},
company: {
type: String,
required: [true, "Please provide a company"],
},
item: {
type: String,
required: [true, "Please provide a item"],
},
address: {
type: String,
required: [true, "Please provide a address"],
},
addressDetail: {
type: String,
required: false,
},
agreedToTerms: {
type: Boolean,
required: [true, "Please provide a agreedToTerms"],
},
isDone: {
type: Boolean,
default: false, // 기본값으로 false 설정
},
date: {
type: Date,
default: () => {
let now = new Date();
now.setHours(now.getHours() + 9);
return now;
}, // 현재 날짜 및 시간으로 기본값 설정
},
});
DataSchema.methods.validateFields = function () {
const errors = this.validateSync();
if (errors) {
throw new Error(errors.errors);
}
};
export default (mongoose.models.inquiry ||
model<IData>("inquiry", DataSchema)) as Model<IData>;
// 1. model<IData>("inquiry", DataSchema)은 inquiry라는 모델의 이름으로 MongoDB 모델을 생성
// 2.만약 이미 같은 이름의 모델(inquiry)이 존재한다면 해당 모델을 사용하고, 존재하지 않으면 새로운 모델을 생성
- 이 코드는 Mongoose를 사용하여 MongoDB에 대한 데이터 모델을 정의하는 모듈입니다.
- 위의 예제에서는 요청값(body)의 각 필드의 필수값여부, 데이터 형식 등을 사전에 정의해둔것이죠.
- ( 이 메서드는 현재 데이터의 유효성을 검사하는 데 사용되며, 유효성 검사에서 오류가 발생하면 에러를 throw하도록 되어 있습니다. )
- 위에서 사용한 create 메서드는 MongoDB 모델 인스턴스에서 제공하는 메서드 중 하나이며, 신규 데이터를 생성하고 데이터베이스에 추가하는 데 사용된 것입니다!
🌱 마무리
- 간단한 프로젝트 예시를 통해 Next.js에서 서버리스 함수를 통한 API 라우팅 구현방식과, DB를 연결하는 방법에 대해 살펴보았습니다.
- 기존에는 서버 구축을 위해 별도로 node의 express 프레임워크 등을 이용하였는데, Next.js의 서버리스 함수(기능)을 통해 간편하고, 빠르게 웹을 제작할 수 있었습니다.
- 다음에는 Next-auth를 이용하여 로그인을 구현해보는 과정을 포스팅 해보겠습니다!
'Project > 탑개미자원' 카테고리의 다른 글
[Framer Motion] 컴포넌트가 사라질때(언마운트시) 애니메이션 효과를 적용할 수 있을까? (0) | 2023.11.27 |
---|---|
[Vercel] vercerl에서 한글 도메인을 등록할 수 있을까? (0) | 2023.11.21 |