[Flask] 3-14 검색

2023. 12. 3. 22:37Web/Flask

728x90

문답변에 대한 데이터가 계속 쌓여가는 게시판이므로

검색기능은 필수라고 할 수 있다

검색의 대상은 제목, 질문의 내용, 질문 작성자, 답변의 내용, 답변 작성자 정도로 하면 적당할 것이다

"파이썬"이라고 검색을 하면 "파이썬" 이라는 문자열이

제목, 내용, 질문 작성자, 답변, 답변 작성자에 존재하는지 찾아보고

그 결과를 화면에 보여주어야 한다

 

검색 기능을 위해 잠시 데이터베이스 지식을 공부할 필요가 있다

조인, 아우터조인, 서브쿼리에 대해서 알아볼 것이다

 

조인

조인은 동일한 데이터로 연결된 두 모델을 함께 조회할 때 사용한다

 예를들어 작성자 이름이 "홍길동"인 질문을

검색할 수 있는 방법에 대해서 생각해 보면

 

절차 1. User 모델에서 username이 '홍길동'인 데이터의 id 조사하기

절차 2. 절차 1에서 조사한 id와 Question 모델의 user_id가 같은 데이터인지 조사하기

 

코드를 작성하면 아래와 같다

하지만 조인을 사용하면 아래와 같이 간편하게 검색할 수 있다

이 코드는 Question 모델과 User 모델을 join 함수로 조인한 것이다

조인은 일종의 교집합 역할을 한다

조인을 하면 이후 filter 함수로 User.username이 '홍길동'인 Question 모델 데이터를 얻을 수 있다

 

아우터조인

아우터조인은 조금 복잡하다

플라스크 셸을 실행해 보자

Question 모델과 Answer 모델을 사용하도록 다음과 같이 코드를 입력하자

Question 모델의 데이터를 모두 검색하자

필자의 경우 Question 모델의 데이터가 300건 조회되었다

count() 함수는 실행쿼리의 건수를 조회하는 함수이다

같은 방법으로 Answer 모델의 데이터 개수를 알아보자

Answer 모델의 데이터는 11개이다

 

위에서 배운 조인을 사용해 보자

Question과 Answer를 조인한 이후의 데이터 개수이다

여기서는 데이터를 11개 얻을 수 있다

 

아우터조인을 설명하기 전에 생각해야 할 내용이 있다

만약 "파이썬"이라는 문자열을 포함한 질문 또는 답변을 찾아 조회하고 싶다면

위에서 설명한 조인을 사용하면 안 된다

이유는 답변이 없는 질문은 모두 제외되기 때문이다

비록 답변이 없는 질문이라도 질문 내용에 "파이썬"이 포함되어 있으면

검색 결과에 포함되어야 상식적이다

 

바로 아우터조인(outerjoin)으로 이 문제를 해결할 수 있다

아래 아우터조인을 사용한 결과이다

질문 개수인 300보다 더 큰 숫자인 309가 나타났다

9만큼 증가한 값이 나타난 이유는 "질문"에 등록된 답변이 1개 이상인 경우가 있기 때문이다

총 질문 개수인 300에 답변 개수가 1개 이상인 데이터 9개가 더해져 총 309가 나오는 것이다

 

조회된 질문 질문에 달린 답변 중복 여부
1 질문 1에 달린 답변1 중복
1 질문 1에 달린 답변2 중복
2 질문 2에 달린 답변1 중복
2 질문 2에 달린 답변2 중복
3 답변 없음 중복 아님
4 답변 없음 중복 아님
309 답변 없음 중복 아님

하지만 우리가 필요한 데이터는 중복을 제외한 질문이므로 중복을 제거해야 한다

위에서 보듯이 하나의 질문에 여러개의 답변이 있는 경우 중복 데이터가 발생했다

중복을 제거하려면 distinct 함수를 사용해야 한다

distinct 함수를 사용하면 중복 데이터가 제거되므로 데이터는 300개 검색된다

 

이처럼 아우터조인은 기준 모델(Question)의 데이터가 조회 대상에서 제외되지 않는다

아우터조인을 하면 조인 대상인 Answer 모델로 검색할 수 있는 효과가 있다

이 코드는 질문 내용 또는 답변 내용에 "파이썬"이라는 문자열이 있는 질문을 조회한다

아우터조인을 사용했으므로 "답변이 없는 질문"도 검색대상에 포함된다

 

여기서 사용한 ilike 함수는 대소 문자를 구분하지 않고 데이터를 검색한다

검색어 앞뒤에 있는 % 기호는 "파이썬"이라는 문자열 앞뒤에 추가된 문자열이 있어도 상관없다는 의미이다

 

서브쿼리

Question 모델과 연결된 다른 모델을 검색하려면 Answer, User 모델을 조인(또는 아우터조인)해야 한다

 이렇게 모델 자체를 사용하기보다는 필요한 데이터만 모아서 조회하는 서브쿼리를 만들어 조인해야 할 필요도 있다

 

