본문 바로가기

정석성형외과 — 기계가 읽을 수 있는 사이트로 다시 만든 기록

나비랑 발행 케이스 스터디AEO구조화 데이터병원

요약

나비랑은 정석성형외과 새 사이트의 웹·구조화 데이터 작업을 수행하면서, 검색엔진과 답변엔진이 읽는 층(구조화 데이터·메타데이터·공유 카드·llms.txt·배포 가드)을 2026년 8월 28일 커밋 b486c20 한 번으로 정리했습니다. 같은 날 curl과 데스크톱 브라우저 UA로 잰 기존 사이트는 홈 문서 498바이트에 읽히는 글자가 0자였고, 하위 페이지에서 추출되는 359~406자는 전부 좌측 메뉴였습니다. 새 사이트는 아직 콘텐츠가 실데이터로 확정되기 전 단계라 유입·순위·인용률 같은 성과 수치는 이 글에 없습니다. 측정이 시작되지 않았습니다.

세 줄 요약

  • 나비랑은 정석성형외과 새 사이트의 웹·구조화 데이터 작업을 수행했고, 검색엔진과 답변엔진이 읽는 층 — 구조화 데이터·메타데이터·공유 카드·llms.txt·배포 가드 — 을 2026년 8월 28일 커밋 b486c20 한 번(50개 파일 · 1,138줄 추가 · 161줄 삭제)으로 정리했습니다. 사이트맵과 robots 규칙은 그 이전 단계에서 이미 같은 원칙으로 만들어져 있었고, 이 커밋은 robots.ts에 AI 크롤러 분류 주석을 더했습니다.
  • 같은 날 실측한 기존 사이트는 사람에게는 정상적으로 보였습니다. 기계가 받아 가는 HTML에서 글자가 아예 안 나온 것은 아닙니다 — 하위 페이지에서 359~406자가 추출되지만 그 전부가 좌측 메뉴이고, 본문에 해당하는 글자는 0자였습니다. 본문은 2000×4792 픽셀짜리 JPEG 한 장 같은 이미지 안에 있었습니다.
  • 성과 수치는 이 글에 없습니다. 측정이 시작되지 않았습니다.

사람에게는 멀쩡한 사이트였고, 기계에게는 빈 문서였습니다

홈 문서는 498바이트였고, 그 안에서 읽히는 글자는 0자였습니다. 화면이 정상적으로 보여도, 크롤러가 받아 가는 HTML 원문에 글자가 없으면 검색엔진과 답변엔진에게 그 문서는 없는 것과 같습니다. 화면에 보이는데 HTML 원문에 없으면 없는 것입니다.

나비랑은 이 층을 SEO·AEO·GEO 세 층으로 나눠서 봅니다. 기존 사이트가 걸려 있던 지점은 가장 아래층이었습니다. SEO가 문서를 색인에 올리기 위해 전제하는 기술 접근성 — 자바스크립트 없이도 본문이 존재하는가 — 에서 막혀 있었고, 그래서 위의 두 층은 시작될 수도 없었습니다.

읽을 글자가 이미 있는 사이트가 답변에 발견되지 않는 문제는 성격이 다릅니다. 그쪽은 Reviewloger 사례에 따로 적어 두었습니다.

2026년 8월 28일, 기존 사이트를 실측했습니다

측정 조건은 이렇습니다. 2026년 8월 28일, curl에 데스크톱 브라우저 UA를 붙여 HTML 원문을 받고, 스크립트와 주석을 제거한 뒤 태그를 걷어내고 공백을 한 칸으로 정규화한 문자열의 길이를 셌습니다. 자바스크립트는 실행하지 않았습니다 — 상당수의 수집기가 그 상태로 문서를 읽기 때문에, 그것이 기계가 보는 최소한의 모습입니다. 하루치 1회 취득이라 재현성은 확인하지 않았습니다. 서버가 시점에 따라 다른 응답을 낼 가능성은 이 측정으로 배제되지 않습니다.

