
개인 웹사이트를 위한 JSON-LD 설명
요약
웹페이지의 의미론적 구조를 강화하기 위한 JSON-LD의 기본 원리와 구현 방법을 설명합니다. Schema.org 컨텍스트를 활용하여 크롤러가 데이터를 정확히 파싱하도록 돕는 구조화된 데이터 작성법을 다룹니다.
핵심 포인트
- JSON-LD는 웹 크롤러가 사이트 구조를 이해하도록 돕는 구조화된 데이터 형식임
- Schema.org를 표준 컨텍스트로 사용하여 데이터의 의미를 정의함
- @graph와 @id를 활용해 노드 간의 관계를 그래프 형태로 표현 가능
- 고유 식별자(@id)를 통해 여러 페이지에 걸친 데이터 병합을 지원함
JSON-LD(JSON Linked Data로도 알려짐)는 웹페이지에 구조화된 데이터 (structured data)를 추가하기 위한 형식입니다. 이는 웹 크롤러 (web crawlers)가 사이트의 의미론적 구조 (semantic structure)를 이해하도록 돕고, 더 풍부한 링크 미리보기를 제공할 자격을 부여하며, 잠재적으로 검색 순위 (search ranking)를 개선할 수도 있습니다.
이 사이트를 구축하는 과정을 설명한 첫 번째 포스팅 이후 4개월이 지났으며, Wakatime의 추정에 따르면 조사 및 테스트에 소비된 시간을 제외하고 현재까지 약 100시간을 코딩하는 데 사용했습니다. 그 이후로 이 사이트는 각 페이지에 JSON-LD를 추가하는 것을 포함하여 많은 다듬기 과정을 거쳐왔습니다.
JSON-LD 기본 원리 (Fundamentals)
페이지에 JSON-LD를 추가하려면 <head> 섹션의 적절한 위치에 다음을 추가하세요:
<script type="application/ld+json">
{
"@context": "https://schema.org",
...
각 부분이 어떤 역할을 하는지 나누어 살펴보겠습니다.
<script type="application/ld+json">
이것은 MIME 타입이 application/ld+json인 새로운 스크립트를 선언합니다. 이 타입이 지정되어 있기 때문에 브라우저의 JS 엔진은 이를 실행하지 않습니다. Googlebot과 같은 특화된 크롤러 (crawlers)들이 이러한 요소를 찾아 내용을 파싱 (parse)합니다.
{
"@context": "https://schema.org"
}
여기서 JSON 객체가 초기화되고 @context 속성이 https://schema.org로 설정됩니다. JSON-LD에서 데이터의 구조는 적절한 컨텍스트 (context)를 할당함으로써 결정됩니다. 웹 크롤러들은 JSON에 대한 모든 유효한 키-값 쌍 (key-value pairs)을 정의하는 Schema.org를 표준으로 사용합니다.
이제 우리의 JSON-LD가 따르는 스키마 (schema)를 정의했으므로, 웹페이지를 설명할 수 있습니다!
{
"@graph": [
{
...
JSON-LD 문서는 @graph 아래에 저장된, 레이블이 지정된 방향성 그래프 (labelled, directed graph)로 생각할 수 있습니다. 그래프는 서로 방향성 아크 (directed arcs)로 연결된 여러 노드 (nodes)를 포함합니다.
노드 (Nodes)는 다음을 가집니다:
@type
- 노드가 무엇인지 설명합니다. 예:
WebSite또는SoftwareApplication
@id
-
노드의 고유 식별자 (unique identifier)로, 일반적으로 끝에 고유한 해시 값 (hash value)이 붙은 URL입니다.
-
속성 (Properties) - 노드의 속성을 설명하는 키-값 쌍 (Key/Value pairs)
위의 예시에서, 타입 (type)은 WebSite이고, ID는 https://hawksley.dev/#website이며, url과 name이라는 두 개의 속성 (properties)을 가지고 있습니다.
웹 크롤러 (Web crawlers)는 노드 (node)들이 동일한 ID를 공유하는 한, 여러 페이지에 걸쳐 해당 노드의 속성들을 병합할 수 있습니다. 하지만 LLM (Large Language Models)과 같이 단일 페이지 만을 읽는 스크래퍼 (scrapers)는 속성을 병합하지 않습니다. JSON-LD가 여러 페이지에서 재사용될 때, 이러한 균형을 고려하는 것이 중요합니다. ID는 노드를 고유하게 식별할 수 있도록 #website와 같이 URL 뒤에 해시 (#)가 붙은 형태를 사용하는 것이 가장 좋은 관행 (best practice)입니다.
Schema.org 컨텍스트 (context)는 많은 유형의 노드를 정의하지만, 이 가이드에서는 SEO (Search Engine Optimization)에 눈에 띄는 영향을 미치는 노드들만 다룰 것입니다. 더 많은 내용에 관심이 있다면 시맨틱 웹 (semantic web)을 찾아보세요. 매우 흥미로운 탐구 주제가 될 것입니다.
이제 우리 사이트의 각 페이지에 어떤 노드들을 포함해야 하는지로 넘어가 보겠습니다. 각 유형별로 이 사이트에서 사용 중인 JSON-LD를 포함해 두었으니, 이를 복사하여 붙여넣은 뒤 본인에게 맞게 수정하여 사용하시면 됩니다.
WebSite
앞서 WebSite의 일부를 확인했습니다! 이제 전체 버전을 소개합니다:
{
"@type": "WebSite",
"@id": "https://hawksley.dev/#website",
...
WebSite는 사이트에 대한 메타데이터 (metadata)를 설명합니다. 이는 크롤러에게 사이트를 어떻게 표시할지에 대한 힌트를 제공합니다.

여기서 Google이 name 필드를 도메인을 대표하는 것으로 해석하여 검색 결과에 적절하게 라벨을 붙이고 있는 것을 볼 수 있습니다.
WebSite는 모든 페이지에 적용되지만, 모든 페이지에 전체 버전을 포함할 필요는 없습니다. 도메인의 루트 페이지 (root page)는 상세하게 작성되어야 하지만, 다른 페이지들은 축소된 버전을 사용해도 무방합니다:
{
"@type": "WebSite",
"@id": "https://hawksley.dev/#website",
...
이렇게 하면 단일 페이지 크롤러 (single-page crawlers)에게 사이트 이름을 올바르게 지정할 수 있는 충분한 컨텍스트 (context)를 제공하면서도, 불필요하게 모든 세부 정보를 전달하지 않아도 됩니다.
WebPage
WebPage는 현재 페이지를 설명하지만, 이를 BlogPosting (나중에 다룸)과 같은 다른 유형과 구분하는 것이 중요합니다. WebPage
는 물리적인 페이지 자체, 즉 HTML을 나타냅니다. 여기에는 페이지의 콘텐츠가 포함됩니다.
{
"@type": "WebPage",
"@id": "https://hawksley.dev/blog/hack-club-campfire/#webpage",
...
WebPage에는 더 구체적인 하위 유형 (subtypes)들이 있습니다. 이 포스트에서는 ProfilePage와 CollectionPage를 다룰 것입니다. 덜 일반적인 유형들은 Schema.org의 WebPage 정의 하단에서 찾을 수 있습니다.
Person
개인 웹사이트의 모든 페이지가 가져야 할 또 다른 노드 (node)는 Person입니다. 이는 당신이 누구인지를 설명하며, Google은 이를 콘텐츠 품질 지표 (content quality metric)의 일부로 사용합니다. 점점 더 많은 LLM 크롤러 (crawlers) 또한 답변에서 누구를 인용할지 결정하기 위해 이를 사용하고 있습니다.
WebSite와 달리, 이는 충분히 중요한 문맥 (context)이므로 사이트의 모든 페이지에 포함해야 합니다.
Warning - Quite Long!
{
"@type": "Person",
"@id": "https://hawksley.dev/#person",
...
휴! Person에는 속성 (properties)이 아주 많습니다. 내용을 채울 때는 덜 적는 것보다 더 상세하게 기술하는 것이 도움이 된다고 생각합니다. 가장 중요한 속성들을 살펴보겠습니다:
url
- 당신의 루트 페이지 (root page)를 가리키며, 노드를 고정 (anchoring)합니다.
name, givenName, familyName
- 당신의 이름을 명확하게 설명합니다.
image
- 가급적 당신의 사진이나 당신이 소속된 로고를 사용하세요. 당신을 대표하는 이미지 (canonical image)와 연결해 줍니다.
sameAs
- 특히 이름이 흔한 경우, 동명이인 식별 (disambiguation)에 엄청나게 유용합니다. 크롤러에게 당신의 다른 프로필이 무엇인지 깔끔하게 알려주어, 여러 페이지에 걸쳐 당신에 대한 지식 그래프 (knowledge graph) 표현을 구축할 수 있게 합니다. 이 글을 쓰는 시점에, /g/11m62cgdtf는 저의 Google 지식 그래프 ID입니다.
Person의 다른 속성들은 더 많은 세부 정보를 추가하는 데 유용하지만, 엄격하게 필수적인 것은 아닙니다. 원한다면 약간의 영향만 미친 채로 이를 줄일 수 있습니다.
ProfilePage
ProfilePage
예상하시겠지만, ProfilePage는 사이트 내에서 특정 인물에 관한 페이지를 설명합니다. 예를 들어, 저는 제 자신에 대해 이야기하는 홈 페이지에 이 노드 (node)를 사용합니다. 여러분의 사이트에서는 '소개(About)' 페이지에 배치하는 것이 더 적절할 수 있습니다.
{
"@type": "ProfilePage",
"@id": "https://hawksley.dev/#webpage",
...
두 노드 (node) 사이의 관계를 생성하기 위해, 이를 더 넓은 범위의 WebSite 노드 (node)와 연결하는 isPartOf를 사용하는 것이 중요합니다. mainEntity 역시 마찬가지로, 크롤러 (crawler)에게 해당 페이지가 누구에 관한 것인지 알려줍니다. dateCreated와 dateModified를 포함하는 것은 크롤러 (crawler)에게 좋은 최신성 신호 (freshness signal)가 되지만, 사이트에 해당 정보가 즉시 사용 가능하지 않더라도 너무 걱정할 필요는 없습니다.
SoftwareApplication
페이지에 소프트웨어 (software)를 전시하고 있다면, 해당 소프트웨어에 대한 메타데이터 (metadata)를 설명하기 위해 SoftwareApplication 노드 (node)를 포함하는 것이 좋습니다.
{
"@type": "SoftwareApplication",
"@id": "https://hawksley.dev/#project-yt-play",
...
SoftwareApplication보다 더 구체적으로 지정하고 싶다면, 이 노드 (node)에 사용할 수 있는 다른 유효한 타입 (type)으로는 MobileApplication, WebApplication, 그리고 VideoGame이 있습니다.
url 속성 (property)은 crates.io와 같이 프로젝트가 배포된 곳으로 연결되는 링크여야 합니다. sameAs는 소스 코드 저장소 (repository)와 같이 프로젝트와 연관된 다른 페이지들을 위한 것입니다.
applicationCategory에는 많은 유효한 값들이 있으며, Google의 SoftwareApplication 정의 페이지에서 목록을 확인할 수 있습니다.
여러분의 프로젝트가 FOSS (자유 오픈 소스 소프트웨어)라 할지라도, offers를 포함하되 가격 (price)을 0으로 설정해야 합니다.
BreadcrumbList
BreadcrumbList는 매우 유용하며, 루트 페이지 (root page)를 제외한 모든 페이지에 포함되어야 합니다. 이는 페이지의 실제 경로가 반드시 아닐지라도, 페이지의 분류 (categorisation)를 설명하는 데 사용됩니다.
{
"@type": "BreadcrumbList",
"@id": "https://hawksley.dev/blog/hack-club-shipwrecked/#breadcrumb",
...
BreadcrumbList는 페이지의 경로를 설명합니다. 이를 포함함으로써, 검색 엔진 (search engine)이 특정 페이지의 경로를 어떻게 표현할지 제어할 수 있습니다.

여기서 제 블로그 게시물에 대한 검색 결과는 https://hawksley.dev › Blog 경로를 포함하고 있습니다.
만약 귀하의 사이트가 이미 짧은 경로를 사용하고 있다면, 이 노드는 미미한 이득이며 생략할 수 있습니다. 하지만 경로가 더 길다면, BreadcrumbList (브레드크럼 리스트)는 이를 단축하는 데 유용합니다.
CollectionPage (컬렉션 페이지)
CollectionPage 노드는 주로 목록을 포함하는 페이지에 사용할 수 있는 WebPage (웹 페이지)의 하위 유형입니다. 예를 들어, 저의 /elsewhere/ 페이지는 저의 다른 모든 프로필을 나열하며, /blog/는 저의 모든 블로그 게시물을 나열합니다.
{
"@type": "CollectionPage",
"@id": "https://hawksley.dev/elsewhere/#webpage",
...
여러분은 이미 이러한 속성 대부분을 접해보았으므로, 대부분 직관적으로 이해될 것입니다. breadcrumb (브레드크럼)을 올바른 BreadcrumbList에 연결했는지 확인하세요! 의미가 통하려면 반드시 현재 페이지에 있는 것이어야 합니다.
Blog (블로그)
블로그의 인덱스 페이지나 홈 페이지에 Blog 노드를 추가해야 합니다. 이는 귀하의 WebSite (웹사이트)와 귀하가 게시하는 개별 블로그 게시물 사이의 디딤돌 역할을 합니다.
{
"@type": "Blog",
"@id": "https://hawksley.dev/blog/#blog",
...
dateModified (수정 날짜)는 좋은 최신성 신호(freshness signal)이지만, 바로 사용할 수 없다면 걱정하지 마세요. license (라이선스)를 포함하면 크롤러(crawler)가 어떤 상황에서 귀하의 글을 사용할 수 있는지 알 수 있게 해줍니다.

이전에 JSON-LD에 대해 조사해 보셨다면, publisher (발행인) 속성이 Organization (조직)이 아닌 Person (개인)으로 설정되어 있다는 점에 놀랄 수도 있습니다. 예전에는 조직을 요구하기도 했지만, 이후 Google의 문서가 완화되어 Person 또한 완전히 유효하며, 개인 웹사이트의 경우에는 오히려 더 정확하다고 할 수 있습니다.
BlogPosting (블로그 게시물)
우리가 다룰 마지막 노드는 BlogPosting입니다. 이는 게시된 모든 블로그 게시물에 포함되어야 하며, 크롤러에게 추가 정보를 제공하여 검색 결과에서 더 정확한 배치와 더 풍부한 세부 정보를 포함한 방식으로 게시물을 더 정확하게 표현할 수 있도록 합니다.
{
"@type": "BlogPosting",
"@id": "https://hawksley.dev/blog/need-for-post-quantum-cryptography/#blogposting",
...
이것은 개인 사이트이므로, author (작성자)와 publisher (발행자)가 모두 동일한 Person (인물) 노드를 가리켜도 괜찮습니다. image (이미지) 속성은 해당 포스트가 링크 미리보기에 이미 사용 중인 OG (Open Graph) 이미지와 동일해야 합니다.
결론
축하합니다, 이것이 개인 사이트에 필요한 모든 JSON-LD입니다! 여러분이 자신의 개인 사이트에 복사하여 붙여넣고 구현하기 가장 쉽도록 이 포스트를 구성했습니다. 빌드 단계가 없는 정적 사이트 (Static Site)를 운영하더라도, 사이트의 루트 페이지에 최소한 WebSite (웹사이트), ProfilePage (프로필 페이지), 그리고 Person (인물)을 추가함으로써 이점를 얻을 수 있습니다.
질문이 있으시면 이메일을 통해 연락해 주세요. 최선을 다해 도와드리겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 HN AI Posts의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기