Skip to Content
Packages@howmuchhome-web/utils-ts

@howmuchhome-web/utils-ts

프레임워크에 의존하지 않는 순수 TypeScript 유틸리티 모음입니다. 색상·날짜·숫자·문자열·객체 변환, 타입 유틸, AES 암호화 등을 제공합니다.

  • 의존성: date-fns, uuid, react(일부 파일에서 JSX 반환용). 별도 빌드 없이 소스(.ts/.tsx)를 직접 소비합니다(main: index.ts).
  • 사용처: @howmuchhome-web/api, @howmuchhome-web/ui, @howmuchhome-web/domains, @howmuchhome-web/funnel, @howmuchhome-web/editor, @howmuchhome-web/common-components, @howmuchhome-web/bridge-protocol, apps/app, apps/dashboard 등.
  • ⚠️ 전부 native/SSR-safe 는 아닙니다. 타입·순수 함수 위주지만 dom.ts/media.ts/device.ts(브라우저 전역)와 aes-encryption.ts(Node crypto) 같은 export 는 native·SSR 에서 실패합니다 → native 주의.
import {clamp, phoneFormatter, objectKeySnakeToCamel} from '@howmuchhome-web/utils-ts';

배럴(index.ts) 구조

index.ts는 다음 파일들을 re-export 합니다.

color · date · device · dom · function · media number · object · string · type · types · array · address · aes-encryption

주의: crypto.ts(NumberCodec)와 promise.ts(withMinimumDelay)는 배럴에 포함되어 있지 않습니다. 이 심볼들은 필요 시 파일 경로로 직접 import 해야 합니다.

// 배럴에 없는 심볼은 경로로 직접 import import {NumberCodec} from '@howmuchhome-web/utils-ts/crypto'; import {withMinimumDelay} from '@howmuchhome-web/utils-ts/promise';

유틸 인벤토리

색상 (color.ts)

심볼시그니처설명
hexToHSL(hex: string) => {h, s, l}HEX → HSL. 파싱 실패 시 {h:0, s:0, l:0} 반환.
hslToHex(h: number, s: number, l: number) => stringHSL → #rrggbb.
generateRandomColors(count: number) => string[]골든 앵글(137°) 기반으로 시각적으로 구분되는 색상 배열 생성.

날짜 (date.ts)

date-fns(format, isSameDay, isSameYear)와 date-fns/localeko를 사용합니다.

심볼시그니처설명
dateObjectToEngDate(date: Date) => stringMMM D, YYYY 형식(예: JAN 3, 2026).
getDateTimeFromDateObject(data: Date) => stringYYYY.MM.DD 오전/오후 H시 M분.
getDateFromDateObject(date: Date) => stringKST(+9h) 기준 YYYY.MM.DD.
getTimeFromDateObject(date: Date, second = false) => stringKST 기준 HH:mm(또는 HH:mm:ss).
getDDay(from: Date, to: Date) => number두 날짜 간 D-day(일수). to는 당일 23:59:59 기준.
getDDayFromNow(to: Date) => numbergetDDay(new Date(), to).
displayedDatetime(date: Date) => string오늘이면 HH:mm, 아니면 YY.MM.DD.
getDisplayedCreatedAt(date: Date) => string같은 날 HH:mm, 그 외 yyyy.MM.dd(date-fns).
displayTimeLapse(date: Date) => string당일이면 N시간/N분 전·방금 전, 그 외 YY.MM.DD.
isValidBirthDate(dateString: string) => boolean6자리/8자리 생년월일 유효성. 길이 검증은 별도 필요.
convertToLocaleDate(date: Date) => Date로컬 타임존 오프셋을 적용한 Date.

참고: 파일 내부에 export 되지 않은 getDifferenceStringFromNow(상대 시간 문자열) 헬퍼가 존재하지만 외부로 노출되지 않습니다.

숫자 (number.ts)

심볼시그니처설명
clamp(value, min, max) => number값을 [min, max]로 제한.
absClamp(value, max) => number절댓값을 max로 제한하되 부호 유지.
hashNumber(target: string) => number문자열 → 32bit 양수 해시.
findNearestNumber(from: number, to: number[]) => number배열에서 from에 가장 가까운 값.
formatPercentage(value: number) => string정수면 그대로, 소수면 소수점 2자리. NaN이면 '0'.

문자열 (string.tsx)

.tsx 확장자로, 일부 함수가 React 엘리먼트(ReactElement/Fragment)를 반환합니다. uuidv4를 사용합니다.

