검색어를 입력하면 실시간으로 용어와 글을 찾습니다.

v2.3 Playbook·Technical SEO

MKTG Edge 시맨틱 숏코드 컴포넌트 아키텍처 구축 및 파트너 연동 가이드

차트를 제외한 mktg.kr의 7대 핵심 시맨틱 숏코드 컴포넌트(<SemanticTable />, <SemanticFAQ />, <SoftwareCard />, <PersonBadge />, <YouTubeEmbed />, <LinkCard />, <Term />)의 기획 배경, Schema.org JSON-LD 자동 주입 아키텍처, 전체 Props 규격 및 외부 콘텐츠 에디터 연동 가이드를 정리한 공식 기술 문서입니다.

작성자: ·분류: Technical SEO··구글 검색 선호 출처 등록
📖 목차 (TABLE OF CONTENTS)펼치기 / 접기

“에디터는 직관적인 숏코드로 빠르게 작성하고, 빌드 파이프라인은 검색엔진과 AI 에이전트를 위한 시맨틱 HTML5와 Schema.org JSON-LD를 100% 무결하게 컴파일한다.”

본 문서는 mktg.kr에 구축된 시맨틱 숏코드(MDX Custom Components) 시스템의 기획부터 아키텍처 설계 과정, 그리고 파트너사 및 자체 콘텐츠 에디터에서 즉시 연동할 수 있도록 차트(<Chart />)를 제외한 7대 핵심 시맨틱 숏코드의 전체 입력 규격과 라이브 렌더링 결과를 집대성한 엔지니어링 플레이북입니다.

(※ 데이터 시각화 차트 컴포넌트에 대한 상세 가이드는 시맨틱 데이터 시각화 차트(<Chart />) 시스템 기획 및 구축기 문서를 참조하세요.)


1. 시맨틱 숏코드 시스템 기획 배경 및 설계 철학

일반 마크다운(Markdown)은 문단(p), 제목(h2~h4), 리스트(ul/ol)를 빠르게 작성하는 데는 탁월하지만, 테크니컬 SEO와 생성형 엔진 최적화(GEO) 관점에서는 세 가지 구조적 한계를 지닙니다:

  1. 표준 마크다운 테이블의 접근성(A11y) 및 모바일 레이아웃 한계:
    • 기본 마크다운 표(| 컬럼 |)는 <caption>, scope="col", 행 헤더(<th scope="row">) 속성을 지원하지 않으며, 모바일 화면에서 표가 깨지거나 카드 테두리가 본문 가독성을 해치는 문제가 발생합니다.
  2. UI와 Schema.org 구조화 데이터(JSON-LD)의 이원화로 인한 휴먼 에러:
    • 소프트웨어 리뷰(SoftwareApplication), 자주 묻는 질문(FAQPage), 전문가 인용(Person) 등을 작성할 때 본문 HTML과 <script type="application/ld+json">을 따로 작성하면 수치나 문구가 불일치하여 구글 서치 콘솔의 구조화 데이터 수동 조치(스팸 판정) 리스크가 발생합니다.
  3. 전문 기술 용어 설명을 위한 문맥 단절:
    • 독자가 아티클을 읽다 생소한 기술 용어를 만났을 때 외부 검색으로 이탈하지 않도록, 본문 흐름을 깨지 않는 인라인 호버 툴팁과 용어사전 내부 링크 구조가 필요했습니다.

1.1 MKTG Edge 시맨틱 숏코드의 3대 핵심 원칙

  • 원칙 1. Single Source of Truth (UI + JSON-LD 동시 컴파일): 에디터가 숏코드 속성(Props)에 값을 한 번만 입력하면, 시각적 모노그래프 UI 카드와 검색엔진용 Schema.org JSON-LD 스크립트가 동일한 데이터에서 동시에 생성됩니다.
  • 원칙 2. Zero-JS 네이티브 웹 표준 우선: 테이블, FAQ 아코디언(<details> / <summary>), 카드, 배지 등 모든 시맨틱 숏코드는 클라이언트 자바스크립트(0KB) 없이 순수 HTML5/CSS로 작동하여 Core Web Vitals(INP, CLS, LCP) 만점을 보장합니다.
  • 원칙 3. Zero-Import 전역 주입: 작성자가 파일 상단에 복잡한 import 경로를 신경 쓰지 않고 본문 어디서나 <SemanticTable />, <SemanticFAQ /> 태그를 바로 호출할 수 있도록 설계했습니다.

2. 전역 MDX 자동 주입 및 하이브리드 렌더링 아키텍처

