deep-dive2025-04-30·9·103/348

Vercel Postgres 연결 설정 완벽 가이드

Vercel Postgres를 사용하여 PostgreSQL 데이터베이스를 연결하고 활용하는 방법을 다룹니다.

Introduction

Vercel Postgres는 Vercel 플랫폼에서 네이티브로 지원하는 PostgreSQL 데이터베이스입니다. 이번 포스트에서는 Vercel Postgres를 설정하고 연결하는 방법을 살펴보겠습니다.

Environment

# Vercel Postgres 패키지
npm list @vercel/postgres
# @vercel/postgres@0.5.0

# ORM (선택)
npm list drizzle-orm
# drizzle-orm@0.29.0

# 환경 변수
POSTGRES_URL=postgres://...
POSTGRES_PRISMA_URL=postgres://...
POSTGRES_URL_NON_POOLING=postgres://...

Problem

Vercel 환경에서 PostgreSQL 연결에 문제가 발생했습니다:

# 연결 에러
Error: connect ECONNREFUSED 127.0.0.1:5432

# 연결 풀 고갈
Error: Too many connections

# 타임아웃
Error: Connection timeout

Solution

1. Vercel Postgres 설정

# Vercel 대시보드에서 설정
1. Storage 탭 선택
2. Create Database 클릭
3. Postgres 선택
4. 연결 문자열 복사

2. 기본 연결 설정

// lib/db.js
import { sql } from '@vercel/postgres';

export async function query(text, params) {
    const start = Date.now();
    const result = await sql.query(text, params);
    const duration = Date.now() - start;

    console.log('Executed query', { text, duration, rows: result.rowCount });
    return result;
}

3. Drizzle ORM 설정

// drizzle.config.js
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
    schema: './lib/schema.js',
    out: './drizzle',
    dialect: 'postgresql',
    dbCredentials: {
        url: process.env.POSTGRES_URL,
    },
});
// lib/schema.js
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
    email: text('email').notNull().unique(),
    createdAt: timestamp('created_at').defaultNow(),
});

4. 연결 관리

// lib/db.js
import { sql } from '@vercel/postgres';

class Database {
    constructor() {
        this.pool = null;
    }

    async connect() {
        if (!this.pool) {
            this.pool = sql;
        }
        return this.pool;
    }

    async query(text, params) {
        const client = await this.connect();
        const start = Date.now();

        try {
            const result = await client.query(text, params);
            const duration = Date.now() - start;

            console.log('Query executed', {
                text: text.substring(0, 50),
                duration: `${duration}ms`,
                rows: result.rowCount,
            });

            return result;
        } catch (error) {
            console.error('Query error', { text, error: error.message });
            throw error;
        }
    }

    async transaction(callback) {
        const client = await this.connect();

        try {
            await client.query('BEGIN');
            const result = await callback(client);
            await client.query('COMMIT');
            return result;
        } catch (error) {
            await client.query('ROLLBACK');
            throw error;
        }
    }
}

export const db = new Database();

5. API 라우트에서 사용

// pages/api/users.js
import { db } from '../../lib/db';

export default async function handler(req, res) {
    try {
        switch (req.method) {
            case 'GET':
                const users = await db.query('SELECT * FROM users');
                return res.status(200).json(users.rows);

            case 'POST':
                const { name, email } = req.body;
                const result = await db.query(
                    'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
                    [name, email]
                );
                return res.status(201).json(result.rows[0]);

            default:
                res.setHeader('Allow', ['GET', 'POST']);
                return res.status(405).end();
        }
    } catch (error) {
        console.error('API error:', error);
        return res.status(500).json({ error: 'Internal Server Error' });
    }
}

6. 연결 상태 확인

// pages/api/health.js
import { sql } from '@vercel/postgres';

export default async function handler(req, res) {
    try {
        const result = await sql`SELECT NOW()`;
        res.status(200).json({
            status: 'healthy',
            timestamp: result.rows[0].now,
        });
    } catch (error) {
        res.status(500).json({
            status: 'unhealthy',
            error: error.message,
        });
    }
}

Lessons Learned

  1. 환경 변수 관리: 연결 문자열은 환경 변수로 관리하세요
  2. 연결 풀 설정: Vercel Postgres는 자동으로 연결 풀을 관리합니다
  3. 트랜잭션 활용: 여러 작업을 원자적으로 처리하기 위해 트랜잭션을 사용하세요
  4. 에러 핸들링: 연결 에러를 적절히 처리하세요
  5. 모니터링: 쿼리 성능을 지속적으로 모니터링하세요

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.