Railway: 에이전트 시대의 배포 플랫폼

Railway: 에이전트 시대의 배포 플랫폼

작은 서비스를 하나 만들었는데, 막상 배포하려고 하면 손이 멈출 때가 있습니다. 서버는 어디에 띄우지? 데이터베이스는 누가 만들지? 환경 변수는 어디에 넣지? 로그는 어떻게 보지? Dockerfile까지 써야 하나? 이런 질문들이 한꺼번에 밀려오죠.

요즘은 여기에 질문이 하나 더 붙습니다. AI 코딩 에이전트가 이 배포 환경을 얼마나 잘 이해할 수 있을까? 사람 혼자 터미널에서 손으로 맞추는 시대라면 대시보드 몇 번 클릭해도 괜찮았습니다. 하지만 Claude Code, Codex, Cursor 같은 코딩 에이전트와 함께 일하려면 배포 흐름도 코드와 문서로 설명할 수 있어야 합니다.

Railway는 이 지점에서 꽤 흥미로운 선택지입니다. 웹 앱, 워커, 데이터베이스, 볼륨 같은 서비스를 하나의 프로젝트 안에서 관리하고, GitHub 푸시나 CLI 명령으로 배포할 수 있습니다. 여기에 railway.json, 환경 변수 참조, 로그, MCP 서버 배포까지 엮으면 “사람이 쓰기 쉬운 플랫폼”을 넘어 “에이전트가 따라오기 쉬운 플랫폼”으로 볼 수 있어요.

이번 글에서는 Railway의 기본 개념부터 실제 배포 흐름, 그리고 MCP(Model Context Protocol) 서버와 같은 agentic(에이전트형) 개발 방식까지 이어서 살펴보겠습니다.

Railway가 해결하는 문제

Railway를 한 문장으로 말하면 애플리케이션과 필요한 인프라를 한 프로젝트 안에서 묶어 배포하는 플랫폼입니다. 코드를 GitHub 저장소에 올려두면 Railway가 빌드하고, 실행하고, 로그를 보여주고, 필요한 경우 PostgreSQL 같은 데이터베이스도 같은 프로젝트 안에 붙여줍니다.

전통적인 클라우드에서는 작은 웹 서비스를 하나 띄우려고 해도 여러 개념을 따로 배워야 했습니다. 컨테이너 레지스트리, 로드 밸런서, 보안 그룹, 데이터베이스, 시크릿 매니저, 로그 수집기가 전부 따로 있었죠. Railway는 이런 요소를 서비스 단위로 단순화합니다.

Railway 프로젝트 안에는 여러 서비스가 들어갈 수 있습니다. 예를 들어 API 서버 하나, PostgreSQL 데이터베이스 하나, 백그라운드 워커 하나를 같은 프로젝트 캔버스에 놓고 서로 연결하는 식입니다. 서비스마다 환경 변수와 배포 설정을 따로 가질 수 있고, 같은 프로젝트 안의 다른 서비스 값을 참조할 수도 있어요.

작은 팀이나 개인 프로젝트에서는 판단해야 할 것이 줄어든다는 점이 큽니다. “일단 배포하고 피드백을 받자”는 단계에서는 인프라 설계보다 제품 검증이 더 중요하잖아요. Railway는 이때 필요한 최소한의 운영 범위만 열어줍니다.

GitHub에 푸시하면 배포된다

가장 흔한 흐름은 GitHub 저장소를 Railway 프로젝트에 연결하는 방식입니다. 저장소를 연결하면 특정 브랜치에 푸시될 때마다 Railway가 새 배포를 만듭니다. GitHub Actions를 직접 작성하지 않아도 기본적인 빌드와 배포 파이프라인이 생기는 셈이죠.

로컬에서 CLI로 시작할 수도 있습니다. 공식 문서에서는 Railway CLI로 프로젝트를 초기화하고 배포하는 흐름을 이렇게 보여줍니다.

railway login
railway init
railway up

이미 Railway 프로젝트가 있고 현재 디렉터리를 그 프로젝트에 연결하려면 다음 흐름이 더 자연스럽습니다.

railway link
railway up -d

CLI는 단순한 “수동 배포 버튼”이 아닙니다. 에이전트와 함께 작업할 때는 의미가 더 커집니다. 코딩 에이전트는 브라우저 대시보드를 직접 이해하기보다 터미널 명령어와 설정 파일을 더 안정적으로 다룹니다. railway link, railway up, railway logs 같은 명령이 문서화되어 있으면 에이전트가 배포 상태를 확인하고 실패 원인을 좁히는 데 도움을 줄 수 있어요.

