본문 바로가기
개발/PHP

MVC모델을 적용한 블로그(게시판) 만들기 - 1. Front Controller(요구사항 받아들이기)

by 똘똘이박사 2018. 6. 21.

PHP MVC 모델을 적용한 블로그 만들기



1. Front Controller (요구사항 받아들이기)


PHP MVC 모델을 적용한 블로그는 아래와 같은 구조를 가지고 있습니다.


                  





.htaccess 파일 만들기


이전 개념잡기 포스팅에서 말씀 드렸던 것처럼 MVC모델은 모든 요구사항을 한 곳으로 모일 수 있게 해야 합니다.

따라서 .htaccess 파일을 만들어 모든 내용을 index.php 로 들어 올 수 있게끔 만들어 주어야 합니다.


/.htaccess


Options -MultiViews

RewriteEngine On

Options -Indexes

RewriteCond %{REQUEST_FILENAME} !-d

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-l

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]


가장 중요한 내용은 가장 아랫줄 입니다.


     localhost:8080/home/index/board/


라는 URL은


     index.php?url=home/index/board 


와 같은 형식으로 넘겨지게 됩니다.


따라서 index.php는  GET방식으로 url 이라는 파라미터에 받게 되며

url 파라미터에는 home/index/board 라는 값을 가지고 있습니다.





index.php - 모든 페이지의 시작


index.php 로 모든 요청사항이 모인다고 말씀드렸었지만

사실 index.php 파일은 별 내용이 없습니다.


index.php


<?php


require_once 'application/libs/config.php';

require_once _ROOT.'/application/vender/autoload.php';


new \application\libs\application();

 

정말 중요한 내용은

이번 application 이라는 객체 안에서 이루어 지게 됩니다.


index.php 에서 삽입하는 파일은 다음과 같습니다.


 - config.php : 여러 가지 상수들을 정의 합니다.


 - autoload.php : 최근의 PHP 개발 트렌드에 맞추어 모든 클래스는 자동으로 불러 올 수 있도록 autoload 를 사용하였습니다.

                    (autoload 에 대해 더 알고 싶으신 분들은 이 포스팅을 참조해 주세요)






config.php


config.php 는 여러가지 환경 변수에 해당하는 값들을 상수로 정의할 예정입니다.


우선은 간단히 아래와 같이 root directory 를 _ROOT 라는 이름으로 등록해 놓습니다.


application/libs/config.php


<?php


define('_ROOT', $_SERVER['DOCUMENT_ROOT']);







autoload.php - 자동으로 클래스를 불러오자!


PHP의 최근 개발 트렌드는 객체지향 개발 방법을 따른 다는 것입니다.

이른바 '모던 PHP' 라는 것입니다.

모두 이 방법을 따를 필요는 없지만

MVC 모델은 객체지향적 개발 방법론을 따릅니다.


따라서 우리도 class를 기반으로 하는 개발 방법을 따를 예정입니다.

객체지향 프로그래밍 방법론을 여기서 깊이 있게 다루지는 않습니다.

하지만, 기본적으로 파일 하나에 하나의 클래스를 지게 되어야 한다는 것만 짚고 넘어가도록 하겠습니다. 

(파일 하나에 여러 클래스가 존재 할 수는 있지만 권장하지 않는 방법입니다.)


autoload를 사용하지 않고 require 등을 사용해 클래스를 불러올 수 있지만

한 프로그램에서 수많은 클래스를 불러온다고 했을때, 불러오는 클래스 만큼 수많은 require 문이 필요 하게 됩니다.

덩치가 큰 프로그램일 경우는 상당히 복잡해 질 수 있습니다.


autoload는 이런 복잡함을 해결해 줄 수 있는 방법입니다.


하지만 autoload는 사용자가 직접 세부 내용을 정의해 주어야 한다는 불편함? 이 있습니다.

프로그램의 전체적인 구조를 개발자가 직접 구성할 수 있다는 잇점이 있을 수 있지만

오래전부터 PHP를 절차지향적 방법으로 개발해 오던 개발자에게는 어려운 내용일 수 있습니다.

(저도 이해하는데 상당히 애를 먹었네요.)


application/vender/autoload.php


<?php


