Files
vexplor/docs/NodeJS_Refactoring_Rules.md
2025-08-25 14:08:08 +09:00

1714 lines
50 KiB
Markdown

# 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: 사용자 관리 기능 Node.js 리팩토링 계획 (1주)**
**📋 사용자 관리 기능 Node.js 리팩토링 개요**
**목표**: 기존 Java Spring Boot의 사용자 관리 기능을 Node.js + TypeScript로 완전 리팩토링
**기존 Java 백엔드 (`@backend/`) 분석**
- Spring Framework 기반의 `AdminController``AdminService`
- MyBatis를 사용한 데이터베이스 접근
- 사용자 CRUD, 권한 관리, 상태 변경 등 완전한 기능 구현
**현재 Node.js 백엔드 (`@backend-node/`) 상황**
- 기본적인 사용자 목록 조회만 더미 데이터로 구현
- 실제 데이터베이스 연동 부족
- 사용자 관리 핵심 기능 미구현
**🎯 리팩토링 목표**
1. **기존 Java 백엔드의 사용자 관리 기능을 Node.js로 완전 이전**
2. **Prisma ORM을 활용한 데이터베이스 연동**
3. **기존 API 응답 형식과 호환성 유지**
4. **보안 및 인증 기능 강화**
**🛠️ 단계별 구현 계획**
**Phase 2-2A-1: 데이터베이스 스키마 및 모델 정리 (1일)**
- [x] Prisma 스키마에 `user_info` 테이블 정의 완료
- [ ] 사용자 관련 추가 테이블 스키마 확인 (부서, 권한 등)
- [ ] 데이터 타입 및 관계 정의
**Phase 2-2A-2: 핵심 사용자 관리 API 구현 (3일)**
**사용자 CRUD API**
```typescript
// 기존 Java AdminController의 핵심 메서드들
- GET /api/admin/users - (, )
- GET /api/admin/users/:userId -
- POST /api/admin/users - /
- PUT /api/admin/users/:userId/status -
- DELETE /api/admin/users/:userId -
```
**사용자 관리 부가 기능**
```typescript
- POST /api/admin/users/check-duplicate - ID
- POST /api/admin/users/reset-password -
- GET /api/admin/users/:userId/history -
- GET /api/admin/departments -
```
**Phase 2-2A-3: 서비스 레이어 구현 (2일)**
**AdminService 확장**
```typescript
// 기존 Java AdminService의 핵심 메서드들
- getEtcUserList() -
- getEtcUserInfo() -
- saveEtcUserInfo() - /
- checkDuplicateEtcUserId() -
- changeUserStatus() -
- getUserHistoryList() -
```
**데이터베이스 연동**
```typescript
- Prisma ORM을 PostgreSQL
-
-
```
**Phase 2-2A-4: 보안 및 검증 강화 (1일)**
**입력값 검증**
```typescript
-
- SQL
- XSS
```
**권한 관리**
```typescript
-
-
- (RBAC)
```
**🔄 구현 우선순위**
**High Priority (1-2일차)**
1. 사용자 목록 조회 API (실제 DB 연동)
2. 사용자 상세 조회 API
3. 사용자 등록/수정 API
4. 기본적인 에러 핸들링
**Medium Priority (3-4일차)**
1. 사용자 상태 변경 API
2. 사용자 ID 중복 체크 API
3. 부서 목록 조회 API
4. 페이징 및 검색 기능
**Low Priority (5일차)**
1. 사용자 변경 이력 API
2. 비밀번호 초기화 API
3. 사용자 삭제 API
4. 고급 검색 및 필터링
**🔧 기술적 고려사항**
**데이터베이스 연동**
- Prisma ORM 사용으로 타입 안전성 확보
- 기존 PostgreSQL 스키마와 호환성 유지
- 마이그레이션 스크립트 작성
**API 호환성**
- 기존 Java 백엔드와 동일한 응답 형식 유지
- 프론트엔드 변경 최소화
- 점진적 마이그레이션 지원
**성능 최적화**
- 데이터베이스 인덱스 활용
- 쿼리 최적화
- 캐싱 전략 수립
**📊 테스트 계획**
1. **단위 테스트**: 각 서비스 메서드별 테스트
2. **통합 테스트**: API 엔드포인트별 테스트
3. **데이터베이스 테스트**: 실제 DB 연동 테스트
4. **성능 테스트**: 대용량 데이터 처리 테스트
**📝 기존 Java 코드 분석 결과**
**AdminController 주요 메서드**
```java
// 사용자 목록 조회
@RequestMapping("/admin/userMngList.do")
public String userMngList(HttpServletRequest request, @RequestParam Map paramMap)
// 사용자 정보 저장
@RequestMapping("/admin/saveUserInfo.do")
public String saveUserInfo(HttpServletRequest request, @RequestParam Map paramMap)
// 사용자 ID 중복 체크
@RequestMapping("/admin/checkDuplicateUserId.do")
public String checkDuplicateUserId(HttpServletRequest request, @RequestParam Map paramMap)
// 사용자 상태 변경
@RequestMapping("/admin/changeUserStatus.do")
public String changeUserStatus(HttpServletRequest request, @RequestParam Map paramMap)
```
**AdminService 주요 메서드**
```java
// 사용자 목록 조회
public List<Map<String, Object>> getEtcUserList(HttpServletRequest request, Map<String, Object> paramMap)
// 사용자 정보 저장
public Map<String, Object> saveEtcUserInfo(HttpServletRequest request, Map<String, Object> paramMap)
// 사용자 ID 중복 체크
public Map<String, Object> checkDuplicateEtcUserId(Map<String, Object> paramMap)
// 사용자 상태 변경
public Map<String, Object> changeUserStatus(Map<String, Object> paramMap)
```
**📋 다음 단계**
이 계획에 따라 **Phase 2-2A**를 시작하여 단계적으로 사용자 관리 기능을 구현하겠습니다.
**시작 지점**: 사용자 목록 조회 API부터 실제 데이터베이스 연동으로 구현
## 🗄️ 테이블 타입관리 백엔드 Node.js 리팩토링 계획
### 📋 테이블 타입관리 기능 Node.js 리팩토링 개요
**목표**: 기존 Java Spring Boot의 테이블 타입관리 기능을 Node.js + TypeScript로 완전 리팩토링
**기존 Java 백엔드 (`@backend/`) 분석**
- Spring Framework 기반의 `TableManagementController``TableManagementService`
- MyBatis를 사용한 PostgreSQL 메타데이터 조회
- `table_labels`, `column_labels` 테이블을 통한 테이블/컬럼 설정 관리
- `information_schema` 활용한 데이터베이스 구조 자동 조회
**현재 Node.js 백엔드 (`@backend-node/`) 상황**
- 테이블 타입관리 기능 미구현
- PostgreSQL 메타데이터 조회 기능 부재
- 컬럼 설정 관리 기능 부재
**🎯 리팩토링 목표**
1. **기존 Java 백엔드의 테이블 타입관리 기능을 Node.js로 완전 이전**
2. **PostgreSQL `information_schema` 활용한 메타데이터 조회**
3. **`table_labels`, `column_labels` 테이블을 통한 설정 관리**
4. **기존 API 응답 형식과 호환성 유지**
**🛠️ 단계별 구현 계획**
**Phase 2-5-1: 데이터베이스 스키마 및 모델 정리 (1일)**
- [ ] Prisma 스키마에 `table_labels`, `column_labels` 테이블 정의
- [ ] PostgreSQL 메타데이터 조회를 위한 권한 설정 확인
- [ ] 데이터 타입 및 관계 정의
**Phase 2-5-2: 핵심 테이블 타입관리 API 구현 (3일)**
**테이블 메타데이터 API**
```typescript
// 기존 Java TableManagementController의 핵심 메서드들
- GET /api/table-management/tables - (information_schema )
- GET /api/table-management/tables/:tableName/columns -
- POST /api/table-management/tables/:tableName/columns/:columnName/settings -
- POST /api/table-management/tables/:tableName/columns/settings -
```
**PostgreSQL 메타데이터 조회**
```typescript
// information_schema를 활용한 테이블 목록 조회
const getTableList = async (): Promise<TableInfo[]> => {
const query = `
SELECT
t.table_name as "tableName",
COALESCE(tl.table_label, t.table_name) as "displayName",
COALESCE(tl.description, '') as "description",
(SELECT COUNT(*) FROM information_schema.columns
WHERE table_name = t.table_name AND table_schema = 'public') as "columnCount"
FROM information_schema.tables t
LEFT JOIN table_labels tl ON t.table_name = tl.table_name
WHERE t.table_schema = 'public'
AND t.table_type = 'BASE TABLE'
AND t.table_name NOT LIKE 'pg_%'
AND t.table_name NOT LIKE 'sql_%'
ORDER BY t.table_name
`;
const result = await client.query(query);
return result.rows;
};
```
**Phase 2-5-3: 서비스 레이어 구현 (2일)**
**TableManagementService 확장**
```typescript
// 기존 Java TableManagementService의 핵심 메서드들
- getTableList() - (information_schema )
- getColumnList(tableName) -
- updateColumnSettings() -
- updateAllColumnSettings() -
```
**데이터베이스 연동**
```typescript
- PostgreSQL
-
-
```
**Phase 2-5-4: 컬럼 설정 관리 기능 (1일)**
**컬럼 설정 데이터 구조**
```typescript
interface ColumnSettings {
columnLabel: string; // 컬럼 표시명
webType: string; // 웹 입력 타입 (text, number, date, code, entity)
detailSettings: string; // 상세 설정
codeCategory: string; // 코드 카테고리
codeValue: string; // 코드 값
referenceTable: string; // 참조 테이블
referenceColumn: string; // 참조 컬럼
}
```
**UPSERT 방식으로 컬럼 설정 저장**
```typescript
const updateColumnSettings = async (
tableName: string,
columnName: string,
settings: ColumnSettings
): Promise<void> => {
const query = `
INSERT INTO column_labels (
table_name, column_name, column_label, web_type,
detail_settings, code_category, code_value,
reference_table, reference_column
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT (table_name, column_name) DO UPDATE SET
column_label = EXCLUDED.column_label,
web_type = EXCLUDED.web_type,
detail_settings = EXCLUDED.detail_settings,
code_category = EXCLUDED.code_category,
code_value = EXCLUDED.code_value,
reference_table = EXCLUDED.reference_table,
reference_column = EXCLUDED.reference_column,
updated_date = now()
`;
await client.query(query, [
tableName,
columnName,
settings.columnLabel,
settings.webType,
settings.detailSettings,
settings.codeCategory,
settings.codeValue,
settings.referenceTable,
settings.referenceColumn,
]);
};
```
**🔄 구현 우선순위**
**High Priority (1-2일차)**
1. 테이블 목록 조회 API (information_schema 활용)
2. 컬럼 정보 조회 API
3. 기본적인 에러 핸들링
**Medium Priority (3-4일차)**
1. 개별 컬럼 설정 업데이트 API
2. 전체 컬럼 설정 일괄 업데이트 API
3. 테이블/컬럼 라벨 자동 생성 기능
**Low Priority (5일차)**
1. 컬럼 설정 검증 로직
2. 메타데이터 캐싱 기능
3. 고급 검색 및 필터링
**🔧 기술적 고려사항**
**PostgreSQL 메타데이터 조회**
- `information_schema` 접근 권한 확인
- 시스템 테이블 제외 로직 (`pg_*`, `sql_*` 테이블 제외)
- 성능 최적화를 위한 인덱스 활용
**데이터베이스 연동**
- Prisma ORM 대신 PostgreSQL 클라이언트 직접 사용
- 메타데이터 조회를 위한 특수 쿼리 처리
- 트랜잭션 관리 및 롤백 처리
**API 호환성**
- 기존 Java 백엔드와 동일한 응답 형식 유지
- 프론트엔드 변경 최소화
- 점진적 마이그레이션 지원
**📊 테스트 계획**
1. **단위 테스트**: 각 서비스 메서드별 테스트
2. **통합 테스트**: API 엔드포인트별 테스트
3. **데이터베이스 테스트**: 실제 PostgreSQL 메타데이터 조회 테스트
4. **성능 테스트**: 대용량 테이블/컬럼 조회 테스트
**📝 기존 Java 코드 분석 결과**
**TableManagementController 주요 메서드**
```java
// 테이블 목록 조회
@GetMapping("/tables")
public ResponseEntity<Map<String, Object>> getTableList(HttpServletRequest request)
// 컬럼 정보 조회
@GetMapping("/tables/{tableName}/columns")
public ResponseEntity<Map<String, Object>> getColumnList(
HttpServletRequest request, @PathVariable String tableName)
// 컬럼 설정 업데이트
@PostMapping("/tables/{tableName}/columns/{columnName}/settings")
public ResponseEntity<Map<String, Object>> updateColumnSettings(
HttpServletRequest request, @PathVariable String tableName,
@PathVariable String columnName, @RequestBody Map<String, Object> settings)
// 전체 컬럼 설정 일괄 업데이트
@PostMapping("/tables/{tableName}/columns/settings")
public ResponseEntity<Map<String, Object>> updateAllColumnSettings(
HttpServletRequest request, @PathVariable String tableName,
@RequestBody List<Map<String, Object>> columnSettings)
```
**TableManagementService 주요 메서드**
```java
// 테이블 목록 조회
public List<Map<String, Object>> getTableList()
// 컬럼 정보 조회
public List<Map<String, Object>> getColumnList(String tableName)
// 컬럼 설정 업데이트
public void updateColumnSettings(String tableName, String columnName, Map<String, Object> settings)
// 전체 컬럼 설정 일괄 업데이트
public void updateAllColumnSettings(String tableName, List<Map<String, Object>> columnSettings)
```
**MyBatis Mapper 주요 쿼리**
```xml
<!-- 테이블 목록 조회 -->
<select id="selectTableList" resultType="map">
SELECT
t.table_name as "tableName",
COALESCE(tl.table_label, t.table_name) as "displayName",
COALESCE(tl.description, '') as "description",
(SELECT COUNT(*) FROM information_schema.columns
WHERE table_name = t.table_name AND table_schema = 'public') as "columnCount"
FROM information_schema.tables t
LEFT JOIN table_labels tl ON t.table_name = tl.table_name
WHERE t.table_schema = 'public'
AND t.table_type = 'BASE TABLE'
AND t.table_name NOT LIKE 'pg_%'
AND t.table_name NOT LIKE 'sql_%'
ORDER BY t.table_name
</select>
<!-- 컬럼 정보 조회 -->
<select id="selectColumnList" parameterType="map" resultType="map">
SELECT
c.column_name as "columnName",
COALESCE(cl.column_label, c.column_name) as "displayName",
c.data_type as "dbType",
COALESCE(cl.web_type, 'text') as "webType",
COALESCE(cl.detail_settings, '') as "detailSettings",
COALESCE(cl.description, '') as "description",
c.is_nullable as "isNullable",
c.column_default as "defaultValue",
c.character_maximum_length as "maxLength",
c.numeric_precision as "numericPrecision",
c.numeric_scale as "numericScale",
cl.code_category as "codeCategory",
cl.code_value as "codeValue",
cl.reference_table as "referenceTable",
cl.reference_column as "referenceColumn"
FROM information_schema.columns c
LEFT JOIN column_labels cl ON c.table_name = cl.table_name AND c.column_name = cl.column_name
WHERE c.table_name = #{tableName}
ORDER BY c.ordinal_position
</select>
```
**📋 다음 단계**
이 계획에 따라 **Phase 2-5**를 시작하여 단계적으로 테이블 타입관리 기능을 구현하겠습니다.
**시작 지점**: 테이블 목록 조회 API부터 PostgreSQL 메타데이터 조회로 구현
#### **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 (`table_labels`, `column_labels` 테이블 기반)
- [ ] PostgreSQL 메타데이터 조회 API (`information_schema` 활용)
- [ ] 컬럼 설정 관리 API (웹 타입, 참조 테이블, 코드 카테고리 등)
#### **Phase 2-6: 다국어 및 공통 관리 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 설정으로 환경변수 로딩 문제 해결
14. **어드민 메뉴 인증**: 새 탭에서 열리는 어드민 페이지의 토큰 인증 문제 해결 - localStorage 공유 활용
15. **관리자 메뉴 내 페이지 이동 토큰 문제**: 레이아웃 레벨 토큰 확인 및 동기화 구현
16. **API 클라이언트 통일**: 모든 API에서 apiClient 사용으로 토큰 자동 전달 보장
17. **토큰 동기화 유틸리티**: localStorage와 sessionStorage 간 토큰 동기화 및 복원 기능
## 🔐 인증 및 보안 가이드
### 어드민 메뉴 토큰 인증 문제 해결
#### 문제 상황
- 어드민 버튼 클릭 시 새 탭에서 어드민 페이지가 열림
- 새 탭에서 토큰 인증 문제 발생 가능성
- URL 파라미터로 토큰 전달은 보안상 위험
#### 해결 방안 (권장)
**1. localStorage 공유 활용 (가장 간단)**
```typescript
// AdminButton.tsx - 수정 없음
const handleAdminClick = () => {
const adminUrl = `${window.location.origin}/admin`;
window.open(adminUrl, "_blank");
};
// admin/page.tsx - AuthGuard 적용
("use client");
import { AuthGuard } from "@/components/auth/AuthGuard";
import { CompanyManagement } from "@/components/admin/CompanyManagement";
export default function AdminPage() {
return (
<AuthGuard requireAdmin={true}>
<CompanyManagement />
</AuthGuard>
);
}
```
**2. BroadcastChannel API 활용 (고급)**
```typescript
// utils/tabCommunication.ts
export class TabCommunication {
private channel: BroadcastChannel;
constructor() {
this.channel = new BroadcastChannel("auth-channel");
}
// 토큰 요청
requestToken(): Promise<string | null> {
return new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve(localStorage.getItem("authToken"));
}, 100);
this.channel.postMessage({ type: "REQUEST_TOKEN" });
const handler = (event: MessageEvent) => {
if (event.data.type === "TOKEN_RESPONSE") {
clearTimeout(timeout);
this.channel.removeEventListener("message", handler);
resolve(event.data.token);
}
};
this.channel.addEventListener("message", handler);
});
}
}
```
**3. 쿠키 기반 토큰 (가장 안전)**
```typescript
// backend-node/src/controllers/authController.ts
static async login(req: Request, res: Response): Promise<void> {
// HTTPOnly 쿠키로 토큰 설정
res.cookie('authToken', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000, // 24시간
});
}
```
#### 보안 고려사항
1. **URL 파라미터 사용 금지**: 토큰이 URL에 노출되어 보안 위험
2. **HTTPS 필수**: 프로덕션 환경에서는 반드시 HTTPS 사용
3. **토큰 만료 처리**: 자동 갱신 또는 재로그인 유도
4. **CSRF 방지**: 토큰 기반 요청 검증
5. **로그아웃 처리**: 모든 탭에서 토큰 제거
#### 구현 우선순위
**1단계 (즉시 적용)**
- AuthGuard를 사용한 어드민 페이지 보호
- localStorage 공유 활용
**2단계 (1-2일 내)**
- 토큰 유효성 검증 API 추가
- 에러 처리 개선
**3단계 (3-5일 내)**
- 세션 관리 개선
- 토큰 갱신 로직 추가
### JWT 토큰 관리 모범 사례
#### 프론트엔드 토큰 관리
```typescript
// lib/api/client.ts
const TokenManager = {
getToken: (): string | null => {
if (typeof window !== "undefined") {
return localStorage.getItem("authToken");
}
return null;
},
isTokenExpired: (token: string): boolean => {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true;
}
},
};
```
#### 토큰 동기화 유틸리티
```typescript
// lib/sessionManager.ts
export const tokenSync = {
// 토큰 상태 확인
checkToken: () => {
const token = localStorage.getItem("authToken");
console.log("🔍 토큰 상태 확인:", token ? "존재" : "없음");
return !!token;
},
// 토큰 강제 동기화 (다른 탭에서 설정된 토큰을 현재 탭에 복사)
forceSync: () => {
const token = localStorage.getItem("authToken");
if (token) {
// sessionStorage에도 복사
sessionStorage.setItem("authToken", token);
console.log("🔄 토큰 강제 동기화 완료");
return true;
}
return false;
},
// 토큰 복원 시도 (sessionStorage에서 복원)
restoreFromSession: () => {
const sessionToken = sessionStorage.getItem("authToken");
if (sessionToken) {
localStorage.setItem("authToken", sessionToken);
console.log("🔄 sessionStorage에서 토큰 복원 완료");
return true;
}
return false;
},
// 토큰 유효성 검증
validateToken: (token: string) => {
if (!token) return false;
try {
// JWT 토큰 구조 확인 (header.payload.signature)
const parts = token.split(".");
if (parts.length !== 3) return false;
// payload 디코딩 시도
const payload = JSON.parse(atob(parts[1]));
const now = Math.floor(Date.now() / 1000);
// 만료 시간 확인
if (payload.exp && payload.exp < now) {
console.log("❌ 토큰 만료됨");
return false;
}
console.log("✅ 토큰 유효성 검증 통과");
return true;
} catch (error) {
console.log("❌ 토큰 유효성 검증 실패:", error);
return false;
}
},
};
```
#### 관리자 레이아웃 토큰 확인
```typescript
// app/(main)/admin/layout.tsx
export default function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const [isAuthorized, setIsAuthorized] = useState<boolean | null>(null);
// 토큰 확인 및 인증 상태 체크
useEffect(() => {
const checkToken = () => {
const token = localStorage.getItem("authToken");
// 토큰이 없으면 sessionStorage에서 복원 시도
if (!token && sessionToken) {
const restored = tokenSync.restoreFromSession();
if (restored) {
setIsAuthorized(true);
return;
}
}
// 토큰 유효성 검증
if (token && !tokenSync.validateToken(token)) {
localStorage.removeItem("authToken");
sessionStorage.removeItem("authToken");
setIsAuthorized(false);
return;
}
if (!token) {
setIsAuthorized(false);
return;
}
// 토큰이 있으면 인증된 것으로 간주
setIsAuthorized(true);
// 토큰 강제 동기화 (다른 탭과 동기화)
tokenSync.forceSync();
};
// 초기 토큰 확인
checkToken();
// localStorage 변경 이벤트 리스너 추가
const handleStorageChange = (e: StorageEvent) => {
if (e.key === "authToken") {
checkToken();
}
};
// 페이지 포커스 시 토큰 재확인
const handleFocus = () => {
checkToken();
};
window.addEventListener("storage", handleStorageChange);
window.addEventListener("focus", handleFocus);
return () => {
window.removeEventListener("storage", handleStorageChange);
window.removeEventListener("focus", handleFocus);
};
}, [pathname]);
}
```
#### API 클라이언트 통일
```typescript
// lib/api/user.ts - 수정 전 (fetch 사용)
async function apiCall<T = any>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: {
"Content-Type": "application/json",
...options.headers,
},
credentials: "include",
...options,
});
// 토큰 수동 추가 필요
}
// lib/api/user.ts - 수정 후 (apiClient 사용)
export async function getUserList(params?: Record<string, any>) {
try {
const response = await apiClient.get("/admin/users", {
params: params,
});
// 토큰 자동 추가됨
return response.data;
} catch (error) {
console.error("❌ 사용자 목록 API 오류:", error);
throw error;
}
}
```
#### 백엔드 토큰 검증
```typescript
// middleware/authMiddleware.ts
export const authenticateToken = (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): void => {
try {
const authHeader = req.get("Authorization");
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
res.status(401).json({
success: false,
error: {
code: "TOKEN_MISSING",
details: "인증 토큰이 필요합니다.",
},
});
return;
}
const userInfo: PersonBean = JwtUtils.verifyToken(token);
req.user = userInfo;
next();
} catch (error) {
res.status(401).json({
success: false,
error: {
code: "INVALID_TOKEN",
details: "토큰 검증에 실패했습니다.",
},
});
}
};
```
### 토큰 인증 문제 해결 완료 사항
#### ✅ 해결된 문제들
1. **어드민 메뉴 토큰 인증 문제**
- 새 탭에서 열리는 어드민 페이지의 토큰 공유 ✅
- localStorage 기반 토큰 동기화 ✅
2. **관리자 메뉴 내 페이지 이동 시 토큰 문제**
- 레이아웃 레벨에서 토큰 확인 로직 추가 ✅
- 실시간 토큰 동기화 및 검증 ✅
3. **사용자 관리 메뉴 특정 인증 문제**
- API 클라이언트 통일 (fetch → apiClient) ✅
- 토큰 자동 전달 활성화 ✅
#### 🔧 구현된 기능들
- **토큰 동기화 유틸리티**: `tokenSync` 모듈
- **강화된 인증 체크**: 레이아웃 레벨 토큰 검증
- **API 클라이언트 통일**: 모든 API에서 토큰 자동 전달
- **디버깅 도구**: 상세한 토큰 상태 확인 및 API 테스트
#### 📝 테스트 방법
1. **Admin 버튼 클릭** → 어드민 페이지 열기
2. **사이드바 메뉴 클릭** → 다른 관리자 페이지로 이동
3. **디버깅 페이지 확인**`/admin/debug-layout`에서 토큰 상태 확인
4. **API 테스트** → 각 메뉴에서 API 호출 정상 작동 확인
## 🎯 성공 지표
1. **성능 개선**: API 응답 시간 30% 단축
2. **개발 생산성**: 새로운 기능 개발 시간 50% 단축
3. **유지보수성**: 코드 복잡도 감소
4. **확장성**: 마이크로서비스 아키텍처 준비
---
**마지막 업데이트**: 2024년 12월 20일
**버전**: 2.0.0
**작성자**: AI Assistant
**현재 상태**: Phase 1 완료, Phase 2-1A 완료, Phase 2-1B 완료, Phase 2-2A 완료 ✅ (메뉴 API 구현 완료, 어드민 메뉴 인증 문제 해결, 토큰 인증 문제 완전 해결), Phase 2-5 계획 수립 ✅ (테이블 타입관리 백엔드 Node.js 리팩토링 계획 완성)