라라벨의 엘로퀀트 ORM에서 값 객체 사용하기 2부

today 2019-02-08 face Posted by appkr turned_in Work & Play forum 0

지난 포스트에 이어서, 이번 포스트에서 두번째 세번째 방법을 설명합니다.

  1. 변경자와 접근자(Attribute Mutator & Accessor)를 이용하는 방법
  2. LOB(Large Object)를 이용하는 방법
  3. 참조(외래키)를 이용하는 방법

ORM Basic

본문 들어가기 전에 잠깐 딴 얘기할게요.

객체는 어느 순간 소멸됩니다. PHP는 프로세스가 종료되면 메모리 자원을 운영체제에 반환하면서 소멸되겠죠, 또는 가비지 콜렉션에 의해서든지요. 다른 프로세스에서 직전에 사용하던 객체의 상태를 이어서 사용하기 위해서 영속화라는 과정이 필요합니다. 대부분의 애플리케이션이 영속화의 도구로 DB를 사용하죠.

그런데 말입니다. 객체와 DB는 데이터를 다루는 방식이 완전히 다릅니다. 그래서 그림에서 보듯이 객체와 DB간의 차이점을 해결해주는 것이 ORM입니다.

라라벨의 엘로퀀트 ORM에서 값 객체 사용하기

today 2019-02-06 face Posted by appkr turned_in Work & Play forum 0

이 포스트에서는 엘로퀀트 모델에서 값 객체(Value Object)를 사용하는 몇 가지 방법을 고객 모델을 예제로 설명합니다.

  1. 변경자와 접근자(Attribute Mutator & Accessor)를 이용하는 방법
  2. LOB(Large Object)를 이용하는 방법
  3. 참조(외래키)를 이용하는 방법

먼저 이 포스트에서 사용할 용어를 설명하겠습니다.

모델

현실 세계의 복잡한 물체 또는 사상을 목적에 적합하도록 핵심만 간소화한 것. x:1 비행기 프라모델은 실물을 본따 만든 모형입니다. 웹 서비스를 개발하는 컨텍스트에서 모델은 비즈니스에 참여하는 여러 실체의 본질적인 특징만 뽑아 추상화한 것입니다. e.g. 고객 모델

엔티티와 값 객체

모델은 다시 1) “엔티티”와 2) “값 객체”로 분류할 수 있습니다. 고객#1과 고객#2는 고유한 식별자로 구분지을 수 있으므로 엔티티라 하는 반면, 고객의 주소는 전체 값으로만 서로 같고 다름을 식별할 수 있으므로 값 객체라 합니다. 값 객체는 밸류 오브젝트, 또는 줄여서 밸류라고 부르기도 합니다. e.g. 고객 1번과 고객 2번은 식별자에 의해 서로 다른 모델. 고객 1번과 2번은 가족인데, 이들의 주소 ‘서울특별시 강남구 삼성동 162-17’과 ‘서울특별시 강남구 삼성동 162-17’은 전체 문자열이 같으므로 같은 주소임.

원시 타입과 박스 타입

PHP 언어에서 int, float, string, array, bool과 같은 데이터 타입을 원시 타입(Primitive Type)이라 합니다. 64bit 환경에서는 int라 쓰지만 4byte가 아니라 8bytes 즉 2^64 메모리 공간을 차지하고, float도 double과 같은 표현 범위를 가집니다(see http://php.net/manual/en/language.types.php). 여튼, 아래 예처럼 고객을 의미할 때 customer:string처럼 원시타입으로 표현하기 보다는 customer:Customer로 쓰는 것이 더 많은 컨텍스트를 전달 할 수 있습니다. 여기서 후자를 박스 타입(Boxed Type)이라 부를 수 있습니다. 이를 다시 맥락에 따라서 엔티티나 값 객체로 부를 수도 있고요.

$c = '홍길동';

// v.s.

$c = new Customer('홍길동');

One to Many 조인에서 Many 쪽 최종 레코드만 조회하기

today 2018-11-20 face Posted by appkr turned_in Work & Play forum 0

고객 목록에 고객별 최근 주문 1건에 대한 요약 정보를 보여주세요.

라는 요구사항이 있습니다.

“고객” 객체와 “주문” 객체간의 관계는 다음과 같습니다.

+------------+           0..* +------------+
|  Customer  | <>-----------> |   Order    |
+------------+                +------------+

애플리케이션 레이어에서 구현한다면, ORM을 통해 구한 Collection<Customer>을 순회하면서, Customer객체의 멤버 필드인 Collection<Order>를 대상으로 최근 Order 객체만 필터링했거나, 적절한 순서로 정렬하여 뽑아 쓰기 쉽도록 했을겁니다. 요런 느낌으로요(검증 안된 Pseudo Code 입니다).

// Service Layer
public function listCustomers()
{
    return $customers->map(function (Customer $customer) {
        $sorted = $customer->orders->sortByDesc('id')->values();
        $customer->setRelation('orders', $sorted);
        return $customer;
    });
}

// Controller/View 등
foreach ($customers as $customer) {
    $lastOrder = $customer->orders->first();
    $lastOrder->order_number; // 최근 주문 번호   
}

문제점들이 눈에 띕니다.

  • 시간복잡도는 O(m x n)입니다.
  • CPU와 메모리를 혹사시킵니다.
  • Customer.orders: Collection<Order>를 미리 로드하지 않았다면, N + 1 문제가 발생합니다.
  • 이 외에도 제가 보지 못한 문제점들이 더 있을 겁니다…

읽기 전용 쿼리이므로 ORM을 쓰지 않아도 됩니다. 싸고, 빠르고, 안전하게 SQL만으로 뽑아내는 방법을 찾아봤습니다.

PHP 객체의 복제 특성

today 2018-11-04 face Posted by appkr turned_in Learn & Think forum 0

PHP 객체를 다른 변수에 할당(대입)하면, 객체 자체가 메모리 복제되어 새로운 변수에 할당되는 것이 아니라, 원본 객체가 담긴 메모리 번지만 참조됩니다(Like Pointer in C language).

  • $foo$foo2Foo 클래스의 인스턴스가 담긴, 같은 메모리 번지를 가리킵니다.
  • $foo3$foo 인스턴스로 부터 복제했으므로, 복제 시점의 $foo의 상태를 그대로 가져오지만, 서로 다른 객체입니다.
  • $foo4Foo 클래스의 새로운 인스턴스이므로, 당연히 $foo, $foo2, $foo3와 다른 객체입니다.

keyboard_arrow_up