1425 lines
46 KiB
Markdown
1425 lines
46 KiB
Markdown
|
|
# WACE ERP 백엔드 아키텍처 상세 분석
|
||
|
|
|
||
|
|
> **작성일**: 2026-02-06
|
||
|
|
> **분석 대상**: ERP-node/backend-node
|
||
|
|
> **Stack**: Node.js + Express + TypeScript + PostgreSQL Raw Query
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 목차
|
||
|
|
|
||
|
|
1. [전체 디렉토리 구조](#1-전체-디렉토리-구조)
|
||
|
|
2. [API 라우트 목록 및 역할](#2-api-라우트-목록-및-역할)
|
||
|
|
3. [인증/인가 워크플로우](#3-인증인가-워크플로우)
|
||
|
|
4. [비즈니스 도메인별 모듈 분류](#4-비즈니스-도메인별-모듈-분류)
|
||
|
|
5. [미들웨어 스택 구성](#5-미들웨어-스택-구성)
|
||
|
|
6. [서비스 레이어 패턴](#6-서비스-레이어-패턴)
|
||
|
|
7. [멀티테넌시 구현 방식](#7-멀티테넌시-구현-방식)
|
||
|
|
8. [에러 핸들링 전략](#8-에러-핸들링-전략)
|
||
|
|
9. [파일 업로드/다운로드 처리](#9-파일-업로드다운로드-처리)
|
||
|
|
10. [외부 연동](#10-외부-연동)
|
||
|
|
11. [배치/스케줄 처리](#11-배치스케줄-처리)
|
||
|
|
12. [컨트롤러/서비스 상세 역할](#12-컨트롤러서비스-상세-역할)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. 전체 디렉토리 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
backend-node/
|
||
|
|
├── src/
|
||
|
|
│ ├── app.ts # Express 앱 진입점, 라우트 등록, 미들웨어 설정
|
||
|
|
│ ├── config/
|
||
|
|
│ │ └── environment.ts # 환경변수 관리 (PORT, DB, JWT, CORS 등)
|
||
|
|
│ ├── controllers/ # 69개 컨트롤러 (요청 처리 및 응답)
|
||
|
|
│ ├── services/ # 87개 서비스 (비즈니스 로직)
|
||
|
|
│ ├── routes/ # 77개 라우터 (엔드포인트 정의)
|
||
|
|
│ ├── middleware/ # 4개 미들웨어 (인증, 권한, 에러 핸들링)
|
||
|
|
│ ├── database/ # DB 연결 풀, 커넥터, 마이그레이션
|
||
|
|
│ ├── utils/ # 16개 유틸리티 (JWT, 암호화, 로거 등)
|
||
|
|
│ ├── types/ # 26개 TypeScript 타입 정의
|
||
|
|
│ ├── interfaces/ # 인터페이스 정의
|
||
|
|
│ └── tests/ # 테스트 파일
|
||
|
|
├── scripts/ # 배치 및 유틸리티 스크립트
|
||
|
|
├── data/ # JSON 기반 설정 데이터
|
||
|
|
├── uploads/ # 파일 업로드 디렉토리
|
||
|
|
└── package.json # 의존성 관리
|
||
|
|
```
|
||
|
|
|
||
|
|
### 주요 특징
|
||
|
|
- **Layered Architecture**: Controller → Service → Database 3계층 구조
|
||
|
|
- **TypeScript Strict Mode**: 타입 안전성 보장
|
||
|
|
- **Raw Query 기반**: Prisma → PostgreSQL Raw Query 전환 완료
|
||
|
|
- **Connection Pool**: pg 라이브러리 기반 연결 풀 관리
|
||
|
|
- **마이크로서비스 지향**: 도메인별 명확한 분리
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. API 라우트 목록 및 역할
|
||
|
|
|
||
|
|
### 2.1 인증 및 관리자 (Auth & Admin)
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/auth/login` | POST | 로그인 (JWT 토큰 발급) | ❌ |
|
||
|
|
| `/api/auth/signup` | POST | 회원가입 (공차중계) | ❌ |
|
||
|
|
| `/api/auth/me` | GET | 현재 사용자 정보 조회 | ✅ |
|
||
|
|
| `/api/auth/logout` | POST | 로그아웃 | ✅ |
|
||
|
|
| `/api/auth/refresh` | POST | JWT 토큰 갱신 | ✅ |
|
||
|
|
| `/api/auth/switch-company` | POST | 관리자 전용: 회사 전환 | ✅ |
|
||
|
|
| `/api/admin/menus` | GET | 메뉴 목록 조회 | ✅ |
|
||
|
|
| `/api/admin/users` | GET/POST/PUT | 사용자 관리 (CRUD) | ✅ |
|
||
|
|
| `/api/admin/companies` | GET/POST/PUT/DELETE | 회사 관리 (CRUD) | ✅ |
|
||
|
|
| `/api/admin/departments` | GET | 부서 목록 조회 | ✅ |
|
||
|
|
|
||
|
|
### 2.2 테이블 및 데이터 관리
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/table-management/tables` | GET | 테이블 목록 조회 | ✅ |
|
||
|
|
| `/api/table-management/columns` | GET | 컬럼 정보 조회 | ✅ |
|
||
|
|
| `/api/table-management/entity-joins` | GET/POST | 테이블 조인 설정 | ✅ |
|
||
|
|
| `/api/data/*` | GET/POST/PUT/DELETE | 동적 테이블 데이터 CRUD | ✅ |
|
||
|
|
| `/api/ddl/*` | POST | DDL 실행 (테이블 생성/수정/삭제) | ✅ (Super Admin) |
|
||
|
|
|
||
|
|
### 2.3 화면 및 폼 관리
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/screen-management/*` | GET/POST/PUT/DELETE | 화면 메타데이터 관리 | ✅ |
|
||
|
|
| `/api/screen-groups/*` | GET/POST/PUT/DELETE | 화면 그룹 관리 | ✅ |
|
||
|
|
| `/api/dynamic-form/*` | GET/POST | 동적 폼 생성 및 렌더링 | ✅ |
|
||
|
|
| `/api/admin/web-types` | GET/POST | 웹 컴포넌트 타입 표준 관리 | ✅ |
|
||
|
|
| `/api/admin/button-actions` | GET/POST | 버튼 액션 표준 관리 | ✅ |
|
||
|
|
| `/api/admin/template-standards` | GET/POST | 템플릿 표준 관리 | ✅ |
|
||
|
|
| `/api/admin/component-standards` | GET/POST | 컴포넌트 표준 관리 | ✅ |
|
||
|
|
|
||
|
|
### 2.4 플로우 및 데이터플로우
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/flow/definitions` | GET/POST/PUT/DELETE | 플로우 정의 관리 | ✅ |
|
||
|
|
| `/api/flow/definitions/:id/steps` | GET/POST | 플로우 단계 관리 | ✅ |
|
||
|
|
| `/api/flow/connections` | GET/POST/DELETE | 플로우 연결 관리 | ✅ |
|
||
|
|
| `/api/flow/move` | POST | 데이터 이동 실행 | ✅ |
|
||
|
|
| `/api/flow/audit/:flowId` | GET | 플로우 오딧 로그 조회 | ✅ |
|
||
|
|
| `/api/dataflow/*` | GET/POST/PUT/DELETE | 데이터플로우 관계 관리 | ✅ |
|
||
|
|
| `/api/dataflow-diagrams/*` | GET/POST/PUT/DELETE | 데이터플로우 다이어그램 | ✅ |
|
||
|
|
| `/api/dataflow/execute` | POST | 데이터플로우 실행 | ✅ |
|
||
|
|
|
||
|
|
### 2.5 배치 관리
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/batch-configs` | GET/POST/PUT/DELETE | 배치 설정 관리 | ✅ |
|
||
|
|
| `/api/batch-configs/connections` | GET | 사용 가능한 커넥션 목록 | ✅ |
|
||
|
|
| `/api/batch-configs/:id/execute` | POST | 배치 수동 실행 | ✅ |
|
||
|
|
| `/api/batch-management/*` | GET/POST | 배치 실행 관리 | ✅ |
|
||
|
|
| `/api/batch-execution-logs` | GET | 배치 실행 이력 조회 | ✅ |
|
||
|
|
|
||
|
|
### 2.6 외부 연동
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/external-db-connections` | GET/POST/PUT/DELETE | 외부 DB 연결 관리 | ✅ |
|
||
|
|
| `/api/external-db-connections/:id/test` | POST | 외부 DB 연결 테스트 | ✅ |
|
||
|
|
| `/api/external-rest-api-connections` | GET/POST/PUT/DELETE | 외부 REST API 연결 | ✅ |
|
||
|
|
| `/api/external-calls/*` | GET/POST | 외부 API 호출 설정 | ✅ |
|
||
|
|
| `/api/multi-connection/query` | POST | 멀티 DB 통합 쿼리 | ✅ |
|
||
|
|
|
||
|
|
### 2.7 메일 관리
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/mail/accounts` | GET/POST/PUT/DELETE | 메일 계정 관리 | ✅ |
|
||
|
|
| `/api/mail/templates-file` | GET/POST/PUT/DELETE | 메일 템플릿 관리 | ✅ |
|
||
|
|
| `/api/mail/send` | POST | 메일 발송 (단일/대량) | ✅ |
|
||
|
|
| `/api/mail/sent` | GET | 발송 이력 조회 | ✅ |
|
||
|
|
| `/api/mail/receive` | GET | 메일 수신함 조회 | ✅ |
|
||
|
|
|
||
|
|
### 2.8 기타 도메인
|
||
|
|
|
||
|
|
| 엔드포인트 | 메서드 | 역할 | 인증 |
|
||
|
|
|-----------|--------|------|------|
|
||
|
|
| `/api/dashboards/*` | GET/POST | 대시보드 관리 | ✅ |
|
||
|
|
| `/api/admin/reports/*` | GET/POST | 리포트 생성 및 조회 | ✅ |
|
||
|
|
| `/api/files/*` | POST | 파일 업로드/다운로드 | ✅ |
|
||
|
|
| `/api/delivery/*` | GET/POST | 배송/화물 관리 | ✅ |
|
||
|
|
| `/api/risk-alerts/*` | GET/POST | 리스크/알림 관리 | ✅ |
|
||
|
|
| `/api/todos/*` | GET/POST/PUT/DELETE | To-Do 관리 | ✅ |
|
||
|
|
| `/api/bookings/*` | GET/POST | 예약 관리 | ✅ |
|
||
|
|
| `/api/digital-twin/*` | GET/POST | 디지털 트윈 (야드 관제) | ✅ |
|
||
|
|
| `/api/schedule/*` | GET/POST | 스케줄 자동 생성 | ✅ |
|
||
|
|
| `/api/work-history/*` | GET | 작업 이력 조회 | ✅ |
|
||
|
|
| `/api/table-history/*` | GET | 테이블 변경 이력 조회 | ✅ |
|
||
|
|
| `/api/roles/*` | GET/POST | 권한 그룹 관리 | ✅ |
|
||
|
|
| `/api/numbering-rules/*` | GET/POST | 채번 규칙 관리 | ✅ |
|
||
|
|
| `/api/entity-search/*` | GET | 엔티티 검색 | ✅ |
|
||
|
|
| `/api/cascading-*` | GET/POST | 연쇄 드롭다운 관계 | ✅ |
|
||
|
|
| `/api/category-tree/*` | GET/POST | 카테고리 트리 | ✅ |
|
||
|
|
| `/api/vehicle/*` | GET/POST | 차량 운행 이력 | ✅ |
|
||
|
|
| `/api/tax-invoice/*` | GET/POST | 세금계산서 관리 | ✅ |
|
||
|
|
|
||
|
|
**총 77개 라우터 파일, 200개 이상의 엔드포인트 제공**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 인증/인가 워크플로우
|
||
|
|
|
||
|
|
### 3.1 인증 메커니즘
|
||
|
|
|
||
|
|
```
|
||
|
|
로그인 요청 (userId, password)
|
||
|
|
↓
|
||
|
|
1. AuthController.login()
|
||
|
|
↓
|
||
|
|
2. AuthService.processLogin()
|
||
|
|
├─ 비밀번호 검증 (BCrypt + 마스터 패스워드)
|
||
|
|
├─ 사용자 정보 조회 (user_info 테이블)
|
||
|
|
├─ 로그인 로그 기록 (LOGIN_ACCESS_LOG)
|
||
|
|
└─ JWT 토큰 생성 (JwtUtils.generateToken)
|
||
|
|
↓
|
||
|
|
3. JWT 토큰 응답
|
||
|
|
├─ accessToken (24시간 유효)
|
||
|
|
├─ refreshToken (7일 유효)
|
||
|
|
└─ userInfo (userId, userName, companyCode, userType)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 JWT 토큰 구조
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// JWT Payload
|
||
|
|
{
|
||
|
|
userId: string; // 사용자 ID
|
||
|
|
userName: string; // 사용자 이름
|
||
|
|
companyCode: string; // 회사 코드 (멀티테넌시 핵심)
|
||
|
|
userType: string; // 사용자 유형 (SUPER_ADMIN, COMPANY_ADMIN, USER)
|
||
|
|
userLang?: string; // 사용자 언어
|
||
|
|
iat: number; // 발급 시간
|
||
|
|
exp: number; // 만료 시간
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.3 미들웨어 체인
|
||
|
|
|
||
|
|
```
|
||
|
|
1. refreshTokenIfNeeded (자동 토큰 갱신)
|
||
|
|
↓
|
||
|
|
2. authenticateToken (JWT 검증 및 사용자 정보 설정)
|
||
|
|
↓
|
||
|
|
3. 권한 미들웨어 (선택적)
|
||
|
|
├─ requireSuperAdmin (회사코드 '*' 필수)
|
||
|
|
├─ requireAdmin (회사관리자 이상)
|
||
|
|
├─ requireCompanyAccess (회사 데이터 접근 권한)
|
||
|
|
├─ requireDDLPermission (DDL 실행 권한)
|
||
|
|
└─ requireUserManagement (사용자 관리 권한)
|
||
|
|
↓
|
||
|
|
4. Controller 실행
|
||
|
|
↓
|
||
|
|
5. errorHandler (에러 발생 시)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.4 권한 레벨 (3단계)
|
||
|
|
|
||
|
|
| 레벨 | companyCode | userType | 권한 범위 |
|
||
|
|
|------|-------------|----------|----------|
|
||
|
|
| **Super Admin** | `*` | `SUPER_ADMIN` | 전체 시스템 접근, DDL 실행, 회사 생성/삭제 |
|
||
|
|
| **Company Admin** | 회사코드 | `COMPANY_ADMIN` | 자사 데이터 관리, 사용자 관리, 설정 변경 |
|
||
|
|
| **일반 사용자** | 회사코드 | `USER` | 자사 데이터 조회/수정 (권한 범위 내) |
|
||
|
|
|
||
|
|
### 3.5 토큰 갱신 전략
|
||
|
|
|
||
|
|
- **자동 갱신**: 토큰이 1시간 이내 만료 시 응답 헤더(`X-New-Token`)에 새 토큰 포함
|
||
|
|
- **명시적 갱신**: `/api/auth/refresh` 엔드포인트 호출
|
||
|
|
- **만료 처리**: 만료된 토큰은 401 Unauthorized 응답 (`TOKEN_EXPIRED`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 비즈니스 도메인별 모듈 분류
|
||
|
|
|
||
|
|
### 4.1 관리자 영역 (Admin)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `adminController.ts`, `adminService.ts`, `adminRoutes.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 메뉴 관리 (CRUD, 복사, 상태 토글, 일괄 삭제)
|
||
|
|
- 사용자 관리 (등록, 수정, 상태 변경, 비밀번호 초기화)
|
||
|
|
- 회사 관리 (등록, 수정, 삭제, 조회)
|
||
|
|
- 부서 관리 (조회, 사용자-부서 통합 저장)
|
||
|
|
- 로케일 설정 (다국어 지원)
|
||
|
|
- 테이블 스키마 조회 (엑셀 매핑용)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 멀티테넌시 기반 회사별 데이터 격리
|
||
|
|
- Super Admin만 회사 생성/삭제 가능
|
||
|
|
- 사용자 변경 이력 추적
|
||
|
|
|
||
|
|
### 4.2 테이블 및 데이터 관리 (Table Management & Data)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `tableManagementController.ts`, `tableManagementService.ts`
|
||
|
|
- `dataController.ts`, `dataService.ts`
|
||
|
|
- `entityJoinController.ts`, `entityJoinService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 테이블 목록 조회 (PostgreSQL information_schema 활용)
|
||
|
|
- 컬럼 정보 조회 (타입, 라벨, 제약조건, 참조 관계)
|
||
|
|
- 동적 테이블 데이터 CRUD (Raw Query 기반)
|
||
|
|
- 테이블 조인 설정 및 실행 (1:N, N:M 관계)
|
||
|
|
- 컬럼 라벨 및 설정 관리 (table_type_columns)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 캐시 기반 성능 최적화 (테이블/컬럼 정보)
|
||
|
|
- 멀티테넌시 자동 필터링 (`company_code` 조건)
|
||
|
|
- 코드 타입 컬럼 자동 처리 (공통 코드 연동)
|
||
|
|
|
||
|
|
### 4.3 화면 관리 (Screen Management)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `screenManagementController.ts`, `screenManagementService.ts`
|
||
|
|
- `screenGroupController.ts`, `screenEmbeddingController.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 화면 메타데이터 관리 (테이블 연결, 컬럼 설정, 레이아웃)
|
||
|
|
- 화면 그룹 관리 (폴더 구조)
|
||
|
|
- 화면 임베딩 (부모-자식 화면 데이터 전달)
|
||
|
|
- 동적 폼 생성 (JSON 기반 폼 설정 → React 컴포넌트)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- Low-Code 화면 구성
|
||
|
|
- 웹 컴포넌트 타입 표준 기반 렌더링
|
||
|
|
- 버튼 액션 표준 지원 (저장, 삭제, 조회, 커스텀)
|
||
|
|
|
||
|
|
### 4.4 플로우 관리 (Flow Management)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `flowController.ts`, `flowService.ts`
|
||
|
|
- `flowExecutionService.ts`, `flowStepService.ts`
|
||
|
|
- `flowConnectionService.ts`, `flowDataMoveService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 플로우 정의 관리 (작업 흐름 설계)
|
||
|
|
- 플로우 단계 관리 (스텝 생성, 수정, 삭제)
|
||
|
|
- 플로우 연결 관리 (스텝 간 조건부 연결)
|
||
|
|
- 데이터 이동 실행 (스텝 간 데이터 이동)
|
||
|
|
- 오딧 로그 조회 (변경 이력 추적)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 비주얼 워크플로우 엔진
|
||
|
|
- 조건부 분기 지원
|
||
|
|
- 배치 데이터 이동 지원
|
||
|
|
|
||
|
|
### 4.5 데이터플로우 (Dataflow)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `dataflowController.ts`, `dataflowService.ts`
|
||
|
|
- `dataflowDiagramController.ts`, `dataflowDiagramService.ts`
|
||
|
|
- `dataflowExecutionController.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 테이블 관계 정의 (1:1, 1:N, N:M)
|
||
|
|
- 데이터플로우 다이어그램 생성 (ERD 같은 시각화)
|
||
|
|
- 데이터플로우 실행 (자동 데이터 동기화)
|
||
|
|
- 관계 기반 데이터 조회 (조인 쿼리 자동 생성)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 그래프 기반 데이터 관계 모델링
|
||
|
|
- 다이어그램별 관계 그룹화
|
||
|
|
|
||
|
|
### 4.6 배치 관리 (Batch Management)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `batchController.ts`, `batchService.ts`
|
||
|
|
- `batchSchedulerService.ts`, `batchExecutionLogService.ts`
|
||
|
|
- `batchExternalDbService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 배치 설정 관리 (CRUD)
|
||
|
|
- Cron 기반 스케줄링 (node-cron)
|
||
|
|
- 배치 수동/자동 실행
|
||
|
|
- 실행 이력 조회 (성공/실패 로그)
|
||
|
|
- 외부 DB 연동 배치 지원
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 실시간 스케줄러 업데이트
|
||
|
|
- 다중 DB 간 데이터 동기화
|
||
|
|
- 실행 시간 제한 및 오류 알림
|
||
|
|
|
||
|
|
### 4.7 외부 연동 (External Integration)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `externalDbConnectionController.ts`, `externalDbConnectionService.ts`
|
||
|
|
- `externalRestApiConnectionController.ts`, `externalRestApiConnectionService.ts`
|
||
|
|
- `externalCallController.ts`, `externalCallService.ts`
|
||
|
|
- `multiConnectionQueryService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 외부 DB 연결 관리 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB)
|
||
|
|
- 외부 REST API 연결 관리
|
||
|
|
- 멀티 DB 통합 쿼리 실행
|
||
|
|
- 연결 테스트 및 상태 확인
|
||
|
|
- 크레덴셜 암호화 저장
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- 5종 DB 지원 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB)
|
||
|
|
- Connection Pool 기반 연결 관리
|
||
|
|
- 비밀번호 암호화 (AES-256-CBC)
|
||
|
|
|
||
|
|
### 4.8 메일 관리 (Mail Management)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `mailAccountFileController.ts`, `mailAccountFileService.ts`
|
||
|
|
- `mailTemplateFileController.ts`, `mailTemplateFileService.ts`
|
||
|
|
- `mailSendSimpleController.ts`, `mailSendSimpleService.ts`
|
||
|
|
- `mailSentHistoryController.ts`, `mailSentHistoryService.ts`
|
||
|
|
- `mailReceiveBasicController.ts`, `mailReceiveBasicService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 메일 계정 관리 (SMTP/IMAP 설정)
|
||
|
|
- 메일 템플릿 관리 (JSON 기반 컴포넌트 조합)
|
||
|
|
- 메일 발송 (단일/대량, 첨부파일 지원)
|
||
|
|
- 발송 이력 조회 (30일 자동 삭제)
|
||
|
|
- 메일 수신함 조회 (IMAP)
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- Nodemailer 기반 발송
|
||
|
|
- 템플릿 변수 치환
|
||
|
|
- 대량 발송 지원 (100건/배치)
|
||
|
|
- 메일 예약 발송
|
||
|
|
|
||
|
|
### 4.9 대시보드 (Dashboard)
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `DashboardController.ts`, `DashboardService.ts`
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 대시보드 위젯 관리 (차트, 테이블, 카드)
|
||
|
|
- 실시간 데이터 조회 (집계 쿼리)
|
||
|
|
- 사용자별 대시보드 설정
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
- JSON 기반 위젯 설정
|
||
|
|
- 캐시 기반 성능 최적화
|
||
|
|
|
||
|
|
### 4.10 기타 도메인
|
||
|
|
|
||
|
|
**파일 및 주요 기능**:
|
||
|
|
- **리포트**: 리포트 생성 및 조회 (`reportController.ts`, `reportService.ts`)
|
||
|
|
- **파일**: 파일 업로드/다운로드 (`fileController.ts`, Multer)
|
||
|
|
- **배송/화물**: 배송 관리, 화물 추적 (`deliveryController.ts`)
|
||
|
|
- **리스크/알림**: 리스크 알림, 캐시 기반 자동 갱신 (`riskAlertController.ts`)
|
||
|
|
- **To-Do**: 할 일 관리 (`todoController.ts`)
|
||
|
|
- **예약**: 예약 요청 관리 (`bookingController.ts`)
|
||
|
|
- **디지털 트윈**: 야드 관제, 3D 레이아웃 (`digitalTwinController.ts`)
|
||
|
|
- **스케줄**: 스케줄 자동 생성 (`scheduleController.ts`)
|
||
|
|
- **작업 이력**: 작업 로그 조회 (`workHistoryController.ts`)
|
||
|
|
- **권한 그룹**: 권한 그룹 관리 (`roleController.ts`)
|
||
|
|
- **채번 규칙**: 자동 채번 규칙 (`numberingRuleController.ts`)
|
||
|
|
- **엔티티 검색**: 동적 엔티티 검색 (`entitySearchController.ts`)
|
||
|
|
- **연쇄 드롭다운**: 조건부 드롭다운 (`cascadingController.ts` 시리즈)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 미들웨어 스택 구성
|
||
|
|
|
||
|
|
### 5.1 미들웨어 실행 순서 (app.ts 기준)
|
||
|
|
|
||
|
|
```
|
||
|
|
1. 프로세스 레벨 예외 처리 (unhandledRejection, uncaughtException)
|
||
|
|
2. 보안 헤더 (helmet)
|
||
|
|
3. 압축 (compression)
|
||
|
|
4. 바디 파싱 (express.json, express.urlencoded)
|
||
|
|
5. 정적 파일 서빙 (/uploads)
|
||
|
|
6. CORS (cors)
|
||
|
|
7. Rate Limiting (express-rate-limit)
|
||
|
|
8. 토큰 자동 갱신 (refreshTokenIfNeeded)
|
||
|
|
9. [라우트별 미들웨어]
|
||
|
|
├─ authenticateToken (모든 /api/* 라우트)
|
||
|
|
├─ 권한 미들웨어 (선택적)
|
||
|
|
└─ 컨트롤러 실행
|
||
|
|
10. 404 핸들러
|
||
|
|
11. 에러 핸들러 (errorHandler)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 미들웨어 파일
|
||
|
|
|
||
|
|
#### `authMiddleware.ts`
|
||
|
|
- **authenticateToken**: JWT 토큰 검증 및 사용자 정보 설정
|
||
|
|
- **optionalAuth**: 선택적 인증 (토큰 없어도 통과)
|
||
|
|
- **requireAdmin**: 관리자 권한 필수 (userId === 'plm_admin')
|
||
|
|
- **refreshTokenIfNeeded**: 토큰 자동 갱신 (1시간 이내 만료 시)
|
||
|
|
- **checkAuthStatus**: 인증 상태 확인 (유효성 검사만)
|
||
|
|
|
||
|
|
#### `permissionMiddleware.ts`
|
||
|
|
- **requireSuperAdmin**: 슈퍼관리자 권한 필수 (companyCode === '*')
|
||
|
|
- **requireAdmin**: 관리자 이상 권한 필수 (Super Admin + Company Admin)
|
||
|
|
- **requireCompanyAccess**: 회사 데이터 접근 권한 체크
|
||
|
|
- **requireUserManagement**: 사용자 관리 권한 체크
|
||
|
|
- **requireCompanySettingsManagement**: 회사 설정 변경 권한 체크
|
||
|
|
- **requireCompanyManagement**: 회사 생성/삭제 권한 체크
|
||
|
|
- **requireDDLPermission**: DDL 실행 권한 체크
|
||
|
|
|
||
|
|
#### `superAdminMiddleware.ts`
|
||
|
|
- **requireSuperAdmin**: 슈퍼관리자 권한 확인 (DDL 전용)
|
||
|
|
- **validateDDLPermission**: DDL 실행 전 추가 보안 검증 (5초 간격 제한)
|
||
|
|
- **isSuperAdmin**: 슈퍼관리자 여부 확인 유틸 함수
|
||
|
|
- **checkDDLPermission**: DDL 권한 체크 (미들웨어 없이 사용)
|
||
|
|
|
||
|
|
#### `errorHandler.ts`
|
||
|
|
- **AppError**: 커스텀 에러 클래스 (statusCode, isOperational)
|
||
|
|
- **errorHandler**: 전역 에러 핸들러 (PostgreSQL, JWT 에러 처리)
|
||
|
|
- **notFoundHandler**: 404 에러 핸들러
|
||
|
|
|
||
|
|
### 5.3 보안 설정
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// helmet: 보안 헤더 설정
|
||
|
|
helmet({
|
||
|
|
contentSecurityPolicy: {
|
||
|
|
directives: {
|
||
|
|
'frame-ancestors': ['self', 'http://localhost:9771', 'http://localhost:3000']
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// Rate Limiting: 1분당 10,000 요청 (개발), 100 요청 (운영)
|
||
|
|
rateLimit({
|
||
|
|
windowMs: 1 * 60 * 1000,
|
||
|
|
max: process.env.NODE_ENV === 'development' ? 10000 : 100,
|
||
|
|
skip: (req) => {
|
||
|
|
// 헬스 체크, 자주 호출되는 API는 제외
|
||
|
|
return req.path === '/health'
|
||
|
|
|| req.path.includes('/table-management/')
|
||
|
|
|| req.path.includes('/external-db-connections/')
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// CORS: 환경별 origin 설정
|
||
|
|
cors({
|
||
|
|
origin: process.env.NODE_ENV === 'development'
|
||
|
|
? true
|
||
|
|
: ['http://localhost:9771', 'http://39.117.244.52:5555'],
|
||
|
|
credentials: true
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 서비스 레이어 패턴
|
||
|
|
|
||
|
|
### 6.1 서비스 레이어 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
Controller (요청 처리)
|
||
|
|
↓
|
||
|
|
Service (비즈니스 로직)
|
||
|
|
↓
|
||
|
|
Database (Raw Query 실행)
|
||
|
|
↓
|
||
|
|
PostgreSQL (데이터 저장소)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.2 데이터베이스 접근 방식
|
||
|
|
|
||
|
|
#### `db.ts` - Raw Query 매니저
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 기본 쿼리 실행
|
||
|
|
async function query<T>(text: string, params?: any[]): Promise<T[]>
|
||
|
|
|
||
|
|
// 단일 행 조회
|
||
|
|
async function queryOne<T>(text: string, params?: any[]): Promise<T | null>
|
||
|
|
|
||
|
|
// 트랜잭션
|
||
|
|
async function transaction<T>(callback: (client: PoolClient) => Promise<T>): Promise<T>
|
||
|
|
|
||
|
|
// 연결 풀 상태
|
||
|
|
function getPoolStatus(): { totalCount, idleCount, waitingCount }
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Connection Pool 설정
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
new Pool({
|
||
|
|
host: dbConfig.host,
|
||
|
|
port: dbConfig.port,
|
||
|
|
database: dbConfig.database,
|
||
|
|
user: dbConfig.user,
|
||
|
|
password: dbConfig.password,
|
||
|
|
|
||
|
|
// 연결 풀 설정
|
||
|
|
min: process.env.NODE_ENV === 'production' ? 5 : 2,
|
||
|
|
max: process.env.NODE_ENV === 'production' ? 20 : 10,
|
||
|
|
|
||
|
|
// 타임아웃 설정
|
||
|
|
connectionTimeoutMillis: 30000, // 30초
|
||
|
|
idleTimeoutMillis: 600000, // 10분
|
||
|
|
statement_timeout: 60000, // 60초
|
||
|
|
query_timeout: 60000,
|
||
|
|
|
||
|
|
application_name: 'WACE-PLM-Backend'
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.3 서비스 패턴 예시
|
||
|
|
|
||
|
|
#### 멀티테넌시 쿼리 패턴
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Super Admin: 모든 데이터 조회
|
||
|
|
if (companyCode === '*') {
|
||
|
|
query = 'SELECT * FROM table_name ORDER BY company_code';
|
||
|
|
params = [];
|
||
|
|
}
|
||
|
|
// 일반 사용자: 자사 데이터만 조회 (Super Admin 데이터 제외)
|
||
|
|
else {
|
||
|
|
query = `
|
||
|
|
SELECT * FROM table_name
|
||
|
|
WHERE company_code = $1 AND company_code != '*'
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
`;
|
||
|
|
params = [companyCode];
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 트랜잭션 패턴
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
await transaction(async (client) => {
|
||
|
|
// 1. 부모 레코드 삽입
|
||
|
|
const parent = await client.query(
|
||
|
|
'INSERT INTO parent_table (...) VALUES (...) RETURNING *',
|
||
|
|
[...]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 2. 자식 레코드 삽입
|
||
|
|
await client.query(
|
||
|
|
'INSERT INTO child_table (parent_id, ...) VALUES ($1, ...) RETURNING *',
|
||
|
|
[parent.rows[0].id, ...]
|
||
|
|
);
|
||
|
|
|
||
|
|
return { success: true };
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 캐시 패턴
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 캐시 조회
|
||
|
|
const cachedData = cache.get<T>(CacheKeys.TABLE_LIST);
|
||
|
|
if (cachedData) {
|
||
|
|
return cachedData;
|
||
|
|
}
|
||
|
|
|
||
|
|
// DB 조회
|
||
|
|
const data = await query<T>('SELECT ...');
|
||
|
|
|
||
|
|
// 캐시 저장 (10분 TTL)
|
||
|
|
cache.set(CacheKeys.TABLE_LIST, data, 10 * 60 * 1000);
|
||
|
|
|
||
|
|
return data;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.4 외부 DB 커넥터 패턴
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// DatabaseConnectorFactory.ts
|
||
|
|
export class DatabaseConnectorFactory {
|
||
|
|
static createConnector(dbType: string, config: ConnectionConfig): DatabaseConnector {
|
||
|
|
switch (dbType) {
|
||
|
|
case 'postgresql': return new PostgreSQLConnector(config);
|
||
|
|
case 'mysql': return new MySQLConnector(config);
|
||
|
|
case 'mssql': return new MSSQLConnector(config);
|
||
|
|
case 'oracle': return new OracleConnector(config);
|
||
|
|
case 'mariadb': return new MariaDBConnector(config);
|
||
|
|
default: throw new Error(`Unsupported DB type: ${dbType}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 사용 예시
|
||
|
|
const connector = DatabaseConnectorFactory.createConnector('mysql', config);
|
||
|
|
await connector.connect();
|
||
|
|
const result = await connector.executeQuery('SELECT * FROM users');
|
||
|
|
await connector.disconnect();
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 멀티테넌시 구현 방식
|
||
|
|
|
||
|
|
### 7.1 핵심 원칙
|
||
|
|
|
||
|
|
**CRITICAL PROJECT RULES**:
|
||
|
|
1. **모든 쿼리는 company_code 필터 필수**
|
||
|
|
2. **req.user!.companyCode 사용 (클라이언트 전송 값 신뢰 금지)**
|
||
|
|
3. **Super Admin (company_code = '*')만 전체 데이터 조회**
|
||
|
|
4. **일반 사용자는 company_code = '*' 데이터 조회 불가**
|
||
|
|
|
||
|
|
### 7.2 쿼리 패턴
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const companyCode = req.user!.companyCode;
|
||
|
|
|
||
|
|
// Super Admin: 모든 회사 데이터 조회
|
||
|
|
if (companyCode === '*') {
|
||
|
|
query = 'SELECT * FROM users ORDER BY company_code';
|
||
|
|
params = [];
|
||
|
|
}
|
||
|
|
// 일반 사용자: 자사 데이터만 조회 (Super Admin 제외)
|
||
|
|
else {
|
||
|
|
query = `
|
||
|
|
SELECT * FROM users
|
||
|
|
WHERE company_code = $1 AND company_code != '*'
|
||
|
|
`;
|
||
|
|
params = [companyCode];
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.3 테이블 설계
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 모든 비즈니스 테이블에 company_code 컬럼 필수
|
||
|
|
CREATE TABLE table_name (
|
||
|
|
id SERIAL PRIMARY KEY,
|
||
|
|
company_code VARCHAR(50) NOT NULL, -- 회사 코드
|
||
|
|
...
|
||
|
|
INDEX idx_company_code (company_code)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Super Admin 데이터: company_code = '*'
|
||
|
|
-- 회사별 데이터: company_code = '회사코드'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.4 회사 전환 (Super Admin 전용)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// POST /api/auth/switch-company
|
||
|
|
{
|
||
|
|
targetCompanyCode: "ILSHIN" // 전환할 회사 코드
|
||
|
|
}
|
||
|
|
|
||
|
|
// 응답
|
||
|
|
{
|
||
|
|
success: true,
|
||
|
|
token: "새로운 JWT 토큰", // companyCode가 변경된 토큰
|
||
|
|
userInfo: { companyCode: "ILSHIN", ... }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.5 권한 체크
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 회사 데이터 접근 권한 확인
|
||
|
|
export function canAccessCompanyData(user: PersonBean, targetCompanyCode: string): boolean {
|
||
|
|
// Super Admin: 모든 회사 접근 가능
|
||
|
|
if (user.companyCode === '*') {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 일반 사용자: 자사만 접근 가능
|
||
|
|
return user.companyCode === targetCompanyCode;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. 에러 핸들링 전략
|
||
|
|
|
||
|
|
### 8.1 에러 핸들링 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
Controller (try-catch)
|
||
|
|
↓ 에러 발생
|
||
|
|
Service (throw error)
|
||
|
|
↓
|
||
|
|
errorHandler (미들웨어)
|
||
|
|
├─ PostgreSQL 에러 처리
|
||
|
|
├─ JWT 에러 처리
|
||
|
|
├─ 커스텀 에러 처리 (AppError)
|
||
|
|
└─ 응답 전송 (JSON)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 커스텀 에러 클래스
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
export class AppError extends Error {
|
||
|
|
public statusCode: number;
|
||
|
|
public isOperational: boolean;
|
||
|
|
|
||
|
|
constructor(message: string, statusCode: number = 500) {
|
||
|
|
super(message);
|
||
|
|
this.statusCode = statusCode;
|
||
|
|
this.isOperational = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 사용 예시
|
||
|
|
throw new AppError('중복된 데이터가 존재합니다.', 400);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.3 PostgreSQL 에러 처리
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// errorHandler.ts
|
||
|
|
if (pgError.code === '23505') { // unique_violation
|
||
|
|
error = new AppError('중복된 데이터가 존재합니다.', 400);
|
||
|
|
} else if (pgError.code === '23503') { // foreign_key_violation
|
||
|
|
error = new AppError('참조 무결성 제약 조건 위반입니다.', 400);
|
||
|
|
} else if (pgError.code === '23502') { // not_null_violation
|
||
|
|
error = new AppError('필수 입력값이 누락되었습니다.', 400);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.4 에러 응답 형식
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": false,
|
||
|
|
"error": {
|
||
|
|
"code": "UNIQUE_VIOLATION",
|
||
|
|
"message": "중복된 데이터가 존재합니다.",
|
||
|
|
"details": "사용자 ID가 이미 존재합니다.",
|
||
|
|
"stack": "..." // 개발 환경에서만 포함
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.5 에러 로깅
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// logger.ts (Winston 기반)
|
||
|
|
logger.error({
|
||
|
|
message: error.message,
|
||
|
|
stack: error.stack,
|
||
|
|
url: req.url,
|
||
|
|
method: req.method,
|
||
|
|
ip: req.ip,
|
||
|
|
userAgent: req.get('User-Agent')
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.6 프로세스 레벨 예외 처리
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// app.ts
|
||
|
|
process.on('unhandledRejection', (reason, promise) => {
|
||
|
|
logger.error('⚠️ Unhandled Promise Rejection:', reason);
|
||
|
|
// 프로세스 종료하지 않고 로깅만 수행
|
||
|
|
});
|
||
|
|
|
||
|
|
process.on('uncaughtException', (error) => {
|
||
|
|
logger.error('🔥 Uncaught Exception:', error);
|
||
|
|
// 심각한 에러 시 graceful shutdown 고려
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 파일 업로드/다운로드 처리
|
||
|
|
|
||
|
|
### 9.1 파일 업로드
|
||
|
|
|
||
|
|
**파일**: `fileController.ts`, `fileRoutes.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Multer 설정
|
||
|
|
const storage = multer.diskStorage({
|
||
|
|
destination: (req, file, cb) => {
|
||
|
|
cb(null, 'uploads/');
|
||
|
|
},
|
||
|
|
filename: (req, file, cb) => {
|
||
|
|
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
||
|
|
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
const upload = multer({
|
||
|
|
storage,
|
||
|
|
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB
|
||
|
|
fileFilter: (req, file, cb) => {
|
||
|
|
// 허용된 확장자 체크
|
||
|
|
const allowedTypes = /jpeg|jpg|png|gif|pdf|doc|docx|xls|xlsx/;
|
||
|
|
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
|
||
|
|
const mimetype = allowedTypes.test(file.mimetype);
|
||
|
|
|
||
|
|
if (mimetype && extname) {
|
||
|
|
return cb(null, true);
|
||
|
|
} else {
|
||
|
|
cb(new Error('허용되지 않는 파일 형식입니다.'));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// 라우트
|
||
|
|
router.post('/upload', authenticateToken, upload.single('file'), uploadFile);
|
||
|
|
router.post('/upload-multiple', authenticateToken, upload.array('files', 10), uploadMultipleFiles);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.2 파일 다운로드
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 정적 파일 서빙 (app.ts)
|
||
|
|
app.use('/uploads',
|
||
|
|
(req, res, next) => {
|
||
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||
|
|
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
||
|
|
res.setHeader('Cache-Control', 'public, max-age=3600');
|
||
|
|
next();
|
||
|
|
},
|
||
|
|
express.static(path.join(process.cwd(), 'uploads'))
|
||
|
|
);
|
||
|
|
|
||
|
|
// 다운로드 엔드포인트
|
||
|
|
router.get('/download/:filename', authenticateToken, async (req, res) => {
|
||
|
|
const filename = req.params.filename;
|
||
|
|
const filePath = path.join(process.cwd(), 'uploads', filename);
|
||
|
|
|
||
|
|
if (!fs.existsSync(filePath)) {
|
||
|
|
return res.status(404).json({ error: '파일을 찾을 수 없습니다.' });
|
||
|
|
}
|
||
|
|
|
||
|
|
res.download(filePath);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.3 화면별 파일 관리
|
||
|
|
|
||
|
|
**파일**: `screenFileController.ts`, `screenFileService.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 화면별 파일 업로드
|
||
|
|
router.post('/screens/:screenId/files', authenticateToken, upload.single('file'), uploadScreenFile);
|
||
|
|
|
||
|
|
// 화면별 파일 목록 조회
|
||
|
|
router.get('/screens/:screenId/files', authenticateToken, getScreenFiles);
|
||
|
|
|
||
|
|
// 파일 삭제
|
||
|
|
router.delete('/screens/:screenId/files/:fileId', authenticateToken, deleteScreenFile);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. 외부 연동
|
||
|
|
|
||
|
|
### 10.1 외부 DB 연결
|
||
|
|
|
||
|
|
**지원 DB**: PostgreSQL, MySQL, MSSQL, Oracle, MariaDB
|
||
|
|
|
||
|
|
**파일**:
|
||
|
|
- `externalDbConnectionService.ts`
|
||
|
|
- `PostgreSQLConnector.ts`, `MySQLConnector.ts`, `MSSQLConnector.ts`, `OracleConnector.ts`, `MariaDBConnector.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 외부 DB 연결 설정
|
||
|
|
{
|
||
|
|
connection_name: "외부 ERP DB",
|
||
|
|
db_type: "mysql",
|
||
|
|
host: "192.168.0.100",
|
||
|
|
port: 3306,
|
||
|
|
database: "erp_db",
|
||
|
|
username: "erp_user",
|
||
|
|
password: "encrypted_password", // AES-256-CBC 암호화
|
||
|
|
company_code: "ILSHIN",
|
||
|
|
is_active: "Y"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 연결 테스트
|
||
|
|
POST /api/external-db-connections/:id/test
|
||
|
|
{
|
||
|
|
success: true,
|
||
|
|
message: "연결 테스트 성공"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 쿼리 실행
|
||
|
|
POST /api/external-db-connections/:id/query
|
||
|
|
{
|
||
|
|
query: "SELECT * FROM products WHERE category = ?",
|
||
|
|
params: ["전자제품"]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 10.2 외부 REST API 연결
|
||
|
|
|
||
|
|
**파일**: `externalRestApiConnectionService.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 외부 REST API 연결 설정
|
||
|
|
{
|
||
|
|
connection_name: "날씨 API",
|
||
|
|
base_url: "https://api.weather.com",
|
||
|
|
auth_type: "bearer", // bearer, api-key, basic, oauth2
|
||
|
|
auth_credentials: {
|
||
|
|
token: "encrypted_token"
|
||
|
|
},
|
||
|
|
headers: {
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
},
|
||
|
|
company_code: "ILSHIN"
|
||
|
|
}
|
||
|
|
|
||
|
|
// API 호출
|
||
|
|
POST /api/external-rest-api-connections/:id/call
|
||
|
|
{
|
||
|
|
method: "GET",
|
||
|
|
endpoint: "/weather",
|
||
|
|
params: { city: "Seoul" }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 10.3 멀티 DB 통합 쿼리
|
||
|
|
|
||
|
|
**파일**: `multiConnectionQueryService.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 여러 DB에서 동시 쿼리 실행
|
||
|
|
POST /api/multi-connection/query
|
||
|
|
{
|
||
|
|
connections: [
|
||
|
|
{
|
||
|
|
connectionId: 1,
|
||
|
|
query: "SELECT * FROM orders WHERE status = 'pending'"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
connectionId: 2,
|
||
|
|
query: "SELECT * FROM inventory WHERE quantity < 10"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
// 응답
|
||
|
|
{
|
||
|
|
success: true,
|
||
|
|
results: [
|
||
|
|
{ connectionId: 1, data: [...], rowCount: 15 },
|
||
|
|
{ connectionId: 2, data: [...], rowCount: 8 }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 10.4 Open API Proxy
|
||
|
|
|
||
|
|
**파일**: `openApiProxyController.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 날씨 API
|
||
|
|
GET /api/open-api/weather?city=Seoul
|
||
|
|
|
||
|
|
// 환율 API
|
||
|
|
GET /api/open-api/exchange-rate?from=USD&to=KRW
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. 배치/스케줄 처리
|
||
|
|
|
||
|
|
### 11.1 배치 스케줄러
|
||
|
|
|
||
|
|
**파일**: `batchSchedulerService.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 배치 설정
|
||
|
|
{
|
||
|
|
batch_name: "일일 재고 동기화",
|
||
|
|
batch_type: "external_db", // external_db, rest_api, internal
|
||
|
|
cron_schedule: "0 2 * * *", // 매일 새벽 2시
|
||
|
|
source_connection_id: 1,
|
||
|
|
source_query: "SELECT * FROM inventory",
|
||
|
|
target_table: "inventory_sync",
|
||
|
|
mapping: {
|
||
|
|
product_id: "item_id",
|
||
|
|
quantity: "stock_qty"
|
||
|
|
},
|
||
|
|
is_active: "Y"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 스케줄러 초기화 (서버 시작 시)
|
||
|
|
await BatchSchedulerService.initializeScheduler();
|
||
|
|
|
||
|
|
// 배치 수동 실행
|
||
|
|
POST /api/batch-configs/:id/execute
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.2 Cron 기반 자동 실행
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// node-cron 사용
|
||
|
|
const task = cron.schedule(
|
||
|
|
config.cron_schedule, // "0 2 * * *"
|
||
|
|
async () => {
|
||
|
|
logger.info(`배치 실행 시작: ${config.batch_name}`);
|
||
|
|
await executeBatchConfig(config);
|
||
|
|
},
|
||
|
|
{ timezone: 'Asia/Seoul' }
|
||
|
|
);
|
||
|
|
|
||
|
|
// 스케줄 업데이트
|
||
|
|
await BatchSchedulerService.updateBatchSchedule(configId);
|
||
|
|
|
||
|
|
// 스케줄 제거
|
||
|
|
await BatchSchedulerService.removeBatchSchedule(configId);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.3 배치 실행 로그
|
||
|
|
|
||
|
|
**파일**: `batchExecutionLogService.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 배치 실행 이력
|
||
|
|
{
|
||
|
|
batch_config_id: 1,
|
||
|
|
execution_status: "success", // success, failed, running
|
||
|
|
start_time: "2024-12-24T02:00:00Z",
|
||
|
|
end_time: "2024-12-24T02:05:23Z",
|
||
|
|
rows_processed: 1523,
|
||
|
|
rows_inserted: 1200,
|
||
|
|
rows_updated: 300,
|
||
|
|
rows_failed: 23,
|
||
|
|
error_message: null,
|
||
|
|
execution_log: "..."
|
||
|
|
}
|
||
|
|
|
||
|
|
// 배치 이력 조회
|
||
|
|
GET /api/batch-execution-logs?batch_config_id=1&page=1&limit=10
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.4 자동 스케줄 작업
|
||
|
|
|
||
|
|
**파일**: `app.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 메일 자동 삭제 (매일 새벽 2시)
|
||
|
|
cron.schedule('0 2 * * *', async () => {
|
||
|
|
logger.info('🗑️ 30일 지난 삭제된 메일 자동 삭제 시작...');
|
||
|
|
const deletedCount = await mailSentHistoryService.cleanupOldDeletedMails();
|
||
|
|
logger.info(`✅ 30일 지난 메일 ${deletedCount}개 자동 삭제 완료`);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 리스크/알림 자동 갱신 (10분 간격)
|
||
|
|
const cacheService = RiskAlertCacheService.getInstance();
|
||
|
|
cacheService.startAutoRefresh();
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. 컨트롤러/서비스 상세 역할
|
||
|
|
|
||
|
|
### 12.1 인증 및 관리자
|
||
|
|
|
||
|
|
#### `authController.ts` / `authService.ts`
|
||
|
|
- **로그인**: 비밀번호 검증, JWT 토큰 발급, 로그인 로그 기록
|
||
|
|
- **회원가입**: 공차중계 사용자 등록
|
||
|
|
- **토큰 갱신**: accessToken, refreshToken 갱신
|
||
|
|
- **현재 사용자 정보**: JWT 기반 사용자 정보 조회
|
||
|
|
- **로그아웃**: 토큰 무효화 (클라이언트 측 처리)
|
||
|
|
- **회사 전환**: Super Admin 전용 회사 전환
|
||
|
|
|
||
|
|
#### `adminController.ts` / `adminService.ts`
|
||
|
|
- **메뉴 관리**: 메뉴 트리 조회, 메뉴 CRUD, 메뉴 복사, 상태 토글, 일괄 삭제
|
||
|
|
- **사용자 관리**: 사용자 목록, 사용자 CRUD, 상태 변경, 비밀번호 초기화, 변경 이력
|
||
|
|
- **회사 관리**: 회사 목록, 회사 CRUD (Super Admin 전용)
|
||
|
|
- **부서 관리**: 부서 목록, 사용자-부서 통합 저장
|
||
|
|
- **로케일 설정**: 사용자 언어 설정 (ko, en)
|
||
|
|
- **테이블 스키마**: 엑셀 업로드 컬럼 매핑용 스키마 조회
|
||
|
|
|
||
|
|
### 12.2 테이블 및 데이터
|
||
|
|
|
||
|
|
#### `tableManagementController.ts` / `tableManagementService.ts`
|
||
|
|
- **테이블 목록**: PostgreSQL information_schema 조회
|
||
|
|
- **컬럼 정보**: 컬럼 타입, 제약조건, 라벨, 참조 관계
|
||
|
|
- **컬럼 라벨 관리**: 다국어 라벨, 표시 순서, 표시 여부
|
||
|
|
- **컬럼 설정**: 입력 타입, 코드 카테고리, 필수 여부, 기본값
|
||
|
|
- **테이블 조회 설정**: 조회 컬럼, 정렬 순서, 필터 조건
|
||
|
|
- **캐시 관리**: 테이블/컬럼 정보 캐시 (10분 TTL)
|
||
|
|
|
||
|
|
#### `dataController.ts` / `dataService.ts`
|
||
|
|
- **동적 데이터 조회**: 테이블명 기반 데이터 조회 (페이지네이션, 필터, 정렬)
|
||
|
|
- **동적 데이터 생성**: INSERT 쿼리 자동 생성
|
||
|
|
- **동적 데이터 수정**: UPDATE 쿼리 자동 생성
|
||
|
|
- **동적 데이터 삭제**: DELETE 쿼리 자동 생성 (논리 삭제 지원)
|
||
|
|
- **멀티테넌시 자동 필터링**: company_code 자동 추가
|
||
|
|
|
||
|
|
#### `entityJoinController.ts` / `entityJoinService.ts`
|
||
|
|
- **조인 설정 관리**: 테이블 간 조인 관계 설정 (1:N, N:M)
|
||
|
|
- **조인 쿼리 실행**: 설정된 조인 관계 기반 데이터 조회
|
||
|
|
- **참조 데이터 캐싱**: 자주 사용되는 참조 데이터 캐시
|
||
|
|
|
||
|
|
### 12.3 화면 관리
|
||
|
|
|
||
|
|
#### `screenManagementController.ts` / `screenManagementService.ts`
|
||
|
|
- **화면 메타데이터 관리**: 화면 설정 (테이블, 컬럼, 레이아웃)
|
||
|
|
- **화면 목록 조회**: 회사별, 화면 그룹별 필터링
|
||
|
|
- **화면 복사**: 화면 설정 복제
|
||
|
|
- **화면 삭제**: 논리 삭제
|
||
|
|
- **화면 설정 조회**: 화면 메타데이터 상세 조회
|
||
|
|
|
||
|
|
#### `dynamicFormController.ts` / `dynamicFormService.ts`
|
||
|
|
- **동적 폼 생성**: JSON 기반 폼 설정 → React 컴포넌트
|
||
|
|
- **폼 유효성 검사**: 필수 입력, 데이터 타입, 길이 제한
|
||
|
|
- **폼 제출**: 데이터 저장 (INSERT/UPDATE)
|
||
|
|
|
||
|
|
#### `buttonActionStandardController.ts` / `buttonActionStandardService.ts`
|
||
|
|
- **버튼 액션 표준**: 저장, 삭제, 조회, 엑셀 다운로드, 커스텀 액션
|
||
|
|
- **버튼 액션 설정**: 액션 타입, 파라미터, 권한 설정
|
||
|
|
|
||
|
|
### 12.4 플로우 및 데이터플로우
|
||
|
|
|
||
|
|
#### `flowController.ts` / `flowService.ts`
|
||
|
|
- **플로우 정의**: 플로우 생성, 수정, 삭제, 조회
|
||
|
|
- **플로우 단계**: 단계 생성, 수정, 삭제, 순서 변경
|
||
|
|
- **플로우 연결**: 단계 간 연결 (조건부 분기)
|
||
|
|
- **데이터 이동**: 단계 간 데이터 이동 (단일/배치)
|
||
|
|
- **오딧 로그**: 플로우 실행 이력 조회
|
||
|
|
|
||
|
|
#### `dataflowController.ts` / `dataflowService.ts`
|
||
|
|
- **테이블 관계 정의**: 테이블 간 관계 설정 (1:1, 1:N, N:M)
|
||
|
|
- **데이터플로우 다이어그램**: ERD 같은 시각화
|
||
|
|
- **데이터플로우 실행**: 관계 기반 데이터 동기화
|
||
|
|
- **관계 조회**: 다이어그램별 관계 목록
|
||
|
|
|
||
|
|
### 12.5 배치 관리
|
||
|
|
|
||
|
|
#### `batchController.ts` / `batchService.ts`
|
||
|
|
- **배치 설정 관리**: 배치 CRUD
|
||
|
|
- **배치 실행**: 수동 실행, 스케줄 실행
|
||
|
|
- **배치 이력**: 실행 로그, 성공/실패 통계
|
||
|
|
- **커넥션 조회**: 사용 가능한 외부 DB/API 목록
|
||
|
|
|
||
|
|
#### `batchSchedulerService.ts`
|
||
|
|
- **스케줄러 초기화**: 서버 시작 시 활성 배치 등록
|
||
|
|
- **스케줄 등록**: Cron 표현식 기반 스케줄 등록
|
||
|
|
- **스케줄 업데이트**: 배치 설정 변경 시 스케줄 재등록
|
||
|
|
- **스케줄 제거**: 배치 삭제 시 스케줄 제거
|
||
|
|
|
||
|
|
#### `batchExternalDbService.ts`
|
||
|
|
- **외부 DB 배치**: 외부 DB → 내부 DB 데이터 동기화
|
||
|
|
- **컬럼 매핑**: 소스-타겟 컬럼 매핑
|
||
|
|
- **데이터 변환**: 타입 변환, 값 변환
|
||
|
|
|
||
|
|
### 12.6 외부 연동
|
||
|
|
|
||
|
|
#### `externalDbConnectionController.ts` / `externalDbConnectionService.ts`
|
||
|
|
- **외부 DB 연결 관리**: 연결 CRUD
|
||
|
|
- **연결 테스트**: 연결 유효성 검증
|
||
|
|
- **쿼리 실행**: 외부 DB 쿼리 실행
|
||
|
|
- **테이블 목록**: 외부 DB 테이블 목록 조회
|
||
|
|
- **컬럼 정보**: 외부 DB 컬럼 정보 조회
|
||
|
|
|
||
|
|
#### `externalRestApiConnectionController.ts` / `externalRestApiConnectionService.ts`
|
||
|
|
- **REST API 연결 관리**: API 연결 CRUD
|
||
|
|
- **API 호출**: 외부 API 호출 (GET, POST, PUT, DELETE)
|
||
|
|
- **인증 처리**: Bearer, API Key, Basic, OAuth2
|
||
|
|
- **응답 캐싱**: API 응답 캐싱 (TTL 설정)
|
||
|
|
|
||
|
|
#### `multiConnectionQueryService.ts`
|
||
|
|
- **멀티 DB 통합 쿼리**: 여러 DB에서 동시 쿼리 실행
|
||
|
|
- **결과 병합**: 여러 DB 쿼리 결과 병합
|
||
|
|
- **오류 처리**: 부분 실패 시 에러 로그 기록
|
||
|
|
|
||
|
|
### 12.7 메일 관리
|
||
|
|
|
||
|
|
#### `mailAccountFileController.ts` / `mailAccountFileService.ts`
|
||
|
|
- **메일 계정 관리**: SMTP/IMAP 계정 CRUD
|
||
|
|
- **계정 테스트**: 연결 유효성 검증
|
||
|
|
- **계정 상태**: 활성/비활성 상태 관리
|
||
|
|
|
||
|
|
#### `mailTemplateFileController.ts` / `mailTemplateFileService.ts`
|
||
|
|
- **메일 템플릿 관리**: 템플릿 CRUD
|
||
|
|
- **템플릿 컴포넌트**: JSON 기반 컴포넌트 조합 (헤더, 본문, 버튼, 푸터)
|
||
|
|
- **변수 치환**: {변수명} 형태의 변수 치환
|
||
|
|
|
||
|
|
#### `mailSendSimpleController.ts` / `mailSendSimpleService.ts`
|
||
|
|
- **메일 발송**: 단일 발송, 대량 발송 (100건/배치)
|
||
|
|
- **첨부파일**: 다중 첨부파일 지원
|
||
|
|
- **참조/숨은참조**: CC, BCC 지원
|
||
|
|
- **발송 이력**: 발송 성공/실패 로그 기록
|
||
|
|
|
||
|
|
#### `mailSentHistoryController.ts` / `mailSentHistoryService.ts`
|
||
|
|
- **발송 이력 조회**: 페이지네이션, 필터링, 검색
|
||
|
|
- **이력 삭제**: 논리 삭제 (30일 후 물리 삭제)
|
||
|
|
- **자동 삭제**: Cron 기반 자동 삭제 (매일 새벽 2시)
|
||
|
|
|
||
|
|
#### `mailReceiveBasicController.ts` / `mailReceiveBasicService.ts`
|
||
|
|
- **메일 수신**: IMAP 기반 메일 수신
|
||
|
|
- **메일 목록**: 수신함 목록 조회
|
||
|
|
- **메일 읽기**: 메일 상세 조회, 첨부파일 다운로드
|
||
|
|
|
||
|
|
### 12.8 대시보드 및 리포트
|
||
|
|
|
||
|
|
#### `DashboardController.ts` / `DashboardService.ts`
|
||
|
|
- **대시보드 위젯**: 차트, 테이블, 카드 위젯
|
||
|
|
- **실시간 데이터**: 집계 쿼리 기반 실시간 데이터 조회
|
||
|
|
- **사용자 설정**: 사용자별 대시보드 레이아웃 저장
|
||
|
|
|
||
|
|
#### `reportController.ts` / `reportService.ts`
|
||
|
|
- **리포트 생성**: 동적 리포트 생성 (PDF, Excel, Word)
|
||
|
|
- **리포트 템플릿**: 템플릿 기반 리포트 생성
|
||
|
|
- **리포트 스케줄**: 정기 리포트 자동 생성 및 메일 발송
|
||
|
|
|
||
|
|
### 12.9 기타 도메인
|
||
|
|
|
||
|
|
#### `deliveryController.ts` / `deliveryService.ts`
|
||
|
|
- **배송 관리**: 배송 정보 등록, 조회, 수정
|
||
|
|
- **화물 추적**: 배송 상태 추적
|
||
|
|
|
||
|
|
#### `riskAlertController.ts` / `riskAlertService.ts` / `riskAlertCacheService.ts`
|
||
|
|
- **리스크 알림**: 리스크 기준 설정, 알림 발생
|
||
|
|
- **자동 갱신**: 10분 간격 자동 갱신 (캐시 기반)
|
||
|
|
|
||
|
|
#### `todoController.ts` / `todoService.ts`
|
||
|
|
- **To-Do 관리**: 할 일 CRUD, 상태 변경 (대기, 진행, 완료)
|
||
|
|
|
||
|
|
#### `bookingController.ts` / `bookingService.ts`
|
||
|
|
- **예약 관리**: 예약 요청 CRUD, 승인/거부
|
||
|
|
|
||
|
|
#### `digitalTwinController.ts` / `digitalTwinLayoutController.ts` / `digitalTwinDataController.ts`
|
||
|
|
- **디지털 트윈**: 야드 관제, 3D 레이아웃, 실시간 데이터 시각화
|
||
|
|
|
||
|
|
#### `scheduleController.ts` / `scheduleService.ts`
|
||
|
|
- **스케줄 자동 생성**: 작업 스케줄 자동 생성 (규칙 기반)
|
||
|
|
|
||
|
|
#### `workHistoryController.ts` / `workHistoryService.ts`
|
||
|
|
- **작업 이력**: 작업 로그 조회, 필터링, 검색
|
||
|
|
|
||
|
|
#### `roleController.ts` / `roleService.ts`
|
||
|
|
- **권한 그룹**: 권한 그룹 CRUD, 사용자-권한 매핑
|
||
|
|
|
||
|
|
#### `numberingRuleController.ts` / `numberingRuleService.ts`
|
||
|
|
- **채번 규칙**: 자동 채번 규칙 설정 (접두사, 연번, 접미사)
|
||
|
|
|
||
|
|
#### `entitySearchController.ts` / `entitySearchService.ts`
|
||
|
|
- **엔티티 검색**: 동적 엔티티 검색 (테이블, 컬럼, 조건 기반)
|
||
|
|
|
||
|
|
#### `cascadingController.ts` 시리즈
|
||
|
|
- **연쇄 드롭다운**: 조건부 드롭다운 관계 설정
|
||
|
|
- **자동 입력**: 연쇄 자동 입력 관계 설정
|
||
|
|
- **상호 배제**: 상호 배타적 선택 관계 설정
|
||
|
|
- **다단계 계층**: 계층 구조 관계 설정
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 통계 요약
|
||
|
|
|
||
|
|
| 구분 | 개수 |
|
||
|
|
|------|------|
|
||
|
|
| **컨트롤러** | 69개 |
|
||
|
|
| **서비스** | 87개 |
|
||
|
|
| **라우터** | 77개 |
|
||
|
|
| **미들웨어** | 4개 |
|
||
|
|
| **엔드포인트** | 200개 이상 |
|
||
|
|
| **데이터베이스 커넥터** | 5종 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB) |
|
||
|
|
| **유틸리티** | 16개 |
|
||
|
|
| **타입 정의** | 26개 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔧 기술 스택
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"런타임": "Node.js 20.10.0+",
|
||
|
|
"언어": "TypeScript 5.3.3",
|
||
|
|
"프레임워크": "Express 4.18.2",
|
||
|
|
"데이터베이스": "PostgreSQL (pg 8.16.3)",
|
||
|
|
"인증": "JWT (jsonwebtoken 9.0.2)",
|
||
|
|
"암호화": "BCrypt (bcryptjs 2.4.3)",
|
||
|
|
"로깅": "Winston 3.11.0",
|
||
|
|
"스케줄링": "node-cron 4.2.1",
|
||
|
|
"메일": "Nodemailer 6.10.1 + IMAP 0.8.19",
|
||
|
|
"파일 업로드": "Multer 1.4.5",
|
||
|
|
"보안": "Helmet 7.1.0",
|
||
|
|
"외부 DB": "mysql2, mssql, oracledb",
|
||
|
|
"캐싱": "In-Memory Cache (Map 기반)",
|
||
|
|
"압축": "compression 1.7.4",
|
||
|
|
"Rate Limiting": "express-rate-limit 7.1.5"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📁 핵심 파일 경로
|
||
|
|
|
||
|
|
```
|
||
|
|
backend-node/
|
||
|
|
├── src/
|
||
|
|
│ ├── app.ts # 앱 진입점
|
||
|
|
│ ├── config/environment.ts # 환경변수 설정
|
||
|
|
│ ├── database/
|
||
|
|
│ │ ├── db.ts # Raw Query 매니저
|
||
|
|
│ │ ├── DatabaseConnectorFactory.ts # DB 커넥터 팩토리
|
||
|
|
│ │ └── [DB]Connector.ts # 각 DB별 커넥터
|
||
|
|
│ ├── middleware/
|
||
|
|
│ │ ├── authMiddleware.ts # JWT 인증
|
||
|
|
│ │ ├── permissionMiddleware.ts # 권한 체크
|
||
|
|
│ │ ├── superAdminMiddleware.ts # Super Admin 체크
|
||
|
|
│ │ └── errorHandler.ts # 에러 핸들링
|
||
|
|
│ ├── utils/
|
||
|
|
│ │ ├── logger.ts # Winston 로거
|
||
|
|
│ │ ├── jwtUtils.ts # JWT 유틸
|
||
|
|
│ │ ├── encryptUtil.ts # BCrypt 암호화
|
||
|
|
│ │ ├── passwordEncryption.ts # AES 암호화
|
||
|
|
│ │ ├── cache.ts # 캐시 유틸
|
||
|
|
│ │ └── permissionUtils.ts # 권한 유틸
|
||
|
|
│ └── types/
|
||
|
|
│ ├── auth.ts # 인증 타입
|
||
|
|
│ ├── tableManagement.ts # 테이블 관리 타입
|
||
|
|
│ └── ...
|
||
|
|
└── package.json # 의존성 관리
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 서버 시작 프로세스
|
||
|
|
|
||
|
|
```
|
||
|
|
1. dotenv 환경변수 로드
|
||
|
|
2. Express 앱 생성
|
||
|
|
3. 미들웨어 설정 (helmet, cors, compression, rate-limit)
|
||
|
|
4. 데이터베이스 연결 풀 초기화
|
||
|
|
5. 라우터 등록 (77개 라우터)
|
||
|
|
6. 에러 핸들러 등록
|
||
|
|
7. 서버 리스닝 (기본 포트: 8080)
|
||
|
|
8. 데이터베이스 마이그레이션 실행
|
||
|
|
9. 배치 스케줄러 초기화
|
||
|
|
10. 리스크/알림 자동 갱신 시작
|
||
|
|
11. 메일 자동 삭제 스케줄러 시작
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔒 보안 고려사항
|
||
|
|
|
||
|
|
1. **JWT 기반 인증**: 세션 없이 무상태(Stateless) 인증
|
||
|
|
2. **비밀번호 암호화**: BCrypt (12 rounds)
|
||
|
|
3. **외부 DB 크레덴셜 암호화**: AES-256-CBC
|
||
|
|
4. **SQL Injection 방지**: Parameterized Query 필수
|
||
|
|
5. **XSS 방지**: Helmet 보안 헤더
|
||
|
|
6. **CSRF 방지**: CORS 설정 + JWT
|
||
|
|
7. **Rate Limiting**: 1분당 요청 수 제한
|
||
|
|
8. **DDL 실행 제한**: Super Admin만 가능 + 5초 간격 제한
|
||
|
|
9. **멀티테넌시 격리**: company_code 자동 필터링
|
||
|
|
10. **에러 정보 노출 방지**: 운영 환경에서 스택 트레이스 숨김
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 추천 개선 사항
|
||
|
|
|
||
|
|
1. **API 문서화**: Swagger/OpenAPI 자동 생성
|
||
|
|
2. **단위 테스트**: Jest 기반 테스트 커버리지 확대
|
||
|
|
3. **Redis 캐싱**: In-Memory 캐시 → Redis 전환
|
||
|
|
4. **로그 중앙화**: Winston → ELK Stack 연동
|
||
|
|
5. **성능 모니터링**: APM 도구 연동 (New Relic, Datadog)
|
||
|
|
6. **Docker 컨테이너화**: Dockerfile 및 docker-compose 개선
|
||
|
|
7. **CI/CD 파이프라인**: GitHub Actions, Jenkins 연동
|
||
|
|
8. **API Rate Limiting 세분화**: 엔드포인트별 제한 설정
|
||
|
|
9. **WebSocket 지원**: 실시간 알림 및 데이터 업데이트
|
||
|
|
10. **GraphQL API**: REST API + GraphQL 병행 지원
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**문서 작성자**: WACE 백엔드 전문가
|
||
|
|
**최종 수정일**: 2026-02-06
|
||
|
|
**버전**: 1.0.0
|
||
|
|
|