mktg.kr에서 시맨틱 숏코드가 생성되고 페이지에 렌더링되기까지의 전체 파이프라인 구조입니다.

[1. 컴포넌트 모듈 계층 (src/components/semantic/)]
 ├── SemanticTable.astro    (반응형 모노그래프 표 + sr-only caption + scope)
 ├── SemanticFAQ.astro      (네이티브 <details> 아코디언 + FAQPage JSON-LD)
 ├── SoftwareCard.astro     (제품 스펙 그리드 + SoftwareApplication JSON-LD)
 ├── PersonBadge.astro      (저자/전문가 프로필 + Person JSON-LD)
 ├── YouTubeEmbed.astro     (YouTube Lite Facade + oEmbed + VideoObject JSON-LD)
 ├── LinkCard.astro         (외부 URL Open Graph 메타 자동 추출 + 북마크 카드)
 ├── Term.astro             (인라인 용어사전 호버 툴팁 + /glossary/[slug] 링킹)
 └── AutoLinkParagraph.astro / FigureImage.astro (문단 자동 링킹 & 이미지 <figure> 변환)
       │
       ▼ (src/components/semantic/index.ts 배럴 모듈 통합 export)
[2. 라우트 템플릿 전역 주입 계층]
 ├── 블로그 상세: src/pages/blog/[slug].astro
 └── Docs 상세:  src/pages/docs/[...slug].astro
       │  const mdxComponents = { SemanticTable, SemanticFAQ, FAQ, SoftwareCard, PersonBadge, YouTubeEmbed, YouTube, LinkCard, BookmarkCard, Term, ... }
       │  <Content components={mdxComponents} />
       ▼
[3. 최종 빌드 산출물 (HTML5 + JSON-LD)]

2.1 구축 과정에서 해결한 핵심 엔지니어링 이슈

  1. Vite 개발 서버 @/ 별칭 Import 오류 해결 (Global Injection 전환):
    • 초기에는 각 .mdx 문서 상단에 import SemanticTable from '@/components/SemanticTable.astro'를 선언했으나, Astro Content Layer와 Vite 모듈 러너 환경에서 경로 해석 충돌이 발생했습니다.
    • 이를 해결하기 위해 동적 라우트([slug].astro, [...slug].astro)에서 <Content components={mdxComponents} /> 형태로 모든 숏코드 컴포넌트를 사전 전역 주입하여, MDX 본문에서는 import 없이 순수 숏코드 태그만 사용하도록 표준화했습니다.
  2. 로컬 MDX와 원격 CMS(Notion/Emdash) 하이브리드 렌더링 분기:
    • 원격 마크다운은 marked 파서와 autoLinkGlossaryTerms()를 거치고, 로컬 .mdx 문서는 Astro의 네이티브 render() 파이프라인을 타도록 엄격히 분리하여 JSX 숏코드 컴포넌트가 문자열로 이스케이프되지 않고 온전한 HTML과 JSON-LD로 실행되도록 구현했습니다.
  3. MDX 문단 내 자동 용어사전 링킹 (AutoLinkParagraph, AutoLinkListItem):
    • 일반 <p>와 <li> 태그를 커스텀 시맨틱 래퍼로 오버라이드하여, 에디터가 별도 태그를 쓰지 않아도 본문 중 235개 등록 기술 용어가 최초 1회 자동으로 툴팁 링크로 변환되도록 구축했습니다.

3. 7대 시맨틱 숏코드 컴포넌트 규격 및 라이브 데모

파트너사 또는 자체 콘텐츠 에디터에서 바로 생성·활용할 수 있는 7가지 시맨틱 숏코드(차트 제외) 의 전체 속성(Props) 명세와 실제 렌더링 결과입니다.

3.1 한눈에 보는 7대 숏코드 요약표

