예정된 포스트 스트림 변경 사항 - 테마와 플러그인 준비 방법

레거시 “위젯” 렌더링 시스템에서 벗어나 현대적인 Glimmer 컴포넌트로 대체하고 있습니다.

최근 Glimmer 컴포넌트를 사용하여 게시물 스트림을 현대화했습니다. 이 가이드에서는 기존 위젯 기반 시스템에서 새로운 Glimmer 구현으로 플러그인과 테마를 마이그레이션하는 방법을 안내합니다.

걱정하지 마세요—이 마이그레이션은 처음에는 복잡해 보일 수 있지만 실제로는 훨씬 더 직관적입니다. 새로운 시스템은 기존 위젯 시스템보다 더 직관적이고 강력하도록 설계되었으며, 이 가이드가 이 과정을 도와줄 것입니다.

타임라인

이는 변경될 수 있는 예상 일정입니다

2025년 2분기:

  • :white_check_mark: 핵심 구현 완료
  • :white_check_mark: 공식 플러그인과 테마 컴포넌트 업그레이드 시작
  • :white_check_mark: Meta에서 활성화됨
  • :white_check_mark: 업그레이드 권장 사항 게시(이 가이드)

2025년 3분기:

  • :white_check_mark: 공식 플러그인과 테마 컴포넌트 업그레이드 완료
  • :white_check_mark: glimmer_post_stream_mode 기본값을 auto로 설정하고 콘솔 비추천(deprecation) 메시지 활성화
  • :white_check_mark: 관리자 경고 배너와 함께 비추천 메시지 활성화(2025년 7월 예정)
  • 서드파티 플러그인과 테마 업데이트 필요

2025년 4분기:

  • :white_check_mark: 새 게시물 스트림 기본 활성화
  • :white_check_mark: 기능 플래그 설정 및 레거시 코드 제거

나에게 어떤 의미가 있는가?

플러그인이나 테마 중 게시물 스트림을 커스터마이징하기 위해 ‘위젯’ API를 사용하는 것이 있다면, 새로운 버전에서 작동하도록 업데이트해야 합니다.

새 게시물 스트림을 어떻게 시험해 볼 수 있나?

새 게시물 스트림을 시험해 보려면 사이트 설정에서 glimmer_post_stream_mode 설정을 auto로 변경하기만 하면 됩니다. 호환되지 않는 플러그인이나 테마가 없는 경우 새 게시물 스트림이 활성화됩니다.

glimmer_post_stream_modeauto로 설정되면 Discourse는 호환되지 않는 플러그인과 테마를 자동으로 감지하며, 발견되면 브라우저 콘솔에 해당 플러그인이나 테마가 업데이트되어야 하는지 정확히 식별하는 유용한 경고 메시지와 관련 코드를 찾기 위한 스택 트레이스가 표시됩니다.

이 메시지는 플러그인이나 테마의 어떤 부분이 새로운 Glimmer 게시물 스트림과 호환되도록 업데이트되어야 하는지 정확히 파악하는 데 도움이 됩니다.

새 게시물 스트림 사용 중 문제가 발생하더라도 걱정하지 마세요. 당분간은 설정을 disabled로 변경하여 기존 시스템으로 돌아가실 수 있습니다. 호환되지 않는 확장 기능이 설치되어 있더라도 시험해 보고 싶다면, 관리자 권한으로 옵션을 enabled로 설정하여 새 게시물 스트림을 강제할 수 있습니다. 단, 사용 중인 커스터마이징에 따라 사이트가 올바르게 작동하지 않을 수 있으므로 주의하여 사용하세요.

커스텀 플러그인이나 테마가 설치되어 있습니다. 업데이트가 필요한가요?

다음 중 하나 이상의 커스터마이징을 수행하는 경우 플러그인이나 테마를 업데이트해야 합니다:

  • 다음 위젯에 decorateWidget, changeWidgetSetting, reopenWidget 또는 attachWidgetAction을 사용하는 경우:

    • actions-summary
    • avatar-flair
    • embedded-post
    • expand-hidden
    • expand-post-button
    • filter-jump-to-post
    • filter-show-all
    • post-article
    • post-avatar-user-info
    • post-avatar
    • post-body
    • post-contents
    • post-date
    • post-edits-indicator
    • post-email-indicator
    • post-gap
    • post-group-request
    • post-links
    • post-locked-indicator
    • post-meta-data
    • post-notice
    • post-placeholder
    • post-stream
    • post
    • poster-name
    • poster-name-title
    • posts-filtered-notice
    • reply-to-tab
    • select-post
    • topic-post-visited-line
  • 다음 API 메서드 중 하나를 사용하는 경우:

    • addPostTransformCallback
    • includePostAttributes

