코드이그나이터4 마크다운 블로그 리팩토링 - 5 - 글 조회 엔티티/ 서비스 레이어 분리하기

글 조회 엔티티/ 서비스 레이어 분리하기

이번 챕터의 코드는 https://github.com/koeunyeon/ci4/commits/refacto-post-read-entity-service 에 있습니다.


글 조회 엔티티 / 서비스 레이어 분리하기에서는 엔티티, 서비스, 그리고 뷰를 변경합니다.

글 서비스에 글 조회 메소드 추가

개별 글 정보를 컨트롤러에서 분리해 내기 위해 서비스에 메소드를 추가하겠습니다.
app/Services/PostService.php

public function find($post_id){
    $postModel = new PostsModel();
    return $postModel->asObject("App\Entities\PostEntity")->find($post_id); // (1)
}

(1) 개별 글을 조회합니다.
asObject(클래스경로) 메소드는 모델의 정의된 리턴값의 타입을 변경하고 싶을 때 사용합니다. 우리가 만들었던 PostsModel 클래스는 리턴 타입을 선언하지 않았으므로 기본값인 배열이었습니다. 배열을 PostEntity 타입으로 변경하기 위해 asObject 메소드를 사용했습니다.
배열 형식을 엔티티로 변경하는 이유는, 배열은 메소드를 가질 수 없기 때문입니다. 구조체와 객체의 차이처럼 메소드를 가질 수 없는 타입은 데이터를 조작하기 위해 외부 함수의 힘을 빌릴 수밖에 없죠. 데이터를 조작하는 방식을 객체 내부로 변경하기 위해 배열을 엔티티로 바꿉니다.
모델에서 리턴 타입을 변경해도 되지 않냐고 반문할 수도 있습니다. 물론 그것도 가능합니다만, 이미 수정, 삭제 등 다른 기능들이 모델의 리턴값으로 배열 타입을 기대하고 있으므로, 모델의 리턴 타입을 변경하는 순간 다른 기능들이 동작하지 않습니다. 원치 않는 부수 효과(사이드 이펙트)가 생기는 셈이기 때문에 차례로 변경하고 모든 기능에서 배열을 사용하는 부분이 제거되고 나면 모델의 리턴 타입을 배열 => postEntity로 변경하겠습니다.
만약 리턴 타입이 객체 형식으로 지정되어 있어도 배열 형식으로 바꾸고 싶다면 asArray 메소드를 사용할 수도 있습니다.

글 엔티티에 작성자인지 확인하는 메소드 추가

데이터 관점에서 바라보면, 글의 작성자인지 확인하는 역할은 개별 글을 나타내는 엔티티가 맡고 있는 것이 올바릅니다. 따라서 엔티티에 작성자인지 확인하는 메소드를 추가하겠습니다.
app/Entities/PostEntity.php

public function isAuthor($member_id){ // (1)
    return $this->attributes['author'] == $member_id;
}

(1) 사용자의 정보는 PostEntity가 관리할 책임이 없기 때문에 사용자 정보 member_id는 파라미터로 입력받습니다.
엔티티는 그저 자신의 속성인 author와 파라미터로 입력받은 사용자가 동일한지 확인해 줍니다.

엔티티를 작성할 때 주의해야 할 건, 엔티티와 데이터베이스를 분리해서 생각하는 겁니다. 데이터베이스와 상관 없이 개별 인스턴스가 작동하는 도메인 로직에만 집중하세요.

글 서비스에 작성자인지 확인하는 메소드 추가

이미 글 엔티티에 작성자인지 확인하는 메소드가 있으므로, 글 서비스는 글 엔티티를 이용해서 작성자인지 확인합니다. 글 서비스에 아래의 메소드를 추가하겠습니다.
app/Services/PostService.php

public function isAuthor($post, $member_id){ // (1)
    return $post->isAuthor($member_id);
}

(1) 파라미터는 PostEntity 타입의 $post 객체, 그리고 사용자 식별자입니다. 이 때 $post 객체에는 이미 글 정보가 설정된 상태이므로 글 엔티티에 작성자가 맞는지 확인할 수 있습니다.

글 조회 엔드포인트 수정하기

글 조회 엔드포인트를 아래와 같이 변경합니다.
app/Controllers/Post.php

// 조회
public function show($post_id)
{
    $postService = PostService::factory(); // (1)
    $post = $postService->find($post_id); // (2)
    if (!$post) {
        return $this->response->redirect("/post");
    }

    $isAuthor = $postService->isAuthor($post, LoginHelper::memberId()); // (3)

    return view('/post/show',[
        'post' => $post,
        'isAuthor' => $isAuthor
    ]);
}

(1) 글 서비스를 팩토리를 통해 생성합니다. 글 서비스는 (2) 글을 조회할 때도 사용하고, 작성자인지 확인할 때 (3) 도 사용합니다.

(2) 글 서비스를 이용해서 글 정보를 가져옵니다.

(3) 글 서비스를 통해 작성자인지 여부를 확인합니다.

이제 글 조회 엔드포인트는 데이터베이스에 직접 접속하는 코드가 없어졌습니다. 모두 서비스를 통해 데이터에 접근합니다.

글 조회 뷰 수정하기

우리는 글 뷰(app/Views/post/show.php)에서 배열 형식을 사용했었죠. 하지만 이제 조회에서 PostEntity 타입을 사용하므로, 수정해 보겠습니다.
뷰 파일의 $post[키] 처럼 배열로 접근하는 속성을 멤버변수에 접근하는 코드인 $post->키 형식으로 모두 바꾸면 아래와 같습니다.
app/Views/post/show.php

<?= $this->extend('/post/layout') ?>
<?= $this->section('content') ?>

<header class="blog-post-header">
    <h2 class="title mb-2"><?= esc($post->title) ?></h2>
    <div class="meta mb-3">
        <span class="date"><?= esc($post->created_at) ?></span>
    </div>
</header>

<div class="blog-post-body">
    <?= $post->html_content ?>
</div><!--//blog-comments-section-->
<?php if ($isAuthor) : ?>
<div style="text-align: right;">
    <form method="POST" action="/post/delete"> <!-- (2) -->
        <a href="/post/edit/<?= $post->post_id ?>" class="btn btn-info">수정</a>
        <input type="hidden" name="post_id" value="<?= $post->post_id?>" />
        <input type="submit" class="btn btn-danger" value="삭제">
    </form>
</div>
<?php endif ?>
<?= $this->endSection() ?>

엔드 투 엔드 테스트

테스트 코드는 글 관련 코드를 모두 수정 후 작성하겠습니다.
일단 localhost:8080/post/show/1 에서 잘 보이는지 직접 확인해 볼께요.

엔드 투 엔드 테스트로는 이상 없어보입니다.

좋은 웹페이지 즐겨찾기