npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xfilecom/core-sdk

v1.4.5

Published

Core SDK for NestJS microservices - 통합 개발 키트

Readme

@xfilecom/core-sdk

NestJS 마이크로서비스용 Core SDK — DB, 인증, 로깅, 공통 응답, 설정 로드, 해시 검증 등을 하나의 패키지로 제공합니다.

목차


설치

npm install @xfilecom/core-sdk

설정 파일 위치 (표준)

다른 서비스/팀과 동일한 방식으로 사용하려면 설정 파일 위치를 아래 규칙으로 통일합니다.

| 항목 | 규칙 | |------|------| | 파일 경로 | {프로젝트 루트}/application.yml 또는 application.yaml | | 프로젝트 루트 | 기본값 process.cwd(). 필요 시 loadApplicationYaml({ rootDir }) 로 지정 | | 파일명 우선순위 | application.ymlapplication.yaml (먼저 찾은 것 사용) | | 환경별 병합 | 같은 루트에 application-{env}.yml (예: application-development.yml) 있으면 base 위에 deep merge |

코드에서 루트만 맞추면 항상 같은 위치에서 설정을 읽습니다.

import { loadApplicationYaml, DEFAULT_CONFIG_FILENAMES } from '@xfilecom/core-sdk';

// 기본: process.cwd() 에서 application.yml / application.yaml 탐색
const config = await loadApplicationYaml();

// 루트 지정 (예: 패키지 루트)
const config = await loadApplicationYaml({ rootDir: path.join(__dirname, '..') });

// 환경별 설정 병합
const config = await loadApplicationYaml({ env: process.env.NODE_ENV });
  • 파일이 없으면 예외 없이 빈 객체 {} 반환.
  • 상수: DEFAULT_CONFIG_FILENAMES, ENV_CONFIG_FILENAME_PATTERN (index에서 export).

설정 소스 (.env vs YAML)

YAML을 쓸지, 환경 변수(.env)만 쓸지, 또는 둘 다 쓸지를 설정할 수 있습니다.

옵션 (configSource)

| 값 | 동작 | |----|------| | yaml | application.yml만 읽음 (기본값) | | env | YAML을 읽지 않고, process.env(DB_, JWT_, HASH_* 등)로 config 객체 생성 | | yamlWithEnvOverrides | YAML 로드 후, 같은 키가 환경 변수에 있으면 그 값으로 덮어씀 |

어디에 설정하나요?

1) 코드에서 지정 (권장)

loadApplicationYaml() 호출 시 옵션으로 넘깁니다.

import { loadApplicationYaml, type ConfigSource } from '@xfilecom/core-sdk';

// YAML만 사용 (기본)
const config = await loadApplicationYaml();

// .env(환경 변수)만 사용 — application.yml 없이 DB_*, JWT_* 등으로 config 구성
const config = await loadApplicationYaml({ configSource: 'env' });

// YAML 먼저 로드하고, 환경 변수로 덮어쓰기
const config = await loadApplicationYaml({ configSource: 'yamlWithEnvOverrides', env: process.env.NODE_ENV });

2) 환경 변수로 지정

코드는 그대로 두고, 실행 환경 또는 .env 파일에 다음 키로 소스를 정합니다.

| 설정 위치 | 예시 | |-----------|------| | .env 파일 | CONFIG_SOURCE=env 또는 CONFIG_SOURCE=yamlWithEnvOverrides | | 쉘/실행 시 | CONFIG_SOURCE=env node dist/main.js |

  • CONFIG_SOURCE 가 없으면 기본값 yaml 로 동작합니다.
  • 상수 이름: CONFIG_SOURCE_ENV_KEY ('CONFIG_SOURCE') — index에서 export.
# .env
CONFIG_SOURCE=env
DB_HOST=localhost
JWT_SECRET=my-secret

