요약

import { PrismaMariaDb } from '@prisma/adapter-mariadb'; // prisma 6.11.0 이상

const prisma = new PrismaClient({
  adapter: new PrismaMariaDb({
    host: process.env.DATABASE_HOST,
    user: process.env.DATABASE_USER,
    password: process.env.DATABASE_PASSWORD,
    database: process.env.DATABASE_NAME,
    timezone: "Z", // SET time_zone='+00:00'
  })
});

배경

// MySQL
SELECT @@global.time_zone, @@session.time_zone, @@system_time_zone; -- SYSTEM SYSTEM KST
SELECT now(); -- 2025-12-17 13:00:00

// TypeScript
console.log(process.env.TZ); // Asia/Seoul
const now = new Date();
console.log(now.toString());
// Wed Dec 17 2025 13:00:00 GMT+0900 (Korean Standard Time)

// Primsa
const result: { "NOW()": Date }[] = await prisma.$queryRaw`SELECT NOW()`;
console.log(result[0]["NOW()"].toString());
// Wed Dec 17 2025 22:00:00 GMT+0900 (Korean Standard Time)

/* T_T!!!!! 오 이런.. new Date()와 SELECT NOW()가 9시간이나 차이가 나네요.. */

Prisma의 멘탈 모델

Prisma는 DB의 타임존을 고려하지 않습니다.
Prisma의 멘탈 모델은 DB가 UTC로 동작 중이라고 생각합니다.
그래서 CRUD시 파라미터와 결과를 타임존 오프셋 만큼 자동으로 보정해줍니다.
여기서 문제가 계속 발생합니다.

  • 조회시 타임존 오프셋만큼 시간이 줄어듬 (sub)
  • 입력 및 수정시 타임존 오프셋만큼 시간이 늘어남 (add)

해결 방법

이를 해결하기 위해 Prisma Client Extensions으로 간단한 미들웨어를 만들어 사용하거나,
schema.prisma에서 @default(now()) 대신 @default(dbgenerated("NOW()")) 를 사용해야 했습니다.
관련하여 Prisma 내부에서 DB의 타임존 관련 옵션을 지원하지 않기 때문입니다.

Prisma의 기본 내장 드라이버를 통해 쿼리하는 모습

→ Prisma의 기본 내장 드라이버를 통해 쿼리하는 모습

Driver Adapter

그리고 시간이 지나,
Prisma 6.11에 와서 Driver Adapter가 MySQL을 (정확히는 MariaDB를) 지원하기 시작했습니다!

이것으로 Prisma에서는 지원하지 않지만,
mariadb-connector-nodejs에서는 지원하는 타임존 관련 옵션을 사용할 수 있게 되었습니다.
(해당 패키지를 Driver Adapter 형태로 래핑하여 Prisma가 직접 제공하고 있습니다.)

Prisma의 Driver Adapter를 통해 쿼리하는 모습

→ Prisma의 Driver Adapter를 통해 쿼리하는 모습

설명

PrismaMariaDb의 타임존 옵션을 Z로 주게 되면,
매 쿼리의 시작에 SET time_zone='+00:00'가 추가됩니다.

이것으로 MySQL의 @@global.time_zone, @@session.time_zone, @@system_time_zone과 무관하게
UTC(Zulu Time) 기준으로 쿼리가 실행됩니다.
이제 Prisma의 멘탈 모델과 일치하고, 여러 문제들이 해결됩니다.

여러 미들웨어를 추가하지 않아도 되고,
prisma가 생성하는 schema.prisma@default(now())를 매번 수정하지 않고 그대로 사용할 수 있어 편리합니다.
좋네요!