:light_bulb: 위의 커스터마이징 중 하나를 사용하는 확장 기능이 있는 경우, 토픽 페이지를 방문할 때 업그레이드해야 하는 플러그인이나 컴포넌트를 식별하는 콘솔 경고가 표시됩니다.

비추천 ID는 다음과 같습니다: discourse.post-stream-widget-overrides

:warning: 인스턴스에서 테마를 하나 이상 사용하는 경우, 경고가 활성 플러그인과 현재 사용 중인 테마 및 테마-컴포넌트에 대해서만 표시되므로 모든 테마를 확인해야 합니다.

대체 방안은 무엇인가?

새로운 Glimmer 게시물 스트림은 게시물의 표시 방식을 커스터마이징하는 여러 가지 방법을 제공합니다:

  1. 플러그인 아울렛(Plugin Outlets): 게시물 스트림의 특정 지점에 콘텐츠를 추가하는 데 사용됩니다.
  2. 트랜스포머(Transformers): 항목을 커스터마이징하거나 데이터 구조를 수정하거나 컴포넌트의 동작을 변경하는 데 사용됩니다.

includePostAttributes를 addTrackedPostProperties로 대체

플러그인이 includePostAttributes를 사용하여 게시물 모델에 속성을 추가하는 경우, 대신 addTrackedPostProperties를 사용하도록 업데이트해야 합니다.

이전:

api.includePostAttributes('can_accept_answer', 'accepted_answer', 'topic_accepted_answer');

이후:

api.addTrackedPostProperties('can_accept_answer', 'accepted_answer', 'topic_accepted_answer');

addTrackedPostProperties 함수는 게시물 업데이트를 위해 속성을 추적 대상으로 표시합니다. 플러그인이 게시물에 속성을 추가하고 렌더링 중에 이를 사용하는 경우 중요합니다. 이렇게 하면 이러한 속성이 변경될 때 UI가 자동으로 업데이트됩니다.

일반적인 마이그레이션 패턴

플러그인 아울렛 사용

플러그인 아울렛은 게시물 스트림의 특정 지점에 콘텐츠를 추가하는 주요 도구입니다. 이는 커스텀 콘텐츠를 삽입할 수 있는 지정된 장소라고 생각할 수 있습니다.

이것은 위젯 데코레이션에서 벗어나기 위한 핵심입니다.

Glimmer 게시물 스트림은 자주 커스터마이징되는 콘텐츠를 아울렛으로 감쌉니다. renderBeforeWrapperOutletrenderAfterWrapperOutlet 플러그인 API 함수를 사용하여 그 앞이나 뒤에 콘텐츠를 삽입하세요.

1. 위젯 데코레이션을 플러그인 아울렛으로 대체

가장 일반적인 커스터마이징은 게시물에 콘텐츠를 추가하는 것입니다. 위젯 시스템에서는 decorateWidget을 사용했습니다. Glimmer에서는 대신 플러그인 아울렛을 사용합니다.

이전:

// 플러그인의 초기화 프로그램의 일부

import { withPluginApi } from "discourse/lib/plugin-api";
// ... 다른 import

function customizeWidgetPost(api) {
  api.decorateWidget("post-contents:after-cooked", (helper) => {
    const post = helper.getModel();
    if (post.post_number === 1 && post.topic.accepted_answer) {
      return helper.attach("solved-accepted-answer", { post });
    }
  });
}

export default {
  name: "extend-for-solved-button",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizeWidgetPost(api);
    });
  }
};

이후:

// 플러그인의 .gjs 초기화 프로그램의 일부

import Component from "@glimmer/component";
import { withPluginApi } from "discourse/lib/plugin-api";
import SolvedAcceptedAnswer from "../components/solved-accepted-answer";
// ... 다른 import

function customizePost(api) {
  api.renderAfterWrapperOutlet(
    "post-content-cooked-html",
    class extends Component {
      static shouldRender(args) {
        return args.post?.post_number === 1 && args.post?.topic?.accepted_answer;
      }

      <template>
        <SolvedAcceptedAnswer 
          @post={{@post}} 
        />
      </template>
    }
  );
}

