# TaxBaik - 세무사 백원숙 전문성 표현 홈페이지

**온라인 세무 상담 플랫폼** | 블로그 SEO 최적화 | 전국 고객 확보

CI deploy trigger verification note.

---

## 개요

TaxBaik는 세무사 백원숙의 전문성을 온라인으로 표현하기 위해 설계된 풀스택 웹 애플리케이션입니다.

### 핵심 포지셔닝
> "사업자 세금 + 부동산 + 가족자산 = 맞춤형 세무 파트너"

### 전문가 자격
- 세무사 (세무 대리인, 기획세무사)
- 부동산중개사
- 보험설계사

---

## 기술 스택

| 계층 | 기술 |
|-----|------|
| **백엔드** | ASP.NET Core 10, C# |
| **공개 사이트** | Razor Pages (SSR) |
| **관리자** | Blazor Server + MudBlazor |
| **데이터베이스** | PostgreSQL 18.4 |
| **ORM** | Dapper |
| **리버스 프록시** | Nginx |
| **배포** | Gitea Actions CI/CD, systemd 단일 서비스 |
| **아키텍처** | DDD (Domain-Driven Design), Layered Architecture |

---

## 프로젝트 구조

```
TaxBaik/
├── TaxBaik.Domain/           # 비즈니스 규칙, 엔티티, 인터페이스
├── TaxBaik.Infrastructure/   # DB 접근, Dapper 구현체, 마이그레이션
├── TaxBaik.Application/      # 서비스, DTO, 비즈니스 워크플로우
├── TaxBaik.Web/              # Razor Pages + 관리자 통합 앱 (port 5001)
├── db/migrations/            # 데이터베이스 마이그레이션 SQL
├── deploy/                   # systemd 서비스 파일, Nginx 설정
└── .gitea/workflows/         # CI/CD 파이프라인
```

---

## 주요 기능

### 공개 사이트 (TaxBaik.Web)

- **SEO 최적화 블로그** (5개 카테고리)
  - 사업자 세무
  - 부동산 세금
  - 종합소득세
  - 부가가치세
  - 가족자산·증여

- **온라인 상담 폼**
  - 이름, 연락처, 이메일
  - 상담 분야 선택
  - 문의 내용

- **반응형 디자인**
  - 모바일 우선 (375px+)
  - 데스크톱 최적화
  - 접근성 고려

- **성능 최적화**
  - gzip 압축
  - 이미지 lazy load
  - CSS/JS 최적화

### 관리자 백오피스 (TaxBaik.Web 내 Blazor Server)

- **대시보드**
  - 이번달 문의 수
  - 신규 문의 수
  - 포스트 통계

- **블로그 관리**
  - CRUD 기능
  - 카테고리 관리
  - SEO 메타데이터
  - 발행/임시저장

- **문의 관리**
  - 문의 목록
  - 상태 변경 (신규 → 연락함 → 완료)
  - 상세 보기

- **사이트 설정**
  - 연락처 정보
  - 소셜 미디어 링크

---

## 빠른 시작

### 개발 환경 설정

**필수 요구사항:**
- .NET 10.0 SDK
- PostgreSQL 18.4
- Git

**설정 단계:**

```bash
# 1. 저장소 클론
git clone http://178.104.200.7/kjh2064/taxbaik.git
cd taxbaik

# 2. 데이터베이스 생성
createdb taxbaikdb
psql -d taxbaikdb -f db/migrations/V001__InitialSchema.sql
psql -d taxbaikdb -f db/migrations/V002__SeedData.sql
psql -d taxbaikdb -f db/migrations/V003__SeedAdminAndBlogPosts.sql
psql -d taxbaikdb -f db/migrations/V004__CreateSiteSettings.sql

# 3. 로컬 개발 환경 실행
$env:ASPNETCORE_ENVIRONMENT="Development"
dotnet watch run --project .\src\TaxBaik.Web\TaxBaik.Web.csproj

# 4. 브라우저 열기
# 공개 사이트: http://localhost:5001/
# 관리자: http://localhost:5001/admin/login
```

### 초기 로그인 정보

- 운영 관리자: `admin / Admin@123456`
- E2E 자동화: `test_admin / TestAdmin@123456`
- 프로덕션 배포 시 비밀번호 변경은 필수이며, 검증용 비밀번호는 Gitea Secrets로 관리합니다.

