Django에서 검색어 하이라이트 구현하기

Kangjik Kim·2025년 7월 1일
0

검색 기능을 구현할 때 사용자 경험을 향상시키는 중요한 요소 중 하나는 검색 결과에서 검색어를 시각적으로 강조하는 것입니다. 오늘은 Django에서 검색어 하이라이트 기능을 구현하는 방법을 알아보겠습니다.

1. 기본 검색 기능 구현

먼저 간단한 검색 폼과 결과를 보여주는 기본 구조를 만듭니다:

{# templates/search.html #}
<form method="get">
    <select name="search_type">
        <option value="title">제목</option>
        <option value="content">내용</option>
        <option value="all">제목+내용</option>
    </select>
    <input type="text" name="search_keyword" value="{{ search_keyword }}" placeholder="검색어">
    <button type="submit">검색</button>
</form>

<div class="search-results">
    {% for item in results %}
        <div class="result-item">
            <h3>{{ item.title }}</h3>
            <p>{{ item.content }}</p>
        </div>
    {% endfor %}
</div>

2. 검색어 하이라이트 필터 만들기

templatetags 디렉토리를 생성하고 하이라이트 필터를 구현합니다:

# myapp/templatetags/highlight.py
from django import template
from django.utils.safestring import mark_safe
import re

register = template.Library()

@register.filter
def highlight(text, search):
    """
    텍스트 내의 검색어를 하이라이트 처리하는 필터

    Args:
        text: 원본 텍스트
        search: 하이라이트할 검색어
    """
    if not search or not text:
        return text

    # 특수문자가 포함된 검색어 처리를 위한 이스케이프
    search = re.escape(search)
    # 대소문자 구분 없이 검색어 매칭
    pattern = re.compile(f'({search})', re.IGNORECASE)
    # 매칭된 부분을 <strong> 태그로 감싸기
    highlighted = pattern.sub(r'<strong>\\1</strong>', str(text))

    return mark_safe(highlighted)

3. 템플릿에 하이라이트 적용하기

{% extends 'base.html' %}
{% load highlight %}  {# 커스텀 필터 로드 #}

{% block content %}
    <form method="get">
        <select name="search_type">
            <option value="title" {% if search_type == 'title' %}selected{% endif %}>제목</option>
            <option value="content" {% if search_type == 'content' %}selected{% endif %}>내용</option>
            <option value="all" {% if search_type == 'all' %}selected{% endif %}>제목+내용</option>
        </select>
        <input type="text" name="search_keyword" value="{{ search_keyword }}" placeholder="검색어">
        <button type="submit">검색</button>
    </form>

    <div class="search-results">
        {% for item in results %}
            <div class="result-item">
                {# 검색 타입에 따라 하이라이트 적용 #}
                {% if search_type == 'title' or search_type == 'all' %}
                    <h3>{{ item.title|highlight:search_keyword }}</h3>
                {% else %}
                    <h3>{{ item.title }}</h3>
                {% endif %}

                {% if search_type == 'content' or search_type == 'all' %}
                    <p>{{ item.content|highlight:search_keyword }}</p>
                {% else %}
                    <p>{{ item.content }}</p>
                {% endif %}
            </div>
        {% endfor %}
    </div>
{% endblock %}

4. 스타일 적용하기

검색어 하이라이트를 시각적으로 돋보이게 만듭니다:

/* static/css/style.css */
.search-results .result-item strong {
    background-color: #fff3cd;  /* 연한 노란색 배경 */
    padding: 2px 4px;
    border-radius: 2px;
    font-weight: bold;
    color: #856404;  /* 진한 갈색 텍스트 */
}

/* 또는 다른 스타일 옵션 */
.search-results .result-item strong {
    background: linear-gradient(transparent 60%, #ffd700 60%);  /* 밑줄 형태의 하이라이트 */
    padding: 0 2px;
}

5. View 구현

from django.views.generic import ListView
from django.db.models import Q

class SearchView(ListView):
    template_name = 'search.html'
    context_object_name = 'results'

    def get_queryset(self):
        queryset = YourModel.objects.all()
        search_type = self.request.GET.get('search_type', '')
        search_keyword = self.request.GET.get('search_keyword', '')

        if search_keyword:
            if search_type == 'title':
                queryset = queryset.filter(title__icontains=search_keyword)
            elif search_type == 'content':
                queryset = queryset.filter(content__icontains=search_keyword)
            elif search_type == 'all':
                queryset = queryset.filter(
                    Q(title__icontains=search_keyword) |
                    Q(content__icontains=search_keyword)
                )

        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['search_type'] = self.request.GET.get('search_type', '')
        context['search_keyword'] = self.request.GET.get('search_keyword', '')
        return context

6. 주요 특징

  1. 대소문자 구분 없음
    • re.IGNORECASE 플래그를 사용하여 대소문자 구분 없이 검색어 매칭
  2. 특수문자 처리
    • re.escape()를 사용하여 특수문자가 포함된 검색어도 안전하게 처리
  3. HTML 안전성
    • mark_safe()를 사용하여 HTML 태그가 이스케이프되지 않도록 처리
    • 검색어만 하이라이트되고 다른 HTML 태그는 그대로 유지
  4. 조건부 하이라이트
    • 검색 타입에 따라 제목 또는 내용에만 하이라이트 적용

7. 성능 고려사항

  1. 데이터베이스 쿼리
    • 검색은 데이터베이스 단에서 처리
    • 하이라이트는 템플릿 렌더링 시점에서 처리
  2. 캐싱
    • 검색 결과가 자주 바뀌지 않는다면 캐싱 고려
    • 하이라이트 처리된 결과를 캐시할 수 있음

마무리

검색어 하이라이트는 작은 기능이지만 사용자 경험을 크게 향상시킬 수 있습니다. Django의 템플릿 필터 시스템을 활용하면 이러한 기능을 깔끔하게 구현할 수 있습니다.

더 나아가 다음과 같은 개선사항을 고려해볼 수 있습니다:

  • 여러 검색어 동시 하이라이트
  • 검색어 주변 컨텍스트만 표시
  • 정규식을 사용한 더 복잡한 검색어 매칭

참고 자료

0개의 댓글