페이지문서 크기추출 텍스트img / alt 있는 것h1·h2·h3JSON-LD
/498B0자0개
의료진 소개 /sub/sub_1/sub_1.php52,006B384자 (한글 222자)30 / 00·0·00개
진료시간·오시는 길 /sub/sub_1/sub_3.php54,260B394자 (한글 230자)31 / 00·0·00개
하위 C /sub/sub_2/sub_2_1.php52,459B406자 (한글 232자)30 / 00·0·00개
하위 D /sub/sub_2/sub_3.php52,465B406자 (한글 232자)30 / 00·0·00개
하위 E /sub/sub_3/sub_1.php52,978B359자 (한글 206자)36 / 20·0·00개
하위 F /sub/sub_4/sub_1.php52,935B361자 (한글 207자)37 / 20·0·00개

시술 페이지 넷은 주소만 적고 이름을 옮기지 않았습니다. 측정 근거로 필요한 것은 주소와 수치이지 시술명이 아닙니다.

표에서 가장 중요한 칸은 세 번째입니다. 52KB짜리 문서에서 읽히는 글자가 400자 안팎이고, 그 400자가 본문이 아닙니다. 의료진 소개·하위 D·하위 E·하위 F 네 장의 추출 텍스트를 토큰으로 비교하면 88~101개 토큰 중 55개가 공용이고, 페이지마다 다른 부분은 좌측 메뉴에서 펼쳐진 섹션 이름뿐이었습니다. 한 페이지에만 있는 고유 토큰은 그 페이지 좌측 메뉴의 하위 항목 이름 둘뿐이었습니다. 서로 다른 진료 페이지인데 기계가 받아 가는 글자는 사실상 같았습니다.

문서 머리도 같았습니다. 측정한 6장이 <title>·meta description·og:title·og:description글자 단위로 똑같이 쓰고 있었고, 제목은 전부 “정석성형외과”였습니다. 설명은 6장이 모두 같은 문자열이었고, 지역명·시술명·의료진 경력을 늘어놓은 키워드 나열이었습니다(문자열 자체는 옛 사이트의 광고 문구라 여기 옮기지 않습니다). og:image도 6장 모두 banner_logo.jpg 한 장이라 어느 페이지를 공유해도 같은 그림이 나갑니다. 한 페이지 안에 <title>이 2개, meta description이 2개 들어 있었고 두 번째 설명의 내용은 공백 한 칸이었습니다.

본문은 이미지 안에 있었습니다

글자가 어디로 갔는지는 이미지 파일을 재 보면 드러납니다.

  • 하위 D의 본문 이미지 /sub/sub_2/img/sub_3.jpg2000×4792 픽셀 · 1,902,966바이트(약 1.86MB) JPEG 한 장
  • 의료진 소개의 /sub/sub_1/img/sub_1.jpg — 2000×2392 픽셀 · 1,002,213바이트, 최종 수정 2021년 6월 23일
  • 하위 E·F는 본문이 여러 장으로 쪼개져 있습니다 — 2000×1108(176,251바이트), 2000×1113(177,123바이트) 등

같은 페이지에서 읽히는 텍스트가 406자이고 그 406자가 전부 메뉴라면, 남는 설명은 하나입니다. 본문이 그림 안에 구워져 있다는 것입니다. 이미지 30여 장 중 alt 속성이 붙은 것은 0~2개였으므로, 그림 옆에 글자로 남은 설명도 없었습니다.

다만 정확히 적어 두겠습니다. 이번 측정에서 이미지 속 글자를 판독(OCR)하지는 않았습니다. “본문이 이미지 안에 있다”는 결론은 해상도·용량과, HTML에서 읽히는 텍스트가 메뉴뿐이라는 두 가지 사실로 뒷받침한 것입니다.

발견 경로도 끊겨 있었습니다