이렇게 하면 loadApplicationYaml() 호출 시 옵션에 configSource를 안 넘겨도, process.env.CONFIG_SOURCE를 읽어서 동일하게 동작합니다. (앱에서 dotenv 등으로 .env를 먼저 로드해야 함)

configSource: 'env' 일 때 매핑

환경 변수만 쓸 때 아래 키들이 config 객체로 매핑됩니다.

  • database: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME(또는 DB_DATABASE), DB_SSL, DB_CONNECTION_LIMIT
  • jwt: JWT_SECRET, JWT_EXPIRES_IN
  • hashVerification.default: HASH_VERIFICATION_SECRET
  • hashVerification.profiles.email: EMAIL_HASH_SECRET (없으면 HASH_VERIFICATION_SECRET)

사용 범위 요약

| 구분 | 내용 | |------|------| | 모듈 | CoreModule.forRoot(), forMicroservice(), forHttpApi(), forHybrid() | | 설정 | loadApplicationYaml() / loadApplicationYamlSync() — 표준 경로 application.yml 또는 configSource: 'env' 로 환경 변수만 사용 | | 설정 루트·로더 | resolveConfigRootDir() (모노레포 등 설정 디렉터리 탐색), createConfigLoader() (한 번 로드 + 캐시 + getFullConfig / getServiceConfig) | | Crypto | pbkdf2HashPassword / pbkdf2VerifyPassword (saltHex:hashHex), lookupHash / compareLookupHash (키 파생 옵션), deriveKeySha256 | | 해시/검증 | hashValue, compareValue, hashByKey, compareByKey, hashEmail, compareEmail, lookupHash, compareLookupHash (YAML hashVerification + keyDerivation 연동) | | DB | Drizzle ORM + MySQL, DatabaseQuery, ServiceHelpers (getDb, withTransaction, createPaginationResult 등) | | 컨트롤러 | ControllerHelpers (parsePagination, parseSort, parseFilters, success, error) | | 인증 | JwtAuthGuard, RolesGuard, AuthHelpers, @Public(), @User(), @Roles() | | 인터셉터 | Logging, ErrorHandling, ResponseTransform, DatabaseCheck | | 필터 | AllExceptionsFilter | | 기타 | ErrorUtils, LoggerHelpers, ConfigValidator, getLoggingConfigFromEnv |


CoreModule

마이크로서비스

import { Module } from '@nestjs/common';
import { CoreModule } from '@xfilecom/core-sdk';

@Module({
  imports: [CoreModule.forMicroservice()],
})
export class AppModule {}

HTTP API

import { Module } from '@nestjs/common';
import { CoreModule } from '@xfilecom/core-sdk';
import { schema } from './database/schema';

@Module({
  imports: [
    CoreModule.forHttpApi({
      database: { schema },
    }),
  ],
})
export class AppModule {}

하이브리드 (HTTP + TCP)

import { Module } from '@nestjs/common';
import { CoreModule } from '@xfilecom/core-sdk';

@Module({
  imports: [CoreModule.forHybrid()],
})
export class AppModule {}

커스텀 설정 (forRoot)

import { Module } from '@nestjs/common';
import { CoreModule } from '@xfilecom/core-sdk';
import { mySchema } from './database/schema';

@Module({
  imports: [
    CoreModule.forRoot({
      database: { schema: mySchema },
      jwt: { secret: process.env.JWT_SECRET, expiresIn: '7d' },
      response: { commonResponse: true },
      interceptors: { logging: true, databaseCheck: true },
      guards: { jwt: true },
      filters: { exception: true },
    }),
  ],
})
export class AppModule {}

설정 파일 (application.yml)

  • 위치: 설정 파일 위치 (표준) 참고.
  • 소스 선택: 설정 소스 (.env vs YAML) 참고 — configSource 또는 CONFIG_SOURCE 로 YAML / .env 선택 가능.
  • 비동기: loadApplicationYaml(options?)
  • 동기: loadApplicationYamlSync(options?) (bootstrap 전 등)
  • syncEnvKeys: YAML 로드 후 config 일부를 process.env에 채울 수 있음. ConfigValidator / JwtAuthGuard 등이 env를 참조할 때 YAML만 써도 연동 가능.
