사례 4주차 2026-08-09 · 나만의 지식체계 구축 연재

배포했는데 "서버가 안 되는 것 같아요" — 원인이 다른 장애 3개를 하루에 잡은 이야기

새 기능(그래프뷰·챗봇)을 배포했더니 "서버가 작동 안 하는 것 같다"는 말을 들었어요. 확인해보니 원인이 서로 완전히 다른 장애 3개가 연달아 있었어요 — 배포 자체가 실패했던 것, 안내한 주소가 틀렸던 것, 새로 붙인 도구가 기존 설정과 충돌했던 것. 하나씩 재현 가능한 절차로 진단하고 고쳤어요.

📝 한줄 요약

지식체계 사이트에 그래프뷰와 챗봇을 새로 붙이고 배포했는데, 실제로 접속해보니 안 됐어요. 원인을 하나씩 벗겨보니 서로 무관한 장애 3개가 겹쳐 있었어요 — ① 배포 자체가 조용히 실패한 것, ② 사이트 설정에 적힌 주소가 실제 배포 주소와 달랐던 것, ③ 새로 추가한 패키지가 기존 설정과 버전 충돌을 일으킨 것. 셋 다 로컬에서는 멀쩡한데 실제 배포에서만 드러나는 유형이라, “로컬 빌드 성공 = 다 됐다”가 아니라는 걸 제대로 겪었어요.

바쁘시면 이것만 읽어도 돼요:

  • 배포 후 확인은 npm run build가 끝난 화면이 아니라, 실제 배포된 주소에 직접 접속해서 해야 해요. 둘은 다른 걸 보장해요.
  • “안 된다”는 말을 들으면 바로 코드부터 고치지 말고, 먼저 어떤 상태인지 로그로 확인하는 게 순서예요. 배포 플랫폼은 대부분 --logs 같은 명령으로 실패 원인을 그대로 보여줘요.
  • 여러 도구·설정을 조합해 쓸 때는, 각각이 요구하는 버전 조건이 서로 맞는지 미리 봐야 해요. 하나를 고치려고 버전을 올렸다가 다른 하나가 깨지는 경우가 실제로 있어요.

Before — 배포는 됐다는데, 실제로는 안 됨

그래프뷰(/concepts 하단 인터랙티브 그래프)와 챗봇(사이트 전역 우측 하단 위젯)을 새로 만들어서 커밋·push했어요. 이 기능들이 서버에서 API를 호출해야 해서, 사이트를 완전 정적(static)에서 하이브리드(서버 함수 포함) 모드로 바꾸는 구조적 변경도 같이 들어갔어요. push하고 나서 “서버가 작동안하는것 같은데”라는 말을 들었어요.

과정 — 장애 3개를 순서대로 벗겨냄

1단계 — 배포 상태부터 확인

바로 코드를 뒤지지 않고, 먼저 실제로 무슨 일이 있었는지부터 봤어요.

vercel ls

최신 배포가 ● Error 상태였어요. 로그를 봤어요.

vercel inspect <배포주> --logs
The following Serverless Functions contain an invalid "runtime":
- _render (nodejs18.x)

원인: 쓰고 있던 Vercel 어댑터(@astrojs/vercel@7.8.2)가 내부적으로 Node 18·20만 알고 있었어요. 그런데 Vercel의 클라우드 빌드 환경은 이미 Node 24를 기본으로 쓰고 있었고, 어댑터는 모르는 버전을 만나면 무조건 “nodejs18.x”로 폴백하도록 짜여 있었어요. 문제는 Vercel이 그 nodejs18.x 런타임 자체를 이미 완전히 없애버려서, 폴백한 값이 애초에 유효하지 않았던 거예요.

해결: 어댑터가 아는 버전(20.x)으로 강제 고정했어요.

// package.json
"engines": { "node": "20.x" }

다시 배포하니 ● Ready로 성공했어요.

2단계 — “논문 페이지도 안 된다”는 새 제보

배포는 성공했는데, 이번엔 “논문 페이지도 작동을 안하네”라는 말을 들었어요. 서버 쪽부터 다시 확인했어요.

curl -sL -o /dev/null -w "%{http_code}\n" https://my-learning-hub.vercel.app/papers
# → 200 (근데 최종 도착지를 보니 auth/login 페이지)

