단 30줄의 코드로 리파지토리 패턴을 구현하는 방법 free

2019-09-24

라라벨을 쓰는 가장 큰 이유 중 하나는 엘로퀀트 ORM 입니다. 한 번 써보면 다시는 날쿼리로 돌아가고 싶지 않게되죠.(최소한 저는 그랬습니다 ^^)


엘로퀀트 ORM은 매우 편리하지만 엘로퀀트 ORM의 모델은 일반적으로 이야기하는 모델에 리파지토리가 결합되어 있는 셈이라 리파지토리를 따로 분리해야한다는 의견도 많습니다. 실제로 예전 라라벨 공식 매뉴얼에 포함된 튜토리얼에서는 리파지토리 패턴을 안내하기도 했었죠.


리파지토리 패턴을 쉽게 구현하게 해주는 패키지도 여럿 있습니다. 1일 1식 라라벨 32호33호에 소개해드리고 xly.kr에도 적용하고 있는 라라벨 제네레이터도 리파지토리를 함께 만들어주는 패키지입니다. 이런 패키지들의 문제는 리파지토리 패턴을 쉽게 구현해주기는 하지만 엘로퀀트 ORM의 편리함을 많이 포기해야한다는 점입니다.


리파지토리를 이용하다보면 엘로퀀트 모델이 다 만들어놔서 그냥 가져다 쓰면 되는데 괜히 단계만 하나 늘려서 불편하다는 느낌을 받을 때가 있습니다. 이미 엘로퀀트 모델에 구현되어있는 기능들은 아래와 같은 일이 벌어지기 쉽죠.


// ProductController
public function index(ProductRepository $productRepository)
{
$products = $productRepository->all();
}

// ProductRepository
public function all()
{
return $this->model->all();
}

엘로퀀트 모델이 리파지토리 관련 기능을 가지고 있기 때문에 리파지토리를 만들 때 엘로퀀트 모델을 상속받아서 만들면 어떨까라고 생각하고 시도해본적이 있습니다. 저는 실력이 모자라서 당시에 구현에 실패했는데요, 이 아이디어를 단 30여줄의 간단한 코드로 구현한 Laravel Eloquent Repositories라는 패키지가 있었네요.


이 패키지를 사용하려면 컴포저로 추가만하면 됩니다. 별다른 설정은 필요 없습니다.


composer require mratiebatie/laravel-repositories

사용방법은 아래와 같습니다.



  1. Illuminate\Database\Eloquent\Model을 상속받아 리파지토리 클래스를 만든다.

  2. MrAtiebatie\Repository 트레이트를 추가한다.

  3. $model 프로퍼티에 사용할 모델을 넣어둔다.


코드로 보면 간단합니다. 아래는 패키지 홈페이지에 있는 예제입니다. 중간중간 한글로 커맨트를 추가했습니다.


<?php

namespace App\Repositories;

use MrAtiebatie\Repository;
use Illuminate\Database\Eloquent\Model;

/**
* Product repository
*/
class ProductRepository extends Model // 1. Illuminate\Database\Eloquent\Model을 상속
{
use Repository; // 2. MrAtiebatie\Repository 트레이트를 추가

/**
* The model being queried.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $model; // 3. $model 프로퍼티에 사용할 모델을 넣어둔다.

/**
* Constructor
*/
public function __construct()
{
$this->model = app(\App\Models\Product::class); // 3. $model 프로퍼티에 사용할 모델을 넣어둔다.
}

/**
* Find published products by SKU
* @param {int} $sku
* @return {Product}
*/
public function findBySku(int $sku): Product {
return this->whereIsPublished(1)
->whereSku($sku)
->first();
}
}

findBySku 같이 기존 엘로퀀트 모델에 없는 기능은 리파지토리에 추가하고, 원래 엘로퀀트 모델에 있는 기능은 리파지토리에서 바로 사용하면 됩니다. 마치 리파지토리를 엘로퀀트 모델 처럼 사용할 수 있는 것이죠.


리파지토리를 엘로퀀트 모델 처럼 사용할 수 있게 됨에 따라 아래와 같은 코드를 작성할 수도 있습니다.


// ProductController

public function someMethod(ProductRepository $productRepository)
{
$product = $productRepository->whereIsPublished(1)
->whereSku($sku)
->first();

...
}

하지만 이렇게 쓰면 굳이 리파지토리 클래스를 만든 이유가 없어지죠. 그래서 이 패키지 제작자는 다음과 같은 경험에서 우러나온 규칙을 지킨다고 하네요.



2개 이상의 엘로퀀트 매서드를 연결하게되면 리파지토리 매서드로 만들어라.



제가 보기에도 이 법칙을 따르는게 가장 현명해보입니다.


일단 저는 너무 마음에 들어서 바로 적용해서 사용하고 있고, 기존 라라벨 제네레이터가 만들어준 리파지토리는 걷어냈습니다. 다행히 애플리케이션의 크기가 크지 않아서 리파지토리를 교체하는 작업은 금새 끝났네요.


PS. 본문에서는 편의상 리포지토리 구현체를 주입했지만 리포지토리 인터페이스를 주입하는게 정석입니다. (알고 계시겠지만 혹시나 해서 ^^)


1일 1식 라라벨 59호

2019년 9월 24일

유료 구독자 전용 레터입니다.

한 달 1만원으로 매일 라라벨 관련 메일 받아보시고 과거 메일도 열람하세요. 일반 구독으로 공개글만 받아보실 수도 있습니다.

구독하기 버튼을 눌러주시면 구독과 동시에 xly에도 가입됩니다.

이현석

바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.