검색 기능 고도화를 어떻게 하면 좋을까?
일반적으로 django에서 검색 기능을 만든다고 하면
대부분은 아래와 같이 만든다.

Product라는 모델이 있고 name을 기준으로 검색을 한다고 하면

search_text <= 이건 검색하는 단어

product_list = Product.objects.filter(name__icontains=search_text)

대부분 이렇게 검색 필터를 만들 것이다.

하지만 여기에는 치명적인 단점들이 존재한다.
예를 들면 Product의 이름이 "아이폰 14,128G, 실버" 라고 한다면
누군가를 "아이폰14"로 붙여서 검색을 한다면 결과에 잡히지 않는다.
또 "아이폰 실버"라고 검색해도 나오지 않는다.

문제는 여기서 끝이 아니다.
"맥북m2", "애플워치se2" 이런 영어와 숫자까지 함께 들어간다면,
어떻게 해야할까? 

해당 텍스트와 정확한 이름(띄어쓰기포함)이 없으면
그냥 결과는 아무것도 나오지 않는다.

"맥북m2"를 검색한 고객의 의도는 무엇일까? 
맥북프로 m2 모델을 검색하고 싶었을 수도 있고 맥북에어 m2 모델을 검색하고 싶었을 수도 있다. 
또 그것 뿐만 아니라 16인지 m2 모델도 있고, 14인치 m2 모델이 있을 수도 있다.

고객의 의도를 알 수 없는 우리는 최대한 많은 검색 결과를 알려줘야 하는데
위의 로직대로 구현을 해버리면 아무 검색 결과도 나오지 않게된다.

왜나하면 상품명은
Apple 2022 맥북에어, 실버, m2, 8G
애플 2021 맥북프로 16, 실버, m2 
이런 형태로 되어 있기 때문이다.

그럼 우리가 취해야 하는 것은 무엇일까?
일단 구글에 더 좋은 방법으로 한 것이 없을까 열심히 서칭을 해봤지만,
나오지 않았다.

그래서 다음에 생각한 것은 Product에 검색할 때 이용할 검색 참고 field를 하나 만들어서,
Apple 2022 맥북에어, 실버, m2, 8G의 해당 필드에 
맥북에어, 맥북m2, 맥북 실버, 2022 맥북 등 검색에 쓰일만한 텍스트들을 모두 넣어두는 것이다.
그래서 제품명 + 해당 필드에 있는 내용을 활용해서, 검색기능을 고도화 시키는 것이다.
그럼 유사한 keyword에도 검색결과가 잡히게 할 수 있으니 좋은 방법이기도 하다!
(실제로 커머스에서 많이 사용하는 방법이기도 하다)

하지만 상품이 많다면... 그리고 처음부터 도입시켜 놓지 않았다면
막막할 수 밖에 없다. 그리고 해당 검색 field에 모든 예시들을 넣어놓을 수 없을테니깐 이슈가 또 발생할 것 같았다.

그럼 현재 상황에서 가장 간단하게 최선의 방법을 선택하면 어떻게 하면 될까?
머리를 돌려보았다.

내가 생각했던 방법은 아래와 같다.
실제 고객이 검색하고 아무런 결과값을 보내지 못 했던 검색어가 아래에 있다.

"맥북m2", "애플워치se2" 

결국 찾고 싶은 것은
"맥북", "m", "2"가 모두 포함된 제품의 이름을 찾으면 되지 않을까?

애플워치se2는
"애플워치", "se", "2"가 모두 포함된 제품의 이름을 찾으면 되지 않을까?

완벽하지 않지만 당장에는 이렇게 하게 되면 
"맥북m2"라고 검색을 해도

Apple 2022 맥북에어, 실버, m2, 8G
애플 2021 맥북프로 16, 실버, m2 

이 2개의 제품이 모두 검색되게 할 수 있을 것이다.
왜냐하면 위의 제품명에는 맥북이라는 글자와 m이라는 글자, 2라는 글자 모두 포함되어 있기 때문이다.
(이 예시를 보면 m2를 합쳐서 생각하면 더 좋을 것 같긴 하다. 하지만 더 고려해야 할 것들이 많으니 일단은 넘어가자)