표 1: MKTG Edge 7대 시맨틱 숏코드 컴포넌트 아키텍처 요약
표 1: MKTG Edge 7대 시맨틱 숏코드 컴포넌트 아키텍처 요약
컴포넌트 태그주요 용도자동 주입되는 Schema.org / 메타클라이언트 JS
<SemanticTable />비교표, 벤치마크, 사양 매트릭스시맨틱 <table>, <caption>, scope="col/row"0 KB (Zero-JS)
<SemanticFAQ /> (또는 <FAQ />)자주 묻는 질문 아코디언 (PAA/GEO 대응)FAQPage, Question, Answer JSON-LD0 KB (Native <details>)
<SoftwareCard />프레임워크, SaaS, 마케팅 툴 소개 및 평점SoftwareApplication, Offer, AggregateRating JSON-LD0 KB (Zero-JS)
<PersonBadge />저자 바이라인, 전문가 인용구, 인터뷰이 프로필Person, Organization(worksFor), sameAs JSON-LD0 KB (Zero-JS)
<YouTubeEmbed /> (또는 <YouTube />)YouTube 영상 임베딩 및 비디오 검색 최적화VideoObject JSON-LD + oEmbed 제목 자동 조회Lite Facade (클릭 시 로드)
<LinkCard /> (또는 <BookmarkCard />)외부/내부 URL 프리뷰 북마크 박스Open Graph (og:title, og:description, og:image) 자동 추출0 KB (Zero-JS)
<Term />본문 내 전문 기술 용어 호버 툴팁 및 사전 연결용어사전 엔티티 링크 (/glossary/[slug])전역 싱글톤 공유

3.2 ① 시맨틱 테이블 (<SemanticTable />)

외곽 박스 테두리와 불필요한 배경색을 제거하고, 상단 2px 블랙 헤어라인과 행 구분선만으로 정갈한 에디토리얼 가독성을 구현한 표 컴포넌트입니다.

📌 Props 입력 규격

  • caption (또는 title) string: 표 상단에 노출되는 제목 라벨이자 검색엔진용 <caption> 텍스트
  • headers string[] (필수): 열 제목 배열 (예: ["구분", "기존 방식", "개선 방식"])
  • rows (string | number)[][] (필수): 2차원 행 데이터 배열. 각 행의 첫 번째 셀(cellIdx === 0)은 자동으로 <th scope="row">로 승격되어 접근성을 높입니다.

📌 숏코드 작성 예시

<SemanticTable
  caption="표 2: 모놀리식 CMS 대 Astro 하이브리드 헤드리스 아키텍처 비교"
  headers={["비교 항목", "전통적 모놀리식 CMS", "Astro + Headless 하이브리드"]}
  rows={[
    ["초기 응답 속도 (TTFB)", "600ms ~ 1,500ms (DB 쿼리 병목)", "50ms 미만 (글로벌 Edge 정적 서빙)"],
    ["자바스크립트 페이로드", "수백 KB (테마 및 플러그인 스크립트)", "기본 0 KB (필요한 아일랜드만 선택 로드)"],
    ["보안 공격 표면", "플러그인 및 런타임 RCE 상시 노출", "정적 HTML 서빙으로 오리진 공격 원천 차단"],
    ["구조화 데이터 관리", "서드파티 SEO 플러그인 의존", "시맨틱 컴포넌트 기반 JSON-LD 자동 컴파일"]
  ]}
/>

📌 실제 렌더링 결과

표 2: 모놀리식 CMS 대 Astro 하이브리드 헤드리스 아키텍처 비교
표 2: 모놀리식 CMS 대 Astro 하이브리드 헤드리스 아키텍처 비교
비교 항목전통적 모놀리식 CMSAstro + Headless 하이브리드
초기 응답 속도 (TTFB)600ms ~ 1,500ms (DB 쿼리 병목)50ms 미만 (글로벌 Edge 정적 서빙)
자바스크립트 페이로드수백 KB (테마 및 플러그인 스크립트)기본 0 KB (필요한 아일랜드만 선택 로드)
보안 공격 표면플러그인 및 런타임 RCE 상시 노출정적 HTML 서빙으로 오리진 공격 원천 차단
구조화 데이터 관리서드파티 SEO 플러그인 의존시맨틱 컴포넌트 기반 JSON-LD 자동 컴파일

3.3 ② 시맨틱 FAQ 아코디언 (<SemanticFAQ /> 또는 <FAQ />)

구글 검색의 FAQ 리치 결과, PAA(People Also Ask) 박스, 그리고 ChatGPT·Perplexity 등 생성형 AI 답변 엔진(GEO) 이 질문-답변 쌍을 즉각 추출할 수 있도록 FAQPage Schema.org JSON-LD를 자동으로 생성·주입하는 아코디언 컴포넌트입니다.

  • 네이티브 HTML5 <details> 및 <summary> 요소를 사용하여 자바스크립트 0KB 로 동작하며, 접혀 있는 답변도 브라우저 페이지 내 검색(Ctrl+F) 시 자동으로 펼쳐집니다.