export default {
  name: "extend-for-solved-button",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizePost(api);
    });
  }
};

2. 게시자 이름 뒤에 콘텐츠 추가

플러그인이 게시자 이름 뒤에 콘텐츠를 추가하는 경우, post-meta-data-poster-name 아울렛과 함께 renderAfterWrapperOutlet API를 사용합니다.

이전:

// 플러그인의 초기화 프로그램의 일부

import { withPluginApi } from "discourse/lib/plugin-api";
// ... 다른 import

function customizeWidgetPost(api) {
  api.decorateWidget(`poster-name:after`, (dec) => {
    if (!isGPTBot(dec.attrs.user)) {
      return;
    }

    return dec.widget.attach("persona-flair", {
      personaName: dec.model?.topic?.ai_persona_name,
    });
  });
}

export default {
  name: "ai-bot-replies",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizeWidgetPost(api);
    });
  }
};

이후:

// 플러그인의 .gjs 초기화 프로그램의 일부

import Component from "@glimmer/component";
import { withPluginApi } from "discourse/lib/plugin-api";
// ... 다른 import

function customizePost(api) {
  api.renderAfterWrapperOutlet(
    "post-meta-data-poster-name", 
    class extends Component {
      static shouldRender(args) {
        return isGPTBot(args.post?.user);
      }

      <template>
        <span class="persona-flair">{{@post.topic.ai_persona_name}}</span>
      </template>
    }
  );
}

export default {
  name: "ai-bot-replies",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizePost(api);
    });
  }
};

3. 게시물 콘텐츠 앞에 콘텐츠 추가

// 테마의 .gjs 초기화 프로그램의 일부

import Component from "@glimmer/component";
import { withPluginApi } from "discourse/lib/plugin-api";
// ... 다른 import

function customizePost(api) {
  api.renderBeforeWrapperOutlet(
    "post-article",
    class extends Component {
      static shouldRender(args) {
        return args.post?.topic?.pinned;
      }

      <template>
        <div class="pinned-post-notice">
          This is a pinned topic
        </div>
      </template>
    }
  );
}

export default {
  name: "pinned-topic-notice",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizePost(api);
    });
  }
};

4. 게시물 콘텐츠 뒤에 콘텐츠 추가

// 테마의 .gjs 초기화 프로그램의 일부

import Component from "@glimmer/component";
import { withPluginApi } from "discourse/lib/plugin-api";
// ... 다른 import

function customizePost(api) {
  api.renderAfterWrapperOutlet(
    "post-article",
    class extends Component {
      static shouldRender(args) {
        return args.post?.wiki;
      }

      // 실제 컴포넌트에서는 다음과 같은 템플릿을 사용합니다:
      <template>
        <div class="wiki-post-notice">
          This post is a wiki
        </div>
      </template>
    }
  );
}

export default {
  name: "wiki-post-notice",
  initialize() {
    withPluginApi((api) => {
      customizePost(api);
      // ... 다른 커스터마이징
    });
  }
};

트랜스포머 사용

트랜스포머는 Discourse 컴포넌트를 커스터마이징하는 강력한 방법입니다. 전체 컴포넌트를 재정의하지 않고 데이터나 컴포넌트 동작을 수정할 수 있습니다.

게시물 스트림 커스터마이징에 가장 관련이 높은 값 트랜스포머 중 일부는 다음과 같습니다:

트랜스포머 이름 설명 컨텍스트
post-class 주요 게시물 요소에 적용되는 CSS 클래스를 커스터마이징합니다. { post }
post-meta-data-infos 게시물에 표시되는 메타데이터 컴포넌트 목록을 커스터마이징합니다. 게시물 날짜, 편집 표시기 등을 추가, 제거 또는 순서 변경할 수 있습니다. { post, metaDataInfoKeys }
post-meta-data-poster-name-suppress-similar-name 사용자 이름이 닉네임과 유사할 때 사용자의 전체 이름 표시를 억제할지 결정합니다. 억제하려면 true를 반환합니다. { post, name }
post-notice-component 게시물 알림을 렌더링하는 데 사용되는 컴포넌트를 커스터마이징하거나 교체합니다. { post, type }
post-show-topic-map 첫 번째 게시물에서 토픽 맵 컴포넌트의 가시성을 제어합니다. { post, isPM, isRegular, showWithoutReplies }
post-small-action-class 작은 액션 게시물에 커스텀 CSS 클래스를 추가합니다. { post, actionCode }
post-small-action-custom-component 표준 작은 액션 게시물을 커스텀 Glimmer 컴포넌트로 교체합니다. { post, actionCode }
post-small-action-icon 작은 액션 게시물에 사용되는 아이콘을 커스터마이징합니다. { post, actionCode }
poster-name-class 게시자 이름 컨테이너에 커스텀 CSS 클래스를 추가합니다. { user }

