Framework/Django

[Dj] 장고 검색창 기능 구현하기

QUERY 2021. 8. 20. 04:50

 


장고-로고
장고 로고


장고에서 검색창을 만들고, 검색 기능을 구현하는 방법에 대해서 알아보겠습니다. 작동 원리는 간단합니다. 크게 두 가지 원리만 이해하고 있으면 됩니다. 첫째, 검색창에 검색어를 입력하고 검색 버튼을 눌렀을 때, 검색 결과를 보여줄 html 페이지와 해당 html 페이지의 url이 필요하고, 둘째, 검색창에 입력한 내용이 실제 웹사이트의 데이터베이스에 있는지 확인한 뒤, 그 결과를 html 페이지에 반환해주면 됩니다. 글로 읽어선 한 번에 이해가 되지 않을 수 있으니, 단계별로 차근차근 풀어가 보도록 하겠습니다.

 


검색창 기능을 구현하기에 앞서, 검색창 샘플을 만들어보도록 하겠습니다. 검색창 구현에 앞서 준비물이 필요합니다. 사전에 사용자가 입력한 검색어를 찾아볼 수 있는 데이터베이스 모델이 세팅돼 있어야 합니다. 설명의 이해를 돕기 위해 요리법을 검색할 수 있는 요리 웹사이트를 만든다고 가정하고, 간단히 Recipe(요리법) 모델을 만들어보겠습니다.

>>> config/models.py

from django.db import models


class Recipe(models.Model):
        name = models.CharField(max_length=100)
        country = models.CharField(max_length=100)
        ingredient = models.TextField()

        def __str__(self):
                return self.name 

 

 

또한, 검색창 제작을 위해 bootstrap5가 사용되었습니다. 부트스트랩 5의 사용방법은 다른 포스팅을 참고하시기 바랍니다.

navbar-검색창과-검색버튼-예시
navbar의 검색창과 검색버튼

>>> nav 부분        # 위치는 기호에 따라 base.html에 위치할 수도 있고, navbar.html로 따로 관리할 수도 있습니다.

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="#"> Navbar </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item"><a class="nav-link active" aria-current="page" href="#"> Home </a></li>
                <li class="nav-item"><a class="nav-link" href="#"> Link </a></li>
            </ul>
            <form class="d-flex" method="POST" action="{% url 'search' %}">
            {% csrf_token %}        
            <input class="form-control me-2" type="search" placeholder="Search" name="searched" aria-label="Search">
            <button class="btn btn-outline-success" type="submit"> Search </button>
        </form>

        </div>
    </div>
</nav>