import { loadApplicationYaml, loadApplicationYamlSync } from '@xfilecom/core-sdk';

const config = await loadApplicationYaml();
const dbHost = config?.database?.host ?? process.env.DB_HOST;

const configWithEnv = await loadApplicationYaml({ env: process.env.NODE_ENV });
const configSync = loadApplicationYamlSync({ rootDir: path.join(__dirname, '..') });

// YAML 로드 후 JWT 등을 process.env에 동기화 (Guard/ConfigValidator와 연동)
const config = await loadApplicationYaml({
  env: process.env.NODE_ENV,
  syncEnvKeys: {
    JWT_SECRET: 'jwt.secret',
    JWT_EXPIRES_IN: 'jwt.expiresIn',
    HASH_VERIFICATION_SECRET: 'hashVerification.default.secret',
  },
});

설정 파일 안에는 DB, JWT, hashVerification 등 자유롭게 정의하고, 코드에서 config?.database, config?.hashVerification 등으로 접근하면 됩니다. JWT는 CoreModule.forHttpApi({ jwt: config?.jwt }) 로 넘기면 ConfigValidator.validateJwtConfig 시 env 검사를 건너뜁니다.


설정 루트·Config 로더

모노레포·실행 위치와 관계없이 설정 디렉터리를 찾고, 한 번 로드 → env 동기화 → 전역 캐시 → getFullConfig / getServiceConfig 패턴을 쓰려면 아래 API를 사용합니다.

resolveConfigRootDir(options?)

application.yml / application.yaml 이 있는 디렉터리를 후보 순서대로 탐색해 반환합니다. 기본 후보는 모노레포 패턴(libs/config/config, ../../libs/config/config, ../libs/config/config, cwd)입니다.

import { resolveConfigRootDir, loadApplicationYamlSync, DEFAULT_CONFIG_ROOT_CANDIDATES } from '@xfilecom/core-sdk';

// 기본 (process.cwd() 기준 후보 탐색)
const rootDir = resolveConfigRootDir();

// 후보·파일명 지정
const rootDir2 = resolveConfigRootDir({
  cwd: process.cwd(),
  candidates: ['libs/config/config', 'config', '.'],
  configFilenames: ['application.yml', 'application.yaml'],
});

const config = loadApplicationYamlSync({ rootDir, env: process.env.NODE_ENV });

createConfigLoader(options)

한 번 로드한 config를 캐시하고, getFullConfig() / getServiceConfig(serviceKey, overrides?) 를 반환합니다. auth-service, mail-service, chat-service 등에서 동일한 패턴으로 사용할 수 있습니다.

  • getFullConfig(): 이미 로드된 full config 반환 (최초 호출 시 1회 로드 후 저장).
  • getServiceConfig(serviceKey, overrides?): full.common + full[serviceKey] 병합. overrides.portEnvKey 가 있으면 process.env[portEnvKey]server.port 덮어씀.
import { createConfigLoader, resolveConfigRootDir } from '@xfilecom/core-sdk';

const { getFullConfig, getServiceConfig } = createConfigLoader({
  rootDir: resolveConfigRootDir(),
  env: process.env.APP_PROFILE ?? (process.env.NODE_ENV === 'production' ? 'prod' : 'dev'),
  syncEnvKeys: {
    JWT_SECRET: 'auth-service.jwt.secret',
    JWT_EXPIRES_IN: 'auth-service.jwt.expiresIn',
  },
  globalKey: 'auth-service',
});

// 서비스별 config (common + auth-service 병합, 포트는 AUTH_SERVICE_PORT 로 덮어쓰기)
export const getAuthServiceConfig = () =>
  getServiceConfig('auth-service', { portEnvKey: 'AUTH_SERVICE_PORT' });