📌 Props 입력 규격

  • title string (선택, 기본값: "자주 묻는 질문 (FAQ)"): FAQ 섹션 상단 라벨
  • subtitle string (선택): 상단 보조 설명 텍스트
  • items Array<{ question: string; answer: string; open?: boolean }> (필수): 질문(question)과 답변(answer) 객체 배열 (답변 내 간단한 HTML 태그 지원)
  • exclusive boolean (선택, 기본값: false): true로 설정하면 HTML5 표준 <details name="..."> 속성이 활성화되어 한 번에 하나의 질문만 열리는 배타적 아코디언으로 작동합니다.
  • defaultOpenFirst boolean (선택, 기본값: true): 첫 번째 질문을 기본으로 펼쳐둘지 여부

📌 숏코드 작성 예시

<SemanticFAQ
  title="자주 묻는 질문 (FAQ) — 시맨틱 숏코드 연동"
  subtitle="파트너 에디터 및 외부 CMS 연동 시 자주 문의하는 핵심 기술 사양"
  exclusive={true}
  items={[
    {
      question: "<SemanticFAQ />와 <FAQ /> 중 어떤 태그 이름을 써야 하나요?",
      answer: "두 태그 모두 동일하게 작동합니다. 명시적인 이름을 선호하면 <code>&lt;SemanticFAQ /&gt;</code>를, 빠르고 간결한 숏코드 작성을 원하면 <code>&lt;FAQ /&gt;</code>를 사용하시면 됩니다."
    },
    {
      question: "FAQPage JSON-LD 스키마를 별도로 페이지 상단에 또 작성해야 하나요?",
      answer: "아닙니다. 컴포넌트에 전달된 <code>items</code> 배열을 기반으로 HTML 태그를 정제한 뒤, 표준 <code>FAQPage</code> JSON-LD 스크립트를 컴포넌트가 스스로 생성하여 주입합니다."
    },
    {
      question: "아코디언이 닫혀 있으면 검색엔진 크롤러가 답변 내용을 무시하지 않나요?",
      answer: "표준 HTML5 <code>&lt;details&gt;</code> 태그 내부의 콘텐츠는 초기 DOM에 완전히 존재하므로 Googlebot, Naver Yeti 및 AI 크롤러가 100% 정상 수집하며, 사용자의 페이지 내 텍스트 검색(Find in page) 시에도 브라우저가 자동으로 해당 항목을 열어줍니다."
    }
  ]}
/>

📌 실제 렌더링 결과

자주 묻는 질문 (FAQ) — 시맨틱 숏코드 연동

파트너 에디터 및 외부 CMS 연동 시 자주 문의하는 핵심 기술 사양

Q1.<SemanticFAQ />와 <FAQ /> 중 어떤 태그 이름을 써야 하나요?
두 태그 모두 동일하게 작동합니다. 명시적인 이름을 선호하면 <SemanticFAQ />를, 빠르고 간결한 숏코드 작성을 원하면 <FAQ />를 사용하시면 됩니다.
Q2.FAQPage JSON-LD 스키마를 별도로 페이지 상단에 또 작성해야 하나요?
아닙니다. 컴포넌트에 전달된 items 배열을 기반으로 HTML 태그를 정제한 뒤, 표준 FAQPage JSON-LD 스크립트를 컴포넌트가 스스로 생성하여 주입합니다.
Q3.아코디언이 닫혀 있으면 검색엔진 크롤러가 답변 내용을 무시하지 않나요?
표준 HTML5 <details> 태그 내부의 콘텐츠는 초기 DOM에 완전히 존재하므로 Googlebot, Naver Yeti 및 AI 크롤러가 100% 정상 수집하며, 사용자의 페이지 내 텍스트 검색(Find in page) 시에도 브라우저가 자동으로 해당 항목을 열어줍니다.

3.4 ③ 소프트웨어 애플리케이션 카드 (<SoftwareCard />)

아티클에서 다루는 프레임워크, SaaS, SEO 분석 도구, 오픈소스 라이브러리의 핵심 제원과 평점을 요약해 보여주는 제품 카드입니다. 호출 시 SoftwareApplication, Offer, AggregateRating JSON-LD가 자동 삽입됩니다.

📌 Props 입력 규격

  • name string (필수): 소프트웨어/솔루션 명칭 (예: "Astro")
  • applicationCategory (또는 category) string (선택, 기본값: "Web Development Application"): 제품 분류 카테고리
  • operatingSystem string (선택, 기본값: "Web / Cross-platform"): 구동 환경 및 OS
  • price string (선택, 기본값: "Free"): 가격 정책 표기 (내부 숫자를 추출해 Offer.price 스키마에 자동 반영)
  • rating number | string (선택): 5점 만점 기준 평점 (예: "4.9" 입력 시 우측 상단 별점 배지 및 AggregateRating 스키마 활성화)
  • description string (선택): 제품 핵심 요약 설명
  • url string (선택): 공식 웹사이트 링크
  • logo string (선택): 로고 이미지 경로 (미입력 시 기본 아이콘 폴백)