물론 프로덕션에서는 무조건 로컬에서 railway up을 치기보다 GitHub 연결을 기본값으로 두는 편이 좋습니다. 코드는 Git이 진실의 원천이고, 배포는 커밋에서 출발해야 추적이 쉬워지거든요. CLI는 초기 설정, 임시 환경, 디버깅, 개인 프로젝트 배포에 쓰고, 팀 단위 운영은 GitHub 자동 배포로 묶는 식이 균형이 좋습니다.

환경 변수는 플랫폼에 둔다

배포 플랫폼을 쓸 때 가장 많이 터지는 문제가 환경 변수입니다. 로컬에서는 .env 파일 덕분에 잘 되는데, 배포하면 DATABASE_URL이 없어서 죽는 경우가 흔하죠.

Railway에서는 서비스마다 Variables를 설정할 수 있습니다. 예를 들어 앱 서비스에서 같은 프로젝트 안의 PostgreSQL 서비스 주소를 참조하려면 이렇게 둘 수 있습니다.

DATABASE_URL=${{Postgres.DATABASE_URL}}

이 문법의 핵심은 값을 복사하지 않는다는 점입니다. 데이터베이스 서비스가 가진 DATABASE_URL을 앱 서비스가 참조합니다. 데이터베이스 주소나 비밀번호가 바뀌어도 앱 서비스의 변수 정의는 같은 의미를 유지하죠.

에이전트 친화적인 개발에서는 이런 참조가 특히 중요합니다. .env 파일에 실제 비밀 값을 넣어두면 에이전트가 파일을 읽거나 수정하는 과정에서 의도치 않게 노출될 수 있습니다. 반대로 저장소에는 필요한 변수 이름만 문서화하고, 실제 값은 Railway Variables에 두면 역할이 분리됩니다.

docs/env.md
# 환경 변수

DATABASE_URL
Railway PostgreSQL 서비스의 DATABASE_URL을 참조합니다.
프로덕션에서는 Railway Variables에 ${{Postgres.DATABASE_URL}} 형태로 설정합니다.

MCP_AUTH_TOKEN
원격 MCP 서버 호출을 보호하는 Bearer 토큰입니다.
로컬 .env 예시는 공유할 수 있지만 실제 값은 Railway에만 둡니다.

Railway는 GitHub 배포에서 RAILWAY_GIT_COMMIT_SHA, RAILWAY_GIT_BRANCH, RAILWAY_GIT_REPO_NAME 같은 배포 메타데이터 변수도 제공합니다. 애플리케이션 로그에 커밋 해시를 남기거나, /health 응답에 현재 버전을 표시하고 싶을 때 유용합니다.

src/version.ts
export const version = {
  commit: process.env.RAILWAY_GIT_COMMIT_SHA ?? "local",
  branch: process.env.RAILWAY_GIT_BRANCH ?? "local",
};

이런 작은 정보가 운영에서 생각보다 큰 차이를 만듭니다. 장애가 났을 때 “지금 떠 있는 코드가 어떤 커밋인가?”를 바로 알 수 있으니까요.

Dockerfile과 railway.json

Railway는 프로젝트를 자동으로 감지해서 빌드할 수 있지만, 배포 환경을 명시적으로 고정하고 싶다면 Dockerfile을 쓰는 편이 낫습니다. 특히 네이티브 패키지가 있거나, 특정 런타임 버전이 중요하거나, 로컬과 프로덕션의 실행 방식을 맞추고 싶다면 Dockerfile이 가장 직접적인 문서가 됩니다.

작은 Bun 기반 서버라면 이런 형태로 시작할 수 있습니다.

Dockerfile
FROM oven/bun:1

WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

COPY . .

CMD ["bun", "run", "start"]

여기서 조심할 점은 웹 서비스와 워커를 구분하는 겁니다. 웹 서비스는 외부 요청을 받아야 하므로 애플리케이션이 Railway가 제공하는 포트에서 HTTP 서버를 열어야 합니다. 반면 큐 처리기, Discord 게이트웨이 봇, 스케줄러 같은 프로세스는 포트를 열 필요가 없는 워커에 가깝습니다. 이 차이를 헷갈리면 헬스 체크나 라우팅에서 막히기 쉽습니다.

Railway 설정을 저장소에 남기고 싶다면 railway.json을 사용할 수 있습니다. 공식 문서의 예시처럼 빌더와 Dockerfile 경로, 배포 전 명령을 코드로 적어둘 수 있어요.

railway.json
{
  "$schema": "https://railway.com/railway.schema.json",
  "build": {
    "builder": "DOCKERFILE",
    "dockerfilePath": "Dockerfile"
  },
  "deploy": {
    "preDeployCommand": "bunx prisma migrate deploy"
  }
}

