AIREITER

AI 이미지

FLUX.2 ProGPT-Image 2Wan 2.7 Image ProGPT 4o ImageSeedream 5.0 ProSeedream V5 liteSeedream V4.5더 보기

AI 비디오

Kling 3.0 Motion ControlSora 2 ProKling 3.0 TurboSora 2Kling 3.0Grok Imagine 1.5Veo 3.1더 보기

LLM

Gemini 3.6 FlashGemini 3.1 ProKimi K3Gemini 3 ProGemini 2.5 ProClaude Opus 5Claude Fable 5더 보기
곧 출시Seedance 2.5
Super ResolutionLyric Video GeneratorGPT Image 2 1K GeneratorGPT Image 2 Product Mockup GeneratorUse GPT-5.6 Online
API 문서가격
블로그업데이트LLM API GuideClaude API GuideKimi K3 API Guide
템플릿
  • AIReiter
  • 블로그
  • B2B 광고 인텔리전스는 결국 HTML에서 온다: 리디자인에도 버티는 파서 설계법

B2B 광고 인텔리전스는 결국 HTML에서 온다: 리디자인에도 버티는 파서 설계법

마지막 업데이트: 2026-07-31 07:03:16

이번 분기에 경쟁 B2B 기업이 어느 국가에서 광고를 집행하는지, 얼마나 오래 돌렸는지, 대략적인 노출 규모는 어느 정도인지, 어떤 타기팅 세그먼트를 겨냥하는지 알고 싶을 때가 있다. 이런 정보는 광고 투명성 규제 준수를 위해 공개된 각 플랫폼의 광고 라이브러리에 대체로 들어 있다. 하지만 DevTools를 열어 한참을 뒤져 보면 깔끔한 JSON 엔드포인트는 없고, 서버에서 렌더링된 HTML 페이지 하나만 남는 경우가 많다. 파서를 만들어 데이터를 뽑아낸 뒤 3주가 지나 플랫폼이 디자인을 바꾸면 어떨까. 에러는 나지 않는데 모든 필드가 비어 있는 결과만 조용히 반환될 수 있다. 빈 데이터를 근거로 의사결정할 뻔하는 상황이다.

이 글에서는 리디자인 이후에도 이런 파서를 유지하는 방법과, 유지보수 과정에서 모델이 실제로 할 수 있는 일을 다룬다. 먼저 데이터 경계를 분명히 하자. 여기서 다루는 데이터는 모두 각 플랫폼의 공개 광고 라이브러리와 크리에이티브 센터에서 가져오며, 본인 계정으로 일반 로그인해 접근한다. 서명 우회나 비공개 엔드포인트 사용은 없다. 이 원칙은 뒤에서 보겠지만 파서 설계에 명시적으로 반영된다.

B2B 광고 인텔리전스가 HTML 파싱으로 귀결되는 이유

광고 라이브러리라도 데이터를 노출하는 방식은 크게 갈린다. Meta 광고 라이브러리처럼 구조화된 검색과 JSON을 제공하는 소비자용 라이브러리가 있고, 검색부터 세션이 필요한 유형도 있다. 반면 대부분의 B2B 플랫폼 광고 라이브러리는 JSON 엔드포인트 없이 서버 렌더링 HTML만 제공한다. 이유는 간단하다. 이 서비스는 개발자용 제품 API가 아니라 광고 투명성 규제를 충족하기 위한 산출물이기 때문이다.

개발자가 호출하도록 만들어진 것이 아니므로 버전도, 변경 로그도, 하위 호환성 약속도 없다. 사람에게 보여주기 위한 페이지를 서버가 HTML로 렌더링해 줄 뿐이고, 우리가 쓸 수 있는 유일한 “API”는 웹페이지 자체다.

따라서 태생적으로 취약할 수밖에 없다. 타사의 UI 구현 세부 사항에 의존해야 하고, 상대는 원할 때 언제든 바꿀 수 있으며, 그 사실을 알릴 의무도 없다. JSON API의 필드 변경은 적어도 ‘변경’으로 취급되지만, HTML 리디자인은 그들 입장에서는 일상적인 프런트엔드 개선일 뿐이다. 이를 피할 방법은 없다. 대신 파서가 조용히 빈값을 돌려주지 않도록, 깨지더라도 정상적으로 실패하고 리디자인 후 빠르게 고칠 수 있게 작성해야 한다.

