K

데이터베이스 연결하기

앱 전용 Postgres DB를 켜고, 코드에서 읽고 쓰고, 콘솔에서 표로 확인하는 방법이에요. 템플릿 앱은 이미 켜져 있어요.

주문 목록이나 회원 정보처럼 앱이 다루는 데이터는 앱 전용 데이터베이스에 저장해요.
템플릿으로 만든 앱은 이미 켜져 있어서, 바로 표를 만들고 읽고 쓰면 돼요.

만들어지는 건 표준 Postgres예요. 그래서 postgres · pg · Prisma · Drizzle 등 평소 쓰던 도구를 그대로 써요.

템플릿으로 만든 앱이라면

Next.js · Astro 템플릿은 아래가 이미 되어 있어요.

준비된 것내용
axhub.yaml 선언database: engine: postgres가 들어 있어요
접속 코드lib/db.tsdb()가 접속을 맡아요
표 만들기같은 파일의 ensureSchema()CREATE TABLE을 적어요
읽고 쓰기
import { db, ensureSchema } from '@/lib/db';

await ensureSchema();
const rows = await db()`SELECT * FROM todos WHERE user_key = ${userKey}`;

값을 ${...}로 넘기면 알아서 안전하게 처리돼요 — 문자열을 직접 이어 붙이지 마세요.

표를 새로 만들거나 컬럼을 늘리려면 ensureSchema() 안의 CREATE TABLE IF NOT EXISTS 블록을 늘리면 돼요. 평범한 SQL이에요.

내 컴퓨터에서 개발할 때

템플릿에 Postgres를 띄우는 명령이 들어 있어요.

npm run db:up      # 로컬 Postgres 시작
npm run db:reset   # 데이터까지 지우고 새로 시작

.env.localDATABASE_URL이 이 로컬 DB를 가리켜요. 배포하면 AxHub가 넣어 주는 값으로 바뀌고, 코드는 그대로예요.

직접 켜는 경우

템플릿을 안 썼다면 axhub.yaml에 두 줄을 넣고 배포해요.

axhub.yaml
database:
  engine: postgres

engine에는 현재 postgres만 넣을 수 있어요. 파일을 고치는 대신 터미널에서 켜려면 axhub apps raw-db enable --app <앱> --execute도 같은 효과예요.

다음 배포에서 앱 전용 DB가 만들어지고, 접속 정보가 환경변수로 들어와요.

환경변수용도
DATABASE_URL평소 데이터를 읽고 쓸 때 쓰는 주소예요. 접속을 모아서 관리해 주는 중계기(커넥션 풀러)를 거쳐요
DIRECT_DATABASE_URL표 구조를 바꾸는 작업(마이그레이션)처럼, 중계기 없이 직접 붙어야 하는 일에 써요
pg 드라이버
import { Pool } from 'pg';

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const { rows } = await pool.query('SELECT * FROM orders WHERE done = $1', [false]);
Prisma (schema.prisma)
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")        // 쿼리
  directUrl = env("DIRECT_DATABASE_URL") // 마이그레이션
}

prepare: false가 필요할 수 있어요. DATABASE_URL이 거치는 중계기는 prepared statement를 지원하지 않아서, postgres 같은 드라이버는 이 옵션을 꺼야 동작해요. 로컬에서는 잘 되다가 배포 후에만 실패한다면 이걸 확인하세요.

postgres(process.env.DATABASE_URL, { prepare: false })

production(실제 서비스)과 staging(시험용)은 서로 다른 DB를 받아요. 각 환경에 배포하면 그 환경의 DB가 만들어지고, 서로의 데이터가 섞이지 않아요.

콘솔에서 표로 보기

데이터가 실제로 어떻게 쌓였는지는 앱 콘솔 → 리소스 탭 → 테이블에서 그대로 조회할 수 있어요.

터미널에서는 이렇게 봐요.

axhub tables db-list --app demo                 # 테이블 목록
axhub tables db-rows orders --app demo          # 행 조회
axhub tables db-rows orders --app demo --environment staging

앱 코드 안에서는 SDKsdk.apps.rawDb.tables(appId) · sdk.apps.rawDb.tableRows(appId, 'orders')를 부르면 같은 걸 읽어요 — SDK 설정은 SDK 설치하기에서 해요.

세 곳 모두 읽기 전용이라 여기서 데이터를 고칠 수는 없어요. 그리고 앱 소유자가 열람 정책으로 잠가 둔 앱이라면, 관리자라도 콘솔 · 터미널에서 행 내용 열람이 제한될 수 있어요.

알아두면 좋은 것

  • 화면만 있는 앱에는 못 붙여요 — 정적 호스팅 앱은 계속 돌고 있는 서버가 없어서 접속 정보를 넣을 곳이 없어요. 선언한 채 배포하면 배포가 명확히 실패해요.
  • 선언을 지워도 DB는 사라지지 않아요 — 데이터와 주입은 유지돼요. 정말 끄려면 axhub apps raw-db disable --app <앱> --execute를 쓰세요. 이때도 표와 데이터는 그대로 남고 접속만 끊겨요 — 다시 켜면 새 비밀번호로 접속 정보가 다시 들어와요.
  • DATABASE_URL은 직접 덮어쓸 수 없어요 — DB를 켠 앱에서는 AxHub가 넣어 주는 값이 항상 이겨요. 같은 이름으로 저장한 환경변수는 무시돼요. (DB를 켜지 않은 앱이라면 외부 DB 주소를 자유롭게 넣어도 돼요.)
  • 이미 떠 있는 앱에는 소급 주입되지 않아요 — 켠 뒤 첫 배포부터 환경변수가 들어가요.
  • SDK에는 행 쓰기 API가 없어요 — 쓰기는 앱 런타임의 DATABASE_URL로 해요.

이렇게 보이면 성공

배포가 끝나고, 앱 콘솔 → 리소스 탭 → 테이블에서 내 앱의 테이블과 행이 표로 보이면 성공이에요. 앱 코드가 DATABASE_URL로 읽고 쓴 내용이 그대로 나타나요.

다음은 스토리지 연결하기에서 이미지 · 첨부파일 둘 곳을 만들어요.