본문으로 건너뛰기

DeviceIdentifier 값 객체 구현 가이드

1. 개요

device-identifier.value-object.ts 파일은 앱 인증 시스템에서 사용되는 디바이스 식별자를 위한 값 객체(Value Object)를 구현합니다. 이 값 객체는 SHA-256으로 해시된 64자 16진수 문자열 형태의 디바이스 ID를 캡슐화하고 유효성을 검증합니다.

2. 구현 코드

// 파일 경로: libs/feature/auth/src/lib/domain/value-objects/device-identifier.value-object.ts

/**
* DeviceIdentifier 값 객체는 해시된 디바이스 ID를 나타냅니다.
* 불변성을 가지며, 생성 시 유효성 검증을 수행합니다.
*/
export class DeviceIdentifier {
constructor(private readonly value: string) {
if (!this.isValid(value)) {
throw new Error('유효하지 않은 디바이스 식별자 형식입니다. (64자 16진수 문자열 필요)');
}
}

/**
* 입력된 값이 유효한 DeviceIdentifier 형식(64자 16진수 문자열)인지 검증합니다.
* @param value 검증할 문자열
* @returns 유효하면 true, 그렇지 않으면 false
*/
private isValid(value: string): boolean {
// DeviceIdentifier는 64자 16진수 문자열이어야 함 (SHA-256 해시)
return /^[0-9a-f]{64}$/i.test(value);
}

/**
* 값 객체가 가지고 있는 실제 디바이스 식별자 문자열을 반환합니다.
* @returns 디바이스 식별자 문자열
*/
getValue(): string {
return this.value;
}

/**
* 다른 DeviceIdentifier 값 객체와 동등성을 비교합니다.
* 객체 참조가 아닌 실제 값으로 비교합니다.
* @param other 비교할 다른 DeviceIdentifier 객체
* @returns 값이 같으면 true, 다르면 false
*/
equals(other: DeviceIdentifier): boolean {
// null 또는 undefined 비교 방지
if (other === null || other === undefined) {
return false;
}
return this.value === other.getValue();
}
}

3. 주요 설계 원칙 및 설명

  • 불변성 (Immutability): readonly 키워드를 사용하여 생성 후 내부 값이 변경되지 않도록 보장합니다. 이는 예측 가능하고 안정적인 동작을 가능하게 합니다.
  • 자체 검증 (Self-Validation): 생성자(constructor)에서 isValid 메서드를 통해 입력 값의 유효성(64자 16진수 형식)을 즉시 검증합니다. 유효하지 않은 값으로 객체가 생성되는 것을 방지합니다.
  • 값 기반 동등성 (Value-Based Equality): equals() 메서드를 제공하여 객체의 메모리 주소가 아닌 실제 값(value)을 기준으로 동등성을 비교합니다. 값 객체의 핵심적인 특징입니다.
  • 명확한 의도 (Clear Intent): 단순한 문자열 대신 DeviceIdentifier 타입을 사용함으로써 코드 내에서 해당 값의 의미와 제약 조건을 명확하게 전달합니다.

4. 사용 예제

// DeviceIdentifier 생성 (유효한 경우)
const validHash = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2'; // 64자 16진수
const deviceId = new DeviceIdentifier(validHash);
console.log(deviceId.getValue()); // 'a1b2c3d4...'

// DeviceIdentifier 생성 (유효하지 않은 경우 - 에러 발생)
try {
const invalidId = new DeviceIdentifier('invalid-hash-string');
} catch (error) {
console.error(error.message); // '유효하지 않은 디바이스 식별자 형식입니다...'
}

// 동등성 비교
const anotherDeviceId = new DeviceIdentifier(validHash);
console.log(deviceId.equals(anotherDeviceId)); // true

const differentDeviceId = new DeviceIdentifier('f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9');
console.log(deviceId.equals(differentDeviceId)); // false