1. 게시물에 커스텀 클래스 추가

// 플러그인의 초기화 프로그램의 일부
import { withPluginApi } from "discourse/lib/plugin-api";

function customizePostClasses(api) {
  api.registerValueTransformer(
    "post-class",
    ({ value, context }) => {
      const { post } = context;

      // 특정 사용자의 게시물에 커스텀 클래스 추가
      if (post.user_id === 1) {
        return [...value, "special-user-post"];
      }

      return value;
    }
  );
}

export default {
  name: "custom-post-classes",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizePostClasses(api);
    });
  }
};

2. 커스텀 게시물 메타데이터 추가

post-meta-data-infos 트랜스포머를 사용하면 게시물 메타데이터 섹션에 커스텀 컴포넌트를 추가할 수 있습니다.

// 플러그인의 초기화 프로그램의 일부
import { withPluginApi } from "discourse/lib/plugin-api";

function customizePostMetadata(api) {
  // 메타데이터 섹션에서 사용할 컴포넌트 정의
  // 컴포넌트는 트랜스포머 콜백 외부에서 생성해야 합니다.
  // 그렇지 않으면 메모리 문제가 발생할 수 있습니다.
  const CustomMetadataComponent = <template>...</template>;

  api.registerValueTransformer(
    "post-meta-data-infos",
    ({ value: metadata, context: { post, metaDataInfoKeys } }) => {
      // 특정 게시물에 대해서만 컴포넌트 추가
      if (post.some_custom_property) {
        metadata.add(
          "custom-metadata-key",
          CustomMetadataComponent,
          {
            // 날짜 앞에 위치
            before: metaDataInfoKeys.DATE,
            // 그리고 답변 탭 뒤에 위치
            after: metaDataInfoKeys.REPLY_TO_TAB,
          }
        );
      }
    }
  );
}

export default {
  name: "custom-post-metadata",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizePostMetadata(api);
    });
  }
};

discourse-activity-pub 플러그인에서 가져온 실제 예시입니다:

// discourse-activity-pub 플러그인의 초기화 프로그램의 일부
import { withPluginApi } from "discourse/lib/plugin-api";
import ActivityPubPostStatus from "../components/activity-pub-post-status";
import {
  activityPubPostStatus,
  showStatusToUser,
} from "../lib/activity-pub-utilities";

function customizePost(api, container) {
  const currentUser = api.getCurrentUser();

  const PostMetadataActivityPubStatus = <template>
    <div class="post-info activity-pub">
      <ActivityPubPostStatus @post={{@post}} />
    </div>
  </template>;

  api.registerValueTransformer(
    "post-meta-data-infos",
    ({ value: metadata, context: { post, metaDataInfoKeys } }) => {
      const site = container.lookup("service:site");
      const siteSettings = container.lookup("service:site-settings");

      if (
        site.activity_pub_enabled &&
        post.activity_pub_enabled &&
        post.post_number !== 1 &&
        showStatusToUser(currentUser, siteSettings)
      ) {
        const status = activityPubPostStatus(post);

        if (status) {
          metadata.add(
            "activity-pub-indicator",
            PostMetadataActivityPubStatus,
            {
              before: metaDataInfoKeys.DATE,
              after: metaDataInfoKeys.REPLY_TO_TAB,
            }
          );
        }
      }
    }
  );
}

export default {
  name: "activity-pub",
  initialize(container) {
    withPluginApi((api) => {
      customizePost(api, container);
      // ... 다른 커스터마이징
    });
  }
};

5. 게시물의 cooked 콘텐츠 앞이나 뒤에 콘텐츠 삽입

플러그인이 게시물 텍스트 뒤에 콘텐츠를 추가하는 경우, post-content-cooked-html 아울렛과 함께 renderAfterWrapperOutlet API를 사용합니다.

이전:

// 플러그인의 초기화 프로그램의 일부
import { withPluginApi } from "discourse/lib/plugin-api";

