# 보험유니버스 백엔드 API (insuniverse-server)

보험 분석 및 관리 통합 플랫폼 **보험유니버스**의 백엔드 API 서버입니다.

| 항목 | 스펙 |
|------|------|
| 프레임워크 | NestJS 10.4 |
| 언어 | TypeScript 5.5 |
| ORM | Drizzle ORM 0.33 |
| 데이터베이스 | MySQL 8 |
| 런타임 | Node.js 20 |
| 포트 | 7200 |

---

## 목차

1. [프로젝트 구조](#1-프로젝트-구조)
2. [로컬 개발 환경 설정](#2-로컬-개발-환경-설정)
3. [환경변수](#3-환경변수)
4. [데이터베이스](#4-데이터베이스)
5. [외부 서비스 연동](#5-외부-서비스-연동)
6. [스케줄러](#6-스케줄러)
7. [배포](#7-배포)
8. [API 문서 (Swagger)](#8-api-문서-swagger)
9. [개발 컨벤션](#9-개발-컨벤션)

---

## 1. 프로젝트 구조

NestJS **모노레포** 구조로, 1개의 앱(`api`)과 공유 라이브러리(`libs`)로 구성되어 있습니다.

```
insuniverse-server/
├── apps/
│   └── api/
│       └── src/
│           ├── main.ts              # 앱 진입점 (포트 7200)
│           ├── app.module.ts        # 루트 모듈 (50개+ 모듈 임포트)
│           ├── config/              # 설정
│           ├── constants/           # 상수
│           ├── filters/             # 예외 필터
│           ├── helpers/             # 헬퍼 함수
│           ├── interceptors/        # 인터셉터
│           ├── pipes/               # 파이프
│           ├── schedules/           # 스케줄러 (13개)
│           └── modules/             # 비즈니스 모듈 (34개)
│
├── libs/                            # 공유 라이브러리
│   ├── aligo/                       # 카카오 알림톡 / SMS
│   ├── chat/                        # Google Chat 웹훅
│   ├── cloudfront/                  # AWS CloudFront 캐시 무효화
│   ├── codef/                       # CODEF 건강보험공단 연동
│   ├── crypto/                      # AES-256 암·복호화
│   ├── drizzle/                     # Drizzle ORM 설정 및 DB 연결
│   ├── excel/                       # ExcelJS 기반 엑셀 생성
│   ├── mailer/                      # Nodemailer 이메일 발송
│   ├── pdf/                         # PDF 생성 (pdf-lib)
│   ├── pg/                          # 결제 게이트웨이 (토스/스마트로)
│   ├── s3/                          # AWS S3 파일 업로드
│   ├── separator/                   # 데이터 파싱 유틸
│   ├── utils/                       # 공통 유틸리티
│   ├── webclient/                   # HTTP 클라이언트 (Axios)
│   └── youtube/                     # YouTube API
│
├── drizzle/
│   ├── schema.ts                    # 테이블 정의
│   └── relations.ts                 # 관계 정의
│
├── .env.example                     # 환경변수 템플릿
├── nest-cli.json                    # NestJS 모노레포 설정
├── tsconfig.json                    # TypeScript 설정
└── apps/api/dockerfile              # Docker 빌드 파일
```

### Path Alias

`tsconfig.json`에 정의된 경로 별칭입니다.

| Alias | 실제 경로 | 용도 |
|-------|----------|------|
| `@api/*` | `apps/api/src/*` | API 앱 내부 모듈 참조 |
| `@lib/*` | `libs/*` | 공유 라이브러리 참조 |
| `@libs/toss` | `libs/pg/src/toss` | 토스페이먼츠 모듈 |
| `@libs/smartro` | `libs/pg/src/smartro` | 스마트로 결제 모듈 |

---

## 2. 로컬 개발 환경 설정

### 요구사항

- **Node.js** 20.x
- **MySQL** 8.x
- **npm**

### 설치 및 실행

```bash
# 1. 의존성 설치
npm install

# 2. 환경변수 설정
cp .env.example apps/api/.env
# apps/api/.env 파일을 열어 실제 값을 채워주세요.
# (DB 접속 정보, API 키 등은 팀 내부에서 공유)

# 3. 개발 서버 실행 (hot-reload)
npm run start:dev

# 4. 프로덕션 빌드 & 실행
npm run build
npm run start:prod
```

서버가 정상 기동되면 `http://localhost:7200` 에서 접근 가능합니다.

---

## 3. 환경변수

### 로컬 개발

`.env.example` 파일을 `apps/api/.env`로 복사한 후 값을 채워 사용합니다.

```bash
cp .env.example apps/api/.env
```

> 실제 API 키, 시크릿 등은 팀 내부 문서(노션)를 참고하세요.

### 배포 환경 (dev / stg / prod)

환경변수 파일은 **AWS S3 버킷**에서 관리됩니다.

| 환경 | S3 경로 |
|------|---------|
| dev | `s3://insuniverse-dev-envs/api/dev.env` |
| stg | `s3://insuniverse-stg-envs/api/stg.env` |
| prod | `s3://insuniverse-prod-envs/api/prod.env` |

CI/CD 파이프라인에서 빌드 시 S3에서 자동 다운로드합니다.

---

## 4. 데이터베이스

### 테이블 네이밍 규칙

| 프리픽스 | 의미 | 예시 |
|---------|------|------|
| `tn_` | Transaction (거래/운영 데이터) | `tn_member`, `tn_order` |
| `tc_` | Code (코드성/마스터 데이터) | `tc_compare_basic` |
| `th_` | History (이력 데이터) | `th_invoice` |
| `tt_` | Treat (진료 원본 데이터) | `tt_treat_basic` |
| `log_` | Log (로그) | `log_treat` |

### 컬럼 네이밍 규칙

각 테이블은 **고유 접두어**를 가지며, 모든 컬럼에 해당 접두어를 붙입니다.

| 테이블 | 접두어 | 컬럼 예시 |
|--------|--------|----------|
| `tn_member` | `mem_` | `mem_id`, `mem_name`, `mem_phone` |
| `tn_order` | `ord_` | `ord_id`, `ord_type`, `ord_oid` |
| `tn_subscribe` | `sub_` | `sub_id`, `sub_state`, `sub_next_pay_day` |
| `tn_payment` | `pmt_` | `pmt_id`, `pmt_amount`, `pmt_tid` |

### B2C 테이블

정회원 테이블과 구조가 동일하되 프리픽스만 다릅니다. (`b` 접두어 추가)

| 정회원 테이블 | B2C 테이블 | 컬럼 프리픽스 |
|-------------|-----------|-------------|
| `tn_order_detail` | `tn_b2c_order_detail` | `odd_` → `bod_` |
| `tn_analysis_basic` | `tn_b2c_analysis_basic` | `asb_` → `basb_` |
| `tt_treat_basic` | `tt_b2c_treat_basic` | `trb_` → `btrb_` |

### 암호화 필드

개인정보 필드는 **AES-256**으로 암호화되어 저장됩니다.

- `tn_member`: `mem_name`, `mem_email`, `mem_phone`
- `tn_user`: `usr_name`, `usr_phone`, `usr_birth`

> 암·복호화는 `libs/crypto` 모듈을 통해 처리합니다.

### Drizzle ORM 명령어

```bash
# DB → 스키마 파일로 가져오기 (introspect)
npm run db:pull

# 스키마 변경 마이그레이션 파일 생성
npm run db:generate
```

> **주의: `npm run db:push`는 사용하지 마세요.**
> DB에 등록된 프로시저가 손실될 수 있습니다.
> 스키마 변경이 필요한 경우 `ALTER TABLE` SQL을 직접 작성하여 적용합니다.

### 스키마 변경 절차

1. `drizzle/schema.ts` 수정
2. `ALTER TABLE` SQL 직접 작성 (모든 컬럼에 COMMENT 필수)
3. 담당자 확인 후 DB에 SQL 실행
4. 해당 테이블을 사용하는 코드 영향도 체크 (Repository, Service, DTO, 엑셀 다운로드 등)

---

## 5. 외부 서비스 연동

| 서비스 | 용도 | 라이브러리 위치 |
|--------|------|---------------|
| CODEF | 건강보험공단 진료내역 조회 | `libs/codef/` |
| 토스페이먼츠 | 구독 결제 / 빌링키 관리 | `libs/pg/src/toss/` |
| 스마트로 | 결제 승인 / 취소 | `libs/pg/src/smartro/` |
| 알리고 | 카카오 알림톡 / SMS 발송 | `libs/aligo/` |
| Firebase | 모바일 푸시 알림 (FCM) | `apps/api/src/modules/fcm/` |
| AWS S3 | 파일 업로드 / 관리 | `libs/s3/` |
| AWS CloudFront | CDN 캐시 무효화 | `libs/cloudfront/` |
| MOK (드림시큐리티) | 본인인증 (휴대폰) | `apps/api/src/modules/auth/` |
| Nodemailer | 이메일 발송 | `libs/mailer/` |
| YouTube API | 교육 영상 정보 조회 | `libs/youtube/` |
| vFax | 팩스 발송 | `apps/api/src/modules/` |
| Google Chat | 배포/운영 알림 웹훅 | `libs/chat/` |

---

## 6. 스케줄러

스케줄러는 `apps/api/src/schedules/` 에 위치하며, **AWS EventBridge**를 통해 실행됩니다.
NestJS의 `@nestjs/schedule`이 아닌 **독립 실행 스크립트** 방식입니다.

| 스케줄러 | 설명 |
|---------|------|
| `regular-subscription` | 일반FC 정기구독 결제 |
| `regular-agency-subscription` | 기업FC 정기구독 결제 |
| `alimtalk-status` | 알림톡 발송 완료 여부 확인 |
| `analysis-status` | 주문상세(분석) 상태 변경 |
| `delete-treat` | 심평원 원본 임시 데이터 삭제 |
| `disable-subscription` | 구독 해지 처리 |
| `settlement-agency` | 대리점 정산 |
| `settlement-client` | 법인 정산 |
| `delete-analysis` | 분석 결과 데이터 삭제 |
| `delete-fc` | FC 삭제 처리 |
| `sales-statistics` | 영업대리점 데일리 통계 발송 |
| `expired-money` | 머니 소멸 처리 |
| `delete-invoice-files` | 청구서 파일 삭제 |

### Docker에서 스케줄러 실행

Docker 이미지는 `RUN_MODE`와 `SCHEDULER_TYPE` 환경변수로 API/스케줄러를 구분합니다.

```bash
# API 서버 실행
docker run -e RUN_MODE=api ...

# 특정 스케줄러 실행
docker run -e RUN_MODE=scheduler -e SCHEDULER_TYPE=regular-subscription ...
```

> 새로운 스케줄러 생성 방법은 **노션 위키 "ECS기반의 스케줄러 생성하기"** 문서를 참고하세요.

---

## 7. 배포

### 브랜치 전략

| 브랜치 | 환경 | 용도 |
|--------|------|------|
| `dev` | 개발 | 개발 서버 배포 |
| `stg` | 스테이징 | QA / 검증 |
| `prod` | 프로덕션 | 실 서비스 |

### CI/CD 파이프라인

`dev`, `stg`, `prod` 브랜치에 push 시 GitHub Actions가 자동 실행됩니다.

```
push → GitHub Actions 트리거
  ├── 1. 빌드 (npm install → npm run build)
  ├── 2. S3에서 환경변수(.env) 다운로드
  ├── 3. Docker 이미지 빌드 (Node 20 Alpine, 멀티스테이지)
  ├── 4. ECR에 이미지 푸시
  ├── 5. ECS 태스크 정의 업데이트
  ├── 6. ECS 서비스 배포
  └── 7. Google Chat 배포 알림
```

### AWS 인프라 구성

| 구성요소 | 네이밍 규칙 |
|---------|-----------|
| ECS 클러스터 | `insuniverse-{env}` |
| ECR 리포지토리 | `insuniverse/{env}/api` |
| ECS 태스크 정의 | `insuniverse-{env}-api` |
| S3 환경변수 | `insuniverse-{env}-envs/api/{env}.env` |

---

## 8. API 문서 (Swagger)

프로덕션을 제외한 환경에서 Swagger UI를 제공합니다.

- **로컬**: `http://localhost:7200/api-docs` (기본 경로, 실제 경로는 swagger.ts 참조)

`nest-cli.json`에 `@nestjs/swagger` 플러그인이 설정되어 있어 DTO 클래스에서 자동으로 스키마를 생성합니다.

---

## 9. 개발 컨벤션

### 코딩 스타일

상세 가이드는 [`docs/CODE_STYLE_GUIDE.md`](../docs/CODE_STYLE_GUIDE.md) 를 참고하세요.

### 커밋 메시지

```
<type>: <description>
```

| type | 용도 |
|------|------|
| `feat` | 새로운 기능 |
| `fix` | 버그 수정 |
| `refactor` | 리팩토링 |
| `docs` | 문서 변경 |
| `chore` | 빌드, 설정 등 기타 |
| `perf` | 성능 개선 |

### DTO 작성 시 주의사항

NestJS에서 `plainToInstance`의 `excludeExtraneousValues: true` 옵션을 사용합니다.
**DTO 클래스의 모든 응답 필드에 `@Expose()` 데코레이터를 반드시 추가하세요.** 누락 시 API 응답에서 해당 필드가 제외됩니다.

```typescript
import { Expose } from 'class-transformer';

export class UserResponseDto {
  @Expose()
  memId: number;

  @Expose()
  memName: string;
}
```