스트리밍 파서는 코드보다 유지보수 부담을 줄인다

처음에는 lxml이나 BeautifulSoup으로 페이지 전체 DOM 트리를 만들고 .find()를 연달아 호출하고 싶어진다. 물론 동작은 한다. 하지만 이 대상에는 적합하지 않다. DOM 트리는 브라우저가 페이지를 렌더링하는 과정에서 만드는 중간 산출물이다. MDN의 DOM 설명처럼 문서를 스크립트가 구조적으로 접근할 수 있는 노드 트리로 파싱한 결과다. 그런데 여기서 필요한 것은 몇 개 필드의 추출이지, 전체 트리가 아니다. 굳이 그 구조에 자신을 묶어둘 이유도 없다.

내가 선택한 방식은 Python 표준 라이브러리의 HTMLParser를 상속한, 정확히 881줄짜리 스트리밍 파서다. starttag / data / endtag 콜백 몇 개가 상태 머신을 움직인다. 스캔하며 필요한 정보를 누적하다가 카드 경계를 만나면 레코드를 하나 내보내고 상태를 비운 뒤 계속 진행한다. 전체 DOM 트리는 한 번도 만들지 않는다.

스트리밍 방식의 이점은 분명하다. 우선 메모리다. 상세 페이지 HTML은 수십 KB에서 수백 KB에 쉽게 이른다. DOM 트리는 페이지 전체 구조를 메모리에 올리지만, 스트리밍 파서는 ‘지금 어디를 읽고 있는지’, ‘현재 카드에서 어디까지 왔는지’만 상태로 유지한다. 더 중요한 것은 사고 부담이다. .find('div').find('div')[2]를 쓰는 순간 파싱은 DOM의 계층적 위치에 묶인다. 그런데 리디자인은 바로 그 계층을 가장 쉽게 바꾼다. 컨테이너를 하나 더 감싸거나 래퍼를 분리하는 것만으로도 모든 위치가 밀린다. 상태 머신은 ‘지금 읽는 대상이 의미상 카드의 시작인가, 노출 수치인가, 타기팅 태그인가’만 묻게 만든다. 위치는 바뀌어도 의미는 쉽게 바뀌지 않는다.

리디자인에 무너지지 않는 파서의 세 가지 원칙

결국 핵심은 세 가지다. 모두 실제 리디자인을 겪으며 얻은 원칙이다.

첫째, 위치가 아니라 의미를 기준으로 앵커를 잡는다. 상태 머신은 ‘총 노출 수’, ‘집행 기간’처럼 사람이 읽는 필드 라벨, 역할을 나타내는 마커, 블록의 의미적 경계 같은 신호를 따라 움직인다. 절대 ‘위에서 세 번째 노드’ 같은 기준을 쓰지 않는다. 검증 기준도 간단하다. 이 요소가 다른 위치로 옮겨지거나 한 단계 더 감싸진다면, 파싱은 여전히 동작하는가? 그렇다면 유효한 앵커다. 위치 기반 앵커는 첫 리디자인에 깨지지만, 의미 기반 앵커는 순수한 스타일 변경의 대부분을 버틴다.

둘째, 필드가 없다고 예외를 던지지 말고 성능을 낮춰 계속 진행한다. 각 카드를 누적하기 전, 모든 필드를 빈 기본값으로 가진 템플릿에서 시작한다. 텍스트는 빈 문자열, 숫자는 None, 목록은 빈 배열이다. 읽을 수 있는 값은 채우고, 읽지 못한 값은 비워 둔다. 필드 하나를 추출하지 못했다고 카드 전체가 죽거나 페이지 전체가 중단되어서는 안 된다. CTA 문구가 없는 광고라도 노출 수와 타기팅 국가는 여전히 필요하다. 중요하지 않은 필드 하나가 페이지가 제공할 수 있었던 인텔리전스 전체를 망가뜨리게 하는 것은 최악의 설계다.