검색 기능을 구현하려면 답변 내용뿐 아니라 답변 작성자도 검색 조건에 포함해야 한다

서브쿼리는 아래처럼 작성할수 있다

서브쿼리는 답변 모델과 사용자 모델을 조인하여 만든 것으로

검색 조건에 사용할 답변 내용 Answer.content과 답변 작성자

User.username가 쿼리 조회 항목으로 추가되었다

이 서브쿼리와 질문 모델을 연결할수 있는 질문 id에 해당하는

Answer.question_id도 조회 항목에 추가되었다

 

서브쿼리를 생성하면 아래와 같이 Question 모델과 서브쿼리를 아우터조인할 수 있다

sub_query.c.question_id에 사용한 c는 서브쿼리의 조회 항목을 의미한다

sub_query.c.question_id는 서브쿼리의 조회 항목 중 question_id를 의미한다

sub_query를 아우터조인했으므로 sub_query의 조회 항목을 filter 함수에 조건으로 추가할 수 있다

이 코드로 답변 내용 또는 답변 작성자 이름에서 "파이썬" 문자열을 포함하는 질문 목록을 조회할 수 있다

 

GET

검색 기능을 구현하기 전에 검색 기능을 GET 방식으로 구현해야 하는 이유에 대해 알아보자

아래 앞에서 kw를 사용한 코드의 일부이다

kw는 다음처럼 GET방식으로 전달되어야 index함수에서 읽을 수 있다

POST 방식으로 검색, 페이징 기능을 만들면 웹 브라우저에서

"새로고침" 또는 "뒤로가기"를 했을 때 "만료된 페이지" 오류를 종종 만난다

POST 방식은 같은 POST 요청이 발생하면 중복을 방지하기 때문이다

 

검색 기능 만들어 보기

이제 화면에서 kw와 page를 동시에 GET방식으로 호출하는 방법에 대해서 알아보자

 

검색 창

아래와 같이 질문 목록 화면에 검색 창을 추가하자

[파일명: projects\myproject\pybo\templates\question\question_list.html]

<table> 태그 상단 우측에 검색어를 입력할 수 있는 텍스트창을 생성하였다

 맨 밑에 있던 "질문 등록하기" 버튼은 검색 창의 좌측으로 이동했다

 

자바 스크립트에서 이 텍스트창에 입력된 값을 읽기 위해 다음처럼 id 속성을 추가한 점에 주목하자

 

검색 폼

page와 kw를 동시에 GET 방식으로 요청할 수 있는 form 엘리먼트를 추가하자

[파일명: projects\myproject\pybo\templates\question\question_list.html]

GET 방식으로 요청해야 하므로 method 속성에 "get"을 설정했다

kw와 page는 이전에 요청했던 값을 기억해야 하므로 value 속성에 그 값을 대입했다

kw와 page의 값은 목록 조회 함수에서 전달할 것이다

form 엘리먼트의 action 속성은 "폼이 전송되는 URL"이므로 목록 조회 URL인 url_for('question._list')를 지정했다

 

페이징

그리고 기존의 페이징 처리 방식도 ?page=1 에서 값을 읽어 요청하는 방식으로 변경해야 한다

이유는 검색과 페이징이 동시 처리되려면 위에서 작성한 form을 통해 페이징이 요청되어야 하기 때문이다

[파일명: C:\projects\myproject\pybo\templates\question\question_list.html]

모든 페이지 링크를 href 속성에 직접 입력하는 대신 data-page 속성으로 값을 읽을 수 있도록 변경했다

 

검색 스크립트

페이징과 검색을 요청하는 자바스크립트 코드를 다음과 같이 추가하자

검색버튼을 클릭하는 경우는 새로운 검색에 해당되므로

page에 항상 1을 설정하여 요청하도록 했다

 

검색 함수

이제 검색어가 조회에 적용될 수 있도록 question_views.py 파일의 _list 함수를 아래처럼 수정하자

[파일명: projects\myproject\pybo\views\question_views.py]
[파일명: projects\myproject\pybo\views\question_views.py]

이 코드는 화면에서 전달받은 검색어(kw)가 있으면

질문 제목, 질문 내용, 질문 작성자, 답변 내용, 답변 작성자 항목에서

OR 조건으로 검색한 질문을 반환한다

입력으로 받은 page와 kw의 값을 템플릿으로 전달하도록 render_template 함수에 추가했다

 

검색 확인

검색 창에 "마크다운"이라고 검색어를 입력한 다음 <찾기> 버튼을 눌러 보자

아래와 같이 "마크다운" 문자열을 포함하는 질문이 검색될 것이다

정상 동작 확인

728x90

'Web > Flask' 카테고리의 다른 글

[Flask] 3-13 마크다운 기능 적용하기  (0) 2023.12.03
[Flask] 3-12 앵커  (1) 2023.12.03
[Flask] 3-11 추천 기능 추가하기  (2) 2023.12.03
[Flask] 3-10 게시물 수정 & 삭제  (2) 2023.12.03
[Flask] 3-9 글쓴이 표시하기  (1) 2023.12.03