이 파일은 사람에게도 좋지만 에이전트에게 특히 좋습니다. “배포 전에 마이그레이션을 돌려야 한다”는 지식이 대시보드 어딘가에만 있으면 에이전트는 놓치기 쉽습니다. 반대로 저장소의 설정 파일에 있으면 코드 수정과 함께 읽고 추론할 수 있죠.

다만 모든 것을 preDeployCommand에 몰아넣는 건 피해야 합니다. 마이그레이션, 시드, 캐시 워밍처럼 실패했을 때 영향이 큰 작업은 여러 번 실행해도 결과가 꼬이지 않는 멱등성을 갖춰야 합니다. 에이전트가 배포 자동화를 만져도 안전하려면 배포 스크립트 자체가 반복 실행에 강해야 해요.

Railway에서 MCP 서버 배포하기

MCP 서버는 에이전트에게 외부 시스템을 열어주는 작은 백엔드입니다. 로컬 파일 시스템을 읽거나, GitHub 이슈를 다루거나, 데이터베이스 스키마를 조회하거나, 사내 API를 호출하는 도구를 MCP로 노출할 수 있죠. 클로드 코드에서 MCP 서버를 연동하는 방법을 보면 로컬 서버는 stdio, 원격 서버는 HTTP 방식으로 연결하는 흐름이 나옵니다.

Railway는 원격 MCP 서버를 띄우기에 잘 맞습니다. 이유는 단순합니다. MCP 서버도 결국 HTTP 서버이고, 인증 토큰과 로그와 배포 파이프라인이 필요하기 때문입니다. Railway는 이 셋을 작은 서비스 단위로 다룰 수 있게 해줘요.

예를 들어 내부 도구를 감싼 MCP 서버를 Railway에 올린다고 해볼까요. 외부 URL이 생기는 순간 인증이 필수입니다. Railway 공식 MCP 서버 가이드도 Bearer 토큰으로 엔드포인트를 보호하는 예시를 보여줍니다.

src/auth.ts
export function requireBearerToken(request: Request) {
  const expected = process.env.MCP_AUTH_TOKEN;
  const actual = request.headers.get("authorization")?.replace("Bearer ", "");

  if (!expected || actual !== expected) {
    return new Response("Unauthorized", { status: 401 });
  }

  return null;
}

서버 코드에서는 MCP 요청을 처리하기 전에 이 검사를 통과시키면 됩니다. 전송 방식이 Server-Sent Events든 Streamable HTTP든 핵심은 같습니다. 원격 MCP 서버는 공개 인터넷에 떠 있으므로, “URL을 아는 사람만 쓰겠지”라고 생각하면 안 됩니다.

에이전트 쪽 설정은 프로젝트에 공유 가능한 형태로 남겨두는 것이 좋습니다. 예를 들어 .mcp.json에는 서버 주소와 헤더 구조만 두고, 실제 토큰은 각자의 환경 변수에서 읽게 할 수 있습니다.

.mcp.json
{
  "mcpServers": {
    "internal-tools": {
      "type": "http",
      "url": "https://internal-tools.up.railway.app/mcp",
      "headers": {
        "Authorization": "Bearer ${MCP_AUTH_TOKEN}"
      }
    }
  }
}

이렇게 하면 팀원은 같은 MCP 서버를 쓰되, 토큰은 각자의 로컬 환경이나 비밀 관리 도구에서 주입할 수 있습니다. 에이전트가 프로젝트를 열었을 때도 “이 프로젝트에는 internal-tools MCP 서버가 필요하구나”를 바로 알 수 있고요.

에이전트형 개발을 위한 저장소 구조

Railway를 에이전트와 함께 잘 쓰려면 대시보드 설정만 믿으면 안 됩니다. 에이전트가 읽을 수 있는 정보는 가능한 한 저장소에 있어야 합니다. 이 관점은 Harness Engineering에서 다룬 내용과도 이어집니다. 좋은 프롬프트보다 좋은 작업 환경이 결과를 더 크게 바꿉니다.

가장 먼저 AGENTS.mdCLAUDE.md에 배포 흐름을 짧게 적어두세요.

AGENTS.md
# 배포

- 플랫폼: Railway
- 프로젝트: acme-api
- 서비스: api, worker, Postgres
- 기본 배포: main 브랜치 GitHub 자동 배포
- 수동 배포: railway link 후 railway up -d
- 로그 확인: railway logs
- 필수 변수: DATABASE_URL, MCP_AUTH_TOKEN
- 배포 전 검증: bun run typecheck, bun run test

이 정도만 있어도 에이전트의 행동이 달라집니다. 배포 문제가 났을 때 무작정 코드를 바꾸기 전에 로그를 확인하고, 환경 변수를 의심하고, 타입 체크를 먼저 돌리는 식으로 흐름을 잡을 수 있거든요.

