<object>을 사용한 인터랙티브 SVG?

상호작용이 가능한 SVG 기반 데이터 시각화 자료를 임베드하려고 합니다. SVG는 별도의 시스템에서 생성하지만, 제 Discourse 설치와 동일한 2차 도메인(second-level domain) 내에 있습니다. OBJECT 태그를 사용해 SVG를 불러오려고 시도해 보았는데요…

<object type="image/svg+xml" data="full_URL_to_foo.svg">
(object 로딩에 실패하면 표시될 내용)
</object>

단순히 IMG 태그로 해당 SVG(매번 동적으로 생성됩니다)를 불러오면 시각적 요소는 표시되지만, (예상대로) 상호작용은 불가능합니다…

<img src="full_URL_to_foo.svg">

Discourse와 SVG에 대해 힌트를 얻을 수 있는 방향이나 조언이 있을까요? :slight_smile:

이를 작동시키는 더 쉬운 방법은 iframe을 사용하는 것입니다. allowed_iframes 사이트 설정에 SVG 도메인을 추가하고 HTML을 사용하여 게시물에 추가하세요.

음, 그래서 그게 쉬운 방법이군요.

시도해 볼 수 있는 어려운 방법은 없을까요?

…왜냐하면 SVG 안에 브라우저 위치를 변경하는 링크를 넣고 싶거든요. IFRAME 안의 SVG에 있는 링크는 IFRAME 안에 표시되는 내용만 변경하거든요…

IFRAME 바로 전에 주제(topic)에 j/s를 임베드하고, SVG의 페이지 onclick에서 j/s 함수를 호출하는 방식은 역시 작동하지 않을 것 같다는 생각이 듭니다.

제 말이 맞나요? Mitigate XSS Attacks with Content Security Policy 처럼 얇은 j/s 코드를 추가한 다음, IFRAME 안의 SVG에서 그 코드를 호출해 보는 게 좋을까요? …아니면 "IFRAME 안"은 완전히 밀폐된 성채인가요?

아니면 SVG를 페이지에 직접 인라인으로 넣는 방법을 물어봐야 하나요? 그런 다음 테마 컴포넌트에서 주입된 j/s를 사용해 다른 서버와 상호작용하여 SVG를 동적으로 업데이트하는 방식이 좋을까요?

힌트 좀 주세요 :slight_smile:

svg를 특별한 div로 감싸서 포스트에 넣고, decorateCooked 콜백을 사용하여 테마 컴포넌트로 object 태그를 만드는 방식이 작동할 수 있습니다. Discourse 테마 개발자 가이드를 확인해 보세요.

…혹시 따라 하고 계신 분들을 위해 말씀드리면, decorateCooked()는 더 이상 사용되지 않습니다. 대신 decorateCookedElement()를 사용 중입니다.

CORS/Access-Control-Allow-Origin 문제로 막혀 있습니다. 이 방식이 어떻게 동작하는지 잘 모르겠네요…

저는 두 개의 사이트를 가지고 있습니다. 호스팅된 Discourse 사이트(forum.moversmindset.com)와 Apache 기반의 Wordpress 사이트(moversmindset.com)입니다. Wordpress 사이트에는 눈에 띄는 콘텐츠가 없습니다. 전부 RSS 피드를 생성하거나 미디어를 서빙하는 용도로만 쓰이고 있습니다. 해당 도메인에 직접 접속하면 그냥 포럼으로 리다이렉트됩니다.

GET 요청에 대해 SVG 형식의 응답을 반환하는 디렉토리가 있습니다. 예를 들어(실제 URL은 아닙니다) https://moversmindset.com/foo/bar.php 입니다.

Discourse 테마에서 스크립트 코드를 실험 중입니다. (결국 정식 플러그인으로 만들 예정입니다) 특정 DIV에 data-custom 속성을 추가해 두고, 그 DIV들에 대해 api.decorateCooked()를 호출하고 있습니다. 즉, decorateCooked()에 의해 호출되는 함수 안에서 다음과 같은 일을 하고 있습니다.