읽을 글자가 없다는 것과 별개로, 크롤러가 페이지를 찾아가는 길 자체가 없었습니다.

  • 한 페이지에 들어 있는 <a href>는 정확히 2개이고 둘 다 외부 소셜 계정(네이버 블로그·카카오 채널)입니다. 사이트 내부 페이지로 가는 링크는 0개입니다.
  • 내부 이동은 전부 <li onclick="location.href=..."> 형태입니다. 페이지당 이동 대상이 25~31개 있지만 전부 자바스크립트 이벤트라 링크로 수집되지 않습니다.
  • 메뉴 마크업에서 확인되는 하위 페이지는 측정한 6장을 합쳐 24장이고(한 페이지의 마크업에는 자기 섹션이 펼쳐진 17~23장만 나옵니다), 거기 도달하는 경로는 자바스크립트 실행뿐입니다. sitemap.xml은 404입니다.
  • robots.txt는 248바이트이고 Sitemap: 지시문이 없습니다. 그리고 /js를 Disallow 하는데, 메뉴를 만드는 스크립트가 /js/jquery.menu.js/js/common.js에 있습니다 — 자바스크립트를 실행하는 크롤러조차 메뉴를 만들 재료를 가져가지 말라는 지시를 받습니다.
  • 측정한 하위 페이지 전부가 canonical로 홈 주소를 지정하고 있습니다. 각 페이지가 자기 자신이 아니라 본문 0자인 홈을 정본으로 선언하고 있었습니다.
  • 홈 문서 498바이트 안에서는 meta refresh가 자기 자신을 가리키고, 그 주소를 다시 받아도 같은 498바이트가 돌아옵니다. 문서에 들어 있는 것은 자바스크립트로 주소를 넣는 프레임 하나이고, 그 프레임에는 병원 콘텐츠가 0자입니다. 자바스크립트를 실행하지 않는 클라이언트에게 홈은 본문 없는 순환입니다.

한편 옛 사이트는 방치돼 있지 않았습니다. naver-site-verification 메타 태그가 들어 있어 네이버 서치어드바이저 소유확인은 되어 있었습니다. 관리 의지가 없어서 생긴 상태가 아니라 당시 흔했던 제작 방식이 그대로 남아 있는 상태입니다. 그래서 이 글의 주어는 병원이 아니라 우리가 한 기술 작업입니다.

새 사이트 — 페이지가 아니라 병원을 서술합니다

새 사이트는 Next.js(App Router)와 Payload CMS로 만든 한 애플리케이션이고, 프런트 라우트는 (frontend)·(home) 두 개의 라우트 그룹으로 나뉩니다. 구조화 데이터와 메타데이터 작업은 커밋 b486c20(2026-08-28, 50개 파일·1,138줄 추가·161줄 삭제) 한 번에 들어갔습니다.

넣은 것

실제로 나가는 최상위 schema.org 타입은 여섯입니다 — MedicalClinic, WebSite, BreadcrumbList, MedicalWebPage, Physician, FAQPage. 그 안에 중첩되는 타입이 MedicalProcedure, MedicalCondition, Physician, PostalAddress, ListItem, Question, Answer, SearchAction, EntryPoint입니다.

MedicalClinicWebSite는 페이지가 아니라 사이트의 성질이라서 레이아웃에서 한 번만 나갑니다. 페이지마다 적으면 반드시 한 장이 빠지고, 그 한 장에서 이 기관은 정체가 없는 문서가 됩니다. 다만 (home) 라우트 그룹은 아직 이 레이아웃 밖에 있어, 지금 상태로는 그 문제가 메인과 게시글에서 그대로 남아 있습니다. 값은 전부 사람이 CMS에 입력한 것을 읽고, 빈 값은 자리표시자를 채우는 대신 필드 자체가 사라집니다. 사람이 입력하지 않은 고정값은 medicalSpecialty: PlasticSurgery 하나뿐인데, 이것은 자유 문자열이 아니라 schema.org가 정의한 열거값이고 이 기관의 정의상 참이기 때문입니다.

sameAs에는 병원이 직접 운영하는 채널(카카오 채널·네이버 블로그·유튜브)만 넣었습니다. sameAs는 “다른 곳에 있는 같은 주체”라는 뜻이지, 남이 관리하는 디렉터리에 등재됐다는 뜻이 아니기 때문입니다.

넣지 않은 것

빼기로 한 판단이 넣은 것만큼 많습니다. 전부 코드 주석에 이유를 남겼습니다.

뺀 것이유
aggregateRating · review보고할 평점이 없습니다. 별점을 얻으려고 값을 만들어 넣는 것은 거짓이고, 의료광고이기도 합니다
priceRange수술비는 $$ 같은 대역으로 표현되는 값이 아니고, 정석성형외과 새 사이트는 가격을 게시하지 않습니다
openingHoursSpecification정석성형외과 새 사이트 CMS의 진료시간은 “평일 … 목요일, 일요일 휴무” 같은 자유 문장입니다. 요일 행으로 바꾸려면 ‘평일’이 어느 요일인지 추론해야 하고, 추론한 휴진일은 사람을 잠긴 문 앞으로 보냅니다
geo(좌표)기록된 좌표가 없습니다. 도로명 문자열에서 추정한 지오코드는 소수점을 단 지어낸 사실입니다