export { getFullConfig };
  • rootDir 없으면 resolveConfigRootDir() 로 자동 해석.
  • globalKey: 동일 키로 캐시해 두어 재호출 시 같은 객체 반환.
  • YAML 구조 예: common: { ... }, auth-service: { server: { port: 3001 }, jwt: { ... } }, mail-service: { ... }.

Crypto (비밀번호·조회용 해시)

PBKDF2 비밀번호 (usdt3·기존 DB 호환)

저장 형식 saltHex:hashHex, 기본 pbkdf2-sha512 100_000회·64바이트. bcrypt와 형식이 달라 기존 usdt3 DB와 호환하려면 아래 API 사용.

import {
  pbkdf2HashPassword,
  pbkdf2VerifyPassword,
  PBKDF2_DEFAULT_ITERATIONS,
  PBKDF2_DEFAULT_KEYLEN,
  PBKDF2_DEFAULT_DIGEST,
  type Pbkdf2Options,
} from '@xfilecom/core-sdk';

// 해시 (salt 미지정 시 랜덤 salt 자동 생성)
const stored = pbkdf2HashPassword('myPassword');
// 옵션 지정 시
const stored2 = pbkdf2HashPassword('myPassword', undefined, {
  iterations: 100_000,
  keylen: 64,
  digest: 'sha512',
});

// 검증
const ok = pbkdf2VerifyPassword('myPassword', stored);
const ok2 = pbkdf2VerifyPassword('myPassword', stored2, { iterations: 100_000, keylen: 64, digest: 'sha512' });

조회용 해시 (키 파생 — usdt3·기존 DB 호환)

기존 DB가 HMAC 키 = sha256(secret) 방식이면 keyDerivation: 'sha256' 사용. YAML 프로필 또는 lookupHash/compareLookupHash 로 동일한 해시 생성.

import { lookupHash, compareLookupHash, emailNormalizer, digitsNormalizer } from '@xfilecom/core-sdk';

// secret을 sha256(secret)으로 파생해 HMAC (usdt3 호환)
const emailHash = lookupHash('[email protected]', process.env.EMAIL_HASH_SECRET, {
  keyDerivation: 'sha256',
  normalizer: emailNormalizer,
});
const match = compareLookupHash('[email protected]', row.email_hash, process.env.EMAIL_HASH_SECRET, {
  keyDerivation: 'sha256',
  normalizer: emailNormalizer,
});

YAML에서 프로필별로 지정할 때는 hashVerification.profiles.emailkeyDerivation: sha256 추가 후 hashByKey('email', ...) 사용하면 동일한 결과.


Hash verification

이메일·전화번호·식별자 등 문자열을 YAML 설정 기반으로 HMAC 해시 후 저장/비교할 수 있습니다. 설정은 표준 위치의 application.yml 에 두면 됩니다.

YAML 구조 (application.yml 에 추가)

hashVerification:
  default:
    algorithm: sha256
    secret: ${HASH_VERIFICATION_SECRET}
    encoding: hex
    keyDerivation: sha256   # usdt3 등 기존 DB 호환 시: HMAC 키 = sha256(secret)
  profiles:
    email:
      secret: ${EMAIL_HASH_SECRET}
      normalizer: email
      keyDerivation: sha256
    phone:
      secret: ${PHONE_HASH_SECRET}
      normalizer: digits
      keyDerivation: sha256
    userId:
      secret: ${USER_ID_HASH_SECRET}
    tenantId:
      secret: ${TENANT_HASH_SECRET}
  • default: 해당 키가 없을 때 사용하는 공통 설정.
  • profiles: 키별 설정. 새 키만 YAML에 추가하면 코드 수정 없이 hashByKey(key, value, hv) 로 처리 가능.
  • normalizer: email(trim+소문자), digits(숫자만). 생략 시 입력 그대로 해시.
  • keyDerivation: none(기본)=secret 그대로 HMAC 키로 사용. sha256=sha256(secret)을 HMAC 키로 사용(usdt3·기존 DB 호환).

