AJAX 호출로 reopenWidget 사용

이 주제는 여러 번 거론되었지만 매번 중단되는 경향이 있습니다(보통 개발자가 실제로 AJAX 호출을 필요로 하지 않기 때문이죠). 하지만 저는 Discourse 데이터베이스에 저장되지 않은 데이터에 접근하는 플러그인(자체적인 Discord 봇)을 개발 중이며, 해당 데이터를 사용하여 게시물을 꾸미고자 합니다.

export default Component.extend({
init() {
this._super(…arguments);

var self = this;
const store = getOwner(this).lookup("service:store");

  let positions = [];

store
  .findAll("position", {
    ...args,
  })
  .then((data) => {

    for (const [key, position] of Object.entries(data.content)) {
      positions.pushObject(EmberObject.create({ ...position }));
    }
  });

withPluginApi("1.4.0", (api) => {
  api.reopenWidget("post", {
    html(attrs) {
      let position = positions.filter(
        (position) => position.user.id == attrs.user.id
      );

      if (position.length > 0) {
        attrs.cooked = "Has Position";
      }

      return this.attach("post-article", attrs);
    },
  });
});

},
});

지금까지 이 코드의 다양한 변형을 시도해 보았으며, store 쿼리의 .then 부분 안에 api 함수 호출을 넣는 방식 등 여러 가지 방법을 해 보았지만, 결과는 모두 동일하게 스크롤을 해야만 게시물의 변경 사항이 렌더링되는 문제만 반복되고 있습니다.

모든 시도는 늦은 밤에 이루어졌기 때문에 매우 자명한 것을 놓치고 있을 가능성이 높지만, 도움을 주신다면 정말 감사하겠습니다.

무엇을 하려는지에 따라 다르겠지만, 공식 Discord Ruby API를 사용하여 데이터를 Discourse DB로 가져오고, 클라이언트로 직렬화한 후 위젯에서 사용하는 건 어떨까요?

주요 애플리케이션 API가 대부분의 용도에 충분하다고 생각했기 때문에, 불필요한 복잡성처럼 느껴졌습니다. 다만, 솔직히 말해서 디스코스의 Ruby 쪽 구현을 더 공부해야 하므로, 제 이해가 잘못되었을 수도 있습니다.

그래도 게시물을 꾸미는 기능이 제대로 작동하지 않으면, 결국 그 방향으로 가야 할 것 같습니다.

@merefield 님께 감사의 말씀을 전하기 위해 다시 돌아와서, 직렬화(serializing)에 대해 더 조사해 보았더니 JS에서 요청하던 방식 대신 시리얼라이저에서 API 호출을 수행하는 방식으로 작업을 완료할 수 있었습니다.

이 방식이 권장되지 않는 이유는 수없이 많을 것이라고 생각하지만, 제 경우에는 잘 작동하므로 그대로 진행할 예정입니다. 미래에 이 내용을 찾는 분들을 위해, 다음 코드를 사용하면 데이터를 시리얼라이저에 넣을 수 있습니다:

add_to_serializer(:topic_view, :data_name) do
    JSON.parse(Net::HTTP.get(URI('https://yourwebsite.com?topic_id=' + object.topic.id.to_s)))
end

이렇게 하면 reopenWidget 호출 내에서 데이터에 접근할 수 있습니다. 제 경우처럼 주제(topic)에 관련된 데이터를 topic_view 시리얼라이저에 붙여 넣고, 게시물을 수정하면서 이를 다음과 같이 접근합니다:

api.reopenWidget("post", {
          html(attrs) {
            let data_name = this.model.topic.data_name;

            // 데이터로 작업 수행

            return this.attach("post-article", attrs);
          },
        });

앞서 언급했듯이, 이것이 올바른 방식이 아닐 수 있는 이유는 매우 많을 것입니다. 하지만 제 사용 사례에서는 로드 시간에 미치는 영향이 최소한으로 작동하고 있습니다.