의사 페이지의 Physician에도 같은 이유로 평점과 후기를 넣지 않았습니다. 구조화 데이터의 목적을 의미 표현구글의 리치 결과로 처음부터 갈라 놓은 것도 같은 맥락입니다. Physician·MedicalProcedure·MedicalCondition·MedicalWebPage는 구글이 향상된 결과로 바꿔 주지 않는 정당한 타입이고, 배지가 나오는 마크업을 갖다 붙이고 싶은 유혹 때문에 그 구분을 코드에 다시 적어 두었습니다.

문서 머리·주소·빵부스러기를 각각 헬퍼 하나로 모았습니다

화면과 스키마가 같은 데이터를 읽게 만든다는 일반 원칙은 구조화 데이터, 어디부터 넣어야 하나요?에 있습니다. 여기 적는 것은 이 프로젝트에서 그것을 어떤 코드로 만들었는가입니다.

문서 머리를 빌더 하나로. 페이지 헤드는 pageMetadata() 하나가 만들고, 이 빌더를 쓰는 페이지가 13장입니다. 모으기 전에는 상세 5장이 각자 Open Graph 세 필드를 손으로 적고 목록 7장에는 아무것도 없었습니다. Next.js는 메타데이터를 얕게 병합하기 때문에 레이아웃에 기본값을 넣는 방식으로는 고칠 수 없었습니다 — 자기 Open Graph를 내보내는 페이지는 상위 값을 확장하는 게 아니라 통째로 대체합니다. 함께 max-snippet: -1을 명시했습니다. 스니펫 길이 제한은 이 페이지를 얼마나 인용해도 되는지 엔진에게 알려 주는 값이고, 이 작업의 논지는 본문이 인용될 값어치가 있다는 것이기 때문입니다.

주소는 한 헬퍼에서. canonical과 사이트맵 항목과 모든 JSON-LD의 @id가 같은 absoluteUrl()에서 나옵니다. 셋이 한 페이지의 주소를 두고 서로 다른 말을 할 수 없습니다. 빌더는 절대 URL을 받지 않고 사이트 상대 경로만 받습니다 — 호출부가 절대 URL을 넘길 수 있게 되는 순간, 그중 하나는 반드시 다른 오리진을 넘깁니다. 작업 중에 metadataBase가 아예 없다는 것도 드러났습니다. 그 값이 없으면 Next.js는 상대 경로 에셋을 Open Graph가 요구하는 절대 URL로 바꾸지 못하므로, 이번에 만든 공유 카드가 한 장도 해석되지 않았을 상태였습니다. 화면에도 로그에도 아무 이상이 보이지 않는 종류의 결함입니다.

빵부스러기는 화면과 같은 배열에서. BreadcrumbList를 페이지에서 Breadcrumb 컴포넌트 안으로 옮겼습니다. 옮기기 전에는 화면에 경로를 보여주는 페이지가 12장인데 구조화 데이터를 실제로 내보내는 페이지는 상세 5장뿐이었고, 목록·검색 7장은 경로를 보여주면서 아무것도 내보내지 않았습니다. BreadcrumbList는 정석성형외과 새 사이트가 내보내는 타입 중 구글이 결과 화면에 실제로 렌더하는 유일한 타입이라, 그 7장은 사이트가 가진 단 하나의 리치 결과를 비워 두고 있던 셈입니다. 이제 화면의 경로와 마크업이 같은 배열 하나에서 나오고, 한 페이지에서 두 번 내보내지 말라는 경고를 컴포넌트에 함께 적었습니다 — 한 페이지의 BreadcrumbList 두 개는 보강이 아니라 모순입니다.

FAQ는 화면이 렌더하는 답만. FAQPage는 페이지가 실제로 보여 주는 같은 배열에서만 만듭니다. 방문자가 볼 수 없는 답을 마크업하는 것은 클로킹이고, 이 프로젝트에서는 페이지의 검토 경로를 거치지 않은 임상 답변을 게시하는 일이 됩니다. 질문이나 답이 빈 항목은 걸러내고, 남는 것이 없으면 FAQPage 자체가 나가지 않습니다. 구글은 FAQ 리치 결과를 대부분의 사이트에서 걷어 갔지만 그래도 넣은 이유는 리치 결과가 아니라 인용 후보입니다 — 질문과 답이 이미 짝지어진 형태를 찾는 답변엔진은 다른 소비자입니다.

