Relations

Odyssey·2025년 3월 22일
0

Next.js_study

목록 보기
38/58
post-thumbnail

2025.3.22 토요일의 공부기록

내용 3줄정리

  1. Prisma ORM은 모델 간 관계를 선언할 때, 데이터베이스에 존재하지 않는 가상 필드를 제공해 더 직관적으로 데이터를 다룰 수 있도록 한다.
  2. 실제 데이터베이스에서는 @relation(fields: [userId], references: [id])와 같은 외래키 설정을 통해 참조 무결성을 유지하며, 설정 후에는 반드시 마이그레이션을 수행해야 한다.
  3. 사용자 삭제 시 연결된 데이터 처리 방식을 결정하는 referential actions를 적절히 지정하면 데이터베이스 무결성을 유지하면서 안정적인 애플리케이션 운영이 가능하다.

Prisma의 관계(Relations)란?

Prisma의 관계(Relations) 란 두 모델 간의 연결을 나타낸다. 데이터베이스 관점에서는 외래키를 통해 모델 간의 관계를 나타내지만, Prisma에서는 더 쉽고 직관적인 방법으로 이 관계를 정의할 수 있다.

예를 들어, 한 명의 사용자가 여러 개의 블로그 게시물이나 여러 개의 토큰을 가질 수 있는 경우, 이는 일대다(one-to-many) 관계로 정의할 수 있다.

관계의 종류

Prisma에서 지원하는 주요 관계는 아래와 같다.

  • 일대일 (one-to-one)
    예) 사용자(User) ↔ 프로필(Profile)

  • 일대다 (one-to-many)
    예) 사용자(User) ↔ 블로그 게시물(Post)

  • 다대다 (many-to-many)
    예) 게시물(Post) ↔ 태그(Tag)


Prisma 관계(Relations) 설정 예시와 설명

아래는 사용자(User)가 여러 개의 SMS 인증 토큰(SMSToken)을 갖는 일대다(one-to-many) 관계의 예시이다.

Prisma 스키마 예시

schema.prisma 파일에 관계 설정을 하면 다음과 같다.

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

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id         Int        @id @default(autoincrement())
  username   String     @unique
  email      String?    @unique
  password   String?
  phone      String?    @unique
  github_id  String?    @unique
  avatar     String?
  created_at DateTime   @default(now())
  updated_at DateTime   @updatedAt
  SMSToken   SMSToken[]
}

model SMSToken {
  id         Int      @id @default(autoincrement())
  token      String   @unique
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
  user       User     @relation(fields: [userId], references: [id])
  userId     Int
}

실습 코드 설명

User 모델의 SMSToken 필드:

SMSToken SMSToken[]
  • 이 필드는 Prisma의 ORM 수준에서만 존재하는 가상 필드로, 실제 데이터베이스에 테이블이나 컬럼으로 존재하지 않는다.
  • Prisma Client가 사용자의 토큰 데이터를 쉽게 조회할 수 있도록 도와주는 역할을 한다.

SMSToken 모델의 useruserId 필드:

user   User @relation(fields: [userId], references: [id])
userId Int
  • user: Prisma 수준에서 관계를 설정하는 가상 필드이다.
  • userId: 실제 데이터베이스에서 사용되는 외래키 필드로, User 모델의 id와 연결된다.
  • @relation(fields: [userId], references: [id]): 어떤 필드가 외래키인지, 그리고 어떤 필드를 참조하는지를 명확히 설정해준다.

즉, Prisma 관계는 두 가지 필드로 구성된다:

  • Prisma 가상 필드: Prisma ORM에서만 존재하며 데이터베이스에는 없다.
  • 실제 외래키 필드: 데이터베이스에 실제로 저장되어 있으며 외래키로 작동한다.

Prisma 관계(Relations) 공식 문서 바로가기

Prisma Client를 활용한 관계 데이터 조회 예시

Prisma Client를 사용하면 간편하게 관계 데이터를 조회할 수 있다. 예를 들어 특정 사용자(id:1)가 가진 토큰을 조회하는 코드이다.

import { PrismaClient } from "@prisma/client";

const db = new PrismaClient();

async function test() {
  const token = await db.sMSToken.findUnique({
    where: {
      id: 1,
    },
    include: {
      user: true,
    },
  });
  console.log(token);
}
test();

export default db;

관계 설정 후 마이그레이션 적용하기

관계를 설정하거나 변경한 후에는 마이그레이션을 통해 데이터베이스에 반영해야 한다.

npx prisma migrate dev

이 명령어를 통해 설정된 관계가 데이터베이스 구조에 안전하게 반영된다.


Prisma VSCode 익스텐션으로 자동완성

