#200 뒤의 검사 내용을 읽었다
웹페이지가 열리고 health가 200을 반환해도 DB가 준비됐는지는 별도였다. 응답을 만든 경로에서 실제로 DB에 연결했는지, 검사하지 않은 대상을 정상으로 표시하고 있지는 않은지 확인했다.
이번 대상 웹의 일반 페이지는 DB를 쓰지 않았다. 여기에 다른 용도의 DB 연결 감시를 추가하면서, 그 DB가 응답하지 않는 상태와 웹 업무 장애를 같은 상태로 표시하면 안 됐다.
#선택 감시라는 뜻을 남겼다
Next.js Route Handler인 health의 GET에서 기존 공통 DB 검사를 호출했다. DB 패키지는 postgres인 Postgres.js 클라이언트를 drizzle-orm/postgres-js에 연결해 사용하고 있었다. health는 그 클라이언트의 unsafe("SELECT 1 AS ok")로 한 행을 받아 ok === 1인지 확인했다. 여기서는 고정 SQL만 쓰며 사용자 입력을 넣지 않는다.
설정 확인·클라이언트 획득·pool 대기·질의 전체에 2초 제한을 뒀다. 제한을 넘기면 해당 점검 query의 cancel()만 시도하고, 다른 요청이 사용하는 공유 pool은 종료하지 않았다. 설정 누락은 config_missing, 시간 초과는 timeout, 나머지 연결·질의 실패는 unavailable로 정리했다. 연결 문자열이나 원본 오류는 공개 응답에 싣지 않았다.
클라이언트의 공개 상위 소스는 Postgres.js, 연결 방식은 Drizzle PostgreSQL 문서에 있다. 이 구현에서는 Postgres.js 경로를 사용했다.
Redis는 HTTP 클라이언트인 @upstash/redis를 재사용했다. 접속 응답뿐 아니라 점검용 UUID 키의 쓰기와 읽기도 확인했다. 실제 검사 체인을 줄이면 다음과 같다. key와 nonce는 점검마다 새로 만든다.
const [pong, written, read] = await probe
.multi()
.ping()
.set(key, nonce, { ex: 60, nx: true })
.get<string>(key)
.exec();
PONG, OK, 원래 nonce가 모두 맞아야 정상이다. NX는 이미 있는 키를 덮어쓰지 않게 하고 EX 60은 60초 뒤 만료시킨다. PING 성공만으로 쓰기 권한까지 있다고 판단하지 않기 위한 검사다. 업무 키는 쓰지 않았다. Upstash Redis 클라이언트의 HTTP 요청에도 2초 제한을 두고 재시도는 껐다. SQL과 Redis는 Promise.all로 함께 실행하되 각각 결과를 남겼다.
이 웹에서는 해당 DB와 Redis가 선택 감시 대상이었다. 실패할 때 JSON 상태는 저하로 표시하지만 health와 ready의 HTTP 200은 유지하는 계약이었다. 응답의 의미를 줄이면 다음과 같다. 실제 서비스의 현재 응답을 옮긴 표는 아니다.
| 검사 | 결과 | 응답 의미 |
|---|---|---|
| app | ok | 웹 응답 가능 |
| postgres | fail / timeout | 선택 감시 경고 |
| 전체 | degraded / 200 | 웹 중단과 구분 |
이 계약에서 숫자 200만 읽으면 의존성 실패를 놓친다. 반대로 선택 DB 경고만 보고 웹 전체가 중단됐다고 판단해도 맞지 않는다.
일반 페이지에 DB 기능을 도입한다면 계약을 다시 정해야 한다. 당시 감시 연결이 성공했다는 결과로 앞으로의 업무 연결까지 준비됐다고 볼 수는 없었다.
#권한과 주소도 실제로 확인했다
모의 테스트에서는 정상·실패·시간 초과·설정 누락을 나눠 검증했다. 실제 연결 과정에서는 주소 문제와 권한 거절도 만났다. 연결 응답이 온다는 것과 점검에 필요한 쓰기·읽기가 허용된다는 것은 별도였다.
이를 보완한 뒤 10월 2일 운영에 반영하고 공개 응답에서 SQL과 Redis의 정상 상태를 확인했다. 그날 20:30 예약 실행에서도 정상 결과가 기록됐다. 소스에 검사 코드를 넣은 단계, 배포한 단계, 예약으로 실제 호출된 단계를 구분해 남겼다.
같은 검사를 health와 ready 양쪽에서 실행하므로, 예약 수집에서는 한 경로만 사용하도록 정리했다. 두 URL을 모두 호출하면 같은 의존성 검사를 반복하게 된다.
#예약이 돈다는 것 다음의 질문
DB 감시에는 비활성 일시정지를 예방하려는 목적도 있었다. 하지만 예약 질의가 성공했다는 결과만으로 일시정지가 앞으로 발생하지 않는다고 보장할 수는 없었다. 그 예방 효과는 장기 관측이 필요한 항목으로 남겼다.
기존 수집기는 선택 DB 경고도 감지했지만, 경고의 중요도와 실패 이유, 필수 검사 누락을 더 잘 구분하는 후속 작업이 있었다. 이 글을 정리하면서 그 후속 설정이나 현재 예약 실행까지 다시 확인한 것은 아니다.
기존 조용한 모니터링 글에서는 언제 알릴지를 다뤘다. 이번에는 알리기 전에 무엇을 검사했는지가 먼저였다. 페이지 응답, 의존성 연결, 실제 업무 동작은 서로 다른 근거가 필요했다.
당시 끝낸 것은 선택 SQL·Redis 검사 구현과 배포, 한 예약 구간의 실행 확인이다. 현재의 상시 정상, 전체 업무 동작, 장기 일시정지 예방 효과는 이 결과에 포함하지 않았다.