셋째, 결과에 완전성 정보를 함께 담아 ‘비어 있음’과 ‘고장’을 구분한다. 가장 놓치기 쉽고, 비용도 가장 큰 원칙이다. ‘파싱된 광고 0개’는 완전히 다른 두 상황을 뜻할 수 있다. 광고주가 이번 분기에 실제로 광고를 집행하지 않았거나, 페이지 구조가 바뀌어 어떤 앵커도 잡지 못해 파서가 깨졌을 수 있다. 반환값에서 둘은 반드시 구별되어야 한다. 이를 위해 카드 수, 페이지가 선언한 총량, 페이지네이션 상태 같은 보조 증거를 함께 반환한다. 예를 들어 ‘카드 수 0개인데 페이지 메타데이터는 배치가 있어야 한다고 말하고, 다음 페이지 마커도 없다’는 조합이라면 실제 빈 결과가 아니라 구조 변경으로 판단할 수 있다. 이때 빈 목록을 무심히 반환하는 대신 명확한 에러를 내야 한다.

앞서 말한 데이터 경계도 이 계층에서 구현된다. 파서는 로그인 페이지로 튕겼는지 확인하고, 제목이 로그인·가입 페이지임을 발견하는 즉시 파싱을 중단하고 오류를 낸다. 본인 계정으로 일반적으로 볼 수 있는 공개 페이지만 처리하며, 로그인 장벽 앞에서 멈추고 이를 넘어서려 하지 않는다.

인텔리전스의 핵심은 광고 필드가 아니라 필터 축이다

여기까지 읽으면 광고 하나의 모든 필드를 깔끔하게 뽑는 것이 목표처럼 보일 수 있다. 하지만 그렇지 않다. 광고 한 건의 필드는 그 자체로는 죽은 데이터에 가깝다. 진짜 가치가 생기는 지점은 광고를 어떤 축으로 나눠 볼 수 있느냐다. 광고 라이브러리의 검색 필터는 이미 인텔리전스 차원 목록이다. 이를 프로그래밍 가능한 쿼리 파라미터로 정리하면 ‘광고 하나’가 아니라 ‘경쟁사의 출시 움직임을 보여주는 단면’을 얻는다.

  • 국가: 광고를 집행하는 시장과 집행하지 않는 시장을 확인한다. B2B 기업이 특정 국가에서 갑자기 광고를 시작했다면, 자사 웹사이트보다 먼저 확장 움직임을 드러내는 신호일 수 있다.

  • 집행 기간(시작일·종료일): 해당 크리에이티브가 얼마나 오래 운영됐는지 보여준다. 장기 집행 광고는 가장 강한 신호다. 전환되지 않는 크리에이티브에 계속 비용을 지불하는 사람은 없기 때문이다. 집행 기간 자체가 상대가 실제 비용을 들여 대신 수행한 A/B 테스트의 결과다.

  • 노출 범위(최소/최대): 대략적인 집행 규모를 가늠하는 지표다. 절대값은 정확하지 않지만, 어떤 광고가 우선순위 높은 집행인지 순위를 매기기에는 충분하다.

  • 타기팅 조건: 포함하거나 제외한 타기팅이 무엇인지 확인한다. 상대가 자사 제품을 누가 구매할 것이라 판단하는지 가장 직접적으로 보여주는 오디언스 인텔리전스다.

필드 추출은 수단이고, 이 차원들이 목적이다. 파서를 작성할 때는 거꾸로 생각해야 한다. 이 축으로 조회하고 정렬하려면 어떤 최소 필드를 안정적으로 추출해야 하는가? 나머지 화려한 필드는 추출하지 못해도 인텔리전스의 가치에는 큰 영향이 없다.

리디자인 후에는 모델에게 구·신 HTML 차이를 분석시켜라