📌 숏코드 작성 예시

<SoftwareCard
  name="Astro"
  applicationCategory="Web Framework"
  operatingSystem="Node.js / Edge Runtime"
  price="Free (MIT Open Source)"
  rating="4.9"
  logo="/images/logos/astro-icon.svg"
  description="콘텐츠 중심 웹사이트를 위한 아일랜드 아키텍처(Islands Architecture) 기반 초고속 웹 프레임워크"
  url="https://astro.build"
/>

📌 실제 렌더링 결과

Astro

Astro

Web Framework

★ 4.9 / 5.0

콘텐츠 중심 웹사이트를 위한 아일랜드 아키텍처(Islands Architecture) 기반 초고속 웹 프레임워크

OS 환경Node.js / Edge Runtime
가격 정책Free (MIT Open Source)

3.5 ④ 저자 및 전문가 배지 (<PersonBadge />)

아티클 본문 중간이나 하단에서 집필진, 감수자, 또는 인용된 업계 전문가의 신원(E-E-A-T)을 명확히 밝힐 때 사용하는 컴포넌트입니다. Person 스키마와 소속 기관(worksFor), 소셜 프로필(sameAs) JSON-LD를 자동으로 생성합니다.

📌 Props 입력 규격

  • name string (필수): 인물 성명 (대표 저자인 "정상원" 입력 시 src/lib/author.ts의 마스터 엔티티 @id 및 기본 프로필과 자동 연동)
  • jobTitle string (선택): 직책/직함 (예: "Head of Marketing Engineering")
  • affiliation string (선택): 소속 조직/기업명 (예: "MKTG Edge")
  • avatar string (선택): 프로필 이미지 경로 (미입력 시 이름 첫 글자 이니셜 아바타 자동 생성)
  • url string (선택): 저자 소개 또는 공식 프로필 URL
  • sameAs string | string[] (선택): LinkedIn, GitHub 등 동일인 검증용 외부 소셜 URL 배열

📌 숏코드 작성 예시

<PersonBadge
  name="정상원"
  jobTitle="Head of Marketing Engineering"
  affiliation="MKTG Edge"
  avatar="/images/authors/sangwon.jpeg"
  url="https://mktg.kr/author/sangwon"
  sameAs={["https://www.linkedin.com/in/jeongsangwon/"]}
/>

📌 실제 렌더링 결과


3.6 ⑤ 인라인 기술 용어 툴팁 (<Term /> / <GlossaryTooltip />)

mktg.kr의 용어사전(src/data/glossary.json, 235개 전문 용어) 데이터베이스와 실시간으로 바인딩되는 인라인 숏코드입니다. 마우스를 올리거나 모바일에서 탭하면 용어의 정의와 비즈니스 영향이 담긴 팝오버 카드가 나타납니다.

📌 Props 입력 규격

  • name (또는 term) string: 용어사전에 등록된 한글 표준 용어명 또는 영문 약어 (예: "SSG", "Core Web Vitals")
  • slug string (선택): 특정 용어 슬러그를 직접 지정할 때 사용 (예: "headless-content-management-system")
  • children (내부 텍스트) (선택): 태그 사이에 원하는 문구를 넣으면 해당 문구에 툴팁이 걸리고, 비워두면(<Term name="SSG" />) name 값이 그대로 출력됩니다.

📌 숏코드 작성 예시

모던 웹 퍼블리싱에서는 <Term name="SSG">정적 사이트 생성(SSG)</Term>과 <Term slug="headless-content-management-system">헤드리스 CMS</Term>를 결합하여 보안과 성능을 동시에 달성합니다.

📌 실제 렌더링 결과

모던 웹 퍼블리싱에서는 정적 사이트 생성(SSG)과 헤드리스 CMS를 결합하여 보안과 성능을 동시에 달성합니다.


3.7 ⑥ YouTube 비디오 임베드 (<YouTubeEmbed /> 또는 <YouTube />)

