이 세 가지 Discourse 개념, theme-component, plugin, pluginAPI의 차이를 설명해 주실 수 있나요?
특히 theme-component와 plugin의 차이에 대해 궁금합니다. 포럼을 커스터마이즈하고 싶을 때, 무엇을 만들어야 하는지 어떻게 판단해야 할까요?
(소개에서 이 차이를 놓쳤다면 죄송합니다. 하지만 해당 문서에서 그 부분을 찾지 못하겠습니다)
이 세 가지 Discourse 개념, theme-component, plugin, pluginAPI의 차이를 설명해 주실 수 있나요?
특히 theme-component와 plugin의 차이에 대해 궁금합니다. 포럼을 커스터마이즈하고 싶을 때, 무엇을 만들어야 하는지 어떻게 판단해야 할까요?
(소개에서 이 차이를 놓쳤다면 죄송합니다. 하지만 해당 문서에서 그 부분을 찾지 못하겠습니다)
저는 디스커스(Discourse) 초보자는 아니지만, 전문가도 아닙니다.
테마 컴포넌트(Theme component)는 HTM, CSS, JavaScript를 사용하여 기본 테마(base theme)를 강화합니다. 기본 테마라고 별도로 언급하는 이유는, 보통 단순히 '테마’라고 불리는데 사람들이 이 차이를 명확히 구분하지 않는 경우가 많아서 추측을 해야 하기 때문입니다. 테마 또는 테마 컴포넌트는 관리자가 사이트를 다운하지 않고도 설치할 수 있으며, 디스커스 유료 고객이라면 이것들을 추가할 수도 있습니다. (목록) 참고: 디스커스 테마 사용 입문가 가이드
플러그인(Plugin)은 Ruby를 사용하며 거의 모든 것이 가능합니다. 디스커스 유료 고객이라면 활성화가 허용되는 플러그인 세트가 제한되어 있지만, 셀프 호스팅(self-hosting)을 사용한다면 원하는 만큼 추가할 수 있습니다. 다만, 업그레이드 시 커스텀 플러그인이 사이트를 깨뜨리는 사례를 많이 보셨을 것입니다. 활성화 시 재시작이 필요하지 않지만, 처음 설치할 때는 재시작이 필요할 것 같습니다. 플러그인에 대한 제 경험은 관리 메뉴에서 활성화하는 것뿐이므로, 다른 분들이 더 자세히 설명해 줄 수 있을 것입니다. (목록) 참고: 디스커스 플러그인 만들기 입문가 가이드 - 1부
저는 플러그인을 개발해 본 적이 없으므로, 질문하신 내용이 Discourse API Ruby Gem을 가리키는 것이라고 추측합니다. 참고: Use the Discourse API ruby gem
또한 웹훅(Webhooks)으로 이루어진 API도 있으며, 보통 curl이나 다른 프로그래밍 언어와 함께 사용됩니다. Ruby를 사용하지 않아도 되므로 편리합니다.
이 부분도 직접 해본 적은 없지만, PostgreSQL 데이터베이스 레벨에서 프로그래밍하는 것도 가능합니다. 하지만 매우 숙련되고 자신의 실력에 확신이 있는 경우가 아니라면 추천하지 않습니다.
도움이 되셨길 바랍니다(HTH).
수정(EDIT)
디스커스 개발자로 전력을 다하고 싶으시다면 보너스 라운드
@EricGT 님의 답변은 이미 잘 설명해 주셨는데, 그 위에 몇 가지 더 보태겠습니다.
사이트를 커스터마이징하기 시작할 때 가장 좋은 출발점은 테마입니다. 아래는 몇 가지 참고 자료입니다:
디자이너를 위한 Discourse 테마 가이드
개발자를 위한 Discourse 테마 가이드
테마 크리에이터와 테마 CLI를 사용하여 Discourse 테마 제작을 시작하는 입문자 가이드
감사합니다, 여러분. 정말 도움이 됩니다. 제가 파악한 핵심적인 구별은 다음과 같습니다:
– 프론트엔드에 표시되는 것만 변경하려면 테마를 만들어야 합니다.
– 백엔드와의 상호작용이 필요한 변경을 하려면 플러그인을 만들어야 합니다.
이렇게 이해한 것이 맞을까요?
구체적으로 고민 중인 예시가 하나 있습니다: 카테고리 모더레이터가 해당 카테고리에서 주제를 고정할 수 있도록 하려고 합니다. 대략적인 흐름은 다음과 같다고 생각합니다:
해당 사용자가 해당 카테고리의 모더레이터인지 평가합니다 (이 작업에는 사용자 및 카테고리에 대한 정보를 위해 백엔드에 접근해야 합니다)
사용자가 모더레이터라면 고정 버튼을 표시합니다 (이것은 프론트엔드 작업입니다)
사용자가 고정 버튼을 클릭하면 해당 주제를 상단으로 올립니다 (이 작업이 Discourse 코드베이스에서 어디에서 일어나는지 정확히 모르겠습니다 – 프론트엔드와 백엔드 모두에서 일어나는 걸까요?)
여기서 백엔드와 통신해야 하므로 (아마 (1)번에서, (3)번에서도?) 플러그인을 사용해야 할 것 같습니다. 맞을까요?
표면적인 부분은 그럴 수 있지만, 권한과 보안과 관련이 있으므로 백엔드도 관여하게 됩니다. 프론트엔드에 사용자의 권한을 결정할 재량권을 줄 수는 없거든요.
“이 사용자가 해당 카테고리의 модератор(관리자)인가?”라는 질문에 프로그래밍적으로 답하는 것이 프론트엔드와 백엔드에 걸쳐 여러 파일을 관여하게 된다는 말씀이신가요? 음…
테마에서는 사용자가 модератор(관리자)인지 여부를 파악하고, Discourse API가 프론트엔드에서 호출할 수 있는 엔드포인트를 노출한다면 백엔드 호출도 수행할 수 있어야 합니다(테마는 JavaScript를 사용할 수 있으므로). 따라서 [1]번의 경우 플러그인이 필요하지 않을 가능성이 높습니다. 백엔드 동작을 변경하거나 API를 노출해야 할 때만 필요하게 됩니다.
하지만 [3]번의 경우엔 아마도 필요할 것입니다. @merefield가 말했듯이 이는 권한과 보안과 관련이 있기 때문입니다(백엔드가 관리자의 주제 고정(pinning)을 차단하고 있다면, 이를 허용하도록 변경해야 합니다).
위에서 언급했듯이, 해당 검증(사용자가 관리자인지 여부 확인)만 수행하려면 플러그인이 필요하지 않을 가능성이 높지만, 사용자에게 카테고리 고정 권한을 부여하는 동작 때문에 필요할 것입니다. Discourse에 모든 관리자가 주제를 고정할 수 있도록 허용하는 옵션이 있다면(있을지 확신은 못 합니다) 플러그인이 필요하지 않을 것입니다(다만, 고정 버튼이 관리자에게 표시되지 않는다면 테마를 사용하여 관리자에게 버튼을 표시하고 클릭 시 JavaScript로 엔드포인트를 호출해야 하므로, 테마도 필요하지 않을 것입니다).
테마와 플러그인, 그리고 제가 언급했던 구체적인 예시에 대해 매우 유용한 답변이네요. 감사합니다.
지금까지 변경 사항을 입력하는 방식은 Discourse 코드의 세부 사항을 파고드는 것이었습니다(이 객체는 어디에 정의되어 있는가, 이 템플릿은 무엇에 의해 제어되는가, 이 작업을 처리하는 로직은 어디에 있는가 등). 하지만 이 방법은 매우 느렸습니다.
아마도 더 효율적인 방법은 API에 초점을 맞추는 것일 것 같습니다. 이렇게 하면 성숙한 Discourse 코드의 모든 세부 사항을 파고들 필요가 없고, 플러그인 대신 테마를 구축하는 데 집중할 수 있습니다–아니면 단순히 “customize” 대시보드에서 변경 사항을 입력하는 것만으로도 충분할 수 있습니다.
결국, API가 어떻게 작동하는지 파악하는 것이 Discourse 코드베이스를 파악하는 것보다 훨씬 관리하기 쉬워 보입니다.
제가 언급했던 예시에 맞춰 생각해보겠습니다: 사용자가 특정 카테고리의 moderatord(모더레이터)라면, 그 사용자에게 카테고리 페이지에서 토픽을 고정할 수 있는 권한을 부여하는 것입니다.
플러그인 없이 이 작업을 수행할 수 있을까요? 제가 그려본 시나리오를 한번 살펴보겠습니다:
1. 사용자가 카테고리의 moderatord(모더레이터)인가?
현재 api에서 사용자가 특정 카테고리의 moderatord(모더레이터)인지 알려주는 것을 찾지 못했습니다. 카테고리를 가져오는 GET 요청에서 찾아볼 수 있을 것으로 예상했지만, 그런 요청을 찾지 못했습니다. 아니면 사용자를 가져오는 GET 요청에서 찾을 수 있을지도 모르지만, 거기서는 사용자가 moderatord(모더레이터)로 있는 카테고리 목록을 볼 수 없었습니다.
이 정보를 추가할 수 있을까요?
또는 대안으로, 사용자가 moderatord(모더레이터)임을 식별하기 위해 사용자 또는 카테고리에 custom_field를 생성한 다음, 카테고리 페이지가 로드될 때 해당 커스텀 필드를 호출하는 API 요청을 만들 수도 있습니다.
2. 사용자가 moderatord(모더레이터)인 경우 고정 버튼 표시.
(1)번 질문에 답할 수 있다면, 사용자가 moderatord(모더레이터)인 경우에만 버튼을 표시하도록 프론트엔드 JavaScript+CSS로 이 버튼을 추가할 수 있을 것이라고 가정합니다.
3. 사용자(moderatord)가 버튼을 클릭하여 토픽 고정
API에서 토픽은 “pinned” 속성(부울 값)을 가진 것 같습니다. 각 토픽은 하나의 카테고리에만 속하는 것으로 보이므로, 이는 해당 카테고리에서 토픽이 고정되어 있는 상태와 일치한다고 가정합니다.
따라서 여기서 moderatord가 “고정” 버튼을 누르면, 토픽의 “pinned” 상태를 True로 업데이트할 수 있을 것입니다. 이것이 작동하지 않는다면, 커스텀 필드도 해결책이 될 수 있습니다(다만, 토픽에 커스텀 필드를 추가하는 방법을 아직 찾지 못했습니다).
즉, 위와 같이 또는 유사한 방식으로 API를 사용하면, 플러그인을 사용하여 수행했을 경우 Discourse 코드베이스의 파일들을 많이 파고들어야 하는 작업을 훨씬 쉽게 달성할 수 있을 것 같습니다.
이렇게 이해하는 것이 맞을까요?
찾으셨나요: Discourse API 역공학하기
이전에 본 적은 있지만, 지금 좀 더 자세히 살펴보고 있습니다. 상기시켜 주셔서 감사합니다.
다음 사항들을 정리해 보고 있습니다:
– API에서 특정 카테고리의 모더레이터가 누구인지에 대한 정보를 어디서 가져올 수 있는지 (카테고리나 사용자에 대해 반환되는 정보에서는 보이지 않는데, 분명 어딘가에는 있을 텐데 말이죠)
– API를 사용해 항목에 새로운 필드를 추가할 수 있는지? 예를 들어, API를 사용해 주제에 사용자 정의 필드를 추가할 수 있을까요? (일반적으로 주제에는 사용자 정의 필드가 없는 것 같습니다)
데이터베이스를 살펴보는 것이 좋겠어요.
데이터베이스에는 posts 테이블이 있으며, 여기에 필드를 추가할 수는 있지만, 그렇게 하면 공식 지원 범위에서 벗어나 도움을 받을 수 없게 됩니다.
감사합니다–정말 많은 도움이 됩니다.
어떤 데이터베이스를 말씀하시는 건가요?
확인 차 말씀드리자면, 제 생각은 플러그인이 필요할 수 있는 일부 작업을 API를 통해 수행하는 것입니다. 예를 들어, 제 사이트 자체가 특정 사용자(또는 해당되는 경우 그룹)가 해당 카테고리의 관리자인지 여부를 판단하기 위해 API 호출을 수행하는 것입니다. 이 호출은 커스터마이즈 패널, 또는 테마나 플러그인에 하드코딩될 것입니다.
이러한 유형의 호출을 수행하려면 인증이 필요할 것이며, 이는 관리자 패널에서 API 키를 생성해야 한다는 것을 의미합니다. 관리자 패널의 API 생성 과정에는 "설명(Description)"과 "사용자 수준(User Level)"이 필요하지만, 이것이 여기에서 어떻게 적용되는지 잘 모르겠습니다. 제 경우, 제 앱이 API 호출을 수행합니다. 특정 사용자를 위해 호출되는 것이 아닙니다. 따라서 제가 잘못 이해하고 있을 수 있습니다.
그런 종류의 API 호출에 적절한 사용자 수준이 무엇인지, 또는 그곳에 무엇을 입력해야 하는지 아시나요?
감사합니다. 이 항목은 별도로 진행하겠습니다.
아니요, 각 동작에 대한 접근 권한은 서버에서 강제 적용됩니다.
lib/guardian/ 및 lib/guardian.rb의 함수를 오버라이드하여 카테고리별 관리자가 주제를 고정할 수 있도록 허용한 다음, 플러그인 대신 Theme JS와 동일한 메커니즘을 사용하여 적절한 시점에 “주제 고정” 옵션이 나타나도록 UI를 변경해야 합니다.
아, 이제 이해가네요. 그러면 해당 기능을 위해 API를 사용하는 것은 작동하지 않을 것 같군요 (귀하의 답변 덕분에 많은 시간을 절약할 수 있을 것 같습니다).
일반적인 고정(pinning) 동작과는 약간 다른 방식으로 시도해 볼 수도 있겠습니다. 대신, 특정 사용자에게 카테고리별 "소유권"을 부여하고, 이를 통해 해당 카테고리 내에서 특정 주제를 강조할 수 있게 하는 방식이죠.
JSON API를 통해 이를 구현하는 방식은 다음과 같습니다. 커스터마이징 대시보드에서 관련 사용자에게 커스텀 필드(예: category-name: owner)를 부여하거나 유사한 설정을 하는 거죠. 그리고 카테고리 페이지가 로드될 때 API를 호출하여 사용자를 평가합니다. 해당 사용자가 해당 커스텀 필드를 가지고 있다면 “강조” 버튼을 표시하고, 사용자가 특정 주제에 대해 “강조” 버튼을 누르면 해당 주제를 카테고리의 강조 그룹(이것도 해당 카테고리의 커스텀 필드입니다)에 할당하는 방식입니다.
이것은 대략적인 구상일 뿐이며, 각 단계를 하나씩 확인해 줄 필요는 없습니다(코딩을 진행하면서 어쨌든 조정해야 할 수도 있으니까요). 하지만 지금 제 질문은 이렇습니다. JSON API를 이렇게 사용하는 방식, 특히 제 discourse 앱 내부에서 커스텀 필드를 생성하고 가져오는 것이 가능할까요?
JQ331 님,
하지만 지금 제 질문은: Discourse 앱 내부에서 커스텀 필드를 생성하고 가져오는 데 이 JSON API 사용 방식이 작동할 수 있을까요?
테마 컴포넌트, 플러그인, Discourse API의 차이점에 대한 질문들에는 이미 훌륭한 답변들이 많이 올라와 있어 제가 더 많은 가치를 추가하기는 어려울 것 같습니다. 하지만 혹시 도움이 될 수도 있을 것 같아 또 다른 관점을 제시해 보겠습니다.
테마 컴포넌트와 플러그인 모두 템플릿 훅(플러그인 훅)을 사용하여 Ember.js 라이프사이클 내에서 코드를 평가합니다(참고로 이 부분을 공부해 두면 좋습니다).
또한, Discourse API는 테마 컴포넌트와 플러그인 모두에서 사용할 수 있습니다.
API는 기본적으로 기반이 되는 PostgreSQL DB의 데이터를 전부 공개하는 것은 아니지만, 그 중 일부를 노출합니다.
기능을 개발할 때는 API부터 시작하여 필요한 데이터가 API를 통해 조회 가능한지 확인하는 것이 좋습니다.
API에 없는 필요한 데이터가 있다면, PostgreSQL DB를 살펴보고 해당 데이터가 DB에 존재하는지 확인해야 합니다.
필요한 추가 데이터가 DB에 존재한다면, 그 데이터를 노출해야 하며, 일반적으로 이는 Discourse 데이터 직렬화기(serializer)에 데이터를 추가하고 API를 확장하는 것을 의미합니다.
데이터 직렬화기는 API가 노출하는 JSON 객체를 생성하는 과정일 뿐입니다. 이 과정을 확장하여 더 많은 객체를 추가할 수 있습니다.
PostgreSQL DB를 조사하는 방법은 다양할 것입니다(예를 들어 GitHub에서 코드를 읽는 것 등). 저는 DB에 직접 로그인하여 테이블을 확인하고, 테이블 구조와 각 테이블에 어떤 필드가 있는지(기본적인 SQL을 사용하여) 연구합니다.
요약하자면(간단하게 정리하자면), API와 DB 테이블을 모두 참고 자료로 활용해야 합니다. 일반적으로 API가 기본적으로(OOTB) 제공하지 않는 데이터를 추가하여 "API를 확장"하고 싶을 때는 이를 위해 플러그인을 생성합니다. 그러면 해당 데이터가 확장된 API와 함께 노출되며, 이후 테마 컴포넌트와 플러그인 모두에서 그 데이터를 사용할 수 있게 됩니다.
이 관점이 조금이라도 유용하거나 도움이 되기를 바랍니다.
정말 훌륭한 설명입니다. 이렇게 답변해 주셔서 진심으로 감사합니다. 이전에는 깨닫지 못했던 점을 강조해 주셨는데요:
이 답변들을 종합해 보면 (말씀하신 대로) 많은 경우 JSON API를 상호작용하는 것이 좋은 시작점이 될 수 있으며, 이를 통해 새로운 테마나 플러그인을 코딩할 필요가 없을 수도 있다는 것입니다. 하지만 API를 통해 노출되지 않는 데이터 유형도 있습니다. 이러한 데이터 유형에 접근하고 작업을 하려면 Discourse 데이터 직렬화기(serializer)를 사용하여 해당 데이터를 노출해야 하며, 이를 직렬화하려면 플러그인을 사용해야 합니다.
API를 통해 사용할 수 없는 데이터의 좋은 예 중 하나는 그룹의 소유자(group owners)입니다. 이렇게 말하는 이유는 (그룹 소유자에 대한 접근과 관련하여) 다음과 같은 답변이 있기 때문입니다:
혼란스러운 점 하나가 있습니다–Discourse API에서 특정 그룹을 가져올 때 반환되는 속성 중 하나가 "is_group_owner": true로 나열되어 있는데, 이것이 정확히 무엇을 의미하는지 잘 모르겠습니다…
하지만 그룹 소유자를 가져오려면 그룹 소유자 특성을 직렬화해야 할 것 같습니다.
Discourse 직렬화기를 사용하는 좋은 예시가 있을까요? 이것은 봤지만, 그 중요성을 고려할 때 몇 가지 예제가 포함된 튜토리얼이 매우 도움이 될 것입니다.
제가 찾은 가장 가까운 예시는 다음과 같습니다:
이것은 도움이 되지만, 정확하지는 않습니다 (적어도 "invalid plugin"이라는 오류가 발생합니다). 그룹 인덱스 페이지에서 각 그룹의 소유자에 접근할 수 있도록 어떻게 수정해야 하는지 모르겠습니다.
플러그인 예제를 어떻게 시도해 보셨는지 정확히 알 수 없지만, 다른 주제에서 제가 게시한 파일 구조와 코드를 개발 인스턴스에서 사용했을 때 정상적으로 실행되었습니다.
is_group_owner는 그룹을 보고 있는 현재 사용자의 맥락에서 사용됩니다.
ajax 호출을 통해 소유자 정보를 가져올 수 있지만, 제 지식 범위 내에서 이는 목록의 각 개별 그룹에 대해 수행해야 하며, 결과적으로 수많은 요청이 발생하게 됩니다. 전체적으로 볼 때 이 방법으로 작동하게 만드는 것은 어려울 것이라고 생각합니다. 어쨌든 실험해 보고 싶으시다면, 아래 개념 증명 코드 스니펫을 테마에서 시도해 볼 수 있습니다. GROUP_NAME을 그룹 이름 중 하나로 교체하면 됩니다. (편집: ajax 호출을 활용하는 방법의 예가 포함된 테마 컴포넌트도 있습니다: discourse-featured-topics/common/head_tag.html at master · awesomerobot/discourse-featured-topics · GitHub)
<script type="text/discourse-plugin" version="0.8.40">
const { ajax } = require("discourse/lib/ajax");
ajax(`/groups/GROUP_NAME/members.json`).then(response => {
console.log(response.owners.map(owner => owner.username))
});
</script>
이 모든 점을 고려할 때, 플러그인을 사용하는 것이 확실히 가장 쉽고 깔끔한 해결책입니다.