이런 파서는 리디자인이 오면 언젠가 반드시 깨진다. 그리고 고치는 과정이야말로 모델이 들어갈 자리다. 중요한 점은 모델이 파싱 자체에 들어가는 것이 아니라는 사실이다. 파싱은 결정론적 작업이며, 하드코딩된 상태 머신으로 처리해야 한다. 결정론적 작업을 모델에 맡기지 말아야 하는 이유와 같은 원칙이다. 모델의 역할은 유지보수다.

흐름은 이렇다. 본인 계정으로 광고 라이브러리를 열고, 리디자인 전 HTML 사본과 리디자인 후 새 HTML을 모두 저장한다. 현재 파서가 추출하는 필드 목록과 함께 모델에 제공한 뒤, 구·신 구조 차이를 기준으로 어떤 필드의 의미적 앵커가 바뀌었는지, 새 앵커는 어디인지, 몇 줄만 바꾸면 되는 최소 수정안이 무엇인지 찾게 한다. 이는 전형적인 추론 티어 작업이다. 단순히 ‘구조가 조정됐다’고 되풀이하는 것이 아니라, 새 구조에 대응하는 의미가 여전히 존재하는지 찾아 실제로 수정 가능한 계획을 내놓아야 한다. 단계마다 모델에 요구하는 역량은 다르다. 하나의 티어만 전 과정에 쓰면 비용 또는 정확도에서 손해를 본다.

단계

필요 역량

선택

model id

SSR HTML 페이지 전체를 읽고 구·신 구조를 대응시킴

긴 컨텍스트, 수십~수백 KB 상세 페이지를 한 번에 처리

Kimi K3

kimi-k3

리디자인 후 구·신 차이를 읽고 앵커가 이동한 지점을 판단해 최소 수정안 제시

강한 추론, 현상 반복이 아니라 구조를 근거로 설명

Claude Opus 5

claude-opus-5

수백 개 광고주의 카드를 대량으로 정규화·태깅해 인텔리전스로 변환

저렴한 비용, 수백~수천 건의 고동시성 호출

Claude Sonnet 5

claude-sonnet-5

픽스처 비교 실패 시 차이의 원인 분석

중간 수준 추론, ‘기대 필드와 실제 추출값’을 비교해 설명

GPT-5.6 Sol

gpt-5.6-sol

핵심은 두 번째 티어이며, 모델을 바꿨을 때 결과 차이가 눈에 보이는 유일한 단계다. 추론 티어가 정말 가치 있는지 내 말을 믿을 필요는 없다. 검증 절차는 짧다.

  1. 리디자인 전 페이지 HTML과 후 페이지 HTML을 각각 저장한다. 본인 계정으로 광고 라이브러리를 열어 페이지를 저장하면 된다.

  2. 두 HTML과 현재 파서의 필드 목록을 claude-opus-5와 gpt-5.6-sol에 모두 제공한다.

  3. 한 가지만 본다. ‘기존에는 total impressions 라벨을 앵커로 잡았는데, 새 버전에서는 해당 라벨의 컨테이너 역할이 바뀌었으니 X를 앵커로 바꿔야 한다’처럼 구체적인 의미 앵커 변경을 짚는가? 아니면 ‘구조가 조정됐으므로 재적응을 권장한다’는 식의 모호한 답만 하는가?

  4. 전자는 바로 적용할 수 있고, 후자는 아무 정보도 주지 않는다. 이 차이가 모델 선택 기준이며, 리디자인 당일 얼마나 많은 맹목적 시행착오를 할지를 직접 결정한다.

한 번만 비교해 봐도 어떤 차이인지 어떤 벤치마크보다 직접적으로 알 수 있다.

진짜 문제는 모델 교체 비용이다

네 가지 티어는 세 벤더, 세 SDK, 세 인증 방식, 세 에러 포맷에 걸쳐 있다. 단계별로 세 클라이언트를 연결해야 한다고 생각하면, 대부분은 ‘가끔 있는 파서 수정과 대량 데이터 정리’에 그만한 가치가 없다고 판단한다. 결국 전 과정을 하나의 모델로 처리하게 되고, 리디자인 수정에는 ‘재적응을 권장한다’고만 말하는 티어를 쓰며 이유도 모른 채 시간을 낭비한다.

