IT/컴퓨터 > Django (10개의 글)

Django 커스텀 manage.py 명령어 만들기 

2020. 10. 05 IT/컴퓨터 > Django
  장고 프로젝트에서 manage.py 를 이용해 프로젝트 관련 명령을 실행할 때가 있다. 등등 테스트용 서버를 띄우거나 models.py 내의 모델을 수정하고 새로 마이그레이션 하는 등의 작업을 manage.py 라는 파일을 통해 터미널의 커맨드 라인에서 입력해서 하도록 되어 있다.   (참고로 sub 커맨드 없이 그냥 python manage.py 만 실행해 보면 실행 가능한 sub커맨드들을 볼 수 있고, 각각에 대해서 help를 쳐서 사용법을 살펴볼수 있다.)   이렇게 장고에서 기본적으로 제공하는 커맨드들 이외에, 원하는 동작을 수행하는 sub 커맨드를 직접 작성할 수도 있다. 어떤 작업이 필요할 때 꼭 manage.py 를 통하지 않아도 파이썬 자체 명령으로 실행되는 별도의 파일을 만들어 실행할 수도 있지만, 이렇게 하면 기본적인 장고 설정들이 로딩되지 않기 때문에 프로젝트의 models.py 에서 사용하던 모델들을 불러와 데이터베이스와 연동된 작업을 하는것이 어렵다.  manage.py 를 통해 실행되는 커맨드들은 장고 환경을 자동으로 로딩해 주고 실행되기 때문에 Django shell 에서 작업하는것과 같이 프로젝트에서 사용하던 model 들을 별도의 복잡한 설정 없이 바로 import 해서 데이터베이스와 관련된 작업을 할 수 있어 편리한 점이 많다. (Django shell 을 띄우는 것은 python manage.py shell 명령으로 가능) 그래서 주로 데이터베이스에 항목들을 일괄 입력/수정하는 등의 작업이 반복적으로 필요할 때 이런 커스텀 커맨드들을 등록해 놓으면 편리하다. 이렇게 커스텀 커맨드를 등록해 놓으면 리눅스 환경에서 cron 을 이용해 반복적으로 해야 하는 일이 있을때도 커맨드 한 줄로 처리할수 있다.   커맨드를 만들려면 우선 등록된 app 디렉토리 아래에 management/commands 라는 디렉토리를 만들어 주어야 한다. manage.py 가 있는 프로젝트의 루트 디렉토리를 my_project/ 라고 하면  my_project/ 디렉토리 아래에 기존에 my_app/ 이라는 앱 디렉토리가 있고 그 디렉토리 안에 views.py, models.py, urls.py 등의 기본 파일들이 있을 것인데 그 아래에 management/commands 디렉토리를 만들어 주고, commands 디렉토리에 실행하고 싶은 커맨드를 my_command.py 식으로 만들어 주면 된다. 즉 안에 my_command.py 가 들어가는것이고, 이렇게 등록해주면 프로젝트의 root 디렉토리에서  와 같이 실행할 수 있다.   커맨드의 기본 형식은 다음과 같다.   BaseCommand 클래스를 임포트해주고 이를 상속받는 class Command 를 정의해준다. 커맨드 사용법을 문자열로 help 변수에 저장해주면  를 실행했을 때 해당 내용이 출력된다.   클래스 내의 handle() 메서드에 실행하고 싶은 동작을 일반 파이썬 함수로 작성해 주면 된다. 위의 경우  와 같이 실행되는 것을 볼 수 있다.     커맨드에 동작 옵션을 추가해줄 수 있다. 클래스 내에 add_arguments() 메서드를 정의해주면 된다.   저렇게만 써 놓으니 좀 막연한데, 간단한 예제를 작성해본다. my_app 앱 안의 models.py 에 사용자들끼리 주고받는 쪽지를 만들어주는 UserMessage 라는 모델이 정의되어 있고, 해당 모델로 생성된 사용자 쪽지들이 데이터베이스에 저장되어 있다고 가정하고 예시를 작성해 보겠다. 쪽지들은 읽으면 read 상태로 되고 따로 보관함에 보관할수 있는데, 보관함에 보관하지 않으면 unarchived 상태라고 가정한다.  이 중 읽은 쪽지를 일괄적으로 삭제하거나, 아니면 보관함에 보관되지 않은 쪽지들을 일괄적으로 삭제하는 커맨드를 작성하고 싶다고 해 본다. 우선    위의 커맨드는  와 같이 실행할 수 있고, 아무 옵션을 주지 않은 경우 특정 기간이 아닌 모든 쪽지에 대해 read 상태인 (즉 이미 읽은) 쪽지를 삭제하게 된다.   add_argument() 내에서 recent_range 로 지정된 인자는 위치 인자이므로 플래그 없이 바로 옵션의 값을 입력하면 된다. 이 경우 전체 쪽지 중 최근 30일간 작성된 쪽지들을 추려서 작업하게 된다.   이런 식으로 옵션과 그 값을 입력해 주면 unarchived, 즉 보관함에 보관되지 않은 쪽지는 모두 삭제한다.   특정한 여러 개의 인자들을 리스트로 전달해서 작업하는것도 가능하다.   이렇게 작성하면 지우고 싶은 메세지의 ID 를 지정해서 삭제해줄 수 있다 (여기서 ID는 데이터베이스에 저장된 순서대로 자동으로 붙는 번호이므로 사실 message의 ID를 알아내 지운다는것이 약간 현실적인 사용 례는 아닌것같지만 개념상 딱히 좋은 예가 당장 떠오르지 않아 이렇게 해 보았다. 특정 유저나 특정 단어가 들어간 쪽지를 삭제한다든지 하는 식으로 응용할수 있을듯)   이런식으로 여러 개의 숫자를 리스트로 전달해 일괄 처리하는것도 가능하다.   옵션들은 add_arguments() 메서드 안에 parser.add_arguments()를 통해 제한 없이 추가 가능하다.    다른 프로젝트에서 매주 업데이트되는 정보를 웹에서 크롤링해 와서 데이터베이스에 입력하는 작업을 하고 있는데, 이런식으로 커스텀 커맨드를 작성한 후 리눅스의 cron 으로 매주 한번씩 작업이 되게 하고 있다.    기타 자세한 내용은 Django 의 공식 문서를 참조하면 좋을 듯한데 아쉽게 아직 한글 번역은 되지 않은것같다.  공식 문서: https://docs.djangoproject.com/ko/3.1/howto/custom-management-commands/

Django GET 검색시 MultiValueDictKeyError 해결 

2020. 10. 03 IT/컴퓨터 > Django
간단한 Django 트러블슈팅 하나 포스트.   view 를 작성하다 보면 폼에서 GET 메소드를 이용하여 결과를 받아 처리해서 페이지를 표시해줄 일이 종종 있다. 주로 블로그의 글 등 어떤 object들의 검색 결과를 받아 표시해줄 때이다. 이때 GET 으로 전달된 파라미터가 있는 경우에는 해당하는 파라미터를 전달받아 필요한 처리를 해서 템플릿을 띄워 주고, 파라미터가 전달되지 않은 경우에는 기본 페이지를 보여주는 등의 처리를 하게 된다.   GET method로 폼을 제출하면 제출된 값들은 request.GET 객체에 딕셔너리 형태로 전달되게 된다 (정확히는 파이썬 딕셔너리가 아닌 장고 고유의 QueryDict 객체임).   예를 들어 블로그의 포스트를 검색하기 위한 다음과 같은 폼이 있다면    검색 버튼을 르면 각각 입력 필드의 name 인 post_search_type 과 post_search_keyword 를 key로, 사용자가 입력한 값을 value로 갖는 request.GET 객체가 생성된다. 예를 들어 option을 제목(title) 로 선택하고 'test' 라는 키워드로 검색을 한다고 할 때,  해당 작업을 처리하는 views.py 에서 request.GET을 출력해보자.   터미널에 다음과 같이 나올 것이다.   그래서 각각의 키에 해당하는 작업을 처리하기 위해서는 request.GET['post_search_type'] 혹은 request.GET['post_search_keyword'] 식으로 해당하는 값을 불러와서 처리를 해 주면 된다.     그런데 이 때 문제가, 폼에 아무 값이 전달되지 않고 페이지가 처음으로 로딩되는 경우, 혹은 query 값이 없이 url 을 통해 직접 페이지에 접근하는 경우에는 아직 QueryDict에 해당하는 key 값과 value가 생성되지 않은 상태이다. 이 상태에서 request.GET['post_search_type'] 또는 request.GET['post_search_keyword'] 의 값을 변수에 할당하려고 하면 다음과 같이 MultiValueDictKeyError 가 발생하게 된다.     (settings.py 에서 DEBUG = True 로 해 놓은 상태에서는 이렇게 에러 내용이 보이고, DEBUG = False 인 상태에서는 Internal Server Error (500) 으로 표시될 것이다.)   이를 방지하기 위해서 바로 QueryDict 의 키 값을 부르는 대신 다음과 같이 request.GET.get() 메서드를 이용할 수 있다.    이렇게 하면 get() 안에 전달한 키 값으로 QueryDict에 해당하는 key와 value 가 있는지 찾아서, key가 있는 경우에는 그 key의 value 값을 반환해주고, key를 찾지 못한 경우에는 에러를 일으키지 않고 None을 반환해 준다.  None이 반환된 경우에는 아직 검색 키워드가 입력되지 않은 상태임을 의미하므로 다음과 같이 기본 페이지를 로딩하는 등의 처리를 해 주면 되겠다.   프로젝트 진행중 잠깐 막혀서 고생한 내용이라 기록을 해 둔다.

