언어·프레임워크/Next.js

[Next.js] 인프런 강의 "Next.js 필수 개발 가이드 3시간 완성!" 정리(Prisma)

DandyNow 2024. 2. 13. 18:25
728x90
반응형

Prisma ORM 적용

1. VSCode에서 Prisma Extension 설치

더보기

Name: Prisma
Id: Prisma.prisma
Description: Adds syntax highlighting, formatting, auto-completion, jump-to-definition and linting for .prisma files.
Version: 5.9.1
Publisher: Prisma
VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=Prisma.prisma

 

2. prisma 설치

npm i prisma

😉 공식 문서 : https://www.prisma.io/

 

3. prisma 명령어

  • 명령어 목록 보기 : npx prisma
  • 기본 설정 및 prisma 폴더 생성 : npx prisma init
  • prisma model 코드 포맷팅(model 코드 작성 후 실행하면 코드가 정렬됨) : npx prisma format
  • 마이그레이션(RDBMS의 경우) : npx prisma migrate dev

 

4. MySQL 연동 방법

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}
// .env

DATABASE_URL="mysql://root:1234@localhost:3306/mydb"

😉 공식 문서 : https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-mysql

 

5. Prisma Client 설치

  • 스키마에 정의된 모델에 접근 및 CRUD 기능을 제공한다.
  • 단 하나의 Prisma Client 인스턴스만  실행되어야 한다(Too Many Prisma Client 에러 발생 할 수 있음).
npm install @prisma/client

😛 공식 문서 : https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-prismaclient-npm-package

 

6. Prisma Client 적용

아래 공식 문서의 코드(db.ts)를 사용하면 단 하나의 Prisma Client 인스턴스 사용을 보장할 수 있다.

// prisma/client.ts

// 공식 문서에서 싱글톤 코드 복붙함
// 이 모듈을 통해 Prisma Client 기능을 이용하게 됨
import { PrismaClient } from "@prisma/client"; // 설치한 모듈 로딩

const prismaClientSingleton = () => {
  return new PrismaClient();
};

declare global {
  var prisma: undefined | ReturnType<typeof prismaClientSingleton>;
}

const prisma = globalThis.prisma ?? prismaClientSingleton();

export default prisma;

if (process.env.NODE_ENV !== "production") globalThis.prisma = prisma;

😛공식 문서 : https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices#solution

 

7. API에 Prisma Client 적용

✔️ 유효성 검사 스키마(schema)

Zod를 이용해 유효성 검사를 할 것이다.

// src/app/api/users/schema.ts

import { z } from "zod";

const schema = z.object({
  name: z.string().min(3),
  email: z.string().email(),
});

export default schema;

 

📌 GET

// src/app/api/users/route.tsx

import { NextRequest, NextResponse } from "next/server";
import prisma from "@/../prisma/client"; // 위에서 작성한 싱글톤 패턴 적용된 모듈 로딩
import schema from "./schema";

export async function GET(request: NextRequest) {
  const users = await prisma.user.findMany(); // Prisma Client의 findMany() 메서드 이용해 전체 유저 조회

  return NextResponse.json(users);
}

// (생략)
// src/app/api/users/[id]/route.tsx

import { NextRequest, NextResponse } from "next/server";
import prisma from "@/../prisma/client"; // 위에서 작성한 싱글톤 패턴 적용된 모듈 로딩
import schema from "../schema";

export async function GET(
  request: NextRequest,
  { params }: { params: { id: number } }
) {
  const user = await prisma.user.findUnique({ // Prisma Client의 findUnique() 메서드 이용해 유저 1명 조회, 존재하지 않는 데이터인 경우 null 반환
    where: { id: parseInt(params.id) },
  });

  if (!user) {
    return NextResponse.json({ error: "Use Not Found" }, { status: 404 });
  }
  return NextResponse.json(user);
}

// (생략)

 

📌 POST

// src/app/api/users/route.tsx

import { NextRequest, NextResponse } from "next/server";
import prisma from "@/../prisma/client";
import schema from "./schema";

// (생략)

export async function POST(request: NextRequest) {
  const body = await request.json();
  const validation = schema.safeParse(body);
  if (!validation.success) {
    return NextResponse.json(validation.error.errors, { status: 404 });
  }

  // 동일한 유저 정보가 있는지 확인
  const user = await prisma.user.findUnique({
    where: { email: body.email },
  });
  
  // 동일 유저가 있을 경우 400 에러 반환
  if (user) {
    return NextResponse.json({ error: "User Already Exists" }, { status: 400 });
  }

  // 새로운 유저 생성
  const newUser = await prisma.user.create({
    data: {
      name: body.name,
      email: body.email,
    },
  });

  return NextResponse.json(newUser, { status: 201 });
}


