문서 / specs/001-harness-public-site/research.md

Research: 5240lab 하네스 공개 사이트

공개 사이트 구현 전 조사한 선택지와 근거.

Next.js 실행 형태

Decision: Next.js 16.3.0 App Router와 output: 'standalone'을 사용한다. 페이지는 요청 데이터가 없는 서버 컴포넌트로 사전 렌더하고 health만 경량 route handler로 제공한다.

Rationale: 사용자가 Next.js를 선택했고, 같은 서버의 insapien-docs가 Next.js 16.3.0과 React 19.2.8을 사용한다. 로컬 공식 문서는 standalone 출력이 선별된 런타임 의존성과 최소 server.js를 생성하며 public.next/static은 release에 별도로 복사해야 한다고 명시한다. 원본 저장소 접근이 없는 읽기 전용 사이트에 맞고 versioned release 배포가 가능하다.

Alternatives considered:

  • 정적 export: 운영은 가장 단순하지만 사용자가 Next.js 서버 방식을 선택했고 향후 release health와 제한적 서버 기능 확장 여지를 유지하기로 했다.
  • 일반 next start: 동작하지만 전체 프로젝트와 node_modules가 필요해 release 격리와 복구가 불명확하다.
  • custom server: standalone 파일 추적과 충돌하며 이 범위에 필요하지 않다.

렌더링과 클라이언트 경계

Decision: 모든 설명·근거·평가를 서버 렌더 HTML에 포함하고, 근거 필터와 작은 disclosure만 클라이언트 컴포넌트로 둔다. JavaScript가 없으면 전체 목록을 그대로 표시한다.

Rationale: 헌법의 점진적 공개와 접근성 원칙을 충족하고, 콘텐츠 사이트에서 클라이언트 번들과 실패 지점을 최소화한다.

Alternatives considered:

  • 전체 SPA: 필터 구현은 단순하지만 JavaScript가 핵심 콘텐츠의 전제가 된다.
  • 전부 서버 링크 필터: JavaScript 없이 동작하지만 네트워크 이동과 URL 조합이 작은 데이터 규모에 비해 복잡하다.

근거 검증 모델

Decision: 사람이 content/evidence.json에서 공개 후보를 선별하고, Node 기반 검증기가 5240lab 루트 내부의 상대 경로·행 범위·기대 발췌를 대조해 공개 스냅샷을 생성한다. 검증기는 중복 ID와 민감 패턴도 차단한다.

Rationale: 자동 전체 스캔보다 공개 범위를 통제하면서도 행 이동, 내용 변경과 민감정보 유출을 빌드에서 탐지할 수 있다. 런타임에는 원본 경로나 읽기 권한이 없다.

Alternatives considered:

  • 수동 발췌만 저장: 가장 쉽지만 원문과 분리되어 빠르게 낡는다.
  • 런타임 원본 조회: 최신 상태는 보이지만 공개 공격 표면과 서버 경로 노출 위험이 커서 제외했다.
  • 저장소 전체 자동 스캔: 탐지 범위가 넓지만 비공개 문맥을 자동 공개할 위험이 있다.

공개 금지 검사

Decision: manifest 경로와 공개 발췌에 대해 절대 Unix/Windows 경로, private key, 자격 증명 할당, 알려진 토큰 형태, 이메일·전화번호와 비정상적으로 긴 고엔트로피 문자열을 보수적으로 차단한다. 허용이 필요한 비민감 값은 코드 변경과 테스트를 통해서만 예외 처리한다.

Rationale: deny 패턴만으로 완전한 비밀 탐지는 불가능하지만, 사람 선별과 경계 allowlist에 추가되는 방어층으로 실수를 조기에 막는다.

Alternatives considered:

  • 정규식 없음: 선별자의 실수에 취약하다.
  • 자동 마스킹 후 공개: 문맥을 오판할 수 있으므로 실패 후 명시적 수정이 안전하다.

성숙도 평가

Decision: 계층마다 L0~L4를 부여하고 적용 범위·강제력·검증 가능성·추적성·운영 안전성 다섯 축을 각각 0~20점으로 평가한다. 총점은 합계이며 개선 전후는 같은 축과 근거로 비교한다.

Rationale: 단계는 상태를 설명하고 점수는 변화를 보여준다. 구성 파일 존재만으로 상위 단계가 되지 않도록 L2부터 실제 차단 증거, L3부터 반복 측정 증거를 요구한다.

Alternatives considered:

  • 단계만 사용: 전후 개선 폭을 비교하기 어렵다.
  • 점수만 사용: 무엇이 자동 강제되고 측정되는지 의미가 흐려진다.

테스트 전략

Decision: 순수 검증·점수 로직은 Node test runner와 tsx, 서버 렌더 계약은 React 서버 렌더, 사용자 흐름·접근성·반응형은 Playwright와 axe-core로 검증한다. TypeScript, ESLint, production build와 HTTP smoke를 하나의 verify 게이트로 묶는다.

Rationale: 각 실패를 가장 작은 계층에서 빠르게 잡고, 실제 브라우저와 배포 환경에서만 드러나는 문제를 별도 수용 검사로 보완한다.

Alternatives considered:

  • 브라우저 테스트만 사용: 느리고 근거 검증 실패 원인을 격리하기 어렵다.
  • 정적 검사만 사용: 키보드, 반응형, 실제 라우팅과 JavaScript 없음 동작을 보장하지 못한다.

배포와 롤백

Decision: .next/standalone, .next/static, public을 release ID 디렉터리에 묶는다. 후보 release를 임시 포트에서 실행해 health와 내부 smoke를 통과시킨 뒤 current 심볼릭 링크를 전환하고 systemd를 재시작한다. 외부 HTTPS smoke 실패 시 이전 링크로 복구하고 다시 시작한다.

Rationale: 빌드 실패는 현재 서비스를 건드리지 않고, 활성화 실패는 동일한 artifact 단위로 복구할 수 있다. 사이트 자체가 설명하는 운영 하네스를 실제로 사용한다.

Alternatives considered:

  • 작업 디렉터리에서 바로 재시작: 빠르지만 현재 사용자 변경과 실패 빌드가 운영에 섞인다.
  • 컨테이너 배포: 격리는 좋지만 현재 단일 Next.js 사이트에는 추가 운영 계층이다.

매뉴얼 동기화

Decision: /guide를 사용자 매뉴얼, README.md를 개발자 매뉴얼, docs/evidence-maintenance.md를 콘텐츠 관리자 매뉴얼, docs/operations.md를 운영 매뉴얼로 정의한다. 기능 작업마다 영향을 판정하고 코드와 같은 release에서 검증한다.

Rationale: 매뉴얼을 구현 후 후속 작업으로 남기지 않고 기능 ID와 완료 게이트에 포함한다. 이 프로젝트 자체가 구상부터 매뉴얼까지 연결하는 하네스의 사례가 된다.

Alternatives considered:

  • 공개 가이드만 유지: 개발·운영 절차가 코드에만 남는다.
  • 모든 설명을 README에 통합: 독자별 목적과 공개 경로가 섞인다.