[1일 1식 라라벨] ERD를 그려주는 Laravel ER Diagram Generator

이 글은 2019년 7월 15일에 1일 1식 라라벨에 발행된 글입니다. 일부 시점이나 버전 정보가 블로그 발행시점과 다를 수 있습니다. 8월호 구독자를 모집하고 있습니다. 월 1만원으로 최신 라라벨 소식을 받아보세요.

충분한 여유를 가지고 프로젝트를 진행한다면 코드를 작성하기 전에 설계도 하고, ERD(Entity Relation Diagram)도 그리고 할 것이다. 하지만 세상은 언제나 나에게 호의적이지만은 않다. 사전에 계획 문서를 충분히 작성할 시간이 부족한 프로젝트를 맡게 될 수도 있고, 처음엔 그리 복잡하지도 않고 혼자 진행해서 문서를 만들지 않고 진행했을 수도 있다. 그런데 이후에 클라이언트가 문서를 요구하거나, 혹은 동료가 더 생겨서 코드를 빠르게 파악하는데 도움이 되는 문서가 필요해지는 경우가 있다. Laravel ER Diagram Generator는 이런 경우에 요긴하게 쓸 수 있는 패키지이다.

Laravel ER Diagram Generator는 모델 파일에 정의된 관계를 분석해서 ERD를 생성한다. 다음은 Laravel ER Diagram Generator로 생성한 Laravel.io의 ERD이다.(출처:  공식 저장소)

설치

우선 PHP 7.1 이상이 필요하고, graphviz가 설치되어 있어야 한다.

brew install graphviz

홈스테드

sudo apt-get install graphviz

윈도우 환경이면 공식 웹사이트에서 다운로드 한다.

패키지는 컴포저로 설치한다.

composer require beyondcode/laravel-er-diagram-generator --dev

개발에만 필요하고 프로덕션 환경에는 필요 없다. --dev 옵션을 잊지 말자.

사용

아티즌 커맨드로 사용한다.

php artisan generate:erd

옵션과 커스텀 설정은 저장소 문서를 참고하자.

소감

트위터에 코드를 먼저 짜고 분석을 나중에 하냐는 조롱조의 글도 있다. 하지만 앞서 언급한 것 처럼 내 손에 넘어오는 시점에 어떠한 문서도 없이 코드만 달랑 넘어오는 경우가 분명히 있고, 이럴 때는 코드를 기반으로 문서를 만들어주는 도구가 유용하게 쓰일 수 있다. 실무적인 용도 외에 학습용으로도 꽤 유용할 수 있다. 타인의 코드로 학습하기 때문에 코드에서 부터 분석을 시작할 수 밖에 없기 때문이다.

조금 아쉬운 점은 엘로쿼트 모델을 상속받은 엔티티만이 분석 대상에 포함된다는 점이다. 도메인 주도 개발을 지향하거나, 클린 아키텍처를 지향하는 경우 라라벨에 의존하지 않는 도메인 모델과 서비스를 핵심으로 삼을 수도 있는데, 이러면 이 패키지로는 분석이 불가능하다.

[1일 1식 라라벨] 특정 버전의 라라벨 설치하기

이 글은 2019년 7월 4일에 1일 1식 라라벨에 발행된 글입니다. 일부 시점이나 버전 정보가 블로그 발행시점과 다를 수 있습니다. 8월호 구독자를 모집하고 있습니다. 월 1만원으로 최신 라라벨 소식을 받아보세요.

며칠 전에 라라벨 5.8.27이 나왔다. 새로 설치하는 사람이라면 특별한 이유가 없는 한 최신 버전을 설치하는게 좋다. 메뉴얼에서 안내하는 대로 설치하면 자연히 최신 버전이 설치된다. 하지만 어떠한 이유에서건 과거의 버전을 설치할 필요가 생길 수 있다.

구글에서 laravel install specific version 으로 검색하면 특정 버전의 라라벨을 설치하는 방법을 어렵지 않게 찾을 수 있다.

문제는 저 글들이 공통적으로 알려주는 방법으로는 내가 원하는 정확한 버전을 설치할 수 없다는 점이다. 위 이미지에 나온 검색 결과 중 첫번째 글의 베스트 답변은 컴포저로 버전을 명시해서 설치하라는 것이다.

그럼 이 방법으로 직전 버전인 5.8.26을 설치해보자.

composer create-project laravel/laravel="5.8.26" myProject

5.8.26 버전의 패키지를 찾을 수 없다! 그렇다면 두번째 베스트 답변은 어떨까?

--5.8.26이라는 옵션은 존재하지 않는다며 설치 실패! 두 방법 모두 내가 원하는 버전이 설치 안되는 수준이 아니라 아예 설치 자체가 안된다.

laravel/laravel과 laravel/framework

왜일까? 위의 컴포저 명령문을 다시 한 번 자세히 살펴보면 laravel/laravel 패키지를 설치를 시도한다는걸 알 수 있다. laravel/laravel의 깃헙 저장소에 가서 릴리즈 내역을 보면 최신 버전이 5.8.17이다. 5.8.27은 어디로 간거지?

