[작성자:] changwoo

  • LG Innotek

    LG Innotek

    개요

    • 홈페이지 디자인, 프론트: 클리어픽스
    • 제작 기간: 2017년 2월 ~ 6월
    • 사용 라이브러리: 워드프레스,  Uncode 테마, Ivy-MU 0.3

    (더 보기…)

  • 워드프레스에서 PHP 세션은 독이 될 수 있습니다.

    이전에 잠시 쓴 포스트의 검증을 위한 작업을 했습니다. 두 포스트를 통해 저는 PHP 세션(session)은 어지간하면 사용하지 말기를 권고드리며, 다른 방법을 사용하시기를 조언드립니다. 세션 사용시 사이트의 성능 저하가 발생할 수 있기 때문입니다.

    (더 보기…)

  • 모니터 후드를 만들다.

    크리스마스 아닌가? 크리스마스 기념 특별 모니터 후드를 제작했다.

    이 물건은 모니터 캘리브레이션에 사용하는 거라 그런지 생각보다 단가가 높다. 그러나 나는 그런 전문 용도로 쓰기 보다는 단지 빛만 차단하면 되니까, 사기에는 돈이 너무 아깝다.

    나 깉이 생각하는 블로거들이 많더라. 한 분 참고해서 하나 제작해 봤다. 생각보다 어렵지 않게 하니 만들 수 있더라. 재료비도 1만원 내외로 그다지 비싸지도 않고.

  • 손준호와 조화 12월 23일 공연

    손준호와 조화 공연 “털신을 신고”

    12월 23일 오후 7시 30분 합정 에디토리얼 카페 비플러스에서

    Video #01/02

    (더 보기…)

  • 매듭 묶기 연습 시작.

    드디어 매듭 묶기를 시작했다.

    취미 생활은 여러 가지인 것이 좋은 것 같다. 하나에 몰두해 보고, 또 조금 싫증나면 바로 다른 취미로 넘어갈 수 있어 신선한 리듬이 계속 될 수 있으니까.

    매듭 묶기를 실천함에 있어 느낀 점이 참 많다. 간단히 기록만 하려다가, 이참에 조금 썰을 적어 놓기로 하자.

    todoist.com이 큰 역할을 했다. 매듭을 묶는다는 첫 발을 띄우기 위해 작은 일로 조각내고, 마감 기한을 정하고, 실천하면 할 일 항목에 체크를 하여 일이 진행되고 있음을 인지시켰다. 이 앱은 내가 하고 싶은 일을 기록하여 잊지 않도록 도와주었다. 그리고 이 일의 마감기한을 설정하고, 마감기한이 임박하거나 지나면 그 일을 하려고 계획하고 있었음을 계속 알려 주었다.

    사실 “매듭 묶기”를 취미로 두자고 기록한 후 한 달이 지나도 매듭 묶기를 시작하지 못했다. 그렇게 시일이 지나도 일을 시작하지 못하자 나는 왜 이 일을 시작하지 못하는지에 대해 궁금증이 생겼다.

    되게 간단한 일인데? 왜 자꾸 잊어버리고 하지 않는 것일까? 왜 자꾸 귀찮아할까? 그것은 간단해 보여도 실은 나에게는 “쉽지 않은 일”이었던 것이다. 생전 처음으로 시작하는 일, 누구의 도움도 없이, 또 절박한 동기 없이 시작하는 일이다. 처음 하는 행동은 심리적으로 매우 거부감을 주는 일임을 잘 알지 못했었다. 정녕 간단하다면 바로 했겠지, 왜 차일피일 미루었겠는가? 귀찮음 뒤에는 간단해 보여도 심리적으로는 그다지 넘기 어려운 큰 산이 있다는 뜻인 것이다.

    그래서 나는 “매듭 묶기” 취미 할 일을 찬찬히 뜯어 생각해 보았다. 생각해 보니 이 목표는 매우 두루뭉술하다는 것을 깨달았다. 매듭 묶기를 대체 어떻게 시작해야 할지에 대해서는 전혀 언급되지 못한 것이다.

    그래서 나는 이 목표를 게임처럼 어떤 메인 퀘스트로 생각하고, 이 목표를 해결하기 위한 서브 퀘스트들을 생각해 보았다.

    매듭을 묶기 위해서는 줄이 필요하다. 예전에 빨랫줄을 매듭묶기 연습을 위해 사 두었다. 서랍에 처박아 두었다. 그러면 잊어버린다. 중요한 아이템을 습득해 직장 책상 위에 두었다. 언제든 줄을 보고 다른 서브퀘스트를 시작하기 쉽게.

    줄을 놔 둔다고 매듭 묶기를 시작하는 것은 아니었다. 따라해 볼 교재가 필요하다. 이것은 간단하다. 내 페이스북 저장된 페이지에는 이미 작년이나 제작년부터 “해 봐야지” 하면서 놓아둔 매듭법 사진이 있다. 이 사진을 휴대전화에 저장해 두었다. 나중에는 하나 프린트해서 가지고 다니리라.

    빨랫줄이 책상에 있다고 해도 매듭 연습을 바로 시작하지는 못했다. 이것이 진짜 제대로 취미가 되려면 자꾸 내가 줄에 노출되어야만 한다는 사실을 깨달았다. 빨랫줄을 잘라서 호주머니에 넣어야 한다. 할 일에 넣었다.

    이렇게 작은 서브퀘스트를 조직하자, 일이 풀리기 시작했다. 줄을 적당한 크기로 2개 잘라 호주머니에 넣었다. 먼저 줄을 사 두고, 이것을 보기 쉽고 손닿기 좋은 곳에 놔 두지 않았다면 또 생각만 하고 실천하지 못했을 일이다. 그제서야 나 스스로도 ‘매듭짓는 연습을 해야 한다’는 분위기가 생가기 시작했다. 화장실 갈 때 휴대전화와 매듭을 챙겨 보았다. 화장실에서 일을 보는 동안 휴대전화에 저장된 매듭 사진을 보고, 쉬운 것 하나를 골라 연습했다. 쉬운 거라 바로 습득이 된다. 이제야 성공이다. 매듭짓는 연습을 시작하게 된 것이다.

    이 간단한 일을 시작하는데 드는 수고가 이렇게 많았다니?! 간단하지만 간단하지만은 않은 일이었구나. 나 스스로 너무 자만한 것이다. 습관을 만드는 일은 단순히 보기에는 간단하지만, 사실 알고 보면 꽤 많은 심리적인 벽을 부수고 허무는 과정이 필요함을 깨달았다. 어떤 일을 하고는 싶은데 막상 귀찮음과 게으름에 부딫쳐 일을 시작하지도 못하고 있다면 생각해보자. 그 귀찮음과 게으름을 뛰어넘을 만큼 심리적인 에너지(동기)와 시간이 수반되면 그걸 뛰어넘고 실천할 것이다. 그러나 만약 그렇지 못하다면 생각보다 그 일을 수행하기 위한 전제 조건, 예를 들면 왜, 무엇을, 어떻게 그것을 해야 하는지, 그 일을 하기 위해 꼭 필요한 것은 무엇인지, 이런 것들을 무시한채 무작정 행동고 있는 것이 아닌지 점검해 봐야 한다고 생각했다.

     

     

     

  • nonce 필드 때문에 캐싱이 안 된다고?

    뭐, nonce 필드 때문에 캐싱이 안 된다고?

    사실이다. nonce 값은 사이트의 보안을 위해 필수적이다. 그러나 잘못 쓰면 페이지 캐싱이 되지 않아 사이트 성능에 큰 손실을 줄 수 있다.

    (더 보기…)

  • 2017 웹 어워드 수상작 발표

    회사 동료로부터 ‘2017 웹 어워드 수장작 발표’라는 웹페이지(http://www.i-award.or.kr/Web/Prize/2017/Awarded01.aspx)를 받아 보았다.

    이 중에 워드프레스로 만들어진 것은 몇 개나 있을까 궁금하여 급히 알아 보았다. 워낙 급하게 조사한 거고, 중간에 접속도 원활하지 않은 점도 있어서 정확한 것은 아니지만, 추정컨데 총 114개 사이트 중 7개가 워드프레스였다. 약 6%.

    다음은 수상작 중 워드프레스 기반으로 보이는 목록이다.

    – LG챌린저스 http://www.lgchallengers.com/
    – LG 옳은미래 http://www.lgfuture.com
    – LG전자 공식 블로그 'Social LG전자' http://social.lge.co.kr/
    – 스텝스 앱 홍보 웹사이트 http://steps.plus/
    – 카페 바라보다 https://baraboda.com
    – 서경대학교 예술종합평생교육원 웹사이트 https://lifeedu.skuniv.ac.kr/
    – 랑벨 https://lanbelle.com

    엘지 사이트가 굉장히 많네, 엘지가 워드프레스를 굉장히 적극적으로 이용하고 있는 것이 보인다. 수상 여부를 떠나서 엘지 이노텍 사이트도 워드프레스이고…

  • 게토리아 피자

    게토리아 피자

    여기 피자는 정밀 맛있어!

  • Uncode 테마 + KBoard 게시판은 성능 저하를 일으킬 수 있습니다.

    불타는 금요일을 보내고 토요일 새벽까지 디버깅을 한 결과입니다.
     Uncode 테마와 KBoard 게시판을 사용할 경우 워드프레스의 성능 저하를 유발시킬 수 있습니다. 둘을 따로따로 쓰면 문제가 없지만 같이 사용하는 경우 사이트에 로딩이 생기며, 특히 비주얼 컴포저 사용시 심각한 로딩 시간이 생기게 됩니다.

    원인은 KBoard의 세션과 Uncode 테마의 ‘list_images‘ AJAX 동작 때문에 발생합니다. KBoard의 메인 파일인 kboard/index.php 파일의 초반에는 session_start()로 세션을 시작하는 구문이 있습니다. 그리고 이 세션은 KBoard 곳곳에서 활용됩니다.

    한편 Uncode는 list_images라는 ajax 액션을 통해 uncode_list_images()라는 함수를 동작시킵니다(uncode/core/inc/admin.php: 1086). 이 함수는 무려 모든 이미지 파일을 불러와 그 이미지 파일의 용량을 계산합니다. 그리고 아래와 같은 메시지를 응답으로 넘깁니다.

    The Adaptive Images system is using 87.6M of the 6.29G space left.

    JSON 포맷이 아닌, 그냥 단순 텍스트 응답으로 날아옵니다. 이 응답이 자바스크립트 같은 곳에서 프로그래밍적으로 유의미한지는 확인하지 않았으나, 그렇게 보이지는 않습니다.

    문제는 이미지의 양이 많아질 때 터집니다. 제가 작업 중인 사이트는 이미지 파일만 1만개에 가깝게 유지하고 있습니다. 사이트의 용량만도 기가바이트급입니다. 언코드는 무식하게도 이 1만개에 육박하는 이미지를 매번 관리자 화면 로딩시 별도의 AJAX 요청을 통해 모두 쿼리로 불러와, 용량을 계산하고, 위 메시지를 출력하고 있던 것입니다.  이 계산을 위해 대략 수 초, 심하면 저의 경우처럼 30~40초까지 걸리기도 합니다.

    이제 이 상태에 KBoard가 끼어들면 문제가 정말 심각해집니다.  KBoard는 로딩될 때마다 세션을 실행합니다. 세션에는 한 번에 하나의 연결만 접근할 수 있으므로 둘 이상의 연결을 동시 처리할 수 없습니다. 그런데 언코드는 파일 용량을 계산하는 AJAX를 페이지를 부를 때마다 자동으로 실행합니다. 병목구간이 발생하는 것입니다.

    비주얼 컴포저로 각 위젯을 편집 버튼을 누르면 각 UI 정보는 AJAX를 통해 얻어 옵니다. 그러나 이미 이미지 용량 계산 때문에 다른 AJAX 요청에는 응답할 수 없는 상태입니다. 그래서 꽤 오랫동안 로딩이 걸리게 되는 것입니다. 아마 다른 페이지에 접근할 때도 마찬가지로 로딩이 심하게 발생할 수 있을 것입니다.

    문제를 해결하려면

    1. Uncode와 KBoard를 같이 쓰지 않는다.
    2. 같이 써야 한다면 Uncode의 이미지 계산하는 부분, uncode/core/inc/admin.php 파일의 1086번째 줄 부근의
      /* AJAX call to load all images */
      add_action( 'wp_ajax_list_images', 'uncode_list_images' );

       부분을 주석 처리.

    3. 코어를 건드리는 것은 부담스러우므로 차일드 테마나, 별도의 플러그인을 생성.
      add_action( 'plugins_loaded', 'my_uncode_kboard_fix' );
      
      function my_uncode_kboard_fix() {
        if( has_action( 'wp_ajax_list_images', 'uncode_list_images' ) {
          remove_action( 'wp_ajax_list_images', 'uncode_list_images' ) ;
        }
      }

    매번 이런 용량 계산을 하는 언코드도 문제지만, KBoard도 굳이 세션을 써야 했나 생각이 듭니다.

  • 3명 대 30명

    미국에 있는 (한국인)직원 3명이 하는 일을 한국에 있는 직원30명이 한다

    3명 대 30명

  • 손준호와 조화 gaga77page 라이브 영상

    2017년 10월 28일 오후 7시, 서울 마포구 서교동 gaga77page

    공연정보:  http://storefarm.naver.com/gaga77page/products/2214780554

    손준호와 조화 페이스북: https://www.facebook.com/sonjohwa1/

    (더 보기…)

  • WP AJAX 사용할 때 작은 팁들

    요즘은 웹 페이지들이 엄청 인터랙티브하다. 그만큼 비동기 호출, AJAX의 사용이 많을 수 밖에 없는 환경이다. 한편 워드프레스에서는 admin-ajax.php라는 곳에서 거의 모든 AJAX 요청을 처리하게 된다. 이 포스트에서는 워드프레스 플러그인에서 AJAX 요청을 작성할 때 참고하면 좋을 팁을 몇 가지 적어 보도록 한다.

    (더 보기…)

  • REST API와 커스텀 포스트 연동 시 주의할 사항.

    REST API와 커스텀 포스트 연동 시 주의할 사항.

    REST API v2를 사용하면 프론트 구성을 대폭 간결하게 만들 수 있다. 그리고 워드프레스 자체에서 backbone.js, underscore.js를 내장하고 있고, 이를 곳곳에 활용하고 있으며, 심지어 backbone.js의 model과 collection 개념을 사용하여 REST API를 구성하고 있다. 아직 REST API의 여러 부분을 깊숙하게 파악하지는 못했지만, 얼추 프로그램을 작성해 보면 한결 가볍게 페이지 구성이 가능해짐을 직접 느낄 수 있었다.

    이 포스트는 커스텀 포스트와 REST API를 사용하면서 삽질한 결과를 간단히 남겨 놓기 위해 작성한다.

    (더 보기…)

  • 기본 권한과 메타 권한

    기본 권한과 메타 권한

    이전 포스트에서 예고한 대로 이번에는 기본 권한과 메타 권한에 대해 포스팅해 보고자 합니다.

    구 워드프레스에는 회원들의 권한을 구분하기 위해 ‘레벨’이라는 개념을 사용했습니다. 예전 제로보드4 에서도 볼 수 있었던 개념입니다. 레벨 1부터 숫자가 높을 수록 권한이 낮아지는 방식의 권한 테이블 방식으로, 방법은 간단하나 썩 훌륭한 방법은 아니었습니다. 왜냐면 한 회원은 반드시 어떤 레벨에 종속되어 버리는 문제가 있고, 또 실제 운영에 있어서 권한의 높낮이보다 각기 회원의 세밀한 권한 설정도 중요할 때가 많은데, 이를 확장할 방법이 부족한 것이었죠.

    워드프레스 2.0버전부터 새로운 개념인 역할과 권한(roles and capabilities)이 생겼습니다. 이 달라진 권한 개념은 레벨이라는 개념을 완전히 버리고 새롭게 디자인된 권한 설정입니다. 그럼 역할과 권한이 어떤 특성을 가졌는지부터 간단하게 설명해 보도록 할께요.

    (더 보기…)

  • 워드프레스 권한 실습 – 포스트 권한 집중 탐구

    한때, 워드프레스는 사람들이 무료이고 쓰기 쉬워서 찾는 거지, 구식 개발이고 소스 코드도 알아보기 어렵고… 아무튼 이건 진짜 별로다. 이렇게 생각한 적이 있었습니다. 그러다가 생각이 바뀌게 된 계기가 있는데, 바로 역할과 권한(Role and Capability, 줄여서 RC) 체계를 접하게 된 것이었죠. 이후 워드프레스에 대한 평가가 긍정적으로 변했죠. 특히나 RC의 풍부한 표현은 정말 마음에 듭니다. 조금 어렵기는 해도, 사용자 레벨 같은 거와는 비교할 바가 아닙니다.

    워드프레스는 CMS입니다. 단연코 콘텐츠의 관리와 유통에 있어 전문가입니다. 잘 뜯어보면 세심하게 배려한 부분이 정말 많습니다. 개발자 입장에서 볼 때 어떤 콘텐츠를 잘 관리하고 전파할 목적이라면, 어설프게 만들지 말고 워드프레스 써라고 충고하고 싶습니다. 10여년간 그들이 쌓아 올린 콘텐츠 관리에 대한 노하우는 만만한 것이 아닙니다.

    사실 그동안 역할과 권한은 잘 쓰면 굉장히 멋진 것이 될 거라 생각은 해 왔습니다. 그러나 그 잘 쓰는 법을 여전히 잘 터득하지 못한 것 같았습니다. 물론 코덱스를 잘 읽으면 어지간한 정보는 습득할 수는 있지만 이것저것 만져보면서 직접 경험해보는 해킹(hacking)이 필요했습니다. 직접 좌충우돌하는 경험을 하고 싶었습니다. 그리고 이 포스트는 그 경험을 기록하기 위해 작성하는 것입니다. 

    (더 보기…)

  • 플러그인 엔진 컨셉: 자동 훅 전수자와 전수자 응용

    자동 혹 전수자(AutoHookInitiator, 오토 훅 이니시에이터)의 메소드 이름 규칙에 의해 액션과 필터가 자동으로 등록됩니다. 동작은 자동 발견 개시자(AutoDiscoverLauncher, 오토 디스커버 론처)가 동적으로, 알아서 시켜 줍니다. 프로그래머는 자잘한 콜백 선언은 엔진에 맡겨 두고 콜백 로직에만 집중하면 됩니다.

    개발하면서 느끼는 훅과 콜백의 문제점이 있습니다. 바로 훅의 선언과 콜백의 구현이 구현 차원에서 서로 떨어져 있다는 점입니다. 물론 액션 필터 선언 후 바로 콜백을 선언함으로써 그 코드의 거리를 좁혀 놓을 수는 있습니다. 그러나 코드가 점점 커질수록 이 거리를 완전히 붙여놓기 쉽지 않아집니다.

    콜백끼리 서로 공유해야 할 데이터가 있는 경우나, 보다 좋은 코드를 위해 클래스를 사용하는 경우 액션, 필터의 선언과 그 콜백은 더욱 그 거리를 가까이 하기 쉽지 않습니다. 그래서 나중에 유지 보수를 하는 중에 계속 에디터 창을 위아래로 옮겨 가면서 훅 선언과 콜백을 대조해야만 합니다.

    그런데 잘 생각해 보면 액션, 필터의 선언과 그 콜백은 거리로는 떨어져 있지만 의미적으로는 한 덩어리라고 볼 수 있습니다. 단지 언어적으로 그 둘을 한번에 표현할 방법이 없기에 구현상의 이유로 떨어진 것 뿐이라고 생각할 수 있습니다.

    AutoHookInitiator 등장

    자동 혹 전수자(AutoHookInitiator, 오토 훅 이니시에이터)는 이런 문제를 해결하기 위하여 고안하였습니다. 제가 이전 포스팅을 통해 전수자란 존재를 소개하면서 전수자는 기능적으로 관련 있는 훅들을 하나의 클래스로 묶어 통일성을 꾀하는 장치라고 말씀드린 적이 있습니다. 그리고 이런 전수자를 잘 응용하면 꽤 재미난 것들을 보여줄 수 있다고도 하였는데요, 이번에 소개해드릴 이 자동 훅 전수자가 그 대표격입니다.

    보통 훅과 콜백을 이렇게 선언합니다.

    add_action( 'admin_init', 'my_callback' ) ;
    
    function my_callback() {
    ....
    }

    앞에서 말씀드렸듯이 add_action()과 my_callback()의 가독성을 위하여 그 둘을 딱 붙여서 놓는 경우가 많습니다만, 어쩔 때는 거대한 함수들에 둘러싸여있는 통해 add_action()이 잘 보이지 않을 때도 있고, my_callback() 또한 항상 add_action()과 가깝게 있을 수 없는 경우가 많습니다.

    그러나 AutoHookInitiator는 이런 개념을 송두리째 바꾸어버립니다. 단지 콜백 함수를 작성하는 것만으로도 액션과 필터는 자동으로 등록됩니다. 아래는 자동 훅 전수자의 예입니다.

    class SampleInitiator extends AutoHookInitiator
    {
        public function action_init()
        {
            ....
        }
        
        public function action_5_admin_init()
        {
            ....
        }
    
        public function action_10_3_save_post($post_id, $post, $updated)
        {
            ....
        }
    
        public function filter_the_title($title)
        {
            ....
            return $title;
        }
    }
    

    …. 으로 생략한 부분은 콜백이 구체적으로 할 부분입니다. 클래스 코드 내부에서 전혀 add_action, add_filter를 쓰지 않았지만, 이 클래스를 통해 분명히 저는 3개의 액션과 1개의 필터를 선언하였습니다. 비밀은 메소드의 이름에 있습니다. 이 클래스 메소드의 이름을 부모 클래스인 AutoHookInitiator에서 정한 규칙에 맞게 지어주기만 하면 액션과 필터는 알아서 등록됩니다. 신기하지 않나요?

    메소드 이름 규칙은 이렇습니다.

    1. 모든 메소드는 public이어야 합니다. 당연히 코어에서 콜백을 실행해야 하는데 public이지 않으면 코어가 접근할 수 없겠죠.
    2. 액션은 반드시 action, 필터는 filter로 시작해야 합니다.
    3. action, filter 다음에는 우선순위를 뜻하는 정수를 붙일 수 있습니다. 선언하지 않으면 기본인 10입니다.
    4. 우선순위를 지정하면 인자의 개수를 이어 붙일 수도 있습니다 선언하지 않으면 기본인 1입니다.
    5. 그 다음 훅의 이름을 쓰면 됩니다.
    6. 각 문자열은 모두 언더바(_)로 이어 줍니다.
    7. 한 전수자에 같은 훅을 중복하는 경우, 훅 이름 부분 뒤에 두 개의 언더바를 붙인 다음 별도의 문자열로 구분을 할 수 있습니다.
      예) public function action_admin_menu__main_menu(); // 메인 메뉴 추가 액션
      public function action_admin_menu__sub_menus(); // 서브 메뉴 추가 액션

    위 코드에서 선언된 훅을 분석해 보고, 종래의 방식으로 다시 풀어 쓰면 이렇습니다.

    • action_init: action으로 시작하므로 액션입니다. 훅의 이름은 ‘init’입니다. 그러므로,
    add_action( 'init', array( $this, 'action_init' ) );
    • action_5_admin_init: 마찬가지로 액션입니다. 5는 우선순위입니다. 훅의 이름은 ‘admin_init’입니다. 그러므로,
    add_action( 'admin_init', array( $this, 'action_init' ), 5 );
    • action_10_3_save_post( … ): 우선 순위 10, 인자 3개를 받는 콜백 함수입니다. 훅의 이름은 ‘save_post’입니다. 그러므로,
    add_action( 'save_post', array( $this, 'save_post' ), 10, 3 );
    • filter_the_title: 이번에는 filter로 시작하므로 필터입니다. 훅 이름은 ‘the_title’ 입니다. 그러므로,
    add_filter( 'the_title', array( $this, 'filter_the_title' ), 10, 1 );

    AutoHookInitiator의 장점

    간결함

    메소드의 이름을 지음과 동시에 액션/필터의 선언이 동시에 지원됩니다. 코드가 분리되지 않아 가독성도 좋습니다. 저는 어떤 기능을 구현하면서 몇가지 훅을 시험적으로 사용해 봅니다. 경험에 따라 최적의 훅을 단번에 지목해 구현하는 경우도 있지만, 어쩔 때는 몇 번이고 액션과 필터를 수정하면서 해당 기능을 개선하려고 애를 씁니다. 그럴 때마다 훅과 콜백을 수정하는 것도 상당히 귀찮은 일입니다. 그러나 AutoHookInitator를 이용하면 그 불편이 많이 완화됩니다. 메소드의 이름과 시그니쳐만 살짝 변경하면 되니까요.

    콜백 메소드 작명도 상당히 귀찮은 일이고, 어쩌다 이름을 잘못 지으면 나중에 고치기도 쉽지 않습니다. 액션, 필터 선언한 곳까지 찾아가서 또 고쳐야 합니다. 그러나 AutoHookInitiator에서는 작명의 고통도 많이 완화됩니다. 별로 신경 쓰지 않아도 꽤 사려 깊게 이름이 지어집니다.

    한편 PSR을 사용하여 메소드나 변수는 camelcase가 기본인데, 이렇게 언더바를 사용하면 상당히 이질적일 것입니다. 저는 오히려 이 이질적인 것이 더욱 “콜백”임을 강조하게 되어 긍정적으로 생각합니다. 이런 메소드를 보면 단박에 ‘아 이거 콜백 함수구나’라고 알아차릴 수 있습니다.

    add_action, add_filter를 생략한다고 해서 개발 속도가 극적으로 빨라지는 것은 아닙니다. 그냥 기존의 방식을 써도 무방하지만, 그럼에도 불구하고, AutoHookInitiator는 편합니다. 사실 add_action, add_filter는 겉치레거든요. 이걸 생략하고 바로 콜백 구현에 들어가는 것은 개발 단계에 있어서는 상당히 실리적입니다.

    아, 여기서는 add_action, add_filter 만 이야기했습니다. 그러나 엔진에 구현된 AutoHookInitiator는 activation, deactivation 훅에도 대응됩니다.

    뷰로 바로 넘어가는 콜백 패턴: v-액션, v-필터

    워드프레스 특성상 콜백의 역할이 그다지 두텁지 않은 경우에는 콜백에 로직을 모두 구현해도 큰 문제가 없는 경우가 종종 있습니다. 아니, 그냥 콜백에 로직을 구현해야 오히려 더 깔끔할 때가 있습니다.

    반면 콜백에서 해야 할 일이 상당히 두터운 경우에는 별도의 뷰를 만들어 그 뷰에서 업무를 처리(dispatch)해야 할 경우가 생기기도 합니다. 예를 들어 어떤 어드민 메뉴를 만들 때입니다. 어드민 메뉴 페이지는 add_menu_page() 함수를 통해 삽입할 수 있습니다. 여기 인자 중에 콜백 함수도 포함되어 있는데, 이 콜백함수는 화면의 출력을 담당하므로 로직이 조금 복잡해질 가능성이 있습니다. 이걸 로직으로 작성하면 이렇게 됩니다.

    class SampleInitiator extends AutoHookInitiator
    {
        public function action_admin_menu()
        {
          $view = new AdminMenuView();
          $view->dispatch();
        }   
    }
    
    ....
    
    class AdminMenuView extends BaseView
    {
        public function AdminMenuView()
        {
          add_menu_page(
            ...
            array($this, 'outputMenuPage')  // 메뉴 페이지가 보여 줘야 할 내용을 출력하는 콜백
            ....
          );
        }
        
        public function outputMenuPage()
        {
          // 페이지 렌더링
          .....
        }
    }

    콜백 함수에서 별도의 뷰를 사용할 때 상당히 많은 경우 뷰를 인스턴스화 하고, 약속된 메소드인 dispatch()를 호출하는 패턴을 만날 수 있습니다. 그렇다면 전수자에서 이렇게 축약할 수 있습니다. 저는 이것을 메소드 이름 앞에 ‘v_’를 붙였다고 해서 ‘v 액션’이나 ‘v 필터’라고 부릅니다.

    class SampleInitiator extends AutoHookInitiator
    {
        public function action_admin_menu()
        {
          $view = new AdminMenuView();
          $view->dispatch();
        }
        
        // 위 메소드와 같은 역할을 합니다.
        public function v_action_admin_menu()
        {
          return AdminMenuView::getClass();
        }
    }

    View 클래스가 BaseView를 상속받아서 dispatch()라는 메소드와 getClass()라는 FQCN을 리턴하는 스태틱 메소드를 선탑재하고 있습니다. 이렇게 전수자 – 뷰 간의 관계가 단순하게 명세됩니다. AdminMenuView또한 독립적인 뷰로서, 나중에 요구사항이 변경될 때, 이를 태면 AdminExtraMenuView 같은 객체로 대체되어야 할 경우 아주 간단하게 수정될 수 있습니다.

    Replacement: 동적인 훅에 대비

    훅의 이름은 사실 동적입니다. 예를 들어 save_post_{$post_type} 같은 훅이나 , wp_ajax_{$action} 같은 것들입니다. 변수의 이름에 따라 훅 이름이 변경되는 경우입니다. 이럴 땐 AutoHookInitiator를 쓰기 곤란합니다. 함수 이름을 동적으로 변경할 수는 없으니까요.

    이 때를 대비하여 AutoHookInitiator는 setReplacement()  메소드를 가지고 있습니다.

    class SampleInitiator extends AutoHookInitiator
    {
        public __construct()
        {
          $this->setReplacement(
            'wp_ajax_my_action',
            'wp_ajax_action'
          );
        }
        
        public function action_wp_ajax_action()
        {
          ....
        }
        
        ....
    }

    setReplacement()의 첫번째 인자가 코어에 전달될 진짜 훅 이름입니다. 두번째 인자는 우리 전수자에서 찾을 수 있는 가칭 훅 이름입니다. 바로 밑의 메소드가 action_wp_ajax_action인 것이 보이죠? wp_ajax_action이라는 훅 이름은 실제로 add_action() 함수를 호출할 때는 wp_ajax_my_action으로 변경됩니다. 이렇게 동적인 훅 이름에도 대처할 수 있습니다.

    Context: 선택적 전수자 실행

    전수자는 단순히 훅만 그룹화하는 클래스가 아닙니다. 전수자 안에 조직화된 훅들은 문맥에 따라 그 실행을 조절할 수 있습니다. 엔진이 제공하는 기본적인 전수자 실행 문맥은 다음과 같습니다. 그리고 문맥은 사용자가 추가할 수 있습니다. 

    • admin: 관리자 화면 문맥. “관리자 화면일 때만”이란 의미를 가집니다.
    • ajax: ajax 문맥. “현재 AJAX 요청을 처리하는 중”이란 의미입니다.
    • autosave: 포스트를 자동 저장하는 문맥입니다.
    • cron: 크론 동작 문맥입니다.
    • front: “관리자 화면이 아닌 요청일 때”란 의미를 가지는 문맥입니다.

    예를 들어 ‘<plugin-dir>/src/Initiators‘란 곳이 전수자들을 놓아 두는 디렉토리라면, 여기에 문맥과 동일한 이름의 디렉토리를 만들고, 그 디렉토리에 전수자 클래스를 배치하면 해당 전수자들은 해당 문맥에서만 실행됩니다.

    이 디렉토리 아래 다음과 같은 상대 경로로 전수자가 존재한다고 생각해 봅니다.

    • admin/AdminInitiator.php
    • ajax/AjaxInitiator.php
    • front/FrontInitiator.php
    • frontNoAjax/MyInitiator.php
    • CustomPosts/CustomPostInitiator.php

    admin, ajax, front는 기본 문맥입니다. 따라서 해당 디렉토리 안에 저장된 전수자는 각각 관리자, AJAX, 전단부 요청에만 훅을 등록하도록 동작합니다. 나머지 frontNoAjax, CustomPosts는 문맥에 포함되지 않으므로 ‘항상’ 훅을 등록하도록 동작합니다. 그러나 만일 frontNoAjax에 “front 요청이지만 AJAX 요청은 제외하는 문맥”이라는 문맥을 따로 정의하여 자동 발견 개시자(AutoDiscoverLauncher)에 등록하면 해당 문맥에서만 훅을 등록할 것입니다.

    엔진에서 기본적으로 디렉토리에 기반하여 문맥 설정을 해 주므로 별도의 코딩 없이도 훅 등록을 적절히 조절할 수 있습니다. 가령 구분 없이 개발을 한 후, 재차 잘 정리합니다. 관리 화면에서만 등록해도 좋은 훅들만 따로 모아 하나의 전수자로 몰아 놓고, 이 전수자를 admin 디렉토리로 옮겨 두면 됩니다. 일일이 메인 로직에서 코딩하는 것보다 훨씬 더 간편합니다.

    여기까지

    전수자가 하나의 클래스이고, 객체라는 존재를 잘 활용하면 단순히 훅을 그룹화하는 것 이상의 능력을 발휘할 수 있습니다. 저는 플러그인을 위한  엔진을 개발하고 있고 위 내용은 실제 제 엔진에서 구현되어 사용중인 기능들입니다. 아직 소스를 일반에게 공개 배포하는 단계는 무리이지만, 어떻게든 이런 개념들을 제가 만들고 있고 발전시키고 있다는 사실은 알리고 싶었습니다.

    두서없이 글을 적고 있고 이 글을 누가 읽고 이해할까… 이런 생각입니다. 그렇지만 앞으로 이런 개념들은 Ivy-MU라는 플러그인 엔진, 그리고 킹콩보드3 등을 통해 세상에 공개될 날이 올 테니, 그 동안에 해 둘 수 있는 또 하나의 준비라고 생각하고 있습니다. 그 날이 오기를 고대하면서, 계속 정진하고 있겠습니다.

     

  • 플러그인 엔진 컨셉 잡소리: PSR

    플러그인 엔진 컨셉 중 가장 먼저 언급하고 싶은 것들.

    1. PSR 코딩 스탠다드

    워드프레스는 나름의 Best Practice코딩 스탠다드가 있습니다. 그러나 플러그인 엔진은 PSR 1, 2의 코딩 스탠다드를 더 선호합니다. 사실 이렇게 작성해서 플러그인 제출에 탈락할지 말지는 잘 모르겠습니다만, 엄연히 오픈소스에 커뮤니티 기반의 프로젝트인 워드프레스가 단지 자신들의 스탠다드를 지키지 않았다고 해서 플러그인 제출을 막을 것 같지는 않을 거라 생각합니다.

    파일 이름과 클래스 이름 모두 PSR을 따르는데, 이는 Autoloading을 사용할 때 이 쪽의 이름 규칙이 보다 편리하기 때문입니다.

    2. Autoloading, Composer

    PSR-1, PSR-2에 이어 PSR-4 또한 적극적으로 도입하여 사용합니다. 메인 파일 처음에 들어가는 “require_once vendor/autoload.php” 구문 이외에는 클래스나 함수를 부르기 위해서는 require, include 구문을 쓰지 않습니다. 이렇게 autoloading을 사용하면 코드 작성과 유지가 매우 쉬워집니다.

    물론 autoloading을 위하여 컴포저 (composer)를 도입합니다. 컴포저가 있는 만큼 외부 라이브러리 관리도 한결 편리해집니다.

    물론 기존의 워드프레스식 파일 이름과 워드프레스식 이름 관례를 쓰더라도 autoloading을 사용하지 못하는 것은 아닙니다. 그러나 워드프레스식 클래스 이름과 파일 이름의 명명법이 다르기 때문에 일일이 파일 이름과 클래스 이름에 대해 문자열 처리를 해야 하는 불필요한 비용이 발생합니다.

    예를 들어 클래스 이름이 WP_Member_Contacts 라는 클래스를 만들었다고 합니다. 그러면 이 클래스의 파일 이름은 ‘class-wp-member-contacts.php’ 정도로 될 것입니다.  그러면 WP_Member_Contacts가 class-wp-member-contacts.php 파일에 대응된다는 사항은 ‘composer dump-autoload’ 명령을 통해 autoloading 라이브러리가 미리 잘 캐싱해 둡니다. 여기까지는 좋습니다. 아무 문제가 없습니다.

    그러나 엔진을 제작하면서는 약간 고민거리가 생깁니다. 엔진에서는 class-wp-member-contacts.php 파일을 만나게 되면 이 파일은 어떤 클래스를 가지고 있는지를 알아야 할 때가 생깁니다. 아까의 반대입니다. 물론 규칙이 있으니 쉽게 변환할 수 있습니다. class- 접두를 빼고, 모든 하이픈을 언더바로 바꾼 다음 ucfirst를 적용하면 됩니다(사실 PHP FQCN(fully-qualified class name)은 대소문자를 가리지 않으므로 꼭 하지는 않아도 됩니다). 그러나 일일이 파일 이름에 대해 문자열을 계산해야 하는 것이 번거롭습니다.

    PSR-4와 PSR-1 규칙에 따라 클래스 이름과 파일이름을 CamelCase로 통일하면 그런 문자열 계산 비용이 사라집니다. 그리고 namespace의 한 마디가 디렉토리 깊이 하나로 매핑되는 구조 또한 매우 합리적입니다.

  • 헬조선? 그렇지만 우리는 변화할 겁니다.

    헬조선? 그렇지만 우리는 변화할 겁니다.

    헬조선이라는 말 아시죠? 솔직히 저도 이 말, 엄청 동의하면서 살고 있었습니다. 그러나 저번 총선과 이번 대선을 보고 느꼈습니다. 우리 나라는 계속 변하고, 나아질 것이라는 희망을 얻었습니다. 모든 문제가 한 번에 사라지지는 않을 겁니다. 사회 여러 곳에서 여전히 지옥같이 답답한 면이 있다고는 생각한다. 또 새로운 문제들이 생겨나겠죠, 당연히. 그러나 이 사회가 변해간다는 사실을, 성숙해진다는 사실을 보고 희망을 얻었습니다.

    단순히 특정 후보를 지지하기 때문에 그런 생각을 가지는 것이 아닙니다. 인터넷을 통해 사람들은 보다 폭넓은 의견과 정보를 얻을 수 있게 되었습니다. 이런 환경에서 자라난 본격적인 세대, 30대와 20대들은 본격적으로 나라를 바꿀 거에요. 그러나 변화의 본물결은 지금의 10대가 주축이 될 것입니다. 정말 멋진 나라로 탈바꿈하리라 생각합니다.

    특성 이미지 출처: http://www.fmkorea.com/best/648971228

     

  • 플러그인 엔진의 컨셉에 대하여

    이번 포스트에서는 만들고자 하는 플러그인 엔진에 대한 개략적인 컨셉을 적고자 합니다.

    워드프레스 플러그인 제작을 시작한 것은 2014년입니다. 그동안 몇몇 플러그인을 제작하였고, 몇 번의 플러그인 강의를 한 적이 있습니다. 몇 번의 플러그인 개발을 해 보며 느낀 것이 있습니다. 워드프레스는 훌륭한 CMS이지만, 훌륭한 웹 프레임워크는 아닌 것 같다는 겁니다. 다른 웹 프레임워크와 비교하면 솔직히 좀 아쉬운 것들이 많은 것이 사실이죠.

    사실 워드프레스를 위한 개발 프레임워크들이 여럿 있지만, 저는 그냥 제 플러그인 엔진을 만들기로 결심했습니다. 네, 바퀴를 재발명하는 것 같은 어리석은 일일 수도 있겠습니다. 사실 거창한 이유는 아닙니다. 워드프레스 플러그인 개발자로 지내며 제 나름대로 고민한 것을 정리하고 싶었고, 제가 저의 결과물을 얼마만큼 체계적으로 정리할 수 있는지 궁금했습니다.

    프레임워크 비슷한 것을 만드는 것은 사실 처음이 아닙니다. 아마 아무도 모르시겠지만 “Axis”라는 프레임워크라는 이름을 붙이기도 민망한 것을 만들고 처참히 실패가 적이 있었습니다. 지금도 마찬가지입니다. 제가 무슨 깊은 경험이 있어서 프레임워크를 운운하겠습니까. 그저 해보면서 성장하기를 바라는 것이겠죠. 여전히 민망하고 또 어려움을 느낍니다.

    실무에서 배움

    현재 워드프레스 개발자로 지내고 있으며, 실무를 접하며 겪게 되는 문제들은 상당히 흥미롭습니다.  어떤 일들은 지엽적이지만, 때때로 클라이언트들은 지금껏 접해보지 못한 상황을 문제로 가져옵니다. 저는 제가 흥미로운 다양한 경험을 해 볼 수 있는, 나름대로 좋은 환경에 있다고 생각합니다.  그리고 이런 문제들은 제가 상상할 수 있는 가장 우아한 방법으로 해결하고 싶습니다.

    물론 당장 그런 최상의 결과를 내는 것은 매우 어려운 일입니다. 그런 좋은 코드는 거저 만들어지는 것이 아닙니다. 꾸준히, 정말 꾸준히 여러 이슈를 거쳐 제련되어야 합니다.

    불행히도 클라이언트들을 위해 만드는 소위 ‘남을 위해 짜는 코드’들은 그렇게 지속적인 관심을 쏟기 어렵습니다. 빠른 시간에 만들어야 하고, 또 한 번 데드라인에 맞춰 완성된 결과는 적절히 유지보수하는 것 말고는 지속적으로 업데이트하여 쓰기가 어렵습니다. 클라이언트에게 최상의 코드를 제공해 드리지 못하는 점 죄송하게 생각합니다. 그러나 저도 사람인지라, 만들고 나서야 결과를 아니까요. 사람의 최선에는 늘 한계가 있더군요.

    엔진은 우선 저희 경험을 최대한 완성도 있게, 재활용될 수 있도록 레거시 코드를 정돈한다는 느낌으로 작성할 것입니다. 자주 반복되는 요소들은 그 다음에는 더욱 효율적인 방법으로, 간결하게, 이전보다 더욱 잘 작성될 것이라는 희망을 가지고 코드를 작성합니다.

    MTIV 패턴의 도입

    ‘MTIV’는 제가 생각한 플러그인을 위한 MVC류의 개발 패턴입니다.

    저는 Axis라는 구작을 만들 때부터 워드프레스 플러그인에 최대한 MVC 패턴을 도입하려고 애를 썼습니다. 솔직히 지금도 MVC의 정수를 이해하지는 못한다고 생각하며, 타 프로그래머에 비해 오히려 많이 부족하다고 생각합니다. 그럼에도 불구하고 이것을 고집하는 이유는, 이것이 더 나은 방법이라고 생각하기 때문입니다.

    개발을 하면서 코드를 이렇게 되도록 노력하고 있습니다. “처음에는 힘들고 귀찮지만, 한 번 짜 둔 코드는 유지보수하기 극도로 쉽게.”

    확실히 MVC 류의 프로그래밍 패턴이 뛰어난 유연성을 가지고 있고, 유지보수하기 좋은 구조를 가지고 있습니다. 많은 웹 프레임워크들도 MVC, 또는 이에서 파생된 패턴을 사용하고 있습니다.

    그런데 이 MVC를 바로 워드프레스 플러그인이나 테마 개발에 사용하려고 하면, 그다지 쉽지 않을 것입니다. 적어도 저는 그랬습니다. 이 부분은 저의 개발 엔진에 있어 가장 중요한 부분이기 때문에, 몇 가지 이유를 들어 그 이유를 자세히 설명해 보고자 합니다.

    우선 워드프레스 코어 자체가 이런 패턴을 직접적으로 사용하지 않기 때문에,  좋은 코드 교범을 보기가 힘듭니다.

    그리고 다른 웹 프레임워크와는 다른 워드프레스의 특성에서도 기인한다고 생각합니다. 다른 웹 프레임워크들은 사용자가 전달한 request 부터 서버가 도로 보낼 response까지의 작업을 온전히 개발자에게 맡깁니다. 웹 프레임워크들은 그 작업을 보다 편리하게 지원해주는 역할을 맡았죠. 설계도와 지휘봉은 모두 개발자에게 있습니다.

    그러나 워드프레스는 그렇지 않습니다. 이미 코어가 request를 받아 response를 대부분 처리하는 상태입니다. 플러그인, 테마 개발자는 코어에 이미 코어 개발자가 만들어 둔 고리, 훅(hook)에 자신의 콜백을 등록하는 방법으로, 즉 이미 완성된 코어를 확장하는 방법으로 개발을 진행합니다. 이것이 워드프레스식 웹 개발입니다.

    통상적인 웹 프레임워크들의 동작

    여기서의 예는 조금 복잡하니, 어려운 분들은 뛰어넘어도 됩니다.

    보통 웹 프레임워크는 URL 라우팅 규칙을 설정하고, 그 라우팅 규칙에 따라 하나의  request를 하나의 함수로 대응하는 구조로 되어 있습니다.

    여기서 제가 자주 사용하던 웹 프레임워크인 Django를 예를 들면, 장고는 MVC의 변종인 MTV 패턴을 사용합니다. Django의 URL 라우팅은 주로 각 앱의 urls.py 라는 파일에서 정합니다. 아래 코드는 그 예입니다.

    from django.conf.urls import url
    
    from . import views
    
    urlpatterns = [
        url(r'^$', views.index, name='index'),
    ]

    어떤 도메인의 ‘/’ 경로로 접근한 request는 views.index라는 View 함수가 처리를 합니다.

    from django.http import HttpResponse
    
    from .models import Question
    
    
    def index(request):
        latest_question_list = Question.objects.order_by('-pub_date')[:5]
        output = ', '.join([q.question_text for q in latest_question_list])
        return HttpResponse(output)

    index라는 View 쪽 함수 예입니다. 모델을 불러와 request가 원하는 방식으로 자료를 가공하여 response를 만들어내는 것입니다.

    워드프레스 코어의 동작

    워드프레스의 주 진입점은 워드프레스 디렉토리 최상위의 index.php와 wp-admin 디렉토리의 index.php, edit.php, post.php 등등이 있습니다. 일반적인 프론트 화면의 진입점인 최상위의 index.php를 보면 wp-blog-header.php 파일을 읽어들이는 코드가 있습니다.

    wp-blog-header.php 에서 WP 클래스의 main() 메소드가 실행됩니다. 이 메인 메소드는 아래처럼 되어 있습니다.

    public function main($query_args = '') {
        $this->init();
        $this->parse_request($query_args);
        $this->send_headers();
        $this->query_posts();
        $this->handle_404();
        $this->register_globals();
    
        /**
         * Fires once the WordPress environment has been set up.
         *
         * @since 2.1.0
         *
         * @param WP &$this Current WordPress environment instance (passed by reference).
         */
        do_action_ref_array( 'wp', array( &$this ) );
      }

     사용자의 request를 받아서 일괄적으로 처리하는 구조입니다. 여기서 우리가 코드를 함부로 변경할 수 없는 대신 action, filter를 사용하여 코어의 동작을 살짝살짝 변경시킵니다.

    MTIV의 I (initiator, 전수자)

    장고의 MTV라는 컨셉이 플러그인 개발에는 더 적절하다고 생각했습니다. M은 모델(Model)입니다. 데이터를 다루는 영역입니다. T는 템플릿(template)을 의미합니다. 화면을 어떻게 보여줄 지를 결정하는 시각적인 영역입니다. V는 뷰(View)로서 request를 받아 reponse를 생성하는 역할을 맡습니다. 뷰는 모델과 템플릿사이에 위치하며, 모델이 전하는 자료를 요청에 맞게 처리 가공하여 템플릿에게 넘겨주는 역할을 합니다. 템플릿은 뷰가 전해준 자료를 바탕으로 HTML 문서 등을 결과로 찍어냅니다.

    물론 MTV 같은 패턴을 워드프레스에 도입할 수 없는 것은 아니지만, 그것이 약간은 껄끄럽습니다. 앞서 이야기하였듯 워드프레스는 타 웹프레임워크처럼 request 부터 자연스럽게 로직을 처리하는 구조가 아니기 때문이라고 생각합니다.

    워드프레스 플러그인은 그 특성이 있습니다. 계속 이야기하였듯, 훅과 콜백입니다. 통상의 웹 개발자는 request에 초점을 맞추고 request에 맞춰 response를 작성하는데 온 힘을 기울인다면, 플러그인 개발자들은 코어에서 제공하는 (혹은 타 플러그인이 정의한) 훅에 관심을 두고, 원하는 기능을 작성하기 위해 적절한 훅을 탐색하고, 그 훅에 맞춰 적절한 콜백을 작성하는 데 노력을 기울입니다.

    저는 Axis 때무터 이 훅과 콜백에 천착해 왔습니다. 이 훅과 콜백의 특성상 기존의 MVC나 MTV들을 여타 웹 프레임워크 처럼 플러그인에 쓰기 쉽지 않다고 생각하였습니다. 그 대안이 “MTIV“입니다. ‘I’가 하나 추가된 것이죠. 저는 이 ‘I’를 전수자, 또는 이니시에이터(initiator)라고 부릅니다. 

    전수자의 가장 기본적인 역할은 콜백을 문맥에 맞게 정리하는 것입니다. 그리고 전수자가 잘 정돈한 콜백 함수에서 비로소 MTV의 패턴 도입이 이루어집니다.

    보통 어떤 구성 요소(component)들은 적어도 하나 이상의 액션, 필터를 조합하여 이뤄집니다. 그런데 이 액션과 필터들은 “어떤 상황에 대한 대응”을 설정하는 것이지 그 자체로 어떠한 구성 요소를 설명하는 것이 아니라, 차후 유지보수시 기능 분석이 약간 힘든 측면이 있습니다.

    예를 들어

    add_action( 'a_action', 'my_a_action_callback');
    function my_a_action_callback () {
    ...
    }
    
    add_action( 'b_action', 'my_b_action_callback');
    function my_b_action_callback () {
    ...
    }
    
    add_action( 'c_action', 'my_c_action_callback');
    function my_c_action_callback () {
    ...
    }
    
    ...

     이런 식으로 평면적인 액션 선언 및 콜백 함수로만 코드를 짜게 된다고 생각해 보세요. 또 구성 요소에 쓰이는 훅들이 겹치지 않으리란 보장이 있나요? 어떤 훅이 어떨 때 쓰이는지 쉽게 추적이 가능할까요?

    전수자는 이런 문제를 해결하기 위해 생각해 내었습니다. 한 구성 요소에 필요한 훅과 콜백을 의미 있게 하나의 클래스로 묶어 관리합니다. 훅, 콜백 선언이 한 클래스에 의미 있게 관리되므로 코드 유지보수에 더욱 편리합니다.

    물론 전수자를 쓰는 것도, 또 전수자의 콜백에서 MTV를 사용해야 하는 것은 강제 사항이 아닙니다. 예를 들어 워드프레스에서 메일을 보낼 때 본문에 html 코드를 삽입하고 싶다면 아래 같은 코드를 쓰는데, 아무리 봐도 이런 콜백 때문에 뷰를 쓰는 건 닭 잡는 칼에 소 잡는 칼 쓰는 격입니다.

    add_filter( 'wp_mail_content_type', 'set_content_type' );
    function set_content_type( $content_type ) {
      return 'text/html';
    }

    이럴 때는 그냥 전수자 콜백에서 그냥 처리해도 무방합니다.

    또 이미 많은 사람들이 저와 같이 생각을 하고 있고 비슷하게 의미 있는 훅과 콜백을 클래스로 묶거나 적절히 모듈화 하는 것으로 알고 있습니다. 그것과 전수자는 거의 동일합니다. 그러나 제가 생각하는 전수자 개념의 핵심은 “모듈화”만을 위한 것이 아닙니다. 워드프레스 플러그인 구조에서 원할 때 제대로 MTV를 사용할 수 있도록 길을 연다는 점, 이것이 중요합니다.

    제 경험에 미루어 보면 MTV 패턴은 플러그인 개발자가 원한다면 어떻게든 구현하여 사용할 수 있는 부분인 것 같더군요. 그러나 구현을 하게 되면 도대체 어디에서부터 뷰를 생성하고 그 로직을 전개할지 난감해질 때가 있었습니다. 모든 콜백에 대해 ‘뷰’라는 패턴을 가진 객체를 생성해야만 할까요?  애초에 훅, 콜백과 뷰는 잘 어울릴 수 있는 구조인걸까요? 설령 가능하다면 어떻게 뷰를 정돈된 형태로 유지할 수 있을까요?

    다행히 MTIV 구조에서는 전수자가 가장 먼저 기능별로 훅과 콜백을 정리해 두고 있습니다. 그 콜백을 처리하는 것이 뷰이기 때문에, 자연스럽게 뷰의 생김새도 전수자의 파일 구성을 따라 갑니다. 그래서 기능별로 만들어진 전수자 파일 구조 및 코드를 따라가면 자연스럽게 해당 심화 로직인 뷰 코드를 추적할 수 있습니다. 코드의 흐름을 쫓아가기 매우 쉬우며, 따라서 유지보수도 어렵지 않게 될 수 있습니다.

    전수자 예제

    아주 간단한 전수자 예제 인터페이스 코드를 작성해 보았습니다.

    interface Initiator
    {
        public function init_hooks($main);
    }

     $main으로는 플러그인 메인 파일이 입력됩니다. 훅 작성시 때때로 저 메인 파일이 필요하거든요.

    플러그인 활성화 때 어떤 역할(role)을 생성해야 한다고 가정합니다. 그러면 이 때 필요한 전수자를 작성한다면

    class Roles_Caps implements Initiator {
    
      public function init_hooks( $main = '' ) {
        register_activation_hook( $main, array( $this, 'callback_activation_hook' ) );
        register_deactivation_hook( $main, array( $this, 'callback_deactivation_hook' ) );
      }
    
      public function callback_activation_hook() {
        $this->add_role();
      }
    
      public function callback_deactivation_hook() {
        $this->remove_role();
      }
    
      private function add_role() {
        add_role(...);
      }
    
      private function remove_role() {
        remove_role(...);
      }
      // (이하생략)
    }

     이렇게 클래스를 작성하고, 메인 파일에서는

    $initiator = new Roles_Caps();
    $initiator->init_hooks(__FILE__);

    처럼 실행할 수 있습니다.

    보다 뷰와 템플릿 로직이 강화되어야 하는 경우를 생각해 봅시다. 아주 간단히 전수자 클래스의 내부 코드를 이렇게 작성할 수 있습니다.

    class Menu_Initiator implements Initiator {
    
      public function init_hooks($main = '') {
        $this->view = new Menu_View();
        add_action('admin_menu', array($this, 'callback_admin_menu'));
       ...
      }
    
      public function callback_admin_menu()
      {
        add_menu_page('title', 'title', 'manage_options', array($this, 'dispatch'));
      }
    }

    뷰 코드를 간단하게 설명하자면 이렇습니다.

    class Menu_View {
      public function dispatch() {
        $context = array(
          'title' => 'Hello!';
          ...
        );
        ...
        render('menu.php', $context);
      }
    }

     menu.php 는 템플릿 파일입니다. 콘텍스트에 따라 내용을 찍어냅니다.

    <div class="wrapper">
    ...
    <h2><?php echo esc_html($title); ?></h2>
    ...
    </div>
    

    이렇게 전수자부터 그 틀을 잡고 시작하면 이 이후의 MTV는 보다 형태를 잡기 쉬워집니다.

    이밖에도 전수자만이 가진 독특한 기능들이 있습니다. 이걸 설명하고 싶어 손이 근질거리만, 분량이 너무 많아지니 다음 포스팅으로 미루도록 할께요.

    MU Plugin, Plugin, 그리고 3rd Party Library

    이제는 이런 개발 프레임워크가 더 등장할지 모르겠지만, 개발을 위한 프레임워크가 또다른 형태의 플러그인으로 제공되는 것이 있습니다. Piklist나 Genesis 같은 것들이 그 예입니다.

    저의 플러그인 엔진은 사이트 동작을 위해 반드시 필요하고, 운영시 절대 비활성화 되어서는 안 되는 경우가 대부분이라 기본적으로는 엔진은 MU Plugin 형태로 동작되기를 권장합니다. 그러나 MU 플러그인이나 일반 플러그인이나 큰 차이가 없기 때문에 제작 자체는 일반 플러그인처럼 하고 있습니다.

    그러나 개발 엔진 같은 라이브러리 같은 코드들을 플러그인으로 제공하게 되면 버전 충돌 문제를 피할 수 없습니다.

    가령 제 플러그인 엔진이 굉장히 유명해져서 많이 사용된다는… 아주 즐거운 가정을 해 보자구요. A 플러그인은 제 엔진 1.8 버전을 사용합니다. B 플러그인은 제 엔진 2.0 버전을 사용한다고 해요. 그런데 1.8과  2.0버전은 엄청난 차이가 있어서 이 둘을 같이 사용할 수 없어요. 1.8 방식으로 코드를 쓰면 2.0에서는 오류를 뿜어내고, 그 반대로도 마찬가지입니다. 이 사이트는 A, B 플러그인을 둘 다 필요로 하는데… 어쩌죠?

    이런 경우에 엔진은 서드파티 라이브러리처럼 쓸 수 있도록 할 것입니다. 해당 플러그인이 각 버전의 플러그인을 충돌하지 않도록 각자 독자적인 라이브러리로 내장할 수 도 있게 만들어 저의 엔진을 사용할 수 있도록 만들 것입니다.

    여기까지! 이만 줄임!

    개발에 관해 심각하게 글을 쓰려고 한 건 아닌데… 내용이 많이 길어집니다. 저도 가볍게 제 플러그인 엔진에 대한 컨셉을 정리하고, 기왕 정리한 거 여럿에게 보여 주는 것이 어떨까 싶었습니다. 딱딱하고 지루하게 컨셉을 쓰다 보면 아예 글을 작성할 수 없게 될 것만 같아 설렁설렁 대충대충 블로그 포스팅으로 남깁니다. 플러그인 엔진 버전이 거듭되어 제련되듯, 이러한 설명 자료들도 차차 탄탄하게 기록되었으면 좋겠습니다.