function customizeCooked(api) {
  api.decorateWidget("post-contents:after-cooked", (helper) => {
    const post = helper.getModel();
    if (post.wiki) {
      const banner = document.createElement("div");
      banner.classList.add("wiki-footer");
      banner.textContent = "This post is a wiki";
      element.prepend(banner);
    }
  });
}

export default {
  name: "wiki-footer",
  initialize() {
    withPluginApi((api) => {
      // ... 다른 커스터마이징
      customizeCooked(api);
    });
  }
};

이후:

// 플러그인의 초기화 프로그램의 일부 (.gjs)
import Component from "@glimmer/component";
import { withPluginApi } from "discourse/lib/plugin-api";

// 플러그인 아울렛에서 사용할 컴포넌트 정의
class WikiBanner extends Component {
  static shouldRender(args) {
    return args.post.wiki;
  }

  <template>
    <div class="wiki-footer">This post is a wiki</div>
  </template>
}

function customizePost(api) {
  // renderBeforeWrapperOutlet을 사용하여 게시물 콘텐츠 앞에 콘텐츠 추가
  api.renderAfterWrapperOutlet(
    "post-content-cooked-html",
    WikiBanner
  );
}

export default {
  name: "wiki-footer",
  initialize() {
    withPluginApi((api) => {
      customizePost(api);
      // ... 다른 커스터마이징
    });
  }
};

전환 기간 동안 기존 및 새 시스템 모두 지원

플러그인이나 테마 작성자로서, 확장 기능이 기존 및 새 게시물 스트림 모두에서 작동하도록 전환 기간 동안 두 시스템 모두를 지원하고 싶을 수 있습니다.

많은 공식 플러그인이 사용하는 패턴은 다음과 같습니다:

// solved-button.js
import Component from "@glimmer/component";
import { withSilencedDeprecations } from "discourse/lib/deprecated";
import { withPluginApi } from "discourse/lib/plugin-api";
import RenderGlimmer from "discourse/widgets/render-glimmer";
import SolvedAcceptedAnswer from "../components/solved-accepted-answer";

function customizePost(api) {
  // glimmer 게시물 스트림 커스터마이징
  api.renderAfterWrapperOutlet(
    "post-content-cooked-html",
    class extends Component {
      static shouldRender(args) {
        return args.post?.post_number === 1 && args.post?.topic?.accepted_answer;
      }

      <template>
        <SolvedAcceptedAnswer 
          @post={{@post}} 
          @decoratorState={{@decoratorState}} 
        />
      </template>
    }
  );

  // ...

  // 기존 위젯 코드를 감싸 비추천 경고 억제
  withSilencedDeprecations("discourse.post-stream-widget-overrides", () =>
    customizeWidgetPost(api)
  );
}

// 기존 위젯 코드
function customizeWidgetPost(api) {
  api.decorateWidget("post-contents:after-cooked", (helper) => {
    let post = helper.getModel();

    if (helper.attrs.post_number === 1 && post?.topic?.accepted_answer) {
      // RenderGlimmer를 사용하여 위젯 시스템에서 Glimmer 컴포넌트 렌더링
      return new RenderGlimmer(
        helper.widget,
        "div",
        <template><SolvedAcceptedAnswer @post={{@data.post}} /></template>
        null,
        { post }
      );
    }
  });
}

export default {
  name: "extend-for-solved-button",
  initialize(container) {
    const siteSettings = container.lookup("service:site-settings");

    if (siteSettings.solved_enabled) {
      withPluginApi((api) => {
        customizePost(api);
        // ... 다른 커스터마이징
      });
    }
  },
};

실제 사례

다음은 공식 플러그인에서 가져온 실제 마이그레이션 풀 리퀘스트 링크입니다. 이는 각 유형의 커스터마이징을 어떻게 업데이트했는지 보여줍니다:

문제 해결

새 게시물 스트림 활성화 후 사이트가 깨져 보입니다

  • glimmer_post_stream_modedisabled로 되돌립니다
  • 콘솔에서 구체적인 오류 메시지를 확인합니다
  • 다시 시도하기 전에 문제 있는 플러그인/테마를 업데이트합니다

경고가 표시되지 않지만 커스터마이징이 작동하지 않습니다

  • 커스터마이징이 위에 나열된 위젯을 대상으로 하는지 확인합니다
  • 커스터마이징이 보이는 토픽이 있는 토픽 페이지에서 테스트하고 있는지 확인합니다

도움이 필요하신가요?

버그를 찾았거나 도입한 새 API를 사용하여 커스터마이징을 달성할 수 없는 경우, 아래로 알려주세요.

