<!-- 기능 설계서 — 백엔드 Health/모니터링.
     작성: [AI] Architect. 빈 섹션 금지 — 해당 없으면 "해당 없음" 명시. -->

# 설계서: 백엔드 - Health/모니터링 (#277)

> **상태**: Approved <!-- Draft | Approved | Superseded -->
> **작성**: [AI] Architect · **최종수정**: 2026-06-15
> **추적성** — Redmine: #277 · 관련 ADR: 없음
> · 구현 파일: `backend-java/src/main/java/com/tasteby/controller/HealthController.java` · 테스트: TBD (현재 없음)

## 1. 목적 (Why)
PM2(dev) / Kubernetes (OKE prod) / Nginx Ingress 가 백엔드 프로세스가 살아있는지 판단할 수 있는 경량 HTTP 엔드포인트를 제공해, 장애 시 재기동·트래픽 차단을 자동화한다.

## 2. 범위 (Scope)
- **포함**:
  - `GET /api/health` — 항상 `200 OK` 와 `{"status":"ok"}` 를 반환하는 liveness 체크 엔드포인트.
  - 인증 없이 호출 가능(공개).
- **제외 (out of scope)**:
  - DB/Redis/외부 API 의존성 상태를 포함하는 deep health(readiness) 체크.
  - Spring Boot Actuator (`/actuator/health`) 활성화.
  - 메트릭(Micrometer/Prometheus) 노출.
  - 분산 트레이싱·로그 수집.
  - 알림(Slack/Email/PagerDuty) 통합.

## 3. 인수조건 (Acceptance Criteria)
- [x] `GET /api/health` 호출 시 HTTP 200 과 본문 `{"status":"ok"}` 를 반환한다.
- [x] 인증 없이 누구나 호출 가능하다(`WebConfig` CORS 허용 범위 내).
- [x] 응답 본문은 `Content-Type: application/json` 으로 직렬화된다(Spring 기본).
- [x] 외부 의존성(DB/Redis/LLM)이 다운되어도 본 엔드포인트는 영향을 받지 않는다(순수 인메모리 응답).

## 4. 컨텍스트 & 제약
- **의존성**:
  - Spring Web (`@RestController`, `@GetMapping`).
  - 외부 의존성 없음 — 컨트롤러 자체가 무상태 상수 응답.
  - 호출자: PM2 헬스(현재는 미사용), Kubernetes liveness/readiness probe(향후), Nginx upstream check(현재는 미사용), Uptime 모니터링.
- **제약**:
  - 응답 시간 < 50ms 가정. 어떤 비용 있는 작업도 포함하지 않아야 함.
  - 본 엔드포인트가 200 을 반환한다고 해서 "서비스 정상" 을 의미하지 않음(프로세스 생존만 보장).
- **가정**:
  - 프로세스가 응답할 수 있으면 JVM/Tomcat 이 살아있다는 신호로 충분.
  - 인증 미들웨어(Spring Security/필터)는 `/api/health` 를 통과시킨다(현재 인증 강제 없음).

## 5. 아키텍처 개요
- **모듈**:
  - `HealthController` — 단일 `@RestController` 클래스, 메서드 1개.
- **경계**:
  - I/O: HTTP 입출력만. DB/Redis/LLM 호출 없음.
  - 순수 로직: 상수 `Map` 반환 — 테스트 자체가 거의 불필요한 수준.

```
   Probe/Monitor  ──HTTP GET /api/health──▶  Spring DispatcherServlet
                                                     │
                                                     ▼
                                            HealthController.health()
                                                     │
                                                     ▼
                                          Map.of("status","ok")  → JSON 200
```

## 6. 데이터 모델
- 단순 응답 객체: `Map<String, String>` 의 불변 단일 엔트리.

```json
{ "status": "ok" }
```

- 입력 파라미터/바디 없음.
- 상태 코드: 항상 200. 다른 코드 없음.
- 경계 검증: 해당 없음.

## 7. 함수 명세 (Function Specs)

