헤드리스 WordPress: 분리된 WordPress 생성의 기복
게시 됨: 2022-03-10WordPress는 단순한 블로그 작성 도구로 시작부터 먼 길을 왔습니다. 15년이라는 긴 시간이 지난 후 개발자와 비개발자 모두에게 최고의 CMS 선택이 되었습니다. WordPress는 이제 웹상의 상위 1천만 개 사이트 중 약 30%를 차지합니다.
REST API가 WordPress 코어에 번들된 이후로 개발자는 JavaScript 프레임워크 또는 라이브러리를 사용하여 프런트 엔드 부분을 작성하는 분리된 방식으로 이를 실험하고 사용할 수 있습니다. Infinum에서 우리는 '고전적인' 방식으로 WordPress를 사용하고 있었습니다. 잠시 후 우리는 분리된 접근 방식을 시도하고 싶었습니다. 이 기사에서는 우리가 달성하고자 했던 것과 목표를 구현하는 동안 직면한 것에 대한 개요를 공유할 것입니다.
이 접근 방식의 이점을 얻을 수 있는 여러 유형의 프로젝트가 있습니다. 예를 들어, 단순한 프리젠테이션 사이트나 WordPress를 백엔드로 사용하는 사이트는 분리 접근 방식의 주요 후보입니다.
최근 몇 년 동안 업계는 감사하게도 성능에 더 많은 관심을 기울이기 시작했습니다. 그러나 사용하기 쉽고 포괄적이고 다재다능한 소프트웨어인 WordPress에는 모든 프로젝트에서 반드시 활용되지는 않는 수많은 옵션이 있습니다. 결과적으로 웹 사이트 성능이 저하될 수 있습니다.
추천 자료 : 히트맵을 사용하여 WordPress 웹사이트에서 클릭을 추적하는 방법
웹사이트 응답 시간이 길어 밤에 잠이 오지 않는 경우 이 방법이 도움이 됩니다. 나는 분리된 WordPress를 만드는 기본 사항과 다음을 포함하여 배운 몇 가지 교훈을 다룰 것입니다.
- "분리된 WordPress"의 의미
- 기본 WordPress REST API 작업
- 분리된 JSON 접근 방식으로 성능 향상
- 보안 문제
그렇다면 분리된 WordPress는 정확히 무엇입니까?
WordPress가 프로그래밍 되는 방식과 관련하여 한 가지 확실한 것은 많은 개발자에게 익숙한 MVC (모델 보기 컨트롤러) 디자인 패턴을 따르지 않는다는 것입니다. 그 역사와 "b2"(자세한 내용은 여기)라는 오래된 블로깅 플랫폼의 일종이기 때문에 대부분 절차적 방식(함수 기반 코드 사용)으로 작성되었습니다. WordPress 핵심 개발자는 다른 개발자가 특정 기능을 수정하거나 확장할 수 있는 후크 시스템을 사용했습니다.
작동하는 관리 인터페이스가 장착된 올인원 시스템입니다. 데이터베이스 연결을 관리하고 사용자 인증, 라우팅 등을 처리하는 유용한 API가 많이 노출되어 있습니다.
그러나 REST API 덕분에 데이터 조작 및 데이터베이스 상호 작용을 처리하는 일종의 모델 및 컨트롤러 번들로 WordPress 백엔드를 분리하고 REST API 컨트롤러를 사용하여 다양한 API 끝점을 사용하는 별도의 뷰 레이어와 상호 작용할 수 있습니다. MVC 분리 외에도 보안상의 이유로 또는 속도 향상을 위해 JS 앱을 아래 스키마와 같이 별도의 서버에 배치할 수 있습니다.
분리 접근 방식 사용의 장점
이 접근 방식을 사용하려는 한 가지 이유는 관심사를 분리하는 것입니다. 프론트엔드와 백엔드는 엔드포인트를 통해 상호작용합니다. 각각은 각각의 작업에 대해 특별히 최적화될 수 있는 별도의 서버에 있을 수 있습니다. 즉, 개별적으로 PHP 앱을 실행하고 Node.js 앱을 실행합니다.
프론트엔드를 백엔드에서 분리하면 CMS를 변경하지 않고도 나중에 다시 디자인하기가 더 쉽습니다. 또한 프론트엔드 개발자는 백엔드가 제공하는 데이터로 무엇을 할 것인지만 신경 쓰면 됩니다. 이를 통해 창의력을 발휘하고 ReactJS, Vue 또는 Angular와 같은 최신 라이브러리를 사용하여 매우 동적인 웹 앱을 제공할 수 있습니다. 예를 들어 앞서 언급한 라이브러리를 사용하면 프로그레시브 웹 앱을 빌드하기가 더 쉽습니다.
또 다른 장점은 웹사이트 보안에 반영됩니다. 백엔드를 통해 웹사이트를 악용하는 것은 대중에게 크게 숨겨져 있기 때문에 더 어려워집니다.
추천 자료 : 프로세스로서의 WordPress 보안
분리된 접근 방식 사용의 단점
첫째, WordPress를 분리한다는 것은 두 개의 개별 인스턴스를 유지 관리한다는 의미입니다.
- 백엔드용 워드프레스
- 시기 적절한 보안 업데이트를 포함하는 별도의 프런트 엔드 앱.
둘째, 일부 프론트엔드 라이브러리는 학습 곡선이 더 가파릅니다. 새로운 언어를 배우는 데 많은 시간이 걸리거나(템플릿을 위해 HTML 및 CSS에만 익숙한 경우) 프로젝트에 JavaScript 전문가를 추가로 데려와야 합니다.
셋째, 프론트엔드를 분리하면 WYSIWYG 편집기의 위력을 잃게 되고 워드프레스의 '라이브 미리보기' 버튼도 작동하지 않습니다.
WordPress REST API 작업
코드를 더 자세히 살펴보기 전에 WordPress REST API에 대해 몇 가지 더 알아보겠습니다. WordPress의 REST API의 모든 기능은 2016년 12월 6일 버전 4.7과 함께 제공되었습니다.
WordPress REST API를 사용하면 JSON 개체를 보내고 받아 WordPress 설치와 원격으로 상호 작용할 수 있습니다.
프로젝트 설정
최신 WordPress 설치와 함께 제공되므로 Twenty Seventeen 테마로 작업합니다. Varying Vagrant Vagrants에서 작업 중이며 URL https://dev.wordpress.test/
로 테스트 사이트를 설정했습니다. 이 URL은 기사 전체에서 사용됩니다. 또한 작업할 테스트 데이터가 있도록 wordpress.org Theme Review Teams 저장소에서 게시물을 가져올 것입니다. 그러나 먼저 기본 엔드포인트 작업에 익숙해지고 자체 사용자 지정 엔드포인트를 생성합니다.
기본 REST 끝점에 액세스
이미 언급했듯이 WordPress에는 /wp-json/
경로로 이동하여 검사할 수 있는 여러 내장 엔드포인트가 있습니다.
https://dev.wordpress.test/wp-json/
이 URL을 브라우저에 직접 넣거나 우편 배달부 앱에 추가하면 다음과 같은 WordPress REST API에서 JSON 응답을 얻을 수 있습니다.
{ "name": "Test dev site", "description": "Just another WordPress site", "url": "https://dev.wordpress.test", "home": "https://dev.wordpress.test", "gmt_offset": "0", "timezone_string": "", "namespaces": [ "oembed/1.0", "wp/v2" ], "authentication": [], "routes": { "/": { "namespace": "", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/" } }, "/oembed/1.0": { "namespace": "oembed/1.0", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "namespace": { "required": false, "default": "oembed/1.0" }, "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/oembed/1.0" } }, ... "wp/v2": { ...
따라서 REST를 사용하여 사이트의 모든 게시물을 가져오려면 https://dev.wordpress.test/wp-json/wp/v2/posts
로 이동해야 합니다. wp/v2/
는 게시물, 페이지, 미디어, 분류, 카테고리 등과 같은 예약된 핵심 끝점을 표시합니다.
그렇다면 사용자 지정 끝점을 어떻게 추가합니까?
사용자 지정 REST 끝점 만들기
기존 끝점에 새 끝점 또는 추가 필드를 추가한다고 가정해 보겠습니다. 우리가 할 수 있는 몇 가지 방법이 있습니다. 첫째, 사용자 정의 게시물 유형을 만들 때 자동으로 수행할 수 있습니다. 예를 들어 문서 끝점을 만들고 싶습니다. 작은 테스트 플러그인을 만들어 봅시다. wp-content/plugins 폴더에 test-documentation 폴더를 만들고 다음과 같은 documentation.php 파일을 추가합니다.
<?php /** * Test plugin * * @since 1.0.0 * @package test_plugin * * @wordpress-plugin * Plugin Name: Test Documentation Plugin * Plugin URI: * Description: The test plugin that adds rest functionality * Version: 1.0.0 * Author: Infinum <[email protected]> * Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: test-plugin */ namespace Test_Plugin; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } /** * Class that holds all the necessary functionality for the * documentation custom post type * * @since 1.0.0 */ class Documentation { /** * The custom post type slug * * @var string * * @since 1.0.0 */ const PLUGIN_NAME = 'documentation-plugin'; /** * The custom post type slug * * @var string * * @since 1.0.0 */ const POST_TYPE_SLUG = 'documentation'; /** * The custom taxonomy type slug * * @var string * * @since 1.0.0 */ const TAXONOMY_SLUG = 'documentation-category'; /** * Register custom post type * * @since 1.0.0 */ public function register_post_type() { $args = array( 'label' => esc_html( 'Documentation', 'test-plugin' ), 'public' => true, 'menu_position' => 47, 'menu_icon' => 'dashicons-book', 'supports' => array( 'title', 'editor', 'revisions', 'thumbnail' ), 'has_archive' => false, 'show_in_rest' => true, 'publicly_queryable' => false, ); register_post_type( self::POST_TYPE_SLUG, $args ); } /** * Register custom tag taxonomy * * @since 1.0.0 */ public function register_taxonomy() { $args = array( 'hierarchical' => false, 'label' => esc_html( 'Documentation tags', 'test-plugin' ), 'show_ui' => true, 'show_admin_column' => true, 'update_count_callback' => '_update_post_term_count', 'show_in_rest' => true, 'query_var' => true, ); register_taxonomy( self::TAXONOMY_SLUG, [ self::POST_TYPE_SLUG ], $args ); } } $documentation = new Documentation(); add_action( 'init', [ $documentation, 'register_post_type' ] ); add_action( 'init', [ $documentation, 'register_taxonomy' ] );
새 게시물 유형 및 분류를 등록하고 show_in_rest
인수를 true
로 설정하여 WordPress는 /wp/v2/
네임스페이스에 REST 경로를 자동으로 생성했습니다. 이제 https://dev.wordpress.test/wp-json/wp/v2/documentation
및 https://dev.wordpress.test/wp-json/wp/v2/documentation-category
끝점을 사용할 수 있습니다. https://dev.wordpress.test/?post_type=documentation
으로 이동하는 새로 생성된 문서 사용자 정의 게시물에 게시물을 추가하면 다음과 같은 응답이 제공됩니다.
[ { "id": 4, "date": "2018-06-11T19:48:51", "date_gmt": "2018-06-11T19:48:51", "guid": { "rendered": "https://dev.wordpress.test/?post_type=documentation&p=4" }, "modified": "2018-06-11T19:48:51", "modified_gmt": "2018-06-11T19:48:51", "slug": "test-documentation", "status": "publish", "type": "documentation", "link": "https://dev.wordpress.test/documentation/test-documentation/", "title": { "rendered": "Test documentation" }, "content": { "rendered": "
이것은 일부 문서 내용입니다.
\N", "보호된": 거짓 }, "추천 미디어": 0, "템플릿": "", "문서 범주": [ 2 ], "_연결": { "자신": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4" } ], "수집": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation" } ], "에 대한": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/types/documentation" } ], "버전 기록": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation/4/revisions" } ], "wp:첨부 파일": [ { "href": "https://dev.wordpress.test/wp-json/wp/v2/media?parent=4" } ], "wp:용어": [ { "분류": "문서 범주", "포함 가능": 사실, "href": "https://dev.wordpress.test/wp-json/wp/v2/documentation-category?post=4" } ], "퀴리": [ { "이름": "wp", "href": "https://api.w.org/{rel}", "템플릿": 참 } ] } } ]
이것은 단일 페이지 애플리케이션을 위한 훌륭한 출발점입니다. 사용자 지정 끝점을 추가할 수 있는 또 다른 방법은 rest_api_init
후크에 연결하고 끝점을 직접 만드는 것입니다. 등록한 경로와 약간 다른 custom-documentation
경로를 추가해 보겠습니다. 여전히 동일한 플러그인에서 작업하고 있으므로 다음을 추가할 수 있습니다.
/** * Create a custom endpoint * * @since 1.0.0 */ public function create_custom_documentation_endpoint() { register_rest_route( self::PLUGIN_NAME . '/v1', '/custom-documentation', array( 'methods' => 'GET', 'callback' => [ $this, 'get_custom_documentation' ], ) ); } /** * Create a callback for the custom documentation endpoint * * @return string JSON that indicates success/failure of the update, * or JSON that indicates an error occurred. * @since 1.0.0 */ public function get_custom_documentation() { /* Some permission checks can be added here. */ // Return only documentation name and tag name. $doc_args = array( 'post_type' => self::POST_TYPE_SLUG, 'post_status' => 'publish', 'perm' => 'readable' ); $query = new \WP_Query( $doc_args ); $response = []; $counter = 0; // The Loop if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $post_tags = get_the_terms( $post_id, self::TAXONOMY_SLUG ); $response[ $counter ]['title'] = get_the_title(); foreach ( $post_tags as $tags_key => $tags_value ) { $response[ $counter ]['tags'][] = $tags_value->name; } $counter++; } } else { $response = esc_html__( 'There are no posts.', 'documentation-plugin' ); } /* Restore original Post Data */ wp_reset_postdata(); return rest_ensure_response( $response ); }
다음과 같이 create_custom_documentation_endpoint()
메서드를 rest_api_init
후크에 연결합니다.
add_action( 'rest_api_init', [ $documentation, 'create_custom_documentation_endpoint' ] );
이렇게 하면 해당 경로에 대한 응답을 반환하는 콜백과 함께 https://dev.wordpress.test/wp-json/documentation-plugin/v1/custom-documentation
에 사용자 지정 경로가 추가됩니다.
[{ "title": "Another test documentation", "tags": ["Another tag"] }, { "title": "Test documentation", "tags": ["REST API", "test tag"] }]
REST API로 할 수 있는 다른 많은 작업이 있습니다(자세한 내용은 REST API 핸드북에서 찾을 수 있음).
기본 REST API를 사용할 때 긴 응답 시간 해결
분리된 WordPress 사이트를 구축하려고 시도한 사람에게 이것은 새로운 것이 아닙니다. REST API가 느립니다.
우리 팀과 나는 클라이언트 사이트(분리되지 않음)에서 이상한 WordPress-lagging REST API를 처음 접했습니다. 여기서 사용자 지정 끝점을 사용하여 Advanced Custom Fields Pro 를 사용하여 만든 다른 메타 정보와 함께 Google 지도의 위치 목록을 가져왔습니다. 플러그인. 웹 서버나 다른 네트워크 리소스의 응답성을 나타내는 데 사용되는 첫 번째 바이트(TTFB)가 3초 이상 걸리는 것으로 밝혀졌습니다.
약간의 조사 후에 우리는 기본 REST API 호출이 실제로 정말 느리다는 것을 깨달았습니다. 특히 추가 플러그인으로 사이트를 "부담"했을 때 그렇습니다. 그래서 우리는 작은 테스트를 했습니다. 우리는 몇 가지 인기 있는 플러그인을 설치했고 몇 가지 흥미로운 결과를 발견했습니다. 우편 배달부 앱은 41.9KB의 응답 크기에 대해 1.97초의 로드 시간을 제공했습니다. Chrome의 로드 시간은 1.25초였습니다(TTFB는 1.25초, 콘텐츠 다운로드는 3.96ms). 게시물의 간단한 목록을 검색합니다. 분류, 사용자 데이터, 추가 메타 필드가 없습니다.
왜 이런 일이 일어났습니까?
기본 WordPress에서 REST API에 액세스하면 사용되지 않더라도 전체 WordPress 코어를 로드하여 엔드포인트를 제공합니다. 또한 플러그인을 더 많이 추가할수록 상황이 악화됩니다. 기본 REST 컨트롤러 WP_REST_Controller
는 간단한 웹 페이지를 구축할 때 필요한 것보다 훨씬 더 많은 작업을 수행하는 정말 큰 클래스입니다. 경로 등록, 권한 확인, 항목 생성 및 삭제 등을 처리합니다.
이 문제에 대한 두 가지 일반적인 해결 방법이 있습니다.
- 플러그인 로드를 가로채고 간단한 REST 응답을 제공해야 할 때 플러그인 로드를 모두 방지합니다.
- 최소한의 WordPress만 로드하고 데이터를 일시적으로 저장한 다음 사용자 지정 페이지를 사용하여 데이터를 가져옵니다.
분리된 JSON 접근 방식으로 성능 향상
간단한 프리젠테이션 사이트로 작업할 때 REST API가 제공하는 모든 기능이 필요하지는 않습니다. 물론 여기에서 좋은 계획이 중요합니다. REST API 없이 사이트를 구축하고 몇 년 후에 사이트에 연결하고 싶다고 말하거나 REST API 기능을 사용해야 하는 모바일 앱을 만들고 싶지는 않을 것입니다. 당신은?
이러한 이유로 간단한 JSON 데이터를 제공할 때 도움이 될 수 있는 두 가지 WordPress 기능을 활용했습니다.
- 캐싱을 위한 Transients API,
-
SHORTINIT
상수를 사용하여 필요한 최소 WordPress 로드.
단순 분리된 페이지 끝점 만들기
우리가 말하는 효과를 보여줄 작은 플러그인을 만들어 봅시다. 먼저 json-transient
플러그인에 다음과 같은 wp-config-simple.php 파일을 추가합니다.
<?php /** * Create simple wp configuration for the routes * * @since 1.0.0 * @package json-transient */ define( 'SHORTINIT', true ); $parse_uri = explode( 'wp-content', $_SERVER['SCRIPT_FILENAME'] ); require_once filter_var( $parse_uri[0] . 'wp-load.php', FILTER_SANITIZE_STRING );
define( 'SHORTINIT', true );
wp-settings.php 파일에서 볼 수 있듯이 대부분의 WordPress 코어 파일이 로드되지 않도록 합니다.
여전히 일부 WordPress 기능이 필요할 수 있으므로 파일(예: wp-load.php )을 수동으로 요구할 수 있습니다. wp-load.php 는 WordPress 설치 루트에 있으므로 $_SERVER['SCRIPT_FILENAME']
을 사용하여 파일 경로를 가져온 다음 wp-content
문자열로 해당 문자열을 확장하여 가져옵니다. 이렇게 하면 두 값이 있는 배열이 반환됩니다.
- 우리 설치의 루트;
- 파일 경로의 나머지 부분(우리에게 관심이 없음).
우리는 WordPress의 기본 설치를 사용하고 있으며, 예를 들어 다른 파일 구성에서 WordPress를 분할하는 Bedrock 상용구에서와 같이 수정된 것이 아니라 사용하고 있음을 명심하십시오.
마지막으로 보안을 위해 약간의 정리가 포함된 wp-load.php 파일이 필요합니다.
init.php 파일에 다음을 추가합니다.
* Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: json-transient */ namespace Json_Transient; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } class Init { /** * Get the array of allowed types to do operations on. * * @return array * * @since 1.0.0 */ public function get_allowed_post_types() { return array( 'post', 'page' ); } /** * Check if post type is allowed to be save in transient. * * @param string $post_type Get post type. * @return boolean * * @since 1.0.0 */ public function is_post_type_allowed_to_save( $post_type = null ) { if( ! $post_type ) { return false; } $allowed_types = $this->get_allowed_post_types(); if ( in_array( $post_type, $allowed_types, true ) ) { return true; } return false; } /** * Get Page cache name for transient by post slug and type. * * @param string $post_slug Page Slug to save. * @param string $post_type Page Type to save. * @return string * * @since 1.0.0 */ public function get_page_cache_name_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $post_slug = str_replace( '__trashed', '', $post_slug ); return 'jt_data_' . $post_type . '_' . $post_slug; } /** * Get full post data by post slug and type. * * @param string $post_slug Page Slug to do Query by. * @param string $post_type Page Type to do Query by. * @return array * * @since 1.0.0 */ public function get_page_data_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $page_output = ''; $args = array( 'name' => $post_slug, 'post_type' => $post_type, 'posts_per_page' => 1, 'no_found_rows' => true ); $the_query = new \WP_Query( $args ); if ( $the_query->have_posts() ) { while ( $the_query->have_posts() ) { $the_query->the_post(); $page_output = $the_query->post; } wp_reset_postdata(); } return $page_output; } /** * Return Page in JSON format * * @param string $post_slug Page Slug. * @param string $post_type Page Type. * @return json * * @since 1.0.0 */ public function get_json_page( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } return wp_json_encode( $this->get_page_data_by_slug( $post_slug, $post_type ) ); } /** * Update Page to transient for caching on action hooks save_post. * * @param int $post_id Saved Post ID provided by action hook. * * @since 1.0.0 */ public function update_page_transient( $post_id ) { $post_status = get_post_status( $post_id ); $post = get_post( $post_id ); $post_slug = $post->post_name; $post_type = $post->post_type; $cache_name = $this->get_page_cache_name_by_slug( $post_slug, $post_type ); if( ! $cache_name ) { return false; } if( $post_status === 'auto-draft' || $post_status === 'inherit' ) { return false; } else if( $post_status === 'trash' ) { delete_transient( $cache_name ); } else { if( $this->is_post_type_allowed_to_save( $post_type ) ) { $cache = $this->get_json_page( $post_slug, $post_type ); set_transient( $cache_name, $cache, 0 ); } } } } $init = new Init(); add_action( 'save_post', [ $init, 'update_page_transient' ] );
위 코드의 도우미 메서드를 사용하면 일부 캐싱을 수행할 수 있습니다.
-
get_allowed_post_types()
이 방법을 사용하면 게시물 유형이 사용자 지정 '엔드포인트'에 표시할 수 있음을 알릴 수 있습니다. 이것을 확장할 수 있으며 실제로 이 메서드를 필터링 가능하게 만든 플러그인을 사용하여 추가 항목을 추가할 수 있습니다. -
is_post_type_allowed_to_save()
이 메서드는 단순히 데이터를 가져오려는 게시물 유형이 이전 메서드에서 지정한 허용된 배열에 있는지 확인합니다. -
get_page_cache_name_by_slug()
이 메서드는 데이터를 가져올 트랜션트의 이름을 반환합니다. -
get_page_data_by_slug()
이 메서드는 슬러그 및 게시물 유형을 통해 게시물에 대해WP_Query
를 수행하고get_json_page()
메서드를 사용하여 JSON으로 변환할 게시물 배열의 내용을 반환하는 메서드입니다. -
update_page_transient()
이것은save_post
후크에서 실행되며 데이터베이스의 일시적인 내용을 우리 게시물의 JSON 데이터로 덮어씁니다. 이 마지막 방법을 "키 캐싱 방법"이라고 합니다.
과도 현상에 대해 더 자세히 설명하겠습니다.
과도기 API
Transients API는 특정 기간 동안 WordPress 데이터베이스의 옵션 테이블에 데이터를 저장하는 데 사용됩니다. 이는 지속형 개체 캐시입니다. 예를 들어 크고 느린 쿼리의 결과 또는 페이지 로드 간에 유지될 수 있는 전체 페이지와 같은 일부 개체를 저장하고 있음을 의미합니다. 일반 WordPress 개체 캐시와 유사하지만 WP_Cache
와 달리 과도는 페이지 로드 간에 데이터를 유지하며, 여기서 WP_Cache
(메모리에 데이터 저장)는 요청 기간 동안만 데이터를 보유합니다.
이것은 키-값 저장소입니다. 즉, Memcached 또는 Redis와 같은 in-memory caching systems
이 하는 것과 유사하게 원하는 데이터를 쉽고 빠르게 가져올 수 있습니다. 차이점은 일반적으로 서버에 별도로 설치해야 하는 반면(공유 서버에서 문제가 될 수 있음) 임시 기능은 WordPress에 내장되어 있다는 것입니다.
Codex 페이지에서 언급했듯이 일시적인 현상은 캐싱 플러그인에 의해 본질적으로 가속화됩니다. 데이터베이스 대신 메모리에 일시적인 항목을 저장할 수 있기 때문입니다. 일반적인 규칙은 임시가 항상 데이터베이스에 존재한다고 가정해서는 안 된다는 것입니다. 따라서 임시를 가져오기 전에 임시가 존재하는지 확인하는 것이 좋습니다.
$transient_name = get_transient( 'transient_name' ); if ( $transient_name === false ) { set_transient( 'transient_name', $transient_data, $transient_expiry ); }
만료 없이 사용할 수 있으며(저희처럼) 저장 후 일종의 '캐시 무효화'를 구현했습니다. 그들이 제공하는 모든 훌륭한 기능 외에도 최대 4GB의 데이터를 저장할 수 있지만 단일 데이터베이스 필드에 그렇게 큰 데이터를 저장하는 것은 권장하지 않습니다.
추천 자료 : 조심하세요: 사이트를 안전하지 않게 만들 수 있는 PHP 및 WordPress 기능
최종 엔드포인트: 테스트 및 검증
우리에게 필요한 퍼즐의 마지막 조각은 '종점'입니다. 나는 우리가 결과를 가져오기 위해 특정 파일을 직접 호출하기 때문에 그것이 끝점이 아니지만 여기서 끝점이라는 용어를 사용하고 있습니다. 따라서 다음과 같은 test.php 파일을 생성할 수 있습니다.
get_page_cache_name_by_slug( $post_slug, $post_type ) ); // Return error on false. if ( $cache === false ) { wp_send_json( 'Error, the page does not exist or it is not cached correctly. Please try rebuilding cache and try again!' ); } // Decode json for output. wp_send_json( json_decode( $cache ) );
https://dev.wordpress.test/wp-content/plugins/json-transient/test.php
로 이동하면 다음 메시지가 표시됩니다.
오류, 페이지 슬러그 또는 유형이 없습니다!
따라서 포스트 유형과 포스트 슬러그를 지정해야 합니다. 이제 https://dev.wordpress.test/wp-content/plugins/json-transient/test.php?slug=hello-world&type=post
로 이동하면 다음을 볼 수 있습니다.
오류, 페이지가 존재하지 않거나 올바르게 캐시되지 않았습니다. 캐시를 다시 작성하고 다시 시도하십시오!
오, 기다려! 먼저 페이지와 게시물을 다시 저장해야 합니다. 따라서 시작할 때 이것은 쉬울 수 있습니다. 그러나 이미 100개 이상의 페이지 또는 게시물이 있는 경우 이는 어려운 작업이 될 수 있습니다. 이것이 Decoupled JSON Content 플러그인에서 일시적인 현상을 지우고 일괄적으로 다시 빌드하는 방법을 구현한 이유입니다.
그러나 계속해서 Hello World 게시물을 다시 저장한 다음 링크를 다시 엽니다. 이제 다음과 같이 표시되어야 합니다.
{ "ID": 1, "post_author": "1", "post_date": "2018-06-26 18:28:57", "post_date_gmt": "2018-06-26 18:28:57", "post_content": "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!", "post_title": "Hello world!", "post_excerpt": "", "post_status": "publish", "comment_status": "open", "ping_status": "open", "post_password": "", "post_name": "hello-world", "to_ping": "", "pinged": "", "post_modified": "2018-06-30 08:34:52", "post_modified_gmt": "2018-06-30 08:34:52", "post_content_filtered": "", "post_parent": 0, "guid": "http:\/\/dev.wordpress.test\/?p=1", "menu_order": 0, "post_type": "post", "post_mime_type": "", "comment_count": "1", "filter": "raw" }
그리고 그게 다야. 우리가 만든 플러그인에는 사용할 수 있는 몇 가지 추가 기능이 있지만 간단히 말해서 REST API를 사용하는 것보다 훨씬 빠르게 WordPress에서 JSON 데이터를 가져올 수 있습니다.
전후: 응답 시간 개선
총 응답 시간과 TTFB를 별도로 볼 수 있는 Chrome에서 테스트를 수행했습니다. 응답 시간을 10번 연속으로 테스트했습니다. 처음에는 플러그인을 사용하지 않고 다음에는 플러그인을 추가한 상태에서 테스트했습니다. 또한 게시물 목록과 단일 게시물에 대한 응답을 테스트했습니다.
테스트 결과는 아래 표에 나와 있습니다.
보시다시피 차이가 극심합니다.
보안 문제
잘 살펴보아야 할 몇 가지 주의 사항이 있습니다. 우선, WordPress 세계에서 절대 금물인 WordPress 핵심 파일을 수동으로 로드합니다. 왜요? 글쎄, 코어 파일을 수동으로 가져오는 것이 까다로울 수 있다는 사실 외에도(특히 Bedrock과 같은 비표준 설치를 사용하는 경우) 보안 문제가 발생할 수 있습니다.
이 문서에서 설명하는 방법을 사용하기로 결정했다면 서버 보안을 강화하는 방법을 알고 있어야 합니다.
먼저 test.php 파일과 같은 HTML 헤더를 추가합니다.
header( 'Access-Control-Allow-Origin: your-front-end-app.url' ); header( 'Content-Type: application/json' );
첫 번째 헤더는 지정된 파일로 이동할 때 프런트 엔드 앱만 콘텐츠를 가져올 수 있도록 CORS 보안 조치를 우회하는 방법입니다.
둘째, 앱의 디렉터리 탐색을 비활성화합니다. nginx
설정을 수정하여 이를 수행하거나 Apache 서버에 있는 경우 .htaccess 파일에 Options -Indexes
를 추가할 수 있습니다.
응답에 토큰 검사를 추가하는 것도 원치 않는 액세스를 방지할 수 있는 좋은 조치입니다. 우리는 실제로 이러한 보안 조치를 기본적으로 포함할 수 있도록 Decoupled JSON 플러그인을 수정하는 방법을 연구하고 있습니다.
프론트엔드 앱에서 보낸 Authorization 헤더에 대한 검사는 다음과 같을 수 있습니다.
if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { return; } $auth_header = $_SERVER['HTTP_AUTHORIZATION'];
그런 다음 특정 토큰(프론트엔드 및 백엔드 앱에서만 공유하는 비밀)이 제공되고 올바른지 확인할 수 있습니다.
결론
REST API는 데이터 생성, 검색, 업데이트 및 삭제와 같은 본격적인 앱을 만드는 데 사용할 수 있기 때문에 훌륭합니다. 사용의 단점은 속도입니다.
분명히 앱을 만드는 것은 클래식 웹사이트를 만드는 것과 다릅니다. 우리가 설치한 모든 플러그인이 필요하지는 않을 것입니다. 그러나 프레젠테이션 목적으로 데이터가 필요한 경우 분리된 사이트에서 작업할 때 데이터를 캐싱하고 사용자 지정 파일로 제공하는 것이 현재로서는 완벽한 솔루션인 것 같습니다.
웹사이트 응답 시간을 단축하기 위해 사용자 정의 플러그인을 만드는 것이 과하다고 생각할 수도 있지만 우리는 1초가 중요한 세상에 살고 있습니다. 웹 사이트가 느리면 사용자가 포기할 것이라는 것은 누구나 알고 있습니다. 웹사이트 성능과 전환율 사이의 관계를 보여주는 많은 연구가 있습니다. 그러나 여전히 설득력이 필요한 경우 Google은 느린 웹사이트를 처벌합니다.
이 기사에서 설명하는 방법은 WordPress REST API에서 발생하는 속도 문제를 해결하고 분리된 WordPress 프로젝트에서 작업할 때 추가 향상을 제공합니다. 우리는 모든 요청과 응답에서 마지막 밀리초를 짜내기 위해 끝없는 탐구를 하고 있기 때문에 플러그인을 더욱 최적화할 계획입니다. 그동안 분리된 WordPress의 속도를 높이는 방법에 대한 아이디어를 공유해 주세요!