YouTube 영상 URL만 넣으면 영상 ID를 자동으로 추출하여 16:9 반응형 플레이어로 임베딩하고, 구글 동영상 검색 리치 결과를 위한 VideoObject Schema.org JSON-LD를 자동으로 생성·주입하는 컴포넌트입니다.

  • oEmbed 메타데이터 자동 조회: title이나 author(채널명)를 생략해도 빌드/SSR 시점에 YouTube 공식 oEmbed API를 통해 영상 제목과 채널명을 자동으로 가져옵니다.
  • Lite Facade (Core Web Vitals 보호): 초기 로드 시 무거운 YouTube <iframe> 스크립트 대신 고화질 썸네일(maxresdefault.jpg)과 재생 버튼을 먼저 렌더링하고, 사용자가 클릭하는 순간 실제 플레이어를 로드하여 페이지 속도(LCP/INP/TBT) 저하를 원천 차단합니다.

📌 Props 입력 규격

  • url string (필수, 또는 id 지정): YouTube 영상 주소 (watch?v=..., youtu.be/..., shorts/..., embed/... 모두 지원)
  • title string (선택): 영상 제목 (미입력 시 YouTube oEmbed로 자동 조회)
  • description string (선택): 영상 설명 (VideoObject 스키마의 description에 반영)
  • uploadDate string (선택): 업로드 일자 (YYYY-MM-DD, VideoObject.uploadDate에 반영)
  • duration string (선택): ISO 8601 재생 시간 (예: "PT3M45S")
  • caption string (선택): 영상 하단 <figcaption> 캡션 문구
  • start number (선택): 재생 시작 지점(초 단위, 예: 30)

📌 숏코드 작성 예시

{/* 1) 가장 간단한 사용법: URL만 입력 (제목/채널명/썸네일/스키마 자동 생성) */}
<YouTube url="https://www.youtube.com/watch?v=dsTXcSeAZq8" />

{/* 2) 상세 스키마 메타데이터 직접 지정 */}
<YouTubeEmbed
  url="https://www.youtube.com/watch?v=dsTXcSeAZq8"
  title="Astro in 100 Seconds — 초고속 웹 프레임워크 개요"
  description="Fireship 채널의 Astro 웹 프레임워크 및 아일랜드 아키텍처 핵심 개념 100초 요약 영상"
  uploadDate="2022-08-09"
  duration="PT2M32S"
  caption="영상 1: Astro의 아일랜드 아키텍처와 Zero-JS 작동 원리 (출처: Fireship)"
/>

📌 실제 렌더링 결과

영상 1: Astro의 아일랜드 아키텍처와 Zero-JS 작동 원리 (출처: Fireship)

3.8 ⑦ 외부 URL 프리뷰 북마크 카드 (<LinkCard /> 또는 <BookmarkCard />)

외부 참고 문헌, 공식 문서, 뉴스 기사, 혹은 사이트 내부 아티클의 URL을 전달하면 페이지 썸네일(og:image), 타이틀(og:title), 요약 설명(og:description), 도메인 파비콘을 추출하여 노션(Notion) 스타일의 북마크 박스로 노출하는 컴포넌트입니다.

  • Open Graph 자동 스크래핑: url 속성 하나만 입력해도 빌드/SSR 시점에 대상 웹페이지의 <head> 메타태그를 파싱하여 제목·설명·대표 이미지를 자동으로 채워줍니다.
  • 수동 오버라이드 지원: 보안 방화벽으로 봇 접근을 막는 외부 사이트이거나, 에디터가 원하는 문구로 다듬고 싶을 때는 title, description, image 속성을 직접 지정할 수 있습니다.

📌 Props 입력 규격

  • url string (필수): 연결할 대상 웹페이지 주소
  • title string (선택): 카드 제목 (미입력 시 대상 URL의 og:title 또는 <title> 자동 추출)
  • description string (선택): 요약 설명 2줄 노출 (미입력 시 대상 URL의 og:description 자동 추출)
  • image string (선택): 우측 썸네일 이미지 URL (미입력 시 대상 URL의 og:image 자동 추출)
  • siteName string (선택): 출처 사이트명 (미입력 시 og:site_name 또는 도메인 호스트명 자동 표기)

📌 숏코드 작성 예시

{/* 1) URL만 입력하여 Open Graph 메타데이터 자동 추출 */}
<LinkCard url="https://astro.build/blog/2023-web-framework-performance-report/" />

{/* 2) 타이틀·설명·썸네일을 명시적으로 지정하는 방식 */}
<LinkCard
  url="https://docs.astro.build/en/concepts/islands/"
  title="Astro Islands Architecture — 공식 아키텍처 문서"
  description="정적 HTML 페이지 안에서 인터랙티브한 UI 컴포넌트만 독립된 섬(Island)처럼 병렬 하이드레이션하는 Astro의 핵심 설계 철학을 설명합니다."
  siteName="Astro Docs"
  image="https://astro.build/_astro/social.FlPxeRsG.jpg"
