
Security News
/Research
Popular node-ipc npm Package Infected with Credential Stealer
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.
point3-common-tool
Advanced tools
포인트3 공유 코드베이스
이 저장소는 포인트3에서 공통적으로 사용하는 TypeScript/JavaScript 유틸리티, 값 객체, 로거, 이벤트, 네트워크 어댑터, 테스트 도구 등을 모아놓은 npm 패키지입니다.
팀 내 여러 프로젝트에서 재사용 가능한 코드들을 효율적으로 관리하고, 일관된 개발 환경을 제공합니다.
npm install point3-common-tool
p3Loggers)Winston 기반의 로거 시스템으로 NestJS와 통합되어 있습니다.
import { p3Loggers } from "point3-common-tool";
const logger = new p3Loggers.Logger();
logger.info("정보 로그");
logger.error("에러 로그");
logger.warn("경고 로그");
logger.debug("디버그 로그");
p3Values)import { p3Values } from "point3-common-tool";
const phone = new p3Values.PhoneNumber("010-1234-5678");
console.log(phone.isValid()); // true
console.log(phone.getMasked()); // 010****5678
console.log(phone.getType()); // Mobile 또는 Landline
const email = p3Values.Email.create("user@example.com");
console.log(email.toString()); // user@example.com
console.log(email.Provider); // example.com
const guid = p3Values.Guid.generate();
console.log(guid); // 고유 식별자
const gulid = p3Values.Gulid.create("user");
console.log(gulid.toString()); // user:01HKXYZ123...
const parsed = p3Values.Gulid.parse("user:01HKXYZ123...");
console.log(parsed.prefix); // user
const name = p3Values.Name.create("홍길동");
console.log(name.toString()); // 홍길동
console.log(name.Masked); // 홍*동 (마스킹된 이름)
const money = new p3Values.Money(1000, "KRW");
console.log(money.toString()); // 1000 KRW
const url = new p3Values.CallbackURL("https://example.com/callback");
console.log(url.isValid()); // true
// 일반 매니저 역할
console.log(p3Values.ManagerRoleType.ADMIN); // admin-manager
console.log(p3Values.ManagerRoleType.DEVELOPER); // developer-manager
// Point3 전용 역할
console.log(p3Values.Point3ManagerRoleType.POINT3_ADMIN); // p3-CISO-0
console.log(p3Values.Point3ManagerRoleType.POINT3_DEVELOPER); // p3-DEV-0
axiosAdapter)Axios 기반의 HTTP 클라이언트 템플릿으로 NestJS와 통합되어 있습니다.
import { axiosAdapter } from "point3-common-tool";
// RESTTemplate 생성
const restTemplate = new axiosAdapter.RESTTemplate(
logger, // NestJS LoggerService
"https://api.example.com", // baseURL
10000 // timeout (ms)
);
// Bearer 토큰 설정
restTemplate.setBearer("your-jwt-token");
// Basic 인증 설정
restTemplate.setBasic("username", "password");
// HTTP 요청
const response = await restTemplate.get<UserData>("/users/123");
console.log(response.data); // 응답 데이터
console.log(response.status); // HTTP 상태 코드
// POST 요청
const createResponse = await restTemplate.post<User>("/users", {
name: "홍길동",
email: "hong@example.com"
});
try {
const response = await restTemplate.get("/api/data");
} catch (error) {
if (error instanceof axiosAdapter.UnauthorizedError) {
console.log("인증 실패");
} else if (error instanceof axiosAdapter.ValidationError) {
console.log("유효성 검사 실패:", error.details);
} else if (error instanceof axiosAdapter.NetworkError) {
console.log("네트워크 에러");
}
}
HttpError: 기본 HTTP 에러NetworkError: 네트워크 연결 에러TimeoutError: 타임아웃 에러BadRequestError: 400 에러UnauthorizedError: 401 에러ForbiddenError: 403 에러NotFoundError: 404 에러ValidationError: 422 에러p3Event)NestJS 기반의 이벤트 발행 및 릴레이 시스템으로, Outbox 패턴을 구현하여 안전한 이벤트 처리를 제공합니다.
먼저 BaseEvent를 상속받아 도메인 이벤트를 정의합니다:
import { p3Event, p3Values } from "point3-common-tool";
// 페이로드 인터페이스 정의
interface UserCreatedPayload extends p3Event.Payload {
userId: string;
email: string;
name: string;
toJSON(): JSON;
}
// 이벤트 클래스 정의
export class UserCreatedEvent extends p3Event.BaseEvent<p3Values.Guid, UserCreatedPayload> {
static prefix = "user-created"; // 필수: 이벤트 ID 접두사
constructor(payload: UserCreatedPayload, eventId?: p3Values.Guid) {
super(payload, eventId);
}
}
EventRepository 인터페이스를 구현하여 이벤트 저장소를 만듭니다:
import { Injectable } from "@nestjs/common";
import { p3Event, p3Values } from "point3-common-tool";
@Injectable()
export class UserEventRepository implements p3Event.EventRepository<UserCreatedEvent> {
// 이벤트를 Outbox에 저장
async save(...events: UserCreatedEvent[]): Promise<void> {
// 데이터베이스에 이벤트 저장 로직
// 트랜잭션 내에서 도메인 로직과 함께 저장
}
// 실패한 이벤트를 Dead Letter Queue로 이동
async toDeadletter(...events: UserCreatedEvent[]): Promise<void> {
// Dead Letter 테이블로 이벤트 이동
}
// 지정된 저장소에서 이벤트 조회
async get(from: p3Event.EventStorage, ...params: any[]): Promise<UserCreatedEvent[]> {
// OUTBOX 또는 DEAD_LETTER에서 이벤트 조회
if (typeof params[0] === 'number') {
// 배치 크기만큼 이벤트 조회
const batchSize = params[0];
return this.getEventsByBatch(from, batchSize);
} else {
// 특정 ID들로 이벤트 조회
const eventIds = params as p3Values.Guid[];
return this.getEventsByIds(from, eventIds);
}
}
// 이벤트 삭제 (처리 완료 후)
async delete(from: p3Event.EventStorage, ...eventIds: p3Values.Guid[]): Promise<void> {
// 성공적으로 발행된 이벤트를 저장소에서 삭제
}
}
BaseEventRelayer를 상속받아 실제 이벤트 발행 로직을 구현합니다:
import { Injectable, Logger } from "@nestjs/common";
import { p3Event, p3Values } from "point3-common-tool";
@Injectable()
export class KafkaEventRelayer extends p3Event.BaseEventRelayer {
constructor(
logger: Logger,
private readonly kafkaProducer: any // Kafka Producer 주입
) {
super(logger);
}
// 실제 이벤트 발행 로직 구현
protected async produce(
message: p3Event.BaseEvent<p3Values.Guid, p3Event.Payload>,
from: p3Event.From,
to: Symbol
): Promise<void> {
const topic = to.description || 'default-topic';
await this.kafkaProducer.send({
topic: topic,
messages: [{
key: message.Id.toString(),
value: JSON.stringify({
id: message.Id.toString(),
payload: message.Payload
})
}]
});
}
}
EventRelayableApplication을 상속받아 이벤트 저장소를 등록합니다:
import { Injectable } from "@nestjs/common";
import { ModuleRef } from "@nestjs/core";
import { p3Event } from "point3-common-tool";
// 토큰 정의
export const UserEventRepositoryToken = Symbol.for("UserEventRepository");
export const UserEventTopic = Symbol.for("user-events");
@Injectable()
export class UserService extends p3Event.EventRelayableApplication {
constructor(
@p3Event.RegisterableEventRepository(UserEventTopic, UserEventRepositoryToken)
private readonly userEventRepository: UserEventRepository,
moduleRef: ModuleRef
) {
super(moduleRef);
this.registerEvents(); // 필수: 이벤트 저장소 등록
}
async createUser(userData: CreateUserDto): Promise<User> {
// 1. 도메인 로직 실행
const user = await this.userRepository.save(userData);
// 2. 이벤트 생성
const event = new UserCreatedEvent({
userId: user.id,
email: user.email,
name: user.name,
toJSON: () => ({ userId: user.id, email: user.email, name: user.name } as any)
});
// 3. 이벤트를 Outbox에 저장 (같은 트랜잭션 내)
await this.userEventRepository.save(event);
return user;
}
}
스케줄러나 백그라운드 작업으로 이벤트 릴레이어를 실행합니다:
import { Injectable } from "@nestjs/common";
import { Cron, CronExpression } from "@nestjs/schedule";
@Injectable()
export class EventRelayScheduler {
constructor(
private readonly eventRelayer: KafkaEventRelayer
) {}
@Cron(CronExpression.EVERY_10_SECONDS)
async relayEvents() {
await this.eventRelayer.execute();
}
}
이벤트 릴레이어는 Silent Failure 방식으로 동작합니다. 에러가 발생해도 예외를 던지지 않고 내부적으로 처리합니다:
// ❌ 잘못된 예시 - eventRelayer.execute()는 예외를 던지지 않습니다
try {
await this.eventRelayer.execute();
} catch (error) {
// 이 블록은 실행되지 않습니다
}
// ✅ 올바른 모니터링 방식
@Injectable()
export class EventRelayScheduler {
constructor(
private readonly eventRelayer: KafkaEventRelayer,
private readonly logger: Logger
) {}
@Cron(CronExpression.EVERY_10_SECONDS)
async relayEvents() {
// execute()는 항상 성공적으로 완료됩니다 (내부에서 모든 에러 처리)
await this.eventRelayer.execute();
// 에러 감지는 다른 방법으로 해야 합니다:
// 1. Dead Letter Queue 모니터링
// 2. 로그 모니터링
// 3. 메트릭 수집
}
}
// Dead Letter Queue 모니터링
@Injectable()
export class EventMonitoringService {
async checkDeadLetterQueue() {
const deadLetterEvents = await this.eventRepository.get(
EventStorage.DEAD_LETTER,
1000
);
if (deadLetterEvents.length > 0) {
this.logger.warn(`Dead Letter Queue에 ${deadLetterEvents.length}개의 실패한 이벤트가 있습니다`);
// 알림 발송, 메트릭 수집 등
}
}
}
// 커스텀 에러 처리가 필요한 경우
export class CustomEventRelayer extends BaseEventRelayer {
protected async produce(message, from, to): Promise<void> {
try {
await this.kafkaProducer.send(/* ... */);
// 성공 메트릭 수집
this.metricsService.incrementSuccess();
} catch (error) {
// 실패 메트릭 수집
this.metricsService.incrementFailure();
// 커스텀 알림 로직
await this.notificationService.sendAlert(error);
throw error; // 다시 던져서 Dead Letter Queue로 이동
}
}
}
p3Testing)테스트용 리포지토리 및 유틸리티를 제공합니다.
import { p3Testing } from "point3-common-tool";
const repo = new p3Testing.TestRepository();
// 데이터 저장
repo.save({ id: 1, name: "테스트 데이터" });
// 데이터 조회
const data = repo.findById(1);
console.log(data); // { id: 1, name: "테스트 데이터" }
// 모든 데이터 조회
const allData = repo.findAll();
logger, values, event, testing, network-adapters)에 TypeScript 파일을 추가하거나 수정합니다.index.ts에 export 구문을 추가하여 외부로 노출합니다.index.ts에서 네임스페이스로 묶여 export되는지 확인합니다.git add .
git commit -m "feat: 새로운 기능 추가"
git push origin main
npm run build
dist/ 디렉토리에 빌드 결과물이 생성됩니다.npm test
npm version [patch|minor|major]
npm version patchnpm publish
# 의존성 설치 (필요시)
npm install
# 빌드 및 테스트
npm run build && npm test
# 커밋 (변경사항이 있는 경우)
git add . && git commit -m "chore: 업데이트"
# 버전업 및 배포
npm version patch && npm publish
모든 모듈은 TypeScript로 작성되어 있으며, 완전한 타입 정의를 제공합니다.
// 타입 안전성 보장
import { p3Values, axiosAdapter, p3Event } from "point3-common-tool";
// 자동 완성 및 타입 체크 지원
const email: p3Values.Email = p3Values.Email.create("test@example.com");
const response: axiosAdapter.HttpResponse<UserData> = await restTemplate.get<UserData>("/users");
const event: p3Event.BaseEvent<p3Values.Guid, UserPayload> = new UserCreatedEvent(payload);
주요 의존성 패키지들:
@nestjs/common: NestJS 프레임워크 통합@nestjs/core: NestJS 코어 기능axios: HTTP 클라이언트winston: 로깅 시스템uuid: UUID 생성ulid: ULID 생성moment-timezone: 시간대 처리FAQs
포인트3 공유 코드베이스
We found that point3-common-tool demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.

Security News
TeamPCP and BreachForums are promoting a Shai-Hulud supply chain attack contest with a $1,000 prize for the biggest package compromise.

Security News
Packagist urges PHP projects to update Composer after a GitHub token format change exposed some GitHub Actions tokens in CI logs.