사용자 도구

사이트 도구


wordpress:making_plugin_tutorial

문서의 이전 판입니다!


워드프레스 플러그인 만들기

소개

워드프레스는 단순히 블로깅 용 툴이 아니라 상당히 방대한 규모의 콘텐츠를 수용할 수 있는 잘 짜여진 CMS입니다. 즉, 다양한 콘텐츠를 어떻게 웹 상에서 효율적으로 제공하기 위한 플랫폼입니다. '블로그'는 그러한 콘텐츠의 한 부분집합인 것이구요.

이것은 워드프레스를 통해 제작한 웹사이트는 만들기도 보다 간편하고 차후 유지 관리에도 유리하기 때문에, 워드프레스는 점차 많은 곳에서 채택되어 사용되고 있습니다1). 제작 및 유지보수의 용이함도 있지만, 특히 플러그인을 통한 유연한 기능 확장은 워드프레스의 가장 매력적인 기능이 아닐까 생각합니다.

요즘은 다양한 플러인들이 제작되어 있어, 뭔가 부족하거나 아쉬운 기능은 플러그인을 찾아 보면 구할 수 있는 경우가 아주 많습니다. 단순한 기능 추가부터, 쇼핑몰 구축까지 아주 다양한 분야에서 다양한 종류의 플러그인들이 구비되어 있습니다.

그런데 이러한 플러그인 제작은 어떻게 하는 것일까? 내 입맛에 맞는 워드프레스 플러그인은 제작할 수 없을까? 본 문서에서는 그동안의 짧은 경험을 정리하고자 워드프레스 플러그인 제작 방법을 기술합니다. 짧은 경험이므로 혹여 워드프레스에서 권장할 만한 내용이 아닌 부분도 더러 있을 수 있습니다. 반드시 Codex와 교차 검증하도록 하세요.

플러그인 이름짓기