/*
윈도우 curl을 이용한 요청 테스트
요청 명령 1 : curl -d "{""name"":""lee"", ""email"":""lee@gmail.com""}" -H "Content-Type: application/json" -X POST http://localhost:3000/api/users
요청 결과 1 : {"id":2,"email":"lee@gmail.com","name":"lee","followers":0,"isActive":true,"registeredAt":"2024-02-14T00:00:21.681Z"}
요청 명령 2 : curl -d "{""name"":""lee"", ""email"":""lee@gmail.com""}" -H "Content-Type: application/json" -X POST http://localhost:3000/api/users
요청 결과 2 : {"error":"User Already Exists"}
*/

 

📌 PUT

// src/app/api/users/[id]/route.tsx

import { NextRequest, NextResponse } from "next/server";
import prisma from "@/../prisma/client";
import schema from "../schema";

export async function PUT(
  request: NextRequest,
  { params }: { params: { id: string } } // URL의 경로가 문자열이기 때문에 string 타입 지정
) {
  const body = await request.json();

  const validation = schema.safeParse(body);
  if (!validation.success) {
    return NextResponse.json(validation.error.errors, { status: 404 });
  }

  // 수정할 유저가 있는지 확인
  const user = await prisma.user.findUnique({
    where: { id: Number(params.id) }, // id는 int 타입이기 때문에 형변환
  });

  // 유저가 없다면 404 반환
  if (!user) {
    return NextResponse.json({ error: "User Not Found" }, { status: 404 });
  }

  // 유저 정보 수정
  const updatedUser = await prisma.user.update({
    where: { id: Number(user.id) }, // id는 int 타입이기 때문에 형변환
    data: { name: body.name, email: body.email },
  });

  return NextResponse.json(updatedUser);
}

/*
윈도우 curl을 이용한 요청 테스트
요청 명령 1 : curl -d "{""name"":""won"", ""email"":""won@gmail.com""}" -H "Content-Type: application/json" -X PUT http://localhost:3000/api/users/2
요청 결과 1 : {"id":2,"email":"won@gmail.com","name":"won","followers":0,"isActive":true,"registeredAt":"2024-02-14T00:00:21.681Z"}
요청 명령 2 : curl -d "{""name"":""won"", ""email"":""won@gmail.com""}" -H "Content-Type: application/json" -X PUT http://localhost:3000/api/users/3
요청 결과 2 : {"error":"User Not Found"}
*/

 

📌 DELETE

// src/app/api/users/[id]/route.tsx

export async function DELETE(
  request: NextRequest,
  { params }: { params: { id: string } }
) {

  // 삭제할 유저가 있는지 확인
  const user = await prisma.user.findUnique({
    where: { id: Number(params.id) },
  });

  // 유저가 없다면 404 반환
  if (!user) {
    return NextResponse.json({ error: "User Not Found" }, { status: 404 });
  }

  // 해당 유저 삭제
  await prisma.user.delete({ where: { id: user.id } });

  return NextResponse.json({});
}

/*
윈도우 curl을 이용한 요청 테스트
요청 명령 1 : curl -X DELETE http://localhost:3000/api/users/2
요청 결과 1 : {}
요청 명령 2 : curl -X DELETE http://localhost:3000/api/users/2
요청 결과 2 : {"error":"User Not Found"}
*/

 

✔️ 데이터 모델링(Data Modeling)과 엔티티(Entity)

엔티티(만들고자 하는 애플리케이션의 핵심 요소)를 정의하는 과정을 말한다.

🤔 엔티티와 테이블의 차이

  • 엔티티는 데이터베이스나 SQL상에 존재하지 않는다. 테이블과 달리 엔티티는 실제로 존재하지 않는 아닌 일종의 개념이다. 그러나 테이블은 데이터베이스나 SQL에 실제로 존재하며 물리적인 구조를 지니고 있다.
  • 엔티티는 테이블이 될 수도 있고 안 될 수도 있다. 엔티티는 CDM(Conceptual Data Model)상에서 쓰이는 일종의 '속성(attribute)'의 집합이다. 엔티티가 물리 모델링 안에선 '테이블'이지만, 논리 모델링에선 가능하지만 실제 물리모델링에 쓰지 못하는 추상적인 엔티티 같은 경우 테이블이 될 수 없다.
출처 : https://yunamom.tistory.com/164

 

728x90
반응형