/llms.txt — “여기에 없는 것”까지 적습니다

llms.txt는 공식 표준이 아니고 어떤 주요 엔진도 읽는다고 확인한 바가 없습니다(llms.txt란 무엇이고 꼭 만들어야 하나요?). 비용이 거의 없어 선택 항목으로 만들었고, 이 절은 “읽힌다면 무엇을 읽게 할 것인가”에 대한 기록입니다.

/llms.txt는 커밋된 파일이 아니라 생성 라우트로 만들었습니다. 손으로 쓴 llms.txt는 시술 하나가 발행되는 순간부터 썩기 시작하는 스냅샷이기 때문입니다. 목록은 사이트맵이 쓰는 것과 같은 발행 조회 헬퍼에서 나오므로 미발행 문서는 여기에도 나올 수 없고, 기관 사실(정식 명칭·주소·전화)은 MedicalClinic과 같은 출처를 읽어 두 곳이 다른 주소를 말할 수 없습니다.

특이한 절이 하나 있습니다. “무엇이 없는가” 입니다. 가격·이벤트·할인이 없다는 것, 평점·후기 집계·순위를 게시하지 않는다는 것, 임상 설명이 아직 작성되지 않은 시술은 목록에 없다는 것을 적었습니다. 그리고 정석성형외과 새 사이트의 /llms.txt에 이 문장을 넣었습니다.

목록에 없다는 것은 진료하지 않는다는 뜻이 아니라, 이 사이트가 아직 그에 대해 말하지 않았다는 뜻입니다.

목록을 읽는 쪽은 빠진 항목을 하지 않는 항목으로 읽을 수 있습니다. 침묵이 부정으로 번역되는 것을 막는 문장이 필요했습니다. 파일 머리말에는 두 개의 금지도 함께 적혀 있습니다 — 여기에 없는 사실을 만들지 않는다, 그리고 성과·만족도·순위를 쓰지 않는다. 정석성형외과 새 사이트의 다른 어디에도 없는 종류의 문장을 기계용 파일에만 넣는 것은 클로킹이고, 의료광고이기도 합니다. 인용 규약도 함께 실었습니다. 수치나 판단을 인용할 때는 그것이 실린 개별 페이지 주소를 함께 밝혀 달라는 요청, 그리고 정석성형외과 새 사이트의 어떤 문장도 진찰을 대신하지 않는다는 문장입니다.

robots.txt도 다시 썼습니다. *에 대해 /를 허용하고 /admin/api/만 막습니다. 이 커밋에서 규칙은 한 줄도 바뀌지 않았고, 더한 것은 주석입니다 — AI 크롤러는 현재 학습용·인용용 모두 허용돼 있고, 그것이 검토를 마친 결정이 아니라 아직 내려지지 않은 결정이라는 사실 자체를 파일 주석에 적어 두었습니다. 병원이 나중에 어느 쪽을 닫기로 하면 그 판단이 어디서 시작됐는지 코드가 말해 줍니다. 판단 기준은 AI 크롤러를 robots.txt에서 허용해야 하나요?에 있습니다.

사이트맵은 이 커밋 이전에 이미 같은 원칙으로 만들어져 있습니다. 발행 여부 필터를 다시 적지 않고 페이지가 쓰는 것과 같은 헬퍼를 씁니다. DB 장애 시에는 불완전한 목록을 온전한 것처럼 내보내지 않고 검증 가능한 정적 경로만 냅니다. 검색 페이지와 필터가 걸린 목록 뷰는 일부러 뺐습니다 — 둘 다 noindex, follow이고, 쿼리스트링 조합을 싣는 것은 진짜 페이지와 경쟁하는 얇은 페이지를 만들어 내는 일입니다.

배포를 멈추는 장치

