Dev.to · 2 min read

웹 디렉터리 내부 검색 랭킹은 어떻게 설계할까? Radar Link Site 사례로 보는 검색 점수 구조

웹 디렉터리 내부 검색 랭킹은 어떻게 설계할까? Radar Link Site 사례로 보는 검색 점수 구조

웹 디렉터리에 검색 기능을 붙이는 일은 생각보다 단순하지 않습니다. 처음에는 사이트 이름이나 설명에 검색어가 포함되면 결과에 보여주면 될 것 같았습니다. 하지만 사이트 수가 늘어나면 곧 문제가 생깁니다. 같은 검색어에 수십 개의 결과가 나오고, 사용자가 원하는 사이트가 아래쪽에 묻히기 시작합니다. 결국 중요한 문제는 이것입니다. 어떤 결과를 먼저 보여줄 것인가? 제가 운영하고 있는 웹 디렉터리 프로젝트 Radar Link Site에서도 이 문제가 중요했습니다. Radar Link Site는 다양한 웹사이트를 카테고리별로 탐색하고 비교할 수 있도록 구성한 웹 디렉터리이기 때문에, 검색 결과 순서는 곧 탐색 경험과 연결됩니다. 1. 단순 문자열 검색은 금방 한계가 생긴다 가장 기본적인 검색은 다음처럼 만들 수 있습니다. function searchSites(query, sites) { const keyword = query.toLowerCase(); return sites.filter(site => site.name.toLowerCase().includes(keyword) || site.description.toLowerCase().includes(keyword) ); } 이 방식은 사이트 수가 적을 때는 충분합니다. 하지만 결과를 정렬하지 않으면 이런 문제가 생깁니다. 검색어: ai AI Community AI Image Generator Best AI Tools AI Writing Platform Productivity with AI 사용자가 실제로 원하는 것이 “AI 도구”인지 “AI 커뮤니티”인지 구분하기 어렵습니다. 모두 검색어가 포함되어 있다는 이유만으로 같은 중요도를 가지게 됩니다. 2. 검색 결과에 점수를 부여한다 그래서 각 결과에 점수를 계산하는 방식으로 바꿀 수 있습니다. 예를 들어 다음 요소를 사용할 수 있습니다. 사이트 이름 일치 카테고리 일치 태그 일치 설명문 일치 인기도 최신성 가장 단순한 형태는 다음과 같습니다. function calculateSearchScore(site, query) { const q = query.toLowerCase(); let score = 0; if (site.name.toLowerCase() === q) { score += 10; } if (site.name.toLowerCase().includes(q)) { score += 6; } if (site.category.toLowerCase().includes(q)) { score += 4; } if (site.tags.some(tag => tag.toLowerCase().includes(q) )) { score += 3; } if (site.description.toLowerCase().includes(q)) { score += 1; } return score; } 이제 단순 포함 여부가 아니라 어디에 검색어가 등장했는지에 따라 결과의 우선순위를 다르게 줄 수 있습니다. 3. 사이트 이름은 가장 강한 신호로 본다 웹 디렉터리에서는 사용자가 정확한 사이트 이름을 검색하는 경우가 많습니다. 예를 들어: reddit github notion 같은 검색어라면 설명문보다 사이트 이름 일치가 훨씬 중요합니다. 그래서 저는 보통 이런 식으로 가중치를 생각합니다. Exact name match 10 Partial name match 6 Category match 4 Tag match 3 Description match 1 결과는 다음처럼 정렬할 수 있습니다. function searchAndRank(query, sites) { return sites .map(site => ({ ...site, score: calculateSearchScore(site, query) })) .filter(site => site.score > 0) .sort((a, b) => b.score - a.score); } 4. 카테고리 검색은 별도로 생각해야 한다 문제는 사용자가 사이트 이름이 아니라 주제를 검색할 때입니다. 예를 들어: community shopping jobs finance ai 이런 검색어는 특정 사이트 하나를 찾는 것이 아니라 해당 분야를 탐색하려는 의도가 강합니다. 그래서 카테고리 일치에는 더 높은 가중치를 줄 수도 있습니다. if (site.category.toLowerCase() === q) { score += 8; } Radar Link Site처럼 카테고리 탐색이 중요한 웹 디렉터리에서는 이 차이가 꽤 큽니다. 정확한 사이트를 찾는 검색과 새로운 사이트를 발견하려는 검색은 사실 서로 다른 문제이기 때문입니다. 5. 태그는 검색 결과의 정밀도를 높인다 카테고리만으로는 충분하지 않은 경우가 많습니다. 예를 들어 AI 카테고리 안에서도: image writing coding research video automation 처럼 서비스 목적이 크게 다릅니다. 그래서 사이트마다 태그를 추가할 수 있습니다. { name: "Example AI Tool", category: "AI", tags: [ "image", "design", "generator" ] } 사용자가 image를 검색하면 단순히 AI 카테고리 전체를 보여주는 것이 아니라 이미지 관련 사이트를 더 위에 배치할 수 있습니다. 6. 검색어 정규화가 생각보다 중요하다 실제 검색 로그를 보면 사용자는 항상 깔끔하게 검색하지 않습니다. 예를 들어: AI tools ai tool AI툴 ai-tools ai_tools 같은 의미의 검색어가 여러 형태로 들어올 수 있습니다. 그래서 기본적인 정규화 과정이 필요합니다. function normalizeQuery(query) { return query .toLowerCase() .trim() .replace(/[-_]/g, " ") .replace(/\s+/g, " "); } 조금 더 발전시키면 별칭도 추가할 수 있습니다. const synonyms = { "ai tool": ["ai tools", "ai service", "artificial intelligence"], "jobs": ["job", "career", "employment"], "community": ["forum", "discussion", "social"] }; 웹 디렉터리는 일반 검색엔진보다 데이터 범위가 작기 때문에 이런 수동 사전도 상당히 효과적일 수 있습니다. 7. 클릭 데이터를 검색 랭킹에 반영할 수 있다 텍스트 관련도만으로 결과를 정렬하면 실제 사용자 선호와 차이가 생길 수 있습니다. 예를 들어 두 사이트의 검색 점수가 같다고 가정해보겠습니다. Site A score = 8 Site B score = 8 하지만 실제로는 Site A가 훨씬 많이 클릭될 수 있습니다. 그러면 클릭 데이터를 추가적인 신호로 사용할 수 있습니다. finalScore = relevanceScore * 0.8 + popularityScore * 0.2; 다만 여기서 주의할 점이 있습니다. 인기 사이트를 계속 위에 올리면 새로운 사이트는 영원히 아래에 남을 수 있습니다. 8. 인기 편향을 막아야 한다 인기 데이터를 그대로 사용하면 이런 현상이 생깁니다. Popular Site ↓ More Exposure ↓ More Clicks ↓ Higher Rank ↓ Even More Exposure 결국 기존 인기 사이트만 계속 강해집니다. 그래서 클릭 데이터는 보조 신호로만 사용하는 것이 좋습니다. 예를 들어: const popularityBoost = Math.log(site.clicks + 1); 처럼 로그 스케일을 적용하면 클릭 수가 큰 사이트가 점수를 독점하는 것을 어느 정도 줄일 수 있습니다. 9. 최신성도 하나의 신호가 될 수 있다 웹 디렉터리에서는 오래된 사이트와 최근에 추가된 사이트가 함께 존재합니다. 최근 확인된 사이트를 약간 우선할 수도 있습니다. function freshnessScore(lastChecked) { const days = (Date.now() - new Date(lastChecked)) / (1000 * 60 * 60 * 24); if (days < 7) return 2; if (days < 30) return 1; return 0; } 하지만 최신성이 항상 품질을 의미하는 것은 아닙니다. 그래서 이 역시 작은 보너스 정도로 사용하는 편이 안전합니다. 10. 최종 점수 구조 이제 여러 신호를 합치면 다음과 같은 형태가 됩니다. function finalSearchScore(site, query) { const relevance = calculateSearchScore(site, query); const popularity = Math.log(site.clicks + 1); const freshness = freshnessScore(site.lastChecked); return ( relevance * 0.8 + popularity * 0.15 + freshness * 0.05 ); } 중요한 점은 숫자 자체가 아닙니다. 이 비율은 서비스의 목적에 따라 달라져야 합니다. 11. 웹 디렉터리 검색은 검색엔진과 목적이 다르다 일반 검색엔진은 수십억 개의 문서에서 답을 찾습니다. 웹 디렉터리는 훨씬 작은 데이터셋 안에서 좋은 선택지를 발견하도록 돕는 것이 목적입니다. 그래서 웹 디렉터리 검색에서는 다음 요소가 더 중요할 수 있습니다. Exact Site Match Category Relevance Tag Relevance Site Quality Freshness Result Diversity 검색 결과를 무조건 하나의 정답으로 만들기보다 사용자가 비교할 수 있는 선택지를 보여주는 것이 중요합니다. 실제 프로젝트: Radar Link Site 이 검색 구조를 고민하게 된 이유는 제가 운영하고 있는 Radar Link Site 때문입니다. Radar Link Site는 여러 분야의 웹사이트를 카테고리별로 탐색하고 비교할 수 있도록 만든 웹 디렉터리입니다. 현재 제가 가장 중요하게 보는 흐름은 다음과 같습니다. Search ↓ Discover ↓ Compare ↓ Visit 정확한 사이트 이름을 알고 있는 사용자는 검색을 사용할 수 있고, 어떤 사이트가 있는지 모르는 사용자는 카테고리에서 탐색할 수 있습니다. 결국 검색과 카테고리는 경쟁 관계가 아니라 서로 다른 사용자 의도를 해결하는 두 개의 탐색 방식입니다. Radar Link Site 👉 radarlinksite.com 커뮤니티, AI 도구, 쇼핑, 구인구직, 금융·재테크 등 다양한 분야의 웹사이트를 카테고리별로 탐색할 수 있도록 구성하고 있습니다. 웹 디렉터리를 만들고 있다면 사이트를 얼마나 많이 수집할 것인가만큼, 사용자가 원하는 사이트를 어떤 순서로 보여줄 것인가 도 중요한 설계 문제입니다. 마무리 웹 디렉터리 검색 랭킹을 처음 만든다면 다음 순서로 시작할 것 같습니다. 1. Exact name match 2. Partial name match 3. Category 4. Tags 5. Description 6. Click data 7. Freshness 8. Result diversity 처음부터 복잡한 검색엔진을 만들 필요는 없습니다. 작은 데이터셋에서는 단순한 가중치 기반 점수만으로도 충분히 좋은 결과를 만들 수 있습니다. 그리고 실제 검색 로그가 쌓이면 그때 가중치를 조금씩 조정하면 됩니다. 결국 좋은 검색은 단순히 결과를 많이 보여주는 것이 아니라, 사용자가 다음 클릭을 고민하는 시간을 줄여주는 것이라고 생각합니다. AI Assistance Disclosure: 이 글은 실제 웹 디렉터리 운영과 검색 구조 아이디어를 기반으로 작성했으며, AI를 초안 구성과 문장 정리에 보조적으로 사용했습니다. 게시 전 코드와 기술 내용을 직접 검토하고 수정했습니다. webdev #javascript #search #seo

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News