5.8.17로 설치해보면? 설치된다.

버전을 확인해볼까?

5.8.27!??

이런 현상이 벌어지는 이유는 라라벨의 구조 때문에다. 라라벨은 laravel/laravel과 laravel/framework 패키지를 가지고 있다. laravel/framework가 라라벨 코어 프레임워크이고 laravel/laravel은 laravel/framework를 이용한 뼈대(skeleton) 애플리케이션이다. 즉 laravel/laravel은 일종의 퀵스타트 예제인 셈이다. laravel/laravel과 laravel/framework의 관계는 appkr님의 글에도 잘 정리되어 있으니 참고하자.

laravel/laravel의 composer.json을 살펴보면 다음과 같이 laravel/framework에 의존하는 것을 확인할 수 있다.

"require": {
    "php": "^7.1.3",
    "fideloper/proxy": "^4.0",
    "laravel/framework": "5.8.*",
    "laravel/tinker": "^1.0"
},

“laravel/framework”: “5.8.*” 식으로 의존하기 때문에 5.8의 최신 버전이 설치된다. 따라서 특정 버전의 라라벨을 설치하는 방법은 간단하다. laravel/laravel을 clone 혹은 내려받기한 다음 composer.json에 laravel/framework의 정확한 버전을 지정한 뒤 composer install로 설치하는 것이다.

laravel/laravel clone 혹은 내려받기(설치 아님)

내려받기는 설명할게 없으니 git clone으로 진행해보자.

git clone git@github.com:laravel/laravel.git myProject

laravel/laravel에 기여하려고 클론한 것은 아니니 깃 정보를 제거하자.

cd myProject
rm -rf .git

composer.json을 열고 "laravel/framework": "5.8.*"을 "laravel/framework": "5.8.26" 으로 바꾼다.

"require": {
    "php": "^7.1.3",
    "fideloper/proxy": "^4.0",
    "laravel/framework": "5.8.26",
    "laravel/tinker": "^1.0"
},

컴포저로 의존 패키지들을 설치한다.

composer install

설치가 완료되면 버전을 확인해보자.

php artisan

5.8.26이 설치되었다.

컴포저에 익숙하신 분이라면 별 내용 아닐 수 있는데, 의외로 검색결과에 제대로 된 답변들이 없어서 정리해봤습니다. 필요할 때 도움이 되면 좋겠네요~ 🙂

2019년 7월 4일
1일 1식 라라벨

[1일 1식 라라벨] 모델 변경 이력을 자동으로 저장해주는 패키지 Revisionable

위키백과의 “라라벨” 문서가 수정된 내역

위키의 핵심 기능은 과거의 모든 변경 내역을 조회할 수 있고, 원하면 과거 버전으로 쉽고 되돌아갈 수 있는 것이라 생각한다.
간혹 위키 같이 과거의 변경 내역을 기록으로 남기고 조회하는 기능이 필요할 때가 있다. 내가 운영하는 카페에서는 사물함 관리에 이 기능이 필요했다. 사물함 대여자 정보를 업데이트 할 때 실수를 할 수 있으므로, 변경되기 전의 데이터가 어딘가에 남아있어야 했다. 당시에는 단순하게 똑같은 테이블을 하나 더 만들어서(lockers 테이블과 똑같은 lockers_logs 테이블을 만드는 식으로) 업데이트 전에 백업하는 식으로 구현했다.

이런 기능이 내게만 필요했던 건 아니었는지 크리스 듀엘(Chris Duell)이 Revisionable이라는 패키지를 만들었다. 이 패키지를 사용하면 모델에 변화가 있을때마다 자동으로 ‘누가’, ‘무엇을’, ‘언제’, ‘어떻게’ 수정했는지가 저장된다. 수정내역을 남기고 싶은 모델에 RevisionableTrait 트레이트를 사용한다고 선언하기만 하면 된다. 사물함 관리 기능 만들 때 이 패키지가 알았더라면 ㅠ

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Venturecraft\Revisionable\RevisionableTrait;

class Post extends Model
{
    use RevisionableTrait;
}

실전에서 어떻게 쓰이는지 보는게 가장 와 닿을 것 같다. 내 카페용 애플리케이션에 관심있는 사람들은 없겠지만 겸사겸사 한 번 적용해보겠다.

설치

컴포저로 설치한다.

composer require venturecraft/revisionable

설치가 완료되면 config/app.php 파일의 providers 항목에 RevisionableServiceProvider를 등록한다.

'providers' => [
    Venturecraft\Revisionable\RevisionableServiceProvider::class,
]

설정 파일과 마이그레이션 파일을 퍼블리싱한다.

php artisan vendor:publish --provider="Venturecraft\Revisionable\RevisionableServiceProvider"

// 제대로 진행된다면 아래와 같은 결과 메시지가 터미널에 출력된다.
Copied File [/vendor/venturecraft/revisionable/src/config/revisionable.php] To [/config/revisionable.php]
Copied Directory [/vendor/venturecraft/revisionable/src/migrations] To [/database/migrations]
Publishing complete.

