설계 패키지의 studio-v1.yaml(v3.0.0, 응답 봉투)을 이 저장소에 배선하고 슬라이스 1의 기반을 세운다. 19개 operation 중 getStudioSession과 listStudioCatalog를 구현했다. 계약과 생성 - src/config/openapi/studio-v1.yaml 을 vendor하고 MANIFEST에 출처 커밋을 기록 - openapi-generator로 DTO(model)만 생성한다. generateApis 대신 globalProperties.set(['models': '']) — 그 두 속성은 플러그인 7.18.0에 없다 - useOneOfInterfaces=false. 그 대가로 discriminator union 5종의 Jackson 배선이 깨진다(spec §3.1). 그 5종을 쓰는 7개 operation은 Plan 02에서 전략을 정한 뒤 구현한다 - 생성 코드는 별도 generatedOpenapi sourceSet에 둔다. -Werror가 생성물의 deprecated API 사용을 빌드 실패로 승격하기 때문이다. jar와 test 클래스패스에 별도로 얹는다 오류 계약 - StudioError 23종(계약 ApiError.code와 1:1) + StudioException(ApiErrorCarrier) - StudioExceptionHandler는 techlog 패키지로 범위를 좁힌다. 다른 기능의 오류 응답을 바꾸지 않기 위해서다 - 클라이언트 문구는 레지스트리의 client_safe_message에서 가져오고 예외 메시지는 로그 전용이다(ApiErrorCarrier javadoc의 요구) - 바인딩 예외를 봉투로 옮긴다. 그러지 않으면 bare RFC 7807이 새어 나가 ADR-006을 위반한다 게이트 - TechLogBoundaryArchTest 7종 — spec §4.3의 bounded context 경계. Gradle leaf를 늘릴 수 없어 이 규칙이 경계의 유일한 방어선이다 - StudioErrorRegistryTest — enum ↔ 레지스트리 ↔ 계약 3축 대조, vendor 사본 해시 검증 - StudioContractDriftTest — springdoc 표면이 계약을 벗어나면 실패. @ComponentScan이라 새 컨트롤러가 자동으로 걸린다 - StudioSessionCsrfHeaderProfileContractTest — 배포 가능한 세 프로파일이 계약의 csrf-header-name const로 해소되는지 고정. 이 저장소는 실제 composition root를 테스트에서 부팅할 수 없어 파일 단언으로 그 층을 덮는다 스키마 - V7__techlog_core.sql, 28 테이블. 설계 DDL에서 studio_idempotency(기존 idempotency_record 재사용)와 범위 밖 6종을 제외했다 - 원본의 tech_log 스키마 대신 public을 쓴다. 원본의 SET search_path는 Flyway 세션에만 적용되고 런타임 커넥션 풀은 상속하지 않는다 알려진 제약 - getStudioSession은 세션 인프라(redis-session)가 없어 503 STUDIO_UNAVAILABLE을 반환한다. 계약이 이 operation에 허용하는 유일한 실패 코드다. 가짜 CSRF 토큰으로 200을 만들지 않았다 - 따라서 슬라이스 1의 "프론트 로그인 실동작" 목표는 아직 달성되지 않았다 이 커밋은 AGENTS.md의 human-only 커밋 정책에 대한 저장소 소유자의 명시적 지시로 작성됐다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.2 KiB
5.2 KiB
title, category, error_codes, severity, owner, last_updated, status
| title | category | error_codes | severity | owner | last_updated | status | |
|---|---|---|---|---|---|---|---|
| Runbook — STUDIO_UNAVAILABLE (Tech Log Studio 일시 장애) | TRANSIENT_DEPENDENCY |
|
P1 | oncall | 2026-08-18 | active |
Runbook: STUDIO_UNAVAILABLE (runbook://studio/unavailable)
1. Trigger
이 runbook은 Tech Log Studio API(studio-v1.yaml)가 error.code=STUDIO_UNAVAILABLE
(HTTP 503, category TRANSIENT_DEPENDENCY, retryable=true)을 응답할 때 발동됩니다.
- alert name:
studio_error_rate_critical또는studio_dependency_unavailable - alert payload 필수 field:
operation(문서/미리보기/게시 중 어느 슬라이스인지),error.code,error.category,dependency_name(가능하면),runbook_link - 임계:
- P1:
STUDIO_UNAVAILABLE비율이 1분간 Studio 전체 트래픽의 10% 초과 - P2: 단발성 spike이나 5분 내 자연 회복
- P1:
StudioExceptionHandler(adapter/inbound/web)가 StudioException을 봉투로 변환하는
지점이므로, 이 코드는 항상 Studio facade/use case(application-core)가 자신의 하위
의존성(영속성, 캐시, 오브젝트 스토리지, 렌더링/미리보기 파이프라인 등) 실패를
클라이언트에 안전한 단일 코드로 접어(classify) 던진 결과입니다 — 원인 그 자체가 아니라
Studio가 판단한 결과라는 점을 유의합니다.
2. First Response (5분 이내)
Step 1 — 확인
- 최근 배포 이력 확인 (
app-bootstrap롤아웃, config 변경) — 배포 직후 spike면 롤백 우선 검토 - 로그에서
StudioException이 감싸고 있던 실제 원인을 확인:dev.caskeleton.application.techlog패키지의 use case/facade 로그에서STUDIO_UNAVAILABLE로 분류되기 직전의 원인 예외 (PersistenceFailureException,DependencyFailureException등)를 추적 - runtime-health Dependency Matrix에서 Studio가 의존하는 구성요소(DB, 캐시, object storage)의 required/optional 분류와 현재 상태 확인
- 특정 slice(문서 편집/미리보기/게시)에 국한된 장애인지, Studio 전역 장애인지 구분
Step 2 — 임시 격리
- 원인이 특정 하위 의존성이면 해당 의존성의 runbook으로 전환 (예:
runbook://db/unavailable,runbook://cache/unavailable) —STUDIO_UNAVAILABLE은 진입점일 뿐, 근본 원인 대응은 하위 의존성 runbook이 담당 - 클라이언트(Frontend)는 이미
retryable=true를 신뢰해 지수 백오프 재시도를 수행하므로, 단기 spike는 자연 회복을 우선 관찰 (조기 개입으로 인한 추가 부하 유발 방지)
3. Diagnosis
- log query:
{service="app"} | error.code="STUDIO_UNAVAILABLE" | stats count by operation - 원인 추적: 같은 요청의 correlation id로 use case 로그를 따라가 어떤 하위 호출이
StudioException.withDetails(StudioError.STUDIO_UNAVAILABLE, ...)로 재분류됐는지 확인 - metric panel:
http_server_requests_seconds_count{uri=~"/api/v1/studio/.*", outcome="SERVER_ERROR"}- 하위 의존성 metric (
hikaricp_connections_active,resilience4j_circuitbreaker_state, object storage client 오류율)
- 가능한 원인:
- DB/캐시/오브젝트 스토리지 등 필수 의존성 장애가 Studio 계층까지 전파
- Studio 자체 리소스 고갈 (스레드풀, 커넥션풀)
- 미리보기/렌더링 파이프라인의 타임아웃 누적
- 배포 직후 신규 코드 경로의 미검증 예외가 fallback으로
STUDIO_UNAVAILABLE에 접힘
4. Mitigation
- 단기: 근본 원인이 확인된 하위 의존성이면 해당 dependency runbook의 mitigation을 적용
- Studio 자체 리소스 고갈이면 인스턴스 스케일아웃 또는 커넥션/스레드풀 상향 검토
- 특정 slice(예: 미리보기)만 영향받는다면 해당 slice만 일시적으로 기능 차단하고 나머지(문서 편집/게시)는 정상 유지 검토 — 전면 장애보다 부분 degrade 우선
- 장기: 반복되는 하위 의존성 장애가
STUDIO_UNAVAILABLE로 잦게 나타나면, 해당 의존성의 circuit breaker/timeout 임계를 재조정하고 fallback 경로 보강
5. Escalation
- P1 5분 내 회복 신호 없으면 해당 하위 의존성 오너 팀에 page
- 여러 slice(문서/미리보기/게시)에서 동시에 발생하면 incident commander 호출 (공유 인프라 계층 문제 의심)
6. Recovery / Verification
- 회복 확인 metric:
STUDIO_UNAVAILABLE비율이 5분간 1% 미만으로 유지 - 하위 의존성 metric(circuit breaker CLOSED, connection pool 정상)도 함께 확인
- post-incident:
- 어떤 하위 의존성이
STUDIO_UNAVAILABLE로 접혔는지 기록하고 근본 원인 runbook에 링크 - Studio 클라이언트(Frontend
STUDIO_ERROR_CODES) 쪽 재시도/백오프 동작이 기대대로 작동했는지 확인 - 특정 slice 반복 장애면 chaos test 시나리오 추가 검토
- 어떤 하위 의존성이
7. Related
- error-codes.yaml row:
STUDIO_UNAVAILABLE(feature-techlog-studio-backend,dev.caskeleton.application.techlog.error.StudioError.STUDIO_UNAVAILABLE) - 매핑 지점:
dev.caskeleton.adapter.inbound.web.techlog.StudioExceptionHandler - 관련 runbook:
runbook://db/unavailable,runbook://cache/unavailable - 관련 branch: feature-techlog-studio-backend