이름이 제일 중요합니다! 멋진 이름을 짓도록 하세요. 만드려는 플러그인이 아주 독특해서 그 이름 밖에 생각할 수 없고, 그런 이름은 찾아볼래야 찾아볼 수 없다면 행운입니다만, 대개 사람들이 생각하는 것들이 비슷비슷하기에, 그 이름 또한 비슷비슷할 때가 참 많습니다. m( 이렇게 수많은 플러그인 중에서 내 플러그인이 돋보이려면 이름도 기억하기 쉬우면서 독특해야 합니다. 그러나 기억하셔야 합니다. 가장 중요한 점은 바로 플러그인 이름이 유일해야 한다는 겁니다.

또한 플러그인을 만들면서 여러 변수나 함수 이름을 만들 때 기존의 이름과 충돌하지 않게 하기 위하여 이름공간(namespace) 내지는 접두어를 붙여 변수, 함수, 클래스 이름을 작성합니다. 때때로 너무 많은 이름들이 접두어를 사용하고 있어 심각한 경우 '스머프식 이름 관례(Smurf Naming Convention)2)'가 되버려 난감할 때도 있습니다만, 이런 접두어는 일반적으로 워드프레스 플러그인에서는 흔히 보입니다. 이렇게 일반적으로 플러그인의 이름이나 이름의 약어를 직접적으로 코드에 사용하므로 이름을 보기 좋게 만드는 것이 좋습니다. 아무래도 개발하는 사람이 계속 접하게 되니께요.

플러그인 개요

여기서는 플러그인을 작성하기에 앞서 필요한 기본 컨셉을 간단하게 정리해 보았습니다. 플러그인 프로그래머는 가장 기본적으로 다음 세 가지에 대해서 이해하고 있어야 한다고 생각합니다.

  1. 코어와 플러그인의 관계
  2. 플러그인에서 사용되는 액션, 필터, 훅의 개념
  3. 기본 데이터베이스의 구조 및 개념.

플러그인은 워드프레스의 손님

플러그인은 워드프레스 본체에서 기능을 확장하기 위해 추가적으로 불러 오는 모듈입니다. 여러분도 상식적으로 당연하다 생각하실 겁니다. 여기서 중요한 말은 바로 '불러 오는' 이 부분 입니다. 즉, 플러그인이 자기 필요에 의해 요청을 하는 것이 아니라, 반대로 워드프레스 본체가 필요에 의해 플러그인을 소환하는 형태인 것입니다.

이것은 프로그래밍 측면에서 보면 참조를 하는 주체(主體)와 참조되는 객체(客體)가 각각 누구인지가 결정되어 있다는 겁니다. 보통 우리가 프로그래밍을 하게 되면 우리가 주체, 그러니까 '갑'이 되고, 우리 프로그램에 필요한 기능을 위해 외부 라이브러리를 들여다 사용하게 됩니다. 외부 라이브러리는 객체로서 '을'의 입장이 됩니다. 그러나 플러그인 개발에 있어 주체는 워드프레스 본체이고, 객체는 플러그인입니다. 다시 말해 우리의 플러그인은 워드프레스 프레임워크 상에서 내부적으로 호출되어 사용되는, 일종의 라이브러리가 되는 것입니다. 일반적으로 생각해 보면 이러한 모델이 될 수 밖에 없겠죠.

액션, 필터, 그리고 훅

앞서 플러그인은 워드프레스의 손님이라는 표현을 사용했습니다. 플러그인은 워드프레스의 기능을 확장하기 위해 만들어지지만 워드프레스의 코어를 토대로 만들어지며 코어 없이는 동작하지 않기 때문입니다.

플러그인은 코어에게 원하는 시점에 원하는 동작을 하도록 요청해야 합니다. 예를 들어 플러그인은 워드프레스 대시보드 매뉴가 나올 때 자기 플러그인 메뉴도 같이 나오도록 코어에 요청할 수 있어야 합니다. 혹은 회원 가입을 하거나 회원이 프로필을 바꿀 때 회원에 대해 어떤 특별한 작업을 수행하도록 계획했다면, 그 시점에 그러한 행동을 할 수 있도록 코어에 요청을 해 그러한 작업이 이뤄지도록 무언가를 해야 하겠죠?

이렇게 내가 만든 플러그인과 워드프레스 코어 간에 상호작용이 있어야 동작을 할 수 있습니다. 그 역할을 하는 것이 바로 '액션', 또는 '필터'입니다. 쉽게 말해 액션과 필터 둘 다 PHP로 작성하는 콜백 함수(callback function)입니다. PHP의 call_user_func 함수를 원하는 때 원하는 방법으로 이용하도록 만든 워드프레스의 프로토콜이라 생각하시면 됩니다.

액션과 필터는 일반적인 GUI 프로그래밍에서 자주 쓰이는 '이벤트 핸들링'의 개념과 매우 유사합니다. 가령 유저가 버튼을 눌렀을 때 GUI 프레임워크는 버튼 클릭 이벤트를 발생하고, 이벤트 핸들러가 호출됩어 원하는 동작이 일어나는 것을 기대할 수 있지요.

GUI 프로그래밍에서 어떤 특정 사건을 지칭하는 용어를 '이벤트'라고 말한다면, 워드프레스에서는 이 이벤트라는 용어를 일컬어 ''이라고 부릅니다. 즉 어떤 훅이 발생하면 그 훅에 대해 어떤 이벤트 핸들링을 하게 되는데, 그 핸들러의 종류는 액션과 필터 두 종류로 나뉘는 것입니다.

액션필터가 사실상 콜백 함수라면, 왜 이렇게 둘을 갈라 두었을까요?

액션은 워드프레스가 수행하는 '코드'에 관심이 있습니다. 즉 어떤 훅에 의해 호출이 되면 어떤 코드를 수행할지에 초점을 맞춘 것입니다. 반면 필터는 워드프레스의 '데이터'에 더 관심이 있습니다. 훅에 의해 호출이 되면 전달 받은 데이터를 어떻게 가공할지에 대해 더 초점을 맞춥니다. 물론 어떤 일의 성격상 필터로도 가능한 일이 있고, 액션으로도 똑같이 가능한 작업이 있을 수 있습니다. 예를 들어 포스트의 내용을 적당히 바꾸어야 하는 작업을 해야 한다고 가정해 봅니다. 그러면 이런 작업은 포스트를 출력하는 '액션'에서 출력하기 바로 직전에 내용을 변경할 수도 있지만, 필터에서 데이터를 가져올 때 미리 데이터를 변경한 후 출력하게 만들 수도 있는 것이겠죠.

플러그인이 하는 일을 아주 간단하게 요약하자면 이렇습니다. 코어가 정해 둔 양식에 맞춰 할 행동을 등록하고, 코어에서 제공하는 여러 툴을 이용해 원하는 일을 하는 겁니다. 개념적으로 보면 거의 모든 플러그인들이 이런 방법으로 동작하겠죠.

데이터베이스

워드프레스의 테이블 구조에 대해 어느 정도 이해를 해야 플러그인을 작성할 때 원하는 기능을 효과적으로 구현할 수 있겠죠. 워드프레스는 상당히 다양한 형태의 정보를 저장할 수 있도록 테이블을 디자인해 두었지만, 기본적인 테이블의 수는 열 개 내외정도로, 그리 많지 않습니다. 반드시 먼저 워드프레스의 데이터베이스 구조에 대해 설명한 코덱스 페이지에서 자세한 정보를 먼저 확인하세요.

아래 항목은 가장 기본적인 사항 몇 가지만 나열하여 보았습니다.

메타 테이블

약간의 부연설명을 드리자면, 워드프레스의 테이블 중 '~meta'라는 테이블을 몇 개 발견하실 수 있습니다. 워드프레스 데이터베이스 버전이 변함에 따라 변동이 있을 수는 있겠지만 아마 wp_commentmeta, wp_postmeta, wp_usermeta 같은 테이블 이름을 보실 수 있을 겁니다.

이렇게 ~meta 접미사가 붙은 테이블은 그 접미사가 붙지 않은 테이블의 보조 역할을 맡은 테이블이라고 생각할 수 있습니다. 이러한 메타 테이블은 공통적으로 4개의 필드로 구성되어 있음을 알 수 있습니다.

  • meta_id: 각 테이블 내 레코드의 id입니다.
  • *_id: * 부분은 각 테이블의 상위 개념이 되는 테이블의 id입니다. wp_postmeta이 경우는 post_id, 즉 wp_post.ID를 말하는 것이겠죠.
  • meta_key: 테이블에서 “확장” 하고 싶은 데이터의 를 정의하는 필드입니다.
  • meta_value: 테이블에서 확장하고 싶은 데이터의 을 정의하는 필드입니다.

이렇게 키/값을 사용자가 임의로 설정할 수 있도록 구성했기 때문에 데이터베이스는 많은 필드를 정의하지 않아도 되고, 임의의 필드에 대해서도 잘 대응할 수 있습니다. 보통 회원 정보를 담는 wp_users 테이블에는 회원의 이메일 주소는 기본적으로 저장할 수 있도록 해 둡니다. 그러나 그 이외의 여러 프로필, 가령 페이스북의 아이디나 카카오톡의 아이디, 약간 민감하긴 하지만, 회원의 전화번호 등등은 어떤 필드에 저장하두라고 특별히 정해 두지는 않았습니다. 허나 이런 필드가 필요할 경우 어떻게 해야 할까요? wp_users에 필드를 추가할까요? 그렇지 않습니다. 이렇게 부가적으로 불리는 데이터를 저장하라고 만든 테이블이 wp_usermeta 테이블입니다. 같은 방법으로 wp_commentmeta는 wp_comments에 대응되고 wp_postmeta는 wp_posts에 대응됩니다.

예를 들어, 서버 내 설치된 워드프레스를 개발하는 팀끼리 미리 어떠한 이름을 쓰기로 약속을 합니다. 이 이름을 메타 키로 정의합니다. 예를 들어 페이스북, 카카오톡, 전화번호에 대해 다음과 같이 메타 키로 쓰기로 합니다.

  • 페이스북 아이디: facebook_id
  • 카카오톡 아이디: kakaotalk_id
  • 전화번호: phone_number

그러면 각 회원에 대해 테이블 작업을 하지 않고서도 얼마든지 확장된 정보를 저장할 수 있게 됩니다. 다음과 같은 회원이 있다고 하죠. 편의를 위해 2개의 필드만 나열하겠습니다.

ID user_login
1 admin
2 chulsoo
3 john
4 mary

이 때 이 회원들에게 각각 페이스북 아이디, 카카오톡 아이디, 전화번호를 입력한다면 다음처럼 될 수 있겠죠. 모든 회원이 3가지 값을 다 가지지는 않고 아예 값이 없을 수도 있습니다.

회원 페이스북 아이디 카카오톡 아이디 전화번호
chulsoo chulsoo_face chulsoo_kaka
john 555-1234
mary mary_kaka

위 두 표를 가지고 wp_usermeta 값을 그려 봅니다.

umeta_id user_id umeta_key umeta_value
56 2 facebook_id chulsoo_face
57 2 kakaotalk_id chulsoo_kaka
65 3 phone_number 555-1234
92 4 kakaotalk_id mary_kaka

이런 식으로 저장됩니다.

이렇게 키와 값으로 임의의 데이터에 대해 저장할 수 있는 방법이 있으므로 기본 테이블의 구조를 변경하지 않고도 어지간한 데이터는 큰 문제 없이 처리 가능합니다. 주의할 점은 메타 키 항목은 모든 데이터에 대해 공통적으로 사용해야 하며, 어떤 한 정보를 기록하기 위해 여러 가지 키를 섞어 쓰면 안 됩니다. 다시 말해 전화 번호를 저장하기 위해 'phone_number'라는 키를 쓰기로 약속했으면 끝까지 'phone_number'를 사용해야지, 'phonenum', 'phone', 'num', 'Phone_Number' 등등 원래 약속한 것과 다른 키 이름을 사용해서는 절대 안 됩니다. 이것은 데이터베이스에 'phone_number' 필드를 중복하는 것과 같은 행위입니다.

메타 테이블은 어떠한 형태의 데이터라도 효과적으로 확장하여 데이터베이스에 기록할 수 있도록 해 주는 편리함도 있지만, 만능은 아닙니다. 개발하는 팀 내부에서 명확히 규약을 맞춰 두지 않으면 이름 파편화가 생길 수도 있고, 개별적으로 필드를 설정하는 것만큼 성능이 뒤따라주지 않을 수도 있습니다. 또한 모든 형태의 자료 구조에 대해 100% 효율적으로 딱 들어맞는다고 말하기도 어렵습니다. 이럴 때는 워드프레스가 제공하는 테이블이 아닌 별도의 테이블 형태를 만들어 사용하시면 됩니다. 워드프레스에서 기본적으로 주는 테이블 이외의 테이블을 사용한다고 워드프레스에서 사용할 수 없는 건 절대 아니니까요.

포스트 타입과 커스텀 포스트

워드프레스는 기본적으로는 블로깅 저작 툴로 생각할 수 있습니다. 기본적인 동작은 거의 블로그로써 포스트를 작성하기 위해 있습니다. 그러나 워드프레스는 단순히 블로깅 뿐만 아니라 일반적인 형태의 웹사이트도 잘 지원하도록 확장이 가능합니다. 블로그의 게시물인 '포스트'뿐만 아니라 정적인 웹 페이지를 위해서도, 그리고 어느 정도는 일반적인 형태의 게시물을 저장하기 적절합니다.

워드프레스는 포스트를 작게는 한 블로그의 포스트로서, 크게는 어떤 작은 데이터 단위로서 취급합니다. 그리고 그 데이터 단위를 타입에 따라 구분하여 wp_posts 라는 테이블에 모두 저장하도록 합니다. wp_posts 테이블의 한 레코드는 블로그의 포스트가 될 수 있고, 정적인 페이지가 될 수도 있고, 굳이 웹페이지로 출력될 필요는 없지만 웹사이트 운영에 필요한 어떤 데이터의 한 단위가 될 수도 있습니다.

이 때 wp_post 내부에서 각 레코드가 어떤 타입인지를 정해주기 위해 'post_type'이라는 필드가 있습니다. 이 필드 값은 20자 내의 임의의 문자열을 저장할 수 있는데, 이를 통해 각 레코드의 정보가 어떤 목적을 위해 있는지를 구분해 줍니다. 워드프레스에서 기본적으로 정의한 포스트 타입으로 post, page 두 개를 들 수 있습니다. post는 말 그대로 블로그 포스트를 위한 타입이며, page는 정적인 한 페이지를 위한 타입입니다. wp_post 필드를 적절히 활용하여 저장하고자 하는 데이터를 계획하는 것이 가장 워드프레스와 잘 어울립니다.

예를 들어 어떤 학교의 반에서 학급 신문을 만든다고 합니다. 이 때 워드프레스를 이용해 웹사이트 형태로 구축한다고 합니다. 학급 신문 기사들은 기사 나름대로의 데이터 구조를 가지고 있을 것입니다. 기사 발행 시간이나 수정 시간, 기사를 입력한 사람, 본문, 요약, 헤드라인, 또는 기타 여러 부가적인 정보들을 필요로 하겠죠. 이런 것들을 위해 테이블을 다시 만들 필요는 없어 보입니다. 이렇게 하면 어떨까요?

필요한 정보 활용할 필드
기사 발행 시간 wp_posts.post_date, wp_posts.post_date_gmt 활용
기사 수정 시간 wp_posts.post_modified, wp_posts.post_modified_gmt 활용
기사 작성자 wp_posts.post_author 활용
본문 wp_posts.post_content
요약/헤드라인 wp_posts.post_excerpt
기타 wp_posts 의 필드 혹은 wp_postmeta 활용
카테고리/태그 워드프레스가 기본적으로 제공하는 카테고리, 태그 기능 활용
기사 타입 wp_posts.post_type에서 기사 성격별로 정의.
예) 새 소식: article_news, 사설: article_editorial, …

