06. 후킹 설정

다섯번째 포스팅까지 진행이 완료되어도 실제로 웹사이트를 구동해보면 오류로 인해 제대로 구동되지 않습니다. 필요한 라이브러리나 헬퍼등이 로드 되지 않았기 때문입니다.

application/config/hooks.php 파일을 열어 다음과 같이 추가합니다.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/*
| -------------------------------------------------------------------------
| Hooks
| -------------------------------------------------------------------------
| This file lets you define "hooks" to extend CI without hacking the core
| files.  Please see the user guide for info:
|
|	https://codeigniter.com/user_guide/general/hooks.html
|
*/
$hook['pre_controller'][] = array(
    'class' 	=> 'HookPreController',
    'function' 	=> 'init',
    'filename' 	=> 'HookPreController.php',
    'filepath' 	=> 'hooks'
);
$hook['post_controller_constructor'][] = array(
    'class'    	=> 'HookPostControllerConstructor',
    'function' 	=> 'init',
    'filename' 	=> 'HookPostControllerConstructor.php',
    'filepath' 	=> 'hooks'
);
$hook['display_override'][] = array(
    'class'    	=> 'HookDisplayOverride',
    'function' 	=> 'init',
    'filename' 	=> 'HookDisplayOverride.php',
    'filepath' 	=> 'hooks'
);

여기서 세개의 후킹 클래스를 만들건데요,

pre_controller는 컨트롤러가 초기화 되기 직전에 호출됩니다.
post_controller_constructor 는 컨트롤러가 초기화 된 직후에 호출됩니다.
display_override 는 사용자에게 화면이 보여지기 직전에 호출됩니다.

먼저 application/hooks 폴더에 HookPreController.php 파일을 생성하고 아래와 같은 코드를 붙여넣습니다.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*************************************************************
 * Class HookPreController
 ************************************************************/
class HookPreController {

    /*********************************************************
     * 컨트롤러가 호출되기 직전에 실행합니다.
     ********************************************************/
    function init()
    {
        $uri =& load_class('URI', 'core');
        $seg = $uri->segment(1);

        define("IS_ADMIN_PAGES", strtoupper($seg) === 'ADMIN');
        define("IS_AJAX_REQUEST",  !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtoupper($_SERVER['HTTP_X_REQUESTED_WITH']) == 'XMLHTTPREQUEST' );
    }
}

간단하게 현재페이지의 정보를 상수로 선언하는 후킹클래스입니다. uri중 첫번째가 admin 이라면 admin페이지라고 인식하고, ajax 리퀘스트가 헤더정보에 포함되어있으면 ajax 호출이라고 명시하는 용도입니다.

두번째로 application/hooks 폴더에 HookPostControllerConstructor.php 파일을 추가하고 아래의 코드를 붙여넣습니다.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*************************************************************
 * Class HookPostControllerConstructor
 ************************************************************/
class HookPostControllerConstructor {
    protected $CI;

    function init() {
        // 인스턴스화 된 컨트롤러를 불러와 참조시킨다.
        $this->CI =& get_instance();

        $this->load_config();
        $this->setup_device_view();
    }

    /**************************************************************************************************
     * 환경설정 파일을 로드한다.
     *************************************************************************************************/
    function load_config()
    {
        // 환경설정 파일이 존재하지 않으면 종료
        if(!file_exists(APPPATH.'/config/wwconfig.php')) {
            exit('Config FIle Not Found');
        }
        // 환경설정 파일을 불러옵니다
        require_once(APPPATH.'/config/wwconfig.php');

        $this->CI->config->set_item('base_url', BASE_URL);
        $this->CI->config->set_item('cookie_domain', COOKIE_DOMAIN);
        $this->CI->load->database();

        $this->CI->load->helper(array('url','form','cookie','common'));
        $this->CI->load->library(array('site','session','user_agent'));
    }



    /**************************************************************************************************
     * 현재 접속한 기기정보와, 보기 모드 설정들을 정의한다.
     *************************************************************************************************/
    function setup_device_view()
    {
        // 모바일 접속여부에 따라 device 정보 확인
        $device = $view_mode = $this->CI->agent->is_mobile() ? DEVICE_MOBILE : DEVICE_DESKTOP;

        // 해당 모드로 보기 쿠키가 존재한다면 해당 보기 모드로
        if( get_cookie( COOKIE_VIEWMODE )  && ( get_cookie( COOKIE_VIEWMODE ) == DEVICE_DESKTOP OR get_cookie( COOKIE_VIEWMODE ) == DEVICE_MOBILE) )
        {
            $view_mode = get_cookie(COOKIE_VIEWMODE);
        }

        // 사이트 정보에 저장
        $this->CI->site->device = $device;
        $this->CI->site->view_mode = $view_mode;
    }
}

