ARCHIVE ONLINEENTRIES 83LAST UPDATE ↗ RSS

기록과 생각을, 조용히 그리고 느슨하게 모아둔 곳.

ESSAYS · REVIEWS · TECH · ARCHIVE

사람과 AI가 모두 읽기 좋은 블로그 만들기: AI 친화적 웹(llms.txt·WebMCP)과 DNSSEC 보안 개편 기록

인간 독자에게는 종이책처럼 읽기 편한 화면을, AI 에이전트에게는 군더더기 없는 요약 지도(llms.txt)와 표준 API 카탈로그를 건네면서, 도메인과 서버의 보안 경계는 암호학적으로 단단히 잠근 블로그 개편 기록

목차 (Contents)14 sections

1. 들어가며: 웹사이트의 방문자가 사람만이 아닌 시대

이전 글(「넷 안의 넷: AI 시대, 인터넷은 ‘이너넷’과 ‘아우터넷’으로 분열할까?」)에서 다루었듯, 오늘날 인터넷을 오가는 주체는 사람만이 아니다. 실제로 최근 2주 동안 mixedlog.com의 웹서버 방문 기록(Access Log)을 분석해 보니 꽤 흥미로운 숫자가 확인되었다. 구글(Googlebot 528회)이나 네이버(Yeti 49회) 같은 전통적인 검색엔진 크롤러뿐만 아니라, OpenAI의 GPTBot(565회), 앤트로픽의 ClaudeBot(572회), 퍼플렉시티의 PerplexityBot(429회), 애플의 Applebot(321회) 등 수많은 인공지능(AI) 에이전트들이 쉼 없이 블로그를 찾아오고 있었다.

사실 일반적인 PC 브라우저나 빠른 스마트폰 통신망으로 접속하는 사람 눈에는 기존 블로그도 아무런 문제 없이 깔끔하고 빠르게 잘 보였다. 하지만 시선을 조금 바꾸어 ‘AI 에이전트가 읽을 때의 효율성’과 ‘검색엔진 성능 측정 도구가 채점하는 극한의 모바일 환경’이라는 기준으로 내부를 들여다보자, 평소 눈으로는 보이지 않던 기술적 병목과 빈틈들이 드러났다.

  1. AI 에이전트에게 비효율적이었던 점: AI는 본문 텍스트와 문서 구조만 읽으면 되는데, 화면의 여백·색상·메뉴 배치를 담당하는 수천 줄의 HTML 태그와 스크립트까지 전부 읽느라 토큰(Token, AI가 글자를 처리하는 단위)을 낭비하고 있었다. 게다가 앞단의 보안 네트워크(Cloudflare)가 기본 정책에 따라 ChatGPT나 클로드 같은 정식 AI 봇의 접근까지 403 Forbidden(접근 거부)으로 가로막고 있었다.
  2. 내부 구조와 성능 측정 기준에서 아쉬웠던 점: 일상적인 접속 환경에서는 쾌적했지만, 구글의 웹 성능 측정 도구(Lighthouse)가 해외나 음영 지역의 느린 통신망(Slow 4G)과 캐시 없는 첫 방문을 가정해 채점할 때는 2.1MB짜리 한글 통짜 웹폰트가 지표상 6.4초의 렌더링 지연을 기록하고 있었다. 또한 첫 화면(홈 피드) HTML 안에 긴 논문 전문이 모두 포함되어 있어, 화면에 보이는 것 이상으로 문서 용량(224KB)이 불필요하게 무거운 상태였다.

이번에 진행한 블로그 개편의 목표는 단순하고 명확했다. “글을 읽으러 오는 사람과 AI 에이전트에게는 각각 가장 읽기 편한 전용 통로를 열어주되, 비밀번호나 서버 설정 파일을 노리는 악성 스캐너와 도메인 위변조 시도는 암호학적으로 철저히 차단한다.” 비전공자도 흐름을 쉽게 따라올 수 있도록 비유를 곁들이되, 실제 적용한 웹 표준(RFC)과 기술적 작동 원리까지 하나씩 상세히 기록해 둔다.

2. 여러 AI에게 블로그 진단을 맡겼을 때 드러난 차이

