issue # 패키지 초기 설정
This commit is contained in:
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
@baekyangdan:registry=https://gitea.bkdhome.p-e.kr/api/packages/baekyangdan/npm/
|
||||
//gitea.bkdhome.p-e.kr/api/packages/baekyangdan/npm/:_authToken=d39c7d88c52806df7522ce2b340b6577c5ec5082
|
||||
always-auth=true
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
}
|
||||
1375
package-lock.json
generated
Normal file
1375
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@baekyangdan/core-utils",
|
||||
"version": "1.0.1",
|
||||
"description": "Common Repo",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsup src/index.ts --dts --format cjs,esm"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitea.bkdhome.p-e.kr/baekyangdan/common-typescript-repo.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://gitea.bkdhome.p-e.kr/api/packages/baekyangdan/npm/"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"files": ["dist"],
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"tsup": "^8.5.1"
|
||||
}
|
||||
}
|
||||
6
src/common/code/errorCode.ts
Normal file
6
src/common/code/errorCode.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum ErrorCode {
|
||||
MIN_LENGTH_ERR='Min Length Under',
|
||||
MAX_LENGTH_ERR='Max Length Over',
|
||||
INVALID_TYPE='Invalid Type',
|
||||
INVALID_FORMAT='Invalid Format'
|
||||
}
|
||||
98
src/common/converter/Converter.ts
Normal file
98
src/common/converter/Converter.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { format, isValid, parse } from 'date-fns';
|
||||
import { TimeFormat } from '../format/DateTimeFormat';
|
||||
import { ErrorCode } from '../code/errorCode';
|
||||
|
||||
export class Converter {
|
||||
/**
|
||||
* number.toString().padStart 기능
|
||||
* ***
|
||||
* @param {number} number - 문자열로 바꾸려는 숫자
|
||||
* @param {number} minLength - 최소 길이
|
||||
* @param {string} padString - 채우려는 문자
|
||||
* ***
|
||||
* @returns {string} 변환 후 문자
|
||||
*/
|
||||
numberToPadString(number: number, minLength: number, padString: string): string {
|
||||
return number.toString().padStart(minLength, padString);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} numberString - 숫자로만 이루어진 문자열 혹은 하나의 분할 문자(ex: ':')와 숫자로만 이루어진 문자열
|
||||
* @param split - 분할 문자 default `''`
|
||||
* @returns 변환 후 숫자 배열
|
||||
*/
|
||||
numberStringToArray(numberString: string, split: string = ''): Array<number> {
|
||||
if (numberString.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const numberArray = numberString.split(split);
|
||||
const result: Array<number> = [];
|
||||
numberArray.forEach(number => {
|
||||
const parsedNumber = Number(number);
|
||||
if (!isNaN(parsedNumber)) {
|
||||
throw new Error(ErrorCode.INVALID_TYPE);
|
||||
}
|
||||
result.push(parsedNumber);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date 객체 포맷팅 코드 (시간은 무시)
|
||||
* ***
|
||||
* @param {Date} date - 변환하려는 Date 객체
|
||||
* @param {string} formatter - 변환하려는 포맷
|
||||
* @param {boolean} toUTC9 - UTC+9 기준 여부 (클라이언트에선 필요)
|
||||
* default `false`
|
||||
* ***
|
||||
* @returns 변환 후 문자열
|
||||
*/
|
||||
dateToFormattedString(date: Date, formatter: string, toUTC9: boolean = false): string {
|
||||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
if (toUTC9) {
|
||||
targetDate.setHours(9, 0, 0);
|
||||
}
|
||||
return format(targetDate, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param { Date | string } time - 변환하려는 시간
|
||||
* @param { TimeFormat } formatter - 변환하려는 포맷
|
||||
* @ex `TimeFormat['KOREAN_FULL']` -> `오전 10시 58분 32초`
|
||||
* @param { boolean } toUTC9 - UTC+9 기준 여부 (클라이언트에선 필요)
|
||||
* default `false`
|
||||
* @returns 변환 후 문자열
|
||||
*/
|
||||
timeToFormattedString(time: string | Date, formatter: TimeFormat, toUTC9: boolean = false): string {
|
||||
if (time instanceof Date) {
|
||||
const targetTime = new Date(time);
|
||||
if (toUTC9) {
|
||||
targetTime.setHours(targetTime.getHours() + 9, targetTime.getMinutes(), targetTime.getSeconds());
|
||||
}
|
||||
return format(targetTime, formatter);
|
||||
}
|
||||
|
||||
if (formatter === TimeFormat['KOREAN_SIMPLE'] || formatter === TimeFormat['KOREAN_FULL']) { // ISO to KOREAN
|
||||
const date = parse(time, TimeFormat['ISO_FULL'], new Date());
|
||||
if (!isValid(date)) {
|
||||
throw new Error(ErrorCode.INVALID_FORMAT);
|
||||
}
|
||||
const [hour, minute, second] = this.numberStringToArray(time, ':');
|
||||
date.setHours(toUTC9 ? hour! + 9 : hour!, minute!, formatter === TimeFormat['KOREAN_FULL'] ? second :0);
|
||||
|
||||
return format(date, formatter);
|
||||
} else { // KOREAN to ISO
|
||||
const fullDate = parse(time, TimeFormat['KOREAN_FULL'], new Date());
|
||||
const simpleDate = parse(time, TimeFormat['KOREAN_SIMPLE'], new Date());
|
||||
if (!isValid(fullDate) && !isValid(simpleDate)) {
|
||||
throw new Error(ErrorCode.INVALID_FORMAT);
|
||||
}
|
||||
const date = formatter === TimeFormat['ISO_FULL'] ? fullDate : simpleDate;
|
||||
|
||||
return format(date, formatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/common/format/DateTimeFormat.ts
Normal file
78
src/common/format/DateTimeFormat.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 포맷팅 코드 패턴
|
||||
*
|
||||
* | 패턴 | 의미 | 예시 |
|
||||
* | :--- | :--- | :--- |
|
||||
* | 연도 |
|
||||
* | **`yyyy`** | 연도 전체(4자리) | 2025 |
|
||||
* | **`yy`** | 연도 마지막 두자리 | 25 |
|
||||
* | | | |
|
||||
* | 월 |
|
||||
* | **`M`** | 월 (선행 0 없음) | 1, 12 |
|
||||
* | **`MM`** | 월 (선행 0 있음) | 01, 12 |
|
||||
* | **`MMM`** | 월 약어 | Jan, Dec |
|
||||
* | **`MMMM`** | 월 전체 이름 | January, December |
|
||||
* | 일 |
|
||||
* | **`d`** | 월 중 일 (선행 0 없음) | 1, 31 |
|
||||
* | **`dd`** | 월 중 일 (선행 0 있음) | 01, 31 |
|
||||
* | **`do`** | 일자 + 서수 접미사 | 12th, 1st |
|
||||
* | 요일 |
|
||||
* | **`E`/`EEE`** | 요일 약어 | Fri, 금 |
|
||||
* | **`EEEE`** | 요일 전체 이름 | Friday, 금요일 |
|
||||
*/
|
||||
export enum DateFormat {
|
||||
/**
|
||||
* @example '20251212'
|
||||
*/
|
||||
COMPACT='yyyyMMdd',
|
||||
/**
|
||||
* @example '2025-12-12'
|
||||
*/
|
||||
ISO_DATE = 'yyyy-MM-dd',
|
||||
/**
|
||||
* @example 'Fri, December 12th, 2025'
|
||||
*/
|
||||
ARIA='EEE, MMMM do, yyyy',
|
||||
/**
|
||||
* @example '2025년 12월 12일'
|
||||
*/
|
||||
KOREAN='yyyy년 MM월 dd일',
|
||||
}
|
||||
|
||||
/**
|
||||
* 포맷팅 코드 패턴
|
||||
*
|
||||
* | 패턴 | 의미 | 예시 |
|
||||
* | :--- | :--- | :--- |
|
||||
* | 시간 (24시간) |
|
||||
* | **`H`** | 선행 0 없음 | 9, 15 |
|
||||
* | **`HH`** | 선행 0 있음 | 09, 15 |
|
||||
* | 시간 (12시간) |
|
||||
* | **`h`** | 선행 0 없음 | 9, 3 |
|
||||
* | **`hh`** | 선행 0 있음 | 09, 03 |
|
||||
* | **`a`** | 오전/오후 표시 | AM, PM |
|
||||
* | 분 |
|
||||
* | **`m`** | 선행 0 없음 | 7, 30 |
|
||||
* | **`mm`** | 선행 0 있음 | 07, 30 |
|
||||
* | 초 |
|
||||
* | **`s`** | 선행 0 없음 | 5, 59 |
|
||||
* | **`ss`** | 선행 0 있음 | 05, 59 |
|
||||
*/
|
||||
export enum TimeFormat {
|
||||
/**
|
||||
* @example '오전 10시 32분'
|
||||
*/
|
||||
KOREAN_SIMPLE='a hh시 mm분',
|
||||
/**
|
||||
* @example '오전 10시 32분 25초'
|
||||
*/
|
||||
KOREAN_FULL='a hh시 mm분 ss초',
|
||||
/**
|
||||
* @example '10:32:00'
|
||||
*/
|
||||
ISO_SIMPLE='HH:mm:00',
|
||||
/**
|
||||
* @example '10:32:25
|
||||
*/
|
||||
ISO_FULL='HH:mm:ss'
|
||||
}
|
||||
73
src/common/generator/Generator.ts
Normal file
73
src/common/generator/Generator.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ErrorCode } from "../code/errorCode";
|
||||
|
||||
export class Generator {
|
||||
/**
|
||||
* 랜덤한 숫자 코드 생성
|
||||
* ***
|
||||
* @param {number} length - 생성할 코드 길이
|
||||
* @returns {string} 생성한 코드
|
||||
*/
|
||||
public generateRandomNumberCode(length: number): string {
|
||||
if (length < 1) throw new Error(ErrorCode.MIN_LENGTH_ERR);
|
||||
const randomNumber = Math.random();
|
||||
const randomCode = randomNumber.toString();
|
||||
|
||||
return randomCode.slice(2, length + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 랜덤한 코드 생성
|
||||
* ***
|
||||
* 규칙 1. 영소문자(a_z), 숫자(0_9), 특수문자(!@#$%^) 를 반드시 한 글자씩 포함
|
||||
*
|
||||
* 규칙 2. 최소 길이는 4
|
||||
*
|
||||
* 규칙 3. 무조건 영소문자(a_z) 로 시작함.
|
||||
* ***
|
||||
* @param {number} length - 생성할 코드 길이
|
||||
* @returns {string} 생성한 코드
|
||||
*/
|
||||
public generateRandomCode(length: number): string {
|
||||
if (length < 3) throw new Error(ErrorCode.MIN_LENGTH_ERR);
|
||||
let result = '';
|
||||
const getRandomChar = (string: string): string => {
|
||||
if (!string || string.length === 0) return '';
|
||||
const idx = Math.floor(Math.random() * string.length);
|
||||
return string[idx]!;
|
||||
}
|
||||
const shuffle = (string: string): string => {
|
||||
if (string.length <= 1) return string;
|
||||
let currentIndex = string.length, randomIndex;
|
||||
let result = string.split('');
|
||||
while (currentIndex !== 0) {
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
[result[currentIndex], result[randomIndex]] = [result[randomIndex]!, result[currentIndex]!];
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
const alphabets = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const numbers = '0123456789';
|
||||
const specials = '!@#$%^';
|
||||
const all = alphabets + numbers + specials;
|
||||
|
||||
const firstText = getRandomChar(alphabets);
|
||||
const requiredNumber = getRandomChar(numbers);
|
||||
const requiredSpecial = getRandomChar(specials);
|
||||
|
||||
const restTextList = [requiredNumber, requiredSpecial];
|
||||
|
||||
for (let idx = 3; idx < length; idx++) {
|
||||
restTextList.push(getRandomChar(all));
|
||||
}
|
||||
|
||||
let restText = restTextList.join('');
|
||||
restText = shuffle(restText);
|
||||
|
||||
result = firstText + restText;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
10
src/index.ts
Normal file
10
src/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// 공용 에러 코드
|
||||
export * from './common/code/errorCode';
|
||||
|
||||
export * from './common/converter/Converter';
|
||||
|
||||
export * from './common/generator/Generator';
|
||||
|
||||
export * from './common/format/DateTimeFormat';
|
||||
|
||||
export * from './scheduler/http/HttpResponseMessage';
|
||||
14
src/scheduler/http/HttpResponseMessage.ts
Normal file
14
src/scheduler/http/HttpResponseMessage.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export enum BadRequestMessage {
|
||||
BAD_REQUEST='Bad Request',
|
||||
INVALID_FORMAT='Invalid Format'
|
||||
};
|
||||
|
||||
export enum UnauthorizedMessage {
|
||||
INVALID_TOKEN='Invalid Token',
|
||||
ACCESS_TOKEN_EXPIRED='Access Token Expired',
|
||||
REFRESH_TOKEN_EXPIRED='Refresh Token Expired'
|
||||
};
|
||||
|
||||
export enum InternalServerErrorMessage {
|
||||
INTERNAL_SERVER_ERROR='Internal Server Error'
|
||||
};
|
||||
52
tsconfig.json
Normal file
52
tsconfig.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
// "rootDir": "./src",
|
||||
// "outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "esnext",
|
||||
"types": [],
|
||||
"rootDir": "./src",
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
// "types": ["node"],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationDir": "./dist",
|
||||
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node-modules", "dist", "**/*.spec.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user