Step #0: Project Motivation
2025년은 정말 정신없는 한 해였다. 일적으로도 심적으로도 일은 많았지만 정리되지 않은 채 한 해를 보냈다. 지나고 보니 하루하루 버티는 시간이었고 만약 시간을 조금이라도 내어 일기를 작성했었더라면 싶었다. 일기를 작성해 내 생각과 감정을 하루하루 정리하려는 습관을 들였다면 지나왔던 시간들을 좀 더 내 것으로 만들 수 있지 않았을까-하는 아쉬움이 들었다. 그래서 2026년에는 좀 더 기록하는 습관을 들여보기로 했다. 일기는 강제성이 좀 떨어지니 블로그를 활용해보기로 하였다. 기존에 Naver 블로그를 활용했으나, 개인적인 이야기를 그런 개방적인 곳에 작성하는 것은 좀 망설여저서 개인 서버를 만드는 김에 블로그도 호스팅해보면 어떨까 싶었다.
HTML로 블로그를 처음부터 끝까지 만들어볼까도 생각해봤지만, HTML을 배워본적도 없고, 처음부터 그런 큰 프로젝트를 진행하기에는 좀 부담이 있었다. 그래서 좀 더 손쉽게 블로그를 호스팅할 수 있는 방법을 찾아봤다. 내 기준 옵션은 크게 2가지가 있었다.
- 간단한 구현
- 좀 더 어려운 구현
‘간단한 구현’이 당연한 선택 아닌가?-라고 생각할 수 있지만 그렇지 않다. 개인 블로그를 생성하고자 하는 내 목표를 학습/포트폴리오 목적과 결합시켜 하나의 프로젝트에 담을 수도 있기에 ‘좀 더 어려운 구현’을 선택해 두 마리 토끼를 한 번에 잡는 방법이 개발자인 나에겐 어쩌면 더 나은 선택일 수 있다.
개인적으로는 ‘좀 더 어려운 구현’의 선택지가 좀 더 끌렸다. 하지만, 현실적으로 보았을 때 목표로 하는 큰 프로젝트가 하나 더 있고, 남은 방학은 약 한달 반. 이 프로젝트를 진행하면서도 계속 progress를 기록할 예정이고 일기도 되도록이면 작성할 예정이라 하루 빨리 블로그를 완성하는 것이 필요했다. 따라서 ‘간단한 구현’을 선택했다.
‘간단한 구현’을 제공하는 방법은 꽤 많았다. 그 중에서도 가장 대중적으로 사용되는 WordPress를 선택했다. 아무래도 가장 많이 사용되는 만큼 사용 방법과 확장 벙법이 많이 공유되어있을 것 이라고 생각했다. 무엇보다도 ChatGPT가 잘 학습되어있을 것이라고 생각했다.
WordPress는 Blog/Website CMS(Content Management System)이다. 즉, 콘텐츠(글/페이지/이미지 등)를 코드 없이 만들고, 수정하고, 게시하고, 분류하고, 권한을 관리할 수 있도록 하는 Software이다. 따라서 아래 기능을 코드를 몰라도 쉽게 수정할 수 있도록 서비스를 제공한다.
글 작성/수정/예약 발행 UI(에디터)
글 목록/카테고리/태그/검색
댓글/사용자 관리(관리자, 편집자 등 역할)
테마(디자인)와 플러그인(기능 확장) 구조
URL 구조(퍼머링크)와 SEO 기본 기능
업로드 파일(이미지 등) 관리
반대로 정적 방식은 서버에 index.html, post1.html 같은 파일이 실제로 존재하며 사용자가 요청하면 Apache가 파일 그대로를 전달한다. 장점으로는 단순하고 빠르며 보안상 공격 포인트가 적다. 하지만 글 하나 올릴 때도 이미지와 같은 글이 아닌 컨텐츠를 추가하기가 매우 번거로우며 빌드하고 오류 찾는 과정이 번거롭다. CMS는 글을 DB(이 서버는 MySQL을 사용 중)에 저장한다. 사용자가 특정 post 열람을 요창하면 PHP가 DB에서 글/댓글/설정/theme을 조회하고 HTML을 즉석으로 ‘동적’생성을 하여 반환한다. 관리 UI로 글쓰기/운영이 매우 편하지만 구성요소가 많아지기에 보안/성능/백업이 중요해진다.
Step #1: Project Progress – WordPress 분석
그럼 이제 WordPress가 ‘웹 요청’을 실제로 어떠한 흐름으로 처리하는지 알아보자.
(1) Apache가 HTTP 요청을 받음
브라우저를 통해 blog.mingimangi.com에 접속하면 Apache가 요청을 받는다. 그리고 Apache는 이 URL 요청을 어느 파일/프로그램에게 넘길지에 대한 규칙을 가지고 있다.
URL 요청 -> 파일/프로그램 전환
VirtualHost설정 파일, /etc/apache2/sites-available을 확인해보면 blog.mingimangi.com.conf가 존재. 이곳에서 DocumentRoot를 /var/www/blog으로 지정한다. 이 디렉토리에는 아래 파일들이 저장되어있음.
index.php wp-config.php wp-login.php
license.txt wp-config-sample.php wp-mail.php
readme.html wp-content wp-settings.php
wp-activate.php wp-cron.php wp-signup.php
wp-admin wp-includes wp-trackback.php
wp-blog-header.php wp-links-opml.php xmlrpc.php
wp-comments-post.php wp-load.php
이 php파일들은 WordPress를 구성한다. 즉, WordPress는 PHP로 작성된 application이다.
(2) 서버에서 WordPress가 실행됨
WordPress는 PHP로 작성되는 application이다. 하지만 Apache 혼자 PHP를 실행하지 못하므로 이를 도와주는 프로그램 설치 필요. 본 서버에서는 php_module를 사용한다. WordPress 실행 과정은 다음과 같다.
- DocumentRoot에서 브라우저에서 띄울 파일을 찾을 ‘파일을 찾기 시작하는 기준 폴더’를 제시한다.
- WordPress는 ‘깔끔한 URL’을 사용하고자 한다. 이 말의 의미는 blog의 url을 ‘/2026/01/hello_world’, ‘/category/network/’와 같이 ‘파일 디렉토리’처럼 보이도록 ‘깔끔한 URL’을 쓰고 싶어한다. 하지만 이러한 경로에 실제로 해당하는 파일이 존재하는 것은 아니다. 만약 정적 사이트라면 실제로 존재하여야 하지만, WordPress는 php를 사용하는 동적 사이트이다. 따라서 WordPress는 파일을 미리 만들어두는 것이 아니라, DB에서 글을 읽어서 그때그때 HTML을 생성한다. 따라서 ‘깔끔한, 없는 경로’로 들어오는 요청을 받는 ‘어떠한 구조’가 필요하다. 그래서 WordPress는 이러한 ‘깔끔한, 없는 경로’를 index.php로 보낸다. 그럼 index.php는 해당 요청에 맞는 값을 DB에서 찾아서 HTML을 생성해 응답한다.
- DB는 MySQL/MariaDB와 같은 DB를 사용해 글, 제목, 작성자, 댓글 등 거의 모든 것을 저장한다. 반면 파일은 주로 서버의 disk(/wp-content/uploads)에 저장되고, DB에는 그 파일의 metadata와 path가 저장된다.
php파일을 좀 더 살펴보자. 아래는 실제 이 블로그의 index.php파일이다.

