Menu
Coddy logo textTech

제너레이터와 이터레이터

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 62번째.

제너레이터(Generators)는 전체 Iterator 인터페이스를 구현하지 않고도 이터레이터를 생성하는 간단한 방법을 제공합니다. 제너레이터 함수는 return 대신 yield를 사용하여 값을 한 번에 하나씩 생성하며, 각 값 사이에서 실행을 일시 중지합니다.

다음은 일련의 숫자를 생성하는 기본적인 제너레이터입니다:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

출력:

1
2
3

주요 장점은 메모리 효율성입니다. 메모리에 전체 배열을 생성하는 대신, 제너레이터는 필요할 때마다 값을 생성합니다. 이는 대규모 데이터 세트를 작업할 때 매우 중요합니다. 백만 개의 레코드를 처리하는 제너레이터는 10개를 처리하는 제너레이터와 동일한 메모리를 사용합니다.

제너레이터는 키-값 쌍을 생성(yield)할 수도 있습니다:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

출력:

admin: Alice
editor: Bob

자신만의 클래스를 foreach로 반복 가능하게 만들려면 Iterator 인터페이스를 구현하세요. 하지만 대부분의 경우, 제너레이터(generator)가 상용구 코드(boilerplate code)가 적은 더 깔끔한 해결책을 제공합니다. 이는 클래스가 모든 항목을 한 번에 메모리에 로드하지 않고도 항목 컬렉션을 노출해야 할 때 OOP에서 특히 유용합니다.

challenge icon

챌린지

쉬움

대규모 컬렉션을 한꺼번에 메모리에 로드하지 않고 효율적으로 순회하기 위해 제너레이터(generators)를 사용하는 제품 인벤토리 시스템을 구축해 보겠습니다. 제품을 저장하고 다양한 방식으로 순회할 수 있는 제너레이터 메서드를 제공하는 클래스를 만들 것입니다.

코드는 세 개의 파일로 구성됩니다:

  • Product.php — 인벤토리의 항목을 나타내는 Product 클래스를 생성합니다. 생성자 프로모션(constructor promotion)을 사용하여 public $sku (string), public $name (string), 그리고 public $price (float)를 정의합니다.
  • Inventory.php — 제품 컬렉션을 관리하고 이를 순회하는 제너레이터 메서드를 제공하는 Inventory 클래스를 생성합니다. Product 파일을 포함해야 합니다. 이 클래스는 다음을 수행해야 합니다:
    • 제품을 저장할 private 배열 속성을 가집니다.
    • 컬렉션에 제품을 추가하는 addProduct(Product $product) 메서드를 가집니다.
    • 각 제품을 하나씩 생성(yield)하는 제너레이터 메서드 allProducts(): Generator를 가집니다.
    • 주어진 최소 가격보다 크거나 같은 가격의 제품만 생성하는 제너레이터 메서드 expensiveProducts(float $minPrice): Generator를 가집니다. 이때 키는 SKU이고 값은 제품인 키-값 쌍을 생성해야 합니다.
  • main.php — Inventory 파일을 포함합니다. 하나의 입력값으로 최소 가격 임계값을 받게 됩니다.

    Inventory 인스턴스를 생성하고 다음 세 가지 제품을 추가합니다:

    • SKU: "A001", Name: "Laptop", Price: 999.99
    • SKU: "A002", Name: "Mouse", Price: 29.99
    • SKU: "A003", Name: "Monitor", Price: 349.99

    먼저, allProducts()를 순회하며 각 제품의 이름을 개별 줄에 출력합니다.

    그런 다음 빈 줄을 하나 출력하고, 입력받은 임계값을 사용하여 expensiveProducts()를 순회합니다. 각 고가 제품에 대해 "[sku]: [name] - $[price]" 형식으로 개별 줄에 출력합니다. 가격은 소수점 아래 2자리까지 정확히 포맷팅하여 출력하세요.

이 챌린지는 제너레이터가 컬렉션을 지연(lazily) 순회하게 해주는 방법을 보여줍니다. 각 제품은 요청될 때만 생성되므로, 대규모 인벤토리에 대해 메모리 효율적인 접근 방식이 됩니다. expensiveProducts()에서의 키-값 생성은 제너레이터가 어떻게 구조화된 순회 데이터를 제공할 수 있는지 보여줍니다.

치트 시트

제너레이터는 return 대신 yield를 사용하여 이터레이터를 생성하는 메모리 효율적인 방법을 제공합니다. 제너레이터는 값을 한 번에 하나씩 생성하며, 각 값 사이에서 실행을 일시 중지합니다.

기본적인 제너레이터 함수:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

제너레이터는 키-값 쌍을 생성할 수 있습니다:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

주요 장점은 메모리 효율성입니다. 제너레이터는 메모리에 전체 배열을 빌드하는 대신 필요에 따라 값을 생성합니다. 이는 대규모 데이터 세트나 OOP 컨텍스트의 컬렉션을 작업할 때 특히 유용합니다.

직접 해보기

<?php

require_once 'Inventory.php';

// 입력 읽기
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Inventory 인스턴스 생성

// TODO: 세 개의 제품 추가:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99

// TODO: allProducts()를 순회하며 각 제품의 이름(name)을 출력

// TODO: 빈 줄 출력

// TODO: $minPrice를 사용하여 expensiveProducts()를 순회
// 각 비싼 제품에 대해 "[sku]: [name] - $[price]" 형식으로 출력
// 가격은 소수점 둘째 자리까지 표시

?>
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