DigitalOcean에 Django 프로젝트 배포하기 (3) 

2020. 03. 06 IT/컴퓨터 > Django
우분투 서버에 Django 프로젝트 배포하기 마지막편. DigitalOcean에 Django 프로젝트 배포하기 (1) DigitalOcean에 Django 프로젝트 배포하기 (2)   이제 본격적인 서버 구동을 위해 Nginx 및 Gunicorn 세팅을 해 주어야 한다. 다른 세팅을 해 주기 전에 gunicorn 이 정상적으로 작동하는지 확인해본다. 다음 명령어로 서버를 구동하고, 브라우저에서 droplet 의 IP를 입력하고 8000번 포트로 접속했을 때 index 화면이 나와야 한다. 다만 아직 서버에서 정상적으로 static 파일 설정을 하지 않았으므로 css 파일을 찾을 수 없어서 스타일링은 정상적으로 적용되지 않을 것이다. 위에서 '프로젝트' 는 wsgi.py 파일이 들어있는 Django app 디렉토리 이름이다. 실제 자기가 만들어 준 app 디렉토리 이름으로 적어 준다. 해당 디렉토리 내에 Django가 자동적으로 생성한 wsgi.py 에 설정되어 있는 설정을 적용해서 gunicorn 을 구동하는 것이다.   정상적으로 구동이 된다면 이제 파이썬 가상환경에서 빠져나오기 위해서 를 입력한다. 프롬프트 앞의 (deploy_env) 가 사라진 것을 확인할 수 있다.   이제 리눅스 서버에 systemd 소켓과 서비스 파일을 만들어 준다. (systemd는 리눅스의 서비스 관리자 같은 것으로 이 부분에 대해서는 더 공부가 필요할듯) 해당 파일들을 작성하게 되면 시스템 부팅시에 gunicorn 소켓이 생성되고, 외부에서의 connection을 기다리고 있다가 connection 이 발생하면 systemd가 자동으로 gunicorn 에게 해당 처리를 맡기는 동작이 발생한다. 우분투에 내장된 nano 에디터를 이용해 socket 파일을 작성해주기 위해 다음 명령어를 입력한다.   에디터가 열리면 아래의 내용을 입력하고 저장해준다. (nano 에디터 내에서 도움말이 나오긴 하는데 저장은 Ctrl + O, 종료는 Ctrl + X 이다)   처음 작성할때 [Socket] 섹션에 gunicorn.sock 부분을 gunicorn.socket 이라고 잘못 입력해놓고 서버 구동이 되지 않아서 어디가 잘못되었는지 못 찾고 한참을 헤맸다. 한번 잘못된 파일이 생성되니 쉽게 복구가 되지 않고 결국 이것저것 건드리다가 완전히 망해서 아예 Droplet 을 삭제하고 처음부터 다시 해야 했다. 오타에 주의하자.    마찬가지로 nano 에디터를 이용해 systemd 서비스 파일을 만들어 준다.     여기서도 sitemaster를 자기 id로, django-deploy-exercise 는 자기 프로젝트 root 디렉토리 명으로, '프로젝트' 는 자기 Django app 으로 적절히 적어 주자.   이제 정상적으로 Gunicorn 소켓 파일이 생성되어 구동할수 있다. socket을 구동하면 /run/gunicorn.sock 라는 소켓 파일이 만들어지게 된다. 다시 강조하지만 이걸 gunicorn.sock 이 아니라 gunicorn.socket 이라고 적었다고 droplet 전체를 갈아 엎었으니 조심하자.   다음 명령어로 Gunicorn socket 을 구동하자.   정상적으로 소켓이 작동하는지 다음 명령어로 확인할 수 있다. 정상 동작중이라면 active 라는 상태 표시가 보일 것이다.   다음으로 gunicorn.sock 파일이 정상적으로 생성되었는지를 다음 명령어로 확인해 본다. 아래 줄과 같은 출력이 나오면 정상이다.   에러 메세지가 나는 등의 상황에서 자세한 동작 로그를 보려면 를 입력해보면 확인할 수 있다.   현 상태에서는 gunicorn.socket 만 활성화되어 있고, 소켓이 아직 아무 연결을 수신하지 않은 상태이므로 gunicorn.service 는 아직 활성화되지 않은 상태이다. 를 입력해서 확인해볼 수 있다. 위와 같이 inactive(dead) 라고 나올 것이다. 이때 curl 을 이용해서 연결 신호를 보내서 정상 작동하는지 확인해볼 수 있다. 다음 명령어를 입력하면 index 페이지의 html 내용이 수신되어 화면에 출력되는 것을 볼 수 있다. 이후  를 다시 확인해 보면 와 같이 active 상태가 되면서 아래에 자세한 동작 로그가 나오는 것을 확인할 수 있다.   에러가 있는 경우에는  을 입력해서 세부 내용을 확인해볼 수 있다.   경험상 이 단계에서의 에러는 대부분 gunicorn.socket 혹은 gunicorn.service 파일 내의 사소한 오타에 의한 것이었다. 파일을 잘 확인해 보고 수정해야 할 부분이 있는지 확인해 보자. 파일을 수정한 경우에는  명령으로 gunicorn 을 재기동해 주어야 변경사항이 제대로 반영된다.     마지막 단계로 Nginx 서버의 설정을 해 준다. 다음 명령으로 Nginx의 서버 블록을 sites-available 디렉토리 아래에 만들어 주어야 한다.     우선 포트는 특별한 일이 없으면 80번으로 설정하고, 아래에 server_name 에는 현재 작업중인 Droplet의 IP 주소를 적어 준다. 나중에 커스텀 도메인을 사용하는 경우는 도메인 이름으로 바꾸어 주어야 한다. 이것은 나중에 다뤄 보겠다. 그 아래의 client_body_buffer_size와 client_max_body_size 는 블로그나 게시판에 글을 올리는 등 사용자가 업로드하는 파일들의 크기에 관한 설정인데, Nginx의 기본 설정은 최대 파일 용량 제한이 1MB로 되어 있어 이보다 큰 파일을 업로드하는 경우 에러가 나게 된다. 프로젝트의 사정에 맞게 적당히 설정해 주자. 아래의 favicon 관련 설정은 인터넷 익스플로러 등 브라우저에서 HTTP 헤더가 로딩되기 전에 /favicon.ico 경로에서 에 파비콘 파일을 요청하는 경우에 Nginx 에 에러를 일으킨다고 하는데 이를 무시하기 위한 설정이다. 같은 서버 블록 아래에 static과 media 파일 경로도 설정해 준다. location 을 명시해 주고 실제 파일시스템 상의 경로를 적어 주는데, static 파일의 경우 현재 Django 프로젝트에서 /static/ 으로 들어온 요청은 실제로는 staticfiles 디렉토리에서 서빙하도록 설정되어 있어 위와 같이 'alias' 를 이용해 설정해 주었다 (이전 편 settings.py 참고). Media 파일의 경우는 경로 이름이 media로 실제 디렉토리 이름과 같아서 직접 'root' 를 설정해 /media/ 로 들어온 요청을 '/home/sitemaster/django-deploy-exercise/media/' 에서 서빙하도록 한 것이다.  맨 아래 root 경로 ('location /') 요청에 대한 설정도 위와 같이 작업해 준다.     파일을 저장 후 작성한 파일을 sites-enabled 디렉토리에 링크해 준다. 설정이 제대로 적용되었는지를 다음 명령어로 확인해 본다.   위와 같은 메세지가 보이면 잘 설정된 것이다.   에러가 없다면 다음 명령어로 Nginx를 재기동해 준다.   마지막으로 방화벽을 Nginx 에 대해 열어 주기 위해 다음 명령어를 입력해 준다. 동시에 테스트용으로 썼던 8000번 포트는 더이상 사용하지 않을 것이므로 방화벽에서 허용해 주었던 설정을 삭제한다.   이제 Droplet의 IP 주소로 가 보면 Django 프로젝트의 index 화면이 정상적으로 보일 것이다. 고생해서 만든 프로젝트의 첫 화면이 어디서나 접속 가능한 상태로 뜨는 것은 언제 봐도 감동적이다.   여기까지 하면 DigitalOcean 에서의 배포는 정상적으로 끝난 것이다. 커스텀 도메인을 가지고 있는 경우 도메인에 연결해 주는 작업이 필요한데, 그것은 다음 글에서 다루고 있다. DigitalOcean 에 나의 도메인 연결하기

