GitHub Actions에서 Railway SQLite 데이터베이스를 쿼리하는 방법

발행: (2025년 12월 4일 오전 05:44 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

왜 SSH인가?

Railway은 SQLite 데이터베이스를 인터넷에 노출하지 않으므로, 데이터베이스 파일은 배포된 컨테이너 내부 디스크에 존재합니다. SSH가 유일하게 접근할 수 있는 방법입니다.

railway ssh "node -e \"const db = require('better-sqlite3')('./data/my-demo.db'); const all = db.prepare('SELECT * FROM signups ORDER BY created_at DESC').all(); console.log(JSON.stringify(all)); db.close();\""

문제 1: 올바른 토큰 얻기

RAILWAY_TOKEN프로젝트 토큰이어야 하며, 계정 토큰이 아니어야 합니다.

  1. Railway 프로젝트 대시보드로 이동합니다.
  2. Settings → Tokens 로 이동합니다.
  3. Generate Token 을 클릭합니다.
  4. 토큰을 RAILWAY_TOKEN이라는 이름의 GitHub Actions 비밀 변수로 추가합니다.

문제 2: 인터랙티브 로그인은 CI에서 작동하지 않음

railway login --browserless 를 실행하면 여전히 수동으로 토큰을 붙여넣어야 하므로 실패합니다.

해결 방법: 로그인 명령을 모두 제거합니다. Railway CLI는 RAILWAY_TOKEN 환경 변수가 설정되어 있으면 자동으로 이를 사용합니다.

문제 3: 서비스 이름 vs. 서비스 ID

CI에서 --project 나 서비스 ID를 사용하면 CLI가 RAILWAY_TOKEN을 무시하고 인터랙티브 로그인을 요구합니다.

해결 방법: --service와 함께 친숙한 서비스 이름만 사용하고 --project는 생략합니다.

railway ssh --service your-service-name --environment production \
  "node -e \"const db = require('better-sqlite3')('./path/to/database.db'); const rows = db.prepare('SELECT * FROM your_table').all(); console.log(JSON.stringify(rows)); db.close();\""

문제 4: 따옴표 이스케이프

쉘 → GitHub Actions → YAML 여러 단계의 인용이 명령을 깨뜨렸습니다.

해결 방법: 로직을 별도의 TypeScript 스크립트로 옮기고 워크플로우에서 호출합니다.

// scripts/query-railway.ts
import { execSync } from 'child_process';

interface Record {
  id: number;
  email: string;
  created_at: string;
}

function queryRailway(): Record[] {
  const serviceName = process.env.RAILWAY_SERVICE_NAME || 'my-app-service';
  const environment = process.env.RAILWAY_ENVIRONMENT || 'production';
  const token = process.env.RAILWAY_TOKEN;

  if (!token) {
    console.error('RAILWAY_TOKEN not set');
    process.exit(1);
  }

  const command = `railway ssh --service ${serviceName} --environment ${environment} "node -e \\"const db = require('better-sqlite3')('./data/database.db'); const rows = db.prepare('SELECT * FROM users').all(); console.log(JSON.stringify(rows)); db.close();\\""`; 

  try {
    const output = execSync(command, { encoding: 'utf-8' });
    const lines = output.trim().split('\n');
    for (const line of lines) {
      if (line.trim().startsWith('[') || line.trim().startsWith('{')) {
        return JSON.parse(line.trim());
      }
    }
    console.error('Output from Railway:', output);
    throw new Error('Could not find JSON output from Railway');
  } catch (error) {
    console.error('Failed to query Railway:', error);
    process.exit(1);
  }
}

const records = queryRailway();
console.log(`Successfully fetched ${records.length} records`);

스크립트를 실행하는 워크플로우 단계

- name: Query db
  env:
    RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
    RAILWAY_SERVICE_NAME: my-app-service
    RAILWAY_ENVIRONMENT: production
  run: npx tsx scripts/query-railway.ts

전체 워크플로우

name: My workflow

on:
  workflow_dispatch:
  schedule:
    - cron: '0 */6 * * *'

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Install Railway CLI
        run: npm install -g @railway/cli

      - name: Query db
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
          RAILWAY_SERVICE_NAME: my-app-service
          RAILWAY_ENVIRONMENT: production
        run: npx tsx scripts/query-railway.ts
Back to Blog

관련 글

더 보기 »

SaaS IA 뉴스

SaaS IA 뉴스용 커버 이미지 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazon...

혼돈에서 코드로: ALPHALABS

밤새도록 나를 괴롭힌 문제 나는 누구나 AI 트레이딩 에이전트를 만들고, 전략을 백테스트하며, 성과를 입증할 수 있는 플랫폼을 구축하고 싶었다.