이런 형태로 하게 되면 애플워치se2도 많은 부분 개선된다.
애플워치se2를 한글, 영어, 숫자로 나눠보면
"애플워치", "se", "2"로 나눌 수 있고 이것을 제품명에 모두 포함한 제품들은 아래와 같이 될 수 있다. 

'Apple 2022 애플워치 SE 2세대 알루미늄 케이스, 40mm, GPS, 실버 / 화이트 스포츠밴드',

 'Apple 2022 애플워치 SE 2세대 알루미늄 케이스, 40mm, GPS, 스타라이트 / 스타라이트 스포츠 밴드',
 'Apple 2022 애플워치 SE 2세대 알루미늄 케이스, 40mm, GPS+Cellular, 스타라이트 / 스타라이트 스포츠 밴드',
 'Apple 2022 애플워치 SE 2세대 알루미늄 케이스, 44mm, GPS+Cellular, 미드나이트 / 미드나이트 스포츠밴드',

처음에는 검색해서 나오지 않았던 결과값들이 그래도 나름 괜찮게 그리고 의미있게 결과값들을 뽑아낼 수 있게 된다!!

이까지 아이디어가 나왔으면 이제 우리가 할 일은
정규식만 서칭하면 된다!! 오예!!

한글 정규식

hangle = re.compile("[가-힣]+").findall(search_text)

영어 정규식

english = re.compile("[a-zA-Z]+").findall(search_text)

숫자 정규식

number = re.compile("[0-9]+").findall(search_text)


findall을 하면 list가 만들어지고

search_text = "애플워치se2"
hangle = ["애플워치"]
english = ["se"]
number = ["2"]

그럼 이렇게 된 것을 하나의 list에 넣어준다.

temp_search_text = ["애플워치", "se", "2"]

그럼 우리가 해야 하는 것은 이 list안에 것들이 모두 포함된 제품명을 찾으면 된다!!

그럼 이제 로직으로 보면

hangle = re.compile("[가-힣]+").findall(search_text)
english = re.compile("[a-zA-Z]+").findall(search_text)
number = re.compile("[0-9]+").findall(search_text)

temp_search_text = hangle + english + number
# temp_search_text = ["애플워치", "se", "2"]


product_list = Product.objects.filter(is_deleted=False)

for text in temp_search_text:
    product_list = product_list.filter(name__icontains=text)

result_product_list = []

for product in product_list:
    result_product_list.append(product)


쏘 심플!! django의 ORM 쿼리셋은 lazy loading이라 매번 호출하지 않는다.
실제로 제일 아래 product_list를 for문으로 돌 때 실행되기 때문에
저런 형태로 작성해도 걱정하지 않아도 된다!

그럼 Product의 Name을 기준으로 해당 나눠놓은 텍스트들이 모두 포함된 제품들을 반환할 수 있게 된다. 

DB 변경 없이 코드 몇 줄 변경으로 아주 좋은 효율의 Django 검색 필터를 만들 수 있다.

해당 로직들을 적용 전과 후


만들고 나니, 생각보다 괜찮은 방법인 것 같고,
좋은 아이디어인 거 같아서 공유하기 위해 글을 적는다.

django를 사용하는 주니어 개발자분들에게 도움이 되기를!!

테스트 코드의 작성은 한편으로 굉장히 크게 다가왔다.
아는 지인 개발자 분에게 들었는데, 아예 테스트 코드가 없으면
개발을 한 게 아니라고 본다고 하였다.

그렇다보니 스스로 테스트코드에 대해서 관심을 가졌으나,
직접 사용해보지 못하고 있었다.(물론 나의 학습 부족이 제일 컸다.)

앞으로는 이런 부분에 대해서 더 이상 미루지 않고
학습해서 부딪쳐보기로 하였다.

django에서 혹은 개발에서 테스트의 종류는 크게 3가지로 분류하고 있다.
참고 ( [Django 튜토리얼 파트 10: Django 웹 어플리케이션 테스트하기 - Web 개발 학습하기 | MDN](https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Testing))

  1. unit test(유닛테스트)
    1. 독립적인 class와 function 단위의 테스트
  2. Regression test(버그 수정 테스트)
    1. 발생하였던 버그에 대한 수정 테스트
  3. Integration test(통합테스트)
    1. 유닛 테스크를 완료한 각각의 독립적인 컴포넌트들이 함께 결합하여 수행하는 동작을 검증한다. 각 컴포넌트들의 내부적인 동작까지는 검증할 필요가 없다.
    2. 해석해보면 비즈니스 로직에 대한 검증인거 같다

