MSBuild 2025에서 있었던 NLWeb 발표를 뒤늦게 봤습니다. MS CTO Kevin Scott 님께서 키노트에서 "Agentic Web"이라는 주제로 발표하셨지만, 구체적으로 어떤 모습일지 이해하기는 어려웠습니다. NLWeb 발표에서는 그 주제에 대해 더 구체적인 설명이 있었습니다.

Watch on YouTube

RDF, RSS, Schema.org 프로젝트를 이끌었던 Ramanathan V. Guha님과 구현을 맡으신 Jennifer Marsman 님이 발표하신 "The Making of NLWeb | BRK240" 영상입니다.

https://en.wikipedia.org/wiki/Ramanathan_V._Guha

1시간 분량의 발표를 아주 간략히 요약해 봅니다. 다만 RSS, Schema.org 를 지렛대로 활용하자는 접근을 하는 프로젝트이니 만큼 Schema.org 를 좀 더 설명해 보겠습니다.

Schema.org 표준

많은 기업이 이미 Schema.org 표준을 따르는 구조화된 데이터를 자사 웹사이트에 제공하고 있습니다. 이를 통해 검색 엔진 최적화(SEO)를 위해 검색 엔진에 더 풍부한 정보를 제공합니다.

“김치찌개 레시피”, “책 삼체”로 구글에서 검색해 보면 검색결과 1페이지에 나오는 웹페이지들 대부분이 Schema.org의 표준에 맞는 ld+json 데이터를 HTML에 포함하고 있습니다. 벌써 이런 시대가 왔군요.

김치찌개 레시피 검색결과의 1페이지에 web page 로 네이버블로그, 유투브, 나무위키, 컬리, 만개의레시피, 우리의식탁 사이트가 보입니다. 네이버, 유투브를 제외하면 모두 Schema.org 데이터를 제공합니다. 세어보면 1페이지에 표시되는 웹페이지의 약 60% 라고 볼 수 있습니다. 게다가 "Recipes" Rich Snippet 섹션에 노출될 수도 있습니다.

김치찌개 검색 결과 페이지

책 삼체 검색결과 1페이지에서 나오는 교보문고, Yes24, 브런치 스토리, 알라딘의 페이지에서도 Schema.org ld+json 데이터가 있습니다. 더해서 교보문고의 페이지의 가격, 평점 정보는 Rich Snippet 으로 검색결과 페이지에 직접 구글이 노출해 주고 있습니다.

책 삼체 검색 결과 페이지

마켓컬리의 페이지에서 제공하는 Schema.org ld+json 의 모습은 이렇습니다. Recipe 타입으로 조리시간, 준비시간, 준비물, 조리과정 이 구조화된 형태로 machine-readable 하게 들어 있는 것을 볼 수 있습니다.

