# Admin Pattern Critique And WBS 대상은 어드민 Blog, 문의사항, 등록/수정 페이지 전반이다. 이 문서는 비판, 개선 방향, 정량 완료 기준을 한 곳에 둔다. ## Brutal Critique | 영역 | 현재 문제 | 왜 위험한가 | 개선 기준 | | --- | --- | --- | --- | | API-first 위반 | 어드민 Razor 컴포넌트가 `BlogService`, `InquiryService`, repository를 직접 주입 | 어드민을 클라이언트 사이드 Blazor WebAssembly로 운용할 때 구조가 깨지고 API 계약 테스트가 우회된다 | 모든 어드민 화면은 BrowserClient를 통해 `/api/*` 호출 | | Blog 등록/수정 중복 | `BlogCreate.razor`와 `BlogEdit.razor`가 필드, JS 편집기, 저장 로직을 반복 | 한쪽만 수정되는 파편화가 생긴다 | `BlogForm.razor` + `BlogEditorJsModule` 패턴 | | JS 과다/전역 상태 | `window.easyMDEInstance` 단일 전역 인스턴스 사용 | 페이지 이동/다중 편집/재렌더에서 내용 섞임 위험, Blazor 책임 경계가 흐려진다 | JS 제거 우선 검토, 불가피하면 JS module + element별 instance map + dispose | | 문의 수정 착시 | `InquiryEdit`가 이름/전화/이메일/내용 수정 UI를 보여주지만 실제 저장은 상태/메모 중심 | 운영자가 저장 성공을 믿어도 핵심 데이터가 DB에 반영되지 않을 수 있다 | 전체 수정 API를 만들거나 해당 필드를 read-only 처리 | | 문자열 상태 난립 | 문의 상태, 서비스 유형이 UI 문자열/API 문자열/DB 값으로 분산 | 오타 하나가 통계와 필터를 깨뜨린다 | enum/공통코드/상태 mapper 단일화 | | 삭제 위험 | Blog/Inquiry 삭제가 즉시 hard delete | 운영 감사, 상담 이력, SEO URL 보존에 취약 | soft delete 또는 archive 정책 | | 정합성 부족 | Blog slug 생성이 전체 목록 조회 기반 | 동시 생성 충돌에 약하고 데이터가 늘면 느려진다 | DB unique index + 충돌 재시도 | | 템플릿 부재 | CRUD 페이지마다 버튼, 오류, 로딩, 페이징 패턴이 다름 | 바이브코딩식 흔들림이 반복된다 | List/Form/Detail/PageState 템플릿화 | | 배포 완료 착시 | 문서상 완료 항목과 운영 검증 항목이 섞임 | 체크박스가 실제 성공을 대체한다 | WBS는 수치, 로그, CI URL로만 완료 | ## Target Admin Pattern ```text Razor Page/Form -> BrowserClient with JWT -> Controller DTO -> Application Service -> Repository -> DB constraints/indexes ``` 어드민은 클라이언트 사이드 Blazor WebAssembly 기준이다. 예외는 명시해야 한다. 서버 전용 컴포넌트가 Application Service를 직접 호출해야 한다면 `ENGINEERING_HARNESS.md`의 API-first 기준에 대한 사유와 제거 예정 WBS를 남긴다. ## Quantitative Success Metrics | 지표 | 기준값 | 측정 방법 | | --- | --- | --- | | Admin direct service injection | 0건 | `rg "@inject .*Service|@inject I.*Repository" TaxBaik.Web/Components/Admin` | | Blog create/edit duplicate fields | 0개 중복 폼 | `BlogForm.razor` 단일 사용 여부 | | Admin JavaScript surface | 필수 module만 허용 | `window.*` 전역 admin JS 0건, JS interop 사유 문서화 | | Inquiry visible-but-unsaved fields | 0개 | E2E로 수정 후 API 재조회 | | Protected admin API anonymous access | 0개 | API smoke에서 401/403 확인 | | CI required gates | 6/6 통과 | build, unit, publish, deploy, browser e2e, api smoke | | Playwright admin flows | 8개 이상 통과 | login, blog CRUD, inquiry CRUD/status, responsive, password, smoke | | DB integrity constraints | 핵심 테이블 100% | PK, FK, unique/check/index 리뷰 | | WBS evidence coverage | 100% | 각 완료 항목에 command/log/test 파일 기재 | ## Roadmap | Phase | 목적 | 종료 조건 | | --- | --- | --- | | P0 Harness | 기준 고정과 문서 최소화 | 이 문서와 Engineering Harness가 README에서 참조됨 | | P1 Stabilize | Blog/Inquiry 착시와 중복 제거 | API-first 전환, 공통 폼, 정합성 테스트 통과 | | P2 Harden | DB 제약, 충돌 방지, 삭제 정책 | migration + 회귀 테스트 + E2E 통과 | | P3 Standardize | CRUD 템플릿화와 반복 패턴 제거 | 신규 CRUD 생성 시 템플릿만 사용 | | P4 Integrate | 더존 UX 정신 내재화 | 고밀도 화면, 표준 동선, 빠른 입력, 상태 가시성, 회귀 최소화 검증 | | P5 Operate | CI/CD와 운영 지표 고도화 | 배포본 기준 smoke/E2E/로그 알림 안정화 | ## Detailed WBS | ID | 작업 | 산출물 | 정량 완료 기준 | | --- | --- | --- | --- | | P0-01 | 문서 기준점 정리 | `docs/INDEX.md`, `ENGINEERING_HARNESS.md` | canonical 문서 3개 이하, README 링크 1곳 | | P0-02 | 기존 장문 문서 역할 축소 | README 문서 섹션 정리 | `CLAUDE.md`를 보조자료로 표시 | | P1-01 | Blog API client 도입 | `IBlogBrowserClient`, `BlogBrowserClient` | Blog admin page direct service/repository injection 0건 | | P1-02 | Blog 공통 폼 도입 | `BlogForm.razor` | create/edit 필드 중복 0건, 저장 E2E 2개 통과 | | P1-03 | Markdown editor JS 최소화/격리 | Blazor 대체 또는 JS module | 전역 `window.easyMDEInstance` 사용 0건, JS interop 사유 명시 | | P1-04 | Inquiry 수정 계약 확정 | `UpdateInquiryRequest` 또는 read-only UI | 화면 표시 editable 필드와 저장 필드 불일치 0건 | | P1-05 | Inquiry API client 도입 | `IInquiryBrowserClient` 정비 | Inquiry admin direct service injection 0건 | | P1-06 | 상태/서비스 유형 단일화 | enum/common code/mapper | 상태 문자열 하드코딩 UI 위치 0건 또는 공통 상수 참조 | | P2-01 | Blog slug 충돌 방지 | unique index + retry | 동시 생성 테스트 1개 통과 | | P2-02 | 삭제 정책 정리 | soft delete migration 또는 archive 정책 | hard delete 운영 엔티티 0건 또는 예외 문서화 | | P2-03 | DB index 점검 | migration | 목록/검색/상태 필터 explain 기준 seq scan 위험 제거 | | P2-04 | 낙관적 충돌 방지 | `updatedAt` 조건부 update | stale update API 테스트 1개 이상 통과 | | P3-01 | CRUD 템플릿 작성 | page/form/client/test skeleton | 신규 admin CRUD 생성 시간 30% 감소 | | P3-02 | 공통 PageState/Error 처리 | reusable component/service | admin page 중복 try/catch/snackbar 패턴 50% 감소 | | P3-03 | 메뉴/라우팅 표준화 | route registry 또는 constants | admin route 문자열 중복 50% 감소 | | P4-01 | 더존 UX 패턴 캡슐화 | 고밀도 grid/form/template 규칙 | 신규 어드민 화면이 템플릿을 따르지 않는 경우 0건 | | P4-02 | UX 회귀 검증 | responsive, keyboard flow, density, state visibility test | 핵심 CRUD 화면 E2E 100% 통과 | | P5-01 | CI gate 명문화 | workflow 체크 목록 | 6개 gate 모두 required | | P5-02 | 배포본 API smoke 확장 | workflow curl 추가 | Blog/Inquiry create-read-update test 2xx | | P5-03 | 운영 회귀 대시보드 | test report/version endpoint | 배포 커밋과 E2E 결과 추적 가능 | ## Immediate Refactor Order 1. `InquiryEdit` 착시 제거: 전체 수정 API를 추가하거나 저장 안 되는 필드를 read-only로 바꾼다. 2. `BlogForm.razor`를 만들고 create/edit 중복을 제거한다. 3. Blog/Inquiry 어드민 페이지를 BrowserClient 경유로 바꾼다. 4. 상태/서비스 유형 문자열을 단일 source로 모은다. 5. DB 제약과 삭제 정책을 migration으로 고정한다. ## Completion Rule WBS 항목은 다음 네 가지가 모두 있어야 완료다. - 관련 코드 또는 문서 diff - 로컬 검증 명령과 결과 - CI/CD workflow 성공 - 배포본 기준 API 또는 Browser E2E 증거