구조 

app / tests /



__init__.py

test_models.py

test_forms.py

test_views.py

=> app아래에 tests라는 폴더를 만들고

해당 폴더 아래에 test관련된 파일들을 만든다.

 

실행

from django.test import TestCase



class YourTestClass(TestCase):

    @classmethod

    def setUpTestData(cls):

        print("setUpTestData: Run once to set up non-modified data             for all class methods.")

        pass


    def setUp(self):

        print("setUp: Run once for every test method to setup clean data.")

        pass

기본적으로 TestCase를 상속받아 만든 클래스는 2개의 메쏘드를 정의한다.

  • setUpTestData() 는 클래스 전체에서 사용되는 설정을 위해서 테스트 시작 때 딱 한 번만 실행됩니다. 테스트 메쏘드가 실행되면서 수정되거나 변경되지 않을 객체들을 이곳에서 생성할 수 있습니다.
  • setUp() 은 각각의 테스트 메쏘드가 실행될 때마다 실행됩니다. 테스트 중 내용이 변경될 수 있는 객체를 이곳에서 생성할 수 있습니다 (모든 테스트 메쏘드는 방금 막 생성된 ("fresh") 오브젝트를 입력받게 됩니다).

 

test코드 작성

from django.test import TestCase





class YourTestClass(TestCase):

    

    @classmethod

    def setUpTestData(cls):

        member = Member.objects.create(name='byeonguk')


    def test_name_label(self):

        first_member =Member.objects.get(name='byeonguk').first_name

        self.assertEquals(first_name, 'first name')


    def test_age_bigger_19(self):

        age = Member.objects.get(name='byeonguk').age

        check_age = age > 19

        self.assertTrue(check_age)

  • 체크해주는 함수
  • self.assertEquals => 생각한 값과 같은지 체크해주는 함수
  • self.assertTrue(True) => () 안의 값이 True인지 체크
  • self.assertFalse(False) => () 안의 값이 False인지 체크

 

test코드 실행

python3 manage.py test

 

test코드 더 많은 정보 출력하기

python3 manage.py test --verbosity 2
  • verbosity 는 기본적으로 1이며, 0,1,2,3으로 조절가능

 

test코드 일부만 실행하기

  • 테스트 중 일부만 실행하려면 패키지, 모듈, TestCase 서브클래스, 메서드의 전체 경로를 지정해주면 됩니다.
# Run the specified module

python3 manage.py test catalog.tests





# Run the specified module

python3 manage.py test catalog.tests.test_models





# Run the specified class

python3 manage.py test catalog.tests.test_models.YourTestClass





# Run the specified method

python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two

 

일단 어느정도 정리는 하였으니깐, 이제는 직접 작성해보면서 테스트를 해봐야겠다.

 

 

 

2019.05.09 heroku를 활용한 북마크 배포하기

헤로쿠를 통한 북마크 앱 배포하기

헤로쿠 배포 순서

  1. 헤로쿠 설치하기 The Heroku CLI | Heroku Dev Center
  2. 헤로쿠 추가 필수 모델 설치하기
  3. requirements.txt 파일만들기
  4. 모듈 설정 : setting.py
  5. Procfile 만들기
  6. runtime.txt 만들기
  7. gitignore 만들기
  8. 헤로쿠에 업로드
  9. 헤로쿠 초기화