Revisionable 패키지는 모든 모델의 수정내역을 하나의 테이블로 관리한다. 마이그레이션을 실행한다.

php artisan migrate

데이터가 어떻게 저장되는지 확인하기 위해 마이그레이션 파일을 살펴보자.

public function up()
{
    Schema::create('revisions', function ($table) {
        $table->increments('id');
        $table->string('revisionable_type');
        $table->integer('revisionable_id');
        $table->integer('user_id')->nullable();
        $table->string('key');
        $table->text('old_value')->nullable();
        $table->text('new_value')->nullable();
        $table->timestamps();

        $table->index(array('revisionable_id', 'revisionable_type'));
    });
}

revisionable_type은 수정한 모델의 타입이고 revisionable_id는 수정한 모델의 ID 값이다. 이런 방식으로 하나의 테이블에서 모든 모델의 변경사항을 다루는 걸 일대다 다형성 관계라고 한다. 다형성 관계에 익숙하지 않은 사람들은 매뉴얼을 참고하자.

key는 모델의 어떤 값이 변경되었는지를 저장하는 값이다. 예를 들어 Post 모델의 title이 수정되었다면 key에 ‘title’이 값으로 저장된다.

카페용 애플리케이션에서 사물함은 Locker 모델로 관리했다. Locker 모델에 RevisionableTrait를 끼워넣는다.

<?php

namespace App;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Venturecraft\Revisionable\RevisionableTrait;

class Locker extends Model
{
    use RevisionableTrait;

    ...이하 생략

Locker를 업데이트하는 LockerService::update()는 원래 아래와 같았다.

class LockerService
{
    public function update($request, $locker)
    {
        DB::transaction(function () use ($request, $locker) {
            $this->backup($locker);

            $inputs = $this->buildInputsForUpdate($request, $locker);

            $locker->update($inputs);
        });
    }

코드는 간단하다. 백업하고 업데이트할 데이터를 준비해서 업데이트한다.

이제 Revisionable이 수정내역을 관리해주기 때문에 백업하는 코드는 더이상 필요 없다. 그래서 $this->backup($locker); 줄을 지우고 LockerService::backup() 메소드를 통째로 제거했다.

테스트로 데이터를 하나 변경해봤다. 홍길동이 대여하고 있던 사물함을 장길산이 사용하게 된 시나리오다.

‘사용자’, ‘이용 시작 시점’, ‘이용 종료 시점’, ‘비밀번호’가 바뀌었다. revisions 테이블을 보면 아래와 같이 4개의 데이터가 추가되었다. 

변경 행위는 한 번인데, 변경된 필드마다 데이터가 하나씩 생성되는 점은 조금 아쉽다. 현재로서는 같은 행동이 원인이 되어 바뀌었는지 여부를 판단할 수 있는 유일한 방법은 revisions 테이블의 created_at 이 같은지 확인하는 방법 뿐이다. 수정내역을 사용자의 행동 단위로 묶을 수 있도록 식별값을 하나 더 추가했으면 어땟을까 싶다.

기본적인 기능 외에 아래와 같이 다양한 옵션을 제공한다. 필요할 때 패키지의 매뉴얼을 보고 활용하자.

  • 변경 뿐만 아니라 생성 기록도 저장하기
  • 저장할 필드, 혹은 제외할 필드 지정하기
  • 수정내역 저장 비활성화
  • 수정내역 최대 저장 갯수 지정
  • 출력 형식 변경하기

이 글은 7월 2일자 1일 1식 라라벨에 발행된 글입니다. 8월호 구독자를 모집하고 있습니다. 월 1만원으로 최신 라라벨 소식을 받아보세요.

[1일 1식 라라벨] 2019년 8월호 구독자 모집

1일 1식 라라벨 8월호 구독자를 모집합니다.

7월호 3주차가 끝났는데 다행히 펑크를 낸 날은 없었네요 🙂 용기내서 조금 더 해봅니다.

7월호에 발행했던 글들은 아래와 같습니다.

  • 5.8.25 버전부터 Response::assertViewHas()로 공유 데이터도 확인 가능
  • 모델 변경 이력을 자동을 저장해주는 패키지 Revisionable
  • 5.8.27 버전에 whereHasMorph() 추가됨
  • 특정 버전의 라라벨 설치하기
  • 라라벨 패키지를 선택할 때 물어야 할 8가지 질문
  • 테일러 오트웰은 왜 API 라우트를 routes/web.php에 작성했을까?
  • 테일러 오트웰이 사랑한 헬퍼, tap()
  • 5.8.28 새기능 1 – TestResponse에 tap 기능 추가
  • 5.8.28 새기능 2 – 컬렉션에 병합, 교체 기능 추가
  • 5.8.28 새기능 3 – Session에 only 메소드 추가
  • ERD를 그려주는 Laravel ER Diagram Generator
  • 의존성 충돌 해결하기
  • 라라벨 5.8.29 릴리즈
  • 라라벨 리퀘스트 라이프사이클
  • 라라벨에서 TailwindCSS 사용하기

많은 신청 바랍니다~ 신청 링크 https://forms.gle/ysHkKGdhhKka2ECK8