근데 잘 보면 실제로 ‘깔끔한, 없는 경로’를 처리하는 기능은 보이지 않는다. 즉, 내 서버에서 index.php는 WordPress의 Front Controller(관문)역할만 한다. WordPress의 실행 환경(theme)만 load하고 나머지 URL 분석, DB 조회, 글 찾기, theme rendering 등 주요 기능은 전부 다른 파일에서 이루어진다. 어느 파일에서 이루어지는지는 맨 아랫 줄, require __DIR__ . ‘/wp-blog-header.php’;을 통해서 알 수 있다. 이 줄의 의미는 ‘실제 처리는 wp-blog-header.php를 통해서 한다’라고 해석할 수 있다.
그럼 wp-blog-header.php를 직접 확인해보자.

첫 번째 if-statement는 WordPress 실행 중 같은 파일이 여러 번 include될 가능성이 있는데, 이를 체크하여 재진입을 방지한다. 진입이 성공하면 $wp_did_header를 true로 설정한다.
다음으로 WordPress Library를 로드한다. wp-load.php를 확인해보자.
/**
* Bootstrap file for setting the ABSPATH constant
* and loading the wp-config.php file. The wp-config.php
* file will then load the wp-settings.php file, which
* will then set up the WordPress environment.
*
* If the wp-config.php file is not found then an error
* will be displayed asking the visitor to set up the
* wp-config.php file.
*
* Will also search for wp-config.php in WordPress’ parent
* directory to allow the WordPress directory to remain
* untouched.
*
* @package WordPress
*/
이 파일은 bootstrap file. 즉, 핵심 환경을 설정하고 실행하기 위한 최초의 진입점 역할을 하는 파일이다. ABSPATH를 정의하고 (DocumentRoot가 Root 디렉토리로 지정됨)
/** Define ABSPATH as this file's directory */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
wp-config.php 파일을 load한다. (wp-config.php 파일은 WordPress environment를 set-up하는 wp-settings.php 파일을 load한다) 만약 wp-config.php 파일이 존재하지 않을 경우 visitor로 하여금 error가 보이도록 설정되어있다.
else {
// A config file doesn't exist.
define( 'WPINC', 'wp-includes' );
require_once ABSPATH . WPINC . '/version.php';
require_once ABSPATH . WPINC . '/compat.php';
require_once ABSPATH . WPINC . '/load.php';
// Check for the required PHP version and for the MySQL extension or a database drop-in.
wp_check_php_mysql_versions();
// Standardize $_SERVER variables across setups.
wp_fix_server_vars();
define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
require_once ABSPATH . WPINC . '/functions.php';
$path = wp_guess_url() . '/wp-admin/setup-config.php';
// Redirect to setup-config.php.
if ( ! str_contains( $_SERVER['REQUEST_URI'], 'setup-config' ) ) {
header( 'Location: ' . $path );
exit;
}
wp_load_translations_early();
// Die with an error message.
$die = '<p>' . sprintf(
/* translators: %s: wp-config.php */
__( "There doesn't seem to be a %s file. It is needed before the installation can continue." ),
'<code>wp-config.php</code>'
) . '</p>';
$die .= '<p>' . sprintf(
/* translators: 1: Documentation URL, 2: wp-config.php */
__( 'Need more help? <a href="%1$s">Read the support article on %2$s</a>.' ),
__( 'https://developer.wordpress.org/advanced-administration/wordpress/wp-config/' ),
'<code>wp-config.php</code>'
) . '</p>';
$die .= '<p>' . sprintf(
/* translators: %s: wp-config.php */
__( "You can create a %s file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file." ),
'<code>wp-config.php</code>'
) . '</p>';
$die .= '<p><a href="' . $path . '" class="button button-large">' . __( 'Create a Configuration File' ) . '</a></p>';
wp_die( $die, __( 'WordPress › Error' ) );
}
정상적으로 wp-config.php가 존재할 경우 wp-config.php를 load 한다. wp-config.php는 무엇인가? WordPress의 base configuration을 저장하고 있으며 다음 configuration을 담고 있다.
Database Settings
Secret Keys
Database table prefix
ABSPATH
실제로 확인해보면 설정값이 보인다!
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('WP_CACHE', 비밀~);
define( 'WPCACHEHOME', 비밀~ );
define( 'DB_NAME', 비밀~ );
/** Database username */
define( 'DB_USER', 비밀~ );
/** Database password */
define( 'DB_PASSWORD', 비밀~ );
/** Database hostname */
define( 'DB_HOST', 비밀~ );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
WordPress 설치할 때 설정했던 Secret Keys도 보이며
define('AUTH_KEY', 비밀~);
define('SECURE_AUTH_KEY', 비밀~);
define('LOGGED_IN_KEY', 비밀~);
define('NONCE_KEY', 비밀~);
define('AUTH_SALT', 비밀~);
define('SECURE_AUTH_SALT', 비밀~);
define('LOGGED_IN_SALT', 비밀~);
define('NONCE_SALT', 비밀~);
WordPress database table prefix와 ABSPATH도 보인다.
$table_prefix = 'wp_';
...
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
...
정리하자면 wp-load.php 파일은 WordPress 요청 처리의 ‘정중앙‘이라고 할 수 있으며 wp-config.php 파일은 WordPress의 핵심 설정값이 저장된 파일이다.
Step #1: Project Progress – Blog 꾸미기
Portfolio겸 내 웹사이트의 진입점으로 사용하고 있는 www.mingimangi.com의 디자인을 검은색과 초록 형광색을 사용하여 기존 프로그래머 이미지를 이어가고 싶었다. 그래서 WordPress에서 제공하는 theme, h4x0r을 사용했다.

