워드프레스 플러그인 제작 방법을 숏코드, 관리자 설정 페이지, iframe 위젯 출력, Gutenberg 블록 확장 관점에서 실무 예제로 정리합니다.

워드프레스 플러그인 제작이 필요한 이유
워드프레스 플러그인 제작은 기존 테마를 수정하지 않고도 사이트 기능을 확장할 수 있는 가장 안정적인 방법입니다. 예를 들어 특정 외부 서비스의 데이터를 글 본문, 페이지, 사이드바에 표시하거나 관리자 설정값에 따라 다른 위젯을 출력해야 한다면 테마의 functions.php에 코드를 넣는 것보다 별도 플러그인으로 분리하는 것이 유지보수에 유리합니다.
이번 글에서는 알뜰폰 요금제 위젯처럼 외부 데이터를 워드프레스에 표시하는 플러그인을 만든다고 가정하고, 다음 구조를 기준으로 설명합니다.
plan-apthow-widget/
├── plan-apthow-widget.php
├── includes/
│ ├── class-plan-apthow-widget.php
│ └── class-plan-apthow-admin.php
├── assets/
│ ├── css/
│ │ └── public.css
│ └── js/
│ └── block.js
└── readme.txt
워드프레스 플러그인 개발은 공식 Plugin Handbook에서 권장하는 방식처럼 플러그인 헤더, 훅, 숏코드, 관리자 메뉴, 설정 API 등을 조합해 기능을 확장하는 구조로 접근하는 것이 좋습니다.
1. 플러그인 기본 파일 만들기
워드프레스 플러그인은 최소한 하나의 PHP 파일만 있어도 동작합니다.
wp-content/plugins/plan-apthow-widget/plan-apthow-widget.php
<?php
/**
* Plugin Name: Plan Apthow Widget
* Plugin URI: https://plan.apthow.com/widget/wordpress/
* Description: 워드프레스 글, 페이지, 사이드바에 외부 요금제 위젯을 표시하는 플러그인입니다.
* Version: 1.0.0
* Author: Apthow
* License: GPLv2 or later
* Text Domain: plan-apthow-widget
*/
if (!defined('ABSPATH')) {
exit;
}
define('PLAN_APTHOW_WIDGET_VERSION', '1.0.0');
define('PLAN_APTHOW_WIDGET_URL', plugin_dir_url(__FILE__));
define('PLAN_APTHOW_WIDGET_PATH', plugin_dir_path(__FILE__));
require_once PLAN_APTHOW_WIDGET_PATH . 'includes/class-plan-apthow-widget.php';
require_once PLAN_APTHOW_WIDGET_PATH . 'includes/class-plan-apthow-admin.php';
function plan_apthow_widget_init() {
new Plan_Apthow_Widget();
new Plan_Apthow_Admin();
}
add_action('plugins_loaded', 'plan_apthow_widget_init');
핵심은 다음입니다.
if (!defined('ABSPATH')) {
exit;
}
이 코드는 플러그인 PHP 파일이 브라우저에서 직접 실행되는 것을 막기 위한 기본 보안 코드입니다.

