Files
taxbaik/docs/ENGINEERING_HARNESS.md
T
kjh2064 d780fecf8c
TaxBaik CI/CD / build-and-deploy (push) Successful in 4m30s
Harden admin telemetry and deployment safeguards
2026-07-02 16:10:15 +09:00

9.3 KiB

Engineering Harness

이 문서는 TaxBaik 코드가 매번 흔들리지 않도록 막는 최소 하네스다. 여기에 없는 내용은 추측하지 않고 코드, 테스트, 운영 로그, DB 스키마 중 하나로 확인한다.

Non-Negotiables

항목 기준 실패 판정
Runtime ASP.NET Core net10.0 기준 유지 프로젝트별 TargetFramework 불일치
Public UI 홈페이지/공개 페이지는 서버 사이드 렌더링 기준 공개 페이지가 불필요하게 WASM 번들에 의존
Admin UI 어드민은 클라이언트 사이드 Blazor WebAssembly + MudBlazor + API-first 어드민 컴포넌트가 Application/Repository를 직접 주입
API 모든 운영 기능은 /api/* DTO 경유 UI 전용 서비스 호출만 존재
Auth JWT 인증, 관리자 API는 [Authorize] 익명으로 관리자 데이터 접근 가능
Deploy Gitea Actions CI/CD만 배포 경로 수동 SSH/복사로 운영 반영
Evidence 빌드, 테스트, E2E, API smoke 로그 "확인함", "될 것" 같은 진술
Admin Render InteractiveWebAssemblyRenderMode(prerender: false) 어드민에 InteractiveServerRenderMode 또는 prerender: true 존재
KST Timestamp CI/배포/백업 폴더명과 추적 일시는 TZ=Asia/Seoul date가 기본 UTC 또는 서버 로캘에 종속

Architecture Guardrails

  • Domain은 엔티티, enum, repository interface만 가진다.
  • Application은 use case와 검증 규칙을 가진다. HTTP, JS, MudBlazor, DB 연결 세부를 모른다.
  • Infrastructure는 Dapper SQL과 외부 시스템 구현을 가진다.
  • Web은 Controller, 공개 Razor Pages SSR, Blazor host, 인증/서빙 설정을 가진다.
  • Web.Client/Admin UI는 클라이언트 사이드 Blazor WebAssembly로 본다. 서버 DI 서비스에 의존하지 않고 API client만 호출한다.
  • 관리자 호스트가 prerender를 사용하더라도 데이터 접근 원칙은 WASM + API-first다. prerender는 초기 마크업용이며 비즈니스 로직의 근거가 아니다.
  • 어드민 렌더 모드는 InteractiveWebAssemblyRenderMode(prerender: false)를 기본값으로 둔다. InteractiveServerRenderModeprerender: true는 어드민에서 허용하지 않는다.
  • JavaScript는 최소화한다. 브라우저 API, 인증 토큰 저장, 서드파티 편집기처럼 Blazor/MudBlazor만으로 해결하기 부적절한 경우에만 JS module로 격리한다.
  • 상속은 프레임워크 요구 또는 명확한 다형성 모델에만 사용한다. 폼/테이블/CRUD 재사용은 기본적으로 컴포넌트 합성과 작은 service/client로 처리한다.
  • 과유불급을 지킨다. 실제 재사용이 2곳 미만이면 새 추상화를 만들지 말고 기존 컴포넌트를 직접 조합한다.
  • CI, 배포 폴더명, 백업명, 버전 추적에 쓰는 시간 문자열은 TZ=Asia/Seoul을 기본으로 한다.
  • 클라이언트 오류 수집은 서버/브라우저를 보호하는 목적의 제한형 수집으로만 운영한다. 건당 비동기 전송, 중복 억제, 분당 상한, 서버 rate limit, 실패 시 조용히 폐기, 재시도 폭주 금지.
  • 브라우저에서 발생한 JS 오류는 운영 장애 탐지를 위한 샘플 데이터로만 취급하고, 전체 이벤트 스트림을 보존하려는 설계는 금지한다.
  • 텔레그램 알림은 운영자의 주의 채널이지 이벤트 버스가 아니다. 같은 원인/같은 기간의 중복 알림은 억제하고, 리포트/오류/문의/시작 장애는 종류별 시간창을 분리한다.
  • 오류 알림에는 재현성 6요소를 포함한다: 화면, 기능, 액션, 단계, 데이터 식별자, 현재 라우트. 이 정보가 없으면 운영 대응이 끝나지 않은 것으로 본다.
  • 재현 맥락은 페이지별 수동 JS 호출이 아니라 AdminTelemetryContext 같은 공통 컴포넌트가 담당한다. 새 어드민 화면은 레이아웃 경유 기본값을 자동 상속해야 하며, 예외만 명시적으로 덮어쓴다.

Code Quality Harness

원칙 적용 방식
SOLID 페이지는 orchestration만, 검증은 Application, 저장은 Repository, HTTP 계약은 DTO
유지보수 Blog/Inquiry 같은 CRUD는 List, Form, Client, Dto, Validator 패턴으로 고정
리팩토링 동작 보존 테스트를 먼저 추가하고 작은 단위로 이동
일관성 오류 응답은 ProblemDetails, 페이징은 { data, total, page, pageSize }
파편화 방지 같은 필드/상태/서비스유형 문자열은 enum/상수/공통 코드 중 하나로 단일화
과유불급 추상화는 2개 이상 실제 사용처가 생긴 뒤 도입
정규화 고객, 문의, 상담, 계약, 세금신고는 원천 테이블을 분리
역정규화 대시보드/검색/운영 요약용 스냅샷만 허용하고 원천 id와 갱신 시점을 저장
충돌방지 수정 API는 가능하면 updatedAt 또는 row version 기반 충돌 감지를 둔다
더존 UX 정신 더존 세무회계프로그램처럼 고밀도, 표준 동선, 빠른 입력, 상태 가시성, 회귀 최소화를 기본 UX 원칙으로 삼는다
추측금지 세법, 세율, 더존 필드, 운영 계정, 배포 결과는 공식 자료/코드/DB/로그 없이는 단정하지 않는다
JS 최소화 Blazor/MudBlazor 우선, 불가피한 JS는 module + dispose + 테스트 가능한 얇은 wrapper
공통코드 상태/유형/출처/위험도는 common_codes를 우선 소스로 사용하고 화면 하드코딩을 금지

Data Integrity Harness

  • DB 제약 조건이 1차 방어선이다: NOT NULL, UNIQUE, FK, CHECK, index.
  • Application validation은 사용자 메시지와 use case 규칙을 담당한다.
  • UI validation은 빠른 피드백일 뿐이며 유일한 검증으로 보지 않는다.
  • 관리자 수정 화면에 노출한 필드는 실제 저장되어야 한다. 저장하지 않는 필드는 read-only로 표시한다.
  • 상태 전이는 허용 목록을 둔다. 임의 문자열 저장을 금지한다.
  • 삭제는 운영 데이터 손실 위험이 있으면 soft delete 또는 archive를 우선 검토한다.
  • 콤보 값은 COMMON_CODE_POLICY.md를 1차 기준으로 삼는다.
  • 클라이언트 로그와 장애 진단 로그는 운영 데이터가 아니라 관측 데이터로 본다. 저장 실패는 사용자 흐름을 막지 않으며, 수집 실패 자체를 재시도 루프로 증폭하지 않는다.
  • 동일 오류의 텔레그램 재알림은 일정 기간 1회로 제한하고, 재전송 목적의 루프는 금지한다.
  • 데이터가 오류 재현에 필요하면 entity, entityId, dataKey 같은 최소 식별자만 남기고, 원문 데이터 전체를 로그에 싣지 않는다.

API-First Admin Pattern

새 어드민 기능은 클라이언트 사이드 Blazor WebAssembly를 기준으로 아래 구조를 기본 템플릿으로 따른다.

Layer Naming 책임
DTO CreateXRequest, UpdateXRequest, XResponse HTTP 계약
Controller XController 인증, 라우팅, status code, ProblemDetails
Client IXBrowserClient, XBrowserClient JWT 포함 HTTP 호출
Page XList.razor, XCreate.razor, XEdit.razor 화면 상태와 navigation
Form XForm.razor 입력 컴포넌트와 UI validation
Tests unit + Playwright/API smoke 회귀 방지

Rendering Boundary

영역 렌더링 데이터 접근
Public Home/Blog/Contact 서버 사이드 렌더링 서버 Application Service 직접 사용 가능
Admin 클라이언트 사이드 Blazor WebAssembly JWT 포함 HTTP API만 사용
Shared DTO 서버/클라이언트 공유 가능 UI 전용 상태와 DB 엔티티를 섞지 않음

공개 페이지의 SEO와 초기 로딩은 SSR로 최적화한다. 어드민은 앱처럼 동작해야 하므로 WebAssembly와 API 계약을 기준으로 설계한다.

CI Harness

완료는 로컬 성공이 아니라 CI와 배포본 성공이다.

Gate Command/Check Target
Build dotnet build TaxBaik.sln -c Release --no-restore error 0
Unit dotnet test TaxBaik.sln -c Release --no-build failed 0
Browser npx playwright test --project="Desktop Chrome" failed 0
API Smoke login + protected admin API curl HTTP 2xx
Deploy .gitea/workflows/deploy.yml success
Post Deploy .gitea/workflows/browser-e2e.yml success

Gitea Auth Harness

  • Gitea API와 workflow dispatch에는 GITEA_TOKEN_TAXBAIK만 사용한다.
  • GITEA_TOKEN은 쓰지 않는다.
  • 인증 헤더는 Authorization: token <GITEA_TOKEN_TAXBAIK>를 기본으로 한다.
  • 토큰 검증은 먼저 GET /api/v1/user로 확인하고, 그 다음 workflow_dispatch를 실행한다.
  • 401 invalid username, password or token이 나오면 토큰 이름, 공백, 환경 변수 scope를 먼저 확인한다.

Stop Conditions

  • 동일 개념이 3곳 이상 다른 이름/계약으로 구현되면 기능 추가를 중단하고 정리한다.
  • UI가 저장한다고 보이는 필드를 API/Application이 저장하지 않으면 릴리스하지 않는다.
  • 운영 배포 검증이 CI 밖에서만 가능하면 완료로 보지 않는다.
  • 데이터 모델을 추측해서 세무 규칙이나 더존 UX 관습을 왜곡해 구현하지 않는다.