새 사이트 푸터에는 “개발용 픽스처 데이터로 구성된 화면입니다. 실제 의료기관 정보가 아닙니다.”라는 문장이 조건 없이 렌더되고 있었습니다. 그대로 두었다면 실제 콘텐츠로 전환한 뒤에도 전 페이지가 자기 내용을 부인하는 문장을 달고 나갔을 것이고, 그 문장은 같은 페이지가 내보내는 MedicalClinic 마크업과 정면으로 부딪힙니다. 답변엔진은 하단 부인 문구를 그대로 읽고 인용합니다. 이 문구는 이제 상단 안내와 같은 판단에 물려 있고, 그 판단의 기본값은 픽스처이며 DB에 닿지 못해도 픽스처로 떨어집니다. 콘텐츠가 진짜라는 주장은 반드시 사람이 해야 성립합니다.

배포 전 스크립트(fixture-guard)는 이제 콘텐츠보다 먼저 사이트 주소를 검사합니다. NEXT_PUBLIC_SITE_URL이 비었거나 localhost이거나 https가 아니면 배포를 세웁니다. 다만 이 검사는 npm run build 앞단에 붙는 배포 전 스크립트입니다 — 그 스크립트를 거치지 않은 빌드는 막지 못합니다. NEXT_PUBLIC_ 값은 빌드 시점에 코드로 구워지기 때문에, 그 값 없이 만든 프로덕션 빌드는 canonical·사이트맵 항목·JSON-LD @id·OG 주소가 전부 http://localhost:3000을 가리킨 채 나가고, 그중 어느 것도 화면에 보이지 않아 아무도 이상을 느끼지 못합니다. 가드는 DB를 읽어 콘텐츠가 실제 데이터인지도 묻고, DB에 닿지 못하는 경우 역시 실패로 처리합니다 — 닿지 않는 DB는 콘텐츠가 진짜라는 증거가 아닙니다.

런타임 검사가 아니라 스크립트로 둔 이유도 적어 두었습니다. 런타임 검사는 콘텐츠 실수를 장애로 바꾸지만, 스크립트는 답이 아직 “하지 마라”일 수 있는 마지막 순간에 멈춥니다. 다만 상호·사업자등록번호·의료기관 개설 신고번호가 비어 있으면 실패시키지 않고 경고만 찍습니다. 판단은 사람의 몫이고, 푸터는 빈 값에 자리표시자를 찍는 대신 줄 자체를 렌더하지 않습니다.

푸터의 나비랑 제작 크레딧 링크에는 rel="nofollow noopener"를 달았습니다. 사이트 전역 푸터에서 제작사로 나가는 링크는 구글이 링크 스팸 지침에서 직접 다루는 형태라, follow로 두면 받는 쪽이 손해를 봅니다.

아직 남은 것 — 정리해야 하는 것들