{
  "@context": "https://schema.org/",
  "@type": "Recipe",
  "name": "얼큰한 백종원 돼지고기 김치찌개 레시피 3인분 40분 완성 평점 4.7점(35)",
  "image": [
    "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-0.jpg"
  ],
  "author": {
    "@type": "Person",
    "name": "레지나"
  },
  "datePublished": "2024-10-14T14:00:00+09:00",
  "description": "이번 주말엔 얼큰한 돼지고기 김치찌개를 끓여보았어요. 스팸이나 참치도 좋지만, 신김치와 돼지고기의 조합은 언제나 진리입니다. 평점 4.7점(35)의 평가가 있어요. 14가지 재료로 3인분을 40분 안에 완성! 7단계의 쉬움 난이도 레시피입니다. 1인분당 19,120원 정도의 재료비가 들어요.",
  "recipeCuisine": [
    "한국"
  ],
  "prepTime": "PT0H10M",
  "cookTime": "PT0H30M",
  "totalTime": "PT0H40M",
  "keywords": [
    "백종원",
    "김치찌개",
    "돼지고기 김치찌개",
    "돼지고기",
    "레시피"
  ],
  "recipeYield": "3",
  "recipeCategory": [
    "메인메뉴",
    "식사메뉴"
  ],
  "nutrition": {
    "@type": "NutritionInformation",
    "calories": "350kcal"
  },
  "recipeIngredient": [
    "돼지고기 앞다리살",
    "신김치",
    "물(또는 멸치육수)",
    "양파",
    "대파",
    "청양고추",
    "두부"
  ],
  "recipeInstructions": [
    {
      "@type": "HowToSection",
      "name": "재료 준비하기",
      "itemListElement": [
        {
          "@type": "HowToStep",
          "text": "돼지고기 앞다리살을 먹기 좋게 썰어 신김치 3컵, 대파, 청양고추, 양파 1/4개, 두부 반모와 함께 준비해 돼지고기 김치찌개를 끓입니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-1.jpg",
          "name": "1. 돼지고기 앞다리살을 먹기 좋게 썰어 신김치 3컵, 대파, 청양고추, 양파 1/4개, 두부 반모와 함께 준비해 돼지고기 김치찌개를 끓입니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-1"
        }
      ]
    },
    {
      "@type": "HowToSection",
      "name": "조리하기",
      "itemListElement": [
        {
          "@type": "HowToStep",
          "text": "달군 냄비에 돼지고기, 물 3컵, 새우젓 또는 참치액젓 1큰술을 넣고 센불에서 끓이다가 중불로 줄여 10~15분 이상 끓여 부드럽고 깊은 맛을 냅니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-2.jpg",
          "name": "2. 달군 냄비에 돼지고기, 물 3컵, 새우젓 또는 참치액젓 1큰술을 넣고 센불에서 끓이다가 중불로 줄여 10~15분 이상 끓여 부드럽고 깊은 맛을 냅니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-2"
        },
        {
          "@type": "HowToStep",
          "text": "신김치와 고춧가루 1.5큰술을 추가하고, 물이 줄면 보충하며 오래 끓여 풍미를 완성합니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-3.jpg",
          "name": "3. 신김치와 고춧가루 1.5큰술을 추가하고, 물이 줄면 보충하며 오래 끓여 풍미를 완성합니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-3"
        },
        {
          "@type": "HowToStep",
          "text": "물이 많으면 중강불에서 졸여 농도를 맞추고, 양파를 넣어 달큰한 맛을 더하며 일반 고춧가루만 사용해도 충분한 맛을 냅니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-4.jpg",
          "name": "4. 물이 많으면 중강불에서 졸여 농도를 맞추고, 양파를 넣어 달큰한 맛을 더하며 일반 고춧가루만 사용해도 충분한 맛을 냅니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-4"
        }
      ]
    },
    {
      "@type": "HowToSection",
      "name": "간 맞추기",
      "itemListElement": [
        {
          "@type": "HowToStep",
          "text": "묵은지의 새콤함을 잡기 위해 설탕 2/3큰술과 국간장 1큰술을 추가하고, 다진 마늘 1큰술과 두부를 넣어 한소끔 끓인 후 대파를 넣어 마무리합니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-5.jpg",
          "name": "5. 묵은지의 새콤함을 잡기 위해 설탕 2/3큰술과 국간장 1큰술을 추가하고, 다진 마늘 1큰술과 두부를 넣어 한소끔 끓인 후 대파를 넣어 마무리합니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-5"
        }
      ]
    },
    {
      "@type": "HowToSection",
      "name": "완성하기",
      "itemListElement": [
        {
          "@type": "HowToStep",
          "text": "완성된 돼지고기 김치찌개는 깊고 진한 국물과 부드러운 김치, 쫄깃한 고기가 잘 어우러져 맛있습니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-6.jpg",
          "name": "6. 완성된 돼지고기 김치찌개는 깊고 진한 국물과 부드러운 김치, 쫄깃한 고기가 잘 어우러져 맛있습니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-6"
        }
      ]
    },
    {
      "@type": "HowToSection",
      "name": "TIP",
      "itemListElement": [
        {
          "@type": "HowToStep",
          "text": "돼지고기 김치찌개는 오래 끓일수록 진한 풍미가 더해져, 따뜻한 밥과 함께 온 가족이 즐길 수 있는 든든한 한 끼가 됩니다.",
          "image": "https://kurlylog-img.kurly.com/recipe/paikjongwon-pork-kimchi-stew-recipe-step-7.jpg",
          "name": "7. 돼지고기 김치찌개는 오래 끓일수록 진한 풍미가 더해져, 따뜻한 밥과 함께 온 가족이 즐길 수 있는 든든한 한 끼가 됩니다.",
          "url": "https://www.kurly.com/recipe/671764624701d5e8aa02eeee#step-7"
        }
      ]
    }
  ],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": 4.7,
    "reviewCount": 35
  },
  "video": {
    "@type": "VideoObject",
    "contentUrl": "https://www.youtube.com/watch?v=uhn7MVno0uQ",
    "name": "얼큰한 백종원 돼지고기 김치찌개 레시피 3인분 40분 완성 평점 4.7점(35)",
    "description": "이번 주말엔 얼큰한 돼지고기 김치찌개를 끓여보았어요. 스팸이나 참치도 좋지만, 신김치와 돼지고기의 조합은 언제나 진리입니다. 평점 4.7점(35)의 평가가 있어요. 14가지 재료로 3인분을 40분 안에 완성! 7단계의 쉬움 난이도 레시피입니다. 1인분당 19,120원 정도의 재료비가 들어요.",
    "thumbnailUrl": "https://i.ytimg.com/vi/uhn7MVno0uQ/hqdefault.jpg",
    "uploadDate": "2024-10-14T14:00:00+09:00"
  }
}

