Overview
안녕하세요. 이번 시간에는 워드프레스가 데이타를 저장하는 방법에 대해서 이야기 해보도록 하겠습니다. 현재 최신 버젼의 워드프레스는 WordPress Version: 6.7.2이고 이하 설명은 해당 버젼을 기준으로 진행됩니다.
게시물이나 페이지를 저장하는 테이블
게시물이나 페이지는 직관적입니다. wp_posts테이블에 저장하고요 테이블 상세는 아래와 같습니다.
mysql> desc wp_posts;
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id: 30134210
Current database: ???
+-----------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+---------------------+------+-----+---------------------+----------------+
| ID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| post_author | bigint(20) unsigned | NO | MUL | 0 | |
| post_date | datetime | NO | | 0000-00-00 00:00:00 | |
| post_date_gmt | datetime | NO | | 0000-00-00 00:00:00 | |
| post_content | longtext | NO | | NULL | |
| post_title | text | NO | | NULL | |
| post_excerpt | text | NO | | NULL | |
| post_status | varchar(20) | NO | | publish | |
| comment_status | varchar(20) | NO | | open | |
| ping_status | varchar(20) | NO | | open | |
| post_password | varchar(255) | NO | | | |
| post_name | varchar(200) | NO | MUL | | |
| to_ping | text | NO | | NULL | |
| pinged | text | NO | | NULL | |
| post_modified | datetime | NO | | 0000-00-00 00:00:00 | |
| post_modified_gmt | datetime | NO | | 0000-00-00 00:00:00 | |
| post_content_filtered | longtext | NO | | NULL | |
| post_parent | bigint(20) unsigned | NO | MUL | 0 | |
| guid | varchar(255) | NO | | | |
| menu_order | int(11) | NO | | 0 | |
| post_type | varchar(20) | NO | MUL | post | |
| post_mime_type | varchar(100) | NO | | | |
| comment_count | bigint(20) | NO | | 0 | |
+-----------------------+---------------------+------+-----+---------------------+----------------+
23 rows in set (1.94 sec)
주요 필드를 설명하자면 아래와 같습니다:
- post_type:
- post: 일반 블로그 글
- page: 정적 페이지
- attachment: 이미지, PDF 등 업로드 파일
- custom_post_type: 커스텀 포스트 타입 (예: product, portfolio)
- post_status: publish, draft, private, trash 등
- post_author: 작성자 (user ID)
- post_parent: 페이지 간 계층 구조에서 부모 ID 지정할 때 사용
- post_date: 작성일
- post_content: 본문
- post_title: 제목
카테고리를 저장하는 방식
wp_terms
우선 게시물을 쓰기 전에 선택하고자 하는 카테고리를 먼저 등록하게 되어 있잖아요? 그 카테고리들을 저장하는 테이블은 바로 wp_terms입니다. 테이블 안에 카테고리 이름(name), 슬러그(slug), 고유 ID(term_id)를 저장하고, 테이블 구조는 아래와 같습니다. 여기서 term_group는 원래 다국어 관련 기능을 위해 설계되었지만 거의 사용되지 않습니다.
mysql> desc wp_terms;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| term_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(200) | NO | MUL | | |
| slug | varchar(200) | NO | MUL | | |
| term_group | bigint(10) | NO | | 0 | |
+------------+---------------------+------+-----+---------+----------------+
wp_term_taxonomy
사실 위에서 설명한 wp_terms테이블은 카테고리만을 위해 존재하는 것은 아닙니다. 그 안에 태그도 함께 저장할 수 가 있어요. 그렇다면 해당 레코드가 카테고리인지 태그인지는 어떻게 아느냐? 바로 wp_term_taxonomy테이블에서 해당 term이 어떤 분류 유형(taxonomy)인지 정의 (category, post_tag 등)하고 있습니다. 테이블 상세를 보시면 다음과 같습니다.
mysql> desc wp_term_taxonomy;
+------------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------------+------+-----+---------+----------------+
| term_taxonomy_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| term_id | bigint(20) unsigned | NO | MUL | 0 | |
| taxonomy | varchar(32) | NO | MUL | | |
| description | longtext | NO | | NULL | |
| parent | bigint(20) unsigned | NO | | 0 | |
| count | bigint(20) | NO | | 0 | |
+------------------+---------------------+------+-----+---------+----------------+
term_taxonomy_id에 고유ID를 저장하고, term_id에 wp_terms.term_id를 저장합니다. 그리고 taxonomy에 category인지 post_tag인지를 저장하고, description에 설명을 넣을 수 있어요. 그리고 카테고리인 경우 부모카테고리가 있으면 parent필드에 명시합니다. 그리고 마지막으로 count에는 해당 카테고리에 게시글이 몇개인지가 저장됩니다.
wp_term_relationships
이제 어떤 게시글이 어떤 카테고리에 연결이 되는지를 알아야겠죠? 바로 wp_term_relationships테이블에서 어떤 게시물(post) 이 어떤 카테고리(또는 태그 등)에 연결되어 있는지 저장합니다. 테이블의 내용은 엄청 단순합니다.
mysql> desc wp_term_relationships;
+------------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------------+------+-----+---------+-------+
| object_id | bigint(20) unsigned | NO | PRI | 0 | |
| term_taxonomy_id | bigint(20) unsigned | NO | PRI | 0 | |
| term_order | int(11) | NO | | 0 | |
+------------------+---------------------+------+-----+---------+-------+
Object_id가 바로 post_id가 되고, term_taxonomy_id가 wp_term_taxonomy테이블의 term_taxonomy_id가 됩니다. 여기서 term_order는 하나의 게시물에 여러개의 태그나 카테고리가 걸려있는 경우 보여주는 순서를 정할 수가 있는데 기본적으로는 사용하지 않습니다.
카테고리 + 태그 모두 포함한 SQL 쿼리
위의 관계들을 종합해보면 게시물목록을 불러올 때 사용되는 쿼리는 다음과 같습니다.
SELECT
p.ID AS post_id,
p.post_title,
tt.taxonomy,
t.name AS term_name,
t.slug AS term_slug
FROM wp_posts p
JOIN wp_term_relationships tr ON (p.ID = tr.object_id)
JOIN wp_term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
JOIN wp_terms t ON (tt.term_id = t.term_id)
WHERE p.post_status = 'publish'
AND p.post_type = 'post'
AND tt.taxonomy IN ('category', 'post_tag')
ORDER BY p.ID, tt.taxonomy;
그리고 쿼리결과는 아래와 같습니다.
+---------+---------------------+----------+-----------+------------+
| post_id | post_title | taxonomy | term_name | term_slug |
+---------+---------------------+----------+-----------+------------+
| 1 | Hello world! | category | 테크 | tech |
| 6 | 테스트 포스트 | category | 부동산 | realestate |
| 8 | 뉴스 테스트 | category | 주식 | stocks |
+---------+---------------------+----------+-----------+------------+
워드프레스의 테이블 형태를 이해하는데 도움이 되셨기를 바랍니다. 그럼 다음시간에 뵐게요!