개편 과정에서 흥미로운 실험을 하나 진행했다. 서로 다른 환경에서 작동하는 여러 AI에게 mixedlog.com의 기술적 상태를 점검해 달라고 요청한 것이다. 결과는 AI가 웹에 접근하는 기술적 방식에 따라 극명하게 갈렸다.

  • 실시간 접속 없이 검색 색인만으로 추론해 오진한 경우: 웹페이지를 직접 가져오지(Fetch) 못하는 한 웹챗 AI는 구글 검색 연산자(site:mixedlog.com)에 아직 페이지가 색인되지 않았다는 사실 하나만 보고 “자바스크립트로만 화면을 그리는 빈 껍데기 구조(CSR SPA)여서 검색엔진이 본문을 읽지 못하는 상태”라고 진단했다. 실제로는 워드프레스 서버가 완성된 HTML 본문을 내려주는 서버 사이드 렌더링(SSR) 구조였음에도, 직접 HTTP 응답을 확인하지 않고 흔한 장애 사례를 그대로 대입해 틀린 결론을 내린 것이다.
  • 실제 헤드리스 브라우저(Headless Browser)로 접속해 런타임 오류까지 찾아낸 경우: 반면 실제 크로미움(Chromium) 브라우저 엔진으로 접속한 감사 에이전트는 첫 화면을 구성하는 문서 객체 모델(DOM) 노드의 개수(379개), 본문 목차(TOC) 앵커 링크 21개가 실제 소제목(id)과 1:1로 매칭되는지, 이미지 alt 속성과 Open Graph 메타태그가 존재하는지까지 정확히 검증했다. 특히 일반 방문자 눈에는 보이지 않는 브라우저 개발자 도구 콘솔(Console) 로그에서 WebMCP 검색 도구가 중복 이름으로 등록되며 InvalidStateError: Failed to execute 'registerTool' on 'ModelContext': Duplicate tool name 'search_mixedlog' 예외를 던지고 있다는 점까지 정확히 짚어냈다.

이러한 교차 검증과 클로드(Claude)의 설정 파일 검토, 그리고 구글 Lighthouse 모바일 성능 측정 결과를 바탕으로 수정한 핵심 기술 내역은 다음과 같다.

점검 영역개편 전 상태 (Before)개편 후 상태 (After)
첫 화면 구조와 무게긴 글 전문이 그대로 노출되어 무거움 (224KB)첫 문단 자동 발췌 + 계속 읽기 → 구조로 전환 (105KB로 53% 감량)
모바일 폰트 및 렌더링2.1MB 통짜 폰트 다운로드로 첫 화면이 6.4초 지연유니코드 분할 로드(30KB) + 비동기 스타일시트 적용으로 차단 지연 0ms 달성
AI용 안내서 (llms.txt)카테고리 경로 불일치(404) 및 한글 인코딩 깨짐실제 카테고리 주소 매핑 및 text/plain; charset=utf-8 헤더 명시
AI용 합본 (llms-full.txt)이름만 전체 본문(Full)이고 실제로는 링크 30줄만 존재주요 논문·비평 4편의 전문(3,000여 줄)이 담긴 96KB 마크다운 코퍼스 구축
AI 출입 규칙 (robots.txt)Cloudflare 403 차단 및 ai-train 신호 모순, RFC 9309 규칙 누락정식 AI 봇 200 OK 개방 및 User-agent: * 단일 블록으로 보호 규칙 통합
도메인 위변조 방지상위 레지스트리에 암호화 서명(DS 레코드) 미연결.com 레지스트리에 DNSSEC DS 레코드(Key Tag 2371) 등록 및 AD: true 검증 완료

3. AI 에이전트를 위한 전용 통로 설계 (AI-Native 웹 아키텍처)

최근에는 궁금한 주제가 생겼을 때 검색엔진 결과창을 일일이 클릭하는 대신 ChatGPT, 클로드, 퍼플렉시티 같은 AI에게 질문해 종합된 답변과 출처 링크를 얻는 경우가 많다. 이를 흔히 GEO(Generative Engine Optimization, 생성형 엔진 최적화) 또는 에이전트 친화적 웹(Agent-Ready Web)이라 부른다. 이번 개편에서는 AI 에이전트가 블로그에 도착한 순간부터 본문을 읽고 검색하기까지의 전 과정을 네 가지 웹 표준 기술로 설계했다.

1단계 · 입구 안내도 및 카탈로그 탐색HTTP Link 헤더, /llms.txt, /.well-known/api-catalog로 구조 파악

2단계 · 순수 마크다운 본문 또는 전문 합본 로드Accept: text/markdown 콘텐츠 협상 또는 /llms-full.txt(96KB) 일괄 로드

3단계 · 내장 검색 도구(WebMCP) 및 REST API 실행navigator.modelContext의 search_mixedlog_api로 키워드 직접 질의

3.1 HTTP 콘텐츠 협상(Content Negotiation)을 통한 마크다운 직접 반환

사람이 브라우저로 접속할 때는 종이 질감의 배경색, 여백, 글씨체, 상단 메뉴바가 갖춰진 웹페이지(HTML)가 필요하다. 하지만 글의 내용만 파악하려는 AI 에이전트에게 이러한 화면 꾸밈 코드(HTML 태그, CSS 클래스, 자바스크립트)는 불필요한 짐일 뿐이다. 식당에 비유하면 매장을 찾은 손님에게는 그릇과 상차림을 갖춰 내어주고, 포장(배달) 주문에는 용기에 음식 알맹이만 깔끔하게 담아 주는 것과 같다.

