@hamjimin/xplat-back
v0.7.1
Published
Express + TypeScript 백엔드 스캐폴딩 도구 (zium-backend 기반)
Downloads
249
Maintainers
Readme
@hamjimin/xplat-back
Express + TypeScript 백엔드 프레임워크 & CLI 스캐폴딩 도구
커머스 백엔드에 필요한 인증, 스토리지, 결제, 배송, 쿼리 빌더 등의 모듈을 xplatSystem 싱글턴으로 통합 제공하며,
CLI를 통해 프로젝트 초기화와 MySQL DDL 자동 설치까지 지원합니다.
설치
npm install @hamjimin/xplat-back
# 또는
yarn add @hamjimin/xplat-back빠른 시작
1. 프로젝트 스캐폴딩
npx @hamjimin/xplat-back my-project자동 생성되는 파일:
my-project/
├── src/
│ ├── app.ts # Express 앱 진입점
│ ├── server.ts # 서버 실행 파일
│ ├── restapi/
│ │ └── health.ts # 예시 API 라우트
│ ├── const/
│ │ ├── api_code.ts # API 응답 코드 상수
│ │ └── constants.ts # 공통 상수
│ └── util/
│ └── dbconfig/
│ └── dbConnections.ts # DB 연결 설정 (TypeORM)
├── tsconfig.json
├── package.json
└── .env2. Express 앱 생성
import { createApp } from '@hamjimin/xplat-back';
import * as path from 'path';
const app = createApp({
cors: {
origin: ['http://localhost:3000'],
credentials: true,
},
router: {
directory: path.join(__dirname, 'restapi'),
basePath: '/restapi',
},
trustProxy: true,
});
app.listen(4000, () => {
console.log('Server running on port 4000');
});createApp 옵션:
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| cors.origin | string[] | ['*'] | CORS 허용 origin |
| cors.credentials | boolean | true | 자격 증명 허용 |
| json.limit | string | '1mb' | JSON body 크기 제한 |
| router.directory | string | 'src/restapi' | 라우트 파일 디렉토리 |
| router.basePath | string | '/restapi' | 라우터 base path |
| trustProxy | boolean | true | 프록시 신뢰 |
| middleware | Function[] | [] | 커스텀 미들웨어 |
3. 파일 기반 라우팅
디렉토리 구조가 API endpoint에 자동 매핑됩니다.
src/restapi/
├── health.ts → GET /restapi/health
├── users.ts → /restapi/users
└── users/
└── profile.ts → /restapi/users/profileimport { createRouter } from '@hamjimin/xplat-back';
const router = createRouter(path.join(__dirname, 'restapi'));
app.use('/restapi', router);CLI
npx @hamjimin/xplat-back [project-name] [options]
npx @hamjimin/xplat-back db:setup프로젝트 생성 옵션
| 옵션 | 단축 | 설명 | 기본값 |
|------|------|------|--------|
| --port <port> | -p | 서버 포트 | 4000 |
| --cors <origins> | -c | CORS origins (쉼표 구분) | http://localhost:3000 |
| --router-dir <dir> | -r | 라우터 디렉토리 | src/restapi |
| --router-path <path> | | 라우터 base path | /restapi |
npx @hamjimin/xplat-back my-project --port 3000 --cors "http://localhost:3000,http://localhost:5173"db:setup - MySQL DDL 자동 설치
인터랙티브 UI로 MySQL 접속 정보를 입력하고, 필요한 테이블을 선택하여 바로 생성합니다.
npx @hamjimin/xplat-back db:setup실행 흐름:
🗄️ xplat-back 데이터베이스 설정
? DB Host: localhost
? DB User: root
? DB Password: ****
? DB Name: my_shop
? DB Port: 3306
✅ MySQL 연결 성공!
? 설치할 DDL 모듈을 선택하세요 (스페이스바로 선택):
◉ 사용자 (User / Employee / Partner)
◉ 상품 (Category / Product / Variants / Brand / Side_Product)
◯ 패키지 (Product_Package / Detail / Variant)
◉ 주문 (Orders / Order_Items / Order_Manage)
...
📎 의존성으로 자동 추가된 모듈:
→ 사용자 (01_user.sql)
📋 실행 대상 DDL (3개 파일):
📦 01_user.sql - 사용자
📦 02_product.sql - 상품
📦 04_order.sql - 주문
? 위 3개 DDL 파일을 실행하시겠습니까? Yes
✅ 01_user.sql → user, employee, partner
✅ 02_product.sql → category, product, variants, brand, side_product
✅ 04_order.sql → orders, order_items, order_manage, order_manage_comment
🎉 데이터베이스 설정이 완료되었습니다!제공되는 DDL 모듈 (10개):
| # | 모듈 | 테이블 |
|---|------|--------|
| 1 | 사용자 | user, employee, partner |
| 2 | 상품 | category, product, variants, brand, side_product |
| 3 | 패키지 | product_package, product_package_detail, product_package_variant |
| 4 | 주문 | orders, order_items, order_manage, order_manage_comment |
| 5 | 결제 | payment, calcurate |
| 6 | 배송 | delivery, reservation |
| 7 | 장바구니 | cart, cart_items |
| 8 | 캠페인 | campaign, promotion_detail, event_detail |
| 9 | 리뷰 | product_review, review_images, package_product_review, package_review_images |
| 10 | 연결 테이블 | chain_category_detail, chain_product_detail, chain_variant_detail 외 5개 |
- FK 의존성이 자동 해결됩니다 (예: 주문 선택 시 사용자 자동 포함)
IF NOT EXISTS로 멱등성이 보장되어 중복 실행해도 안전합니다- 이미 존재하는 테이블은 체크박스에 상태가 표시됩니다
xplatSystem 싱글턴
모든 모듈은 xplatSystem 인스턴스를 통해 접근할 수 있습니다.
import { xplatSystem } from '@hamjimin/xplat-back';
xplatSystem.auth // 인증/JWT
xplatSystem.storage // S3 스토리지
xplatSystem.payment // 결제
xplatSystem.shipping // 배송
xplatSystem.orm // 쿼리 빌더
xplatSystem.middleware // 미들웨어
xplatSystem.commerce // NestJS 커머스 모듈모듈 상세
Auth - 인증/JWT
import { xplatSystem } from '@hamjimin/xplat-back';
// 토큰 생성
const token = xplatSystem.auth.generateToken(
{ id: 'user-1', role: 'admin' },
{ secret: process.env.JWT_SECRET!, expiresIn: '1h' }
);
// Access + Refresh 토큰 쌍 생성
const tokenPair = xplatSystem.auth.createTokenPair(
{ id: 'user-1' },
{ secret: process.env.JWT_SECRET!, expiresIn: '15m' }, // access
{ secret: process.env.JWT_SECRET!, expiresIn: '7d' } // refresh
);
// 인증 미들웨어
const authMiddleware = xplatSystem.auth.createMiddleware({
secret: process.env.JWT_SECRET!,
cookieName: 'accessToken',
onSuccess: (req, payload) => { req.user = payload; },
});
app.use('/api/protected', authMiddleware);토큰은 Cookie 또는 Authorization: Bearer 헤더에서 자동 추출됩니다.
Storage - S3 파일 관리
const storage = xplatSystem.storage;
// S3 설정 (환경변수 또는 직접 전달)
// S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY, AWS_REGION
// 업로드
await storage.upload({
key: 'uploads/2025/01/image.png',
body: fileBuffer,
bucket: 'my-bucket',
contentType: 'image/png',
});
// Presigned 다운로드 URL 생성
const url = await storage.getDownloadUrl({
key: 'uploads/2025/01/image.png',
bucket: 'my-bucket',
expiresInSec: 3600,
});
// 삭제
await storage.delete({ key: 'uploads/2025/01/image.png', bucket: 'my-bucket' });유틸 함수:
buildKey(prefix, fileName, date?)- 날짜 기반 S3 key 생성 (prefix/YYYY/MM/DD/fileName)extractKey(url)- S3 URL에서 key 추출getDatePrefix()-YYYY/MM/DD형태의 접두사
Payment - 결제
Toss Payments, KakaoPay를 플러그인 방식으로 지원합니다.
import { TossPaymentProvider, KakaoPayProvider } from '@hamjimin/xplat-back';
const payment = xplatSystem.payment;
// 결제 프로바이더 등록
payment.registerProvider(new TossPaymentProvider({
secretKey: process.env.TOSS_SECRET_KEY!,
testMode: true,
}), true); // true = 기본 프로바이더로 설정
payment.registerProvider(new KakaoPayProvider({
adminKey: process.env.KAKAO_ADMIN_KEY!,
cid: 'TC0ONETIME',
}));
// 결제 요청
const response = await payment.requestPayment({
orderId: 'order-123',
amount: 15000,
customerName: '홍길동',
});
// 결제 승인
const details = await payment.confirmPayment({
paymentKey: response.paymentKey,
orderId: 'order-123',
amount: 15000,
});
// 결제 취소
await payment.cancelPayment({
paymentKey: response.paymentKey,
cancelReason: '고객 요청',
});Shipping - 배송
CJ대한통운, 로젠, 편의점 택배를 플러그인 방식으로 지원합니다.
import { CJLogisticsProvider, LogenProvider } from '@hamjimin/xplat-back';
const shipping = xplatSystem.shipping;
shipping.registerProvider(new CJLogisticsProvider({
apiKey: process.env.CJ_API_KEY!,
customerCode: 'CUSTOMER-001',
}), true);
// 배송비 계산
const fee = await shipping.calculateFee({
destinationZip: '06234',
weight: 2.5,
});
// 운송장 생성
const label = await shipping.createLabel({
orderId: 'order-123',
toName: '홍길동',
toPhone: '010-1234-5678',
toAddress: '서울시 강남구 테헤란로 123',
toZip: '06234',
});
// 배송 조회
const tracking = await shipping.track(label.trackingNumber);ORM - 쿼리 빌더
SQL WHERE / ORDER BY 절을 구조화된 데이터로 생성합니다.
import { xplatSystem } from '@hamjimin/xplat-back';
const orm = xplatSystem.orm;
// WHERE 절 생성
const whereQuery = orm.createWhereQuery({
condition: 'AND',
searchData: [
{ column: 'name', operator: 'LIKE', value: '홍' },
{ column: 'status', operator: 'IN', value: ['active', 'pending'] },
{ column: 'created_at', operator: 'BETWEEN', value: ['2025-01-01', '2025-12-31'] },
],
});
// ORDER BY 절 생성
const sortQuery = orm.createSortQuery([
{ column: 'created_at', operator: 'DESC' },
{ column: 'name', operator: 'ASC' },
]);
// 기존 쿼리에 적용
let query = 'SELECT * FROM user WHERE 1=1';
query = orm.appendWhereClause(query, whereQuery);
query = orm.appendOrderClause(query, sortQuery);지원 연산자: =, !=, LIKE, IN, NOTIN, BETWEEN, ISNULL, ISNOTNULL
Middleware - 미들웨어
const mw = xplatSystem.middleware;
// 요청 로깅
app.use(mw.logger());
// 상세 로깅 (body 포함)
app.use(mw.detailedLogger());
// 필드 검증
app.post('/users', mw.requireFields(['email', 'password']));
// 커스텀 검증
app.post('/users', mw.validator({
body: {
email: (v) => typeof v === 'string' && v.includes('@') || 'Invalid email',
age: (v) => typeof v === 'number' && v > 0 || 'Age must be positive',
},
}));
// 에러 핸들러 + 404 (마지막에 등록)
app.use(mw.notFound());
app.use(mw.errorHandler());NestJS 커머스 모듈
Store/Admin API 분리 구조를 제공합니다.
import { Module } from '@nestjs/common';
import { CommerceModule } from '@hamjimin/xplat-back';
@Module({
imports: [CommerceModule],
})
export class AppModule {}Store API: /store/v1/*
Admin API: /admin/v1/*const { storePrefix, adminPrefix } = xplatSystem.commerce.getVersioning();
// storePrefix: /store/v1
// adminPrefix: /admin/v1환경변수
.env 파일에서 설정 가능한 환경변수:
# 앱
NODE_ENV=development
PORT=4000
CORS_ALLOW_ORIGINS=http://localhost:3000
# 데이터베이스
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_DATABASE=my_database
# AWS S3
AWS_REGION=ap-northeast-2
S3_BUCKET=my-bucket
S3_ACCESS_KEY=
S3_SECRET_KEY=
# JWT
JWT_SECRET=your-secret-keyTypeScript 설정
권장 tsconfig.json:
{
"compilerOptions": {
"target": "ES2021",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"baseUrl": "./src",
"paths": { "@/*": ["*"] }
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Export 목록
// 싱글턴
export { xplatSystem, XplatSystem } from '@hamjimin/xplat-back';
// 앱/라우터
export { createApp, createRouter } from '@hamjimin/xplat-back';
// 모듈
export {
AppModule, RouterModule, AuthModule,
UtilsModule, MiddlewareModule, ConstantsModule,
StorageModule, ORMModule, PaymentModule, ShippingModule,
} from '@hamjimin/xplat-back';
// 결제 프로바이더
export { TossPaymentProvider, KakaoPayProvider } from '@hamjimin/xplat-back';
// 배송 프로바이더
export { CJLogisticsProvider, LogenProvider, CvsProvider } from '@hamjimin/xplat-back';
// 타입
export type {
AppConfig, ExtendedRequest, ExtendedResponse, AuthenticatedRequest,
TokenPayload, TokenOptions, TokenPair,
DetailSearch, SortQueryData, SearchDataItem, ColumnMapper,
S3Config,
PaymentRequestParams, PaymentResponse, PaymentConfirmParams, PaymentDetails,
ShippingFeeParams, ShippingFee, CreateLabelParams, ShippingLabel, TrackingInfo,
} from '@hamjimin/xplat-back';라이센스
ISC