spl_autoload_register(function ($path) {

    $path = str_replace('\\','/',$path);


    $paths = explode('/', $path);


    if (preg_match('/model/', strtolower($paths[1]))) {

        $className = 'models';

    } else if (preg_match('/controller/',strtolower($paths[1]))) {

        $className = 'controllers';

    } else {

        $className = 'libs';

    }


    $loadpath =  $paths[0].'/'.$className.'/'.$paths[2].'.php';

    echo 'autoload $path : ' . $loadpath . '<br>';


    if (!file_exists($loadpath)) {

        echo " --- autoload : file not found. ($loadpath) ";

        exit();

    }

    require_once $loadpath;

});


소스 중간에 보면 model과 controller 만 클래스 $className 변수에 저장하는 것을 볼 수 있습니다.

그것은 Model과 Controller 는 모두 클래스로 만들어져 있지만

View는 화면에 데이터를 출력하기 위해 class 가 아니라 html 코드를 품은 일반적인 php 파일 이기 때문입니다.


모든 클래스 파일이 application 아래 있는 각 기능별 디렉토리 안에 위치가게 됨으로

호출을 하기 위해서는 

new \application\(model or controller)\method명();

형식을 따르게 됩니다.


좀더 쉽게 직관적으로 설명하면

new \application\home\HomeController();

는 application/home 디렉토리 안에 있는 HomeController 클래스를 찾게 됩니다.

즉 객체명 앞의 구문이 모두 경로가 됩니다.


따라서 '\' 문자를 기준으로 경로를 구분하게 되며

두번째 값에 따라  Model의 객체 인지 Controller의 객체 인지를 구분 할 수 있게 됩니다.


해당 경로에 해당하는 컨트롤러가 있으면 해당 파일을 require_once로 불러 오게 합니다.


PHP autoload 와 namespace에 대해 조금 더 자세히 알고 싶다면 아래 포스팅을 참조해 주세요.

참조 : PHP autoload, PHP autoload와 namespace





요청 사항의 분석 1단계 : application.php


URL의 첫번째 값은 상단 메뉴에 대응하도록 설계를 하였습니다.

하지만 URL 값이 모두 완벽하게 넘어 오지 않을 수도 있습니다.

예를 들어 블로그의 메인 페이지로 이동하는데 category 라든가 글의 번호, 페이지 번호 등과 같은 정보는 전혀 필요가 없습니다.

따라서 넘어 오지 않는 값이 존재할 경우에 기본적으로 어떻게 동작해야 할지를 같이 정의해 주어야 합니다.


application/libs/application.php


<?php

namespace application\libs;


class Application

{

    public $controller;

    public $action;


    public function __construct()

    {

        $getUrl = '';

        if (isset($_GET['url'])) {

            $getUrl = rtrim($_GET['url'], '/');

            $getUrl = filter_var($getUrl, FILTER_SANITIZE_URL);

        }


        $getParams = explode('/', $getUrl);


        $params['menu']     = isset($getParams[0]) && $getParams[0] != '' ? $getParams[0] : 'board';

        $params['action']   = isset($getParams[1]) && $getParams[1] != '' ? $getParams[1] : 'index';

        $params['category'] = isset($getParams[2]) && $getParams[2] != '' ? $getParams[2] : null;

        $params['idx']      = isset($getParams[3]) && $getParams[3] != '' ? $getParams[3] : null;

        $params['pageNum']  = isset($getParams[4]) && $getParams[4] != '' ? $getParams[4] : null;


        if (!file_exists('application/controllers/'. $params['menu'] .'Controller.php')) {

            echo "해당 컨트롤러가 존재하지 않습니다.";

            exit();

        }


        $controllerName = '\application\controllers\\'.$params['menu'].'controller';

        new $controllerName($params['menu'], $params['action'], $params['category'], $params['idx'], $params['pageNum']);

    }

}


소스를 설명드리면

url 이라는 인자로 넘어온 값들을 '/' 문자를 기준으로 나누어 저장합니다.

이때 아무 인자도 넘어 오지 않는다면 

boardcontroller class 의 index() 메소드를 호출하게 됩니다.


url 값을 기준으로 해당 클래스가 존재하는지 확인을 한 후에 

해당 클래스의 메소드를 호출 합니다.




반응형