이를 웹 기술에서는 HTTP 콘텐츠 협상(Content Negotiation, RFC 9110)이라고 부른다. 같은 인터넷 주소(URL)에 접속하더라도 클라이언트가 요청 헤더(Request Header)에 Accept: text/html을 보내면 일반 웹페이지를 내려주고, AI 에이전트가 Accept: text/markdown을 담아 보내면 워드프레스의 template_redirect 단계에서 테마 렌더링을 건너뛰고 메타데이터(제목·작성일·카테고리·원문 주소)와 순수 본문만 정제한 마크다운(Markdown) 문서로 즉시 변환해 응답하도록 구현했다.

여기서 기술적으로 매우 중요한 세부 설정이 두 가지 더 들어간다.

  1. 캐시 오염을 막는 Vary: Accept 헤더: 클라우드플레어(CDN)나 브라우저 캐시 서버는 기본적으로 ‘인터넷 주소(URL)’만 보고 응답 결과를 저장해 둔다. 만약 AI 봇이 특정 글을 마크다운으로 요청했는데 캐시 서버가 이를 그대로 저장해 버리면, 직후에 같은 주소로 접속한 일반 사람 독자에게도 화면 디자인이 없는 마크다운 텍스트가 그대로 노출되는 사고가 발생한다. 이를 방지하기 위해 응답 헤더에 Vary: Accept를 명시하여, “요청한 쪽의 Accept 헤더 종류(HTML인지 Markdown인지)에 따라 캐시를 별도로 분리해서 저장하라”고 지시했다.
  2. 헤더만 보고도 길을 찾는 HTTP Link 헤더 (RFC 8288): AI 에이전트가 본문 전체를 내려받기 전에 가벼운 HEAD 요청만 보내더라도 사이트의 주요 통로를 즉시 발견할 수 있도록, 모든 HTTP 응답 헤더에 API 카탈로그 위치(rel="api-catalog")와 REST API 진입점(rel="service-desc")을 가리키는 링크를 심어 두었다.
# AI 에이전트가 순수 마크다운 본문을 요청할 때의 실제 HTTP 응답 헤더
curl -i -H "Accept: text/markdown" https://mixedlog.com/archives/1963

HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
Vary: Accept
Content-Signal: ai-train=yes, search=yes, ai-input=yes
Link: </.well-known/api-catalog>; rel="api-catalog", <https://mixedlog.com/wp-json/>; rel="service-desc"

3.2 llms.txt와 llms-full.txt: 토큰 경제성과 컨텍스트 윈도우 최적화

이번 작업에서 특히 공을 들인 부분은 사이트 루트 경로에 배치한 llms.txt와 llms-full.txt다. 한마디로 비유하면 ‘AI 전용 도서관의 층별 안내도(llms.txt)’와 ‘대표 저작 전권 합본(llms-full.txt)’이다.

기존에도 웹사이트에는 검색엔진 로봇을 위한 sitemap.xml이 있었다. 그렇다면 왜 AI를 위해 별도의 파일이 필요할까? 기술적인 이유는 크게 두 가지다.

  • 맥락(Context)과 우선순위의 부재: sitemap.xml은 사이트 안의 수백 개 URL과 수정 날짜(<lastmod>)만 기계적으로 나열된 전화번호부에 가깝다. AI 에이전트가 이 목록만 보고서는 어느 주소가 블로그의 정체성을 담은 핵심 논문이고 어느 주소가 단순한 태그 목록 페이지인지 알 수 없다.
  • 토큰(Token) 낭비와 컨텍스트 윈도우(Context Window) 오염: 거대언어모델(LLM)이 한 번에 읽고 기억할 수 있는 작업 공간(컨텍스트 윈도우)은 유한하며, 읽어들인 글자 수(토큰)에 비례해 연산 시간과 비용이 증가한다. 일반적인 웹페이지 HTML을 그대로 긁어가면 전체 용량의 70~80%가 메뉴바, 스크립트, 스타일 태그 같은 노이즈여서 AI의 추론 정확도가 떨어지고 응답 속도(TTFT)도 느려진다.

