최초커밋
This commit is contained in:
852
docs/NodeJS_Refactoring_Rules.md
Normal file
852
docs/NodeJS_Refactoring_Rules.md
Normal file
@@ -0,0 +1,852 @@
|
||||
# Java Spring Boot → Node.js + TypeScript 리팩토링 가이드라인
|
||||
|
||||
## 📋 프로젝트 개요
|
||||
|
||||
### 목표
|
||||
|
||||
- 기존 Java Spring Boot 백엔드를 Node.js + TypeScript로 완전 리팩토링
|
||||
- React 프론트엔드와의 완벽한 통합
|
||||
- 타입 안전성과 개발 생산성 향상
|
||||
|
||||
### 기술 스택
|
||||
|
||||
```json
|
||||
{
|
||||
"runtime": "Node.js ^20.10.0",
|
||||
"framework": "Express ^4.18.2",
|
||||
"language": "TypeScript ^5.3.3",
|
||||
"orm": "Prisma ^5.7.1",
|
||||
"database": "PostgreSQL ^8.11.3",
|
||||
"authentication": "JWT + Passport",
|
||||
"testing": "Jest + Supertest"
|
||||
}
|
||||
```
|
||||
|
||||
## 🏗️ 아키텍처 원칙
|
||||
|
||||
### 1. 계층별 분리
|
||||
|
||||
```
|
||||
Controller → Service → Repository → Database
|
||||
↓ ↓ ↓ ↓
|
||||
라우팅 비즈니스로직 데이터접근 PostgreSQL
|
||||
```
|
||||
|
||||
### 2. 의존성 주입 패턴
|
||||
|
||||
```typescript
|
||||
interface IUserService {
|
||||
getUsers(): Promise<User[]>;
|
||||
createUser(user: CreateUserDto): Promise<User>;
|
||||
}
|
||||
|
||||
class UserController {
|
||||
constructor(private userService: IUserService) {}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 타입 안전성 우선
|
||||
|
||||
- 모든 API 응답/요청에 TypeScript 인터페이스 정의
|
||||
- Prisma 스키마 기반 타입 자동 생성
|
||||
- 런타임 타입 검증 (Joi)
|
||||
|
||||
## 📁 프로젝트 구조 규칙
|
||||
|
||||
### 디렉토리 구조
|
||||
|
||||
```
|
||||
src/
|
||||
├── config/ # 설정 파일
|
||||
├── controllers/ # HTTP 요청 처리
|
||||
├── services/ # 비즈니스 로직
|
||||
├── repositories/ # 데이터 접근 계층
|
||||
├── middleware/ # Express 미들웨어
|
||||
├── utils/ # 유틸리티 함수
|
||||
├── types/ # TypeScript 타입 정의
|
||||
├── validators/ # 입력 검증 스키마
|
||||
└── app.ts # 애플리케이션 진입점
|
||||
```
|
||||
|
||||
### 파일 명명 규칙
|
||||
|
||||
- **컨트롤러**: `{Domain}Controller.ts` (예: `UserController.ts`)
|
||||
- **서비스**: `{Domain}Service.ts` (예: `UserService.ts`)
|
||||
- **리포지토리**: `{Domain}Repository.ts` (예: `UserRepository.ts`)
|
||||
- **타입**: `{Domain}.types.ts` (예: `user.types.ts`)
|
||||
- **검증**: `{Domain}.validator.ts` (예: `user.validator.ts`)
|
||||
|
||||
## 💻 코딩 컨벤션
|
||||
|
||||
### 1. TypeScript 규칙
|
||||
|
||||
```typescript
|
||||
// ✅ 권장
|
||||
interface CreateUserRequest {
|
||||
userName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
deptCode?: string;
|
||||
}
|
||||
|
||||
type UserResponse = {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
status: "Y" | "N";
|
||||
regDate: Date;
|
||||
};
|
||||
|
||||
// ❌ 금지
|
||||
const user: any = {};
|
||||
const userName: string = req.body.userName as string;
|
||||
```
|
||||
|
||||
### 2. 클래스 및 함수 정의
|
||||
|
||||
```typescript
|
||||
// ✅ 권장
|
||||
export class UserService {
|
||||
constructor(private userRepository: UserRepository) {}
|
||||
|
||||
async getUsers(params: GetUsersParams): Promise<UserResponse[]> {
|
||||
try {
|
||||
return await this.userRepository.findMany(params);
|
||||
} catch (error) {
|
||||
throw new ServiceError("사용자 목록 조회 실패", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 에러 처리
|
||||
|
||||
```typescript
|
||||
export class ServiceError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public originalError?: Error,
|
||||
public statusCode: number = 500
|
||||
) {
|
||||
super(message);
|
||||
this.name = "ServiceError";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔐 인증 시스템 마이그레이션 가이드라인
|
||||
|
||||
### 1. 기존 인증 시스템 분석
|
||||
|
||||
#### **현재 Java Spring Boot 인증 구조**
|
||||
|
||||
```java
|
||||
// 기존 API 엔드포인트
|
||||
POST /api/auth/login // 로그인
|
||||
POST /api/auth/logout // 로그아웃
|
||||
GET /api/auth/me // 현재 사용자 정보
|
||||
GET /api/auth/status // 인증 상태 확인
|
||||
|
||||
// 기존 핵심 클래스
|
||||
- ApiLoginController: REST API 기반 로그인 컨트롤러
|
||||
- LoginService: 로그인 비즈니스 로직
|
||||
- PersonBean: 사용자 정보 객체
|
||||
- EncryptUtil: 비밀번호 암호화 유틸리티
|
||||
```
|
||||
|
||||
#### **기존 인증 플로우**
|
||||
|
||||
1. **로그인 검증**: `LoginService.loginPwdCheck()` - 사용자 ID/비밀번호 검증
|
||||
2. **세션 관리**: `SessionManager` - HttpSession 기반 세션 관리
|
||||
3. **로그 기록**: `insertLoginAccessLog()` - LOGIN_ACCESS_LOG 테이블에 접속 로그
|
||||
4. **사용자 정보**: `PersonBean` - 세션에 저장되는 사용자 정보 객체
|
||||
|
||||
### 2. Node.js 마이그레이션 전략
|
||||
|
||||
#### **기존 로직 유지 원칙**
|
||||
|
||||
- ✅ 기존 비즈니스 로직 그대로 유지
|
||||
- ✅ 기존 데이터베이스 구조 그대로 사용
|
||||
- ✅ 기존 API 응답 형식 유지
|
||||
- ✅ 기존 로그인 플로우 유지
|
||||
- 🔄 세션 → JWT 토큰으로 변경 (기능은 동일)
|
||||
|
||||
#### **변경 사항**
|
||||
|
||||
```typescript
|
||||
// 기존: HttpSession 기반
|
||||
HttpSession session = request.getSession();
|
||||
session.setAttribute(Constants.PERSON_BEAN, person);
|
||||
|
||||
// 변경: JWT 토큰 기반
|
||||
const token = jwt.sign({ userId: user.userId, ...userInfo }, secret);
|
||||
```
|
||||
|
||||
### 3. 인증 시스템 구현 계획
|
||||
|
||||
#### **Phase 2-1A: 기본 인증 구조 (1주)**
|
||||
|
||||
**3.1 타입 정의 (기존 구조 유지)**
|
||||
|
||||
```typescript
|
||||
// src/types/auth.ts
|
||||
export interface LoginRequest {
|
||||
userId: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
userId: string;
|
||||
userName: string;
|
||||
deptName: string;
|
||||
companyCode: string;
|
||||
companyName: string;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
data?: T;
|
||||
error?: {
|
||||
code: string;
|
||||
details?: any;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**3.2 비밀번호 암호화 (기존 로직 포팅)**
|
||||
|
||||
```typescript
|
||||
// src/utils/passwordUtils.ts
|
||||
// 기존 EncryptUtil.encrypt() 로직을 Node.js로 포팅
|
||||
export class PasswordUtils {
|
||||
static encrypt(password: string): string {
|
||||
// 기존 Java EncryptUtil 로직을 그대로 포팅
|
||||
}
|
||||
|
||||
static matches(plainPassword: string, encryptedPassword: string): boolean {
|
||||
// 기존 Java EncryptUtil.matches() 로직 포팅
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3.3 JWT 토큰 관리**
|
||||
|
||||
```typescript
|
||||
// src/utils/jwtUtils.ts
|
||||
export class JwtUtils {
|
||||
static generateToken(userInfo: UserInfo): string {
|
||||
// PersonBean 정보를 JWT 페이로드로 변환
|
||||
}
|
||||
|
||||
static verifyToken(token: string): UserInfo {
|
||||
// JWT 토큰 검증 및 사용자 정보 추출
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3.4 인증 미들웨어**
|
||||
|
||||
```typescript
|
||||
// src/middleware/authMiddleware.ts
|
||||
export const authenticateToken = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
// JWT 토큰 검증하여 기존 세션 방식과 동일한 효과
|
||||
// req.user에 사용자 정보 설정 (기존 PersonBean과 동일)
|
||||
};
|
||||
```
|
||||
|
||||
#### **Phase 2-1B: 핵심 인증 API (1주)**
|
||||
|
||||
**3.5 로그인 API (`POST /api/auth/login`)**
|
||||
|
||||
```typescript
|
||||
// src/controllers/authController.ts
|
||||
export class AuthController {
|
||||
async login(req: Request, res: Response) {
|
||||
// 기존 ApiLoginController.login() 로직을 그대로 포팅
|
||||
// 1. 사용자 ID/비밀번호 검증
|
||||
// 2. 기존 loginPwdCheck 로직 사용
|
||||
// 3. 로그인 로그 기록
|
||||
// 4. JWT 토큰 발급 (세션 대체)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3.6 인증 서비스**
|
||||
|
||||
```typescript
|
||||
// src/services/authService.ts
|
||||
export class AuthService {
|
||||
async loginPwdCheck(userId: string, password: string): Promise<LoginResult> {
|
||||
// 기존 LoginService.loginPwdCheck() 로직 포팅
|
||||
}
|
||||
|
||||
async insertLoginAccessLog(logData: LoginLogData): Promise<void> {
|
||||
// 기존 insertLoginAccessLog() 로직 포팅
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 파일 구조 (기존 로직 유지)
|
||||
|
||||
```
|
||||
backend-node/src/
|
||||
├── auth/
|
||||
│ ├── authController.ts # ApiLoginController 포팅
|
||||
│ ├── authService.ts # LoginService 포팅
|
||||
│ └── authMiddleware.ts # JWT 기반 인증
|
||||
├── types/
|
||||
│ ├── auth.ts # 기존 DTO 클래스 포팅
|
||||
│ └── common.ts # 기존 ApiResponse 포팅
|
||||
├── utils/
|
||||
│ ├── passwordUtils.ts # EncryptUtil 포팅
|
||||
│ ├── jwtUtils.ts # JWT 토큰 관리
|
||||
│ └── logger.ts # 기존 로깅 로직
|
||||
└── routes/
|
||||
└── authRoutes.ts # 기존 API 엔드포인트
|
||||
```
|
||||
|
||||
### 5. 기존 로직 포팅 우선순위
|
||||
|
||||
#### **우선순위 1: 핵심 인증 로직**
|
||||
|
||||
1. **비밀번호 암호화 유틸리티** (EncryptUtil 포팅)
|
||||
2. **로그인 검증 로직** (LoginService.loginPwdCheck 포팅)
|
||||
3. **로그인 API** (ApiLoginController.login 포팅)
|
||||
|
||||
#### **우선순위 2: 보조 기능**
|
||||
|
||||
1. **로그인 로그 기록** (insertLoginAccessLog 포팅)
|
||||
2. **사용자 정보 조회** (getCurrentUser 포팅)
|
||||
3. **로그아웃 API** (logout 포팅)
|
||||
|
||||
### 6. 테스트 전략
|
||||
|
||||
#### **기존 API 호환성 테스트**
|
||||
|
||||
- 기존 Java API와 동일한 응답 형식 확인
|
||||
- 기존 로그인 플로우 동작 확인
|
||||
- 기존 로그 기록 기능 확인
|
||||
|
||||
#### **보안 테스트**
|
||||
|
||||
- JWT 토큰 유효성 검증
|
||||
- 비밀번호 암호화 정확성 확인
|
||||
- Rate Limiting 동작 확인
|
||||
|
||||
## 🗄️ 데이터베이스 설계 규칙
|
||||
|
||||
### 1. 기존 데이터베이스 스키마 참고
|
||||
|
||||
**참고 문서**: `docs/Database_Schema_Collection.md`
|
||||
|
||||
이 문서에는 기존 PostgreSQL 데이터베이스의 완전한 스키마 정보가 포함되어 있습니다:
|
||||
|
||||
- 전체 테이블 목록 (약 200개 테이블)
|
||||
- 각 테이블의 상세 컬럼 구조
|
||||
- 외래키 관계 정보
|
||||
- 인덱스 및 제약조건 정보
|
||||
- 시퀀스, 뷰, 함수 정보
|
||||
|
||||
### 2. 핵심 테이블 구조
|
||||
|
||||
#### **user_info 테이블**
|
||||
|
||||
```prisma
|
||||
model UserInfo {
|
||||
sabun String? @map("sabun") // 사번
|
||||
userId String @unique @map("user_id") // 사용자 ID (NOT NULL)
|
||||
userPassword String? @map("user_password") // 비밀번호
|
||||
userName String? @map("user_name") // 사용자명
|
||||
userNameEng String? @map("user_name_eng") // 영문명
|
||||
userNameCn String? @map("user_name_cn") // 중문명
|
||||
deptCode String? @map("dept_code") // 부서코드
|
||||
deptName String? @map("dept_name") // 부서명
|
||||
positionCode String? @map("position_code") // 직급코드
|
||||
positionName String? @map("position_name") // 직급명
|
||||
|
||||
// 관계 정의
|
||||
userAuths UserAuth[]
|
||||
deptInfo DeptInfo? @relation(fields: [deptCode], references: [deptCode])
|
||||
|
||||
@@map("user_info")
|
||||
}
|
||||
```
|
||||
|
||||
#### **menu_info 테이블**
|
||||
|
||||
```prisma
|
||||
model MenuInfo {
|
||||
objid Int @id @default(autoincrement()) @map("objid") // 객체ID (NOT NULL)
|
||||
menuType Int? @map("menu_type") // 메뉴타입
|
||||
parentObjId Int? @map("parent_obj_id") // 부모객체ID
|
||||
menuNameKor String? @map("menu_name_kor") // 한글메뉴명
|
||||
menuNameEng String? @map("menu_name_eng") // 영문메뉴명
|
||||
seq Int? @map("seq") // 순서
|
||||
menuUrl String? @map("menu_url") // 메뉴URL
|
||||
menuDesc String? @map("menu_desc") // 메뉴설명
|
||||
writer String? @map("writer") // 작성자
|
||||
regdate DateTime? @map("regdate") // 등록일
|
||||
|
||||
// 관계 정의
|
||||
parent MenuInfo? @relation("MenuToMenu", fields: [parentObjId], references: [objid])
|
||||
children MenuInfo[] @relation("MenuToMenu")
|
||||
menuAuthGroups MenuAuthGroup[]
|
||||
|
||||
@@map("menu_info")
|
||||
}
|
||||
```
|
||||
|
||||
#### **dept_info 테이블**
|
||||
|
||||
```prisma
|
||||
model DeptInfo {
|
||||
deptCode String @id @map("dept_code") // 부서코드 (NOT NULL)
|
||||
parentDeptCode String? @map("parent_dept_code") // 상위부서코드
|
||||
deptName String? @map("dept_name") // 부서명
|
||||
masterSabun String? @map("master_sabun") // 마스터사번
|
||||
masterUserId String? @map("master_user_id") // 마스터사용자ID
|
||||
location String? @map("location") // 위치
|
||||
locationName String? @map("location_name") // 위치명
|
||||
regdate DateTime? @map("regdate") // 등록일
|
||||
dataType String? @map("data_type") // 데이터타입
|
||||
|
||||
// 관계 정의
|
||||
parent DeptInfo? @relation("DeptToDept", fields: [parentDeptCode], references: [deptCode])
|
||||
children DeptInfo[] @relation("DeptToDept")
|
||||
users UserInfo[]
|
||||
|
||||
@@map("dept_info")
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 주요 테이블 카테고리
|
||||
|
||||
#### **사용자/권한 관련**
|
||||
|
||||
- `user_info` - 사용자 정보
|
||||
- `user_info_history` - 사용자 정보 히스토리
|
||||
- `dept_info` - 부서 정보
|
||||
- `authority_master` - 권한 마스터
|
||||
- `rel_menu_auth` - 메뉴 권한 관계
|
||||
|
||||
#### **메뉴/시스템 관련**
|
||||
|
||||
- `menu_info` - 메뉴 정보
|
||||
- `table_labels` - 테이블 라벨
|
||||
- `column_labels` - 컬럼 라벨
|
||||
|
||||
#### **다국어 관련**
|
||||
|
||||
- `multi_lang_key_master` - 다국어 키 마스터
|
||||
- `multi_lang_text` - 다국어 텍스트
|
||||
- `language_master` - 언어 마스터
|
||||
|
||||
#### **비즈니스 로직 관련**
|
||||
|
||||
- `comm_code` - 공통 코드
|
||||
- `company_mng` - 회사 관리
|
||||
- `contract_mgmt` - 계약 관리
|
||||
- `order_mgmt` - 주문 관리
|
||||
- `inventory_mgmt` - 재고 관리
|
||||
- `part_mgmt` - 부품 관리
|
||||
|
||||
### 4. 마이그레이션 관리
|
||||
|
||||
```bash
|
||||
# 마이그레이션 생성
|
||||
npx prisma migrate dev --name add_user_table
|
||||
|
||||
# 마이그레이션 적용
|
||||
npx prisma migrate deploy
|
||||
|
||||
# 스키마 동기화
|
||||
npx prisma db push
|
||||
```
|
||||
|
||||
### 5. 데이터 타입 매핑 규칙
|
||||
|
||||
| PostgreSQL | Prisma | TypeScript |
|
||||
| ----------------------------- | ---------- | ---------- |
|
||||
| `character varying` | `String` | `string` |
|
||||
| `numeric` | `Int` | `number` |
|
||||
| `timestamp without time zone` | `DateTime` | `Date` |
|
||||
| `boolean` | `Boolean` | `boolean` |
|
||||
| `text` | `String` | `string` |
|
||||
|
||||
## 🔐 인증 및 보안 규칙
|
||||
|
||||
### 1. JWT 토큰 구조
|
||||
|
||||
```typescript
|
||||
interface JWTPayload {
|
||||
userId: string;
|
||||
userName: string;
|
||||
email: string;
|
||||
deptCode?: string;
|
||||
permissions: string[];
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 인증 미들웨어
|
||||
|
||||
```typescript
|
||||
export const authenticateToken = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader?.split(" ")[1];
|
||||
|
||||
if (!token) {
|
||||
throw new AuthError("토큰이 제공되지 않았습니다", 401);
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
|
||||
req.user = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
next(new AuthError("유효하지 않은 토큰입니다", 401));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 🌐 API 설계 규칙
|
||||
|
||||
### 1. RESTful API 설계
|
||||
|
||||
```typescript
|
||||
// ✅ 권장
|
||||
GET /api/users // 사용자 목록 조회
|
||||
GET /api/users/:id // 특정 사용자 조회
|
||||
POST /api/users // 사용자 생성
|
||||
PUT /api/users/:id // 사용자 전체 수정
|
||||
PATCH /api/users/:id // 사용자 부분 수정
|
||||
DELETE /api/users/:id // 사용자 삭제
|
||||
```
|
||||
|
||||
### 2. 응답 형식 표준화
|
||||
|
||||
```typescript
|
||||
interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
message?: string;
|
||||
error?: {
|
||||
code: string;
|
||||
details?: any;
|
||||
};
|
||||
pagination?: {
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 컨트롤러 구현
|
||||
|
||||
```typescript
|
||||
export class UserController {
|
||||
constructor(private userService: UserService) {}
|
||||
|
||||
async getUsers(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { page = 1, limit = 10, search } = req.query;
|
||||
const params: GetUsersParams = {
|
||||
page: Number(page),
|
||||
limit: Number(limit),
|
||||
search: search as string,
|
||||
};
|
||||
|
||||
const result = await this.userService.getUsers(params);
|
||||
res.json(
|
||||
successResponse(result.data, "사용자 목록을 성공적으로 조회했습니다")
|
||||
);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 테스트 규칙
|
||||
|
||||
### 1. 테스트 구조
|
||||
|
||||
```typescript
|
||||
describe("UserController", () => {
|
||||
let userController: UserController;
|
||||
let userService: jest.Mocked<UserService>;
|
||||
|
||||
beforeEach(() => {
|
||||
userService = createMockUserService();
|
||||
userController = new UserController(userService);
|
||||
});
|
||||
|
||||
describe("getUsers", () => {
|
||||
it("should return users list successfully", async () => {
|
||||
const mockUsers = [createMockUser()];
|
||||
userService.getUsers.mockResolvedValue({
|
||||
data: mockUsers,
|
||||
pagination: { page: 1, limit: 10, total: 1, totalPages: 1 },
|
||||
});
|
||||
|
||||
const req = createMockRequest({ page: 1, limit: 10 });
|
||||
const res = createMockResponse();
|
||||
const next = jest.fn();
|
||||
|
||||
await userController.getUsers(req, res, next);
|
||||
|
||||
expect(res.json).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: true,
|
||||
data: mockUsers,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 🚀 배포 및 운영 규칙
|
||||
|
||||
### 1. 환경별 설정
|
||||
|
||||
```typescript
|
||||
interface Environment {
|
||||
nodeEnv: "development" | "production" | "test";
|
||||
port: number;
|
||||
database: {
|
||||
url: string;
|
||||
pool: {
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
};
|
||||
jwt: {
|
||||
secret: string;
|
||||
expiresIn: string;
|
||||
};
|
||||
cors: {
|
||||
origin: string[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 로깅 규칙
|
||||
|
||||
```typescript
|
||||
export const logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || "info",
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
|
||||
new winston.transports.File({ filename: "logs/combined.log" }),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 📦 필수 패키지 목록
|
||||
|
||||
### Core Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"express": "^4.18.2",
|
||||
"prisma": "^5.7.1",
|
||||
"@prisma/client": "^5.7.1",
|
||||
"pg": "^8.11.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"helmet": "^7.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nodemailer": "^6.9.7",
|
||||
"winston": "^3.11.0",
|
||||
"joi": "^17.11.0",
|
||||
"redis": "^4.6.10",
|
||||
"compression": "^1.7.4",
|
||||
"express-rate-limit": "^7.1.5",
|
||||
"dotenv": "^16.3.1"
|
||||
}
|
||||
```
|
||||
|
||||
### Dev Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"typescript": "^5.3.3",
|
||||
"@types/node": "^20.10.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/morgan": "^1.9.9",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/sanitize-html": "^2.9.5",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/csv-parser": "^1.2.3",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.11",
|
||||
"supertest": "^6.3.3",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"nodemon": "^3.0.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"eslint": "^8.55.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"prettier": "^3.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 마이그레이션 체크리스트
|
||||
|
||||
### ✅ Phase 1: 기반 구축 (1-2주) - **완료**
|
||||
|
||||
- [x] Node.js + TypeScript 프로젝트 설정
|
||||
- [x] 기존 데이터베이스 스키마 분석 (`docs/Database_Schema_Collection.md` 참고)
|
||||
- [x] Prisma 스키마 설계 및 마이그레이션
|
||||
- [x] 기본 인증 시스템 구현
|
||||
- [x] 에러 처리 및 로깅 설정
|
||||
- [x] 서버 실행 및 포트 설정 (8080 포트)
|
||||
|
||||
### 🔄 Phase 2: 핵심 API 개발 (4-6주) - **진행 중**
|
||||
|
||||
#### **Phase 2-1: 인증 시스템 마이그레이션 (2주) - 우선 진행**
|
||||
|
||||
**Phase 2-1A: 기본 인증 구조 (1주) - ✅ 완료**
|
||||
|
||||
- [x] 기존 인증 타입 정의 (`LoginRequest`, `UserInfo`, `ApiResponse`)
|
||||
- [x] 비밀번호 암호화 유틸리티 (기존 `EncryptUtil` 포팅)
|
||||
- [x] JWT 토큰 관리 유틸리티
|
||||
- [x] 인증 미들웨어 구현
|
||||
|
||||
**Phase 2-1B: 핵심 인증 API (1주) - ✅ 완료**
|
||||
|
||||
- [x] 로그인 API (`POST /api/auth/login`) - 기존 `ApiLoginController.login()` 포팅
|
||||
- [x] 사용자 정보 API (`GET /api/auth/me`) - 기존 `getCurrentUser()` 포팅
|
||||
- [x] 로그아웃 API (`POST /api/auth/logout`) - 기존 `logout()` 포팅
|
||||
- [x] 인증 상태 확인 API (`GET /api/auth/status`) - 기존 `checkAuthStatus()` 포팅
|
||||
- [x] 로그인 로그 기록 기능 (기존 `insertLoginAccessLog()` 포팅)
|
||||
- [x] 데이터베이스 스키마 동기화 (Prisma db pull)
|
||||
- [x] 실제 데이터베이스 연결 및 테스트
|
||||
- [x] 대소문자 처리 문제 해결
|
||||
- [x] 로그인 API 성공 테스트 완료
|
||||
|
||||
#### **Phase 2-2: 사용자 관리 API (1주)**
|
||||
|
||||
- [ ] 사용자 목록 조회 API (`user_info` 테이블 기반)
|
||||
- [ ] 사용자 상세 조회 API
|
||||
- [ ] 사용자 생성/수정 API
|
||||
- [ ] 사용자 비밀번호 변경 API
|
||||
|
||||
#### **Phase 2-2A: 메뉴 관리 API (완료 ✅)**
|
||||
|
||||
- [x] 관리자 메뉴 조회 API (`GET /api/admin/menus`) - **완료: 기존 `AdminController.getAdminMenuList()` 포팅**
|
||||
- [x] 사용자 메뉴 조회 API (`GET /api/admin/user-menus`) - **완료: 기존 `AdminController.getUserMenuList()` 포팅**
|
||||
- [x] 메뉴 정보 조회 API (`GET /api/admin/menus/:menuId`) - **완료: 기존 `AdminController.getMenuInfo()` 포팅**
|
||||
- [x] JWT 토큰 인증 미들웨어 적용
|
||||
- [x] Prisma $queryRaw를 사용한 복잡한 재귀 쿼리 포팅
|
||||
- [x] 환경변수 설정 및 데이터베이스 연결 문제 해결
|
||||
- [x] 프론트엔드 API 클라이언트에 JWT 토큰 자동 추가
|
||||
- [x] 401/500 오류 해결 및 정상 작동 확인
|
||||
|
||||
#### **Phase 2-3: 부서 관리 API (1주)**
|
||||
|
||||
- [ ] 부서 목록 조회 API (`dept_info` 테이블 기반)
|
||||
- [ ] 부서 트리 구조 API
|
||||
- [ ] 부서 생성/수정 API
|
||||
|
||||
#### **Phase 2-4: 메뉴 및 권한 관리 API (1주)**
|
||||
|
||||
- [ ] 메뉴 관리 API (`menu_info` 테이블 기반)
|
||||
- [ ] 권한 관리 API (`authority_master`, `rel_menu_auth` 테이블 기반)
|
||||
- [ ] 사용자별 메뉴 권한 조회 API
|
||||
|
||||
#### **Phase 2-5: 다국어 및 공통 관리 API (1주)**
|
||||
|
||||
- [ ] 다국어 관리 API (`multi_lang_key_master`, `multi_lang_text` 테이블 기반)
|
||||
- [ ] 공통 코드 관리 API (`comm_code` 테이블 기반)
|
||||
|
||||
### Phase 3: 비즈니스 로직 API (3-4주)
|
||||
|
||||
- [ ] 회사 관리 API (`company_mng` 테이블 기반)
|
||||
- [ ] 계약 관리 API (`contract_mgmt` 테이블 기반)
|
||||
- [ ] 주문 관리 API (`order_mgmt` 테이블 기반)
|
||||
- [ ] 재고 관리 API (`inventory_mgmt` 테이블 기반)
|
||||
- [ ] 부품 관리 API (`part_mgmt` 테이블 기반)
|
||||
|
||||
### Phase 4: 고급 기능 (2-3주)
|
||||
|
||||
- [ ] 파일 업로드/다운로드 (`attach_file_info` 테이블 기반)
|
||||
- [ ] Excel 처리 기능
|
||||
- [ ] 이메일 발송 기능 (`mail_log` 테이블 기반)
|
||||
- [ ] 배치 처리 기능
|
||||
- [ ] 로그 관리 API (`login_access_log` 테이블 기반)
|
||||
|
||||
### Phase 5: 테스트 및 최적화 (1-2주)
|
||||
|
||||
- [ ] 단위 테스트 작성
|
||||
- [ ] 통합 테스트 작성
|
||||
- [ ] 성능 최적화
|
||||
- [ ] 보안 검증
|
||||
|
||||
### Phase 6: 배포 및 운영 (1주)
|
||||
|
||||
- [ ] Docker 컨테이너화
|
||||
- [ ] CI/CD 파이프라인 구축
|
||||
- [ ] 모니터링 설정
|
||||
- [ ] 문서화
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **기존 데이터 보존**: 마이그레이션 시 기존 데이터 손실 방지
|
||||
2. **API 호환성**: 프론트엔드와의 호환성 유지
|
||||
3. **보안**: 인증/인가 로직 완전 재구현
|
||||
4. **성능**: 데이터베이스 쿼리 최적화
|
||||
5. **테스트**: 모든 기능에 대한 테스트 코드 작성
|
||||
6. **스키마 참고**: `docs/Database_Schema_Collection.md` 문서를 항상 참고하여 정확한 테이블 구조 반영
|
||||
7. **데이터 타입 매핑**: PostgreSQL → Prisma → TypeScript 타입 매핑 정확성 확인
|
||||
8. **관계 설정**: 외래키 관계를 Prisma 관계로 정확히 매핑
|
||||
9. **히스토리 테이블**: `*_history` 테이블들의 처리 방안 수립
|
||||
10. **임시 테이블**: `temp*`, `*_temp` 테이블들의 정리 및 제거 계획 수립
|
||||
11. **메뉴 API 완료**: `/api/admin/menus`와 `/api/admin/user-menus` API가 성공적으로 구현되어 프론트엔드 메뉴 표시가 정상 작동
|
||||
12. **JWT 토큰 관리**: 프론트엔드 API 클라이언트에서 JWT 토큰을 자동으로 포함하여 인증 문제 해결
|
||||
13. **환경변수 관리**: Prisma 스키마에서 직접 데이터베이스 URL 설정으로 환경변수 로딩 문제 해결
|
||||
|
||||
## 🎯 성공 지표
|
||||
|
||||
1. **성능 개선**: API 응답 시간 30% 단축
|
||||
2. **개발 생산성**: 새로운 기능 개발 시간 50% 단축
|
||||
3. **유지보수성**: 코드 복잡도 감소
|
||||
4. **확장성**: 마이크로서비스 아키텍처 준비
|
||||
|
||||
---
|
||||
|
||||
**마지막 업데이트**: 2024년 12월 20일
|
||||
**버전**: 1.7.0
|
||||
**작성자**: AI Assistant
|
||||
**현재 상태**: Phase 1 완료, Phase 2-1A 완료, Phase 2-1B 완료, Phase 2-2A 완료 ✅ (메뉴 API 구현 완료)
|
||||
Reference in New Issue
Block a user