IMG-LOGO
공지사항 :

PHP에서 함수(function)와 객체(object)

lmkfox - 2025-08-24 07:14:11 22 Views 0 Comment

1) 개념 요약

  • 함수(Function): 입력을 받아 출력을 돌려주는 동작(로직) 자체. 보통 상태를 갖지 않으며 호출될 때마다 같은 입력이면 같은 출력(순수 함수)이 이상적입니다.

  • 객체(Object): 상태(프로퍼티) + 행위(메서드) 묶음. 동일한 메서드라도 객체의 현재 상태에 따라 결과가 달라질 수 있습니다.

2) 문법과 형태

함수

function add(int $a, int $b): int {
    return $a + $b;
}

$sum = add(2, 3);

익명 함수(클로저) / 화살표 함수

$mul = function (int $a, int $b): int { return $a * $b; };

$factor = 10;
$times = fn(int $x) => $x * $factor; // 외부 변수 캡처

일급 호출자(First-class callable) – PHP 8.1+

$len = strlen(...);        // Closure 반환
echo $len("hello");        // 5

class Greeter { public function hi(string $n){ return "Hi, $n"; } }
$g = new Greeter();
$call = $g->hi(...);
echo $call("Ada");         // "Hi, Ada"

객체

class Counter {
    private int $n = 0;

    public function inc(int $step = 1): void {
        $this->n += $step;
    }
    public function value(): int {
        return $this->n;
    }
}

$c = new Counter();
$c->inc(3);
echo $c->value(); // 3

호출 가능한 객체(__invoke)

class Adder {
    public function __invoke(int $a, int $b): int { return $a + $b; }
}
$adder = new Adder();
echo $adder(2, 3); // 5  ← 함수처럼 호출

3) 상태와 수명

  • 함수: 기본적으로 상태 없음. 다만

    • 클로저는 외부 변수를 캡처하여 “약한 상태”를 가질 수 있습니다.

    • 함수 내부 static 변수로 상태를 유지할 수 있지만 테스트/가독성 측면에서 권장되지 않습니다.

  • 객체: 프로퍼티로 상태를 가지며, 메서드는 그 상태를 읽고/변경합니다.

    PHP는 요청-응답형 환경에서는 보통 요청이 끝나면 객체도 소멸합니다(장기 실행 워커는 예외).

4) 네임스페이스/재사용/배포

  • 함수도 namespace에 둘 수 있으며 use function Vendor\pkg\util; 식으로 가져올 수 있습니다.

  • 클래스/객체는 Composer의 PSR-4 오토로딩과 궁합이 좋습니다. 함수는 보통 composer.json의 autoload.files로 한 번에 로딩(또는 직접 include)합니다.

5) 타입, 예외, 테스트

  • 둘 다 매개변수·반환 타입 선언, 예외 던지기/처리가 가능합니다.

  • 테스트/모킹:

    • 순수 함수는 입력 → 출력 검증이 간단하고 빠릅니다.

    • 외부 의존(예: DB, HTTP)이 있는 로직은 인터페이스를 구현한 객체로 분리하면 DI로 쉽게 모킹/스텁 가능. 함수는 전역 의존을 쓰면 모킹이 까다롭습니다.

6) 성능 관점(요지)

  • 함수 호출메서드 호출보다 보통 약간 가볍습니다.

  • OPcache 등으로 차이는 줄어드는 편이고, 대부분의 웹 앱에서는 설계 품질이 성능보다 훨씬 큰 영향을 미칩니다. 미세 최적화는 마지막에.

7) 비교 요약표

관점

함수

객체

상태

기본 없음(클로저/정적변수로 예외)

프로퍼티로 상태 보유

추상화

동작만 캡슐화

상태+행위 동시 캡슐화

재사용

범용 유틸, 순수 계산에 최적

도메인 모델, 규칙/불변식 보존에 최적

의존성

전달 인자 위주

DI 컨테이너/인터페이스/생성자 주입과 궁합

테스트

순수 함수 매우 쉬움

인터페이스 모킹으로 유연

오토로딩

files/include 필요

PSR-4로 자동 로딩 용이

성능

보통 더 가벼움

약간의 오버헤드(보통 무시 가능)

