Dandy Now!
  • [Next.js] 인프런 강의 "Next.js 필수 개발 가이드 3시간 완성!" 정리(Prisma)
    2024년 02월 13일 18시 25분 55초에 업로드 된 글입니다.
    작성자: DandyNow
    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
    반응형
    댓글