The Glimmer Post Stream is enabled on Meta.

If you discover any issues, please post them below.

During upgrading my components, I encountered some strange code:

Firstly, I am somewhat perplexed by @user={{@post}}. Could this be a typographical error?

Secondly, why are the PluginOutlet named post-avatar-flair and the UserAvatarFlair separate elements? Additionally, why isn’t post-avatar-flair a wrapper like other nearby outlets?

It’s working, so I don’t think it’s a typo. Perhaps in this location, the post object has all the attributes which the UserAvatarFlair component expects to find on the @user argument? I agree it’s weird looking!

Based on the PR description, it looks like those things were done for consistency with other similar ‘avatar-flair’ outlets (which were probably introduced before ‘wrapper plugin outlets’ were a thing)

We have just merged a pull request that enables the Glimmer Post Stream by default.

After the next update, compatible sites will automatically use the new Post Stream. Sites using incompatible extensions will fall back to the older version and display warnings in the browser console.

For now, you can manually enforce the use of the old Post Stream by setting glimmer_post_stream_mode to disabled.

If you encounter any issues, please report them below.

이 코드 때문에 경고가 표시되고, 그 경고가 이 스레드로 연결되는 것 같습니다:

api.changeWidgetSetting('post-avatar', 'size', '120');

새 시스템에 맞게 이 부분을 어떻게 업데이트해야 하나요?

이런 식으로:

api.registerValueTransformer("post-avatar-size", () => {
    return "120";
});

좋아요, 감사합니다.

그렇게 하면 되는 것 같습니다. 해당 코드를 입력한 후 글리머 모드에서 모든 force 설정을 임시로 켜서 테스트해 봤는데, 이후 제 사이트에서 아바타 크기와 게시물 목록이 정상적으로 작동하는 것을 확인할 수 있었습니다. 테스트 후, 아직 사용을 권장하지 않는 것 같아 오버라이드를 다시 꺼 두었습니다. 이제 제 사이트는 전환을 위해 준비가 되었기를 바랍니다.

@Boost 님, 안녕하세요. :smiley:

오버라이드를 아직 사용 권장하지 않는다고 하셨는데, 구체적으로 어떤 의미인가요? 사이트가 준비가 되었다면 자동으로 Glimmer Post Stream으로 전환됩니다.

다음 Discourse 설치 업데이트 시, 호환되지 않는 커스텀화가 여전히 있는 사이트에서도 Glimmer Post Stream이 기본적으로 활성화됩니다.

실제로 위젯 렌더링 시스템 전체가 비활성화되므로, 위젯 기반의 커스텀화는 더 이상 렌더링되지 않습니다.

현재로서는 호환되지 않는 사이트에서 관리자가 다음 설정의 값을 수정하여 기존 동작을 다시 활성화할 수 있습니다:

  • glimmer_post_stream_mode
  • deactivate_widgets_rendering

위젯 포스트 스트림을 다시 활성화하려면 두 설정 모두를 변경해야 합니다.

이것은 Discourse 코드베이스에서 기존 코드를 제거하기 전의 마지막 단계입니다. 이 제거 작업은 약 한 달 내에 이루어질 예정이며, 이후에는 위젯을 다시 활성화할 수 없게 됩니다.

실험 설정의 해당 옵션은 사용을 자제하라고 매우 명확하게 경고하고 있습니다:

지정된 사용자 그룹에 대해 ‘glimmer’ 게시물 스트림 구현을 ‘auto’ 모드로 활성화합니다. 이 구현은 현재 활발하게 개발 중이며, 프로덕션 환경에서 사용할 목적으로 설계되지 않았습니다. 구현이 최종 확정되어 공지될 때까지 이를 기반으로 테마/플러그인을 개발하지 마십시오.

따라서 "프로덕션 환경에서 사용할 목적으로 설계되지 않았습니다. 이를 기반으로 테마/플러그인을 개발하지 마십시오"라는 표현은 상당히 강경합니다.

이것이 Glimmer post stream mode auto groups 옵션에 대한 설명입니다.

좋은 지적입니다. 저희가 놓치고 있었습니다.

Glimmer Post Stream이 이제 기본값으로 설정되었으며, 위젯 버전은 레거시로 간주되어 이미 제거 예정으로 등록되어 있습니다. 설정 설명을 업데이트하겠습니다.

위젯을 스트림에서 제거하는 PR이 병합되었습니다.