$.get(‘https://moversmindset.com/foo/bar.php’ … bla bla blah

즉, SVG를 가져와서 DOM에 추가하려고 합니다. 하지만 브라우저 에러 콘솔에 다음과 같은 메시지가 나옵니다:

질문:

이것은 Discourse 설치 환경에서 CORS를 설정해야 한다는 뜻인가요, 아니면 Apache/Wordpress 쪽에서 설정해야 한다는 뜻인가요?

저는 Discourse의 CORS 허용 목록에 https://moversmindset.com을 설정해 두었습니다.

저는 생각하기에 이쪽(서버 측)에 있는 것 같습니다. 아직 확인하지 않으셨다면 Access-Control-Allow-Origin header - HTTP | MDN 를 한번 살펴봐 주시고, 거기서 어떤 단서를 얻을 수 있는지 확인해 주시겠어요?

아주 작은 걸음이지만, 성공했습니다!

apache/WP 서버에 Access-Control-Allow-Origin 헤더를 추가해야 했습니다. 덕분에 Discourse 플랫폼에서 가져온 j/s가 정상적으로 작동하게 되었습니다. 감사합니다.

점점 천천히 이 문제를 파악해 나가고 있습니다. 제 질문을 하기 위해 상당한 세팅이 필요합니다:

SVG

SVG를 생성하는 웹 서버를 하나 가지고 있습니다. 이 질문을 위해 아주 단순한 테스트 SVG를 생성합니다…

<svg xmlns="http://www.w3.org/2000/svg" stroke-linejoin="round" viewBox="0 0 100 100">
<path d="M50,4L4,50L50,96L96,50Z" stroke="#40638C" stroke-width="3"></path>
<path d="M50,5L5,50L50,95L95,50Z" stroke="#333" fill="#40638C" stroke-width="3"></path>
<path d="M37,42c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#40495E" fill="#40495E"></path>
<path d="M35,40c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#333" fill="#555"></path>
</svg>

단순히 멋져 보이는 “병합(merge)” 아이콘일 뿐입니다. PATH 요소가 4개 있다는 점에 유의하세요.

인라인 처리

SVG를 게시물에 넣기 위해 테마를 통해 추가된 자바스크립트를 사용하고 있습니다.

최종 목표는 제대로 된 플러그인을 만드는 것입니다. 하지만 지금은 개념 증명(PoC)을 구축하는 것만 시도하고 있습니다. 그래서 단순히 테마 커스터마이징의 </head> 섹션에 붙여넣기만 했습니다:

<script type="text/discourse-plugin" version="0.8">
var UMB = {
    svgload: function(base, target) {
        var url = base + $(target).text();
        $(target).html('');
        $.ajax({
            method: "GET",
            url: url,
            async: false,
            dataType: "text",
            success: function(data) { $(target).append(data); }
        });
        alert('loaded!');
        $(target).children('path').each(function(){alert('here is a path element');});
    },
}
$.fn.umbdv = function() {
    this.each(
        function() {
            UMB.svgload('__URL_REDACTED__', this);
        }
    );
    return this;
};
api.decorateCooked(
  $elem => $elem.children('.cooked div[data-custom="umbdv"]').umbdv(),
  { id: 'umbdv' }
);
</script>

여기서…

var UMB = {는 여기저기 거대한 익명 함수를 두지 않도록 하는 전역 변수일 뿐입니다.

$.fn.umdv = 는 제 생각에 "플러그인"이라고 불리는, JQuery를 확장하는 것입니다.

api.decorateCooked(는 게시물이 브라우저로 전송되기 전에 게시물을 조작할 수 있게 해줍니다.

사용법 (Incantation)

토론에서 이렇게 작성합니다…

<div data-custom="umbdv">/vtest</div>

해당 DIV에 대해 UMB.svgload('__URL_REDACTED__', this)가 호출됩니다.

UMB.svgload()는 /vtest 문자열을 올바르게 해석하고, URL을 구성하며 AJAX 요청을 수행합니다. append(data)가 성공적으로 수행되고 보옵 내 SVG가 인라인으로 표시됩니다…

UMB.svgload() 내부의 alert()도 예상대로 발화됩니다. (명백히 디버그용 해킹이죠, 그렇죠? :))

질문 (드디어)

Discourse 호스팅 포럼에 스태프 전용(SAFF ONLY) 토론을 하나 만들었습니다. 여기서 이 동작을 실제로 볼 수 있습니다. (관리자 사용자로 내 설치 환경에 들어올 수 있는 Discourse 스태프/지원팀에게 말하는 것입니다.)

https://forum.moversmindset.com/t/svg-experimentation-in-progress/1109

왜…

$(target).children('path').each(function(){alert('here is a path element');});

…이 PATH 요소 중 하나도 선택하지 않는 걸까요?

아무것도 하지 않습니다—오류도 없습니다. 아무것도.

다음 단계

이 자명한 alert() 개념 증명이 작동하게 되면 제가 나아갈 방향은…

“작업하고 있는” DOM 조각이 아직 실제 문서 DOM에 연결되어 있지 않다는 점(UMB.svgload()이 호출되는 시점에서)을 인지하고 있습니다. 그래서 $(target)…이 제가 필요한 것이라고 기대합니다.

궁극적으로, 훨씬 더 복잡한 SVG를 인라인으로 처리해야 하며 더 복잡한 JQuery 선택자를 사용해야 합니다. $(target) 내부의 많은 요소를 찾아 이벤트 핸들러(예: onclick)를 연결하고, 이는 다른 UMB.… 전역 함수를 호출하게 될 것입니다.

원하시면 IFRAME이 최상위 레벨 내비게이션을 변경할 수 있습니다.

http://w3c-test.org/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html

팁 감사합니다!

하지만 지금은 확실히 인라인 SVG를 선호합니다.

아무튼. 몇 주 후. 이게 안 돼.

SVG가 메인 문서에 인라인으로 삽입된 후에는 SVG DOM 요소를 조작할 수 있는 방법이 없어 보여. 그래서 이벤트 트리거/액션을 추가하는 방법을 알아내지 못했어. (SVG가 iframe으로 로드된다면 onclick 등으로 쉽게 할 수 있을 텐데.)
((iframe을 안 쓰는 게 내 목표였거든.))

¯\_(ツ)_/¯