AIReiter는 이 계층을 평탄화한다. 키 하나와 OpenAI 호환 인터페이스 하나로 네 가지 티어를 모두 사용할 수 있으며, 요청 본문의 model 필드만 바꾸면 된다.

# 리디자인 수정: 추론 티어
curl https://aireiter.com/api/v1/chat/completions \
  -H "Authorization: Bearer $AIREITER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [{"role": "user", "content": "<old-new HTML diff + current field list, ask where the anchor drifted>"}]
  }'

# 대량 인텔리전스 정규화: model 필드만 바꾸고 나머지는 그대로
#   "model": "claude-sonnet-5"
# 페이지 전체를 읽는 긴 컨텍스트: "model": "kimi-k3"
# 차이 원인 분석:                   "model": "gpt-5.6-sol"

이미 OpenAI SDK를 사용 중이라면 base_url을 https://aireiter.com/api/v1로 지정하고 나머지는 바꾸지 않으면 된다. Anthropic SDK에서는 같은 키로 POST /api/v1/messages를 호출한다.

가격 측면에서는 Claude 모델이 정가 대비 30% 할인되고, GPT 모델은 반값이며, Kimi K3도 같은 키로 호출할 수 있다. 이 워크플로에서 할인은 가장 큰 비용이 드는 지점에 적용된다. 리디자인 수정은 드물지만 가치가 높은 저빈도 호출이므로 주된 비용이 아니다. 주된 비용은 대량 인텔리전스 정규화다. 경쟁 광고주 20곳을 지켜보며 각각 수십~수백 장의 카드를 모델에 넣어 메시지, 오디언스, 집행 기간을 읽어내는 과정이다. 가장 호출 밀도가 높은 이 작업은 Claude Sonnet을 30% 할인된 가격으로 처리한다. 페이지 전체를 읽고 구조를 맞춰 보는 긴 컨텍스트 입력이 그다음으로 밀도가 높다. 가장 비싼 두 작업이 모두 할인 지점에 걸려 있다.

  • API 키 발급받기

  • 가입 없이 사용해 보기: 리디자인 전후 HTML 한 쌍을 두 모델에 직접 넣고, 어느 쪽이 실제로 앵커 이동 지점을 짚어내는지 비교한 뒤 연동 여부를 결정하면 된다.

수정이 끝나고 원시 카드를 대량으로 정규화했다면, 다음 단계는 이 인텔리전스를 의사결정과 에셋 생성용 크리에이티브 파이프라인으로 보내는 일이다. 그 역할은 키워드에서 완성 광고까지 이어지는 전체 루프가 담당한다. 이 글은 공개 데이터를 안정적으로 가져오는 가장 앞단을 다뤘다.

필드 정확성의 최종 판정은 픽스처가 한다

모델이 제안한 수정안은 검증 전까지 어디까지나 제안일 뿐이다. ‘앵커를 X로 바꿔야 한다’는 말이 그럴듯해 보여도, 모든 카드에서 X가 유지된다는 보장은 없다. B2B 광고에는 이미지+텍스트형, 텍스트 전용형, 캐러셀형, 랜딩 페이지 유무가 다른 형식이 섞여 있다. 모델이 본 두 샘플이 그 모든 경우를 포괄하지 않을 수 있다.

이를 막는 장치는 리버스 엔지니어링에서 모델 환각을 막는 장치와 같다. 바로 고정 벡터 비교다. 알고 있는 입력, 즉 실제 페이지 HTML 몇 개를 저장하고, 한 번 직접 검증한 정답 출력값인 필드 결과와 함께 리포지터리에 커밋된 픽스처로 관리한다. 이후 직접 수정했든 모델 제안을 반영했든 파서를 바꿀 때마다 픽스처 묶음을 다시 실행해 필드별로 비교한다. 이 방법이 있어야 업스트림 리디자인 후 ‘제안을 잘못 적용한 것인가, 아니면 페이지가 또 바뀐 것인가’를 빠르게 구분할 수 있다. 모두 통과하면 수정이 맞고, 일부만 실패하면 해당 사례에서 어떤 필드가 틀렸는지로 문제 계층을 바로 알 수 있다. 모델은 수정안을 만들고, 픽스처는 수정안이 맞는지 판정한다. 둘을 섞어서는 안 된다. 이 차등 비교 방법 전체는 차등 테스트 글에서 설명하며, 광고 라이브러리 파서도 같은 게이트를 적용한다. 이 계층이 없으면 모델의 자신감을 정확성으로 받아들이게 된다. 그러면 ‘제안을 적용했고 에러도 없으니 배포했는데, 3일 뒤 특정 국가 데이터가 계속 비어 있었다’는 일을 겪기 쉽다.