---

## 배포

### 배포 방식

배포는 **Gitea Actions CI/CD**만 사용합니다.

master 브랜치에 푸시하면 파이프라인이 다음 단계를 수행합니다.
1. .NET 빌드 (Release)
2. 단위 테스트 실행
3. Playwright 브라우저 검증 실행
4. `TaxBaik.Web` 게시
5. 원격 서버 배포 디렉토리 업로드 및 `taxbaik_active` 심링크 교체
6. systemd `taxbaik` 단일 서비스 재시작
7. `/`, `/admin/login`, `/blog/{slug}`, `/api/auth/login` 검증

배포 완료 판정은 위 단계가 모두 성공하고, 배포본 기준 Playwright E2E가 통과했을 때만 한다.

**필수 Gitea Secrets 설정:**
- `DEPLOY_USER`: kjh2064
- `DEPLOY_HOST`: 178.104.200.7
- `DEPLOY_SSH_KEY_B64`: base64로 인코딩한 SSH 개인키
- `TAXBAIK_ADMIN_TEST_PASSWORD`: 배포 검증용 관리자 비밀번호 (`test_admin / TestAdmin@123456`)
- `Admin__PasswordResetToken`: 관리자 비밀번호 재설정 API용 서버 비밀값

배포는 Gitea Actions CI/CD로만 수행합니다. 수동 배포 경로는 CI 하네스로 차단되어 있으며, 실패 시 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md)의 CI 점검 절차를 따릅니다.

## E2E / Smoke

공개/관리자 분리 검증은 아래 명령을 사용합니다.

로컬에서 실행할 때는 먼저 `npm run test:e2e`가 가리키는 대상 서버를 띄워둬야 합니다.

| 용도 | Bash | PowerShell |
| --- | --- | --- |
| Public smoke | `E2E_BASE_URL=https://www.taxbaik.com npm run test:e2e:public-smoke` | `$env:E2E_BASE_URL="https://www.taxbaik.com"; npm run test:e2e:public-smoke` |
| Admin smoke | `E2E_BASE_URL=https://www.taxbaik.com npm run test:e2e:admin-smoke` | `$env:E2E_BASE_URL="https://www.taxbaik.com"; npm run test:e2e:admin-smoke` |

직접 smoke 스크립트가 필요하면 이 한 줄만 쓰면 됩니다.

`ROOT_URL="https://www.taxbaik.com/" ADMIN_URL="https://www.taxbaik.com/admin/login" bash ./scripts/taxbaik-smoke.sh`

---

## 개발 지침

### 코드 컨벤션

모든 UI 문자열은 **한국어**로 작성합니다.

```csharp
// ✅ Good
public async Task<bool> SaveAsync() { ... }
var message = "저장되었습니다.";

// ❌ Bad
public bool Save() { ... }
var message = "Saved";
```

### SQL 쿼리 (Dapper)

```csharp
// ✅ 파라미터화된 쿼리 (안전)
await conn.QueryAsync<BlogPost>(
    "SELECT * FROM blog_posts WHERE id = @Id",
    new { Id = id });

// ❌ 문자열 연결 금지 (SQL injection)
var sql = "SELECT * FROM blog_posts WHERE id = " + id;
```

### Repository 패턴

```csharp
public interface IBlogPostRepository
{
    Task<BlogPost?> GetByIdAsync(int id);
    Task<IEnumerable<BlogPost>> GetPublishedAsync();
    // ...
}

public class BlogPostRepository : IBlogPostRepository
{
    private readonly IDbConnectionFactory _connectionFactory;
    
    public async Task<BlogPost?> GetByIdAsync(int id)
    {
        using var conn = _connectionFactory.CreateConnection();
        return await conn.QueryFirstOrDefaultAsync<BlogPost>(...);
    }
}
```

### 마이그레이션

```sql
-- db/migrations/V004__AddNewColumn.sql
ALTER TABLE blog_posts ADD COLUMN status VARCHAR(50) DEFAULT 'draft';
```

마이그레이션은 자동으로 시작시 실행됩니다.

---

## 문제 해결

### 마이그레이션 오류

```bash
# 마이그레이션 테이블 확인
SELECT * FROM schema_migrations;

# 실패한 마이그레이션 롤백 (수동 실행 필요)
psql -U taxbaik -d taxbaikdb -c "DELETE FROM schema_migrations WHERE version='004';"
```