이러한 문제를 해결하기 위해 Answer.AI의 제레미 하워드(Jeremy Howard) 등이 제안한 표준이 바로 llms.txt다. 사이트 루트(/llms.txt)에 마크다운 형식으로 사이트 요약, 카테고리 구조, 대표 글 링크와 한 줄 설명을 정리해 두어 AI가 최소한의 토큰만으로 사이트 전체 지형도를 파악하게 돕는다.

  • /llms.txt (핵심 구조 안내서, 약 2.5KB): 표준 규격에 맞춰 # 사이트 제목, > 한 줄 요약 인용구, 그리고 ## 섹션별 링크 목록 순으로 구성했다. 초기 파일에서 워드프레스의 실제 퍼머링크 구조(/archives/category/...)와 맞지 않아 404 Not Found를 내던 카테고리 주소 6개를 모두 바로잡고, 웹서버 설정에서 Content-Type: text/plain; charset=utf-8을 강제하여 다바이트(Multi-byte) 한글 문자열이 깨지는 현상(Mojibake)을 해결했다.
  • /llms-full.txt (핵심 연구 전문 코퍼스, 약 96KB · 3,000여 줄): 안내도에서 한 걸음 더 나아가, 한 번에 수십만 토큰을 읽을 수 있는 최신 LLM과 RAG(검색 증강 생성) 시스템을 위해 대표 논문 및 비평 4편의 본문 전체, 표, 수식, 참고문헌을 하나의 마크다운 파일에 온전히 담아낸 합본이다. 기존에는 파일 이름만 full이고 내용은 /llms.txt와 똑같은 30줄짜리 링크 목록에 불과했으나, 실제 데이터베이스에서 대표 글들의 전문을 추출해 마크다운으로 변환·병합함으로써 AI가 단 한 번의 HTTP 요청만으로 블로그의 핵심 학술 연구들을 통독할 수 있게 만들었다.
# https://mixedlog.com/llms.txt 실제 구조 발췌
# mixedlog

> An editorial space exploring research, criticism, technology, cycling, and daily logs.