별로 힘들이지 않고 새로운 데이터 형태를 구축할 수 있습니다. 그리고 부족한 필드는 메타 필드를 이용하면 됩니다. 별도의 테이블을 쓰지 않고 포스트 타입을 확장하는 것이 멋진 이유는 이렇습니다. 워드프레스의 기본 타입인 post를 확장하는 것이기 때문에 워드프레스 내부에서 post와 동등한 처리를 할 수 있습니다. 별도의 복잡한 쿼리를 쓰지 않고도 워드프레스가 쓰는 방법 그대로 데이터 작업을 할 수 있습니다. 또한 기존의 UI 요소를 완전히 재활용할 수 있습니다.

이렇게 커스텀 포스트를 만들기 위해서 워드프레스는 일련의 API를 제공하고 있습니다. 더욱 자세한 내용은 Custom Post Types 코덱스, 그리고 register_post_type 함수 등을 참고하세요.

한편 register_post_type의 인자를 보시면 아시겠지만 매우 인자가 복잡합니다. 그래서 쓰기가 좀 복잡하고 불편할 수도 있는데, 이를 위해 Types라는 플러그인이 있으니, 이를 활용하여 보다 편하게 커스텀 포스트 타입을 정의할 수 있습니다.

텀, 택소노미, 그리고 포스트와의 관계