2. 숏코드로 위젯 출력하기
워드프레스에서 가장 간단하게 본문 안에 기능을 삽입하는 방법은 숏코드입니다. 워드프레스는 기본적으로 숏코드를 통해 사용자 정의 출력물을 글, 페이지, 위젯 영역에 넣을 수 있습니다. 블록 편집기에서도 Shortcode 블록을 통해 숏코드 삽입이 가능합니다.
includes/class-plan-apthow-widget.php
<?php
if (!defined('ABSPATH')) {
exit;
}
class Plan_Apthow_Widget
{
public function __construct()
{
add_shortcode('plan_apthow_widget', array($this, 'render_shortcode'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_assets'));
}
public function enqueue_assets()
{
wp_enqueue_style(
'plan-apthow-widget-public',
PLAN_APTHOW_WIDGET_URL . 'assets/css/public.css',
array(),
PLAN_APTHOW_WIDGET_VERSION
);
}
public function render_shortcode($atts)
{
$atts = shortcode_atts(
array(
'type' => get_option('plan_apthow_default_type', 'lowest'),
),
$atts,
'plan_apthow_widget'
);
$allowed_types = array(
'lowest',
'lifetime',
'under_100',
'unlimited',
'data_10gb',
'parents',
'students',
'lifetime_qos',
);
$type = sanitize_key($atts['type']);
if (!in_array($type, $allowed_types, true)) {
$type = 'lowest';
}
$iframe_url = add_query_arg(
array(
'type' => $type,
'source' => 'wordpress',
),
'https://plan.apthow.com/widget/embed'
);
return sprintf(
'<div class="plan-apthow-widget-wrap">
<iframe src="%s" loading="lazy" referrerpolicy="no-referrer-when-downgrade" title="알뜰폰 요금제 위젯"></iframe>
</div>',
esc_url($iframe_url)
);
}
}
사용자는 글 본문에 다음처럼 넣을 수 있습니다.
[plan_apthow_widget]
또는 특정 위젯 타입을 지정할 수 있습니다.
[plan_apthow_widget type="data_10gb"]
[plan_apthow_widget type="parents"]
[plan_apthow_widget type="students"]
3. iframe 방식으로 외부 위젯 출력하기
외부 데이터를 워드프레스 안에 표시할 때는 크게 두 가지 방식이 있습니다.
| REST API 호출 | 디자인 자유도 높음 | API 인증, 캐싱, 에러 처리 필요 |
| iframe 삽입 | 구현 간단, 서버 데이터 자동 반영 | 내부 DOM 제어 어려움 |
요금제 순위처럼 외부 서버에서 데이터를 관리하고 워드프레스에서는 표시만 하면 되는 구조라면 iframe 방식이 실무적으로 적합합니다.
$iframe_url = add_query_arg(
array(
'type' => $type,
'source' => 'wordpress',
),
'https://plan.apthow.com/widget/embed'
);
여기서 중요한 점은 URL을 직접 문자열로 붙이지 않고 add_query_arg()를 사용한다는 점입니다.
4. 반응형 CSS 작성하기
assets/css/public.css
.plan-apthow-widget-wrap {
width: 100%;
max-width: 100%;
margin: 24px 0;
}
.plan-apthow-widget-wrap iframe {
width: 100%;
min-height: 520px;
border: 0;
border-radius: 12px;
overflow: hidden;
background: #ffffff;
}
@media (max-width: 768px) {
.plan-apthow-widget-wrap iframe {
min-height: 640px;
border-radius: 8px;
}
}
모바일에서는 리스트가 세로로 길어질 수 있으므로 min-height를 PC보다 크게 잡는 경우가 많습니다.
5. 관리자 설정 페이지 만들기
플러그인에서 기본 위젯 타입을 관리자 화면에서 선택하게 만들려면 admin_menu, register_setting을 사용합니다.
includes/class-plan-apthow-admin.php
<?php
if (!defined('ABSPATH')) {
exit;
}
class Plan_Apthow_Admin
{
public function __construct()
{
add_action('admin_menu', array($this, 'add_settings_page'));
add_action('admin_init', array($this, 'register_settings'));
}
public function add_settings_page()
{
add_options_page(
'플랜앱트하우 위젯 설정',
'플랜앱트하우 위젯',
'manage_options',
'plan-apthow-widget',
array($this, 'render_settings_page')
);
}
public function register_settings()
{
register_setting(
'plan_apthow_widget_settings',
'plan_apthow_default_type',
array(
'sanitize_callback' => 'sanitize_key',
'default' => 'lowest',
)
);
}
public function render_settings_page()
{
if (!current_user_can('manage_options')) {
return;
}
$default_type = get_option('plan_apthow_default_type', 'lowest');
?>
<div class="wrap">
<h1>플랜앱트하우 위젯 설정</h1>
<form method="post" action="options.php">
<?php settings_fields('plan_apthow_widget_settings'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="plan_apthow_default_type">기본 위젯 타입</label>
</th>
<td>
<select name="plan_apthow_default_type" id="plan_apthow_default_type">
<option value="lowest" <?php selected($default_type, 'lowest'); ?>>최저가 TOP</option>
<option value="lifetime" <?php selected($default_type, 'lifetime'); ?>>평생할인 TOP</option>
<option value="under_100" <?php selected($default_type, 'under_100'); ?>>100원 이하 TOP</option>
<option value="unlimited" <?php selected($default_type, 'unlimited'); ?>>데이터 무제한 TOP</option>
<option value="data_10gb" <?php selected($default_type, 'data_10gb'); ?>>10GB 이상 TOP</option>
<option value="parents" <?php selected($default_type, 'parents'); ?>>부모님 추천 TOP</option>
<option value="students" <?php selected($default_type, 'students'); ?>>학생 추천 TOP</option>
<option value="lifetime_qos" <?php selected($default_type, 'lifetime_qos'); ?>>평생할인 + QoS TOP</option>
</select>
<p class="description">
숏코드에 type 값이 없을 때 기본으로 출력할 위젯입니다.
</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
<hr>
<h2>숏코드 사용 예시</h2>
<code>[plan_apthow_widget]</code><br>
<code>[plan_apthow_widget type="data_10gb"]</code>
</div>
<?php
}
}
6. Gutenberg 블록 지원 구조
Gutenberg 블록 편집기는 워드프레스의 기본 편집 방식입니다. 워드프레스 공식 문서에서도 블록 편집기를 모듈식 블록 기반 편집 환경으로 설명하고 있습니다.
간단한 방식으로는 블록을 직접 개발하지 않고 Shortcode 블록을 사용해도 됩니다.
[plan_apthow_widget type="lowest"]
조금 더 고도화하려면 block.json, index.js, render_callback을 사용하는 동적 블록으로 구현할 수 있습니다.
예시 구조는 다음과 같습니다.
blocks/
└── plan-widget/
├── block.json
├── index.js
└── render.php
블록 등록은 PHP에서 다음처럼 처리합니다.
add_action('init', function () {
register_block_type(__DIR__ . '/blocks/plan-widget');
});
블록 개발은 block.json 기반 등록 방식이 현재 워드프레스 블록 개발에서 일반적으로 사용되는 방식입니다.
7. 보안 처리에서 중요한 부분
워드프레스 플러그인 제작 시 반드시 신경 써야 하는 부분은 입력값 검증과 출력값 이스케이프입니다.
| URL 출력 | esc_url() |
| HTML 속성 출력 | esc_attr() |
| 일반 텍스트 출력 | esc_html() |
| key 형태 값 정리 | sanitize_key() |
| textarea 값 정리 | sanitize_textarea_field() |
| 관리자 권한 확인 | current_user_can() |
예를 들어 숏코드 타입은 사용자가 직접 입력할 수 있으므로 반드시 검증해야 합니다.
$type = sanitize_key($atts['type']);
if (!in_array($type, $allowed_types, true)) {
$type = 'lowest';
}
출력할 때도 다음처럼 처리해야 합니다.
esc_url($iframe_url)
8. 실제 배포 전 체크리스트
워드프레스 플러그인을 배포하기 전에는 다음 항목을 확인하는 것이 좋습니다.
[ ] 플러그인 헤더 정보가 정확한가
[ ] 직접 접근 방지 코드가 있는가
[ ] 숏코드 입력값을 검증하는가
[ ] 출력값에 esc_url, esc_html, esc_attr 처리를 했는가
[ ] 관리자 설정 저장 시 sanitize_callback을 사용하는가
[ ] 모바일 화면에서 iframe 높이가 깨지지 않는가
[ ] 비활성화 후에도 사이트 오류가 발생하지 않는가
[ ] PHP 8 이상에서 경고가 발생하지 않는가
[ ] Gutenberg 편집기에서 정상 출력되는가
마무리
워드프레스 플러그인 제작은 단순히 PHP 파일 하나를 추가하는 작업이 아니라, 숏코드, 관리자 설정, 보안 처리, 프론트 출력 구조를 함께 설계하는 작업입니다.
특히 외부 데이터를 워드프레스에 표시하는 위젯형 플러그인은 iframe 구조를 사용하면 API 인증이나 데이터 동기화 부담을 줄이면서도 안정적으로 최신 콘텐츠를 제공할 수 있습니다.
실제 예제로 구현된 플랜앱트하우 워드프레스 위젯은 아래 페이지에서 확인할 수 있습니다.
플랜앱트하우 워드프레스 위젯 소개
https://plan.apthow.com/widget/wordpress/
플랜앱트하우 워드프레스 플러그인 | 알뜰폰 요금제 위젯
플랜앱트하우 워드프레스 플러그인으로 알뜰폰 최저가, 평생할인, 데이터 무제한 TOP 요금제 위젯을 숏코드와 Gutenberg 블록으로 자동 표시하세요.
plan.apthow.com
추천 태그
워드프레스,워드프레스플러그인,워드프레스플러그인제작,PHP,숏코드,Gutenberg,워드프레스개발,플러그인개발,iframe,워드프레스위젯,플랜앱트하우,알뜰폰위젯,워드프레스SEO
'실무개발 > BackEnd' 카테고리의 다른 글
| Docker로 워드프레스 설치하고 알뜰폰 위젯 플러그인 만들기 (0) | 2026.06.20 |
|---|---|
| PHP API 리팩토링 후기: SRP 원칙으로 요금제 검색 API 구조 개선하기 (0) | 2026.06.18 |
| 스프링부트 Playwright로 HTML을 PDF로 변환할 때 500 에러 해결 방법 (0) | 2026.06.17 |
| PHP7 → PHP8 마이그레이션 오류 모음 및 실무 해결 방법 총정리 (0) | 2026.06.12 |
| Spring Boot WebSocket을 Shared Worker로 운영하는 방법: 여러 탭 연결 최적화 실무 정리 (0) | 2026.06.09 |