교보문고 페이지에 embeding 된 ld+json 데이터는 이렇습니다. 책 제목, 이미지, genre, 저자, 출판사, 가격, 평점요약, 가격 다양한 정보가 구조화되어 들어있습니다.

{
  "@context": "http://schema.org",
  "@type": "Book",
  "name": "삼체 1~3 세트",
  "image": "https://contents.kyobobook.co.kr/sih/fit-in/458x0/pdt/9788954442688.jpg",
  "description": "삼체 1~3 세트 | 무한한 우주는 여전히 신화로 가득 차 있다 류츠신 『삼체』 1~3부 개정본 세트 출간SF의 신화가 된 류츠신『삼체』를 개정본으로 새롭게 만나다 오바마 전 미국 대통령이 선택하고, 노벨문학상 모옌이 극찬한 최고의 작품! 아시아 최초로 휴고상을 수상하며 SF 거장으……",
  "genre": "소설",
  "keywords": "중국소설, SF소설, 9788954442688",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "9.4",
    "ratingCount": "198",
    "bestRating": "10",
    "worstRating": "0"
  },
  "author": {
    "@type": "Person",
    "name": "류츠신 지음 | 이현아 번역"
  },
  "publisher": {
    "@type": "Organization",
    "name": "자음과모음"
  },
  "url": "https://product.kyobobook.co.kr/detail/S000000776061",
  "workExample": [
    {
      "@type": "Book",
      "isbn": "9788954442688",
      "bookFormat": "https://schema.org/Paperback",
      "datePublished": "20240607",
      "potentialAction": {
        "@type": "ReadAction",
        "target": {
          "@type": "EntryPoint",
          "urlTemplate": "https://product.kyobobook.co.kr/detail/S000000776061",
          "actionPlatform": [
            "https://schema.org/DesktopWebPlatform",
            "http://schema.org/MobileWebPlatform"
          ]
        },
        "expectsAcceptanceOf": {
          "@type": "Offer",
          "Price": "60300.0",
          "priceCurrency": "KRW",
          "availability": "https://schema.org/InStock"
        }
      }
    }
  ]
}

Schema.org에 대해 좀 더 자세히 알아봅니다. Schema.org 표준은 2011년 Google, Microsoft, Pinterest, Yandex 네 회사가 연합하여 제정했습니다. 이들 회사가 자금을 지원해 개발되었으며, Google은 검색 측면에서 'Structured Data Markup'이라는 명칭으로 Schema.org 데이터를 검색 최적화(SEO)에 어떻게 적용하는지 설명하고 있습니다.

