151 lines
9.2 KiB
Markdown
151 lines
9.2 KiB
Markdown
# 시스템 아키텍처 정의서 (System Architecture)
|
|
|
|
## 1. 기술 스택
|
|
|
|
| 계층 | 기술 | 버전 |
|
|
|------|------|------|
|
|
| Backend | Spring Boot | 3.x |
|
|
| | Java | 17 |
|
|
| | Spring Data JPA | - |
|
|
| | Spring Security | - |
|
|
| | Spring Batch | - |
|
|
| Frontend | Next.js | 14+ (App Router) |
|
|
| | TypeScript | - |
|
|
| | Tailwind CSS | - |
|
|
| | Pinia (State Management) | - |
|
|
| Database | PostgreSQL | 15 |
|
|
| Infra | Nginx Proxy Manager | - |
|
|
| CI/CD | GitHub Actions | Release 트리거 |
|
|
| Notification | Slack Webhook / Email | - |
|
|
|
|
## 2. 전역 시스템 레이어 구조
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Frontend (Next.js 14+) │
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
|
|
│ │ Pinia │ │ Axios │ │ Tailwind │ │ Dynamic LNB │ │
|
|
│ │ Storage │ │ Intercep.│ │ CSS │ │ (RBAC) │ │
|
|
│ └──────────┘ └──────────┘ └──────────┘ └───────────────┘ │
|
|
└──────────────────────┬──────────────────────────────────────┘
|
|
│ HTTPS / REST API
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Cloudflare Tunnel (SSL / Zero-Trust Proxy) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Backend (Spring Boot 3.x / Java 17) │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Security Layer │ │
|
|
│ │ ┌─────────────────┐ ┌──────────────────────────┐ │ │
|
|
│ │ │ JWT Token │ │ Security Filter Chain │ │ │
|
|
│ │ │ Provider │ │ (RBAC Authorization) │ │ │
|
|
│ │ └─────────────────┘ └──────────────────────────┘ │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ API Layer (REST Controllers) │ │
|
|
│ │ EmployeeController / TicketController / ... │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Service Layer (Business Logic + Transaction) │ │
|
|
│ │ ├─ @Transactional 선언 │ │
|
|
│ │ └─ ApplicationEventPublisher (EDA 발행) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Persistence Layer (Spring Data JPA) │ │
|
|
│ │ ├─ Entity / Repository / Specification │ │
|
|
│ │ ├─ @SQLDelete + @Where (Soft Delete 전역 필터) │ │
|
|
│ │ └─ @SequenceGenerator(allocationSize=50) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Infrastructure Layer │ │
|
|
│ │ ├─ @TransactionalEventListener(AFTER_COMMIT) │ │
|
|
│ │ ├─ @Async Notification Listener │ │
|
|
│ │ └─ Spring Batch SLA Scheduler (Chunk Size=100) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└──────────────────────┬──────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ PostgreSQL 15 Database │
|
|
│ ├─ Sequence (INCREMENT BY 50) │
|
|
│ ├─ Common Code Table (동적 드롭다운) │
|
|
│ └─ Batch Metadata Schema │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## 3. ApiResponse 공통 규격
|
|
|
|
모든 API 응답은 아래 포맷을 강제합니다.
|
|
|
|
**성공:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "요청이 성공적으로 처리되었습니다.",
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
**실패 (비즈니스 예외):**
|
|
```json
|
|
{
|
|
"success": false,
|
|
"message": "이미 결재가 완료된 보고서는 수정할 수 없습니다.",
|
|
"errorCode": "RPT_ERR_001",
|
|
"data": null
|
|
}
|
|
```
|
|
|
|
## 4. 글로벌 예외 처리 (GlobalExceptionHandler)
|
|
|
|
- 모든 도메인 예외는 `BusinessException` (RuntimeException 상속) 통일
|
|
- `@RestControllerAdvice`가 감지 → `ErrorCode` Enum 매핑 → `ApiResponse.error()` 변환
|
|
|
|
### ErrorCode 매트릭스
|
|
|
|
| Enum | HTTP Status | ErrorCode | 메시지 |
|
|
|------|------------|-----------|--------|
|
|
| `RESIGNED_EMPLOYEE` | 400 | EMP_ERR_001 | 퇴사 처리된 직원 정보는 수정할 수 없습니다 |
|
|
| `LOCKED_REPORT` | 400 | RPT_ERR_001 | 결재 완료 보고서는 수정/삭제할 수 없습니다 |
|
|
| `FUTURE_REPORT_DATE` | 400 | RPT_ERR_002 | 미래 날짜의 업무 보고는 작성할 수 없습니다 |
|
|
| `CLOSED_TICKET_MOD` | 400 | TCK_ERR_001 | 종료된 티켓은 댓글/수정이 불가능합니다 |
|
|
| `ASSIGNEE_REQUIRED` | 400 | TCK_ERR_002 | 담당자 미배정 티켓은 IN_PROGRESS 전환 불가 |
|
|
| `EQUIP_NOT_AVAILABLE` | 400 | EQP_ERR_001 | 지급 가능한 재고 상태가 아닙니다 |
|
|
| `INVALID_ASSIGNEE` | 400 | EQP_ERR_002 | 재직 중인 직원에게만 장비 지급 가능 |
|
|
| `SYSTEM_CONCURRENCY` | 409 | SYS_ERR_001 | 동시성 충돌 - 다시 시도해 주세요 |
|
|
|
|
## 5. 이벤트 기반 비동기 알림 (EDA)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
Controller->>Service: 요청 처리
|
|
Service->>DB: 트랜잭션 커밋
|
|
Service->>EventPublisher: 이벤트 발행
|
|
EventPublisher->>Listener: @TransactionalEventListener(AFTER_COMMIT)
|
|
Listener->>Slack/Email: @Async 비동기 발송
|
|
Note over Listener: 메인 트랜잭션과 완전 격리
|
|
```
|
|
|
|
## 6. SLA 만료 배치 아키텍처 (Spring Batch)
|
|
|
|
- **트리거:** Spring Scheduler (매시간)
|
|
- **Chunk Size:** 100건 단위 청크 분할
|
|
- **필터 조건:** `SLA_WARN_FLAG = 'N'` AND `status NOT IN (RESOLVED, CLOSED)` AND `due_date < NOW()`
|
|
- **처리:** 대상 티켓 탐색 → 알림 이벤트 발행 → `SLA_WARN_FLAG = 'Y'` 갱신
|
|
- **PostgreSQL 메타데이터:** 배치 전용 메타 테이블 스키마 별도 구축
|
|
|
|
## 7. Soft Delete 전역 아키텍처
|
|
|
|
```java
|
|
// BaseEntity 패턴
|
|
@SQLDelete(sql = "UPDATE tb_xxx SET is_deleted = 'Y' WHERE id = ?")
|
|
@Where(clause = "is_deleted = 'N'")
|
|
```
|
|
|
|
- 모든 엔티티가 `BaseEntity` 상속 (created_at, updated_at, is_deleted)
|
|
- 조회 시 `@Where` 조건절 자동 적용 → 조건절 누수 방지
|
|
- 물리 삭제 `DELETE` → `UPDATE is_deleted='Y'` 자변환
|