'텀(term)'은 '용어'라는 뜻이고 워드프레스 내부에서는 태그나 카테고리 하나하나의 엔트리로 생각할 수 있습니다. 포스트 타입만으로는 모든 저작물의 분류를 나누기에는 역부족이죠. 한 타입의 저작물 내부에서 세부적으로 정보를 분류해두기 위한 수단으로 태그나 카테고리가 기본적으로 제공됩니다. 태그나 카테고리나 둘 다 어떤 포스트에 대한 부가적인 정보이지만 태그는 수평적인 구조, 카테고리는 위계적인 구조를 가지고 있습니다.

'택소노미(taxonomy)'는 사물이나 개념을 분류하기 위한 방법이라고 생각할 수 있습니다.

예를 들어 워드프레스에서 '쇠고기'이라는 텀을 만들었습니다. 이 텀은 태그로서도 사용될 수 있지만, 블로그의 카테고리를 위해 사용된 텀일 수도 있습니다. 또한 쇠고기는 '음식 재료'를 분류하기 위한 카테고리를 위해 사용될 수 있지만 '내가 만든 요리를 주 재료별로 분류'하기 위해서도 사용할 수 있습니다. '음식 재료별'로 어떤 글을 쓰기 위해 음식 재료별 카테고리를 쓰는 것과, 내가 만든 요리를 포스팅하기 위해 '주 재료별'로 카테고리를 쓰는 것은 전혀 다른 맥락입니다.

