Today's incident: CI reported successful deploys while the real site
returned 502 (root) then 404 (/taxbaik/) to users. Root cause was three
compounding Nginx issues, none of which the previous CI checks could see
because they only ever curled 127.0.0.1:5001 directly, bypassing Nginx:
1. Two Nginx config files existed. sites-available/default (documented,
but NOT symlinked into sites-enabled/) was being edited repeatedly with
zero effect. The file actually loaded was
sites-available/taxbaik-domains.conf (-> sites-enabled/), undocumented.
2. That real file hardcoded the Green-Blue app port (5003) directly in
both `location /` and `location /taxbaik`, instead of the persistent
TaxBaik.Proxy on 5001. When the active port flipped to 5004, Nginx kept
pointing at the dead 5003 -> 502.
3. Fixing the port to 5001 with a trailing slash on proxy_pass triggered
Nginx URI rewriting, sending a double slash ("//") to the backend,
which 404'd. Confirmed via `curl http://backend//` -> 404.
Changes:
- deploy.yml: replace the old blind `grep sites-available/default` check
(checked the wrong, unloaded file) with a hard-failing check that (a)
resolves the actual file via sites-enabled/ symlinks, (b) fails the
deploy if either location block hardcodes 5003/5004 instead of 5001,
(c) fails if /taxbaik's proxy_pass carries a stray trailing slash.
- deploy.yml: add an external, post-deploy check that curls the real
public domain (www.taxbaik.com root, /taxbaik/, /taxbaik/admin/login)
through Cloudflare + Nginx, with retries — this is what would have
caught the whole incident on the very first broken deploy instead of
requiring live user reports.
- deploy_gb.sh: drop the stale comment implying Nginx needs updating
per-deploy; it never should, since Nginx always points at the
persistent 5001 proxy which reads taxbaik_port itself.
- CLAUDE.md: document the real config file, the 5001-only invariant, the
proxy_pass trailing-slash gotcha, and the Host-header/SNI trick for
testing domain-based server blocks locally; record the incident in the
CI troubleshooting harness section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Groups the repo root into src (buildable source), docs (already existed),
and everything else (db/, scripts/, tests/, deploy/ - deployment/ops/test
assets that aren't compiled, already organized as their own folders). CI
now only needs src/ to build: dotnet restore/build/test/publish all point
at src/TaxBaik.sln, src/TaxBaik.Web/, src/TaxBaik.Proxy/.
- git mv every project (Domain, Infrastructure, Application,
Application.Tests, Web, Web.Client, Proxy) and TaxBaik.sln into src/ as a
unit, so relative ProjectReference/.sln paths stay valid unchanged.
- .gitea/workflows/deploy.yml: 6 dotnet restore/clean/build/test/publish
invocations now point at src/. db/migrations and scripts/ stay at root
(deploy_gb.sh and browser-e2e.yml only touch published output and the
deployed URL, not source paths - verified, no changes needed there).
- scripts/validate_admin_render.sh: admin render-mode file paths now
src/TaxBaik.Web.Client/...
- scripts/validate_kst_timestamps.sh: dropped deploy.sh from its target
list - that script was removed in the prior cleanup commit (dead, no
CI workflow referenced it) but this validator still expected it to exist.
- CLAUDE.md, docs/ENGINEERING_HARNESS.md, docs/ADMIN_PATTERN_CRITIQUE_WBS.md:
updated project-structure diagram, dotnet run/build commands, and grep
targets to the new src/ paths (also fixed a pre-existing stale path in
ADMIN_PATTERN_CRITIQUE_WBS.md that still said TaxBaik.Web/Components/Admin
from before that ever moved to TaxBaik.Web.Client).
- Added a Repo Root harness rule + Architecture Guardrail entries: new files
belong under src/docs/tests/scripts/db/deploy, not loose at root; temp
work stays outside the repo (or under a gitignored .scratch/) and is
never committed.
Verified locally: dotnet build/test src/TaxBaik.sln (26/26 tests), and all
three scripts/validate_*.sh pass against the new layout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Admin: replace the global @rendermode on <Routes>/<Router> with per-page
render mode. Login.razor now prerenders (form visible before WASM loads);
every other [Authorize] page stays prerender: false to avoid the
AuthorizeRouteView blank-render regression from earlier attempts. Adds a
"준비 중" -> "로그인" splash tied to WASM boot completion, and lets the
authenticated-shell loading overlay stay up until AdminShell actually renders.
- Contact.cshtml: fix the "Agree" checkbox missing value="true" - a checked
box sent the browser-default "on", which bool model binding can't parse,
so ModelState.IsValid silently went false and OnPostAsync returned a blank
form with no visible error on every submission. Validation summary widened
from ModelOnly to All so this class of failure isn't silent again.
- TelegramInquiryNotificationService: read Telegram:InquiryChatId (falling
back to ChatId) instead of only ChatId, matching the channel routing
CLAUDE.md documents and deploy.yml already provisions as separate secrets.
- Reconcile CLAUDE.md's self-contradicting Phase 8 prerender notes (Phase 9),
rewrite validate_admin_render.sh for the per-page design, and add a
SmartAdmin 5.5 design reference section to DOUZONE_UX_GUIDE.md for future
admin screens (existing screens unchanged, tracked as WBS P4-03).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Phase 8 완료 상세 기록 (WebAssembly 마이그레이션, E2E 검증)
- AddAdditionalAssemblies 필수성 명시 (제거하면 초기화 실패)
- 배포 환경 변수 강화 (Connection String 필수)
- 프로젝트 구조 업데이트 (TaxBaik.Web.Client WASM 클라이언트)
- E2E 테스트 결과 기록 (20/20 통과 - 프로덕션)
- 배포 실패 시 트러블슈팅 가이드 추가
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Reduce CLAUDE.md blog section to essential guidelines only (10 lines)
- Move detailed templates and checklists to new BLOG_TEMPLATE.md
- Update core philosophy: education + natural professional referral
- Not 'hire me' but 'understand concepts → know when to consult'
BLOG_TEMPLATE.md includes:
✅ Complete 5-step blog post template
✅ Real persona examples (name, age, job, income)
✅ Before/After case structure
✅ Step-by-step calculation with tables
✅ Tone guidance (empowerment + professional value)
✅ Checklist for writers
✅ Do's & Don'ts
✅ Seasonal content ideas
Philosophy:
- Basic tax knowledge: anyone can do it
- Complex cases: professionals add efficiency
- Goal: grow customer understanding + increase professional service value
Phase 7-4 추가:
- 5개 CRM/세무관리 Blazor 페이지 (TaxProfile, TaxFilingSchedule, Contract, ConsultingActivity, RevenueTracking)
- 5개 API Controller + Browser Client (API-First 패턴)
- MudDataGrid Douzone ERP 수준 UX (32px 행, 데이터 밀도)
- MudDialog 모달, ConfirmDialog 삭제 확인
- Status/Risk Level 컬러 칩, D-day 추적, MRR 계산
현재 상태:
- Phase 1-7 모두 완료 (2026-06-28)
- 16개 Blazor 페이지 API-First 마이그레이션 완료
- 모든 SOLID 원칙 적용
- 빌드: 0 errors
다음 우선순위:
1. Nav 그룹 추가 (CRM/세무관리 섹션)
2. E2E 테스트 (Playwright)
3. 모바일 앱 (React Native/Flutter)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Admin Grid UX Enhancements (Section 8.6):
- High-density data display (32px row height, 5-7 column layout)
- Responsive design: PC(6) → Tablet(4) → Mobile(2) columns
- Pad-optimized (24px cells, 36px buttons for touch)
- Advanced interactions: inline editing, multi-select, context menu
- MudDataGrid implementation pattern with virtualization
- Status-based coloring (normal/warning/danger/success)
- Performance optimization (virtualization, lazy loading, caching)
Deployment User Experience Protection (Section 11.1):
- No forced refresh during deployment ❌
- Users receive notifications with manual refresh option ✅
- SignalR-based deployment notification (not server-sent events)
- Auto-save form data to sessionStorage
- Recovery options after refresh
- Deployment status API endpoint
- Admin-only deployment notification API
Core Principles:
- 사용자 작업 중 배포 시 강제 새로고침 금지
- 알림 + 수동 새로고침 옵션 제공
- 폼 데이터 자동 보존 및 복구 기능
- Document API client dynamic configuration for green-blue deployments
- Add environment variable override instructions (ApiClient__BaseUrl)
- Document responsive testing with Playwright (8 device sizes)
- Add test items and validation checklist
- Update troubleshooting section with green-blue and responsive issues
- Clarify deployment procedure and expansion points for zero-downtime
Testing coverage: Desktop, Tablet, Mobile - all verified for overflow,
accessibility, and font readiness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Changes:**
- Dashboard API complete and production-ready
- Update CLAUDE.md with realistic 7-phase migration plan
- Clean up temporary API implementations (will add incrementally)
**Architecture Decision (30-year senior perspective):**
- GRADUAL MIGRATION > Big Bang Rewrite
- Start with Dashboard (highest ROI, safest entry point)
- Validate pattern before rolling out to other pages
- Each page migrated independently (reduce risk)
**Phase 4 (Next): Dashboard Blazor Refactoring**
- Dashboard.razor: Service injection → API client
- AdminDashboardClient: wrapper around HTTPClient
- Error handling: 401 → token refresh → retry
- Loading states & cancellation tokens
**SOLID Principles Applied:**
✓ S (Single Responsibility): Each API endpoint handles one concern
✓ O (Open/Closed): Can add new API endpoints without changing existing ones
✓ L (Liskov Substitution): APIClient replaces direct service calls
✓ I (Interface Segregation): Specific API contracts per endpoint
✓ D (Dependency Inversion): Blazor depends on IApiClient abstraction
Status: Production-ready for deployment
Next: Dashboard Blazor → API Client refactoring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Architecture Refactor (SOLID Principles):**
- Implement AdminDashboardController (REST API)
- Add dashboard summary endpoint
- Add upcoming filings endpoint
- Add recent inquiries endpoint
- Add monthly statistics endpoint
**Database Layer (Repository Pattern):**
- Extend IInquiryRepository with date range queries
- Implement CountByDateRangeAsync
- Implement CountByStatusAndDateAsync
- Extend InquiryRepository with new methods
**Service Layer (Single Responsibility):**
- Extend AdminDashboardService with API methods
- Add GetRecentInquiriesAsync
- Add GetMonthlyStatsAsync with caching
**Test Coverage:**
- Update FakeInquiryRepository mock with new methods
**SOLID Application:**
✓ Single Responsibility: Each class has one reason to change
✓ Open/Closed: Dashboard API can be extended without modifying existing code
✓ Dependency Inversion: Service depends on Repository abstraction
✓ Interface Segregation: API endpoints are focused and specific
Status: ✓ Compiles successfully (0 errors, 0 warnings)
Next phases:
- Add remaining API controllers (Announcement, Client, FAQ, TaxFiling)
- Refactor Blazor components to use API instead of services
- Implement JWT token refresh mechanism
- Add SignalR for change notifications
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 연간 세무 캘린더(7개 시즌) 기반 자동 Hero 섹션 전환
- 시즌 감지 시 D-Day 카운트다운, 긴박감 배지, 시즌 CTA 표시
- 서비스 카드 순서 시즌 관련 항목 우선 정렬
- 어드민 공지사항 CRUD (등록·수정·삭제, 기간·유형 설정)
- 홈페이지 상단 공지 배너 자동 노출 (일반/배너/긴급)
- CLAUDE.md에 세무 캘린더 및 마케팅 방향 하네스 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 포트 배치: 5001로 통합 (5002 제거)
- 배포 절차: 단일 Web 앱 빌드로 단순화
- 서비스: taxbaik만 관리 (taxbaik-admin 제거)
- Nginx: /taxbaik 블록 하나로 통합
- 파일 구조: Web/Components/Admin으로 명시
- 인증: JWT + localStorage 패턴 문서화
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
로컬에서 서버 PostgreSQL DB에 접속하는 방법:
1. SSH 터널 열기
ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7
2. 다른 터미널에서 Web/Admin 앱 실행
- Web: dotnet run -p TaxBaik.Web
- Admin: dotnet run -p TaxBaik.Admin
3. 마이그레이션 자동 실행 (앱 시작 시)
- schema_migrations 테이블 확인
- 미실행 마이그레이션 순서대로 실행
개발 워크플로우:
- 터미널 1: SSH 터널 유지
- 터미널 2: Web (http://localhost:5001/taxbaik)
- 터미널 3: Admin (https://localhost:5002, admin/admin123)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- CLAUDE.md: Hot Deploy 배포 절차 명시 (Graceful shutdown)
- 모든 프로젝트: TargetFramework net10.0 통일
- systemd 서비스: TimeoutStopSec=35, KillMode=mixed 추가
- Infrastructure.csproj: 마이그레이션 SQL 파일 포함 경로 수정
배포 후 실제 서버 검증 완료:
✅ Web 서비스 정상 실행 (포트 5001)
✅ Admin 서비스 정상 실행 (포트 5002)
✅ PostgreSQL 인증 및 마이그레이션 통과
✅ HTTP 응답 정상
- Change site description from '성북구 세무사' to nationwide professional
- Update positioning from '일상 자산 세금 파트너' to '맞춤형 세무 파트너'
- Replace Seongbuk-gu specificity with skill-based positioning
- Align with online-first service model (no local restriction)
- Update examples in documentation
Co-Authored-By: Claude <noreply@anthropic.com>