서버는 200을 주는데, 실제로 도착한 곳이 로그인 페이지였어요. astro.config.mjs에 적혀 있던 site: 'https://my-learning-hub.vercel.app'이 애초에 이 프로젝트의 실제 주소가 아니었던 거예요 — 다른 곳으로 리다이렉트되는 이름이었어요.

vercel project ls
# → learning-hub 프로젝트의 실제 주소: learning-hub-virid.vercel.app

실제 주소로 바꾸고 다시 확인하니 논문 페이지도 정상이었어요. (사실 이 시점엔 서버는 처음부터 멀쩡했고, 안내된 주소가 틀렸던 것뿐이었어요.)

3단계 — 챗봇 프로바이더를 바꾸다가 또 충돌

챗봇을 처음엔 Vercel AI Gateway로 Claude를 부르게 만들었는데, 이미 다른 프로젝트용으로 발급해둔 OpenAI 키를 재사용하기로 방향을 바꿨어요. @ai-sdk/openai 패키지를 설치했더니:

npm warn EBADENGINE Unsupported engine
required: { node: '20.x' }, current: { node: 'v22.23.1' }

@ai-sdk/openai는 Node 22 이상을 요구하는데, 1단계에서 어댑터 호환 때문에 20.x로 고정해둔 것과 정면으로 충돌했어요. 20.x를 22.x로 올리면 1단계 문제가 재발하고, 그대로 두면 이 패키지가 안 돌아가는 딜레마였어요.

해결: SDK 패키지 자체를 빼고, OpenAI Chat Completions API를 fetch로 직접 호출했어요. 추가 의존성 없이 표준 REST 호출 하나면 충분했어요.

const res = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
  body: JSON.stringify({ model: 'gpt-4.1-mini', messages: [...] }),
});

After — 뭐가 달라졌나

BeforeAfter
배포 상태● Error (조용히 실패)● Ready
안내한 주소남의 로그인 페이지로 리다이렉트실제 프로덕션 주소로 정확히 도착
챗봇 의존성SDK 버전 충돌로 설치 불가의존성 0개 추가, REST 직접 호출로 정상 동작
검증 방식npm run build 성공만 확인실제 배포 주소에 curl로 직접 확인

배운 것

“빌드 성공”과 “실제로 된다”는 다른 말이에요. 로컬 npm run build가 끝까지 돌았다고 해서 배포 환경에서도 똑같이 동작한다는 보장은 없어요. 오늘 겪은 세 문제 모두 로컬에서는 아무 신호가 없었고, 실제 배포 주소에 접속하거나 배포 로그를 봐야만 드러났어요.

“안 된다”는 제보는 원인을 특정하지 않아요. 같은 “안 된다”는 말이 이번엔 세 번 다 다른 원인이었어요(“서버가 안 됨” → 배포 실패, “논문 페이지가 안 됨” → 주소 오안내, 이후 새로 발견 → 버전 충돌). 매번 “어디가 어떻게 안 되는지” 로그·상태부터 다시 확인하는 게, 짐작으로 코드를 고치는 것보다 빨랐어요.

여러 도구의 버전 요구사항은 서로 부딪힐 수 있어요. 어댑터 때문에 Node 20으로 고정했는데, 나중에 추가한 패키지는 Node 22를 요구했어요. 하나를 고치면 다른 하나가 깨지는 상황에서는, 둘 다 만족시키려 하기보다 아예 그 의존성을 빼는 선택지(SDK 대신 REST 직접 호출)가 더 깔끔했어요.

재사용 자산 — 배포가 “안 된다”는 말을 들었을 때 확인 순서

  • vercel ls(또는 쓰는 플랫폼의 배포 목록)로 최신 배포가 실제로 성공(Ready) 상태인지부터 본다
  • 실패했다면 --logs로 원인 문구를 그대로 읽는다 — 짐작하지 않는다
  • 성공했다면, 설정 파일에 적힌 주소가 아니라 플랫폼이 알려주는 실제 배포 주소로 직접 접속해서 확인한다
  • curl -sL -o /dev/null -w "%{http_code}"로 페이지가 최종적으로 어디 도착하는지(리다이렉트 포함) 확인한다
  • 새 패키지를 추가하기 전에 npm install 경고(EBADENGINE 등)를 그냥 넘기지 않고, 기존에 고정해둔 버전 조건과 부딪히지 않는지 본다
  • SDK 하나 때문에 버전 충돌이 생기면, “SDK 없이 표준 API를 직접 호출”이 더 간단한 해결일 수 있다는 걸 후보로 둔다