마무리

B2B 광고 인텔리전스가 HTML 파싱으로 귀결되는 이유는 광고 라이브러리가 제품 API가 아니라 규제 준수 산출물이기 때문이다. API 계약도 없고 리디자인은 언제든 일어날 수 있다. 리디자인 내성은 세 가지 설계에서 나온다. 위치가 아닌 의미에 앵커를 둘 것, 누락 필드에서 예외를 던지지 말 것, 완전성 정보를 태깅해 ‘비어 있음’과 ‘고장’을 구분할 것이다. 진짜 가치가 있는 것은 광고 한 건의 필드가 아니라 국가·집행 기간·노출 범위·타기팅 조건으로 경쟁사의 출시 움직임을 나누어 보는 차원이다. 모델의 역할도 분명하다. 결정론적 상태 머신인 파싱에 넣는 것이 아니라, 리디자인 후 구·신 HTML을 비교해 수정안을 만드는 유지보수에 추론 티어를 쓰고, 카드를 대량으로 인텔리전스로 전환하는 데 저비용 고동시성 티어를 쓰는 것이다. 다만 필드가 정확한지의 최종 판단은 언제나 픽스처가 내린다. 이 티어들을 하나의 통합 인터페이스에 연결하면 남는 마찰은 model 필드 변경뿐이며, 이는 모델 선택으로 해결된다.

>_AIReiter 모델 디렉터리

이 가이드와 관련된 모델로 빠르게 API 접근

Claude Opus 5

Chat

복잡한 추론, 코딩, 긴 컨텍스트의 전문 작업을 위한 프리미엄 Claude 모델입니다.

anthropicAPI Key 생성 >

Claude Sonnet 5

Chat

고급 추론, 코딩, 일상 업무를 위한 균형 잡힌 Claude 모델입니다.

AnthropicAPI Key 생성 >

Kimi K3

Chat

코딩, 글쓰기, 분석 및 에이전트 워크플로를 위한 장문 맥락 추론 모델입니다.

moonshotAPI Key 생성 >

GPT-5.6 Sol

Chat

까다로운 코딩, 추론, 장문형 에이전트 작업을 위한 프리미엄 GPT-5.6 텍스트 모델.

OpenAIAPI Key 생성 >

Claude Fable 5

Chat

심층 추론과 복잡한 장문 작업을 위한 프리미엄 Claude 모델입니다.

AnthropicAPI Key 생성 >

최근 게시글

GPT-5.6 가격 인하: Luna와 Terra, 이제 실제 비용은 얼마일까

2026-07-31

Invalid API Key: 고치기 전에 401과 403부터 구분하기

2026-07-31

OpenRouter 429 해결법: Provider Error인가, Rate Limit인가?

2026-07-31

DeepSeek V4 Flash vs GLM-5.2: 0731 업데이트 직접 테스트

2026-07-31
AIREITER

문의가 있으신가요? 연락처
[email protected]

LLM

Gemini 3.6 FlashGemini 3.1 ProKimi K3Gemini 3 ProGemini 2.5 Pro

AI 비디오

Kling 3.0 Motion ControlSora 2 ProKling 3.0 TurboSora 2Kling 3.0

AI 이미지

FLUX.2 ProGPT-Image 2Wan 2.7 Image ProGPT 4o ImageSeedream 5.0 Pro

블로그

모두 보기 →

회사

개인정보 처리방침서비스 약관환불 정책

© 2026 AIReiter. All rights reserved.