동적 키로 해시/비교 (권장)

import { loadApplicationYaml, hashByKey, compareByKey } from '@xfilecom/core-sdk';

const config = await loadApplicationYaml({ env: process.env.NODE_ENV });
const hv = config?.hashVerification;

const emailHash = hashByKey('email', '[email protected]', hv);
const phoneHash = hashByKey('phone', '010-1234-5678', hv);
const userIdHash = hashByKey('userId', uid, hv);

const match = compareByKey('email', plainEmail, row.email_hash, hv);

공통 API (config 직접 전달)

import { hashValue, compareValue } from '@xfilecom/core-sdk';

const hv = config?.hashVerification;
const emailHash = hashValue('[email protected]', hv?.profiles?.email ?? hv?.email);
const match = compareValue('[email protected]', row.email_hash, hv?.profiles?.email ?? hv?.email);

이메일 전용

import { hashEmail, compareEmail } from '@xfilecom/core-sdk';

const emailHash = hashEmail('[email protected]', config?.hashVerification?.profiles?.email);
const match = compareEmail('[email protected]', row.email_hash, config?.hashVerification?.profiles?.email);
  • secret 없으면 HASH_VERIFICATION_SECRET(이메일 키는 EMAIL_HASH_SECRET) env 사용.
  • 내장 normalizer: email, digits. BUILT_IN_NORMALIZERS, getProfileConfig, emailNormalizer, digitsNormalizer export 됨.
  • usdt3·기존 DB 호환: 프로필에 keyDerivation: sha256 지정하거나, YAML 없이 lookupHash/compareLookupHash(..., { keyDerivation: 'sha256' }) 사용.

기능 옵션

CoreModule 옵션으로 기능별 on/off 가능. 미설정 시 기본값 true.

| 옵션 | 설명 | 기본값 | |------|------|--------| | database.auto | DB 모듈 사용 | true | | response.commonResponse | { code, data, meta, error } 형식 적용 | true | | interceptors.logging | 요청/응답 로깅 | true | | interceptors.errorHandling | 에러 인터셉터 | true | | interceptors.responseTransform | 응답 변환(CommonResponse) | true | | interceptors.databaseCheck | DB 연결 체크 | true | | guards.jwt | JWT 인증 가드 (forHttpApi/forHybrid 기본 true) | context별 | | filters.exception | AllExceptionsFilter | true |

예: CommonResponse 끄기, 로깅/DB체크 끄기

CoreModule.forHttpApi({
  database: { schema },
  response: { commonResponse: false },
  interceptors: { logging: false, databaseCheck: false },
})

Database

스키마 정의 및 주입

// database/schema.ts
import { mysqlTable, int, varchar, timestamp } from 'drizzle-orm/mysql-core';