그다음은 실행 가능한 피드백 루프입니다. 에이전트는 말로 된 규칙보다 실패하는 명령어를 더 잘 따릅니다. package.json에 검증 명령을 모아두고, Railway 배포 전에도 같은 명령을 돌릴 수 있게 해두세요.

package.json
{
  "scripts": {
    "dev": "bun run src/index.ts",
    "start": "bun run src/index.ts",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "check": "bun run typecheck && bun run test"
  }
}

마지막으로 관측성입니다. Railway 로그는 에이전트가 운영 문제를 추적할 때 출발점이 됩니다. 애플리케이션 로그에는 요청 ID, 배포 커밋, 외부 API 실패 원인처럼 문제를 좁히는 데 필요한 정보를 남겨야 합니다.

src/logger.ts
export function logDeploymentInfo() {
  console.info("deployment", {
    commit: process.env.RAILWAY_GIT_COMMIT_SHA ?? "local",
    branch: process.env.RAILWAY_GIT_BRANCH ?? "local",
    service: process.env.RAILWAY_SERVICE_NAME ?? "local",
  });
}

에이전트에게 “Railway 로그 보고 원인 찾아줘”라고 맡기려면 로그가 사람이 봐도, 기계가 봐도 해석 가능한 형태여야 합니다. JSON 형태의 구조적 로그가 특히 좋습니다.

무엇을 Railway에 올리면 좋을까

Railway는 모든 문제의 답은 아닙니다. 정적 사이트만 있다면 Cloudflare Pages나 GitHub Pages가 더 단순할 수 있고, 엣지에서 아주 짧은 요청을 처리해야 한다면 Cloudflare Workers가 더 잘 맞을 수 있습니다. 반대로 복잡한 사내 네트워크, 엄격한 규정 준수, 세밀한 권한 분리가 필요하다면 AWS나 Google Cloud 같은 범용 클라우드를 써야 할 수도 있어요.

Railway가 빛나는 지점은 작지만 실제로 운영해야 하는 서비스입니다. API 서버, 웹훅 수신기, Slack이나 Discord 봇, 작은 백오피스, 프로토타입, MCP 서버, 데모용 SaaS 백엔드처럼요. 데이터베이스가 필요하고, 환경 변수가 필요하고, 로그를 봐야 하고, GitHub 푸시로 자동 배포되면 좋은 서비스라면 잘 맞습니다.

특히 MCP 서버는 Railway와 궁합이 좋습니다. 로컬 stdio MCP 서버는 개인 작업에는 편하지만 팀 전체가 같은 도구를 쓰기 어렵습니다. Railway에 HTTP MCP 서버로 올려두면 팀원과 에이전트가 같은 엔드포인트를 바라볼 수 있고, 토큰과 로그와 배포 이력도 중앙에서 관리할 수 있습니다.

다만 프로덕션 데이터에 접근하는 MCP 서버는 신중해야 합니다. 읽기 전용 계정을 기본으로 쓰고, 위험한 도구는 아예 노출하지 말고, 토큰은 짧게 회전할 수 있게 설계하세요. 에이전트에게 도구를 열어주는 일은 권한을 위임하는 일입니다. 편의성만 보고 권한을 넓게 주면 나중에 운영 리스크가 됩니다.

마치며

Railway를 단순히 “쉽게 배포하는 서비스”로만 보면 절반만 보는 셈입니다. 요즘 개발 흐름에서는 배포 플랫폼도 에이전트가 이해할 수 있어야 합니다. GitHub 푸시로 배포되고, 환경 변수 참조가 명확하고, railway.json으로 배포 규칙을 남길 수 있고, CLI로 상태를 확인할 수 있고, MCP 서버 같은 에이전트형 인프라를 HTTP 서비스로 띄울 수 있다는 점이 중요합니다.

처음 시작한다면 작은 API 서버 하나를 Railway에 올려보세요. 그다음 PostgreSQL을 붙이고, DATABASE_URL을 참조 변수로 연결하고, AGENTS.md에 배포 절차를 적어보면 됩니다. 여기까지 해두면 사람도 편하고 에이전트도 덜 헤맵니다.

MCP 서버를 직접 운영해보고 싶다면 Railway에 작은 HTTP 서버를 띄우고 Bearer 토큰 인증부터 넣어보세요. 최신 문서를 에이전트에게 주입하는 흐름이 궁금하다면 Context7 MCP 서버도 함께 보면 좋습니다.

더 자세한 내용은 Railway Vibe Coding Deploy 가이드Railway MCP 서버 가이드를 참고하세요.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord