Open Graph 란?

히스토리

웹사이트를 만들다 보면 결국 사람들이 내 사이트를 공유하길 바라게 된다. 그런데 막상 카카오톡이나 페이스북에 링크를 붙여넣으면 밋밋한 URL만 덩그러니 나오는 경우가 있다. 반면 어떤 사이트들은 멋진 썸네일 이미지에 제목, 설명까지 깔끔하게 나온다. 이 차이를 만드는 게 바로 Open Graph Protocol이다.

Open Graph는 Facebook이 2010년에 만든 메타데이터 프로토콜인데, 지금은 사실상 표준처럼 쓰인다. 카카오톡, 트위터, 링크드인, 슬랙 등 거의 모든 플랫폼이 이걸 파싱해서 링크 미리보기를 만든다. 오늘은 이 Open Graph가 어떻게 동작하는지, 그리고 실제로 구현하는 방법을 직접 테스트하며 알아보려고 한다.

Open Graph의 구조

Open Graph는 기본적으로 HTML <head> 태그 안에 <meta> 태그로 정의된다. 크롤러가 페이지를 읽을 때 이 메타 태그들을 파싱해서 미리보기 카드를 만드는 방식이다.

<head>
  <meta property="og:title" content="SQLAlchemy 연관관계 로딩 전략" />
  <meta property="og:description" content="selectinload, joinedload, subqueryload ORM을 사용하다보면..." />
  <meta property="og:image" content="https://example.com/thumbnail.png" />
  <meta property="og:url" content="https://example.com/wiki/sqlalchemy" />
  <meta property="og:type" content="article" />
</head>

가장 중요한 4가지 속성을 먼저 보자:

og:title

페이지 제목이다. SNS에서 링크 미리보기의 큰 텍스트로 나온다. <title> 태그와는 별개로 설정할 수 있다.

og:description

페이지 설명. 보통 2-3줄 정도로 보여지며, 160자 정도가 적당하다. 이게 없으면 크롤러가 페이지 본문에서 임의로 추출하는데, 그럼 이상한 텍스트가 나올 수 있다.

og:image

썸네일 이미지 URL. 절대 경로여야 한다. 상대 경로를 쓰면 크롤러가 못 찾는다. 권장 크기는 1200x630px이다.

og:url

페이지의 정규 URL. 쿼리 파라미터가 붙어도 같은 페이지라면 동일한 URL을 명시해야 한다.

실제로 테스트해보기

일단 간단한 HTML 파일을 하나 만들어보자.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>테스트 페이지</title>

  <!-- Open Graph -->
  <meta property="og:type" content="article">
  <meta property="og:title" content="PostgreSQL Shared Buffer 깊게 파보기">
  <meta property="og:description" content="BufferTag와 Hash Table의 동작 원리를 직접 쿼리를 날려가며 알아봅니다.">
  <meta property="og:image" content="https://my-wiki.com/images/postgres-thumbnail.png">
  <meta property="og:url" content="https://my-wiki.com/wiki/postgres-buffer">
</head>
<body>
  <h1>PostgreSQL Shared Buffer 깊게 파보기</h1>
  <p>웹 서버를 운영하거나 데이터베이스를 운용하게 되는 경우...</p>
</body>
</html>

이제 이걸 배포하고 나면, 페이스북이나 카카오톡에서 어떻게 보일지 미리 확인할 수 있다. 페이스북은 공식 디버거 도구를 제공한다:

https://developers.facebook.com/tools/debug/

여기에 URL을 넣으면 페이스북 크롤러가 어떻게 파싱하는지 바로 확인할 수 있다. 만약 이미지가 안 보인다면 아래를 체크해보자:

  1. 이미지 URL이 https인가?
  2. 이미지 파일이 실제로 접근 가능한가?
  3. 이미지 크기가 너무 작지 않은가? (최소 200x200px)

트위터는 다르다: Twitter Card

트위터는 Open Graph도 읽지만, 자체 프로토콜인 Twitter Card를 선호한다. 다행히 문법은 거의 똑같다:

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="PostgreSQL Shared Buffer 깊게 파보기">
<meta name="twitter:description" content="BufferTag와 Hash Table의 동작 원리를...">
<meta name="twitter:image" content="https://my-wiki.com/images/postgres-thumbnail.png">

twitter:card는 카드 타입을 지정한다:
- summary: 작은 정사각형 이미지
- summary_large_image: 큰 직사각형 이미지 (추천)

트위터도 검증 도구가 있다:

https://cards-dev.twitter.com/validator

동적으로 메타 태그 생성하기

실제 웹사이트에서는 페이지마다 다른 메타 태그를 보여줘야 한다. 내 위키 사이트를 예로 들어보자.

FastAPI + Jinja2 예시

@app.get("/wiki/{article_id}")
async def view_article(article_id: str):
    article = get_article(article_id)

    # 본문에서 description 추출 (마크다운 제거)
    import re
    content = article.content
    content = re.sub(r'```[\s\S]*?```', '', content)  # 코드 블록 제거
    content = re.sub(r'`[^`]+`', '', content)         # 인라인 코드 제거
    content = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', content)  # 링크
    content = re.sub(r'^#{1,6}\s+', '', content, flags=re.MULTILINE)  # 헤더

    description = content.strip()[:160] + "..."

    return templates.TemplateResponse("wiki.html", {
        "article": article,
        "description": description
    })

템플릿에서는 이렇게 쓴다:

<head>
  <meta property="og:type" content="article">
  <meta property="og:title" content="{{ article.title }}">
  <meta property="og:description" content="{{ description }}">
  <meta property="og:url" content="{{ request.url }}">
  <meta property="og:image" content="{{ request.base_url }}static/og-image.png">
</head>

실제 크롤러 동작 확인하기

크롤러가 실제로 뭘 가져가는지 로그로 확인해볼 수 있다. nginx 로그를 보면:

tail -f /var/log/nginx/access.log | grep -i "bot\|crawler"

페이스북 크롤러는 User-Agent가 이렇다:

facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)

카카오톡은:

kakaotalk-scrap/1.0

슬랙은:

Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)

캐싱 문제 해결하기

메타 태그를 수정했는데도 SNS에서 예전 내용이 계속 보인다면? 크롤러가 캐싱을 하기 때문이다.

페이스북 캐시 갱신

curl -X POST \
  -F "id=https://my-wiki.com/wiki/postgres-buffer" \
  -F "scrape=true" \
  "https://graph.facebook.com/"

또는 페이스북 디버거 도구1 에서 "Scrape Again" 버튼을 누르면 된다.

카카오톡 캐시 갱신

카카오톡은 공식 API가 없어서 시간이 지나야 한다. 보통 2주 정도 캐싱하는 것 같다. 급하면 URL에 쿼리 파라미터를 붙이는 편법이 있다:

https://my-wiki.com/wiki/postgres-buffer?v=2

실전 팁

1. 이미지는 CDN에 올려라

Open Graph 이미지는 크롤러가 다운로드할 수 있어야 한다. 로컬 개발 환경(localhost)에 있으면 당연히 안 된다. 꼭 공개 URL을 쓰자.

2. 크기 최적화

1200x630px PNG 파일은 용량이 크다. WebP로 변환하고 압축하면 로딩이 빨라진다:

cwebp -q 80 thumbnail.png -o thumbnail.webp

3. 기본 이미지 준비

특정 페이지에 이미지가 없을 때를 대비해 사이트 로고나 기본 썸네일을 준비해두자:

<meta property="og:image" content="{% block og_image %}{{ request.base_url }}static/default-og.png{% endblock %}">

4. og:type 활용

article만 있는 게 아니다:
- website: 일반 웹페이지
- article: 블로그 글, 위키 문서
- video.movie: 영화
- music.song: 음악

타입에 따라 추가 메타 태그를 쓸 수 있다:

<meta property="og:type" content="article">
<meta property="article:published_time" content="2025-01-15T10:00:00+09:00">
<meta property="article:author" content="roach">
<meta property="article:section" content="Database">
<meta property="article:tag" content="PostgreSQL">
<meta property="article:tag" content="Buffer">

결론

Open Graph는 단순히 메타 태그 몇 개 추가하는 것 같지만, 제대로 설정하면 SNS 유입이 확 늘어난다. 특히 개발 블로그나 기술 문서는 링크 공유가 주요 유입 경로니까 꼭 챙기자.

핵심 체크리스트:
- ✅ og:title, og:description, og:image, og:url 필수 4종
- ✅ 이미지는 1200x630px, 절대 경로, https
- ✅ description은 160자 내외
- ✅ 페이스북/트위터 디버거로 검증
- ✅ 동적 페이지는 서버에서 메타 태그 생성

한 번 설정해두면 모든 SNS에서 자동으로 예쁜 미리보기가 나온다.


  1. 페이스북 공유 디버거 로 캐싱된 오픈그래프를 다시 재 스크랩 시킬수있다. 

🔗 연결된 문서

💬 댓글 0

🕸️ 문서 관계도