export const users = mysqlTable('users', {
  id: int('id').primaryKey().autoincrement(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  password: varchar('password', { length: 255 }).notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});

export const schema = { users };

환경 변수

DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME, DB_SSL, DB_CONNECTION_LIMIT

DatabaseQuery

where는 Drizzle SQL 또는 plain object(eq 조합) 지원.

// select(options)
const result = await this.dbQuery.select({
  table: 'users',
  fields: ['id', 'name', 'email'],
  where: { email: '[email protected]' },
});

// query() — pagination, sort, search
const result = await this.dbQuery.query({
  table: 'users',
  fields: ['id', 'email'],
  sort: [{ field: 'createdAt', order: 'desc' }],
  pagination: { page: 1, limit: 10 },
  search: [{ keyword: 'test', fields: ['email', 'name'] }],
  where: { status: 'active' },
});

// insert / update / delete
await this.dbQuery.insertOne({ table: 'users', fields: ['email', 'name'], values: data });
await this.dbQuery.updateOne({ table: 'users', values: data, where: eq(users.id, id) });
await this.dbQuery.deleteOne({ table: 'users', where: eq(users.id, id) });

// findOne / findById / count / exists
const user = await this.dbQuery.findOne('users', { where: { email: '[email protected]' }, fields: ['id', 'email'] });
const user = await this.dbQuery.findById('users', 1);
const n = await this.dbQuery.count('users', { where: { status: 'active' } });
const ok = await this.dbQuery.exists('users', { where: { email: '[email protected]' } });

ServiceHelpers (Drizzle 직접 사용)

const db = this.helpers.getDb();
const [user] = await db.select().from(users).where(eq(users.email, email)).limit(1);

await this.helpers.withTransaction(async (tx) => {
  await tx.insert(users).values({ email: '[email protected]' });
  await tx.insert(posts).values({ userId: 1, title: 'Hello' });
});

const paginationResult = this.helpers.createPaginationResult(items, total, page, limit);

Controller / Service 예시

// Controller
import { Controller, Get, Request, UseGuards } from '@nestjs/common';
import { ControllerHelpers, Public, User, JwtUser, Roles, JwtAuthGuard, RolesGuard } from '@xfilecom/core-sdk';

@Controller('users')
export class UsersController {
  constructor(private readonly helpers: ControllerHelpers) {}

  @Public()
  @Get()
  async findAll(@Request() req: any) {
    const { page, limit } = this.helpers.parsePagination(req.query);
    const users = await this.userService.findAll(page, limit);
    return this.helpers.success(users, 'Users retrieved');
  }

  @UseGuards(JwtAuthGuard)
  @Get('me')
  getMe(@User() user: JwtUser) {
    return user;
  }

  @UseGuards(JwtAuthGuard)
  @Get('my-id')
  getMyId(@User('sub') userId: string | number) {
    return { userId };
  }

  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin')
  @Get('admin')
  adminOnly() {
    return { message: 'admin only' };
  }
}

// Service
@Injectable()
export class UsersService {
  constructor(
    private readonly helpers: ServiceHelpers,
    private readonly dbQuery: DatabaseQuery,
  ) {}

  async findAll(page: number, limit: number) {
    return this.dbQuery.query({
      table: 'users',
      fields: ['id', 'email', 'name'],
      pagination: { page, limit },
      sort: [{ field: 'id', order: 'desc' }],
    });
  }
}

Guards · Decorators · Interceptors · Filters

  • Guards: JwtAuthGuard, RolesGuard
  • Decorators: @Public(), @User(), @User('sub'), @Roles('admin', 'user')
  • Interceptors: LoggingInterceptor, ErrorHandlingInterceptor, ResponseTransformInterceptor, DatabaseCheckInterceptor
  • Filters: AllExceptionsFilter
  • 응답 타입: CommonResponseDto, CommonErrorDto, PaginationMeta, ResponseMeta

JWT 인증: JwtAuthGuardprocess.env.JWT_SECRET을 참조합니다. YAML만 쓸 경우 loadApplicationYaml({ syncEnvKeys: { JWT_SECRET: 'jwt.secret' } }) 로 config 값을 env에 채우면 Guard와 자연스럽게 연동됩니다. CoreModule.forHttpApi({ jwt: { secret, expiresIn } }) 로 넘기면 ConfigValidator는 env 검사를 건너뜁니다. passport-jwt / @nestjs/passport선택 의존성이며, 없으면 경고 후 인증을 스킵합니다 (개발용). 프로덕션에서는 설치 권장.


기타 유틸

  • ErrorUtils: 에러 메시지/코드 처리
  • LoggerHelpers, ILogWriter, ConsoleLogWriter, FileLogWriter, getLoggingConfigFromEnv: 로깅
  • ConfigValidator: JWT/DB 설정 검증 (validateJwtConfig(throwOnMissing?, jwtOptions?)jwtOptions.secret 이 있으면 env 검사 생략), parseBoolean, parseNumber, parseArray (쉼표 구분 문자열 → 배열)
  • Env: getAppEnv(), isDevelopment(), isProduction(), isStaging(), currentAppEnv, isDev, isProd (APP_ENV / NODE_ENV 기반)

Database 연결 실패 시

  • 연결 실패해도 서버는 기동됩니다 (Graceful Degradation).
  • .envDB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME 설정 후 재시도.
  • Config 직접 주입: CoreModule.forHttpApi({ database: { config: { ... }, schema } }).

수정 사항

JWT 설정 검증 (ConfigValidator)

  • validateJwtConfig(throwOnMissing?, jwtOptions?) 두 번째 인자 추가.
  • options.jwt?.secret 이 있으면 process.env.JWT_SECRET 검사를 건너뛰어 불필요한 경고를 내지 않음.
  • CoreModule 에서 JWT Guard 등록 시 options.jwt 를 넘기도록 변경 → YAML/옵션만으로 JWT 설정 시 경고 제거.

설정 로드 시 env 동기화 (syncEnvKeys)

  • loadApplicationYaml / loadApplicationYamlSync 옵션에 syncEnvKeys?: Record<string, string> 추가.
  • 로드한 config의 dot 경로(예: jwt.secret) 값을 지정한 환경 변수명(예: JWT_SECRET)으로 process.env에 채움.
  • YAML만 사용해도 ConfigValidator, JwtAuthGuard 등이 process.env를 참조할 때 자연스럽게 연동 가능.

설정 루트·Config 로더 (resolveConfigRootDir, createConfigLoader)

  • resolveConfigRootDir(options?): application.yml 이 있는 디렉터리 탐색. 기본 후보: libs/config/config, ../../libs/config/config, ../libs/config/config, cwd. 모노레포·실행 위치 무관하게 동일 규칙 사용 가능.
  • createConfigLoader(options): 한 번 로드 → env 동기화 → 전역 캐시(globalKey) → getFullConfig() / getServiceConfig(serviceKey, { portEnvKey? }) 제공. auth-service, mail-service 등에서 config-root.ts·load-config.ts 대신 core-sdk만으로 동일 패턴 사용 가능.

설정 소스 (configSource)

  • configSource: 'yaml' | 'env' | 'yamlWithEnvOverrides' 옵션 및 CONFIG_SOURCE 환경 변수로 .env vs YAML 선택 가능.
  • configSource: 'env' 시 YAML 없이 process.env 기반 config 객체 생성.

환경 유틸 (Env)

  • getAppEnv(), isDevelopment(), isProduction(), isStaging(), currentAppEnv, isDev, isProd 추가 (APP_ENV / NODE_ENV 기반).

Crypto (비밀번호·조회용 해시)

  • PBKDF2 비밀번호: pbkdf2HashPassword, pbkdf2VerifyPassword — 저장 형식 saltHex:hashHex, 기본 100_000회·sha512·64바이트 (usdt3·기존 DB 호환).
  • 조회용 해시 키 파생: HashVerificationConfig.keyDerivation: 'none' | 'sha256'sha256 이면 HMAC 키로 sha256(secret) 사용 (usdt3·기존 email_hash 등과 동일).
  • lookupHash / compareLookupHash: YAML 없이 secret + keyDerivation 만으로 조회용 해시 생성/비교. deriveKeySha256 export.

Hash verification

  • hashVerification.profiles + hashByKey / compareByKey 로 동적 키별 해시/비교 지원.
  • keyDerivation: 'sha256' 로 기존 DB와 동일한 해시 생성 가능.
  • ConfigValidator.parseArray 추가 (쉼표 구분 문자열 → 배열).

문서

  • JWT 인증: process.env 참조, syncEnvKeys 연동, passport-jwt 선택 의존성 안내.
  • Auth 클라이언트: auth 모듈이 패키지에 포함되면 index에서 export 예정.

라이선스

ISC