1. 헤로쿠 설치하기

  1. [The Heroku CLI | Heroku Dev Center] 접속(https://devcenter.heroku.com/articles/heroku-cli)
  2. 터미널 설치 명령어
brew tap heroku/brew && brew install heroku 
  1. 설치 확인
    1. 터미널 : heroku --version

2. 헤로쿠 추가 모듈 설치하기

  1. 추가 모듈 설치 (pip install 모듈명)
    1. dj-database-url : 데이터베이스 관련 옵션을 변수로 쉽게 접근할 수 있게 해주는 유틸리티
    2. gunicorn : wsgi용 미들웨어 - 웹서버와 장고 사이의 다리 역할
    3. whitenoise : static 파일 서빙용 미들웨어
    4. psycopg2-binary : postgreSQL용 드라이버

3. requirements.txt 파일 만들기

freeze > requirements.txt

4. 모듈 설정

  1. DEBUG = False
  2. ALLOWED_HOST = ['*']
  3. 미들 웨어 추가
    1. 정적파일을 사용하기 위해 미들웨어를 추가하고 STATIC ROOT를 추가해줘야 한다.
    2. 'whitenoise.middleware.WhiteNoiseMiddleware'
  4. STATIC ROOT 추가 : 스태틱 파일을 저장할 경로 설정
    1. STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
  5. 테이터 베이스 옵션 추가(헤로쿠가 알아서 해주는데 무엇을 해줄지 적는다.)
    1. DATABASES 아래에 추가
B43A59AA-6CFC-4541-809D-06DB07F83821

5. Procfile 만들기

  • 최상단에 Procfile 생성
  • web : gunicorn config.wsgi 입력

6. runtime.txt 만들기

  • 최상단에 runtime.txt 만들기
  • python-3.7.0 적어주기

7. gitignore 만들기

*.pvc
*~
/venv
__pycache__
db.sqlite3
.DS_Store

8. 헤로쿠에 업로드

  1. 헤로쿠 로그인
    1. 터미널 : heroku login
  2. git init
  3. git add -A
  4. git commit -m "heroku commit"
  5. heroku create django-bookmarkproject
  6. git push heroku master

9. 헤로쿠 초기화

  1. 헤로쿠 초기화
    1. 데이터베이스 초기화
      1. heroku run python manage.py migrate
    2. 슈퍼 유저 생성
      1. heroku run python manage.py createsuperuser
  2. 헤로쿠 열어보기
    1. heroku open

2019.05.07 인스타그램 클론 코딩하기(회원가입 기능 구현하기1)

인스타그램 만들기(16)

회원가입 기능 구현하기(1~4)

모델 폼을 통해 회원가입 기능 구현하기

  1. views를 통해 로직 구현하기
    1. POST형태로 받을 때와 처음 실행할 때 화면 구현
    2. render의 특징
    3. POST형태의 데이터 저장하기
  2. 템플릿 만들기
  3. url 연결하기
  4. 비밀번호 암호화하기
  5. forms.py를 구현하여 views.py를 간락햐게 만들기
    1. forms.py 오버라이딩하기
    2. views.py 간략히 수정하기
    3. 중복 입력 받기(commit=False)
    4. signup_complete에 회원가입한 사람 이름 띄어주기
    5. form을 통해 받은 자료 암호화하기
      1. set_password
  6. 비밀번호 재입력창 만들기
    1. 비밀번호 중복 확인하기
  7. 해로쿠를 통해 서버 배포하기

1. views.py를 통해 로직 구현하기

2FD6CE77-002F-4EF9-9FB0-F3A94B6A8BA4
  1. 입력 방식이 POST로 오면 해당 정보들을 get을 통해 받고
  2. User() 객체를 만들어서 해당 정보들을 user에 넣고 저장해준다.
  3. 그리고 render를 통해 signup_complete.html을 불러온다.
  4. else: 그렇지 않으면 (기본적으로 보여지는 형태)
  5. context_values를 dictionary 형태로 만들고
  6. render를 통해 signup.html로 옴기고, context_values를 signup.html에 포함시킨다.
  7. signup.html에 {{ form }} 의 형태가 있으면 'this is form'을 불러온다.

render의 특징

  1. render란 무엇인가? (3가지를 한번에 해결해준다)
    1. 템플릿 불러오기
    2. 템플릿 랜더링 하기
      1. context_value를 해당 템플릿에 끼워넣어준다.
    3. HTTP Response하기
      1. 완료된 내용을 화면에 띄어준다.

2. 템플릿 만들기

  1. signup.html 구현하기
998AD2B8-73D8-4557-B3E5-421706DF7247
  • input태그를 활용하여 정보를 넣을 수 있게 하고 name을 넣어서 view와 일치되도록 한다.
  • method는 post 형식으로 하여서 views에서 requests==post 라는 것에 성립되도록 구현한다.
  1. signup_complete.html 구현하기
AC0E7AE6-3923-4C8D-A29B-F355A7CECC99
  • 로그인이 잘 되었다는 문구를 띄어주고
  • login url을 연결해줘서 login 하는 창으로 이동하도록 해준다.

3. url 연결하기

1E80ECA8-BA37-4434-BD0E-8F240AE8BA4D
  • path('signup/', signup, name ='signup') 연결해주기

4. 비밀번호 암호화히기(views.py 수정)

스크린샷 2019-05-07 오후 7 52 04
  • user.password = password 수정
  • user.set_password(password) 로 수정해줌으로서 admin에 들어가서 비밀번호가 잘 설정된 것을 확인할 수 있다.

2019.05.05 인스타그램 클론 코딩하기(좋아요 버튼 구현하기)

인스타그램 만들기(15)

구현 로직

  1. photo 앱 만들기
  2. 모델 설계하기
  3. views 설계하기
    1. list페이지
    2. 상세페이지
    3. 삭제하기
    4. 수정하기
    5. 생성페이지
  4. url 연결시켜주기
  5. template 만들기
    1. create/list/update/delete/detail
  6. 사진 화면에 업로드 할 수 있도록 만들기
  7. success url을 get_absolute_url로 연동시켜보기
  8. account 앱 만들기
    1. 로그인/로그아웃 기능 구현하기
    2. 템플릿에 로그인/로그아웃 보이도록 하기
    3. 로그아웃 되었을 때는 create 및 sign out가 안 보이도록 구현하기(분기)
  9. 권한 문제 해결하기
    1. html 기준에서 해결하기
    2. 링크로 들어와도 안 되도록 해결해보기
      1. view를 조정하기
  10. 댓글 기능 구현하기
    1. 댓글은 상세페이지에서 가능하도록 하기
    2. 소셜 댓글기능으로 구현하기
  11. 좋아요/저장하기 버튼 만들기
    1. 스프라이트 이미지 기법 활용하기
    2. 클릭하면 색깔 바뀌도록 구현하기
    3. 로그인을 해야지 버튼 클릭이 되도록 하고 클릭을 하면 like count 올라가기
    4. like에 대한 정보를 저장하기
    5. 좋아요 counting 표시해주기
    6. 디테일 페이지에서 좋아요를 누르면 디테일 페이지에서 그대로 유지하도록 하기
      1. view와 like에서의 분기
      2. 레퍼러를 활용하여 해당 주소가 어디서부터 시작됬는지 확인한다.
    7. 저장하기 기능 추가하기
  12. 좋아요한 포스팅만 보기 기능 구현하기
  13. 저장한 포스팅 리스트 페이지 구현하기
  14. 좋아요한 포스팅 및 저장한 포스팅 리스트 보기는 로그인한 사람만 보여주기
    1. view단에서 시행
      1. dispatch 활용
      2. LoginRequiredmixin
    2. html 활용
  15. my page에 내가 올린 사진들만 나오도록 구현한다.
  16. my page에 팔로우 기능 추가하기

이번 시간에 할 것(15)

  • user_list를 만들어서 사용자가 올린 사진들만 모아보기 기능 추가
    • user_list라는 views.py에 추가해준다.
    • url을 연결하여 준다.
  • user_list를 볼 수 있는 템플릿을 만든다.
  • base.html을 통해 새로운 카드를 만든다.
    • 해당 링크를 user_list로 연결해준다.

유저 리스트 만들기

  1. views.py를 통해 PhotoMyList를 만든다.
  2. url을 연동시켜준다.
  3. templates를 만든다.
    1. mylist 템플릿을 구현한다.
    2. photo_list 템플릿을 수정하여 mylist 템플릿으로 갈 수 있도록 url을 연결해준다.

views.py 만들기

스크린샷 2019-05-06 오후 1 06 22
  • templates을 mylist로 연결시켜준다.
  • dispatch를 통해 로그인을 했는지 확인해준다.

url 연동시켜주기

스크린샷 2019-05-06 오후 1 07 44
  • url로 이동하여 mylist의 path를 추가해준다.

templates 만들기

photo_mylist 템플릿 만들기

  1. Bootstrap Gallery - examples & tutorial. Basic & advanced usage - Material Design for Bootstrap
  2. 위의 자료를 참고하여 mylist를 구현
스크린샷 2019-05-06 오후 1 10 41
  • object의 list를 돌면서 작성자와 요청자가 같은 것만 보여준다.
  • figure class를 통해서
    , figure class="col-md-4"> 두개를 나눈 값 만큼 1줄에 card를 넣는다. 12/4 = 3개씩
  • 이미지 url을 넣어준다.

photo_list.html 수정하기

스크린샷 2019-05-06 오후 1 14 19
  • 그냥 있었던 마지막 div class col에 새로운 내용을 추가해준다.
  • bootstrap 카드를 참조해서 가지고 온다.
  • url을 연결하여 mylist로 이동하도록 해준다.'
  • title은 접속한 로그인 유저가 나오도록 해줘야 하는데 어떻게 구현해야할지 잘 모르겠다. ㅠㅠ

확인

D35E174D-C704-49FF-9545-AC76A7C8F33C
  • 회원님의 스토리 이동 클릭
4613BA28-58A1-4848-825E-AF7F1B323CB1
  • 아래와 같이 잘 구현된 것을 볼 수 있다.

2019.04.19 장고시작하기

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 장고란 무엇인가?

장고란

파이썬으로 작성된 오픈 소스 웹 애플리케이션 프레임워크로 모델 - 뷰 - 컨트롤러(MVC) 패턴을 따르고 있다.
고도의 데이터베이스 기반 웹사이트를 작성하는데 있어서 수고를 더는 것이 장고의 주된 목표!
컴포넌트의 재사용성과 플러그인화 가능성 빠른 개발등을 강조하고 있다.

  • 컴포넌트 : 소프트웨어 시스템에서 독립적인 업무 또는 독립적인 기능을 수행하는 모듈로서 이후 시스템을 유지보수하는데 있어 교체가 가능한 부품
  • 플러그인 : 웹 브라우저의 표진 기능을 확장해주는 프로그램을 의미한다.(flash, pdfreader등)

장고의 특징

  1. MVC 패턴 기반 ( Model - View - Controller)
    1. 장고에서는 view를 Template, Controller를 View라고 부르므로 MTV 프레임워크라고 부르기도 한다.
  2. 우아한 URL 설계
    1. URL형태를 개발자가 직접 결정할 수 있고 각 URL형태를 파이썬 함수에 직접 연결하도록 되어 있어 개발이 편리하고 이해하기 쉽다.
  3. 관리자 웹 인터페이스 제공
    1. 사용자 관리, 사용자 그룹관리, 권한 뿐만 아니라 모델 객체에 대해서도 목록, 추가, 삭제, 변경의 기능이 관리자 인터페이스에서 모두 제공되어 웹 어플리케이션을 실험해보기에 좋다.

장고 시작하기

프로젝트 할 폴더 생성

  • mkdir djangoproject
  • cd djangoproject

가상 환경 설치 및 실행

  • 장고의 경우 각 프로젝트에 맞게 버전을 설정하여 관리하기 위해 가상 환경 위해서 진행해주는게 좋다.
  • python3 -m venv myvenv : myvenv라는 가상환경 설치
  • source myvenv/bin/activate : myvenv / bin 폴더의 activate 실행 (가상환경실행)

가상 환경 내에서 django 설치

  • pip install django~=2.0.0 (가상 환경위에서는 자동으로 python3가 잡히므로 pip3 해줄필요 없음)

프로젝트 시작하기

  • django-admin startproject mysite . : mysite라는 프로젝트 시작
  • mysite라는 폴더 생성

  • 위와 같이 진행하고 나면 위의 구조로 파일이 생성된다.

데이터베이스 설정

  • python3 manage.py migrate : 현재 장고 프로젝트에서 데이터베이스를 어떤 식으로 저장할지에 대한 자동설정
  • python manage.py runserver : 장고 서버 시작

이까지 진행하면 장고 서버를 시작하여 웹사이트 운영을 시작할 단계가 되었다.

  • 다음부터는 장고의 MVC 패턴 기반 ( Model - View - Controller)에 맞추어서 하나씩 추가적으로 설명해보려고 한다. django 02. 에서 Model에 대해 추가적으로 알아보자!

+ Recent posts