issue # 패키지 초기 설정

This commit is contained in:
geonhee-min
2025-12-12 14:50:44 +09:00
parent ea1c23f566
commit f03f4edc4d
11 changed files with 1744 additions and 0 deletions

3
.npmrc Normal file
View 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
View File

@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib"
}

1375
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View 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"
}
}

View 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'
}

View 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);
}
}
}

View 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'
}

View 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
View 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';

View 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
View 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"]
}