알고리즘 공부, 코딩 테스트 연습을 안한지 꽤 오래 되어 문제를 보기 전부터 많이 긴장 했었다. 그리고 당일이 되어 문제가 공개되고 문제를 풀기 시작하는데 코딩 테스트를 안한지도 오래되고 긴장도 해 단순 입출력을 어떻게 받는지 등이 잠깐 생각이 안났지만 문제를 보다보니 기억이 점점 돌아와 가볍게 풀 수 있었다.
문제의 핵심은 소수점 이하 버림을 해결할 수 있느냐의 문제인것 같다.
처음에는 문제를 아무리 봐도 맞게 푼 것 같은데 몇몇 문제에서 오답이 나와 구름 유니버스에서도 계속 도움을 요청하며 돌아다녔다. 아직 문제가 나온지 얼마안된 초반이라 푼 사람도 몇명이 없어서 도움을 못받고 포기하고 저녁을 먹고 다시 돌아와 다시보니 Math.floor 라고 적어야 하는데 Math.round 라고 작성해 소수점 이하 버림이 아닌 소수점 반올림으로 작성했던것이였다. 그래서 다시 Math.floor 라고 수정해 통과할 수 있었다. 코딩 테스트가 오랜만이라 Math 함수 등도 오랜만에 써서 단순 실수 였었다.
이제 파이보게시판을 만들어서 누구나 접속할 수 있게 되었다. 하지만 점프 투 장고 에서 추가로 설명한 도메인설정, SSL, PostgreSQL 등 도 하지 않았다. 보안이나 관리의 편의성 등에 문제가 있을 수 있는 게시판이지만 유료로 도메인을 등록할 필요성등을 아직 못느꼈기 때문에 하지 않았다. 점프 투 장고 온라인 책을 보며 만들었는데 따라서 파이참, 장고, DB, AWS, 등 대부분이 다 처음 사용해보는것들이였다. 이제 책없이 처음부터 다시 만들라고 하면 못만들것 같긴하지만 그래도 한번 만들어 본 경험이 있기 때문에 책을보며 다시 만든다면 막히는 부분없이 잘 만들 수 있을것같다. 학교에서 그냥 프로그래밍 언어만 배우며 얻지 못했던 지식들이나 학교에서 제대로 공부하지 않았던 부분을 다시 배우며 개발과정 전체의 흐름을 이해하는 계기가 되었다. 파이썬과 장고가 내가 미래에 사용할 주된언어가 될지 아닐지는 모르지만 기본적인 틀을 배웠다고 생각한다. 그냥 학교에서 내주는 하나의 과제가 아닌 스스로 공부하며 시작해보는 프로젝트 자체가 처음이라 정말 좋은 경험이였다. 점프 투 장고를 배우며 다시 정리하는 느낌으로 블로그를 작성했는데 말솜씨가 좋지 않아 점프 투 장고 온라인 책을 그대로 옮겨적는 것 같은 느낌이 되버린것이 정말 아쉽다. 이게 책의 저자님께 피해가 가거나 그렇게 되지를 않기를 바라고 있다. 마지막으로 이 책의 저자님께 감사하다는 말을 드리고 싶다. 다른 사람들도 한번씩 해보는것도 좋을것 같다.
저번 게시물에서 DEBUG=False 로 설정했기 때문에 파이보 이용중 오류가 발생한다면 404오류같은 페이지가 출력되고 오류의 원인같은 정보는 출력되지 않는다. 이제 오류의 원인이 무엇인지 파악을 할 수 없는데 그렇다고 DEBUG=True 로 변경할수는 없다. 이를 해결하기 위해 로그 파일을 이용하는것이 가장 좋다.
disable_existing_loggers 항목은 False로 설정했다. True 로 설정하면 기존에 설정된 로거들을 사용하지 않는다. 파이보도 False 로 설정할 것이다.
filters 는 특정 조건에서 로그를 출력하거나 출력하지 않기 위해서 아용된다. require_debug_false 필터는 DEBUG=False 인지 판단하는 필터이고 require_debug_ture 필터는 디버그가 True 인지 판단하는 필터이다.
formatters 는 로그를 출력할 형식을 정한다. 포맷터에 사용된 항목은 server_time(서버의 시간), message(출력 내용) 이다.
handlers 는 로그의 출력방법을 정의한다. console은 콘솔에 로그를 출력한다. 로그레벨이 INFO 이상이고 디버그가True 일때만 로그를 출력한다.
django.server 는 python manage.py runserver 로 작동하는 개발 서버에서만 사용하는 핸들러로 콘솔에 로그를 출력한다. mail_admins 는 로그내용을 이메일로 전송하는 핸들러로 로그레벨이 ERROR 이상이고 DEBUG=False 일때만 로그를 전송한다. 이 핸들러를 사용하려면 환경설정파일에 ADMINS 항목을추가하고 관리자 이메일을 추가해줘야한다. 그리고 이메일발송을 위한 SMTP 설정도 해줘야한다.
loggers 항목은 로그를 출력하는 프로그램에서 사용하는 로거의 이름을 의미한다. django 는 장고 프레임워크가 사용하는 로거이고 로그 레벨이 INFO이상일 경우에만 로그를 출력한다. django.server 는 개발서버가 사용하는 로거로 로그레벨이 INFO 이상일 경우에만 로그를 출력한다. 'propagate' : False 의 의미는 django.server 가 출력하는 로그를 장고 로거로 전달하지 않는다는 의미이다. True 로 최상위 패키지명이 django 로 동일하기 때문에 django.server 하위 패키지에서 출력하는 로그가 django.server 로거에도 출력되고 django 로거에도 출력되어 이중출력 현상이 발생한다.
로그레벨은 5단계로 구성된다. logging.debug, logging.info, logging.warning, logging.error, logging.critical 함수로 출력된다.
1단계 DEBUG : 디버깅 목적으로 사용된다.
2단계 INFO : 일반 정보를 출력할 목적으로 사용된다.
3단계 WARNING : 경고 정보를 출력할 목적으로 사용된다. 주로 작은문제를 출력한다.
4단계 ERROR : 오류 정보를 출력할 목적으로 사용된다. 주로 큰 문제를 출력한다.
5단계 CRITICAL : 아주 심각한 문제를 출력할 목적으로 사용된다.
로그는 설정한 레벨 이상의 로그만 출력된다. 예를들어 핸들러나 로거의 레벨을 INFO로 설정하면 DEBUG로그는 출력되지 않고, INFO 이상의 로그 INFO, WARNING, ERROR, CRITICAL 로그들만 출력된다.
이 파일들을 적용하기위해 먼저 projects\mysite\config\settings\base.py 에 위에 적힌 DEFAULT_LOGGING 을 복사하자.
복사를 그대로 하는데 이름만 DEFAULT_LOGGING 에서 LOGGING 으로 변경해주자.
그리고 포맷터 standard 를 추가하자.
standard 포맷터에 사용한 항목들은 asctime(현재시간), levelname(로그의 레벨,단계), name(로거명), message(출력 내용) 이다.
handlers 에 file 핸들러도 추가해주자.
file 핸들러에 사용한 항목은
level : 출력 레벨로 INFO를 사용
filters : DEBUG=False 인 운영환경에서 사용
class : 파일 핸들러로 RotatingFileHandler 사용. RotatingFileHandler 는 파일 크기가 설정한 크기보다 커지면 파일 뒤에 인덱스를 붙여서 백업한다. 이 핸들러의 장점은 로그가 무한히 증가해도 일정 개수의 파일로 Rolling 되기 때문에 로그 파일이 너무 켜저 디스크가 꽉 차는 위험을 방지한다.
filename : 로그 파일명은 logs 디렉토리에 mysite.log 로 설정
maxBytes : 로그 파일의 최대 크기는 5MB
backupCount : 롤릴되는 파일의 개수는 총 5개로 설정
formatter : 포맷터는 standard를 사용.
마지막으로 django 로거의 handlers 에 file 핸들러를 추가하자.
설정을 다 완료한 후 git으로 서버에 적용하자. 서버에 적용한 후 서버에 logs 디렉토리를 만들어 주자.
개발환경에서도 projects\mysite\logs 디렉토리를 만들어주자. 그리고 logs 디렉토리는 버전 관리 대상이 아니므로 .gitignore 파일에 logs 디렉토리를 추가하자.
잘 적용되었나 테스트를 해보기 위해
projects\mysite\pybo\views\base_views.py 파일에 다음과 같이 강제로 오류를 발생시켜보자.
index 함수가 호출되면 3을 0으로 나누려고 하기 때문에 ZeroDivisonError 가 발생한다. 깃을 이용해 서버에 적용하자. 설정한 후 서버에서 파이보 메인 페이지에 접속해보자. 'Server Error (500)' 오류가 발생한다. 이제 로그를 확인해보자.
어떤 오류가 발생했는지 로그파일에 출력된다. 앞으로는 tail -f mysite.log 로 로그를 확인하자. 이 명령을 하면 mysite.log 파일에 로그가 깧일 때마다 로그의 내용이 자동으로 출력된다.
지금까지 설정한 로깅은 장고가 사용하는 django 와 django.server 라는 로거로만 사용했다. 이번엔 새로운 로거를 생성해 파이보 프로그램에서 로그를 출력해보자.
먼저 projects\mysite\pybbo\views\base_views.py 를 수정하자.
적어 뒀었던 3/0 강제오류 코드를 지우고 사진과 같이 수정하자. 로그 파일에 로그를 출력하기 위해서는 logging 모듈이 필요하다. logger = logging.getLogger('로거명') 으로 얻은 logger 객체를 이용해 logger.debug, logger.error, logger.warning 등의 함수를 이용해ㅑ 로그를 출력할 수 있다. 이렇게 설정해도 'INFO 레벨로 출력' 이라는 문장은 로그로 출력되지 않는다. 그 이유는 위 코드에서 사용한 pybo 라는 로거는 LOGGING 설정에 등록되지 않은 상태이기 때문이다.
이를 등록하기 위해 projects\mysite\config\settings\base.py 를 다음과 같이 수정하자.
장고 환경셜정 파일에는 DEBUG 라는 항목이 있다. 이 항목은 기본값이 True 이지만 장고 공식 문서에는 '운영 환경에서 DEBUG는 반드시 False로 해야한다' 라고 되어있다.
장고는 실행 도중 오류가 발생하면 디버그가 True 인 경우 오류내용을 상세하기 출력한다. 이 때 settings.py 나 urls.py 같은 파일에 설정한 항목들도 노출된다. 정보 노출이나 해킹 등의 위험 때문에 False 로 해야한다.
projects\mysite\config\settings\prod.py 를 수정하자.
그리고 깃을 커밋해 서버에 적용하자. 하는 방법은 전 게시물을 확인하자.
그 후 http://고정ip/asdf 같이 존재하지 않는 페이지로 접속해 보자.
이전에는 오류의 원인같은 다양한 메시지가 적힌 페이지가 출력되었는데 이제는 아무런 정보도 표시되지 않는다.
정보를 표시하지 않기는 하지만 페이지가 단조롭기 때문에 꾸며보자. 꾸미기 위해 먼저
projects\mysite\config\urls.py 를 수정하자.
handler404 변수를 설정하면 404 오류 발생 시 사용자가 정의한 뷰 함수가 호출된다. 우리는 적어 넣은대로 common/views.py 파일의 page_not_found 함수가 호출될것이다. 이 handler404 변수는 반드시 config\urls.py 에 등록해야한다. common\urls.py 같은 곳에 등록하면 작동하지 않는다. 호출할 함수를 설정했으므로 이제 그 함수를 정의해보자.
projects\mysite\common\views.py 를 수정해주자.
함수가 받는 매개변수중 exception 매개변수는 오류의 내용을 담고있다. 오류의 내용을 화면에 보여주려면 exception 값을 읽어 화면에 출력할 수도있다. 함수에 common/404.html 을 리턴한다고 했으므로 그 파일을 만들어주자.
projects\mysite\templates\common\404.html 을 새로 만들고 다음과 같이 작성해주자.
그리고 깃을 이용해 서버에 적용하자. 서버에 적용한 후 구니콘을 restart 해주자. 이제 파이보를 확인해보자.
너무 쉬운 비밀번호로 생성하려하면 재확인 메세지가 나온다. 그리고 http://고정ip/admin 로 접속해보자.
Nginx가 장고 Admin 에서 사용하는 정적 파일을 제대로 읽지 못해서 이렇게 응답을 한다. 엔진엑스가 바라보는 정적 파일은 /home/ubuntu/projects/mysite/static 디렉토리에 위치해야 한다. 장고 어드민이 사용하는 정적파일들은 다른위치에 있다 이를 해결하기 위해 장고 환경설정 파일에 STATIC_ROOT 디렉토리를 설정하고 python manage.py collecstatic 명령을 수행해 관리자 앱의 정적 파일을 STATOC_ROOT 디렉토리로 복사해야 한다.
먼저 projects\mysite\config\settings\prod.py 파일에 STATIC_ROOT 항목을 추가하자.
BASE_DIR은 /home/ubuntu/projects/mysite 이다. 엔진엑스의 정적 파일위치를 /home/ubuntu/projects/mysite/static 디렉토리로 등록했기때문에 STATIC_ROOT 도 똑같이 설정해줘야한다.
그리고 STATICFILES_DIRS = [] 도 추가해준다. 추가해주는 이유는 base.py 파일에는 STATICFILES_DIR 항목이 이미 있는데 prod.py 에 다시 빈 값으로 설정하는 이유는 STATIC_ROOT 가 설정된 경우 STATICFILES_DIRS 리스트에 STATIC_ROOT 와 동일한 디렉토리 포함되어 있으면 서버 실행시 오류가 발생하기 때문이다. 이를 방지하기 위해 prod.py 파일에 STATICFILES_DIRS = [] 로 설정했다.
파일을 수정했기 때문에 git 에 커밋하자.
커밋 후 내려받자
git pull 로 내려받은 후 서버에 프로그램이 변경되었으므로 Gunicorn 을 재시작 하기위해 sudo systemctl restart mysite.service 명령을 해주자.
이제 python manage.py collecstatic 명령을 수행해 관리자 앱의 정적 파일을 복사하자.
yes 를 입력해 진행하면 복사가 성공되었다는 메세지가 출력된다. static 디렉토리로 이동 후 ls -l 로 확인해 보면 정적파일들이 다 있는것을 확인할 수 있다.
웹서버는 브라우저의 정적 페이지 요청을 처리하고 동적 페이지 요청은 WSGI 서버를 호출하는 역할을 한다. 이번엔 파이보가 사용할 웹 서버인 Nginx 를 설치하고 적용해보자.
그리고 /etc/nginx/sites-available 디렉토리로 이동하자. 이 디렉토리는 Nginx의 설정 파일들이 위치한 디렉토리다. 최초 설치시에는 default 라는 설정파일만 존재한다. 해당 위치로 이동 후 sudo vi mysite 파일을 생성해 작성한다.
listen 80 은 웹 서버를 80포트로 서비스한다는 의미이다. HTTP 프로토콜의 기본포트는 80이다. 이제 http://고정ip:8000 대신 포트를 생략해 http://고정ip 만으로 접속할 수 있다.
server_name 에는 고정 ip 를 등록한다.
location /static 은 종적 파일에 대한 설정으로 /static 으로 시작되는 url 요청은 엔진엑스가 /home/ubbuntu/projects/mysite/static 디렉토리의 파일을 읽어서 처리한다는 설정이다.
location / 은 location /static 에서 설정한 것 이외의 모든 요청은 구니콘이 처리하도록 하는 설정이다. proxy_pass 는 동적 요청이 발생하면 해당요청을 구니콘의 유닉스 소켓으로 보내라는 설정이다.
한마디로 /static 으로 시작되는 url 은 엔진엑스가 나머지 url 은 구니콘이 처리하게 된다.
이제 작성한 mysite 파일을 Nginx 가 환경파일로 읽을 수 있도록 설정하자.
먼저 cd /etc/nginx/sites-enabled 디렉토리로 이동하자. 이 디렉토리는 site-available 디렉토리에 있는 설정 파일 중에서 활성화 하고 싶은 것을 관리하는 디렉토리 이다. ls 명령을 하면 현재 default라는 설정 파일만 링크되어있는데 default 를 삭제하고 mysite 파일을 링크하도록 변경해야 한다.
sudo ln -s /etc/nginx/sites-available/mysite 로 링크한후 ls 로 확인해보면 링크 되어있다.
Nginx 설치할 때 자동으로 실행되므로 앞에서 작성한 엔진엑스 설정을 적용하려면 엔직엑스를 다시시작해야한다.