심볼시그니처설명
phoneFormatter(phone: string | null | undefined) => string010-1234-5678 형식으로 하이픈 삽입.
createRandomString(length: number) => string영숫자 랜덤 문자열.
matchDigits(str: string | number, digits: number, item = '0') => string자릿수 맞춰 앞을 패딩.
splitByKeyword(str: string, keyword: string) => string[]키워드 기준으로 조각·키워드를 번갈아 분할.
textReplacer(text, regex, callback) => ReactNode[]텍스트 특정 부분을 ReactNode로 래핑(하이라이트 등).
applyLineBreak(node) => ReactNode[]\n<br/>로 치환.
extractUrls(text?: string) => string[]텍스트에서 URL 추출.
getUUID4() => stringuuid v4 (재-export).
convertNumberToKoreanString(value: number) => string예: 1234567891억 2345만 6789.
getRootDomain(hostname: string) => string루트 도메인 추출(localhost·IP 예외 처리).
getFileSizeText(size: number) => string바이트 → 1.5MB 형식.
standardSortString(a, b) => number숫자 우선, 그 외 localeCompare 정렬 비교기.
isUUID4(str?: string) => booleanUUID v4 형식 검증.

정규식 상수: simpleUrlRegExp, phoneValidationRegExp, emailValidationRegExp, nameValidationRegExp(한글만), noSpecialCharacterRegExp(영숫자+한글).

문자열 정렬 보조 (array.ts)

심볼시그니처설명
compareMixedStringsAndNumbers(a: string, b: string) => number둘 다 숫자면 수치 비교, 아니면 localeCompare.

파일명은 array.ts지만 배열 유틸이 아니라 문자열/숫자 혼합 정렬 비교 함수 하나만 담고 있습니다. 일반적으로는 배럴에서 import 하세요.

타입 유틸 (types.ts)

reactDispatch를 참조합니다.

심볼설명
DateString, UUIDstring 별칭(의미 명시용).
MaybeAsync<T>T | Promise<T>.
KeysMatching<T, V>T에서 값 타입이 V인 키의 유니온.
DeepPartial<T>재귀적 Partial.
ArrayFromUnion<T>유니온 각 항목을 배열 유니온으로(string | numberstring[] | number[]).
OmitFromUnion<T, K>유니온 분배 Omit.
ElementOfArray<T>배열 요소 타입 추출.
UnionDispatch<T>유니온 분배 Dispatch.
NonNullableProperties<O> / NullableProperties<O>각 프로퍼티를 non-null / nullable로.

객체 (object.ts)

심볼시그니처설명
objectKeySnakeToCamel(obj: any) => any키를 snake_case → camelCase로 in-place 변환(중첩 포함).
objectKeyCamelToSnake<T>(obj: T) => TcamelCase → snake_case(불변, 새 객체 반환). two_way_info는 하위를 변환하지 않음(PRESERVE_CAMEL_CASE_KEYS).
excludeUndefinedFromObject(obj) => objundefined 값 프로퍼티 제거.
groupBy<T>(data: T[], key: string) => {[k]: T[]}지정 키로 그룹핑.
deepEqual<T>(obj1: T, obj2: T) => boolean얕지 않은 재귀 동등 비교.

objectKeySnakeToCamel은 API fetcher(packages/api/fetcher/fetch.ts)에서 서버 응답(snake_case)을 camelCase로 정규화하는 데 쓰입니다. E2E 목 핸들러에서도 요청 body 비교에 사용됩니다.

주소 (address.ts)

심볼시그니처설명
splitStreetAddress(fullAddress: string | null) => {address, detailAddress}도로명 주소와 상세주소 분리.

함수·비동기 (function.ts, promise.ts)

심볼파일시그니처설명
waitForTimeoutfunction.ts(ms: number) => Promise<void>setTimeout Promise 래퍼.
waitForFunctionfunction.ts(callback, options?, ...args) => Promise<void>콜백이 true 될 때까지 폴링(timeout 기본 3000ms, period 기본 100ms).
retryOnErrorfunction.ts(callback, tryLimit, delay = 1000) => Promise<T>실패 시 재시도.
assertUnreachablefunction.ts(x: never) => never완전성(exhaustiveness) 체크용.
withMinimumDelaypromise.ts(promise, delay) => Promise<T>최소 지속시간 보장(스켈레톤 깜빡임 방지 등). 배럴 미포함, 경로 import 필요.

브라우저 DOM·미디어·디바이스 (dom.ts, media.ts, device.ts)

브라우저 전용 API(document, window, Image, FileReader, navigator.vibrate)에 의존하므로 SSR/native 환경에서는 사용 불가합니다.