어떤 이유로든 JS 내에서 이 작업을 수행하려는 분들을 위해, 관련 코드는 게시물을 복호화하는 discourse-encrypt 플러그인에 존재하는 것 같습니다. 하지만 저는 이 방법이 구현하기 훨씬 쉬웠습니다: discourse-encrypt/assets/javascripts/discourse/initializers/decrypt-posts.js at 255724ebc5fc3956f26beca09c1f7cb273d76eb2 · discourse/discourse-encrypt · GitHub

이상하게도 이 문제가 Topic Thumbnails 테마 컴포넌트를 깨뜨려서 이미지들이 과도하게 크게 표시됩니다. GitHub - discourse/discourse-topic-thumbnails: Display thumbnails in topic lists · GitHub

정말 이상한 상호작용이지만, 그 외에는 눈에 띄는 문제가 없습니다.

음… 아키텍처적으로 적절한지 확신이 서지 않습니다. 백엔드 솔루션을 탐색하고 계신 것은 좋은 점이지만 (인증과 인가가 관여되는 경우 원격 서버 호출은 거의 항상 백엔드 프로세스 사이에서 이루어져야 합니다), 데이터는 비동기로 로드되는 것이 바람직하며, 직렬화기는 원격 호출의 완료에 의존하여 종료하지 않아야 합니다. 최소한 해당 호출을 캐시로 감싸는 것이 좋습니다.

진행 상황을 지켜보시되, 해당 변경 사항 적용 전후의 페이지 로드 타이밍에 주의를 기울여 주세요. 상당한 지연이 발생할 수 있습니다.

실제로는 로딩당 최대 10~50ms 정도가 추가되는 수준입니다. 두 서버가 동일한 데이터센터에 위치해 있어 연결 지연 시간이 매우 낮습니다. 사실, 컴포넌트로 데이터를 가져오는 데 제가 사용하는 방식은 Discourse가 자체 정적 페이지를 렌더링하는 것보다 약간 빠르게 페이지를 로드합니다(약 100ms 차이). 이 과정에는 Discord ID를 사용하여 사용자 데이터를 API 응답에 추가한 후 클라이언트로 전달하기 전까지 DB 작업을 포함합니다. (Ruby가 외부 애플리케이션에서 데이터를 요청 → JSON 파싱 → 응답에 데이터를 추가하기 위한 DB query.each 루프 → JSON 생성 및 프론트엔드 클라이언트로 반환). 코드베이스가 얼마나 방대한지 고려하면 이해할 수 있는 부분이지만, 내부적으로 확실히 오버헤드가 존재하여 전통적으로 "비싸다"고 여겨지는 이러한 요청들이 완전히 눈에 띄지 않을 정도의 여유가 있습니다. 그렇다고 해도, 여기서는 빛과 소리의 속도를 이야기하는 것이며, Discourse는 놀라운 플랫폼이므로 최종 사용자에게 여전히 매우 빠릅니다.

캐시는 훌륭한 추가 기능이 될 것이며, 이에 대해 조사해 볼 예정입니다. Ruby에는 아직 매우 초보 단계이므로 사용 가능한 함수에 대해 더 많이 배울수록 업데이트하겠습니다. 앞으로 진행하면서 이 내용을 업데이트해 나갈 계획인데, 현재는 해킹적인 방법일지라도 이러한 방식의 어떤 형태의 메서드가 플러그인 개발 측면에서 잠재적으로 매우 유용할 수 있다고 생각하기 때문입니다. 기존 애플리케이션을 보유한 사람들을 위해, 데이터를 효율적으로 필요한 곳에 배치하기 위해 익숙한 API를 사용할 수 있고, 데이터베이스와 Discourse 간 데이터 동기화 시스템을 구축하거나 추가 모델이나 DB 마이그레이션에 손을 대지 않아도 되므로 개발 시간이 크게 줄어듭니다. 또한 플러그인 개발자가 애플리케이션의 문서화가 잘 된 영역(즉, 플러그인 API)에 머물 수 있게 해줍니다.

귀한 통찰을 제공해 주셔서 진심으로 감사드립니다. 이 플랫폼에 대해 더 많이 배우고,hopefully 미래에 기여할 수 있기를 기대하며 설레는 마음으로 계속 학습해 나갈 것입니다.