Blog 이름은 내 관심분야와 내 이름을 섞어 재치있는 이름을 지어주고 싶었다. 그래서 좀 고민해봤는데 미국 유학시절 불렸던 이름, Min과 요즘 관심있는 분야 System을 잘 섞어 System Min-imization. 즉, System 효율성, 간소화, 최적화 등의 의미를 담아보았다.
Blog의 쓸 글은 크게 3가지 category로 나누기로 하였다: archive, dev, personal_records. 먼저 Archive는 사진과 동영상을 올리는, photo-dump 느낌의 post를 모아두는 category이고 dev는 개인적인 프로젝트와 그 과정에서 겪는 우여곡절과 어떻게 극복했는지에 대한 내용을 모아두는 category이며 마지막으로 personal_records는 앞서 말했던 일기와 비슷한 내용을 모아둘 예정이다.
Step #2: Self Feedback
이번 블로그 개발 프로젝트는 결과만 놓고 보면 비교적 수월하게 마무리할 수 있었던 작업이었다. WordPress라는 검증된 CMS 덕분에 블로그 자체를 구현하는 데에는 큰 기술적 난관이 없었고, 실제로 기본적인 설치와 테마 적용, 카테고리 구성까지는 짧은 시간 안에 완료할 수 있었다. 하지만 이 프로젝트의 목표는 단순히 “블로그를 하나 만드는 것”이 아니라, 이미 완성된 도구를 사용하더라도 그 내부 동작을 이해하려는 개발자적인 접근을 해보는 것이었다.
그래서 단순히 WordPress를 사용하는 데서 멈추지 않고, Apache VirtualHost 설정부터 시작해 요청이 서버에 도달한 이후 어떤 흐름으로 처리되는지를 하나씩 따라가 보았다. 특히 index.php, wp-blog-header.php, wp-load.php, wp-config.php 등의 핵심 파일을 직접 열어보고, 각 파일이 어떤 책임을 가지는지를 코드 수준에서 이해하려고 노력한 점이 이번 프로젝트에서 가장 의미 있는 부분이었다.
앞으로 이 블로그를 더 다양하게 활용할 수 있도록 자주 사용해보겠다. 그럼 다음 포스트도 기대해주시길.