Render에서 배포가 갑자기 안 되기 시작했다. 한 번도 아니고 두 번 연속으로, 서로 다른 이유로. 첫 번째는 Vite 빌드에서 패키지를 못 찾는다는 에러, 두 번째는 Rails 서버가 PostgreSQL에 소켓으로 연결하려다 죽는 오류였다. 둘 다 원인을 찾기까지 상당히 헤맸다.
첫 번째 오류: Cannot find package 'vite-plugin-ruby'
빌드 로그에 이런 에러가 찍혔다.
failed to load config from /opt/render/project/src/.../vite.config.ts
error during build:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'vite-plugin-ruby' imported from vite.config.ts
vite-plugin-ruby는 분명히 package.json의 dependencies에 들어있었다. devDependencies가 아니라 dependencies에.
"dependencies": {
"vite": "^5.4.21",
"vite-plugin-ruby": "^5.1.3",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
...
}
그런데 npm install --omit=dev를 쓰면 이 패키지가 설치가 안 된다. 왜?
npm lockfile의 dev 플래그 문제
package-lock.json을 직접 열어서 해당 항목을 확인했다.
"node_modules/vite-plugin-ruby": {
"version": "5.1.3",
"resolved": "...",
"integrity": "...",
"dev": true,
...
}
dev: true가 박혀있었다.
package.json에는 dependencies에 있지만, package-lock.json에는 "dev": true로 마킹된 것이다. 이게 왜 발생하냐면, 과거에 이 패키지가 devDependencies에 있다가 나중에 dependencies로 옮겨졌는데, lockfile이 제대로 재생성되지 않았기 때문이다.
핵심은 --omit=dev가 package.json이 아닌 package-lock.json의 dev 플래그를 기준으로 패키지를 제외한다는 것이다.
즉:
package.json:dependencies에 있음 → “프로덕션 패키지”package-lock.json:"dev": true→ “이건 dev 패키지임”npm install --omit=dev: lockfile 기준으로 건너뜀 → 설치 안 됨
이 두 파일이 불일치할 때 lockfile이 우선된다.
왜 이런 불일치가 생기나
npm은 lockfile을 생성할 때 각 패키지가 어떤 경로로 요구되는지 추적해서 dev 플래그를 결정한다. package.json에서 devDependencies → dependencies로 패키지를 옮겨도, lockfile을 명시적으로 재생성하지 않으면 기존 dev: true 플래그가 그대로 남는다.
npm install을 그냥 실행하면 되지 않냐고 할 수 있는데, 이 프로젝트는 로컬에서 pnpm을 쓰고 있었다 (pnpm-lock.yaml이 존재). npm으로 install을 시도하면 arborist 내부에서 에러가 난다.
npm error Cannot read properties of null (reading 'matches')
npm error A complete log of this run can be found in: ~/.npm/_logs/...
pnpm이 관리하는 node_modules의 심링크 구조와 npm의 dependency tree 계산이 충돌하는 것이다. 이 상태에서 npm install --package-lock-only도 같은 이유로 실패했다.
해결 방법
가장 빠른 수정은 빌드 스크립트에서 --omit=dev를 --include=dev로 바꾸는 것이다.
# 변경 전
npm install --omit=dev
# 변경 후
npm install --include=dev
--include=dev를 쓰면 lockfile의 dev 플래그와 무관하게 모든 패키지를 설치한다. 빌드 서버에서는 Vite, vite-plugin-ruby 같은 빌드 도구가 반드시 필요하므로 이 방식이 맞다.
근본적인 수정은 package.json을 올바르게 정리하고 lockfile을 재생성하는 것이다. 빌드 도구는 dependencies에 두는 게 맞다 (런타임이 아닌 빌드타임에 쓰이더라도, Render 같은 환경에서는 빌드 단계에 필요하므로).
| 패키지 | 올바른 위치 |
|---|---|
vite, vite-plugin-ruby | dependencies |
@sveltejs/vite-plugin-svelte | dependencies |
@tailwindcss/vite | dependencies |
vitest, jsdom | devDependencies |
@playwright/test | devDependencies |
두 번째 오류: PostgreSQL 소켓 연결 실패
빌드는 통과했는데 이번엔 배포 단계(update_failed)에서 죽었다. Rails 로그를 보니:
Unable to load application: ActiveRecord::ConnectionNotEstablished:
connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
No such file or directory
유닉스 소켓으로 PostgreSQL에 붙으려는 것이다. 이 에러는 DATABASE_URL이 없거나 호스트 없이 로컬 DB를 찾을 때 발생한다. Render에서는 반드시 TCP 연결로 외부 DB에 붙어야 한다.
왜 갑자기 DATABASE_URL이 없어졌나
PostgreSQL 16 → 18 업그레이드 유지보수가 있었다. Render는 이 과정에서 DB의 내부 호스트명을 변경하는 경우가 있다. 만약 DATABASE_URL이 Render의 “linked database” 기능으로 자동 주입되는 게 아니라, 수동으로 환경변수에 설정된 값이라면 업그레이드 후 예전 호스트명을 계속 가리킬 수 있다.
이 경우엔 환경변수가 아예 없어진 상태였다. 이전 DB 인스턴스에서 새 PG18 인스턴스로 마이그레이션되면서 서비스의 환경변수 연결이 끊어진 것으로 보인다.
해결 방법
Render 대시보드 → DB 서비스 → “Connect” 탭에서 Internal Database URL을 복사한다.
내부 URL 형식은 이렇다:
postgresql://user:password@dpg-xxxxxxxxxxxxxxxx-a/dbname
외부 URL과 다르게 호스트명에 .render.com 도메인이 없고, 같은 리전의 Render 서비스끼리만 이 주소로 통신할 수 있다. Render MCP가 있다면 직접 업데이트도 가능하다:
update_environment_variables(
serviceId: "srv-...",
envVars: [{ key: "DATABASE_URL", value: "postgresql://..." }],
replace: false
)
replace: false를 쓰면 기존 환경변수는 유지하고 DATABASE_URL만 덮어쓴다. Render의 PUT API는 기본적으로 전체 교체(replace: true)가 되므로 조심해야 한다.
이번에 확인한 Render 배포 흐름
git push
→ build 단계 (render-build.sh 실행)
→ bundle install
→ npm install
→ vite build
→ pre-deploy 단계 (서버 네트워크 사용 가능)
→ rails db:migrate
→ update 단계 (새 인스턴스 기동)
→ puma 시작
→ DB 연결 확인
→ live (헬스체크 통과)
빌드 컨테이너에서는 내부 DB 호스트명을 못 쓰는 케이스가 있어서 db:migrate는 pre-deploy 단계로 옮겨야 한다는 것도 이번에 알았다. 빌드 컨테이너는 서비스 네트워크 외부에 있기 때문이다.
정리
오늘 배운 것 두 가지:
npm lockfile의
dev플래그는package.json과 독립적으로 관리된다. 패키지를 devDependencies에서 dependencies로 옮기면 lockfile도 재생성해야 한다. 그게 안 되는 환경이라면--include=dev로 lockfile 플래그를 무시하고 전부 설치하는 게 낫다.Render DB 업그레이드 후에는 DATABASE_URL을 반드시 확인해야 한다. 특히 수동으로 환경변수를 설정한 경우, 업그레이드 이후 내부 호스트명이 바뀌어서 연결이 끊길 수 있다.
connection to server on socket "/var/run/postgresql/.s.PGSQL.5432"에러가 나면 DATABASE_URL 누락을 먼저 의심하면 된다.
💬 댓글
비밀번호를 기억해두면 나중에 내 댓글을 삭제할 수 있어요.