DigitalOcean에 Django 프로젝트 배포하기 (2) 

2020. 03. 05 IT/컴퓨터 > Django
DigitalOcean에 Django 프로젝트 배포하기 (1)   #10. Droplet이 성공적으로 만들어졌다면 등록한 이메일로 root 계정의 초기 암호가 배달된다. DigigalOcean에서 제공하는 콘솔을 이용하거나 별도의 SSH 클라이언트를 이용해서 만들어진 droplet 의 IP 주소를 이용해 접속을 한다. SSH 인증을 사용했다면 추가적인 절차가 필요하지만 일단 지금은 password 방식으로 로그인하기로 한다. Login 프롬프트에 root 를 입력하고 처음 주어진 암호를 입력하면, 암호를 리셋하라는 프롬프트가 뜨니 새 암호를 설정해 준다.   #11. 이후 명령으로 우분투 서버 업데이트 사항이 있는지 확인하고 업데이트를 적용해 준다.    업그레이드 가능한 패키지가 있는 경우 명령으로 업그레이드도 같이 해 주는 것이 좋겠다. 업그레이드 중 다음과 같은 메세지가 나오면서 이미 설치된 프로그램에 대해 어떻게 할 것인지 묻는 경우가 있는데, 특별한 경우가 아니라면 그냥 현재 설치된 버전을 유지하는 것으로 선택하자.   업그레이드 후 를 실행하면 필요 없게 된 파일들을 제거해준다.     #12. 새로운 유저를 생성해준다. Root 계정으로 진행해도 되지만, 프로젝트마다 다른 id를 사용하는 것이 편리한 점이 있고, root 계정은 권한이 너무 커서 의도치 않게 치명적인 실수를 저지를 위험이 있으므로 봉인해 두고 새로 만든 계정으로 프로젝트 배포를 진행하는 것이 좋겠다. 식으로 원하는 아이디를 추가하고 패스워드를 설정해 준다. 일단 지금은 sitemaster 라고 지정해 주었다. 이후 이름과 전화번호등 사용자 정보를 입력하는 프롬프트가 뜨지만 입력하지 않아도 아이디 생성은 된다.   #13. 생성된 sitemaster 에 관리자 권한을 주어야 한다. 를 입력해준다. 이렇게 하면 관리자 권한을 얻게 되는데, 이렇게 처리해 준 사용자는 실행하려는 명령어 앞에 sudo 라는 명령어를 덧붙여서 관리자 권한의 작업을 할 수 있다.   #14. 우분투에는 UFW firewall 이라는 방화벽이 포함되어 있어 이를 이용할 수 있는데, 이것과 별도로 DigitalOcean 사이트에서도 방화벽 설정을 할 수 있다. 두 가지 방화벽을 같이 사용하는 경우 충돌이 있을 수 있기 때문에 둘 중 하나만 사용하는 것을 권장하고 있다. UFW를 이용하기 위해서는 설치된 다른 app 들을 ufw 허용 리스트에 등록해 주면 되는데, 명령어로 설치된 app 들을 확인하고, 명령으로 해당 앱이 방화벽을 통과할 수 있도록 설정해 준다.  그 후 명령으로 방화벽을 활성화해 주면 된다. 현재 방화벽의 상태를 명령으로 확인할 수 있다. OpenSSH 는 우리가 만든 서버와의 SSH 통신을 가능하게 해 주는 서비스이므로 방화벽을 통과할 수 있게 등록해 준다.     #15. 이제 새로 생성한 id 로 다시 접속한다. 새로 세션을 열어 접속해도 되고, 명령을 입력해 사용자를 전환해도 된다. - 다음에 빈칸이 하나 있으므로 정확히 입력하자. (su: switch user)   #16. 우선 파이썬 관련된 패키지, postgresql 과 nginx, curl 등 서버 관련된 패키지들을 설치해 준다. 우분투 서버에는 파이썬 2와 3이 모두 설치되어 있지만, Django 버전 3은 파이썬 3.6 이후만 지원하므로 가급적 최신 버전의 장고와 파이썬 3을 이용하는 것이 좋겠다. 현재 프로젝트는 DB를 Sqlite3 를 이용하고 있어서 postgresql 관련 패키지는 당장은 필요 없지만, 향후 프로젝트가 커져서 데이터베이스를 이전할 경우에 대비해서 설치해주었다. 사실 sqlite 도 개인 블로그용 데이터를 저장하는데는 충분하고, 파일 하나로 전체 DB를 다 옮기는 것이 가능해서 소규모 프로젝트에는 편리한 점도 있기 때문에 아직까지는 굳이 바꿀 필요를 못 느끼고 있다. 그리고 이전에 Heroku에서 postgresql 을 설정하다가 골머리를 앓은 기억이 있어 일단 지금은 건드리지 않으려고 한다..   명령으로 설치해준다.     #17. 이제 새로운 Django 프로젝트를 위한 가상환경을 만들어주어야 한다.  명령으로 pip를 최신버전으로 업그레이드해 주고,  명령으로 가상환경을 만들어 주는 virtualenv 패키지를 설치한다.   명령어로 GitHub 에 미리 올려둔 프로젝트를 다운받자. 프로젝트가 성공적으로 clone 되어 해당 프로젝트의 디렉토리가 생성된 것이 확인되면 그 디렉토리로 이동한다.   식으로 해당 프로젝트의 가상환경을 만들어준다. virtualenv 명령 뒤의 가상환경 이름은 다른 이름들과 관련되지 않는 독립된 이름이므로 임의로 지정해 주면 된다. 일단 deploy_env 라고 지정해 주었다. 가상환경이 만들어졌으면 명령을 통해 가상환경을 활성화시켜준다. 명령 프롬프트 앞에 (deploy_env) 와 같이 만들어준 가상환경의 이름이 표시되면 정상적으로 활성화된 것이다.       이유는 잘 모르겠는데 virtualenv 가 잘 작동하지 않는 경우가 있다. 패키지를 설치해도 가상환경 안에서 보이지 않거나, 아니면 반대로 가상환경에서 pip list 를 해 봐도 밖에 있는 패키지들이 전부 보이거나 하는 등의 이상 동작이 생기고 python 2와 python 3가 섞여서 가상환경 안에서도 python 3 버전을 이용하려면 python3 명령어를 이용해야 하는 등..이럴 때는 virtualenv 대신 venv를 사용해 보면 해결될 때도 있다.   venv는 파이썬 3버전에는 포함되어 있는 것으로 알고 있지만, 어쩐 일인지 DigitalOcean 에서 제공하는 우분투 서버에서는 다음 명령을 이용해서 설치해 주어야 한다. venv를 설치해 주고 나면 다음 명령어로 가상환경을 만들어 준다. 환경이 만들어 졌으면 activation 하는 방법은 virtualenv 와 같다.       #18. 이제 Django 및 필요한 패키지들을 가상환경 내에 설치해 준다. 가상환경이 활성화되지 않은 경우 파이썬3 버전의 패키지를 설치하려면 꼭 pip3 을 이용해 설치해야 하지만, 일단 파이썬 3 버전의 가상환경이 활성화된 후에는 설치 프로그램 이름이 pip3 이 아닌 pip 이므로 명령어도 pip3 이 아니라 pip install program 식으로 해야 한다. 경우에 따라서 가상환경 내의 pip가 구 버전인 경우가 있으므로, 우선 를 실행해 pip를 최신 버전으로 업그레이드 해 준다.   이후 로 django, gunicorn 을 설치해준다. psycopg2-binary 는 Postgresql 과 python 을 연결해주는 어댑터 패키지로, postgresql 을 당장 사용하지 않는 경우는 설치하지 않아도 되겠다. 어떨때는 위의 패키지들을 동시에 설치하려고 하면 퍼미션이 없다는 등의 오류를 내면서 가상환경 내에 설치가 되지 않는 경우도 있는데, 이때는 각각의 패키지를 따로 설치해 보면 설치가 되는 것 같다.   프로젝트에 사용된 다른 패키지를 requirement.txt로 freezing 해 놓은 경우에는 로 해당 패키지들을 설치해준다.   Django 프로젝트의 root 디렉토리는 manage.py 가 존재하는 디렉토리이고, root 디렉토리 아래에 프로젝트 메인 디렉토리에 들어가면 settings.py, wsgi.py, urls.py 등의 파일이 있을 것이다. 여기서 settings.py 에 설정을 추가해주어야 한다. 우선 서버에서의 접근을 허용해야 하므로 만들어진 droplet 의 IP 주소를 ALLOWED_HOST에 추가해 주어야 한다. 커스텀 도메인을 사용하는 경우 www.example.com, example.com 등으로 도메인도 같이 추가해 준다. Nginx 를 이용해서 서비스를 구동하는 경우 'localhost'도 추가해 주어야 한다. 127.0.0.1은 Django 내장 테스트용 서버인 runserver 를 구동할 때 쓰인다.   같은 settings.py 파일 안에 css, js 및 image 파일 등의 static 파일들이 어디에 위치하는지 Nginx에게 알려주기 위해서 STATIC_ROOT 설정을 추가해 준다.   나중에 블로그 글에 업로드될 이미지등을 저장하는 media 폴더도 같이 설정해준다.   settings.py 파일을 저장해 주고, 최초로 데이터베이스에 migration 을 해 주기 위해 다음 명령어들을 입력한다. manage.py 가 있는 프로젝트 루트 디렉토리에서 해야 한다.   프로젝트를 관리할 관리자 ID를 다음 명령어로 만들어준다.   프로젝트 내에 있는 static 파일들을 STATIC_ROOT에 지정한 디렉토리로 모아주기 위해 다음을 실행한다. 이제 Django 쪽에서의 준비가 다 되었다. 프로젝트가 잘 뜨는지 테스트를 해 보기 위해 Django 의 내장 테스트용 서버인 runserver를 이용해서 프로젝트를 띄워 본다. runserver가 이용하는 8000번 포트를 개방하기 위해 ufw 설정을 다음과 같이 update 해 주고   다음 명령으로 프로젝트를 구동한다. 이런 화면이 보이면 서버가 구동된 것이고, 이 상태에서 외부 브라우저를 통해    프로젝트 IP:8000을 입력했을 때 프로젝트의 index 화면이 보이면 제대로 작동하는 것이다.   그렇지만 지금은 테스트용 runserver에서 구동한 것이므로 이 상태로 서비스를 할 수는 없고, 본격적으로 서비스를 구동하기 위해서는 웹 서버인 Nginx 와 파이썬 WSGI (web service gateway interface) 서버인 gunicorn 세팅을 해 주어야 한다. 다음편에.. DigitalOcean에 Django 프로젝트 배포하기 (3)

