코드이그나이터4 마크다운 블로그 리팩토링 - 6 - 글 수정, 삭제 서비스 레이어 분리하기

고은연·2021년 11월 9일
0

글 수정 서비스 레이어 분리하기

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

글 수정은 글 생성 + 글 조회 기능이 합쳐져 있죠. 이미 엔티티의 역할은 정해져 있기 때문에 서비스와 뷰, 컨트롤러를 수정하겠습니다.

글 수정 서비스 메소드 추가

글 서비스에 아래의 메소드를 추가합니다.
app/Services/PostService.php

public function update($post, $new_post_data)
{
    $post->fill($new_post_data); // (1)
    $postModel = new PostsModel();
    $isSuccess = $postModel->save($post); // (2)
    return [$isSuccess, $postModel->errors()];
}

(1) 기존에 존재하는 PostEntity 타입 변수 $post에 HTTP로 입력받은 배열 $new_post_data를 채웁니다. 저장하기 전의 데이터는 $new_post_data에 의해 대체됩니다.

(2) 모델을 통해 데이터를 저장합니다. $post에는 이미 주 키에 해당하는 post_id 항목이 존재하므로 update가 실행됩니다.

글 수정 엔드포인트 바꾸기

글 수정 컨트롤러의 edit 메소드를 아래처럼 변경합니다. 특별한 부분은 없으니 설명은 생략하겠습니다.
app/Controllers/Post.php

public function edit($post_id)
{
    if (LoginHelper::isLogin() === false) {
        return $this->response->redirect("/post");
    }

    $postService = PostService::factory();
    $post = $postService->find($post_id);
    if (!$post) {
        return $this->response->redirect("/post");
    }

    if ($postService->isAuthor($post, LoginHelper::memberId()) === false){
        return $this->response->redirect("/post");
    }

    if ($this->request->getMethod() === "get") {
        return view("/post/create",[
            'post_data' => $post
        ]);
    }

    list($isSuccess, $errors) = $postService->update($post, $this->request->getPost());
    if ($isSuccess){
        $this->response->redirect("/post/show/$post_id");
    }else{
        return view("/post/create",[
            'post_data' => $post,
            'errors' => $errors
        ]);
    }
}

글 생성 뷰 바꾸기

글 생성 뷰를 엔티티를 사용하도록 수정해 보겠습니다. 글 조회 뷰와 마찬가지로 배열[] 문법을 객체 -> 문법으로 변경합니다.
app/Views/post/create.php

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


<h2 class="title mb-2">글쓰기</h2>
<form method="POST">
    <div class="form-group"> <!-- (1) -->
        <label for="title">제목을 알려주세요</label>  <!-- (2) -->
        <input type="text" class="form-control" id="title" name="title" value="<?= $post_data->title ?? "" ?>" placeholder="제목" required />  <!-- (3) -->
        <small id="titlehelp" class="form-text text-muted">제목은 4-100글자 사이입니다.</small>  <!-- (4) -->
    </div>
    <div class="form-group">
        <label for="content">내용을 입력하세요</label>
        <textarea name="content" class="form-control" id="content" rows="10" required><?= $post_data->content ?? "" ?></textarea>  <!-- (5) -->
    </div>
    <p style="text-align: right;">
        <input type="submit" class="btn btn-primary" value="저장">
        <a href="/post/" class="btn btn-info">취소</a>
    </p>
    <?php
    if (isset($errors)) {
        echo "<ul>";
        foreach ($errors as $val) {
            echo "<li>$val</li>";
        }
        echo "</ul>";
    }
    ?>
</form>

<?= $this->endSection() ?>

글 생성 엔드포인트 바꾸기

글 수정은 글 생성과 뷰를 공유하죠. 따라서 글 수정 엔드포인트가 엔티티를 사용해서 뷰에 데이터를 전달하면, 글 생성 엔드포인트도 엔티티를 사용하도록 변경해야 합니다.
다행히도 글 생성에는 엔티티를 사용하는 곳이 한 곳만 있네요. 바로 유효성 검사에 실패할 경우 사용자가 입력한 데이터를 다시 보여주는 부분입니다. Post 컨트롤러의 create 엔드포인트 일부를 수정하겠습니다. 기존 코드는 아래와 같습니다.
app/Controllers/Post.php

return view("/post/create", [
    'post_data' => $this->request->getPost(),
    'errors' => $errors
]);

배열을 PostEntity 타입으로 바꿉시다. 변경된 코드는 아래와 같습니다.

$postEntity = new PostEntity();
$postEntity->fill($this->request->getPost());
return view("/post/create", [
    'post_data' => $postEntity,
    'errors' => $errors
]);

글 삭제 서비스 레이어 분리하기

이어서 글 삭제도 레이어를 분리해 보겠습니다. 삭제의 경우에는 특별히 post 객체를 뷰에 전달하거나 하는 일이 없기 때문에 모든 객체 관리를 서비스에 넣는 방법으로 해 볼께요.

글 삭제 서비스 메소드 추가

글 삭제 기능을 작성합니다. 구체적으로 글을 찾고, 작성자인지 검사하고, 삭제하는 비즈니스의 흐름이 작성되어 있습니다.
app/Services/PostService.php

public function delete($post_id, $member_id)
{        
    $post = $this->find($post_id);

    if (!$post) {
        return false;
    }

    if ($this->isAuthor($post, $member_id) === false) { // (1) 
        return false;
    }

    $postModel = new PostsModel();
    $postModel->delete($post->post_id);

    return true;
}

(1) 작성자인지 확인하는 메소드는 내부의 is_author 메소드를 사용해서 검사합니다. 이렇게 메소드 하나를 만들어 놓으면 내부적으로 작성자인지 확인하는 구현 스펙이 변경되어도 한군데만 수정하면 되기 때문에 수정 공수가 적게 들어갑니다.

글 삭제 엔드포인트 수정

글 삭제 엔드포인트는 무척 간단해집니다.
app/Controllers/Post.php

public function delete()
{
    if (LoginHelper::isLogin() === false) { // (1)
        return $this->response->redirect("/post");
    }

    if ($this->request->getMethod() !== "post"){
        return $this->response->redirect("/post");
    }

    $post_id = $this->request->getPost('post_id');
    PostService::factory()->delete($post_id, LoginHelper::memberId());
    return $this->response->redirect("/post");
}

로그인 여부 확인과 HTTP 메소드 체크 등 HTTP 레이어에서 처리해야 할 일을 제외하고 모든 비즈니스 로직은 단 한줄, PostService를 통해 일어납니다.

PostService::factory()->delete($post_id, LoginHelper::memberId());
profile
중년 아저씨. 10 + n년차 백엔드 개발자. 스타트업과 창업, 솔로프리너와 1인 기업에 관심 많아요.

0개의 댓글