개발일기

검색기능 구현하기 본문

Project Portfolio

검색기능 구현하기

츄98 2023. 4. 15. 12:32

그럼 이제 검색기능을 구현해보자!

 

검색 기능의 아이디어는, 검색키워드를 받고 그 키워드가 담겨있는 게시글을 보여주거나,

키워드를 가진 게시글이 없다면 혹은 검색어를 입력하지 않은 경우에는 메세지를 띄워주는 것으로 구현을 했다.

 

구체적으로 코드를 보면서 어떻게 구현했는지 살펴보자.

 

<post views.py>

def search(request):
    if request.method == 'GET':
        searched = request.GET.get("searched","")     
        posts = Posting.objects.filter(content__contains=searched).order_by('-created_at')
        if posts.exists() == False:
            return render(request, 'posting/searched.html', {'searched': searched, 'error': "찾으시는 검색어를 가진 글은 존재하지 않습니다."}) 
        count = posts.count()
        page = int(request.GET.get('page','1'))
        paginator = Paginator(posts,5)
        post_list = paginator.get_page(page)
        return render(request, 'posting/searched.html', {'searched': searched, "post_list": post_list, 'count':count})
    else:
        return render(request, 'posting/searched.html', {})

 

<코드 자세히 살펴보기>

  • posts = Posting.objects.filter(content__contains=searched).order_by('-created_at')
  • 최신순으로 정렬되면서, content__contains=searched의 의미는, 게시글에 검색어가 포함되어있을 경우 가지고오라는 의미이다.
  • if posts.exists() == False: 만약 검색결과가 존재하지 않는다면, 에러메세지를 보여주었다.
  •  count = posts.count() : 검색결과의 개수를 세서 보여주었다.
  • 검색결과를 보여주는 페이지 역시 페이징을 써서 한 페이지 당 5개의 게시글을 보여주도록 코드를 짰다.
  • 페이징과 관련되어서는 이전 글을 참고해주길 바란다!

 

 

<base.html>

<form class="d-flex" method="GET" action="{% url 'search' %}">
    {% comment %} {% csrf_token %} {% endcomment %}
    <input class="form-control me-sm-2" type="search" placeholder="Search" name="searched" aria-label="Search">
    <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
</form>

 

 

<searched html>

{% if searched %}
    {% if error %}
    <div class="alert alert-danger" role="alert">{{ error }}</div>
    {% else %}       
        <h4> 검색하신 {{ searched }} 정보입니다. 총 {{count}}개의 검색결과가 있습니다.</h4>
        <p></p>
        {% for ps in post_list %}
        <div class="col-md-12 mb-2">
            <div class="card">
                <div class="card-body">
                    {% if user == ps.author or user.is_superuser %}
                    <div style="text-align: right">
                        <a href="/api/posts/delete/{{ ps.id }}">
                            <span class="badge rounded-pill bg-danger">삭제</span>
                        </a>
                    </div>
                    {% endif %}
                    <div style="text-align: right">
                        <a href="/api/posts/{{ ps.id }}">
                            <span class="badge rounded-pill bg-success">보기</span>
                        </a>
                    </div>
                    <div class="media">
                        <div class="media-body">
                            <h5 class="mt-0">Title: {{ ps.title }}</h5>
                            <p>{{ ps.content|slice:":15"}}...</p>
                            <p>Category: {{ ps.category }}</p>
                        </div>
                        <div style="text-align: right">
                            <span style="font-size: small">{{ ps.author_name }}-{{ ps.created_at|timesince }} 전</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        {% endfor %}
        <!--페이징 처리 코드는 생략-->
     {% endif %}
{% else %}
    <h3> 검색어를 입력해주세요. </h3>
{% endif %}

 

<코드 자세히 살펴보기>

  • {% if searched %} 검색어가 있다면
  • {% else %} 검색어가 없다면<h3> 검색어를 입력해주세요. </h3> 
  • 검색어가 있지만 검색결과가 없을 경우 {{error}} 를 보여준다.
  • views.py 참고: {'searched': searched, 'error': "찾으시는 검색어를 가진 글은 존재하지 않습니다."}) 
  • 만약 로그인한 사용자와 글쓴이가 같을 경우, 혹은 관리자일 경우, 삭제가 가능하게 했다.
{% if user == ps.author or user.is_superuser %}
<div style="text-align: right">
    <a href="/api/posts/delete/{{ ps.id }}">
        <span class="badge rounded-pill bg-danger">삭제</span>
    </a>
</div>
{% endif %}

 

  • 게시글은, 제목과 글 내용, 카테고리, 저자, 시간이 보이도록 했다.
  • 이때 글 내용은 15자까지만 보이도록 slice문을 추가했다.
<h5 class="mt-0">Title: {{ ps.title }}</h5>
<p>{{ ps.content|slice:":15"}}...</p>
<p>Category: {{ ps.category }}</p>

 

 

검색기능을 하면서 고민했던 부분은, 검색결과를 유지하면서 어떻게 페이징을 구현할 수 있을까 하는 부분이었다.

원래는 검색기능을 post 메서드로 구현했었는데, 페이징과 함께 구현을 하려고 하다보니 get 메소드로 바꾸게 되었다.

get 메소드로 할 경우 쿼리 파라미터로 구현할 수 있기 때문에, 페이지를 넘기더라도 검색결과가 유지될 수 있다.

 

<div style="text-align:center">
    <ul class="pagination">
        <div style="width:35%; margin: 5px;">
            {% if post_list.has_previous %}
                <a class="abutton" href="?searched={{request.GET.searched}}&page=1">맨 앞으로</a>
                <a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.previous_page_number }}">이전</a>
            {% endif %}
        </div>
        <div style="width:30%; margin: 5px;">
            {% for page in post_list.paginator.page_range %}
                {% if page >= post_list.number|add:-2 and page <= post_list.number|add:2 %}
                <span class="{% if page == post_list.number %}current{% endif %}">
                    <a href="?searched={{request.GET.searched}}&page={{ page }}">{{ page }}</a>
                </span>
                {% elif page >= post_list.number|add:-3 and page <= post_list.number|add:3 %}
                    ..
                {% endif %}
            {% endfor %}
        </div>
        <div style="width:35%; margin: 5px;">
            {% if post_list.has_next %}
                <a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.next_page_number }}">다음</a>
                <a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.paginator.num_pages }}">맨 뒤로</a>
            {% endif %}
        </div>
    </ul>
</div>

 

<코드 자세히 살펴보기>

  • <a class="abutton" href="?searched={{request.GET.searched}}&page=1">
  • 검색어를 유지하면서 페이징을 구현할 수 있다.
  • 나머지 코드들은 페이징 구현하기 이전 게시글에서 충분히 설명했기 때문에 설명을 생략하도록 하겠다.

 

<Result>

- 검색결과가 없을 때

- 검색결과가 있을 때

 

- 검색어가 없을 때

- 로그인한 유저가 쓴 글이 검색결과에 있을 때 (삭제 버튼이 보인다.)