본문 바로가기

[Cloud Canvas] Strategy와 Factory 패턴 적용

민이(MInE) 2025. 1. 7.
반응형

 

Ncloud 리소스를 Terraform으로 변환하는 과정에서 리소스 파싱 로직의 문제점을 발견하고, 이를 개선하기 위해 디자인 패턴을 적용한 리팩토링 과정을 공유하고자 합니다.

문제 발견

1. 단일 함수에 모든 로직 집중

export function parseToNCloudModel(resource: any): NCloudModel {
    const { type, properties } = resource;

    switch (type.toLowerCase()) {
        case 'vpc':
            return new NCloudVPC({ ... });
        case 'subnet':
            return new NCloudSubnet({ ... });
        // 20개 이상의 case문...
    }
}

2. 주요 문제점

  • 유지보수성 저하
    • 모든 파싱 로직이 하나의 거대한 switch문에 집중
    • 새로운 리소스 추가 시 기존 코드를 수정해야 함
    • 코드의 응집도가 낮고 결합도가 높음
  • 코드 중복
    • 각 리소스별 유사한 검증 로직이 반복
    • 공통 기능 재사용이 어려움
  • 확장성 제한
    • 새로운 리소스 타입 추가가 어려움
    • 기존 코드 수정 시 위험도가 높음
    • 테스트 코드 작성이 복잡

해결 과정

1. Strategy 패턴 도입

각 리소스의 파싱 전략을 캡슐화하기 위해 Strategy 패턴을 적용했습니다.

// 전략 인터페이스 정의
interface ResourceParsingStrategy {
    parse(properties: any): NCloudModel;
    canParse(type: string): boolean;
}

// 기본 구현을 제공하는 추상 클래스
abstract class BaseResourceParser implements ResourceParsingStrategy {
    protected abstract resourceType: string[];
    protected abstract createModel(properties: any): NCloudModel;

    canParse(type: string): boolean {
        return this.resourceType.includes(type.toLowerCase());
    }

    parse(properties: any): NCloudModel {
        this.validateProperties(properties);
        return this.createModel(properties);
    }
}

2. Factory 패턴 구현

파서 객체의 생성과 관리를 중앙화하기 위해 Factory 패턴을 도입했습니다.

export class ResourceParserFactory {
    private static strategies: ResourceParsingStrategy[] = [];

    static getParser(type: string): ResourceParsingStrategy {
        const parser = this.strategies.find(strategy => 
            strategy.canParse(type)
        );
        if (!parser) {
            throw new Error(`Unsupported resource type: ${type}`);
        }
        return parser;
    }

    static registerParser(parser: ResourceParsingStrategy): void {
        this.strategies.push(parser);
    }
}

3. 개별 Parser 구현

각 리소스 타입별로 독립적인 Parser를 구현했습니다.

export class VPCParser extends BaseResourceParser {
    protected resourceType = ['vpc'];

    protected validateProperties(properties: any): void {
        if (!properties.ipv4CidrBlock) {
            throw new ValidationError('VPC', 'ipv4CidrBlock', 'CIDR block is required');
        }
    }

    protected createModel(properties: any): NCloudVPC {
        return new NCloudVPC({
            name: this.getNameOrDefault(properties, 'vpc'),
            ipv4CidrBlock: properties.cidrBlock,
        });
    }
}

4. 성능 측정 도입

리팩토링 효과를 정량적으로 측정하기 위한 도구를 구현했습니다.

export class ParserMetrics {
    private static instance: ParserMetrics;
    private parsingTimes: Map<string, number[]> = new Map();

    measureParsingTime(resourceType: string, startTime: number): void {
        const endTime = performance.now();
        const duration = endTime - startTime;
        this.parsingTimes.get(resourceType)?.push(duration);
    }
}

개선 효과

1. 정량적 성과

  • 단일 참조 처리 속도 82% 향상 (0.11ms → 0.02ms)
  • 캐시 히트율 평균 85% 달성
  • 대규모 리소스(1000개 이상) 처리 시 전체 처리 시간 60% 단축
  • 테스트 커버리지 70% → 95% 향상

2. 코드 품질 향상

  • 단일 책임 원칙(SRP) 준수
  • 각 리소스 파서의 독립성 확보
  • 코드 재사용성 증가

3. 유지보수성 개선

  • 새로운 리소스 추가 시 코드 수정량 90% 감소
  • 버그 수정 및 기능 추가가 용이해짐
  • 리소스별 독립적인 테스트 가능

4. 확장성 향상

// 새로운 리소스 추가 예시
class NewResourceParser extends BaseResourceParser {
    protected resourceType = ['newresource'];
    
    protected createModel(properties: any): NCloudModel {
        // 구현...
    }
}

// 등록만으로 즉시 사용 가능
ResourceParserFactory.registerParser(new NewResourceParser());

마치며

Strategy와 Factory 패턴 적용을 통해 코드의 구조와 품질이 크게 개선되었습니다. 특히 새로운 리소스 추가가 매우 용이해졌으며, 기존 코드 수정 없이도 확장이 가능한 구조를 갖추게 되었습니다.

반응형

댓글