load_config 함수 에서 두번째 포스팅에서 만든 application/config/wwconfig.php (본인의 취향에 따라 다른이름으로 생성하셨을 수도 있습니다.) 파일을 불러옵니다. 이곳에 각종 상수들을 선언했으므로 이파일이 제대로 존재해야 제대로 구동이 됩니다.

setup_device_view 함수에서 현재 접속한 기기와 뷰모드를 가져와 Site라이브러리에 저장을 해둡니다. $this->site->device 에는 실제로 접속한 기기 (desktop / mobile)을 저장해두고, 불러올 테마는 $this->site->viewmode에 저장해놓습니다. 이렇게 따로 분리해놓는 이유는 모바일에서 PC버젼보기 등을 이용할때 같이 실제 기기와 보는 테마가 다를수 있기 때문입니다.

마지막으로 application/hooks 폴더에 HookDisplayOverride.php 파일을 생성해주고 다음과 같이 코드를 집어넣습니다.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*************************************************************
 * Class HookDisplayOverride
 ************************************************************/
class HookDisplayOverride
{
    protected $CI;

    function init()
    {
        $this->CI =& get_instance();
        
        // RSS파일이거나 SITEMAPMAP.XML 파일 같은경우엔 사용하지 않는다.
        $output = !( $this->CI->uri->segment(1) == 'rss' OR strpos($this->CI->uri->segment(1), "sitemap") !== FALSE ) 
            ? $this->set_layout( $this->CI->output->get_output() ) 
            : $this->CI->output->get_output();
        
        $this->CI->output->_display($output);
    }

    function set_layout($output)
    {
        // AJAX 요청이거나, 테마설정이 FALSE 인경우엔 가공하지 않고 그대로 내보낸다.
        if( IS_AJAX_REQUEST OR $this->CI->theme === FALSE ) return $output;

        // Script Tag를 모두 가져온다.
        preg_match_all("/<script\\b[^>]*>([\\s\\S]*?)<\\/script>/", $output, $matches);
        $output = preg_replace("/<script\\b[^>]*>([\\s\\S]*?)<\\/script>/","", $output);

        // 하단에 붙일 태그를 가공해준다.
        $foot = "";
        $foot .= $this->CI->site->display_js() . PHP_EOL;
        foreach($matches[0] as $match) $foot .= $match;

        // 관리자에서 설정한 추가 script 태그가 있다면?        
        if( IS_AJAX_REQUEST && ! IS_ADMIN_PAGES && ! $this->CI->agent->is_robot() )
        {
            $foot .=  (! empty($this->CI->site->config('extra_tag_script')) ) ? $this->CI->site->config('extra_tag_script').PHP_EOL : '';
        }
        
        $output = str_replace("</body>", $foot.PHP_EOL."</body>", $output);

        // HTML파일을 압축시킨다.
        ini_set("pcre.recursion_limit", "16777");
        $re = '%# Collapse whitespace everywhere but in blacklisted elements.
        (?>             # Match all whitespans other than single space.
          [^\S ]\s*     # Either one [\t\r\n\f\v] and zero or more ws,
        | \s{2,}        # or two or more consecutive-any-whitespace.
        ) # Note: The remaining regex consumes no text at all...
        (?=             # Ensure we are not in a blacklist tag.
          [^<]*+        # Either zero or more non-"<" {normal*}
          (?:           # Begin {(special normal*)*} construct
            <           # or a < starting a non-blacklist tag.
            (?!/?(?:textarea|pre|script)\b)
            [^<]*+      # more non-"<" {normal*}
          )*+           # Finish "unrolling-the-loop"
          (?:           # Begin alternation group.
            <           # Either a blacklist start tag.
            (?>textarea|pre|script)\b
          | \z          # or end of file.
          )             # End alternation group.
        )  # If we made it here, we are not in a blacklist tag.
        %Six';
        $newOutput = preg_replace($re, "", $output);
        if( $newOutput === null) {
            $newOutput = $output;
        }

        return $newOutput;
    }
}

코드의 내용을 요약하자면 AJAX요청이나 테마가 지정되지 않은경우 나중에 넣을 RSS나 Sitemap 을 제외하고는 가공을 하는 내용입니다.

Script 태그를 모두 가져와서 </body> 바로 위로 옮겨주고,
관리자에서 추가스크립트를 설정해서 넣을수 있게 이부분도 추가해줍니다.

Mysql 에서 만들었던 ww_config 테이블에 extra_tag_script 라는 키를 가진 행을 하나 추가해줍시다.

다음페이지에서는 드디어 테마를 이용해서 간단한 페이지를 만들어 테스트해보도록 하겠습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.