/>

📌 실제 렌더링 결과


3.9 [보너스] 마크다운 이미지 <figure> 캡션 자동 변환 규격

별도의 JSX 태그를 쓰지 않고 표준 마크다운 이미지 문법 뒤에 큰따옴표("캡션 내용")만 붙이면, 빌드 파이프라인(rehype-figure 및 커스텀 렌더러)이 자동으로 시맨틱 <figure class="article-image">와 <figcaption>으로 변환합니다.

![대체 텍스트(alt) 입력](/images/blog/sample.png "그림 1: 이미지 하단에 중앙 정렬로 표시될 시맨틱 캡션")

4. 파트너 에디터(CMS) 노코드 숏코드 빌더 연동 명세

파트너사 또는 자체 제작 콘텐츠 에디터에서 개발 지식이 없는 작성자도 버튼 클릭과 폼 입력만으로 위 7가지 숏코드를 생성할 수 있도록, 에디터에 그대로 내장할 수 있는 TypeScript 직렬화(Serializer) 함수 모음입니다.

/**
 * MKTG Edge 7대 시맨틱 숏코드 자동 생성기 (파트너 에디터 내장용)
 */

// 1. <SemanticTable /> 생성기
export function buildSemanticTableShortcode(params: {
  caption: string;
  headers: string[];
  rows: string[][];
}): string {
  return [
    `<SemanticTable`,
    `  caption=${JSON.stringify(params.caption.trim())}`,
    `  headers={${JSON.stringify(params.headers.map((h) => h.trim()))}}`,
    `  rows={${JSON.stringify(params.rows, null, 2).replace(/\n/g, '\n  ')}}`,
    `/>`,
  ].join('\n');
}

// 2. <SemanticFAQ /> 생성기
export function buildSemanticFAQShortcode(params: {
  title?: string;
  subtitle?: string;
  exclusive?: boolean;
  items: Array<{ question: string; answer: string }>;
}): string {
  const lines = [`<SemanticFAQ`];
  if (params.title?.trim()) lines.push(`  title=${JSON.stringify(params.title.trim())}`);
  if (params.subtitle?.trim()) lines.push(`  subtitle=${JSON.stringify(params.subtitle.trim())}`);
  if (params.exclusive) lines.push(`  exclusive={true}`);

  const itemsJson = JSON.stringify(
    params.items.map((it) => ({
      question: it.question.trim(),
      answer: it.answer.trim(),
    })),
    null,
    2
  ).replace(/\n/g, '\n  ');

  lines.push(`  items={${itemsJson}}`);
  lines.push(`/>`);
  return lines.join('\n');
}

// 3. <SoftwareCard /> 생성기
export function buildSoftwareCardShortcode(params: {
  name: string;
  applicationCategory?: string;
  operatingSystem?: string;
  price?: string;
  rating?: string;
  logo?: string;
  description?: string;
  url?: string;
}): string {
  const lines = [`<SoftwareCard`, `  name=${JSON.stringify(params.name.trim())}`];
  if (params.applicationCategory?.trim()) {
    lines.push(`  applicationCategory=${JSON.stringify(params.applicationCategory.trim())}`);
  }
  if (params.operatingSystem?.trim()) {
    lines.push(`  operatingSystem=${JSON.stringify(params.operatingSystem.trim())}`);
  }
  if (params.price?.trim()) lines.push(`  price=${JSON.stringify(params.price.trim())}`);
  if (params.rating?.trim()) lines.push(`  rating=${JSON.stringify(params.rating.trim())}`);
  if (params.logo?.trim()) lines.push(`  logo=${JSON.stringify(params.logo.trim())}`);
  if (params.description?.trim()) {
    lines.push(`  description=${JSON.stringify(params.description.trim())}`);
  }
  if (params.url?.trim()) lines.push(`  url=${JSON.stringify(params.url.trim())}`);
  lines.push(`/>`);
  return lines.join('\n');
}