심볼파일설명
getElementPositiondom.ts요소의 top/bottom 오프셋.
placeCaretAtEnddom.tscontenteditable 캐럿을 끝으로.
locateCaret / locateCaretByXdom.ts캐럿 위치 지정(오프셋/X좌표 기준).
getTextFromChildNodesdom.ts자식 노드에서 줄바꿈 보존 텍스트 추출.
getImageSize / getVideoSize / getMediaSize / getMediaTypemedia.tsBlob의 크기·타입 추출.
vibratedevice.tsvibrate.weak() / .medium() / .strong() 진동(지원 시).

인코딩 (crypto.ts)

심볼시그니처설명
NumberCodecclass숫자 ↔ 짧은 코드 문자열 양방향 인코더.

NumberCodec은 이름과 달리 Node crypto 모듈을 사용하지 않습니다. 62진수 charset(0-9A-Za-z)과 XOR·비트 회전으로 32bit 정수를 난독화된 고정 길이 코드로 변환합니다(암호학적 보안 목적 아님).

import {NumberCodec} from '@howmuchhome-web/utils-ts/crypto'; const codec = new NumberCodec({secretKey: [/* number[] */], codeLength: 6, numRounds: 5}); const code = codec.encode(12345); // 고정 길이 문자열 const value = codec.decode(code); // 12345

AES 암호화 (aes-encryption.ts)

이 파일은 Node의 crypto 모듈(import crypto from 'crypto')을 사용합니다. 배럴(index.ts)에서 re-export 되므로, @howmuchhome-web/utils-ts를 import 하면 이 파일의 crypto 의존성이 함께 딸려옵니다. native 함정은 아래 native 주의를 참고하세요.

심볼시그니처설명
AES128classAES-128-CBC/PKCS5. Java 샘플과 호환되도록 구현. iv는 userId 기반 고정.
encryptAES128CBCSimple(text: string) => stringAES128 기본 인스턴스로 암호화(base64).
decryptAES128CBCSimple(encryptedText: string) => string위의 복호화.
encryptField(value: string) => stringencryptAES128CBCSimple 래퍼, 실패 시 ''.
createAES128(key: string, userId = 'hyphen') => AES128커스텀 키/userId로 인스턴스 생성.
encryptCertificateField(value: string) => string전자증명서 v2용 AES-256-CBC. 랜덤 IV + base64(IV‖암호문).

AES128 / encryptAES128CBCSimple은 키·userId를 환경변수(ENCRYPTION_KEY/NEXT_PUBLIC_ENCRYPTION_KEY, ENCRYPTION_USER_ID/NEXT_PUBLIC_ENCRYPTION_USER_ID)에서 읽으며 fallback 기본값을 가집니다.

encryptCertificateFieldNEXT_PUBLIC_CERTIFICATE_AES_KEY를 사용하며, 없으면 예외를 던집니다. 프론트에서 민감 데이터(주민번호 뒷자리 등)를 암호화해 서버로 보내는 증명서 발급 플로우에서 쓰입니다.

import {encryptCertificateField} from '@howmuchhome-web/utils-ts'; const rrn2Enc = encryptCertificateField(birthPassword); // base64(IV‖암호문)

native 주의

aes-encryption.ts가 Node crypto를 import 하고 이 파일이 배럴에서 re-export 되므로, native(Expo/Hermes) 번들에서는 crypto가 해석되지 않습니다. 이를 위해 metro가 crypto빈 모듈로 stub 합니다.

  • metro EMPTY_STUBScrypto가 등록되어 있어 Unable to resolve 'crypto' 없이 빌드는 통과합니다.
  • 그러나 stub 이므로 native 에서 AES 함수들을 실제로 호출하면 동작하지 않습니다. native 에서는 이 유틸들을 사용하지 마세요.
  • 증명서 발급의 경우, native 는 web encryptCertificateField와 동일 알고리즘(AES-256-CBC · 랜덤 IV · base64(IV‖암호문))을 순수 JS(node-forge)로 재구현한 별도 구현을 씁니다: apps/native-app/src/features/electronic-certificate/lib/encryptCertificateField.ts. 이 native 구현의 출력은 서버(Node crypto)와 양방향 호환됩니다.

자세한 stub·resolver 규칙은 native 앱 · Metro 와 의존성을 참고하세요.

또한 dom.ts / media.ts / device.ts는 브라우저 전용 전역(document, window, Image, FileReader, navigator.vibrate)에 의존하므로 native·SSR 환경에서 호출하면 실패합니다.

Last updated on