### 포트 충돌

```bash
# 포트 확인
lsof -i :5001
lsof -i :5001

# 프로세스 종료
kill -9 <PID>
```

### 데이터베이스 연결

```bash
# 연결 테스트
psql -U taxbaik -d taxbaikdb -c "SELECT 1;"

# 개발 환경에서는 appsettings.Development.json 우선
echo $env:ASPNETCORE_ENVIRONMENT
echo $env:ConnectionStrings__Default
```

---

## 문서

- [docs/INDEX.md](./docs/INDEX.md) - 현재 개발 기준 인덱스
- [docs/ENGINEERING_HARNESS.md](./docs/ENGINEERING_HARNESS.md) - 코드 품질, API-first, CI/CD 하네스
- [docs/DOUZONE_UX_GUIDE.md](./docs/DOUZONE_UX_GUIDE.md) - 더존식 어드민 UX 원칙과 템플릿 기준
- [docs/COMMON_CODE_POLICY.md](./docs/COMMON_CODE_POLICY.md) - 공통코드 저장값/컬럼 길이/하드코딩 금지 기준
- [docs/COMBO_POLICY.md](./docs/COMBO_POLICY.md) - 콤보/검색/선택 입력 정책
- [docs/ADMIN_PATTERN_CRITIQUE_WBS.md](./docs/ADMIN_PATTERN_CRITIQUE_WBS.md) - 어드민 패턴 비판 및 정량 WBS
- [CLAUDE.md](./CLAUDE.md) - 보조 LLM 개발 지침
- [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md) - 배포 완전 가이드
- [SERVER_SETUP.sh](./SERVER_SETUP.sh) - 서버 자동 설치 스크립트

---

## 연락처

- **전화**: 010-4122-8268
- **이메일**: taxbaik5668@gmail.com
- **카카오 채널**: http://pf.kakao.com/_xoxchTX
- **인스타그램**: https://www.instagram.com/taxtory5668/

---

## 라이선스

내부 사용 전용

---

## 개발 로그

### W0 - 프로젝트 기반 구축
- ✅ Visual Studio 솔루션 + 5개 프로젝트 생성
- ✅ Git 초기화 및 Gitea 저장소 연결
- ✅ PostgreSQL 스키마 + 마이그레이션 파일
- ✅ Nginx 리버스 프록시 설정

### W1 - LLM 개발 지침
- ✅ CLAUDE.md 작성 (9개 섹션, 500+ 라인)

### W2 - Domain/Infrastructure/Application
- ✅ 엔티티: BlogPost, Category, Inquiry, AdminUser
- ✅ Dapper Repository 패턴 구현
- ✅ 서비스 레이어 (BlogService, InquiryService)
- ✅ DTO 및 예외 처리

### W3 - 공개 홈페이지 (Razor Pages SSR)
- ✅ 레이아웃 및 네비게이션
- ✅ 메인 랜딩 페이지
- ✅ 블로그 (목록 + 상세)
- ✅ 문의 폼 (유효성 검증)
- ✅ SEO 최적화 (메타 태그, sitemap.xml)
- ✅ robots.txt

### W4 - 관리자 백오피스 (Blazor Server + MudBlazor)
- ✅ 인증 (Cookie 기반, 8시간 세션)
- ✅ 대시보드 (KPI 카드)
- ✅ 블로그 CRUD
- ✅ 문의 관리
- ✅ 사이트 설정

### W5 - 스타일링 & 모바일 UX
- ✅ CSS 디자인 시스템 (색상, 스페이싱, 전환)
- ✅ 모바일 반응형 (375px 초소형 화면까지)
- ✅ 성능 최적화 (gzip, lazy load, 폰트 preconnect)

### W6 - 출시 준비
- ✅ 자동 DB 마이그레이션 (MigrationRunner)
- ✅ 초기 데이터 (관리자 + 블로그 5개 포스트)
- ✅ E2E 테스트 절차
- ✅ 배포 가이드
- ✅ 서버 자동 설치 스크립트
- ✅ Gitea Actions CI/CD

---

**최종 상태**: 진행 중

완료 판정은 실제 빌드, 테스트, 배포 검증, 브라우저 E2E 통과로만 한다.
