Prisma에서 마이그레이션 수행하는 방법(feat. NestJS)

윤학·2024년 3월 4일
0

Prisma

목록 보기
1/1
post-thumbnail

최근 개발하다 Schema를 변경해야 할 일이 있었다.

Prisma를 사용하고 있다 보니 Prisma Schema와 DB Schema를 동일하게 가져가는 작업을 해야 했는데, Prisma는 prisma migrate라고 하는 도구를 통해 마이그레이션을 쉽게 수행할 수 있도록 도와주고 있다.

이를 활용해서 NestJS 환경에서 Prisma를 ORM으로 사용하고 있을 때 마이그레이션 하는 방법을 알아보도록 하자.

DB로는 Postgresql을 사용했다.

대략 마이그레이션이 진행되는 과정은 크게 아래와 같다.

  1. Prisma의 모델을 수정한다.
  2. 수정된 Schema를 DB에 적용할 수 있도록 마이그레이션 파일을 생성한다.
  3. 생성된 SQL 파일을 확인하고, 직접 작성하고 싶은 부분이 있다면 수정한다.
  4. 마이그레이션 파일을 적용한다.
  5. 마이그레이션 수행 결과를 확인한다.
  6. 애플리케이션의 Prisma Client를 최신화한다.

위 단계 중 2~4번은 한 번에 진행할 수 있는데, 이 글에서는 단계별로 알아보고자 모든 단계를 진행해 보며 필요한 부분들을 알아보자.

이미 생성되어 있는 Batch History 테이블의 컬럼("FAILED", "COMPLETED", "PENDING")을 Enum으로 저장하고 있는데, PENDING 값을 RUNNING으로 변경하는 작업을 해보자.

1. Prisma 모델 수정

일단 기존의 Prsima Model을 수정한다.

프로젝트의 prisma/prisma.schema

--------기존 Enum--------
enum batch_status_enum {
  PENDING
  FAILED
  COMPLETED

  @@map("batch_status_enum")
}

--------수정한 Enum--------
enum batch_status_enum {
  RUNNING
  FAILED
  COMPLETED

  @@map("batch_status_enum")
}

2. 마이그레이션 파일 생성

마이그레이션 파일은 기존 DB의 Schema와 방금 수정했던 Prisma Schema를 비교하여 변경 사항을 DB에 적용할 수 있게 SQL로 작성된 파일을 말한다.

해당 파일은 npx prisma migrate dev 명령어를 수행하면 생성이 되는데, 해당 명령어만 입력한다면 앞서 살펴봤던 마이그레이션 진행 과정이 한 번에 진행된다.

6번 과정(Prisma Client 최신화)은 이후에 따로 수행해야 한다.

그래서 생성된 SQL 파일을 바로 DB에 적용하기 때문에 상황에 맞게 SQL을 수정하지는 못한다.

특정 상황에서는 마이그레이션이 실패할 수도 있기 때문에 수정 할 상황이 필요한데 이 부분은 뒤에서 알아보자.

일단, DB에 바로 적용하진 않고 SQL 파일만 생성하기 위해 뒤에 --create-only라는 flag를 주고 실행해 보자.

.env 파일을 활용한다면 DATABASE_URL은 사용하지 않아도 됩니다.

DATABASE_URL='적용할 DB URL' npx prisma migrate dev --create-only --name '생성할 마이그레이션 파일 이름'

그리고 Project의 root 위치에 prisma/migrations를 들어가 보면 설정한 마이그레이션 파일 이름으로 폴더가 생성되고, 안에 SQL 파일이 생성될 것이다.

3. 마이그레이션 파일 확인 및 수정

이 부분은 만약 수정할 부분이 없다면 넘어가도 된다.

이 글에서는 Enum값 중 PENDING을 없애고 RUNNING을 추가한다 했으니 아래와 같은 SQL 문이 생성될 것이다.

BEGIN;
CREATE TYPE "batch_status_enum_new" AS ENUM ('RUNNING', 'FAILED', 'COMPLETED');
ALTER TABLE "batch_history" ALTER COLUMN "status" TYPE "batch_status_enum_new" USING ("status"::text::"batch_status_enum_new");
ALTER TYPE "batch_status_enum" RENAME TO "batch_status_enum_old";
ALTER TYPE "batch_status_enum_new" RENAME TO "batch_status_enum";
DROP TYPE "batch_status_enum_old";
COMMIT;

근데 적용하려는 테이블에 데이터가 없다면 문제없겠지만, 이미 아래와 같이 데이터가 있다면 어떻게 해야 할까

데이터를 reset 하기는 싫으니 기존 PENDING 값을 RUNNING 값으로 UPDATE하는 SQL을 추가로 작성해 보자.

BEGIN;
CREATE TYPE "batch_status_enum_new" AS ENUM ('RUNNING', 'FAILED', 'COMPLETED');
ALTER TABLE "batch_history" ALTER COLUMN "status" TYPE "batch_status_enum_new" USING ("status"::text::"batch_status_enum_new");
ALTER TYPE "batch_status_enum" RENAME TO "batch_status_enum_old";
ALTER TYPE "batch_status_enum_new" RENAME TO "batch_status_enum";
DROP TYPE "batch_status_enum_old";
COMMIT;

UPDATE "batch_history" SET "status" = 'RUNNING' WHERE "status" = 'PENDING';

4. 마이그레이션 파일 DB에 적용

마이그레이션 파일도 수정했겠다 이제 DB에 적용을 해보자.

