29 KiB
29 KiB
Java Spring Boot → Node.js + TypeScript 리팩토링 가이드라인
📋 프로젝트 개요
목표
- 기존 Java Spring Boot 백엔드를 Node.js + TypeScript로 완전 리팩토링
- React 프론트엔드와의 완벽한 통합
- 타입 안전성과 개발 생산성 향상
기술 스택
{
"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. 의존성 주입 패턴
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 규칙
// ✅ 권장
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. 클래스 및 함수 정의
// ✅ 권장
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. 에러 처리
export class ServiceError extends Error {
constructor(
message: string,
public originalError?: Error,
public statusCode: number = 500
) {
super(message);
this.name = "ServiceError";
}
}
🔐 인증 시스템 마이그레이션 가이드라인
1. 기존 인증 시스템 분석
현재 Java Spring Boot 인증 구조
// 기존 API 엔드포인트
POST /api/auth/login // 로그인
POST /api/auth/logout // 로그아웃
GET /api/auth/me // 현재 사용자 정보
GET /api/auth/status // 인증 상태 확인
// 기존 핵심 클래스
- ApiLoginController: REST API 기반 로그인 컨트롤러
- LoginService: 로그인 비즈니스 로직
- PersonBean: 사용자 정보 객체
- EncryptUtil: 비밀번호 암호화 유틸리티
기존 인증 플로우
- 로그인 검증:
LoginService.loginPwdCheck()- 사용자 ID/비밀번호 검증 - 세션 관리:
SessionManager- HttpSession 기반 세션 관리 - 로그 기록:
insertLoginAccessLog()- LOGIN_ACCESS_LOG 테이블에 접속 로그 - 사용자 정보:
PersonBean- 세션에 저장되는 사용자 정보 객체
2. Node.js 마이그레이션 전략
기존 로직 유지 원칙
- ✅ 기존 비즈니스 로직 그대로 유지
- ✅ 기존 데이터베이스 구조 그대로 사용
- ✅ 기존 API 응답 형식 유지
- ✅ 기존 로그인 플로우 유지
- 🔄 세션 → JWT 토큰으로 변경 (기능은 동일)
변경 사항
// 기존: 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 타입 정의 (기존 구조 유지)
// 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 비밀번호 암호화 (기존 로직 포팅)
// 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 토큰 관리
// src/utils/jwtUtils.ts
export class JwtUtils {
static generateToken(userInfo: UserInfo): string {
// PersonBean 정보를 JWT 페이로드로 변환
}
static verifyToken(token: string): UserInfo {
// JWT 토큰 검증 및 사용자 정보 추출
}
}
3.4 인증 미들웨어
// 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)
// src/controllers/authController.ts
export class AuthController {
async login(req: Request, res: Response) {
// 기존 ApiLoginController.login() 로직을 그대로 포팅
// 1. 사용자 ID/비밀번호 검증
// 2. 기존 loginPwdCheck 로직 사용
// 3. 로그인 로그 기록
// 4. JWT 토큰 발급 (세션 대체)
}
}
3.6 인증 서비스
// 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: 핵심 인증 로직
- 비밀번호 암호화 유틸리티 (EncryptUtil 포팅)
- 로그인 검증 로직 (LoginService.loginPwdCheck 포팅)
- 로그인 API (ApiLoginController.login 포팅)
우선순위 2: 보조 기능
- 로그인 로그 기록 (insertLoginAccessLog 포팅)
- 사용자 정보 조회 (getCurrentUser 포팅)
- 로그아웃 API (logout 포팅)
6. 테스트 전략
기존 API 호환성 테스트
- 기존 Java API와 동일한 응답 형식 확인
- 기존 로그인 플로우 동작 확인
- 기존 로그 기록 기능 확인
보안 테스트
- JWT 토큰 유효성 검증
- 비밀번호 암호화 정확성 확인
- Rate Limiting 동작 확인
🗄️ 데이터베이스 설계 규칙
1. 기존 데이터베이스 스키마 참고
참고 문서: docs/Database_Schema_Collection.md
이 문서에는 기존 PostgreSQL 데이터베이스의 완전한 스키마 정보가 포함되어 있습니다:
- 전체 테이블 목록 (약 200개 테이블)
- 각 테이블의 상세 컬럼 구조
- 외래키 관계 정보
- 인덱스 및 제약조건 정보
- 시퀀스, 뷰, 함수 정보
2. 핵심 테이블 구조
user_info 테이블
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 테이블
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 테이블
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. 마이그레이션 관리
# 마이그레이션 생성
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 토큰 구조
interface JWTPayload {
userId: string;
userName: string;
email: string;
deptCode?: string;
permissions: string[];
iat: number;
exp: number;
}
2. 인증 미들웨어
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 설계
// ✅ 권장
GET /api/users // 사용자 목록 조회
GET /api/users/:id // 특정 사용자 조회
POST /api/users // 사용자 생성
PUT /api/users/:id // 사용자 전체 수정
PATCH /api/users/:id // 사용자 부분 수정
DELETE /api/users/:id // 사용자 삭제
2. 응답 형식 표준화
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
error?: {
code: string;
details?: any;
};
pagination?: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
3. 컨트롤러 구현
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. 테스트 구조
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. 환경별 설정
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. 로깅 규칙
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
{
"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
{
"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주) - 완료
- Node.js + TypeScript 프로젝트 설정
- 기존 데이터베이스 스키마 분석 (
docs/Database_Schema_Collection.md참고) - Prisma 스키마 설계 및 마이그레이션
- 기본 인증 시스템 구현
- 에러 처리 및 로깅 설정
- 서버 실행 및 포트 설정 (8080 포트)
🔄 Phase 2: 핵심 API 개발 (4-6주) - 진행 중
Phase 2-1: 인증 시스템 마이그레이션 (2주) - 우선 진행
Phase 2-1A: 기본 인증 구조 (1주) - ✅ 완료
- 기존 인증 타입 정의 (
LoginRequest,UserInfo,ApiResponse) - 비밀번호 암호화 유틸리티 (기존
EncryptUtil포팅) - JWT 토큰 관리 유틸리티
- 인증 미들웨어 구현
Phase 2-1B: 핵심 인증 API (1주) - ✅ 완료
- 로그인 API (
POST /api/auth/login) - 기존ApiLoginController.login()포팅 - 사용자 정보 API (
GET /api/auth/me) - 기존getCurrentUser()포팅 - 로그아웃 API (
POST /api/auth/logout) - 기존logout()포팅 - 인증 상태 확인 API (
GET /api/auth/status) - 기존checkAuthStatus()포팅 - 로그인 로그 기록 기능 (기존
insertLoginAccessLog()포팅) - 데이터베이스 스키마 동기화 (Prisma db pull)
- 실제 데이터베이스 연결 및 테스트
- 대소문자 처리 문제 해결
- 로그인 API 성공 테스트 완료
Phase 2-2: 사용자 관리 API (1주)
- 사용자 목록 조회 API (
user_info테이블 기반) - 사용자 상세 조회 API
- 사용자 생성/수정 API
- 사용자 비밀번호 변경 API
Phase 2-2A: 메뉴 관리 API (완료 ✅)
- 관리자 메뉴 조회 API (
GET /api/admin/menus) - 완료: 기존AdminController.getAdminMenuList()포팅 - 사용자 메뉴 조회 API (
GET /api/admin/user-menus) - 완료: 기존AdminController.getUserMenuList()포팅 - 메뉴 정보 조회 API (
GET /api/admin/menus/:menuId) - 완료: 기존AdminController.getMenuInfo()포팅 - JWT 토큰 인증 미들웨어 적용
- Prisma $queryRaw를 사용한 복잡한 재귀 쿼리 포팅
- 환경변수 설정 및 데이터베이스 연결 문제 해결
- 프론트엔드 API 클라이언트에 JWT 토큰 자동 추가
- 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 파이프라인 구축
- 모니터링 설정
- 문서화
⚠️ 주의사항
- 기존 데이터 보존: 마이그레이션 시 기존 데이터 손실 방지
- API 호환성: 프론트엔드와의 호환성 유지
- 보안: 인증/인가 로직 완전 재구현
- 성능: 데이터베이스 쿼리 최적화
- 테스트: 모든 기능에 대한 테스트 코드 작성
- 스키마 참고:
docs/Database_Schema_Collection.md문서를 항상 참고하여 정확한 테이블 구조 반영 - 데이터 타입 매핑: PostgreSQL → Prisma → TypeScript 타입 매핑 정확성 확인
- 관계 설정: 외래키 관계를 Prisma 관계로 정확히 매핑
- 히스토리 테이블:
*_history테이블들의 처리 방안 수립 - 임시 테이블:
temp*,*_temp테이블들의 정리 및 제거 계획 수립 - 메뉴 API 완료:
/api/admin/menus와/api/admin/user-menusAPI가 성공적으로 구현되어 프론트엔드 메뉴 표시가 정상 작동 - JWT 토큰 관리: 프론트엔드 API 클라이언트에서 JWT 토큰을 자동으로 포함하여 인증 문제 해결
- 환경변수 관리: Prisma 스키마에서 직접 데이터베이스 URL 설정으로 환경변수 로딩 문제 해결
- 어드민 메뉴 인증: 새 탭에서 열리는 어드민 페이지의 토큰 인증 문제 해결 - localStorage 공유 활용
🔐 인증 및 보안 가이드
어드민 메뉴 토큰 인증 문제 해결
문제 상황
- 어드민 버튼 클릭 시 새 탭에서 어드민 페이지가 열림
- 새 탭에서 토큰 인증 문제 발생 가능성
- URL 파라미터로 토큰 전달은 보안상 위험
해결 방안 (권장)
1. localStorage 공유 활용 (가장 간단)
// 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 활용 (고급)
// 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. 쿠키 기반 토큰 (가장 안전)
// 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시간
});
}
보안 고려사항
- URL 파라미터 사용 금지: 토큰이 URL에 노출되어 보안 위험
- HTTPS 필수: 프로덕션 환경에서는 반드시 HTTPS 사용
- 토큰 만료 처리: 자동 갱신 또는 재로그인 유도
- CSRF 방지: 토큰 기반 요청 검증
- 로그아웃 처리: 모든 탭에서 토큰 제거
구현 우선순위
1단계 (즉시 적용)
- AuthGuard를 사용한 어드민 페이지 보호
- localStorage 공유 활용
2단계 (1-2일 내)
- 토큰 유효성 검증 API 추가
- 에러 처리 개선
3단계 (3-5일 내)
- 세션 관리 개선
- 토큰 갱신 로직 추가
JWT 토큰 관리 모범 사례
프론트엔드 토큰 관리
// 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;
}
},
};
백엔드 토큰 검증
// 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: "토큰 검증에 실패했습니다.",
},
});
}
};
🎯 성공 지표
- 성능 개선: API 응답 시간 30% 단축
- 개발 생산성: 새로운 기능 개발 시간 50% 단축
- 유지보수성: 코드 복잡도 감소
- 확장성: 마이크로서비스 아키텍처 준비
마지막 업데이트: 2024년 12월 20일 버전: 1.8.0 작성자: AI Assistant 현재 상태: Phase 1 완료, Phase 2-1A 완료, Phase 2-1B 완료, Phase 2-2A 완료 ✅ (메뉴 API 구현 완료, 어드민 메뉴 인증 문제 해결)