그래서 워드프레스는 각각의 단어, 텀에 대한 정보는 wp_terms에 저장합니다. 그리고 이 텀이 어떤 맥락에서 사용되는지에 대해서는 wp_term_taxonomy에 저장합니다. 여기서 해당 텀이 태그로 사용되는지, 카테고리로 사용되는지도 정의합니다. 마지막으로 어떤 포스트가 어떤 텀/택소노미를 사용하는지는 wp_term_relationships 테이블에 저장합니다. 다시 정리하면,

  • wp_terms: 텀(용어)의 목록을 기록.
  • wp_term_taxonomy: 텀이 어떤 택소노미에 속하는지 정의. 태그로 쓰이는지, 카테고리로 쓰이는지 등등에 대해 설명.
  • wp_term_relationships: 해당 워드프레스의 포스트가 어떤 택소노미-텀을 사용하는지 기록. 즉 포스트의 태그나 카테고리의 사용 현황을 기록.

그렇다면 '쇠고기'에 대한 텀이 워드프레스에서 태그나 카테고리로 쓰이는 예를 들어 보지요. 우선 쇠고기라는 텀을 만듭니다.

wp_terms

term_id name slug term_group
64 쇠고기 beef 0

텀의 이름은 '쇠고기'이지만 워드프레스 코드 내부에서는 보다 프로그래밍 하기 좋게 'beef'라는 문자로 생각합니다. 그래서 우리가 보기에는 '쇠고기'라는 용어는 워드프레스 코드 내부에서는 'beef'라고 해석됩니다 (이렇게 실제 용어를 분리해서 프로그램 내부에서 사용하기 좋게 식별자를 지정한 것을 '슬러그(slug)'라고 말합니다.)

이제 '쇠고기'라는 용어를 태그로 사용합니다.

wp_term_taxonomy

term_taxonomy_id term_id taxonomy description parent count
78 64 post_tag 쇠고기 태그 0 0

워드프레스에서 기본적으로 정의된 포스트 타입인 'post'를 위해 태그를 사용한다면 그 택소노미는 'post_tag'입니다. 마찬가지로 만약 우리가 특정 포스트 타입을 정의한다면 그 포스트 타입을 위해서는 taxonomy 필드에 고유한 문자열을 사용해야 합니다.

그럼 이 용어를 그대로 카테고리의 하나로도 써 보죠. 그럼 우선 상위개념을 텀으로 추가합니다. 내 요리를 재료별로 분류하기 위한 최상위 카테고리는 '내 요리'입니다.

wp_terms

term_id name slug term_group
64 쇠고기 beef 0
65 내 요리 my-cooking-by-ingredients 0

그리고 내 요리의 하위 카테고리로 쇠고기를 다시 추가합니다. 이 작업을 모두 워드프레스 UI에서 할 경우에는 '쇠고기'라는 용어가 중복되어 기록될 수 있습니다. 그렇지만 여기서는 그냥 반복하지 않고 하나의 텀을 재활용하겠습니다.

wp_term_taxonomy

term_taxonomy_id term_id taxonomy description parent count
78 64 post_tag 쇠고기 태그 0 0
79 65 category 쿠킹 최상위 0 0
80 64 category 쇠고기 카테고리 79 0

기본 포스트 타입인 'post'에서는 카테고리를 위한 택소노미는 'category'입니다. 커스텀 포스트에서 별도의 카테고리를 지정하려면 이 'taxonomy' 필드에 적절한 식별자를 따로 지정해야 합니다. 쿠킹 최상위는 parent가 0이고 카테고리로 사용되는 항목인 term_taxonomy_id 80번의 부모는 79번 '쿠킹 최상위'가 지정되어 위계 관계가 이뤄진 것이 보입니다.

이제 이것이 포스트에 적용된 것을 예로 듭니다. 포스트 ID 1023번에는 태그, 포스트 ID 1030번에는 카테고리를 적용해 보죠.

wp_term_relationships

object_id term_taxonomy_id term_order
1023 78 0
1030 80 0

사실 이렇게 카테고리를 지정하는 작업은 UI를 통해서 하는 것이 더 적절합니다. 왜냐하면 wp_term_taxonomy에는 count라는 필드가 있는데, 이 값은 그 분류에 속하는 포스트의 개수를 기록해 두기 때문입니다. 직접 DB를 조작하는 경우는 이 값을 염두에 두고 작업하는 것이 좋습니다.

참고로 term_group은 이렇게 쓰라고 딱 잘라 정해진 규칙은 없습니다. 플러그인 등에서 적절히 앨리어스로 활용할 수 있어 보입니다. 또한 term_order는 0으로 두고 잘 사용하지는 않지만 카테고리 출력 순서가 중요한 경우 지정해 둘 수 있습니다.

플러그인 예제: Hello, World!

플러그인을 이제 실제로 만들어 봅니다. 아주 간단한 플러그인을 만들어 보도록 할께요, 무의미한 플러그인이지만 나름의 동작을 하도록 만듭니다.

  • 플러그인은 메인 페이지 아래 2개의 하위 메뉴를 가집니다.
    • 메인 페이지는 그냥 Hello, World를 출력하고 맙니다.
    • 하위 메뉴 1번에는 텍스트 상자 같은 위젯을 만들고 POST 폼 전송을 합니다.
    • 하위 메뉴 2번에는 1번과 비슷하지만 AJAX를 통해 콜을 하는 메뉴를 만들어 봅니다.
  • 플러그인은 또한 옵션 메뉴를 만듭니다.
    • 간단하게 단 1개의 옵션을 받습니다. 이것은 문자열로서 1번이나 2번 하위 메뉴에서 사용됩니다.
    • 옵션값은 플러그인이 비활성화되면 해당 항목을 DB에서 아예 삭제하여 초기화합니다.
  • 플러그인은 특별하게 어떤 페이지를 생성합니다.
    • 이 페이지는 특별히 어디에 노출되어 있지 않아서, 직접 다른 페이지가 링크를 걸어 주든지, 아니면 사용자가 명시적으로 URL을 타이핑해야 합니다.
    • 페이지의 콘텐츠는 그냥 Hello, World를 출력하고 맙니다.
  • 플러그인은 커스텀 포스트 타입을 하나 생성합니다.
    • 이 커스텀 포스트 타입은 기본 포스트처럼 대시보드에서 작성 가능합니다.
    • 몇몇 메타 필드들이 표시됩니다.