DATABASE_URL='적용할 DB URL' npx prisma migrate dev

실패했다.

에러 내용을 더 자세하게 확인하기 위해서는 postgresql의 conf 파일을 설정하는 과정이 필요한데 해당 방법은 잘 정리되어 있는 글들이 많아 이 글에서는 다루지 않는다.

변경된 Enum 값에는 PENDING 값이 올 수 없다고 하는데 이제 어떻게 해야 할까

먼저, 성공한다면 어떻게 되는지 간단하게 알 필요가 있다.

npx prisma migrate dev를 수행하면 프로젝트에 있는 prisma의 마이그레이션 히스토리와 실제 DB에 적용된 마이그레이션 히스토리를 비교하여 적용되지 않은 파일들을 적용한다.

새로운 테스트 데이터베이스를 생성한다면 생성한 데이터베이스에는 히스토리가 없고 프로젝트 히스토리는 있기 때문에 위 명령어로 테이블들을 모두 세팅할 수 있다.

현재처럼 프로젝트에 새로운 마이그레이션 파일이 하나 생성되어 있고, DB에는 적용되어 있지 않다면 DB에 적용하고 히스토리를 남기게 된다.

DB 히스토리를 보기 위해 SELECT * FROM "_prisma_migrations"쳐보자.

성공한다면 부가적인 정보들과 finished_at 컬럼에 마친 시간, migration_name 컬럼에는 위에서 생성한 마이그레이션 파일의 이름이 저장된다.

이제 다시 돌아와서 실패했을 때 어떻게 히스토리가 생성됐는지 봐보자.

마이그레이션이 진행되다 실패했기 때문에 finished_at 컬럼이 null로 되어있는 것을 볼 수 있다.

실패한 마이그레이션에서 스키마를 다시 되돌리거나 데이터를 변경해야 할 내용이 없기에 해당 마이그레이션을 다시 적용할 수 있게 롤백 처리만 하자.

해당 과정은 각자의 마이그레이션 내용에 따라 문서를 꼭 참고하여 상황에 맞게 적용해야 한다. - 참고

DATABASE_URL='적용할 DB URL' npx prisma migrate resolve --rolled-back 롤백할 마이그레이션 파일 이름

그리고 다시 히스토리 테이블을 조회해 보면 rolled_back_at 컬럼에 롤백한 시간이 입력되고 해당 마이그레이션 파일을 다시 적용할 수 있게 된다.

단순히 DB 히스토리에 마킹만 해주는 것이기 때문에 오류가 나기 전으로 되돌렸다 혹은 정상적으로 고쳤다고 알려준다고 생각하면 될 것 같다.

그리고 적용하기 전에 실패했던 부분에서 다시 오류가 나지 않게 SQL 문을 수정해 주자.

BEGIN;
CREATE TYPE "batch_status_enum_new" AS ENUM ('PENDING','RUNNING', 'FAILED', 'COMPLETED');
ALTER TABLE "batch_history" ALTER COLUMN "status" TYPE "batch_status_enum_new" USING ("status"::text::"batch_status_enum_new");
ALTER TYPE "batch_status_enum" RENAME TO "batch_status_enum_old";
ALTER TYPE "batch_status_enum_new" RENAME TO "batch_status_enum";
DROP TYPE "batch_status_enum_old";
COMMIT;

UPDATE "batch_history" SET "status" = 'RUNNING' WHERE "status" = 'PENDING';

-- AlterEnum
BEGIN;
CREATE TYPE "batch_status_enum_new" AS ENUM ('RUNNING', 'FAILED', 'COMPLETED');
ALTER TABLE "batch_history" ALTER COLUMN "status" TYPE "batch_status_enum_new" USING ("status"::text::"batch_status_enum_new");
ALTER TYPE "batch_status_enum" RENAME TO "batch_status_enum_old";
ALTER TYPE "batch_status_enum_new" RENAME TO "batch_status_enum";
DROP TYPE "batch_status_enum_old";
COMMIT;

다시 DATABASE_URL=적용할 DB URL npx prisma migrate deploy를 수행하여 재배포 해보자.

성공적으로 마쳤다!

오류

만약 DB에 있는 히스토리와 프로젝트 히스토리가 모두 이름까지 같고 수정한 기억이 없는데 npx prisma migrate dev를 수행하면 아래와 같은 내용이 나올 때가 있다.

status를 확인해 봐도 모두 업데이트되어 있다고 나오는데, 이때는 히스토리의 checksum을 변경해서 해결할 수 있다.

이유를 자세히는 모르지만, 추측으론 마이그레이션 파일이 수정됐는지 판단을 마이그레이션 파일의 내용이 아닌 DB _prisma_migrations 테이블의 checksum 필드를 통해서 한다고 하니 그러지 않을까 생각한다.

이때는 shasum -a 256 적용할 마이그레이션 파일 이름을 수행하고 출력되는 값을 _prisma_migrations 테이블에서 동일한 마이그레이션 파일의 checksum 컬럼에 넣어주면 된다.

마치면서

Prisma에서는 prisma migrate라는 도구로 쉽게 마이그레이션을 수행할 수 있도록 해주는 것 같다.

다만, 마이그레이션이 부분적으로 성공하고 실패했을 때 잘 대처해야 하는 느낌이 든다.

테스트 DB에서 먼저 진행하고, 다른 개발 환경에 적용하는 것이 필수인 것 같다.

참고

Understanding Prisma Migrate
Make Prisma Ignore a Migration Change

profile
해결한 문제는 그때 기록하자

0개의 댓글