굵은 글씨로 적어놓은 form태그 부분이 바로 검색창과 검색 버튼입니다. (참고. # 장고에서 POST 메서드를 사용할 땐, 해킹 방지를 위해 반드시 csrf_token를 사용해야 합니다.)

 


 


 

앞서 언급했듯이 Search 버튼을 눌렀을 때, 결괏값을 반환해줄 html 페이지를 만들어야 하고, 해당 페이지의 url 주소를 설정해주어야 합니다. 

>>> searched.html        # 이름은 마음대로 정하셔도 상관없습니다.
                                         # 추후에 다시 수정을 해야 됩니다. 일단 지금은 파일만 만들어놓겠습니다.

{% extends 'base.html' %}        # 각자의 상황에 맞게 'base.html' 부분은 변경하시면 됩니다.
{% block content %}
        
{% endblock content %}
>>> config/urls.py        # 여기서 config는 settings.py가 위치한 폴더를 의미합니다.

from django.contrib import admin
from django.urls import path, include
from .views import search        # 아직 search 함수를 만들기 전이기 때문에, url을 지정한 뒤, search 함수를 만들어줍니다.

urlpatterns = [
        path('admin/', admin.site.urls),
        path('', views.search, name='search'),
]

여기까지 하면 검색창 기능 구현의 원리 중, 첫 번째 단계를 모두 마무리한 것입니다. 계속 이어서 search 함수를 만들고, 해당 함수를 통해 사용자가 검색하는 내용이 우리의 데이터베이스에 있는지 여부를 판단하고, 그 결괏값을 반환해주는 기능을 구현해보도록 하겠습니다.

 

 

이때, 장고 프로젝트에 어떠한 앱도 설치하지 않았을 경우, settings.py가 있는 폴더 안에 views.py 파일을 생성해줍니다. 만약, 설치한 앱이 있는 경우, 해당 앱과 관련된 url들은 해당 앱 안에 urls.py 파일을 만들어 따로 관리해줄 수 있습니다. 하지만 이번 포스팅에선 url 설정이 주가 아니고, 검색창의 작동 원리에 대한 이해가 우선이기 때문에 자세한 언급은 하지 않고 넘어가도록 하겠습니다. 우리는 추가적으로 앱을 설치하지 않았다는 전제 하에, settings.py가 있는 폴더 안에 views.py를 만들어보도록 하겠습니다.

>>> config/views.py        # settings.py가 있는 폴더 안에 views.py를 만들었습니다.

from django.shortcuts import render
from .models import Recipe


def search(request):
        if request.method == 'POST':
                searched = request.POST['searched']        
                recipes = Recipe.objects.filter(name__contains=searched)
                return render(request, 'searched.html', {'searched': searched, 'recipes': recipes})
        else:
                return render(request, 'searched.html', {})

만약 검색 버튼이 POST 방식이면, 검색창에 입력된 내용을 searched라는 변수로 받고, 그 변수가 name__contains 즉, 데이터베이스에 사용자가 검색한 내용이 포함된 object가 있는지 찾아서 그 찾은 내용을 recipes라는 변수에 담아 searched.html 페이지에 딕셔너리 형태로 반환해주겠다는 의미입니다. 만약, 검색 버튼이 POST 방식이 아닌 경우엔, 빈 딕셔너리를 searched.html에 반환해주라는 의미입니다.

이때, request.POST[''] 안에 있는 searched는 검색창을 구현했던 nav 부분에서 form 태그의 input 태그에 있는 name=searched의 searched를 의미합니다.

 


 


 

자, search 함수에서 정의했듯이, 만약 사용자가 검색어를 입력한 뒤, 검색 버튼을 눌렀다면, searched와 recipes 값을 searched.html에서 사용할 수 있게 됩니다. 그럼 넘겨받은 값들을 이용해 searched.html을 완성해보도록 하겠습니다.

>>> searched.html

{% extends 'base.html' %}

{% block content %}
        {% if searched %}        # 만약, 검색창에 입력한 값이 있다면 이라는 의미입니다.
                <h1> 검색하신 {{ searched }} 레시피 정보입니다. </h1>
                <br>
                {% for recipe in recipes %}        # 검색 결과가 여러 개일 수 있기 때문에 반복문을 사용했습니다. 
                        {{ recipe.name }} <br>
                        {{ recipe.country }} <br>
                        {{ recipe.ingredient }} <br>
                {% endfor %}
        {% else %}        # 검색창에 아무것도 입력하지 않았을 경우 뜨는 메시지입니다.
                <h1> 찾고 있는 레시피를 검색창에 입력해주세요. </h1>
        {% endif %}
{% endblock %}

위 예시(searched.html)는 검색창에 입력한 검색 결괏값을 넘겨받아 페이지에 표현하는 기능적인 부분에 초점을 맞췄기 때문에 디자인은 전혀 고려되지 않았다는 점 참고 부탁드립니다. {% else %} 부분은, 만약 사용자가 검색창에 아무것도 입력하지 않았거나, 혹은 해당 url로 바로 접속하려고 했을 경우를 대비해서 작성한 코드입니다.

 


지금까지 검색창의 작동 원리와 검색창을 구현하는 순서에 대해 알아보았습니다. 검색창이 어떤 식으로 작동하는지 그 원리만 알고 있다면, 나머지 부분은 본인의 기호에 맞게 추가/수정/보완해서 사용하시면 됩니다.