DigitalOcean에 Django 프로젝트 배포하기 (1) 

2020. 03. 05 IT/컴퓨터 > Django
새로 블로그를 시작하면서 호스팅을 디지털오션으로 옮겼다. 이전 버전 블로그는 pythonanywhere.com 에서 배포를 했었다. pythonanywhere는 서버쪽 세팅을 알아서 다 해 주기 때문에 특별히 신경쓸 것 없이 하라는 대로 설정만 해 주면 배포가 가능하다. 초보자로서 시행착오를 많이 겪지 않고 처음 장고 프로젝트를 배포하기에 좋았지만, 조금 더 직접 서버 관련된 세팅을 공부해 보고 싶기도 했고, 다른 호스팅 서비스들은 어떤지 궁금하기도 해서 새 프로젝트는 새로운 곳에서 배포를 해 보기로 했다.  DigitalOcean 은 클라우드 서비스 중에서도 개발자들을 위해 좀 더 특화된 곳이라고 하는데, 아마존이나 구글, 마이크로소프트같은 대형 사업자는 아니지만 나름 입지를 갖춘 곳이다.  이번에도 복기 및 향후 프로젝트 배포를 위해서 참고할 겸 DigitalOcean 에서의 Django 프로젝트 배포 과정을 정리해 보았다.   #1. DigitalOcean.com 에 접속하면  보이는 화면. 별도 계정을 만들거나 구글이나 GitHub 계정에 연동해서 사용할 수 있는데, 지금 프로젝트를 진행하고 있는 GitHub 계정에 연동된 계정을 만들어 접속하였다.   #2. GitHub 계정을 이용해서 로그인한 경우에는 자동으로 github 계정 이름이 프로젝트 이름으로 뜬다. 프로젝트는 필요하면 새로 만들어주어도 된다. 디지털오션에서는 서비스하는 독립된 하나의 클라우드 서버를 Droplet 이라고 부른다. 첫 화면에 보이는 왼쪽 메뉴 바에서 droplet을 선택하면 현재 만들어진 Droplet 현황을 볼 수 있고, 아직 하나도 만들어지지 않은 경우에는 새로 만들 수 있는 화면이 나온다.   #3. 현재 이미 만들어 둔 Droplet들이 있는 경우 관리할 수 있는 메뉴가 뜨지만, 하나도 없는 경우 새로 만들 수 있는 메뉴가 보일 것이다. 또는 오른쪽 위의 초록색 Create 버튼을 이용해서 만들 수도 있다.     #4. 서버를 구동하기 위해 원하는 운영체제를 고를 수 있다. 선호하는 운영체제가 있다면 선택해 주면 된다. 가장 많이 쓰는 Ubuntu 18.04 버전을 선택하였다.     #5. 이용 요금제를 선택해야 한다. 아쉽게도 무료 체험판 서버는 만들 수 없다. Pythonanywhere 같이 간단히 시험할 수 있는 무료 서버를 구동할수 있으면 좋을텐데. 일단 세팅을 해 보고 되는지 안 되는지 확인은 하고 실제 서버를 구동할 수 있게 하면 좋을것 같다. 하지만 기본 플랜은 월 5달러이니 큰 부담은 없다. 그것도 시작하자 마자 5달러가 결제되는 것이 아니고, 리소스 사용분만큼만 조금씩 누적되어 금액이 올라간다. 중간에 droplet 을 없애거나 하면 그 때까지 쓴 만큼만 비례해서 계산되는것 같으니 잠깐 실험해 보는 정도로는 1~2달러 아래로 해결이 가능할 듯하다.  어쨌든 결과적으로 세팅이 매우 쉽고 매끄럽게 돼서 결국은 돈 내고 실제 서버를 돌리게 될 것같긴 하다. 별 시행착오 없이 쉽게 진행이 된다. 가장 저렴한 매월 5$ 짜리 플랜으로도 간단한 블로그를 운영하는 데는 아무 문제 없으니 마음에 든다면 5달러짜리로 시작하면 되겠다.   아래쪽에 block storage 를 추가할 수 있는 옵션이 있는데, 당장 저장소 공간이 부족할 일은 없으므로 나중에 해도 될 것 같다.     #6. 데이터 센터는 각각 테스트해보지는 않았지만 싱가폴이 거리가 가까워 조금이라도 전송 속도가 빠르지 않을까 싶어 싱가폴로 선택해 보았다.    #7. 서버 관리자 인증 방법을 선택할 수 있는데, 보안상 SSH key 를 강력하게 추천한다고 하지만 당장은 해킹당할 내용도 없으므로 password 방식을 선택해도 무방하지 않을까 싶다. SSH key 사용하는 법은 나중에 배우기로...새로 배울것이 많다.     #8. 동시에 여러 개의 droplet 을 생성할 수도 있다. 지금은 하나만 선택. Hostname 은 대시보드에 표시되는 이름인데, 그냥 알아보기 쉬운 이름으로 임의로 지정해 주면 된다. 서버 설정등에 사용될 일은 없는 완전히 독립된 이름이다. 그 아래로 tag 지정을 해주거나 백업을 활성화시킬 수 있는 메뉴도 있는데, 당장 하지 않아도 상관 없다.   설정을 다시 확인해 보고 맨 아래의 Create Droplet 버튼을 눌러 주면 droplet 이 만들어진다.     #9. 만들어진 droplet 을 클릭해보면 기본 정보가 나오는데, 지금은 다른 것은 건드릴 필요 없고 IP 주소만 알고 있으면 된다. IP 주소 위에 마우스를 가져가면 클릭해서 바로 복사할 수 있도록 되어 있다.   이후의 설정은 만들어진 서버에 SSH 콘솔을 이용해서 접속한 후 진행해 주게 된다.  Droplet 화면의 오른쪽 끝에 있는 Console 을 클릭하면 웹 상에서 바로 접속되는 콘솔 창이 뜨니 이것을 이용해서 작업해도 된다. 그렇지만 써 보니 동작이 약간 버벅대는 것이 쾌적하지가 않아 별로 추천하고 싶지는 않다. PuTTY 등의 전용 SSH 클라이언트를 이용하는 것이 좋을 것 같다.  다만 회사에서 PuTTY를 이용해 Droplet의 IP에 접속하려고 하니 방화벽에 막히는지 접속이 안 되었는데, 이런 경우에 웹 콘솔이 대책이 될수는 있겠다. 하지만 회사에서는 할 일을 하자..   서버 설정은 다음편에. DigitalOcean에 Django 프로젝트 배포하기 (2)

Django 프레임워크로 RSS 피드 만들기 

2020. 02. 23 IT/컴퓨터 > Django
블로그를 네이버 등 검색엔진에 등록하려면 RSS 피드를 제출하는 것이 좋다고 한다. 사실 예전부터 RSS라는 말은 많이 들어 봤지만 실제로 사용해본 적이 없고, 주변에서 RSS를 통해 구독하는 사람도 본 적이 없어서 어떤 것인지 개념이 모호한 상태였다. 새로 RSS 피드를 만들면서 간단히 공부해 보았다.   RSS는 Really Simple Syndication, 혹은 Rich Site Summary 의 약자라고 하는데, syndication은 또 뭔가? 전통적인 사전적 의미는 기사, 사진, 텔레비전 프로그램 등을 여러 신문사 등에 파는 행위를 syndication이라고 하는 것 같다. 요즘 주로 쓰이는 의미는 블로그등 웹사이트의 컨텐츠를 타겟 구독층에 노출시키는 것을 말한다.   Really Simple Syndication, 즉 자기 글을 아주 간단하게 사람들한테 배포할 수 있는 기술이라는건데..실제로 구동되는 방식은, 사이트에서 RSS 피드를 제공하면 경우 방문자가 관심있는 피드 주소들을 RSS 리더 등의 프로그램을 이용해서 등록해 둔다. 그러면 사이트에 새 글이 올라오는 경우 그 사이트들을 일일이 돌아다니지 않아도 RSS 리더 프로그램이 자동으로 새 글이 떴다는 것을 알려주게 된다. 그렇게 해서 구독자는 쉽게 여러 관심 사이트에 올라오는 새로운 컨텐츠를 확인할 수 있고, 반대로 매체 입장에서도 컨텐츠를 쉽게 사람들에게 전달하는 채널이 되는 것이다. 두번째 약자인 Rich Site Summary 라는 표현은 좀 더 기술적이고 직설적인 표현인데, 개념은 이전에 기술한 사이트맵 (sitemap.xml) 과 비슷한것같다. 사이트의 컨텐츠 내용을 요약해서 보여 주는 페이지라는 얘기다. 참고: Django sitemap 프레임워크를 이용해서 사이트맵 만들기 차이점은, 사이트맵은 간단히 제목과 링크 정도만 제공하는 대신 사이트 전체의 컨텐츠를 모두 요약해서 보여주는 것이 목적인 반면 RSS 피드는 전체 컨텐츠를 다 전달하는 것이 아니라 주로 최근에 발행된 컨텐츠를 모아서 전달해주는 것이라는 점이다. RSS 의 목적상 방문자가 사이트에 직접 들어오지 않아도 내용을 알 수 있어야 하므로 사이트맵처럼 제목 위주의 간단한 요약이 아니라, 글 본문과 첨부된 사진같은 해당 컨텐츠의 내용 전체를 제공해 주는것이 권장된다고 한다 (즉 풍부한 사이트 요약 = Rich Site Summary).   장고(Django) 프레임워크에는 간단하게 RSS 피드를 생성할 수 있는 syndication feed framework 라는 도구가 포함되어 있다. 이를 이용하면 RSS 와 Atom 피드를 쉽게 생성할 수 있다.   #1. 우선 models.py 에 다음과 같이 블로그 글을 정의하는 Post라는 모델이 이미 구성되어 있다. (설명을 간략하게 하기 위해 기타 field 는 생략함)   #2. 이후 feeds.py 라는 파일을 새로 만들어 생성될 RSS feed 의 내용을 정의해 준다. 파일의 위치는 어디이든 상관 없지만, 가급적이면 models.py 와 같은 디렉토리에 만들어주는 것이 관리에 편할 것 같다.   우선 Feed 클래스를 import 해 주고 그 클래스를 상속해 원하는 내용을 담을 feed 클래스를 정의해 주면 된다. models.py 에서 Post 클래스를 import 해 주어야 내용을 읽어올 수 있다.  Feed 클래스에는 미리 정의된 속성이 있는데, 위에서 title, link, description 은 나중에 생성될 RSS feed의 <title>, <link>, <description> 에 보여지는 속성이므로 알맞게 작성하면 되겠다. 이후에 역시 Feed 클래스에 미리 정의된 메서드들을 정의하면 되는데, 우선 가장 기본적인 items() 에는 RSS에 표시할 글들을 Django Queryset 을 이용해서 읽어 오면 된다. 위의 코드는 이 블로그에 등록된 글 중 발행이 된 글만 찾아오도록 필터를 해 주고 그 중 최근 발행 일자 글 10개를 return 하도록 한 것인데, 필터가 필요 없는 경우에는 식으로 하면 될 것이다. Queryset 에서 데이터를 읽어오는 작업은 Django 작업을 하면서 가장 기본적인 내용 중 하나이니 관련된 내용을 나중에 정리해 보겠다. 이렇게 items()를 작성하고 나서, RSS feed에 표시해주고 싶은 내용들을 메서드를 이용해 정의해주면 된다. 위 코드의 item_title(), item_description(), item_link() 의 내용들이다. 참고로 이 메서드의 이름은 임의로 정한 것이 아니라 Feed 클래스 내에 이미 설정되어 있는 것들이다. item_link() 의 경우 해당 글로 바로 연결되는 URL 을 제공해주는 것이다.   #3. 그리고 나서 프로젝트 최상위의 urls.py 에 방금 작성한 feed를 연결할 수 있는 URL 을 매핑해준다. 이렇게 하면 주소창에 windybay.net/feeds/ 를 입력했을때 RSS 피드가 보이게 된다.   여기까지 진행하면 RSS 리더 등에서 feed를 읽어들이는데 문제는 없다. 그런데 다른 사이트들의 경우 RSS feed 링크를 클릭하면 태그에 따라 깔끔하게 정리된 XML 포맷으로 보여지는데, 위의 방법으로 만든 feed 는 지저분하게 일반 text 파일처럼 아무 정리가 안 된 파일이 보이는 것이 문제이다. 예를 들어 아래와 같은 식으로..   그리고 파이어폭스 등 브라우저에 따라서는 feed의 내용이 보이는 것이 아니라 해당되는 파일을 다운로드 하는 창이 바로 열리는 등 불편한 점이 있다. 이것은 Django 가 자동으로 만들어주는 feed 의 기본 타입이 application/rss+xml 이라는 타입이라서 발생하는 문제라고 한다. 컨텐츠의 타입을 application/xml 로 지정해 주면 이런 문제가 해결된다. Django 에서 feed의 타입을 지정해주기 위해 준비해놓은 Class가 여러가지 있는데, 여기서는 DefaultFeed 클래스를 이용해 문제를 해결하면 된다.    #4. 우선 위의 코드에 DefaultFeed를 임포트하는 문을 추가해주고, 이를 상속해서 content_type 을 지정해주는 클래스를 새로 만든다. 그리고 나서 만들어두었던 RecentPostsFeed 클래스 안에 'feed_type' 속성을 지정해주면 된다.   이렇게 하면 브라우저에서 feed의 내용을 읽어 잘 정리된 XML 포맷으로 보여주게 된다.     사실 RSS 피드를 이용해서 글을 구독해본 적이 없어서 관련된 내용을 잘 모르고 있었는데, 내용을 공부하면서 RSS 리더들을 이용해보니 잘 정리된 블로그들을 모아서 구독하는 데는 편리한 점이 있는것 같다. 피드를 작성해 두면 인터넷에 사이트를 노출시키는 데도 도움이 되니 가능하면 RSS 피드도 제공하는 것이 좋겠다.

Django sitemap 프레임워크를 이용해서 사이트맵 만들기 

2020. 02. 20 IT/컴퓨터 > Django
  블로그를 만들고 구글과 네이버 등에 등록을 하고 보니 사이트맵을 제출해야 한다고 한다. 사이트맵이라고 하면 사용자들이 사이트 구조를 파악하기 쉽도록 메뉴를 보기좋게 나열해 놓은 페이지만 생각했었는데, 알고보니 XML 파일로 검색엔진이 사이트 내의 링크 목록을 크롤링하기 쉽게 알려주는 작업이 필요한 것이었다.  사이트맵이 없더라도 검색엔진이 알아서 페이지들을 크롤링하고 링크를 수집할 수는 있지만, 사이트 구조가 복잡하거나 쉽게 노출되지 않는 페이지가 있는 경우에는 사이트 내의 콘텐츠가 검색 결과에 잘 노출되지 않기 때문에 미리 링크가 정리된 XML 사이트맵을 제공해 주는것이 좋다고 한다. 구글과 네이버 검색 등록시에 사이트맵이 제출되어 있으면 더 좋은 점수를 받는다. 웹에서 사이트 주소를 입력하면 XML 파일을 생성해주는 서비스를 하는 사이트들이 있어 그런 곳을 이용하면 쉽게 만들 수 있다고 한다. 하지만 Django로 작업을 한 경우에는 Django 내에 사이트맵을 생성해주는 프레임워크가 내장되어 있어 이것을 이용하면 사이트의 내용이 추가될 때마다 자동으로 업데이트되는 XML 사이트맵을 쉽게 만들 수 있다. 이 포스트에서는 Django를 이용해 사이트맵을 만드는 방법을 정리해 본다. Django 공식 documentation 내용을 주로 참고했다. #1. Sitemap 프레임워크는 sites 프레임워크가 활성화 되어 있어야 동작한다. 우선 프로젝트의 settings.py 파일의 INSTALLED_APPS 목록에 'django.contrib.sites'와django.contrib.sitemaps'   를 추가해 주고 settings.py 안의 적당한 곳에 'SITE_ID = 1' 이라고 지정해 준다.   #2. settings.py 안의 TEMPLATES 설정에서 다음 두 가지 설정이 되어있는지 확인한다. Django 프로젝트를 생성할때 디폴트로 생성되므로 바꾼 적이 없다면 그대로 두면 된다.   #3. 사이트맵의 url 을 설정해 주어야 한다. 프로젝트의 가장 상위의 urls.py 에 다음을 추가해 준다.   이제 도메인/sitemap.xml 에 접근하면 Django 가 동적으로 sitemap 을 생성해 준다. Django 는 sitemap.xml 의 위치를 기준으로 현재 위치와 그보다 하위의 URL 만 포함시키므로, 특별한 이유가 없다면 프로젝트의 root 디렉토리에 sitemap.xml 을 생성해주는것이 좋다.   #4. 사이트맵은 django.contrib.sitemaps 패키지의 Sitemap 클래스를 이용해서 생성한다.  사이트맵의 내용을 담은 sitemaps.py 파일을 따로 작성해 주면 편하다. 우선 블로그의 Post 모델을 불러와서 모든 publish 된 포스트에 대한 링크를 포함하고 있는 사이트맵을 만들어 본다. sitemaps.py 에 새로운 클래스를 생성하고, urls.py 에서 해당 클래스를 포함한 딕셔너리를 #3.에서 지정한  'sitemap.xml' 에 전달해준다.     XML 사이트맵은 URL 정보를 표시하기 위해 기본적으로 <loc> 태그를 반드시 포함해야 하고, 추가로 <lastmod>, <changefreq>, <priority> 태그를 이용해 부가 정보를 표시해줄 수 있다(기타 자세한 사항은 sitemaps.org 를 참고). Sitemap 클래스를 사용하기 위해 items() 메서드가 반드시 포함되어야 하는데, 위에서처럼 QuerySet 을 리턴해주면 사이트맵 제공을 위한 object의 목록을 만들어 준다. Django 에서는 Sitemap 클래스 내의 location() 메서드 혹은 location='value' 속성을 이용해서 <loc> 태그 안에 들어갈 URL을 제공해줄 수 있는데, 따로 명시하지 않으면 items()에서 가져온 object의 get_absolute_url()을 이용해 얻은 URL을 생성해 준다. <lastmod>는 해당 아이템이 최종 수정된 날짜로, 메서드 lastmod() 또는 속성 lastmod='value' 값으로 지정해줄 수 있다. 반드시 파이썬의 datetime 값으로 지정해야 한다. 이 블로그의 경우는 포스트마다 최근 수정한 날짜(modify_date)를 기록하고 있어 그 값을 lastmod 값으로 지정해주도록 메서드를 정의했다. <changefreq>는 메서드 changefreq() 또는 changefreq='value' 속성으로 지정해줄 수 있다. 속성으로 지정해 주는 경우 위처럼 'weekly' 또는 'always', 'hourly', 'daily', 'monthly', 'yearly', 'never' 등으로 지정해줄 수 있다. 이 속성은 검색엔진에 해당 내용이 얼마나 자주 바뀌는지를 알려 주는데, 그냥 참고용 정보일 뿐이고 검색엔진이 지정된 주기로 인덱싱하도록 명령을 내릴 수 있는것은 아니므로, 메서드를 이용해서 아주 엄밀하게 지정해줄 필요는 없을것같고 대략 위의 속성 중에 골라서 지정해주면 될 듯하다. <priority> 는 역시 메서드 또는 속성으로 지정할수 있는데, 0에서 1 사이의 값을 지정하면 된다. 지정해주지 않는 경우 기본 값은 0.5 이다. 이 값은 해당 사이트 내에서 현재 아이템이 얼마나 중요도를 갖는지를 검색엔진에 알려 주려는 목적이고, 다른 사이트와 비교해서 중요도를 나타내는 것은 아니기 때문에 무조건 높다고 좋은것은 아니다. 사이트 관리자가 재량껏 지정하면 되겠다.   #5. 위에서는 models.py 에 정의된 Post 모델에서 QuerySet을 읽어와서 각각의 item을 동적으로 제공했는데, 사이트의 메인 페이지나 기타 정적인 view의 사이트맵을 제공해야 할 필요가 있다. 이를 위해서 sitemaps.py에 정적인 페이지를 위한 클래스를 새로 만들어 준다. items() 에 urls.py 에서 지정한 view의 이름을 리스트 형식으로 리턴해 주고, 이를 urls.py의 sitemaps 변수에 포함시켜 주면 된다. 우선 위에서 작업한 sitemaps.py 에 다음 내용을 추가해준다.   상응하는 urls.py 의 내용은 다음과 같다.   앱 디렉토리 내의 urls.py   #6. 수정사항을 push 해서 반영해주고 도메인/sitemap.xml 로 접속해보면 xml 사이트맵이 보이는 것을 확인할 수 있다.   그러나 잘 보면 사이트 주소가 example.com 으로 우리 홈페이지의 주소가 아닌 것을 볼 수 있는데, 이는 Django admin 에서 수정해 주어야 한다. Admin 화면으로 접속해 보면 'Sites' 항목 아래에 sites 가 있는것을 볼 수 있는데, 여기 들어가보면 site 이름이 example.com 으로 되어 있다.    이 부분을 클릭해서 원하는 도메인 이름으로 바꾸어 준다.   이후 사이트맵에 다시 접속해 보면 정상적으로 도메인이 변경되어 있는 것을 확인할 수 있다.   사이트맵을 작성해 놓으면 검색엔진이 크롤링하기가 수월해지고 그에 따라서 검색 결과에 사이트의 내용이 노출되는데도 도움이 되므로 가능하면 만들어 놓는 것이 좋겠다.

Django 프로젝트에서 robots.txt 파일 제공하기 

2020. 02. 17 IT/컴퓨터 > Django
  Robots.txt 는 웹페이지의 자료를 크롤링하는 검색 엔진 봇들을 위해 어떤 페이지는 정보를 수집해도 되고, 어떤 페이지는 수집을 금할 것인지 알려주는 역할을 하는 간단한 텍스트 파일이다. 검색 엔진 봇들은 홈페이지의 링크를 타고 다니면서 정보를 수집하고 이를 검색 결과에 보여 주므로 홈페이지나 블로그가 효과적으로 검색엔진의 검색 결과에 나오게 하기 위해서 설정을 잘 해 주는것이 중요할 것이다.   내용은 대략 알고 있었지만 사실 실제로 블로그를 만들기 전까지는 현실적으로 어떤 것이 문제가 될 만한 내용인지 와닿지는 않았다. 그런데 블로그를 도메인에 연결하고 공개를 하고 보니 한 가지 치명적인 문제가 발견되었다.    이 블로그는 글 작성후에 publish 버튼을 누르기 전에는 초안 상태로 저장되어 있다가 publish 버튼을 누르면 공개되는 방식으로 만들어져 있는데, 글 작성후 내가 분명히 publish 하지 않고 초안 상태로 놔 두었는데도 어느 정도 시간이 지나면 자꾸 저절로 publish 가 되는 버그가 발견된 것이다. 자동으로 실행되는 함수같은 것을 만든 적도 없는데 왜 이런 현상이 발생하는지 고민이었다.   그런데 서버 로그를 보니 구글 크롤링 봇이 페이지를 들러서 publish 링크를 누르고 다니는 것 같았다. 그래서 아직 publish 되지 않은 글들의 publish 링크에 봇이 접근하지 못하도록 해야 할 필요가 생겼다. 그동안 사이트에 robots.txt 가 없었는데, 구글 봇이 발간되지 않은 글들을 모아놓은 URL에 접근할 수 없도록 해당 내용을 robots.txt에 추가해 주었다. Django 프로젝트에서는 단순히 루트 디렉토리에 robots.txt만 업로드해 놓으면 검색 엔진이 파일을 찾을수가 없고, 따로 설정을 해서 제공해야 되는 것이라서 공부한 내용을 정리해 본다. 크게 세 가지 방법이 있다.   #1. 간단히 해결하려면 urls.py 에 직접 robots.txt 의 내용을 써주면 된다. lambda function 을 활용. 물론 HttpResponse 를 먼저 import 해주어야 한다. 이 방법은 파일 하나만 손대면 되어 간단하지만, robots.txt 의 규칙이 늘어나면  추가해주기가 번거롭고 urls.py 본연의 임무인 url 매핑 이외의 내용으로 파일이 너무 지저분해지는 단점이 있겠다. 원칙적으로 각각의 코드는 자기 본래의 역할에 충실하는 것이 올바른 코딩 방향이므로 이 방법은 별로 추천하고 싶지는 않다.       #2. Django-robots 앱을 사용하는 방법. 링크 - Django-robots documentation robots.txt 를 관리하기 위한 전용 앱이 있다. 를 입력해서 설치할 수 있고 대규모 프로젝트의 경우에는 이 앱을 사용하면 매번 robots.txt를 push 하지 않고도 편리하게 관리할 수 있다고 한다. 혼자 쓰는 블로그같은 소규모 프로젝트에는 직접 robots.txt 를 수정해 주는 정도로 충분한것같아 아직 사용해보지는 않았다.    #3. 실제 robots.txt 를 작성하여 업로드후 url을 지정해 주는 방법. 우선 robots.txt를 작성하여 원하는 디렉토리에 업로드한다. urls.py에서 경로를 정해 줄 것이기 때문에 아무데나 둬도 상관 없지만, 가급적 base.html 이 있는 프로젝트의 가장 root의 template 디렉토리에 넣는것이 관리 면에서 좋겠다. robots.txt를 업로드 후 프로젝트의 메인 urls.py 에 다음과 같이 설정해준다. 일반 html template 과 다르게 타입을 plain text 로 지정해 주는것이 추가된다. root template 디렉토리가 아닌 곳에 robots.txt를 둔 경우에는 적절하게 경로를 바꿔 주면 되겠다. 함수형 view를 사용할수도 있지만 generic class based view 인 TemplateView를 이용하면 간단하다.  robots.txt를 직접 읽어서 서빙해 주는 방식이다. 각각의 파일이 본래 목적에 맞는 동작을 하는 점에서 1번보다는 더 좋은 방식이다. 검색 규칙을 바꾸는 것도 직접 txt 파일만 수정하면 되기 때문에 편리하다. 파일의 규칙이 자주 바뀌는 경우 로컬에서 파일 수정후 서버에 push 해 줘야 하는것은 귀찮을 수도 있겠다. 하지만 이미 완성된 사이트라면 robot 검색 규칙을 바꿀 일은 별로 없을 듯.   이외에 사용하는 서버에 따라 직접 robots.txt를 업로드하고 서버 세팅을 변경해서 Django 를 거치지 않고 제공하는 방법도 있지만, 일을 두 번 해야 하므로 번거롭고 서버마다 세팅이 달라 일괄 적용하기 어려운 점이 있을 것이다. 이 블로그에서는 #3의 방법으로 사용중이다. robots.txt를 설정해주면 검색엔진이 적절히 자료를 수집하는데 도움을 주고 불필요한 수집이나 오동작을 방지할 수 있으므로 어느 홈페이지든지 적절하게 설정해 주는것이 좋겠다.   다만 robots.txt 의 내용은 어디까지나 로봇에게 일러 주는 권장사항일 뿐 기술적으로 접근을 차단하는 것은 아니다. 정상적인 로봇이라면 권장사항을 준수하면서 크롤링하겠지만 악성코드의 경우 이를 무시하고 돌아다닐 수도 있으므로 백엔드에서 근본적으로 접근을 차단할 방법은 별도로 강구해야 할 것이다.   (다시 원래의 publish 기능 얘기로 돌아가면, 근본적으로는 해당 문제는 프로젝트의 소스 코드를 수정해서 해결해야 했다. Publish 기능이 담긴 view를 장고의 @login_required 데코레이터를 이용해서 막아 주어서, 글을 publish할 수 있는 링크는 로그인한 유저에게만 노출되도록 권한 조정을 통해서 해결했다.)

Pythonanywhere.com에 Django 프로젝트 배포하기 (2) 