## Content Sections
- [연구 & 비평](https://mixedlog.com/archives/category/essays): In-depth research papers on media theory, VR immersive theater, archaeology, and cultural criticism.
- [테크 & 코드](https://mixedlog.com/archives/category/tech): Software engineering, web development, cloud architecture, and infrastructure notes.
- [서평](https://mixedlog.com/archives/category/book-reviews): Book reviews and literary reflections.
- [자전거](https://mixedlog.com/archives/category/velo): Long-distance cycling logs, route analysis, and equipment notes.
- [아카이브 & 번역](https://mixedlog.com/archives/category/archive): Archival records, essays, and translations.
- [일상 로그](https://mixedlog.com/archives/category/journal): Personal essays and daily reflections.

## Machine-Readable Resources
- [Full Text Knowledge Base](https://mixedlog.com/llms-full.txt): Complete full-text markdown of cornerstone articles for large language models.
- [REST API](https://mixedlog.com/wp-json/wp/v2/posts): Public read-only WordPress REST API.

3.3 브라우저 내장 AI를 위한 ‘WebMCP’와 표준 디스커버리 명세(RFC 9727)

사람이 웹사이트에서 원하는 주제를 찾을 때는 눈으로 돋보기 모양 아이콘을 찾아 검색창에 글자를 입력한다. 과거의 브라우저 자동화 AI들은 사람처럼 화면을 캡처해 버튼 위치를 추측하고 마우스 클릭을 흉내 내느라 속도가 느리고 화면 디자인이 조금만 바뀌어도 오작동하곤 했다.

이를 해결하기 위해 등장한 차세대 웹 표준이 WebMCP(Web Model Context Protocol)다. 웹페이지가 브라우저의 navigator.modelContext 인터페이스를 통해 “이 페이지에서는 이런 이름의 함수를 이런 입력값(JSON Schema)으로 호출하면 원하는 작업을 바로 실행할 수 있다”고 AI 에이전트에게 직접 도구(Tool)를 건네주는 기술이다.

mixedlog.com에는 두 가지 방식의 WebMCP 도구가 구현되어 있는데, 앞서 브라우저 감사 AI가 찾아낸 콘솔 에러의 원인과 해결 과정은 다음과 같다.

  1. 선언형(Declarative) HTML 검색 폼 도구 (search_mixedlog): 사이트 하단 <form> 태그에 toolname="search_mixedlog"와 tooldescription="..." 속성을 부여해, 브라우저 에이전트가 검색 결과 페이지(/?s=키워드)로 화면을 이동시킬 때 사용하는 도구다.
  2. 명령형(Imperative) 자바스크립트 API 검색 도구 (search_mixedlog_api): 페이지 이동 없이 백그라운드에서 워드프레스 REST API(/wp-json/wp/v2/posts?search=...)를 호출해 글 제목, 주소, 날짜, 요약문을 깔끔한 JSON 데이터로 즉시 돌려주는 도구다. 초기 코드에서는 이 스크립트 도구의 이름까지 search_mixedlog로 똑같이 지정하는 바람에 브라우저의 ModelContext 레지스트리에서 이름 충돌(InvalidStateError: Duplicate tool name)이 발생하고 있었고, 이를 search_mixedlog_api로 분리해 충돌을 깔끔히 해소했다.
// 브라우저 내장 AI 에이전트에게 블로그 검색 API 도구를 등록하는 WebMCP 코드
if (window.navigator && window.navigator.modelContext && typeof window.navigator.modelContext.registerTool === 'function') {
  window.navigator.modelContext.registerTool({
    name: 'search_mixedlog_api',
    description: 'Search mixedlog.com articles via the WordPress REST API and return structured JSON results.',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', description: 'Search keyword or phrase' }
      },
      required: ['query']
    },
    execute: async ({ query }) => {
      const res = await fetch('/wp-json/wp/v2/posts?search=' + encodeURIComponent(query) + '&per_page=5');
      return await res.json();
    }
  });
}

여기에 더해 외부의 자율형 AI 에이전트가 사이트의 API 구조와 인증 여부를 표준화된 경로(/.well-known/)에서 자동으로 탐색할 수 있도록 다음 명세 파일들을 함께 구축했다.

  • /.well-known/api-catalog (RFC 9727 API 카탈로그): IETF 표준인 application/linkset+json 형식에 맞춰 블로그의 REST API 진입점(service-desc), API 문서(service-doc), 상태 확인 주소(status)의 관계를 정의한 지도다.
  • /.well-known/mcp/server-card.json (MCP 서버 카드): 외부 Model Context Protocol 클라이언트가 사이트의 기능과 엔드포인트를 인식할 수 있도록 제공하는 메타데이터 카드다.
  • /auth.md 및 /.well-known/oauth-protected-resource: 자동화된 에이전트가 글을 읽으려 할 때 “로그인이 필요한가? API 키를 입력해야 하는가?”를 판단할 수 있도록, “모든 공개 게시글과 API는 별도의 인증 토큰 없이 누구나 무료로 읽을 수 있음(공개 읽기 범위 scopes_supported: ["public:read"])”을 기계가 읽을 수 있는 JSON과 마크다운으로 명시한 안내서다.

3.4 robots.txt의 RFC 9309 그룹 매칭 함정과 Content-Signal 교정

웹사이트 입구의 로봇 규칙표인 robots.txt에서도 흔히 범하기 쉬운 기술적 오류를 바로잡았다. 개편 전 robots.txt에는 두 가지 문제가 숨어 있었다.

  1. 신호의 자기모순: 맨 위 User-agent: * 블록에는 Content-Signal: ai-train=no(AI 학습 금지)라고 적어두고, 바로 아래에는 User-agent: GPTBot, User-agent: ClaudeBot 블록을 따로 만들어 Allow: /(출입 허용)를 부여해 정책 신호가 서로 충돌하고 있었다.
  2. RFC 9309(로봇 배제 표준)의 그룹 우선순위 함정: 많은 웹 운영자들이 User-agent: *(모든 로봇 공통)에 관리자 페이지 차단(Disallow: /wp-admin/)을 적어두면, 아래에 따로 이름을 적은 GPTBot에게도 그 차단 규칙이 자동으로 상속될 것이라 착각한다. 하지만 RFC 9309 표준 규격상 크롤러는 자신의 이름과 정확히 일치하는 User-agent 그룹 하나만 읽고, 공통(*) 그룹의 규칙은 완전히 무시한다. 즉, GPTBot이나 ClaudeBot을 별도 블록으로 빼서 Allow: /만 적어두면 그 봇들에게는 관리자 페이지나 내부 검색 결과 차단 규칙이 전부 풀려버리는 셈이다.

이에 Cloudflare 보안 설정에서 정식 AI 크롤러를 가로막던 스위치를 해제해 200 OK 응답을 보장하는 동시에, robots.txt에서는 불필요한 개별 봇 나열을 모두 제거하고 단 하나의 User-agent: * 블록으로 일원화했다. 그 안에 IETF 표준 초안인 Content-Signal: ai-train=yes, search=yes, ai-input=yes(학습·검색·실시간 답변 인용 모두 허용)를 선언하고, 워드프레스 관리자 경로와 불필요한 크롤링 예산을 소모하는 내부 검색 결과(/?s=) 차단 규칙을 모든 봇에게 빈틈없이 적용되도록 정리했다.

# 최종 반영된 https://mixedlog.com/robots.txt
User-agent: *
Content-Signal: ai-train=yes, search=yes, ai-input=yes
Allow: /

# WordPress Admin & System Files
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Disallow: /wp-login.php
Disallow: /xmlrpc.php
Disallow: /readme.html
Disallow: /license.txt

# Crawl Budget & Duplicate URL Protection
Disallow: /?s=
Disallow: /search/
Disallow: /*?replytocom=
Disallow: /archives/*/trackback/

Sitemap: https://mixedlog.com/wp-sitemap.xml

4. 콘텐츠는 열어두되 서버와 도메인은 단단히 잠그기 (DNSSEC과 서버 하드닝)

AI 에이전트에게 글 열람을 활짝 열어두면 보안이 약해지지 않을까 걱정할 수도 있다. 하지만 ‘공개된 글을 읽게 해주는 것(콘텐츠 개방)’과 ‘서버 침입이나 도메인 위변조를 막는 것(인프라 보안)’은 완전히 다른 층위의 일이다. 도서관 열람실을 시민에게 개방한다고 해서 지하 문서고의 금고 자물쇠까지 풀어두는 것이 아닌 이치와 같다.

4.1 DNSSEC의 암호학적 신뢰 사슬(Chain of Trust) 완성

사용자가 브라우저 주소창에 mixedlog.com을 입력하면, 컴퓨터는 인터넷 전화번호부 역할을 하는 DNS(도메인 네임 시스템) 서버에 물어봐서 실제 서버의 IP 주소를 받아온다. 그런데 1980년대에 설계된 기본 DNS 프로토콜에는 응답이 진짜 주인에게서 온 것인지 확인하는 인증 장치가 없다. 만약 중간 경로에 있는 공격자가 가짜 IP 주소가 적힌 위조 응답을 먼저 보내면(DNS 스푸핑 / 캐시 포이즈닝), 사용자는 주소창에 mixedlog.com이 정상적으로 찍혀 있는데도 해커가 만든 복제 사이트로 연결될 수 있다.

이를 수학적으로 원천 차단하는 기술이 DNSSEC(Domain Name System Security Extensions, RFC 4033~4035)이다. 비유하자면 ‘전화번호부의 모든 줄마다 위조할 수 없는 암호화 인감도장을 찍고, 상위 기관이 그 인감도장의 진위를 보증하는 구조’다. 실제 작동 원리는 4단계의 신뢰 사슬(Chain of Trust)로 이루어진다.

  1. 레코드 전자서명 (RRSIG): 도메인의 IP 주소 정보를 담은 DNS 레코드 묶음(RRset)을 타원곡선 암호 알고리즘(ECDSAP256SHA256, 알고리즘 13번)의 개인키인 ZSK(Zone Signing Key, 구역 서명 키)로 전자서명하여 RRSIG 레코드에 함께 담아 둔다.
  2. 공개키 게시 및 마스터 서명 (DNSKEY & KSK): 외부에서 이 서명을 검증할 수 있도록 공개키(DNSKEY)를 공개하고, 이 공개키 자체도 최상위 마스터 키인 KSK(Key Signing Key, 키 서명 키 · Key Tag 2371)로 다시 한번 서명한다.
  3. 상위 .com 레지스트리에 인감증명서 등록 (DS 레코드): 그렇다면 해커가 KSK 마스터 키 자체를 통째로 가짜로 바꿔치기하면 어떻게 될까? 이를 막기 위해 KSK 공개키를 SHA-256 해시 함수(Digest Type 2)로 압축한 지문인 DS(Delegation Signer) 레코드를 도메인 등록 대행사를 통해 상위 .com 국제 관리 기관(Verisign)에 등록한다.
  4. 최종 검증 완료 (AD: true): 이제 전 세계 어디서든 구글(8.8.8.8)이나 클라우드플레어(1.1.1.1) DNS 서버가 mixedlog.com을 조회할 때, 루트(.) 서버 → .com 서버의 DS 지문 → mixedlog.com의 DNSKEY 공개키 → RRSIG 전자서명까지 사슬이 단 하나도 끊기지 않고 일치함을 검증한 뒤, 응답 패킷에 AD: true(Authenticated Data, 암호학적 무결성 검증 완료) 깃발을 세워 돌려준다.
# Cloudflare(1.1.1.1) 및 Google(8.8.8.8) DNS에서 검증된 mixedlog.com의 DNSSEC 응답
$ dig +dnssec mixedlog.com @1.1.1.1

;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra ad; ...
;; "ad" (Authenticated Data) 플래그가 활성화되어 위변조되지 않았음이 검증됨

4.2 웹서버 경계 하드닝과 5대 보안 헤더의 역할

실제 서버 로그를 열어보면 유명 검색엔진이나 AI 로봇인 척 이름(User-Agent)을 속이고 들어와 환경변수 파일(.env), 깃 저장소(.git/config), 데이터베이스 백업본(.sql, .bak)을 훔쳐보려는 자동화 봇들의 탐색 시도가 매일 이어진다. 이러한 공격을 애플리케이션(PHP/워드프레스)까지 도달하기 전에 웹서버 앞단에서 즉시 차단하도록 세 가지 방어선을 구축했다.

  • 숨김 파일 및 백업 파일 차단: 점(.)으로 시작하는 숨김 파일과 각종 백업·압축 파일을 찾는 요청은, 표준 공개 경로인 /.well-known/만 예외로 두고 웹서버 단계에서 모두 즉시 차단했다. AI 에이전트용 안내 파일은 열어두되, 서버 내부를 들여다보려는 시도는 문턱에서 돌려보내는 구조다.
  • xmlrpc.php 원천 폐쇄 및 버전 정보 은닉: 워드프레스의 구형 원격 통신 통로인 /xmlrpc.php는 한 번의 HTTP 요청 안에 수백 개의 아이디·비밀번호 조합을 묶어 보낼 수 있는 system.multicall 기능을 지원해 무차별 대입 공격(Brute-force)의 주된 표적이 된다. 현재 사용하지 않는 이 통로를 웹서버 레벨에서 403으로 완전히 차단하고, HTML <head>에서 워드프레스 버전을 노출하는 <meta name="generator"> 태그도 제거했다.
  • 5대 HTTP 웹 보안 헤더 상시 주입: 모든 응답에 브라우저 보안 정책 헤더 5종을 강제 적용했다.
    • Strict-Transport-Security (HSTS): 향후 1년간(max-age=31536000) 모든 하위 도메인을 포함해 평문 HTTP 접속을 금지하고 암호화된 HTTPS 연결만 강제하여 중간자 다운그레이드 공격(SSL Stripping)을 막는다.
    • X-Content-Type-Options: nosniff: 해커가 이미지나 텍스트 파일인 척 악성 스크립트를 업로드했을 때, 브라우저가 파일 내용을 임의로 추측(MIME Sniffing)해 자바스크립트로 실행하는 것을 차단한다.
    • X-Frame-Options: SAMEORIGIN: 외부 피싱 사이트가 mixedlog.com 화면을 투명한 <iframe>으로 몰래 겹쳐 띄운 뒤 사용자의 클릭을 가로채는 클릭재킹(Clickjacking) 공격을 막는다.
    • Referrer-Policy: strict-origin-when-cross-origin: 독자가 외부 링크를 클릭해 이동할 때 세부 글 주소나 검색어 파라미터는 숨기고 도메인 출처만 전달하여 독자의 프라이버시를 보호한다.
    • Permissions-Policy: camera=(), microphone=(), geolocation=(): 웹페이지 내부에서 카메라, 마이크, 위치정보(GPS) 하드웨어 API 호출을 원천 비활성화한다.

5. 눈에 보이지 않던 렌더링 병목까지 걷어내기 (프론트엔드 성능 최적화)

마지막으로 손본 영역은 프론트엔드 구조와 웹폰트 로딩 파이프라인이다. 앞서 언급했듯 국내의 빠른 인터넷이나 스마트폰 환경에서는 기존에도 페이지가 순식간에 열렸기 때문에 사람 눈으로는 불편함을 느끼기 어려웠다. 하지만 HTML 문서의 내부 구조와 구글 Lighthouse가 채점하는 저속 모바일 시뮬레이션(느린 4G 통신망 · CPU 4배 감속 · 캐시 없는 첫 방문) 기준으로 뜯어보면 개선할 여지가 분명했다.

5.1 홈 화면 HTML 경량화와 학술 연구 포스트(표·수식·DOI) 정밀 교정

개편 전에는 첫 화면 목록에서 각 글의 본문 전체(the_content)를 내려준 뒤 화면에 표시하고 있어서, 첫 화면 HTML 문서 크기만 224KB에 달했다. 이를 워드프레스 필터 훅으로 개선해 첫 문단 요약과 계속 읽기 → 링크만 내려주도록 바꾸자, 첫 화면 문서 크기가 105KB로 53% 이상 가벼워졌다.

이와 함께 기존에 발행된 주요 학술 연구 포스트들(VR 이머시브 시어터 연출 문법 연구, 영산강유역 전방후원분 시론)의 세부 조판도 함께 다듬었다. 본문 일부에 남아 있던 마크다운 표 문법(|---|---|)을 학술 논문 스타일(Booktabs)의 <table> 태그로 정식 변환하고, 수학 수식이 포함된 글에는 KaTeX 엔진을 연동했으며, 참고문헌의 해외 논문 디지털 객체 식별자(DOI) 링크를 학술 데이터베이스(CrossRef API)와 전수 대조하여 오기재된 링크를 모두 실제 논문 주소로 바로잡았다.

5.2 Lighthouse의 6.4초 렌더링 차단 지표를 해소한 ‘유니코드 분할 웹폰트’와 ‘강제 리플로우’ 제거

일상적인 접속에서는 폰트가 한 번 캐시되면 즉시 표시되지만, 캐시가 전혀 없는 첫 방문자가 저속 통신망(Slow 4G, 약 1.6Mbps)으로 접속하는 구글 Lighthouse 테스트에서는 초기 렌더링 차단 시간이 6,460ms(약 6.4초)로 기록되고 있었다. 원인은 2.05MB에 달하는 한글 가변 웹폰트(PretendardVariable.woff2) 통짜 파일과 동기식 외부 스타일시트였다.

영문 알파벳은 대소문자를 합쳐 52자에 불과해 폰트 파일 하나가 수십 KB면 충분하지만, 한글은 초성·중성·종성의 조합으로 만들어지는 현대 완성형 글자 수가 11,172자(유니코드 U+AC00~U+D7A3)에 이른다. 저속망 시뮬레이션에서는 이 1만여 글자가 통째로 들어 있는 2.1MB 파일을 다 내려받고 외부 폰트 CSS를 해석할 때까지 브라우저의 렌더링 파이프라인이 대기 상태에 걸렸던 것이다.

이를 브라우저의 중요 렌더링 경로(Critical Rendering Path) 원리에 맞춰 세 단계로 최적화했다.

  1. CSS unicode-range 기반 다이나믹 서브셋(Dynamic Subset) 적용: 11,172자가 통째로 든 단일 폰트 파일 대신, 글자 사용 빈도별로 1만여 자를 90여 개의 작은 조각(.woff2)으로 잘게 쪼개고 CSS @font-face의 unicode-range 속성으로 매핑한 pretendardvariable-dynamic-subset.min.css로 교체했다. 브라우저는 현재 페이지 본문에 실제로 쓰인 글자 코드만 확인한 뒤, 정말 필요한 몇 개의 조각(약 20~30KB 단위)만 선택적으로 다운로드한다.
  2. 비동기(Non-Blocking) 폰트 스타일시트 로딩: 브라우저는 기본적으로 <link rel="stylesheet"> 태그를 만나면 그 파일을 다 받아올 때까지 화면 그리기를 보류한다. 이를 해결하기 위해 외부 폰트 스타일시트 태그에 media="print" onload="this.media='all'" 기법을 적용했다. 브라우저에게 “이 스타일시트는 인쇄용(print)이니 화면을 그릴 때는 기다리지 말고 본문 글자부터 즉시 띄워라. 다운로드가 끝나면 그때 화면용(all)으로 전환해 글씨체를 입혀라”라고 지시한 것이다.
  3. 자바스크립트 강제 동기 레이아웃(Forced Reflow) 108ms 제거: 모바일 화면에서 하단 카테고리·아카이브 목록(<details>)을 기본으로 접어 두던 하단 스크립트가 화면 너비를 확인하기 위해 window.innerWidth 속성을 읽고 있었다. 브라우저는 스타일이 바뀐 직후 innerWidth 같은 기하학적 치수를 읽으면, 정확한 픽셀 값을 계산하기 위해 하던 일을 멈추고 화면 레이아웃을 처음부터 다시 계산하는 강제 리플로우(Forced Reflow)를 일으켜 지표상 108ms의 메인 스레드 지연을 만들고 있었다. 이를 기하학적 재계산을 유발하지 않는 미디어 쿼리 감지기(window.matchMedia('(max-width: 900px)').matches)로 교체하고 requestAnimationFrame 안에 배치해 리플로우 지연을 0ms로 줄였다.

이 세 가지 최적화를 적용한 직후, Lighthouse의 극한 저속망 테스트에서도 6.4초의 렌더링 차단 지연이 완전히 사라졌으며 접근성 100점 · 권장사항 100점 · 검색엔진 최적화(SEO) 100점 · 에이전트 브라우징 4/4 만점을 달성했다.

6. 마치며: 읽히는 방식과 지키는 방식의 균형

독립 블로그를 운영하고 가꾼다는 것은 결국 ‘누구에게 어떻게 읽힐 것인가’와 ‘무엇을 어떻게 지킬 것인가’의 균형을 잡는 일이다. 인간 독자에게는 종이 잡지처럼 차분하고 쾌적한 타이포그래피 화면을, 지식을 탐색하러 온 AI 에이전트에게는 군더더기 없는 마크다운 본문(Accept: text/markdown)과 안내도(llms.txt), 전문 합본(llms-full.txt)을 건네며, 그 아래의 도메인과 서버는 암호학적 서명(DNSSEC)과 웹서버 방어 규칙으로 단단하게 지키는 것. 이번 개편으로 다져놓은 기술적 토대 위에서 앞으로도 연구와 비평, 기술과 일상의 기록들을 차곡차곡 쌓아갈 생각이다.