// 4. <PersonBadge /> 생성기
export function buildPersonBadgeShortcode(params: {
  name: string;
  jobTitle?: string;
  affiliation?: string;
  avatar?: string;
  url?: string;
  sameAs?: string[];
}): string {
  const lines = [`<PersonBadge`, `  name=${JSON.stringify(params.name.trim())}`];
  if (params.jobTitle?.trim()) lines.push(`  jobTitle=${JSON.stringify(params.jobTitle.trim())}`);
  if (params.affiliation?.trim()) {
    lines.push(`  affiliation=${JSON.stringify(params.affiliation.trim())}`);
  }
  if (params.avatar?.trim()) lines.push(`  avatar=${JSON.stringify(params.avatar.trim())}`);
  if (params.url?.trim()) lines.push(`  url=${JSON.stringify(params.url.trim())}`);
  if (params.sameAs && params.sameAs.length > 0) {
    lines.push(`  sameAs={${JSON.stringify(params.sameAs)}}`);
  }
  lines.push(`/>`);
  return lines.join('\n');
}

// 5. <Term /> 인라인 툴팁 생성기
export function buildTermShortcode(params: {
  name?: string;
  slug?: string;
  displayText?: string;
}): string {
  const attr = params.slug
    ? `slug=${JSON.stringify(params.slug.trim())}`
    : `name=${JSON.stringify((params.name || '').trim())}`;
  return params.displayText?.trim()
    ? `<Term ${attr}>${params.displayText.trim()}</Term>`
    : `<Term ${attr} />`;
}

// 6. <YouTubeEmbed /> (또는 <YouTube />) 생성기
export function buildYouTubeShortcode(params: {
  url: string;
  title?: string;
  description?: string;
  uploadDate?: string;
  duration?: string;
  caption?: string;
  start?: number;
}): string {
  const lines = [`<YouTubeEmbed`, `  url=${JSON.stringify(params.url.trim())}`];
  if (params.title?.trim()) lines.push(`  title=${JSON.stringify(params.title.trim())}`);
  if (params.description?.trim()) {
    lines.push(`  description=${JSON.stringify(params.description.trim())}`);
  }
  if (params.uploadDate?.trim()) {
    lines.push(`  uploadDate=${JSON.stringify(params.uploadDate.trim())}`);
  }
  if (params.duration?.trim()) lines.push(`  duration=${JSON.stringify(params.duration.trim())}`);
  if (params.caption?.trim()) lines.push(`  caption=${JSON.stringify(params.caption.trim())}`);
  if (params.start && Number(params.start) > 0) lines.push(`  start={${Number(params.start)}}`);
  lines.push(`/>`);
  return lines.join('\n');
}

// 7. <LinkCard /> 외부/내부 URL 북마크 카드 생성기
export function buildLinkCardShortcode(params: {
  url: string;
  title?: string;
  description?: string;
  image?: string;
  siteName?: string;
}): string {
  const lines = [`<LinkCard`, `  url=${JSON.stringify(params.url.trim())}`];
  if (params.title?.trim()) lines.push(`  title=${JSON.stringify(params.title.trim())}`);
  if (params.description?.trim()) {
    lines.push(`  description=${JSON.stringify(params.description.trim())}`);
  }
  if (params.image?.trim()) lines.push(`  image=${JSON.stringify(params.image.trim())}`);
  if (params.siteName?.trim()) lines.push(`  siteName=${JSON.stringify(params.siteName.trim())}`);
  lines.push(`/>`);
  return lines.join('\n');
}

5. 시맨틱 컴포넌트 신규 추가 및 확장 절차 (개발자용 체크리스트)

향후 새로운 종류의 시맨틱 숏코드 컴포넌트(예: <HowToSteps />, <CalloutBox /> 등)를 mktg.kr에 추가할 때는 아래 4단계 표준 절차를 따릅니다:

  1. 컴포넌트 생성 (src/components/semantic/[ComponentName].astro):
    • 최상위 컨테이너에 not-prose 클래스를 부여하여 Tailwind Typography(.prose) 기본 스타일과의 충돌을 방지합니다.
    • 해당하는 Schema.org 타입이 있다면 컴포넌트 상단에서 <script type="application/ld+json" set:html={JSON.stringify(schema)} />를 출력하도록 설계합니다.
  2. 배럴 파일 등록 (src/components/semantic/index.ts):
    • export { default as NewComponent } from './NewComponent.astro';를 추가합니다.
  3. 전역 MDX 주입 등록 (src/pages/blog/[slug].astro, src/pages/docs/[...slug].astro):
    • 두 라우트 파일의 mdxComponents 객체에 새 컴포넌트를 추가하여, 작성자가 import 없이 본문에서 즉시 사용할 수 있게 만듭니다.
  4. 가이드 문서 업데이트:
    • 본 문서(/docs/semantic-shortcode-components-guide)와 /docs/content-authoring-guide에 Props 명세와 라이브 예시를 추가합니다.