2020. 02. 15 IT/컴퓨터 > Django
이전편에 이어서 pythonanywhere.com 을 이용해 Django 프로젝트를 배포해 본다. 이전 글: Pythonanywhere.com에 Django 프로젝트 배포하기 (1)     #16. Web tab에 가면 [ 자기id.pythonanywhere.com ] 도메인 이름을 확인할 수 있는데, 이 시점에서 브라우저에 이 URL을 입력해보면 기본 Hello, World! 앱을 볼 수 있다.     #17. 우리가 만든 가상 환경의 이름을 Web 탭 중간쯤에 있는 Virtualenv: 섹션에 입력해준다. 주소는 [ /home/자기id/.virtualenvs/myproject ] 와 같다. 기억이 잘 나지 않는 경우 dashboard에서 Files 탭에 가서 루트 디렉토리에서 .virtualenvs/ 디렉토리로 들어가 보면 자기가 만든 가상환경의 이름을 확인할 수 있다. 가상환경을 등록하고 나면 아래에 그 가상 환경에서 작업할 수 있는 console 링크가 생긴다.   #18. Virtualenv 위의 Code: 섹션에 source code 를 입력해준다. 형식은 [ /home/자기id/프로젝트 디렉토리 이름 ] 과 같다. manage.py 가 있는 디렉토리가 맞는지 확인하고 입력한다.   #19. Code 섹션에 있는 WSGI configuration file을 클릭하면 편집기가 나온다. 중간의 Hello world 관련 부분은 주석 처리하거나 지워서 Hello, World! 화면이 나오지 않게 해주고   #20. 같은 파일 아래쪽을 보면 DJANGO 설정 부분이 있는데, 이 부분의 주석처리를 없애 실행되도록 만들어 주면 된다. 이때 path 에는 아까 Code 섹션에서 지정해준 source code 디렉토리 (manage.py가 있는) 를 지정해주고, settings 경로는 path 디렉토리 아래에 있는, settings.py 가 있는 디렉토리를 지정해 준다.   #21. 이제 거의 다 되었는데, 브라우저에 자기id.pythonanywhere.com 주소를 입력해 본다. 이때 다음과 같은 에러 메세지가 나올 수 있다. 이를 해결하기 위해 #20에서 지정한 settings.py 파일을 열고, ALLOWED_HOSTS 에 [ 자기id.pythonanywhere.com ] 을 추가해 준다.   ALLOWED_HOSTS 수정 후 프로젝트의 첫 페이지 화면이 로딩된 것을 확인할 수 있다.    #22. 그러나 사실 아직 static file 경로가 설정되지 않아 css 파일등 정적 파일이 로딩되지 않았다. 우선 #20에서 지정해주었던 settings.py 를 찾아 STATIC_URL과  STATIC_ROOT 을 지정해 주어야 한다. 주의할 점은 STATIC_ROOT를 STATIC_URL과 겹치지 않는 이름으로 지정해 주어야 한다. 여기서는 URL은 /static/, ROOT는 루트 디렉토리 아래의 /web_staticfiles/ 라고 지정했다.   #23. 이후 Web tab으로 돌아와 Static files: 섹션을 찾아 URL 을 지정해 준다. #22에서 지정했던 디렉토리를 입력해 주면 된다.    #24. Bash console 을 열고, 커맨드를 실행해 준다. 이 커맨드를 실행하면 프로젝트 내의 정적 파일들을 위에서 지정해준 STATIC_ROOT 디렉토리로 모아서 실제 웹 서비스가 가동될 때 이곳에서 파일을 읽을 수 있게 해 준다.   #25. 이제 css 파일이 정상적으로 로딩되어 지정해준 폰트가 적용된 것을 볼 수 있다.     이상으로 pythonanywhere.com 에서 Django 프로젝트를 배포해 보았다. Microsoft azure나 Heroku 등의 서비스에서 배포하려고 하다가 설정이 너무 복잡하고 에러가 많아 어려움을 겪었는데, pythonanywhere에서는 상대적으로 간단히 배포가 되는것같다. 호스팅 업체 규모가 크지는 않은것 같아서 대규모 프로젝트에서의 동작은 어떨지 모르겠지만, 개인용 블로그같은 사이트를 쉽게 배포할 때 이용하기는 좋을 것같다.

Pythonanywhere.com에 Django 프로젝트 배포하기 (1) 

2020. 02. 15 IT/컴퓨터 > Django
  국내 웹 개발 환경은 PHP 가 대세이고 호스팅 업체들 역시 대부분 PHP 호스팅만 지원하는 실정이라 장고 프로젝트를 배포하기 쉽지 않다. 서버 호스팅을 하면 되겠지만 맨땅에 리눅스부터 깔고 세팅할 것이 많아 초보자로서는 어려운 점이 많았다. Pythonanywhere.com 이라는 사이트가 있는데, 번거로운 세팅작업을 최소화하고 쉽게 Django 프로젝트를 배포할 수 있도록 해 주는 사이트이다. 이름에서 알 수 있듯이 파이썬 언어에 최적화된 서비스를 제공한다. 프로젝트 배포는 가끔 하게 되는 일이라 처음 세팅 내용은 할때마다 약간씩 삽질을 하게 되는데, 오늘은 다음 프로젝트를 위해 복기도 할 겸 배포 작업을 순서대로 정리를 좀 해보려고 한다.   #1. GitHub을 통해서 배포가 이루어지기 때문에 미리 프로젝트를 Github에 프로젝트를 업로드해서 repository를 만들어 두어야 한다.   #2. 링크 - Pythonanywhere 간단한 가입과정을 거치면 요금제를 선택할 수 있다. 이 블로그는 Hacker 계정으로 운영중인데(월 5달러), 무료인 Beginner 요금제도 배포하고 실제 동작을 체크하는 데에는 문제가 없다. 다만 자기의 domain 을 쓸 수 없고 주어지는 도메인을 써야 한다. 가입후 등록한 email 주소로 오는 확인 메일을 confirm 해주면 정상적으로 계정을 사용할 수 있다.   #3. 로그인하면 볼 수 있는 Dashboard에서는 console 창을 띄우거나 직접 파일관리를 하는 등의 여러 작업을 할 수 있는데, 우선 배포를 위해 Web app을 만들어야 하므로 "Web" 탭으로 들어가 Add a new web app 을 클릭한다.       #4. 도메인 이름을 지정해줄 수 있는데, 아직은 도메인이 없으므로 자동으로 생성해주는 도메인을 사용한다. 만약 도메인을 연결하려는 경우에는 직접 도메인을 입력해주면 되는데, 이때 앞에 www가 포함된 도메인인지 아닌지를 정확히 입력해줘야 한다. 이것을 정확히 하지 않으면 오류가 나면서 페이지에 접속이 안되는데, 나중에 수정이 안되고 web app 전체를 지우고 다시 만들어야 되기 때문에 처음에 신경쓰는것이 좋다.   #5. Django 프로젝트이지만, "Django" 를 클릭하면 처음부터 Diango project를 만드는 과정이 시작된다. 지금처럼 이미 로컬에서 만들어진 프로젝트가 있는 경우는 "Manual configuration" 을 선택해주어야 한다. 수동 구성을 클릭하면 설치할 Python 의 버전을 선택할 수 있는데 프로젝트에 사용한 버전으로 골라주면 된다.     #6. 무료 계정의 경우 3개월동안 접속이 없으면 계정이 비활성화된다. 계정을 유지하려는 경우 버튼을 클릭해서 3개월동안 사이트를 활성화하는 작업을 해준다. 유료 계정의 경우에는 필요없는 작업이다.   #7. 다음 작업을 위해 Console tab으로 가서 Bash console을 연다.         #8. 먼저 가상환경을 만들어줘야 한다. 커맨드로 자신이 사용하는 파이썬 버전과 사용할 가상환경의 이름을 입력해주면 된다. 필요한 파일이 설치된 후 프롬프트 앞에 (myproject) 처럼 가상환경 이름이 보이면 정상적으로 설치된 것이다.   #9. Django 및 필요한 패키지들을 가상환경에 설치해준다. 위의 명령으로 현재 설치된 패키지들을 볼 수 있다. Django 설치를 위해 위와 같이 프로젝트에 사용된 버전을 명시해서 설치해준다. 버전을 생략하고 pip install django 만 입력하면 최신버전이 설치된다.  기존에 설치된 버전을 최신 버전으로 업그레이드하는 경우에는 다음 명령어를 입력하면 된다     #10. Django가 설치된 위치를 로 확인할 수 있다.   #11. 이제 Github의 repository로 가서 repository의 주소를 복사해 온다. Clone or download 버튼을 누르면 주소가 표시되는데, 주소 옆의 클립보드 버튼을 누르거나 Ctrl+C 로 복사할 수 있다.   #12. 다시 console로 돌아와서 명령어로 원격 repository를 다운로드한다.   #13.  ls 커맨드로 디렉토리를 확인하고 해당 디렉토리에 들어가보면 (master) branch 를 확인할 수 있다. 해당 디렉토리에 manage.py 파일이 있어야 정상적으로 작업하고 있는 것이다.   #14. 데이터베이스 테이블을 만들고 migration을 적용시키기 위해 순서로 커맨드를 실행해 준다.   #15. 사이트 관리자인 superuser 계정을 명령을 이용해 만들어준다.   이제 다시 pythonanywhere의 Web tab으로 돌아가 세부 설정을 해 주어야 한다.  다음편에 이어서.. Pythonanywhere.com에 Django 프로젝트 배포하기 (2)
  • 1 (current)