8) 언제 함수를, 언제 객체를 쓸까

함수 추천

  • 수학적 계산, 변환, 파싱 등 입력→출력이 명확한 순수 로직

  • 전역 상태/부작용이 없는 작은 유틸리티

  • 고차 함수 스타일(콜백/매핑/필터링) 작성

객체 추천

  • 상태를 안전하게 관리해야 할 때(예: 누산기, 세션 스코프 도메인 모델)

  • 도메인 규칙/불변식을 타입으로 강제하고 싶을 때(값 객체, 엔티티)

  • 다형성/확장성(전략, 템플릿 메서드, 플러그인 구조)이 중요한 경우

  • 외부 자원(DB/HTTP/캐시) 의존을 인터페이스로 분리하고 테스트/교체 용이성 확보

9) 동일 기능을 함수/객체로 각각 구현해 보기

예: 할인 규칙 적용

함수형(전략을 콜백으로 전달)

/** @param callable(int):int $rule */
function applyDiscount(int $price, callable $rule): int {
    return max(0, $rule($price));
}

$percent10 = fn(int $p) => (int) round($p * 0.9);
$vipFlat   = fn(int $p) => max(0, $p - 5000);

echo applyDiscount(23000, $percent10); // 20700
echo applyDiscount(23000, $vipFlat);   // 18000

객체지향(전략 객체)

interface DiscountRule { public function apply(int $price): int; }

final class Percent implements DiscountRule {
    public function __construct(private float $rate) {}
    public function apply(int $price): int { return (int) round($price * (1 - $this->rate)); }
}

final class Flat implements DiscountRule {
    public function __construct(private int $amount) {}
    public function apply(int $price): int { return max(0, $price - $this->amount); }
}

function applyDiscountO(int $price, DiscountRule $rule): int {
    return max(0, $rule->apply($price));
}

echo applyDiscountO(23000, new Percent(0.1)); // 20700
echo applyDiscountO(23000, new Flat(5000));   // 18000

  • 간단히 쓰려면 함수/클로저가 짧고 편합니다.

  • 규칙이 많아지거나 검증/메타데이터/확장성이 필요하면 객체가 더 체계적입니다.

10) 콜러블(callable)과 비교/동등성

  • PHP의 많은 API는 callable을 받습니다. 가능한 형태:

    • 문자열 "strlen", 배열 [$obj, 'method'], 정적 메서드 "Class::method", 클로저, __invoke 객체.

  • 함수(클로저) 비교

    • 같은 클로저 객체인지 확인은 === 또는 spl_object_id()로 합니다.

    • “동일 로직인지”를 일반적으로 비교할 방법은 없습니다.

  • 객체 비교

    • ===는 같은 인스턴스인지(동일 참조) 비교.

    • ==는 보통 같은 클래스이고 모든 프로퍼티 값이 동일하면 참으로 취급됩니다.

    • 객체 대입은 핸들(참조처럼) 전달입니다. 복제는 clone을 사용합니다.

11) 실무 팁

  • 작은, 순수한 계산은 전역 유틸 함수로 두되 네임스페이스로 충돌을 피하세요.

  • 외부 자원 접근/부작용은 인터페이스 + 구현 객체로 감싸고, 애플리케이션 서비스에서 주입해 쓰세요.

  • **값 객체(Value Object)**를 적극 사용하면 도메인 불변식을 코드로 강제할 수 있습니다.

  • Composer 오토로딩을 쓴다면 클래스 중심 구조가 유지보수에 유리합니다.

  • 과도한 static 메서드 남용은 테스트/확장성 저하의 원인이 됩니다. 상태가 없다면 순수 함수, 상태/폴리시가 있다면 인스턴스 메서드로 분리하세요.

  • call_user_func()는 직접 호출보다 느립니다. 가능하면 직접 호출이나 **일급 호출자(…)**를 사용하세요.

12) 요약

  • 함수는 “작고 순수한 로직”을 빠르게 재사용하기에 최적.

  • 객체는 “상태 + 규칙”을 안전하게 묶고, 확장성과 테스트 용이성을 확보하는 데 최적.

  • 실제로는 둘을 함께 씁니다: 도메인 모델/서비스는 객체로, 작은 계산/유틸은 함수로.


댓글