# 무작위 주제 로딩 카테고리?

**URL:** https://meta.discourse.org/t/category-that-loads-random-topics/356933
**Category:** Support
**Created:** [3월 12, 2025, 10:42오전 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933 "2025-03-12T10:42:09Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![alltiagocom](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alltiagocom/32/492709_2.png) [@alltiagocom](https://meta.discourse.org/u/alltiagocom)
#### Post date: [3월 12, 2025, 10:42오전 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933/1 "2025-03-12T10:42:09Z")

</div>

구글로 검색해 보았지만 이 주제와 관련된 내용을 찾을 수 없었습니다.  
일부 주제들은 매우 가치 있을 수 있지만, 사용자는 최신 주제에 더 집중하는 경향이 있습니다(구글 검색을 통해 포럼에 온 경우를 제외하고요).

컴포넌트나 플러그인을 통해 랜덤으로 주제를 자동으로 불러오는 카테고리나 유사한 섹션을 만들 수 있을까요? 이렇게 하면 3년 전에는 참여가 많지 않았지만 여전히 가치 있는 오래된 게시물들이 다시 주목받을 수 있을 것입니다. 지금은 사용자가 더 많아졌으므로, 그들에게 마땅한 관심을 받을 수 있을지도 모릅니다.

명확히 하자면, 랜덤 주제가 단일 카테고리로 제한되는 것은 원하지 않습니다. 물론 그것도 나쁘지 않을 수 있지만요. 저는 _모든_ 카테고리의 _모든_ 주제를 해당 섹션에 불러올 수 있는 전역적인 기능을 더 생각하고 있습니다. 기본적으로 [https://meta.discourse.org/](https://meta.discourse.org/) 의 홈 페이지에 가면 카테고리에 관계없이 최신 주제 목록을 볼 수 있는 것과 같은 방식이지만, 제 경우에는 주제의 날짜나 최신 댓글에 초점을 두지 않고, 모든 카테고리에서 랜덤으로 주제를 불러오는 것입니다.

의미가 통할지 모르겠네요…

---

<div class="post-metadata">

### Author: ![Canapin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/canapin/32/119591_2.png) [@Canapin](https://meta.discourse.org/u/Canapin)
#### Post date: [3월 12, 2025, 11:25오전 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933/3 "2025-03-12T11:25:28Z")

</div>

무작위 주제라는 아이디어는 좋지만, 몇 가지 조정이 필요할 것 같습니다.  
제 포럼은 20년 이상 된 곳이라, 10년 이상 된 무작위 주제들이 한 페이지에 가득 차 있는 것은 좀 이상해 보일 것입니다.

적은 참여도를 가진 주제들을 대상으로 하려면, 표시될 최대 답글 수와 같은 매개변수가 있어야 합니다.

그렇더라도, 이러한 낮은 참여도의 주제들이 모두 흥미롭다는 뜻은 아닙니다.

특정 기준에 맞는 주제들을 대상으로 하시겠습니까, 아니면 그저… 무작위로 뽑으시겠습니까?

현재 존재하는 가장 가까운 기능은 카테고리 설정 안에 있습니다:

 ![image](https://global.discourse-cdn.com/meta/original/4X/5/4/a/54aeeebc6d863022bac7f9c148d0b07af7b9c734.png)

이것은 여기 #Support 채널에서 사용되었습니다(아직도 사용 중일까요?). #Support 주제의 이상적인 상태는 해결/닫힘이므로, 여기에서 열린 주제란 보통 해결책이 제공되지 않았음을 의미합니다. 따라서 몇 달 또는 몇 년이 지난 후에도 낮은 참여도의 주제들이 다시 살펴볼 기회를 가질 수 있게 해 주며, 이는 항상 가치 있을 수 있습니다.

---

<div class="post-metadata">

### Author: ![alltiagocom](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alltiagocom/32/492709_2.png) [@alltiagocom](https://meta.discourse.org/u/alltiagocom)
#### Post date: [3월 15, 2025, 10:39오전 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933/4 "2025-03-15T10:39:11Z")

</div>

> [@Canapin](#):
>
> 특정 기준에 맞는 주제들을 대상으로 하고 싶나요, 아니면 그냥… 랜덤으로 하고 싶나요?

랜덤으로요.

예를 들어 특정 카테고리를 지정하거나, 아니면 ‘무작위’ 버튼을 두는 것도 좋을 것 같아요.  
아니면 최신 게시글 하단에 3~4개의 오래된 랜덤 게시글을 불러오는 섹션을 두는 것도 괜찮겠네요.

20년 이상 된 포럼의 게시글들도 오늘날 여전히 관련성이 있을 수 있지만, 당시에는 아무도 크게 주목하지 않아서 지금은 좀 더 '관심’을 받아도 좋을 것 같거든요 😉

---

<div class="post-metadata">

### Author: ![alltiagocom](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alltiagocom/32/492709_2.png) [@alltiagocom](https://meta.discourse.org/u/alltiagocom)
#### Post date: [12월 27, 2025, 4:18오후 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933/5 "2025-12-27T16:18:25Z")

</div>

오늘 저는 이 문제를 해결하기 위해 Claude에게 도움을 청하기로 결정했습니다.  
많은 테스트와 수정을 거쳐 다음 결과를 얻었습니다(OPEN 상태의 주제만 로드되도록 했는데, 제 생각에는 이게 더 합리적입니다):

 ![image](https://global.discourse-cdn.com/meta/original/4X/4/8/1/481cb678a28a1d6bac93021211e9a7c48229c06e.png)

“더 많은 랜덤 주제 로드” 버튼을 클릭하면 현재 주제 아래에 5개의 주제가 추가됩니다. 버튼을 다시 클릭하면 5개씩 더 추가되며, 더 이상 주제가 없을 때까지 계속됩니다:

 ![image](https://global.discourse-cdn.com/meta/original/4X/8/b/3/8b3ad734645511cb098ece29081c2404f175055b.png)

랜덤 주제를 로드하려면 다음 주소를 방문하세요:  
`yourwebsite.com/?random`

* * *

구현 방법은 다음과 같습니다:

1 - 컴포넌트 생성  
2 - JS 탭에 다음 코드를 추가:

```js
import { apiInitializer } from "discourse/lib/api";
// URL에 ?random가 있는 경우 즉시 콘텐츠 숨김
if (new URLSearchParams(window.location.search).has('random')) {
  const style = document.createElement('style');
  style.textContent = '#main-outlet { display: none; }';
  document.head.appendChild(style);
}

let loadedTopicIds = new Set();

function addRandomTopics(listDiv, button) {
  // 버튼 숨기고 로딩 표시
  button.style.display = 'none';
  const loadingDiv = document.createElement('div');
  loadingDiv.className = 'loading-more';
  loadingDiv.textContent = '로딩 중...';
  listDiv.appendChild(loadingDiv);
  
  const query = `status:open`;
  
  fetch(`/search.json?q=${encodeURIComponent(query)}`)
    .then((response) => response.json())
    .then((data) => {
      loadingDiv.remove();
      
      if (!data || !data.topics || data.topics.length === 0) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = '더 이상 로드할 주제가 없습니다';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // 이미 로드된 주제 필터링
      const unloadedTopics = data.topics.filter(topic => !loadedTopicIds.has(topic.id));
      
      if (unloadedTopics.length === 0) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = '더 이상 로드할 주제가 없습니다';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // 섞고 최대 5개의 랜덤 주제 선택
      const shuffled = unloadedTopics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, Math.min(5, unloadedTopics.length));
      
      // Discourse 사이트 데이터에서 카테고리 가져오기
      const site = Discourse. __container__.lookup('site:main');
      
      selected.forEach(topic => {
        loadedTopicIds.add(topic.id);
        
        const date = new Date(topic.created_at);
        const formattedDate = date.toLocaleDateString('en-US', { 
          year: 'numeric', 
          month: 'short', 
          day: 'numeric' 
        });
        
        const itemDiv = document.createElement('div');
        itemDiv.className = 'random-topic-item';
        
        const linkDiv = document.createElement('div');
        linkDiv.className = 'random-topic-link';
        
        const link = document.createElement('a');
        link.href = `/t/${topic.slug}/${topic.id}`;
        link.textContent = topic.unicode_title || topic.title;
        
        linkDiv.appendChild(link);
        
        const metaDiv = document.createElement('div');
        metaDiv.className = 'random-topic-meta';
        
        const category = site.categories.find(cat => cat.id === topic.category_id);
        const categoryName = category?.name || '분류 없음';
        
        metaDiv.textContent = `${categoryName} • ${formattedDate}`;
        
        itemDiv.appendChild(linkDiv);
        itemDiv.appendChild(metaDiv);
        
        // 버튼 앞에 삽입
        listDiv.insertBefore(itemDiv, button);
      });
      
      // 사용 가능한 모든 주제를 로드했는지 확인
      if (selected.length < 5 || loadedTopicIds.size >= data.topics.length) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = '더 이상 로드할 주제가 없습니다';
        listDiv.appendChild(noMoreDiv);
      } else {
        button.style.display = 'block';
      }
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      loadingDiv.remove();
      button.style.display = 'block';
    });
}

function loadRandomTopics(container) {
  // 로드된 주제 리셋
  loadedTopicIds = new Set();
  
  container.innerHTML = '<div class="spinner"></div>';
  
  const query = `status:open`;
  
  fetch(`/search.json?q=${encodeURIComponent(query)}`)
    .then((response) => response.json())
    .then((data) => {
      if (!data || !data.topics || data.topics.length === 0) {
        container.innerHTML = '<p>주제를 찾을 수 없습니다</p>';
        return;
      }
      
      // 섞고 5개의 랜덤 주제 선택
      const shuffled = data.topics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, 5);
      
      // HTML 생성
      const listDiv = document.createElement('div');
      listDiv.className = 'random-topics-list';
      
      const heading = document.createElement('h2');
      heading.textContent = '랜덤 공개 주제';
      listDiv.appendChild(heading);
      
      // Discourse 사이트 데이터에서 카테고리 가져오기
      const site = Discourse. __container__.lookup('site:main');
      
      selected.forEach(topic => {
        loadedTopicIds.add(topic.id);
        
        const date = new Date(topic.created_at);
        const formattedDate = date.toLocaleDateString('en-US', { 
          year: 'numeric', 
          month: 'short', 
          day: 'numeric' 
        });
        
        const itemDiv = document.createElement('div');
        itemDiv.className = 'random-topic-item';
        
        const linkDiv = document.createElement('div');
        linkDiv.className = 'random-topic-link';
        
        const link = document.createElement('a');
        link.href = `/t/${topic.slug}/${topic.id}`;
        link.textContent = topic.unicode_title || topic.title;
        
        linkDiv.appendChild(link);
        
        const metaDiv = document.createElement('div');
        metaDiv.className = 'random-topic-meta';
        
        const category = site.categories.find(cat => cat.id === topic.category_id);
        const categoryName = category?.name || '분류 없음';
        
        metaDiv.textContent = `${categoryName} • ${formattedDate}`;
        
        itemDiv.appendChild(linkDiv);
        itemDiv.appendChild(metaDiv);
        listDiv.appendChild(itemDiv);
      });
      
      const button = document.createElement('button');
      button.className = 'btn btn-primary load-more-random';
      button.textContent = '더 많은 랜덤 주제 로드';
      button.addEventListener('click', () => {
        addRandomTopics(listDiv, button);
      });
      
      listDiv.appendChild(button);
      container.innerHTML = '';
      container.appendChild(listDiv);
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      container.innerHTML = '<p>주제 로드 중 오류 발생</p>';
    });
}

export default apiInitializer("1.8.0", (api) => {
  let wasOnRandomPage = new URLSearchParams(window.location.search).has('random');
  
  api.onPageChange(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const isOnRandomPage = urlParams.has('random');
    
    // 랜덤 페이지에 있다가 이제 아닌 경우, 강제 새로고침
    if (wasOnRandomPage && !isOnRandomPage) {
      window.location.reload();
      return;
    }
    
    wasOnRandomPage = isOnRandomPage;
    
    if (!isOnRandomPage) {
      return;
    }
    
    // 콘텐츠 표시 및 교체
    const mainContent = document.querySelector('#main-outlet');
    if (mainContent) {
      mainContent.style.display = 'block';
      mainContent.innerHTML = '<div id="random-topics-container"></div>';
      
      const container = document.querySelector('#random-topics-container');
      loadRandomTopics(container);
    }
  });
});

```

3 - CSS 탭에 다음 코드를 추가:

```css
.random-topics-list {
  max-width: 800px;
  margin: 40px auto;
  padding: 20px;
}

.random-topics-list h2 {
  margin-bottom: 30px;
  font-size: 2em;
}

.random-topic-item {
  margin-bottom: 25px;
}

.random-topic-link a {
  color: #E9E9E9;
  text-decoration: underline;
  font-size: 1.1em;
}

.random-topic-link a:hover {
  color: #229ED7;
}

.random-topic-meta {
  color: #808080;
  font-size: 0.85em;
  margin-top: 4px;
}

.load-more-random {
  margin-top: 30px;
}

.loading-more {
  text-align: center;
  margin-top: 20px;
  color: #808080;
}

.no-more-topics {
  text-align: center;
  margin-top: 30px;
  color: #808080;
  font-style: italic;
}

.spinner {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  animation: spin 1s linear infinite;
  margin: 100px auto;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

```

HTML 커스텀 페이지 대신 일반 Discourse 뷰를 사용하는 방법을 아는 분이 있다면 알려주시면 좋겠습니다! 기능 자체에는 만족하지만, 기본 레이아웃을 사용할 수 있다면 더 좋겠습니다. 방법을 공유해 주실 수 있을까요?