우리가 한 작업만 적고 끝내면 이 글은 절반짜리 기록이 됩니다. 코드와 실제 응답에서 확인되는 미완 항목을 그대로 적습니다.

  • 배포 경로가 아직 가드를 거치지 않습니다. NEXT_PUBLIC_SITE_URLNEXT_PUBLIC_ 접두사가 붙은 값이라 빌드 시점에 코드로 구워집니다 — 런타임 변수로 넣으면 늦습니다. 그 값 없이 만들어진 빌드는 canonical과 sitemap.xmllocrobots.txtSitemap: 지시문이 전부 http://localhost:3000을 가리키는데, 그중 어느 것도 화면에 보이지 않아 아무도 이상을 느끼지 못합니다. 앞서 만든 배포 전 스크립트는 이 조건을 정확히 막지만, 그 스크립트를 거치지 않는 빌드 경로가 따로 있으면 소용이 없습니다. 빌드 환경에 값을 넣는 일과 가드를 배포 경로에 물리는 일이 남아 있고, 그때까지 주소를 이 글에 링크하지 않았습니다.
  • 증례 상세 페이지는 공용 Breadcrumb 컴포넌트를 쓰지 않고 자체 마크업으로 경로를 그리기 때문에, 화면에는 경로가 보이지만 BreadcrumbList는 나가지 않습니다. 이 페이지는 커밋 전에는 BreadcrumbList를 내보내고 있었고, 마크업을 컴포넌트로 모으면서 이 한 장만 옮겨지지 않았습니다. 7장을 되찾으면서 한 장을 잃은 셈이고, 배포 전에 이 페이지를 공용 컴포넌트로 옮깁니다.
  • 메인 페이지와 게시글(home) 라우트 그룹의 별도 레이아웃을 쓰므로 MedicalClinic·WebSite JSON-LD와 하단 타일이 붙지 않습니다.
  • 시술 문서 17건은 전부 미발행입니다. 옛 사이트의 시술 설명이 이미지에 구워진 글이라, 그림을 읽어 옮긴 의료 서술을 개발자가 그의 이름으로 발행할 수는 없습니다. 이름·부위·구조상의 위치만 넣고 임상 필드는 비워 두었으며, 페이지는 그 대기 상태를 그대로 보여 줍니다. 의사 페이지도 같은 이유로 미발행입니다 — 발행된 문서가 하나도 그를 가리키지 않으면 “발행된 개체는 고아일 수 없다”는 테스트가 실패하는데, 그 테스트가 옳다고 보고 단언을 약화하는 대신 발행을 미뤘습니다.
  • 자격 10건은 미검증 상태로 들어가 있어 화면에 출력되지 않습니다. 병원 페이지에서 읽어 옮긴 것이라, 서류를 확인한 사람이 관리자에서 하나씩 넘기게 되어 있습니다.
  • 매부리코·복코·콧볼은 시술이 아니라 고민 페이지로 먼저 만들었습니다. 셋 다 진단명이 아니라 겉으로 보이는 상태를 가리키는 말이라, 시술로 만들면 병원이 설명한 적 없는 시술명을 우리가 붙이는 셈이 됩니다. 요약은 “그 말이 무엇을 가리키는지”까지만 쓰고 원인·판단 기준·수술 방법은 한 글자도 넣지 않았습니다.
  • 하단 타일 6장(800×800 webp)과 공유 카드 7장(1200×630 PNG)을 새로 만들었습니다. 그 전에는 세트로 묶일 이미지 묶음이 한 장도 없었고, 주소를 메신저에 붙이면 제목 한 줄과 빈 회색 상자가 나갔습니다. 타일에는 인물 사진을 쓰지 않았습니다 — 성형외과 사이트에서 얼굴 클로즈업 위에 시술명 라벨이 얹히면 시술 전후 사진으로 읽히기 때문입니다. 공유 카드에는 성과·수치·전후를 넣지 않았습니다. 카드에는 고지 문구를 함께 실을 자리가 없어서, 카드에 찍힌 문장은 조건 없이 혼자 돌아다닙니다.

아직 없는 것 — 성과 수치는 측정이 시작되지 않았습니다

이 글에는 유입·순위·인용률·만족도 수치가 한 줄도 없습니다. 측정이 시작되지 않았기 때문입니다. 새 사이트는 아직 콘텐츠가 실데이터로 확정되기 전 단계이고, 저장소에도 측정 데이터가 없습니다. 커밋 기록에도 같은 확인이 남아 있습니다 — 이번 작업에서 추가된 것 중 성과·평점·가격을 주장하는 것은 없습니다.

배포 후 같은 조건으로 반복 측정해 실제 값이 생기고 병원의 공개 동의를 받으면 별도 글로 공개합니다. 질의 세트 설계 방식과 인용 점유율 집계법은 고객사 건에서는 공개 범위 밖입니다(자사 측정은 조건 전문을 공개합니다). 미리 적지 않는 이유는 AEO 대행사가 말하는 성과 수치, 어디까지 믿을 수 있나요?에 있습니다. 여기서는 더 단순합니다 — 아직 아무것도 측정하지 않았습니다. 진행 중인 다른 프로젝트의 상태도 구축 사례에 같은 원칙으로 적어 두었습니다.

항목내용
기존 사이트 측정일2026년 8월 28일
측정 방법curl + 데스크톱 브라우저 UA로 HTML 원문 취득 · 스크립트·주석 제거 후 태그를 걷어내고 공백을 한 칸으로 정규화한 문자열 길이 · 자바스크립트 미실행 · 이미지 크기는 Range 요청으로 앞부분만 받아 JPEG 마커 판독
반복페이지당 1회 취득 — 반복 측정 아님(2026년 8월 28일)
측정 범위홈 1장 + 하위 페이지 6장, robots.txt·sitemap.xml·llms.txt 응답 코드
하지 않은 것이미지 속 글자 판독(OCR) · 순위 조회 · 유입 데이터 조회
새 사이트 작업 범위 검증저장소 코드와 커밋 b486c20(2026-08-28) 확인
새 사이트 동일 조건 실측아직 같은 조건으로 재지 않았습니다
새 사이트 성과 측정없음 — 측정이 시작되지 않았습니다