Prisma를 사용할 때 VSCode의 Prisma 익스텐션을 활용하면 코드를 작성할 때 자동 완성, 문법 강조 및 코드 포맷팅 등 여러 가지 편리한 기능을 활용할 수 있다.

Prisma 익스텐션을 설치한 뒤, 자동 완성을 활성화하기 위해 다음 설정을 추가한다.

  • VSCode에서 cmd + shift + p(Windows는 ctrl + shift + p)를 누르고, "Open Settings(JSON)" 명령어를 입력하여 JSON 설정파일을 연다.
  • 열린 JSON 설정 파일에 다음 내용을 추가한다.
"[prisma]": {
  "editor.defaultFormatter": "Prisma.prisma"
}
  • 설정 저장 후 Prisma 파일을 저장하면 자동으로 코드가 정리되고, 릴레이션 필드 자동완성 기능이 제공된다.

Referential Actions

Referential Actions(참조 액션) 은 두 모델 간 관계가 설정된 상황에서, 참조되는 레코드가 삭제되거나 변경될 때 어떻게 처리할지를 지정하는 옵션이다.

예를 들어, 특정 사용자가 삭제될 때 그 사용자와 연관된 게시물이나 토큰을 어떻게 처리할지를 결정할 수 있다. 이는 데이터 무결성을 유지하고 데이터베이스의 일관성을 보호하는 데 매우 중요한 개념이다.

Prisma에서 지원하는 Referential Actions 종류 및 적용 예시

Prisma는 다음 다섯 가지의 referential actions를 지원한다.

아래는 onDelete를 기준으로 설명하지만, onUpdate에도 동일하게 적용된다.

  • Cascade
    참조된 레코드를 삭제하면, 연결된 레코드도 자동으로 삭제된다.
    예: 사용자를 삭제하면 사용자의 게시물도 함께 삭제된다.

  • Restrict
    참조된 레코드와 연결된 레코드가 존재하면 삭제를 방지한다.
    예: 게시물이 존재하는 사용자는 삭제할 수 없다.

  • NoAction
    기본적으로는 Restrict와 유사하지만 데이터베이스에 따라 정확한 동작이 다를 수 있다. 일반적으로는 Restrict와 같은 효과를 가진다.

  • SetNull
    참조된 레코드를 삭제하면 연결된 필드를 자동으로 NULL 값으로 설정한다. 단, 해당 필드가 optional(선택적)일 때만 가능하다.
    예: 사용자 계정이 삭제되면 연결된 게시물의 작성자 필드가 NULL이 된다.

  • SetDefault
    참조 필드에 기본값이 지정되어 있을 때, 참조된 레코드를 삭제하면 해당 필드의 값을 기본값으로 설정한다.

<예시>

model User {
  id         Int        @id @default(autoincrement())
  username   String     @unique
  email      String?    @unique
  password   String?
  phone      String?    @unique
  github_id  String?    @unique
  avatar     String?
  created_at DateTime   @default(now())
  updated_at DateTime   @updatedAt
  SMSToken   SMSToken[]
}

model SMSToken {
  id         Int      @id @default(autoincrement())
  token      String   @unique
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId     Int
}

예시 코드 설명

  • onDelete: Cascade를 설정하여 사용자가 삭제될 때 그 사용자가 가진 모든 SMSToken 레코드도 자동으로 함께 삭제되도록 한다.
  • userId 필드는 사용자의 기본 키(id)를 참조하는 외래키로, 필수적으로 존재해야 하므로 선택적(Optional)이 아닌 필수 필드로 설정된다. 따라서 Cascade를 사용할 때는 일반적으로 참조 필드가 필수(non-optional)로 설정된다.

Referential Actions을 설정하지 않았을 때 기본 동작

Prisma에서 참조 액션을 설정하지 않으면 데이터베이스 시스템의 기본 설정을 따른다. 대부분의 데이터베이스 시스템에서 기본적으로는 Restrict 또는 NoAction이 설정되어 있어, 연결된 데이터가 존재할 때는 삭제가 불가능하게 된다.

안정적인 애플리케이션 운영을 위해서는 참조 액션을 명시적으로 설정하는 것이 좋다.


Referential Actions 실무 활용법 예시

상황추천 referential action
사용자 삭제 시 사용자 게시물을 삭제해야 하는 경우onDelete: Cascade
게시물이 존재하면 사용자를 삭제하지 않아야 하는 경우onDelete: Restrict
사용자 삭제 시 게시물은 남기되 작성자만 NULL로 설정onDelete: SetNull

이렇게 각 상황에 맞게 referential actions을 선택하여 데이터베이스 무결성을 유지할 수 있다.

Prisma Referential Actions 공식 문서

0개의 댓글