네이버 블로그 에디터에 카드링크(오글링크) 자동 삽입 — 클립보드 지옥에서 공식 API까지의 트러블슈팅 여정

지난 며칠간 크롬 확장프로그램에 "저장해둔 카드링크를 네이버 블로그 편집기에 자동으로 삽입하는" 기능을 만들면서 겪은 삽질과 결국 찾아낸 해법을 정리해본다. 결론부터 말하면, DOM을 직접 조작하거나 클립보드를 흉내내는 방식은 전부 실패했고, 네이버 에디터가 자체적으로 노출하는 공식 API를 찾아내고 나서야 제대로 동작했다.

■ 문제 상황

네이버 블로그 SmartEditor에서 URL을 붙여넣으면 제목·요약·썸네일이 담긴 카드(오글링크, oglink) 형태로 자동 변환된다. 이 카드를 미리 저장해뒀다가 원하는 글에 한 번에 삽입하는 기능을 만들고 싶었다. 방식은 간단해 보였다 — 카드 컴포넌트를 어떻게든 문서에 넣기만 하면 된다.

■ 1차 시도: 클립보드 + 붙여넣기 이벤트 흉내내기

가장 먼저 시도한 건 카드의 outerHTML을 복사해뒀다가, 커서 위치에서 ClipboardEvent("paste")를 직접 dispatch하는 방식이었다. 결과는 절반의 성공 — DOM 구조는 그럴싸하게 만들어졌지만, 실제로 붙여넣으면 이미지와 텍스트가 따로 분리되거나, 네이버가 자체적으로 재감지한 엉뚱한 카드로 대체되곤 했다.

원인을 추적해보니 두 가지였다.

  1. 컴포넌트 id가 원본과 중복되면서 내부 상태가 꼬임
  2. 더 근본적으로, 크롬은 자바스크립트로 만든 "가짜" ClipboardEvent의 clipboardData를 페이지가 신뢰하지 못하게 막아둔다. 스푸핑 방지를 위한 의도된 보안 정책이라, 아무리 이벤트를 정교하게 만들어도 뚫을 수 없는 벽이었다.

■ 2차 시도: chrome.debugger로 "진짜" 입력 흉내내기

그래서 방향을 바꿨다. 크롬 확장프로그램의 debugger 권한을 쓰면 CDP(Chrome DevTools Protocol)를 통해 진짜 마우스 클릭·키보드 입력과 구분되지 않는 신뢰된(trusted) 이벤트를 만들 수 있다. 임시 요소를 만들어 클릭 → Ctrl+A → Ctrl+C로 복사하고, 실제 편집 영역에 Ctrl+V로 붙여넣는 자동화를 구현했다.

여기서도 우여곡절이 있었다. debugger가 붙는 순간 크롬이 "이 페이지가 디버깅되고 있습니다" 배너를 띄우면서 뷰포트 레이아웃이 바뀌는 바람에, 미리 계산해둔 클릭 좌표가 어긋나 엉뚱한 요소를 클릭하는 문제도 있었다. 좌표 계산 순서를 바꾸고 나서야 클릭·복사·붙여넣기가 안정적으로 성공했다.

그런데 여기서 예상치 못한 벽에 부딪혔다. 클립보드 복사·붙여넣기 메커니즘 자체는 완벽하게 동작하는데도, 네이버 에디터가 붙여넣은 카드 마크업에서 이미지만 남기고 나머지 구조(제목, 요약)를 전부 버려버리는 것이었다. 이건 "붙여넣기가 진짜냐 가짜냐"의 문제가 아니라, 네이버가 낯선 곳에서 들어온 마크업 자체를 신뢰하지 않는 문제였다. 클립보드 트릭으로는 더 이상 해결할 수 없는 영역이었다.

■ 돌파구: 네이버 내부 API 활용

네이버 내부 API를 정밀 분석 했다. 하나씩 뜯어보다가 핵심을 발견했다.

네이버 에디터는 window.SmartEditor(또는 iframe 안에서는 window.frames.mainFrame.SmartEditor)라는 전역 객체를 페이지에 직접 노출해두고 있었다. 그리고 에디터 인스턴스에는 이런 식으로 호출 가능한 공식 명령 체계가 있었다.

editor.execCommand(
SmartEditor.COMMAND.COMMON.INSERT_COMPONENTS,
{ data: { components: [oglinkJson] }, option: { disableMerge: true } }
)

클립보드도, 붙여넣기 이벤트도, 마우스 시뮬레이션도 필요 없었다. 그냥 네이버 자신의 삽입 API를 호출하면 되는 거였다. 이 객체는 페이지의 메인 월드(MAIN world)에만 노출되어 있어서, chrome.scripting.executeScript를 world: 'MAIN' 옵션으로 호출해야 접근할 수 있었다.

■ 마지막 퍼즐: 발행 시점에만 나타나는 오류

API 호출 방식을 바꾸자 삽입 자체는 완벽하게 동작했다. 그런데 이상하게도 "발행" 버튼을 누르면 "정상적이지 않은 입력으로 발행에 실패하였습니다"라는 오류가 떴다. 화면에 보이는 카드는 기존 카드와 DOM까지 한 글자도 다르지 않을 만큼 완벽했는데도 말이다.

DevTools 콘솔에서 에디터의 내부 문서 데이터(_documentService.getDocumentData())를 직접 꺼내서 정상 카드와 비교해보고서야 원인을 찾았다. 정상적으로 URL을 붙여넣어 만들어진 카드에는 전부 oglinkSign이라는 필드가 있었는데, 내가 직접 만든 JSON에는 이 필드가 없었다. 이건 네이버 서버가 URL을 실제로 검증한 뒤 발급하는 서명으로 보였다. 클라이언트가 임의로 만든 데이터는 아무리 구조가 완벽해도 이 서명이 있을 수 없으니, 발행 시점의 더 엄격한 검증에서 걸러지는 것이었다.

■ 최종 해법: 서명째로 재사용하기

새 JSON을 만드는 대신, 사용자가 선택한 "이미 서버 검증을 통과한" 카드의 내부 데이터를 통째로 그대로 복사해서 저장해두고, 나중에 그 원본 데이터를(id만 새로 발급해서) 그대로 다시 삽입하는 방식으로 바꿨다. 서명이 그대로 보존되니 발행도 정상적으로 통과했다.

■ 정리

  • 클라이언트에서 클립보드나 이벤트를 아무리 정교하게 흉내내도, 브라우저와 서비스가 신뢰하지 않는 데이터는 결국 어딘가에서 걸러진다.
  • 비슷한 기능이 이미 있는 서비스라면, 공식적으로 노출된(혹은 노출될 수밖에 없는) 내부 API가 있을 가능성이 높다. DOM을 흉내내기 전에 먼저 찾아볼 가치가 있다.
  • 눈에 보이는 결과(DOM)가 같다고 해서 내부 데이터까지 같은 건 아니다. 문제가 안 풀릴 땐 내부 데이터 구조를 직접 까봐야 진짜 원인이 보인다.