이러한 기능을 위해 자잘한 설명을 모두 할 이유는 없습니다. 필요한 설명은 소스 코드에 모두 첨부해 두도록 하고 몇몇 함수에 대해서만 본 문서에서 언급하는 식으로 진행하겠습니다.

소스 설명

소스는 ​github에 별도로 저장해 두었습니다.

당연한 말이지만 나열된 기능들은 워드프레스 기능의 아주 일부에 불과합니다. 더 자세한 내용은 코덱스를 참고하셔야겠죠. 하지만 거의 일상적으로 사용될 법한 기본적인 플러그인 기능만을 요점 정리 식으로 모아 보았습니다. 아마 플러그인을 제작할 때 참고하는 편리하리라 생각합니다. 별도의 기능을 위해 특정 도움 함수를 만들지 않고 거의 뼈대를 있는 그대로 노출하는 식으로 전개해 보았습니다.

플러그인 소스를 참고하면 알겠지만 대부분이 어떤 액션을 걸고, 그에 대한 콜백 함수를 정의하는 것이 플러그인이 하는 일입니다. 그러다 보니 액션 및 필터의 선언 및 콜백 함수 정의의 범벅으로 코드가 전개되기 마련입니다. 보통 함수를 정의하고 액션(또는 필터)를 선언하든가, 아니면 그 반대로 하는데 저는 액션을 먼저 선언하고, 그 아래에 바로 그 액션에 대한 콜백을 선언하는 식으로 구성했습니다.

사실 플러그인을 제작하다 보면 콜백 함수에서 다시 콜백 함수를 정의해 주어야 경우가 흔합니다. 이렇게 콜백의 깊이가 깊어지면 추적하기도 어렵고 때로는 코드가 지저분하게 될 수도 있습니다. 이 점을 유념하고 플러그인 제작을 하시는 것이 좋겠습니다.

또한 플러그인에서는 MVC 패턴 등을 기본적으로는 지원하지 않습니다. 굳이 어떻게든 PHP 상에서 MVC를 도입하려면 별도의 PHP 프레임워크와 연동을 시키든지, 아니면 정말 하나하나 만들든지… 해야 합니다. 그런데 고작 플러그인 하나 만들자고 프레임워크까지 도입하기는 너무 무겁습니다. 이에 제가 별도로 워드프레스 플러그인과 테마를 위한 MVC 패턴을 도입하기 위한 실험적인 작은 프로젝트를 진행하고 있습니다. GitHub에 있으니 별도로 참고하시면 감사하겠습니다. 이에 대한 설명은 다음에 차차 적어 보도록 하겠습니다.

활성화, 비활성화, 제거

플러그인이 활성화되거나 비활성화 될 때, 그리고 아예 플러그인이 삭제될 때 어떤 콜백 함수를 실행시켜 주도록 할 수 있습니다. 예제에서는 활성화 될 때 현재 사용자의 이름으로 특정 옵션 값을 설정하고, 비활성화될 때에는 그 값을 삭제하도록 만듭니다.

워드프레스 플러그인은 삭제 전 반드시 먼저 비활성화를 시켜 두어야 합니다. 또한 활성화, 비활성화, 삭제 시 불리는 콜백 함수는 어떠한 문자열도 출력해서는 안 됩니다. 워드프레스 코어는 이런 문자열이 발생되면 모두 에러로 간주합니다.

플러그인이 특정 테이블을 관리해야 할 때 활성화, 비활성화, 삭제 액션을 아주 유용히 활용하겠죠. 보통 활성화 될 때 별도의 테이블을 생성하고, 비활성화 하거나 삭제될 때 해당 테이블을 삭제하는 식으로 플러그인을 만듭니다.

메뉴

플러그인에 메뉴를 넣지 않는 경우는 거의 없을 겁니다. 플러그인의 관리자 메뉴 삽입과 관련된 훅은 'admin_menu'입니다. 이 훅으로 액션을 삽입하고 콜백에 적절히 구성된 메뉴를 삽입하면 됩니다.

워드프레스 플러그인 메뉴