이 사례는 새 사이트의 웹·구조화 데이터 작업을 수행한 나비랑이 작성한 기술 기록입니다. 제3자가 검증한 평가가 아니며, 여기서 관찰된 결과가 다른 사이트에서 재현된다는 보장은 없습니다. 기존 사이트의 수치는 2026년 8월 28일에 위 조건으로 측정한 값이고, 새 사이트에 대한 서술은 저장소 코드와 커밋, 그리고 같은 날 확인한 응답에서 확인한 것만 적었습니다.

이 글은 웹 구축·AEO 수행사 관점의 기술 기록입니다. 시술의 효과·안전성이나 의료진에 대한 어떤 표현도 담지 않았습니다 — 의료광고의 주체는 의료기관이고, 나비랑이 낼 수 있는 문장이 아닙니다. 옛 사이트에서 관찰된 문자열은 측정 대상 원문이며 나비랑의 서술이나 주장이 아닙니다.

자주 묻는 질문

Q 옛 사이트가 왜 문제였나요?

A 사람이 브라우저로 열면 정상적으로 보였습니다. 문제는 기계가 읽는 쪽이었습니다. 2026년 8월 28일 curl과 데스크톱 브라우저 UA로 HTML 원문을 받아 보니 홈 문서는 498바이트에 읽히는 글자가 0자였고, 하위 페이지 6장에서 추출되는 359~406자는 전부 좌측 메뉴 목록이라 본문에 해당하는 글자가 한 자도 없었습니다. 본문은 2000×4792 픽셀·약 1.86MB짜리 JPEG 같은 이미지 안에 들어 있었습니다. 한국 병원 사이트에 흔한 구조이고, 이번 재구축은 그 상태를 출발점으로 삼았습니다.

Q 측정이 시작되기 전인데 왜 지금 사례를 공개하나요?

A 작업이 끝난 시점과 측정이 시작되는 시점이 다르기 때문입니다. 코드와 커밋으로 확인되는 작업 범위는 지금 검증할 수 있고, 유입·순위·인용은 사이트가 실데이터로 공개된 이후에야 존재합니다. 둘을 한 글에 섞으면 어느 쪽도 검증되지 않습니다.

Q AEO는 결국 글쓰기 아닌가요?

A 글쓰기는 마지막 단계입니다. AEO(Answer Engine Optimization, 답변엔진 최적화)는 답변엔진이 어떤 문서를 답의 후보로 고를 수 있게 만드는 작업이고, 이 프로젝트에서 실제로 걸려 있던 것은 글의 품질이 아니라 HTML에 읽을 글자가 없다는 것, 내부 링크가 하나도 없다는 것, 모든 페이지가 같은 제목과 같은 canonical을 쓴다는 것이었습니다. 잘 쓴 글도 기계가 가져갈 수 없으면 후보에 오르지 않습니다.

Q 성형외과 사이트인데 왜 평점·후기 마크업을 넣지 않았나요?

A 보고할 평점이 없었고, 별점을 얻으려고 값을 만들어 넣는 것은 의료법상 광고 주체가 의료기관인 문장을 나비랑이 대신 만드는 일이 되기 때문입니다. 그래서 aggregateRating과 review는 처음부터 후보에서 뺐고, 의사 페이지의 Physician에도 같은 이유로 평점과 후기를 넣지 않았습니다. 일반 기준은 /insights/structured-data-start/ 에 있습니다.

Q 나비랑이 이 프로젝트에서 수행한 범위는 어디까지인가요?

A 웹 구축과 콘텐츠 구조, SEO·AEO·GEO 구조, 구조화 데이터, robots.txt·사이트맵·llms.txt 같은 발견 인프라까지입니다. 임상 서술은 범위 밖입니다. 시술 문서 17건은 시드 단계에서 전부 미발행 상태로 들어가 있고, 원장이 원고를 쓰기 전까지 발행되지 않습니다. 이 글의 작업 범위는 전부 저장소 코드와 커밋에서 확인한 사실만 적었습니다.

함께 읽으면 좋은 글

SERVICES

서비스 바로가기

전체 서비스 자세히 보기
무료진단 전화상담 이메일 블로그