https://schema.org https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data

그리고 2025년에는 한글검색에 Google이 검색엔진에서 부스팅을 통해 랭킹에 적극 반영하고 있다는 사실을, 저 두 키워드의 검색 결과를 보면 사실로 받아들일 수 있을 것 같습니다. 또한 검색 엔진 최적화(SEO)가 매우 중요한 시대이므로 모든 회사가 어떤 속도든 정보를 제공할 수밖에 없다고 보고 있습니다. 이렇게 이미 생성된 Schema.org 데이터를 활용하는 것을 NLWeb 에서는 전제하고 구현 방법을 제안하고 있습니다.

NLWeb 의 제안

NLWeb에서는 현재 두 가지 경로 이름(/ask, /mcp)을 제안하고 있습니다. /ask는 자연어 질의를 처리하는 엔드포인트이고, /mcp는 MCP 서버를 제공합니다. The making of NLWeb 발표에서 Python으로 구현해 공개한 코드를 바탕으로 /ask와 /mcp를 어떻게 구현하는지 설명하고 시연하셨습니다.

어떻게 구현할 것인가?

/ask

NLWeb 구성도입니다. Schema.org 데이터를 벡터 데이터베이스에 인덱싱하여 LLM이 활용할 수 있게 합니다. /ask 요청이 들어오면 LLM을 통해 처리합니다. Vector DB에서 쿼리해 가져온 내용을 근거(grounding)로 사용하기도 하고, 랭킹도 LLM으로 수행합니다. 또한 /ask 쿼리에 적합하도록 비문맥화(de-contextualization) 작업을 LLM이 수행합니다. LLM 이 Schema.org 데이터도 많이 학습하여 정말 잘 이해한다고 말씀하셨습니다. 발표에서는 Schema.org 대신 RSS 를 인덱싱해서 자연어 질의를 하는 것을 시연하셨습니다.

NLWeb에 여러 회사가 Early Adopter로 참여하고 있고, 그중 하나인 SeriousEats 사이트에서 자연어 쿼리를 시연했습니다. 시연 장면 중 특히 인상적이었던 부분은 "우리 가족은 아보카도 토스트를 무척 자주 해 먹는데, 딸 중 한 명이 아보카도 알레르기가 있어 아보카도 토스트 레시피 중에 아보카도를 다른 재료로 대체할 수도 있는 레시피를 알려줘"라고 묻는 장면이었습니다. Guha님이 검색 엔진 관점에서 보면 '아보카도'라는 단어가 세 번이나 반복되어 처리하기 까다로운 질의라고 설명하셨습니다.

/mcp - MCP 서버 제공

이 레퍼런스 서버에서 구현해둔 MCP 서버를 클로드 데스크탑에 설정해서 질의하는 시연도 있었습니다.

NLWeb Early Adoptor

초기 도입 기업으로는 TripAdvisor, Snowflake, Shopify, O'Reilly Media 등이 참여하고 있습니다. Milvus와 Qdrant 같은 벡터 데이터베이스 회사도 포함되어 있습니다.

Starting Point

발표 초반에 특히 강조하신 말씀이 있었습니다. '이 NLWeb 구현은 출발점일 뿐이며, 유일한 방법도, 가장 완벽한 방법도 아니다. 다만 출발점이 필요하다.'라고 하셨습니다.

Watch on YouTube

... But there has to be a starting point, a theory of how the web should be existed since Ted Nelson did Xanadu. It required Tim Berners-Lee and then Mark Andreessen to come and say here is an incredibly simple way of doing it. It does work. You put your documents in this format called HTML, and you put it in this folder called htdocs. In fact, it was so simple that Tim Berners-Lee's first paper to the HTML conference on how to build this web was rejected because it was too simple, right? You need that level of simplicity. It needs to kind of work, and it needs to be open so that it can be iterated upon.

다가올 Agentic Web 시대를 상상해 봅니다.