로컬에서는 잘 되던 기능이 배포 후 열리지 않는 날이 있었습니다. 화면이 뜨지 않으면 새 코드의 문제인지, DB 변경 권한인지, Railway가 아직 준비되지 않은 것인지부터 알기 어려웠습니다. 이번 개발은 새 기능보다 “지금 어떤 버전이 실제로 살아 있는가”를 확인하는 장치를 만드는 과정이었습니다.
“배포했다”와 “사용자가 보고 있다”는 다르다
코드를 push하고 Railway 배포 로그가 성공으로 끝나도 브라우저가 보고 있는 화면이 정말 새 버전인지 바로 알 수 없었습니다. 캐시가 남았거나 이전 인스턴스가 요청을 처리할 수 있고, 배포가 끝나기 전에 접속했을 수도 있습니다. 기능이 보이지 않을 때 같은 수정을 다시 배포하는 낭비가 생겼습니다.
홈 화면과 사이드바에 버전과 배포 commit을 표시했습니다. 개발자는 commit을 보고 코드 상태를 확인하고, 사용자는 세 자리 버전으로 지원 요청을 전달할 수 있게 했습니다. 기술 정보를 크게 노출할 필요는 없지만 문제를 재현할 최소 표시는 필요했습니다.
서버가 켜진 것과 준비된 것은 다르다
프로세스가 실행 중이어도 DB 연결과 필수 설정이 준비되지 않으면 정상 요청을 처리할 수 없습니다. Railway의 probe가 단순히 포트가 열렸는지만 확인하면 준비되지 않은 인스턴스에 사용자를 보낼 수 있습니다. 그래서 빠르게 응답하는 readiness endpoint를 만들고 배포 플랫폼이 그 결과를 확인하도록 했습니다.
Health check에 모든 외부 서비스를 넣는 것도 위험합니다. Gmail이 잠시 느리다고 웹사이트 전체를 죽은 것으로 판단하면 오히려 장애가 커집니다. 기본 웹 요청을 처리할 수 있는지와 부가 연동이 정상인지 구분하고, probe는 짧고 예측 가능하게 유지했습니다.
| 확인 항목 | 의미 | 표시 위치 |
|---|---|---|
| Application version | 기능 릴리스 기준 | 사이드바·홈 |
| Deployed commit | 실제 코드 식별 | 운영 정보 |
| Liveness | 프로세스 응답 여부 | 플랫폼 probe |
| Readiness | 요청 처리 준비 여부 | 배포 전환 기준 |
Migration 권한이 운영을 막지 않게
개발 환경의 DB 사용자는 테이블과 인덱스를 자유롭게 바꿀 수 있지만 운영 연결 계정은 권한이 제한될 수 있습니다. 애플리케이션 시작 때 모든 migration을 강제로 실행하면, 선택적인 인덱스나 전화번호 정규화 변경 하나가 전체 사이트 시작을 막을 수 있었습니다.
이번에는 어떤 migration이 필수 스키마 변경인지, 어떤 변경이 별도 관리자 권한으로 실행되어야 하는지 나눴습니다. 권한이 없는 변경을 무시한다는 뜻이 아니라 애플리케이션 시작과 DB 관리 작업의 책임을 분리하는 것입니다. 실패 원인은 로그에 남기고, 운영자가 별도 단계에서 적용 여부를 확인할 수 있게 했습니다.
버전 번호는 장식이 아니라 대화의 기준이다
“지금 화면이 이상해요”보다 “0.9.77에서 Client 검색이 안 됩니다”라는 보고가 훨씬 빠르게 문제를 좁힙니다. 세 자리 버전 표기를 통일하고 배포 commit과 연결하면서, 기능 개발과 운영 확인 사이에 공통 언어가 생겼습니다.
배포 로그만 보지 않고 실제 사용 경로를 확인했다
빌드 성공은 시작일 뿐입니다. 로그인 화면, 홈, Client 검색과 이메일처럼 핵심 경로를 새 인스턴스에서 짧게 확인해야 합니다. 특히 환경변수는 빌드에는 영향을 주지 않지만 실행할 때 문제를 만들 수 있어, 준비 상태와 실제 기능 확인을 분리했습니다. 민감한 값은 화면에 노출하지 않고 설정 존재 여부와 연결 결과만 진단했습니다.
DB migration도 적용 전후를 구분했습니다. 어떤 버전이 어떤 스키마를 필요로 하는지 기록하고, 변경이 실패하면 애플리케이션을 이전 버전으로 돌릴 수 있는지 확인했습니다. 모든 migration을 자동 재시도하면 같은 오류가 계속 발생하거나 부분 적용 상태가 더 복잡해질 수 있으므로 운영자가 판단할 지점을 남겼습니다.
마지막으로 배포가 성공한 뒤에도 이전 버전의 인스턴스가 잠시 남을 수 있다는 점을 고려했습니다. readiness가 통과한 새 인스턴스에만 트래픽을 연결하고, 사용 화면에서 version과 commit을 확인한 다음 배포를 완료로 보았습니다. 이 작은 확인 절차가 “분명 고쳤는데 왜 그대로인가”라는 혼란을 크게 줄였습니다.
운영 확인표에는 담당자와 판단 기준도 함께 적었습니다. 새 인스턴스가 준비되지 않았을 때 기다릴지, 이전 버전을 유지할지, DB 변경을 별도로 실행할지를 로그 한 줄만 보고 즉흥적으로 결정하지 않기 위해서입니다. 배포 시간, 확인한 버전, 핵심 경로의 결과를 짧게 남기면 다음 장애에서도 비교할 기준이 생깁니다. 특히 혼자 개발하고 운영하는 단계일수록 이런 작은 기록이 기억에 의존하는 실수를 줄여줍니다.
실무 체크리스트
- 현재 운영 중인 버전과 commit을 확인할 수 있는가?
- 배포 직후 이전 인스턴스와 새 인스턴스를 구분할 수 있는가?
- liveness와 readiness의 역할을 나눴는가?
- health endpoint가 빠르고 외부 장애에 과민하지 않은가?
- 필수 migration과 선택적 운영 migration을 구분했는가?
- DB 권한 오류가 로그에 구체적으로 남는가?
- 실패한 변경을 별도 절차로 다시 적용할 수 있는가?
- 문제 보고 시 사용자가 버전을 쉽게 전달할 수 있는가?
FAQ
Health check가 정상이면 모든 기능이 정상인가요?
아닙니다. 기본 요청을 받을 준비가 됐다는 신호에 가깝습니다. Gmail이나 DocuSign 같은 부가 연동 상태는 별도 진단으로 보는 편이 안전합니다.
DB migration은 앱 시작 때 자동 실행하는 것이 편하지 않나요?
초기에는 편하지만 운영 규모가 커지면 권한, 실행 시간과 롤백을 관리하기 어렵습니다. 중요한 변경은 배포 절차의 명시적 단계로 분리하는 편이 좋습니다.
버전과 commit을 모두 표시해야 하나요?
일반 사용자는 버전만으로 충분하지만 운영자는 정확한 코드를 확인할 commit이 유용합니다. 서로 다른 대상에게 다른 수준으로 보여줄 수 있습니다.
Readiness 확인은 DB에 복잡한 쿼리를 실행해야 하나요?
복잡한 쿼리는 probe 자체가 부하를 만들 수 있습니다. 연결 여부처럼 빠르고 최소한의 검사부터 시작하는 것이 좋습니다.
공식 자료
- Microsoft Learn: ASP.NET Core Health Checks
- Railway Docs: Healthchecks
- Microsoft Learn: EF Core Migration 적용
관련 글
SmartNexus 서비스를 사용해 보고 싶으신가요?
SmartNexus 도입이나 사용을 원하시면 아래 댓글 또는 support@Active95.com으로 요청해 주세요.