| 함수 | 책임(1줄) | 시그니처(잠정) | 입력 | 출력 | 에러/실패 | 복잡? |
|------|-----------|----------------|------|------|-----------|-------|
| `HealthController.health` | liveness 응답 반환 | `Map<String,String> health()` | 없음 | `{"status":"ok"}` | 이론상 없음(JVM/Tomcat 죽으면 연결 실패) | 단순 |

## 8. 흐름 / 알고리즘
1. 클라이언트(K8s probe / 모니터)가 `GET /api/health` 호출.
2. Spring DispatcherServlet 라우팅 → `HealthController.health()` 호출.
3. 메서드는 `Map.of("status","ok")` 상수 반환.
4. Jackson 이 JSON 직렬화 → HTTP 200 응답.

cron 표현/주기: 본 컨트롤러 자체는 스케줄러가 아님(외부 probe 가 주기적으로 호출). 운영 권장 주기: K8s liveness 10s, readiness 5s.

## 9. 엣지케이스 & 에러 처리
- **JVM 데드락/OOM**: 응답 없음 → probe 가 타임아웃 → 비정상 판정 → 재기동(외부 메커니즘이 처리, 본 엔드포인트가 의도한 시나리오).
- **요청 폭주(DDoS)**: 본 메서드는 매우 가볍지만 인증/레이트리밋이 없음. Nginx Ingress 단에서 처리 가정.
- **DB/Redis 다운**: 본 엔드포인트는 영향 없음(의도된 동작). 단, 운영자가 "서비스 정상"으로 오인할 수 있는 한계 — 별도 readiness 필요(11장 참조).
- **잘못된 HTTP 메서드**: `POST /api/health` 등은 405 (Spring 기본).
- **CORS**: `WebConfig` 의 허용 origin 에 따라 브라우저 접근 제한될 수 있음(서버-서버 probe 에는 영향 없음).

## 10. 테스트 계획
현재 자동 테스트 없음(TBD). 권장 케이스:
- **Unit/Slice (`@WebMvcTest(HealthController.class)`)**:
  - `GET /api/health` → 200 + 본문 `{"status":"ok"}`.
  - `POST /api/health` → 405.
- **Smoke**:
  - 운영/dev 배포 후 `curl -sf https://www.tasteby.net/api/health` 가 0 종료 코드.
- **모킹**: 불필요 — 의존성 없음.

## 11. 리스크 & 대안 검토
- **선택**: 자체 컨트롤러 1개 + 상수 응답.
  - 장점: 의존성 0, 인증/필터 우회 명확, K8s probe 와 1:1 매핑 쉬움.
  - 단점: deep health(DB/Redis 연결 확인) 부재 → "프로세스는 살아있지만 서비스 불가" 상황 감지 못 함.
- **대안**:
  - **Spring Boot Actuator (`/actuator/health`)** — DB/Redis HealthIndicator 자동 구성, 표준화. 보안 노출 범위 제어 필요. → readiness 용도로 향후 도입 후보.
  - **K8s readinessProbe 별도 엔드포인트** (`/api/health/ready`) — DB ping, Redis ping 결과 합산. 본 설계의 자연스러운 확장.
  - **외부 모니터링(UptimeRobot/Healthchecks.io)** — HTTP 200 만으로 충분히 활용 가능.
- **트레이드오프**: 현재는 운영자 1인 / 트래픽 적음 → 최소 구현이 합리적. Prod 안정화 단계에서 Actuator + readiness 분리 권장.
- **되돌리기 어려운 결정 없음**.

## 12. 미해결 질문 (Open Questions)
- DB/Redis 연결을 검사하는 readiness 엔드포인트(`/api/health/ready`)를 언제 도입할 것인가?
- Spring Boot Actuator 를 켜고 노출 범위(어떤 엔드포인트, 어떤 인증)를 어떻게 제어할 것인가?
- Prometheus 메트릭/Grafana 대시보드를 운영할 것인가(현재는 PM2/kubectl 로그 의존)?
- K8s probe 구성(initialDelay, periodSeconds, failureThreshold) 표준값은?
- 헬스 응답에 빌드 버전/커밋 SHA를 포함할 것인가(`/api/version` 분리 vs 통합)?
- 본 엔드포인트도 인증/IP 제한 대상으로 둘 필요가 있는가(공개 권장)?