그림은 플러그인 메뉴가 삽입되고 마우스 커서가 해당 메뉴 페이지 영역에 접근했을 때의 상황을 보여 주고 있습니다. 하나의 메뉴 페이지에 2개의 하위 메뉴 페이지가 삽입된 상태입니다. 그래서 커서가 근접하면 나오는 오른쪽 부분의 서브 메뉴 부분에 총 3개의 항목이 보이게 됩니다. 이렇게 메뉴 페이지를 삽입하면 항상 기본적으로 같은 이름의 하위 메뉴 페이지가 가장 먼저 삽입됩니다. 이 첫번째 메뉴가 의도한 동작이 아니라면 remove_submenu_page를 호출하 삭제하면 됩니다.

POST 액션

어떤 폼을 제작해서 사용자로부터 데이터를 받아 서버로 전달하게 해야 한다고 생각해 봅니다. 이 때 메소드를 지정해 주어야 하는데, 주로 GET이나 POST 방법을 사용합니다. 사실 어떤 주소를 사용하든지는 상관없습니다. 어떤 URL이든 서버가 올바르게 접근 가능한 형태로만 만들면 됩니다. 그리고 우리는 그 URL에 해당하는 곳에 적절하게 PHP 스크립트를 작성하면 되죠. 사용자가 보내준 데이터는 어쨌든 $_GET 변수$_POST 변수에 저장되어 있습니다. 하지만 우리는 워드프레스 플러그인 내부에서 데이터의 송수신을 모두 처리하기를 원합니다. 그래야 워드프레스에서 제공하는 기능을 활용할 수 있죠. DB 접속 등등의 코드를 우리가 다시 반복해야 할 이유가 없으니까요. 그러려면 우리는 워드프레스가 가이드하는 대로 맞춰서 데이터 전송을 준비해야 합니다.

워드프레스는 GET/POST 전송에 대해 하나의 URL을 정해 두고 모든 요청에 대해 처리하도록 디자인되어 있습니다. 대신 각 요청에는 자신이 어떤 요청인지를 스스로 밝히는 식별자를 두고 이 식별자에 의해 어떤 종류의 요청인지 제각각 구분하도록 되어 있습니다. 보통 이 주소는 wp-admin/admin-post.php입니다. 이 주소를 얻기 위해 워드프레스는 admin_url이라는 함수를 제공합니다. 보통 이렇게 사용해서 URL에 관계없이 일괄적으로 주소를 얻어 활용합니다.

<?php echo admin_url( 'admin-post.php' ); ?>

모든 종류의 폼이 이 주소로 들어오므로 반드시 이 요청이 어떤 것인지를 구분해 주어야 합니다. 그러므로 모든 폼은 요청에 대한 식별자를 가지고 있습니다. 이 식별자는 'action'이라는 이름이며 이 이름으로 전달된 값으로 모든 요청을 구분합니다. 값은 그냥 문자열이므로 사용자가 알아서 고유하게 구분하기만 하면 됩니다. 이 값은 사용자에게는 보여질 필요가 없으므로 대부분 input 태그에 hidden 타입으로 미리 폼에 삽입되어 있습니다.

한편 사용자가 이렇게 데이터를 처리해주기를 원한다는 사실을 코어도 알고 있어야 합니다. 액션을 등록해야죠. 이를 위해서 코어가 제공하는 훅이 바로 'admin_post_$action'입니다. 여기서 $action 부분은 자신이 원하는 'action' 변수의 값으로 만들어야 하는 규칙이 있습니다. 이렇게 해 두면 코어는 알아서 폼을 전달 받았을 때 우리가 원하는 함수를 호출해 줍니다. 콜백 함수에서는 적절히 wpdb 같은 것을 이용해서 원하는 동작을 처리하고 원하는 곳으로 사용자를 리다이렉트 시키면 됩니다.

당연하지만 폼으로 전달받은 값이 항상 올바른 값이라고는 가정해서는 안 됩니다. 항상 폼으로 전달된 변수가 혹시나 서버를 공격하는 위험한 코드가 아닌지 검증해야죠. 이에 대해서는 data validation 코덱스를 참고하세요. 그리고 폼에 nonce를 넣어 보호하는 것도 잊지 마세요.

AJAX 액션

Rewrite

로컬라이즈

쿼리 파싱

템플릿 라우팅

wordpress/making_plugin_tutorial.1421898969.txt.gz · 마지막으로 수정됨: 2015/01/22 03:56 저자 changwoo

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki