코드이그나이터4 마크다운 블로그 MVP 만들기 - 6 - 프로젝트에 레이아웃 적용하기

고은연·2021년 10월 22일
1

이번 챕터의 코드는 https://github.com/koeunyeon/ci4/tree/blog-layout 에 있습니다.

레이아웃 분리하기

블로그 글 목록, 블로그 개별 글 페이지를 둘 다 눌러보시면 공통으로 사용되는 부분이 있습니다. 바로 좌측에 보이는 소개 페이지입니다.


브라우저 인스펙터로 보면 <header> 영역임을 알 수 있습니다.


또한 <footer> 영역도 반복되죠.

눈에 보이지는 않지만 <head> 영역도 반복됩니다.
<head>, <header>, <footer> 영역을 공통 레이아웃으로 빼내겠습니다. app/Views/post/layout.php 파일을 만들고 아래 내용으로 대체합니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Bootstrap 4 Blog Template For Developers</title>
    <!-- Meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Blog Template">
    <meta name="author" content="Xiaoying Riley at 3rd Wave Media">
    <link rel="shortcut icon" href="favicon.ico">

    <!-- FontAwesome JS-->
    <script defer src="/assets/fontawesome/js/all.min.js"></script>

    <!-- Theme CSS -->
    <link id="theme-style" rel="stylesheet" href="/assets/css/theme-1.css">

</head>
<body>
<header class="header text-center">
    <h1 class="blog-name pt-lg-4 mb-0"><a href="index.html">Anthony's Blog</a></h1>

    <nav class="navbar navbar-expand-lg navbar-dark" >

        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-controls="navigation" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div id="navigation" class="collapse navbar-collapse flex-column" >
            <div class="profile-section pt-3 pt-lg-0">
                <img class="profile-image mb-3 rounded-circle mx-auto" src="/assets/images/profile.png" alt="image" >

                <div class="bio mb-3">Hi, my name is Anthony Doe. Briefly introduce yourself here. You can also provide a link to the about page.<br><a href="about.html">Find out more about me</a></div><!--//bio-->
                <ul class="social-list list-inline py-3 mx-auto">
                    <li class="list-inline-item"><a href="#"><i class="fab fa-twitter fa-fw"></i></a></li>
                    <li class="list-inline-item"><a href="#"><i class="fab fa-linkedin-in fa-fw"></i></a></li>
                    <li class="list-inline-item"><a href="#"><i class="fab fa-github-alt fa-fw"></i></a></li>
                    <li class="list-inline-item"><a href="#"><i class="fab fa-stack-overflow fa-fw"></i></a></li>
                    <li class="list-inline-item"><a href="#"><i class="fab fa-codepen fa-fw"></i></a></li>
                </ul><!--//social-list-->
                <hr>
            </div><!--//profile-section-->

            <ul class="navbar-nav flex-column text-left">
                <li class="nav-item active">
                    <a class="nav-link" href="index.html"><i class="fas fa-home fa-fw mr-2"></i>Blog Home <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="blog-post.html"><i class="fas fa-bookmark fa-fw mr-2"></i>Blog Post</a>
                </li>
            </ul>

            <div class="my-2 my-md-3">
                <a class="btn btn-primary" href="https://themes.3rdwavemedia.com/" target="_blank">Get in Touch</a>
            </div>
        </div>
    </nav>
</header>
<div class="main-wrapper">
    <section class="blog-list px-3 py-5 p-md-5">
        <div class="container">

    <?= $this->renderSection('content') ?>

    <footer class="footer text-center py-2 theme-bg-dark">
        <!--/* This template is released under the Creative Commons Attribution 3.0 License. Please keep the attribution link below when using for your own project. Thank you for your support. :) If you'd like to use the template without the attribution, you can buy the commercial license via our website: themes.3rdwavemedia.com */-->
        <small class="copyright">Designed with <i class="fas fa-heart" style="color: #fb866a;"></i> by <a href="http://themes.3rdwavemedia.com" target="_blank">Xiaoying Riley</a> for developers</small>
    </footer>

</div><!--//main-wrapper-->

<!-- Javascript -->
<script src="/assets/plugins/jquery-3.4.1.min.js"></script>
<script src="/assets/plugins/popper.min.js"></script>
<script src="/assets/plugins/bootstrap/js/bootstrap.min.js"></script>

<!-- Page Specific JS -->
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.14.2/highlight.min.js"></script>

<!-- Custom JS -->
<script src="/assets/js/blog.js"></script>



</body>
</html>

67라인의 <?= $this->renderSection('content') ?> 항목만 제외하면 일반 HTML입니다.

이제 목록 컨텐츠를 레이아웃을 사용하도록 변경하겠습니다.
app/Views/devblog/list.php

<?= $this->extend('/post/layout') ?>
<?= $this->section('content') ?>
            <div class="item mb-5">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-7.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Lorem Ipsum Dolor Sit Amet</a></h3>
                        <div class="meta mb-1"><span class="date">Published 3 months ago</span><span class="time">5 min read</span><span class="comment"><a href="#">4 comments</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->
            <div class="item mb-5">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-8.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Lorem Ipsum Dolor Sit Amet</a></h3>
                        <div class="meta mb-1"><span class="date">Published 4 months ago</span><span class="time">3 min read</span><span class="comment"><a href="#">2 comments</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->

            <div class="item mb-5">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-9.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Nemo Enim Ipsam Voluptatem </a></h3>
                        <div class="meta mb-1"><span class="date">Published 4 months ago</span><span class="time">8 min read</span><span class="comment"><a href="#">7 comments</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->
            <div class="item mb-5">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-10.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Perspiciatis Unde Omnis </a></h3>
                        <div class="meta mb-1"><span class="date">Published 5 months ago</span><span class="time">15 min read</span><span class="comment"><a href="#">3 comments</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->

            <div class="item mb-5">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-11.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Duis Arcu Tortor</a></h3>
                        <div class="meta mb-1"><span class="date">Published 5 months ago</span><span class="time">10 min read</span><span class="comment"><a href="#">0 comment</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->

            <div class="item">
                <div class="media">
                    <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-12.jpg" alt="image">
                    <div class="media-body">
                        <h3 class="title mb-1"><a href="blog-post.html">Heading Vestibulum Ante Ipsum Primis</a></h3>
                        <div class="meta mb-1"><span class="date">Published 6 months ago</span><span class="time">2 min read</span><span class="comment"><a href="#">8 comments</a></span></div>
                        <div class="intro">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies...</div>
                        <a class="more-link" href="blog-post.html">Read more &rarr;</a>
                    </div><!--//media-body-->
                </div><!--//media-->
            </div><!--//item-->

            <nav class="blog-nav nav nav-justified my-5">
                <a class="nav-link-prev nav-item nav-link rounded-left" href="#">Previous<i class="arrow-prev fas fa-long-arrow-alt-left"></i></a>
                <a class="nav-link-next nav-item nav-link rounded-right" href="#">Next<i class="arrow-next fas fa-long-arrow-alt-right"></i></a>
            </nav>

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

잘 나오는지 확인해 봅시다. http://localhost:8080/devblog/list 주소에 문제가 없는지 확인하세요.

프로젝트 목록 페이지에 템플릿 적용하기

목록 페이지부터 수정해 봅시다. 우리는 이미 레이아웃을 분리한 샘플 파일이 있으므로 상대적으로 수월하게 수정할 수 있습니다.
목록 뷰를 수정할께요. <li> 항목이 사라지고 <div class="item mb-5"> 항목이 들어왔습니다.
app/Views/post/index.php

<?= $this->extend('/post/layout') ?>
<?= $this->section('content') ?>
<?php
foreach($post_list as $post){
    ?>
    <div class="item mb-5">
        <div class="media">
            <!-- <img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="/assets/images/blog/blog-post-thumb-7.jpg" alt="image"> --> <!-- (1) -->
            <div class="media-body">
                <h3 class="title mb-1">
                    <a href="<?= site_url("/post/show/{$post['post_id']}") ?>"><?= $post['title'] ?></a> <!-- (2) -->
                </h3>
                <div class="meta mb-1">
                    <span class="date"><?= $post['created_at'] ?></span> <!-- (3) -->
                    <!-- <span class="comment"><a href="<?= site_url("/post/show/{$post['post_id']}#comment") ?>">4 comments</a></span></div> -->  <!-- (4) -->
                <div class="intro"><?= $post['content'] ?></div>  <!-- (5) -->
                <a class="more-link" href="<?= site_url("/post/show/{$post['post_id']}/#content") ?>">Read more &rarr;</a>  <!-- (6) -->
            </div><!--//media-body-->
        </div><!--//media-->
    </div><!--//item-->
    <?php
}
?>
<?php $pager->setPath("/post"); ?>
<?= $pager->links() ?>
<?= $this->endSection() ?>

템플릿이 잘 적용되었는지 http://localhost:8080/post 에서 확인해 봅시다.

만약 아무것도 나오지 않는다면 글이 없는 것을 의심해 볼 수 있습니다. http://localhost:8080/post/create 에서 데이터를 몇 개 넣고 확인해 봅시다.

잘 나오는 것을 확인했으므로 코드를 확인하겠습니다.

(1) 현재는 글에 이미지를 첨부하는 기능이 없으므로 주석처리합니다.

(2) <h3> 항목은 제목을 나타냅니다.
site_url 함수는 코드이그나이터4의 헬퍼 함수로, 전역으로 로딩됩니다. 상대경로를 파라미터로 입력받아서 절대 경로를 반환합니다.
사이트의 주소는 /.env 파일의 app.baseURL에 의존합니다. 예를 들어 app.baseURL = 'http://localhost:8080'이 설정되어 있다면 site_url("/post/show/1") 함수는 http://localhost:8080/post/show/1 문자열을 반환합니다.

(3) 입력 시간을 보여줍니다.

(4) 댓글은 현재 사용하지 않으므로 주석처리합니다.

(5) 내용을 보여줍니다.

(6) 더보기를 누를 경우 곧바로 상세 페이지의 본문으로 이동하기 위해 프래그먼트(#content)가 사용되었습니다.

프로젝트 상세 페이지에 템플릿 적용하기

상세 페이지도 목록 페이지처럼 blog-post.html 파일 일부분을 잘라서 붙이면 됩니다.
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">
    <?= nl2br(esc($post['content'])) ?>
</div><!--//blog-comments-section-->
<?= $this->endSection() ?>

상세 페이지도 잘 나오는지 확인합니다. http://localhost:8080/post/show/1 주소에서 확인해 봅시다.

프로젝트 글 쓰기 페이지에 템플릿 적용하기

글 쓰기 페이지는 블로그 테마에 없습니다. 그래서 직접 만들어야 합니다.
다만 공통의 레이아웃은 공유하기 때문에, 몇가지만 수정하겠습니다.

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;">  <!-- (6) -->
        <input type="submit" class="btn btn-primary" value="저장">  <!-- (7) -->
    </p>
    <?php
    if (isset($errors)) {
        echo "<ul>";
        foreach ($errors as $val) {
            echo "<li>$val</li>";
        }
        echo "</ul>";
    }
    ?>
</form>

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

코드를 확인해 보겠습니다.
(1) 부트스트랩에서 폼(form)안의 모양을 예쁘게 꾸미려면 반드시 form-group 클래스로 감싸야 합니다. 부트스트랩 폼에 대해 더 알고 싶으시다면 Forms - https://getbootstrap.com/docs/4.0/components/forms/를 참고하세요.

(2) label 태그는 HTML에서 입력창을 설명하는 태그입니다. 주로 설명을 클릭하면 입력창으로 커서를 옮겨갈 때 사용됩니다. "제목을 알려주세요"를 클릭하면 title 항목으로 커서가 옮겨집니다.

(3) 부트스트랩에서 입력창의 클래스는 form-control입니다.
기존에 name 항목만 있던 것과 달리 id='title' 항목이 추가되었는데, 이는 (2)label태그가 id를 기준으로 이동하기 때문입니다.
placeholder는 HTML5에서 추가된 항목으로, 입력 상자 위에 흐릿한 글씨로 내용을 알려주는 것을 말합니다.
required 속성은 반드시 입력되어야 한다는 뜻으로, 저장 버튼을 눌러도 속성이 비어있으면 폼이 submit되지 않고 경고가 나오게 됩니다.

(4) small 태그는 입력창을 설명할 때 사용했습니다.

(5) textarea태그에도 id 태그와 required 태그가 추가되었습니다. 용도는 제목과 동일합니다.
또한 rows 항목이 추가되었는데, textarea에서 rows 속성은 줄(행) 수를 나타냅니다. 내용을 입력하는 란이므로 여유있게 10칸으로 설정했습니다.

(6) 스타일 중 text-align 속성은 하위 엘레먼트의 정렬을 나타냅니다. text-align:right라고 하면 하위 버튼이 오른쪽으로 정렬됩니다.

(7) 부트스트랩에서 버튼을 꾸미려면 btn btn-primary 속성을 주면 됩니다. 버튼의 색깔에 대해서는 부트스트랩 버튼 https://getbootstrap.com/docs/4.0/components/buttons/을 참고하세요.

브라우저에서 확인해 보겠습니다.
http://localhost:8080/post/create 로 접속해 보세요.

profile
중년 아저씨. 10 + n년차 백엔드 개발자. 스타트업과 창업, 솔로프리너와 1인 기업에 관심 많아요.

2개의 댓글

comment-user-thumbnail
2021년 12월 10일

하.. 저두 ci 이용해서 블로그 제작 하고 있는데요 ㅋㅋ
원래는 티스토리 쓰고 있다가 쓴지 이틀 삼일째 로그인 버그 있어서.. 답답해서 직접 구현하고 있습니다 ㅠㅠ
암튼 글 참고해서 열심히 만들어볼게요 ㅋㅋ

1개의 답글