플러터, PM 등 팀스터디를 시작하기 전까지 시간이 조금 남았고, 개강 첫 주라 OT기간이라 과제가 없어 여유가 있어서 간단하게 찍먹해보기에 좋은 게 뭐가 있을까 고민하다가 공대도서관에서 내 눈에 들어온 Django를 실습 없이 아주 간단한 이론만 정리해볼까 한다. 장고 얘기가 나왔을 때 어느정도 큰 틀을 이해할 수 있을 정도로만!
교재는 김석훈 저자의 Django로 배우는 쉽고 빠른 웹개발 파이썬 웹프로그래밍을 참고했다!
Samsung Note에 태블릿으로 정리하다가 하던 부분은 사진으로 벨로그에 올리고 나머지 부분은 앞으로 여기에 채워나가도록 하겠다 !
여기까지가 pdf에 정리한 부분
MVT 방식에서 사용자에게 보여주는 화면 즉, UI를 담당하고 있는 기능이 템플릿 시스템이다. 템플릿 코드를 작성 할 때는 HTML 코드와 장고의 템플릿 코드가 섞인다. 하지만 중요한 점은 템플릿에서는 로직을 표현하는 것이 아니라 사용자에게 어떻게 보여줄지에 대한 룩앤필(look and feel)을 표현한다는 것이다. 즉! 템플릿 코딩은 프로그래밍이라기보다는 화면 구현이다. 템플릿 코드에도 if, for 태그 등이 있지만 이것들은 파이썬 언어의 문법과는 다르고, 템플릿 시스템에서만 사용되는 고유의 문법들이다
예상보다 깊게 하는 것 같아서 기록 없이 넘어가려했지만 CSRF, XSS처럼 정보보호병으로 군생활 할 때 자주 봐서 익숙한 용어들이 나와서 반가운 마음에 템플릿태그 부분을 정리하기로 했다!
템플릿 태그는 {% tag %}형식을 가지며, 템플릿 변수나 템플릿 필터에 비해 좀 더 복잡하다. 텍스트 결과물을 만들기도 하고, 템플릿 로직을 제어하기도 하며, 외부 파일을 템플릿 내부로 로딩하기도 하는 역할을 수행한다.
(참고) 탬플릿 태그는 {%for%} 태그와 {%if%}태그를 가장 많이 사용한다.
이 태그를 사용하면 리스트에 담겨 있는 항목들을 순회하면서 출력할 수 있다.
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
이 예제는 운동선수 리스트에 들어있는 항목을 순회하면서 각 운동 선수의 이름을 보여주는 문장이다. 그리고 loop를 돌면서 사용할 수있는 여러가지 변수들을 제공하고 있는데 정리는 안 하겠다. 궁금하면 찾아보도록 하자 ~
이번엔 if태그이다. 변수를 평가하여 Ture라면 바로 아래의 문장을 표시한다.
그리고 반가운 CSRF태그! POST 방식의 form태그를 사용하는 템플릿 코드에서는 CSRF공격을 방지하기 위해 이 태그를 사용해야 한다. 폼 데이터에 악의적인 스크립트 문장이 들어있을 수도 있기 때문!
<form action="." method="post">{% csrf_token %}
이렇게 form태그 엘리먼트의 첫 줄 다음에 바로 넣어주면 된다. 이 태그를 넣어주면, 장고는 내부적으로 CSRF토큰값의 유효성을 검증한다. 만일 CSRF토큰값 검증에 실패하면 사용자에게 403 ERROR를 보여준다. 단, 주의해야 할 점이 있다. CSRF토큰 값이 유출될 수도 있기 때문에 외부로 URL을 보내는 form에는 이 태그를 사용하지 말도록 하자 !
자 그러면 질문 ! 외부로 URL을 내보내는 form에는 이 태그를 사용하지 않고 어떻게 보안을 유지할 수 있을까? GPT에게 물어서 얻은 답변을 정리하도록 하겠다 !!!
외부 웹사이트 또는 서비스로 데이터를 제출할 때는 CSRF 토큰을 노출하지 않는 것이 중요합니다. 이를 위해서는 다음과 같은 접근 방식 중 하나를 고려할 수 있습니다
토큰을 노출하지 않는 폼 분리: 외부로 제출할 데이터를 포함하는 폼과 CSRF 토큰을 포함하는 폼을 분리합니다. 사용자로부터 데이터를 입력받는 폼과 제출 버튼을 가진 별도의 HTML 페이지를 생성하고, 이 페이지에서 제출 버튼을 누르면 데이터를 처리하는 뷰로 리디렉션하도록 구현할 수 있습니다. 이렇게 하면 CSRF 토큰은 외부로 노출되지 않습니다.
Ajax 요청 사용: 데이터를 외부로 보내야 하는 경우, Ajax 요청을 사용하여 백엔드로 데이터를 전송할 수 있습니다. 이 경우 JavaScript를 사용하여 CSRF 토큰을 요청 헤더에 추가할 수 있으며, 이로써 토큰은 브라우저에 노출되지 않습니다.
예를 들면 아래와 같이 jQuery를 사용하여 Ajax 요청을 보내는 경우 이렇게 CSRF토큰을 헤더에 추가할 수 있다고 한다.
$.ajax({
url: "/external-endpoint/",
type: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken") // CSRF 토큰을 가져와서 헤더에 추가
},
data: {
// 데이터를 여기에 추가
},
success: function(response) {
// 성공적인 응답 처리
},
error: function(error) {
// 오류 처리
}
});
CSRF는 Cross-Site Request Forgery의 약자로, 사이트 간 요청 위조 공격이다. 웹 사이트의 취약점을 공격하는 방식 중의 하나로, 특정 웹 사이트에서 이미 인증을 받은 사용자를 이용하여 공격을 시도한다. 인증을 받은 사용자가 공격 코드가 삽입된 페이지를 열면 공격 대상이 되는 웹 사이트는 위조된 공격 명령이 믿을 수 있는 사용자로부터 발송돈 것으로 판단하기 때문에 공격을 받게 되는 방식이다.
URL 태그도 많이 사용되는데, 주 목적은 소스에 URL을 하드코딩 때리는 것을 방지하기 위함이다.
with 태그는 특정 값을 변수에 저장해두는 기능을 한다.
load 태그는 사용자 정의 태그 및 필터를 로딩해준다.
태그 및 필터는 장고에서 기본적으로 제공하는 것 외에도, 개발자가 필요에 따라 스스로 정의하여 사용할 수 있는데, 이런 것을 사용자 정의 태그, 사용자 정의 필터라고 한다. 이런 태그나 필터를 사용하기 위해서는 사용하기 전에 먼저 로딩을 해줘야하는데 그 역할을 수행하는 태그이다.
템플릿 코드에서도 주석을 쓸 수 있는데 두 가지 방법이 있다.
첫째, 한 줄 주석문으로 {# #} 형식이다.
{# greeting #}hello
이렇게 하면 greeting은 주석 처리되고, hello문구만 나타난다.
둘째, 여러 줄의 주석문으로 {% comment %} 형식이다.
예제는 필요할 때 찾아보삼 쉬움.
자자 이거 중요하고 재밌었음. 템플릿 코드를 이제 렌더링을 해서 HTML 텍스트를 만들 때, 주의해야 할 사항이 하나 있음. 만일 템플릿 변수에 HTML 태그가 들어있는 경우, 그대로 렌더링하면 원하지 않는 결과가 나올 수 있기 때문. 왜냐하면 웹 브라우저에 표시될 때 html태그랑 섞이기 때문임. 아까 위에서도 설명했듯!
그리고 이 약점을 이용해서 공격하는 방법이 XSS 공격임. XSS에 대한 설명만 적고 이어서 설명하겠음!
그 유명한 XSS !! Cross-Site Scripting이다. 정보보호병 면접 때도 열심히 공부해갔는데 면접 때 집요하게 물어봐줘서 집요하게 대답해서 낮은 스펙으로 합격했던 기억이 있어서 아주 좋아하는 녀석이다. 아무튼! XSS는 웹 사이트의 취약점을 공격하는 방식 중의 하나로, 웹 사이트 관리자가 아닌 일반 사용자라도 시도할 수 있는 공격 방법이다. 주로 여러 사용자가 보게 되는 전자 게시판에 악성 스크립트가 담긴 글을 올리는 형태로 이루어진다. 그래서 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다. 이 취약점은 웹 애플리케이션이 사용자로부터 입력받은 값을 제대로 검사하지 않고 사용할 경우 나타난다. SQL인젝션이랑 해킹공격의 기본 양대산맥 방법이다. 그만큼 엔간한 XSS 공격은 거의 안 먹힌다. 흔하기에...
자, 그렇기 때문에 사용자가 입력한 데이터를 그대로 렌더링하는 것은 위험할 수 있다. 그래서 장고는 자동 이스케이프 기능을 제공하고 있다. 즉 장고는 디폴트로 HTML에 사용되는 예약 문자들을 아래처럼 예약 의미를 제거한 문자로 변경해주는 기능을 제공한다.
부등호 < 문자는 < 로 변경함
부등호 > 문자는 > 로 변경함
' 싱글따옴표는 ' 로 변경함 (굳이 왜 이따구로함?)
" 더블따옴표는 &#quot; 로 변경함
& 앰퍼센드는 & 로 변경함
근데 때로는 이런 자동 이스케이프 기능을 비활성화 해야할 때도 있음 그럴 땐 safe필터를 사용하거나 {% autoescape %}태그를 써서 자동으로 이스케이핑 되는 것을 방지할 수 있다고 한다!
뷰가 뭔지 기억 못하면 머리 한 대 쳐라. 뷰는 요청을 받아서 응답을 반환해주는 호출 가능한 객체고 장고에서는 뷰를 함수로도 쓸 수 있고 클래스로도 작성할 수 있다. 그리고 클래스형 뷰가 장점이 더 많다.
클래스형 뷰의 진입 메소드 as_view()라는 메소드가 있다. 이 메소드는 클래스의 인스턴스를 생성하고, 그 인스턴스의 dispatch() 메소드를 호출한다. 벌써 어려우니 하나씩 해보자. 일단 인스턴스는 그냥 객체라고 이해하면 된다고 gpt가 설명한다. 그리고 dispatch()메소드는 요청을 검사해서 GET, POST 등의 어떤 HTTP 메소드로 요청되었는지를 알아낸 다음에, 인스턴스(객체) 속에서 해당 이름을 갖는 메소드로 요청을 중계해준다. 만약 해당 메소드가 정의되어 있지 않다면 HttpResponseNotAllowed 익셉션(exception예외)을 발생시킨다.
그렇다면 클래스형 뷰가 함수형 뷰보다 뭐가 좋을까?
장점1. GET, POST 등의 HTTP 메소드에 따른 처리 기능을 코딩할 때, IF 함수를 쓰지 않고 메소드명으로 구분할 수 있기 때문에 코드의 구조가 깔끔해진다
장점2. 다중 상속과 같은 객체 지향 기술이 가능하므로, 클래스형 제네릭 뷰 및 믹스인 클래스 등을 사용할 수 있고, 이는 코드의 재사용성이나 개발 생산성을 획기적으로 높여준다.
상속이나 제네릭 뷰에 대한 설명은 생략하겠음 손가락 아픔.
장고의 로깅은 기본적으로 파이썬의 로깅 체계를 그대로 따르면서 일부만 추가되었다고한다. 파이썬의 로깅 모듈을 보면 로거, 핸들러, 필터, 포맷터 이렇게 4가지 주요 컴포넌트를 정의하고 있다. 이해를 돕기 위한 사진을 구글에서 찾아보겠다..
자 이 사진을 참고해서 하나씩 간단하게 정리해보자.
로거는 로깅 시스템의 시작점이다. 로그 메시지를 처리하기 위해 메시지를 담아두는 저장소라고 할 수 있다. 그리고 로거는 로그 레벨을 갖게 된다. 음.. 그리고 또 로거에 저장되는 메시지를 로그 레코드라고 한다. 그리고 그 로그 레코드도 녀석마다의 레벨을 갖는다. 결과적으로, 메시지가 로그에 도착하면 로그 레코드의 레벨과 로거의 레벨을 비교해서 진행되는 원리이다. 로그 레코드의 레벨이 로거 레벨과 같거나 높으면 메시지 처리를 진행하고, 더 낮으면 그 메시지를 무시한다. 이렇게 레벨을 기준으로 비교하여 메시지를 처리하고 결정되면 로거는 메시지를 핸들러에게 넘겨준다.
핸들러는 로거에 있는 메시지에 무슨 작업을 할지 결정하는 엔진이다. 즉, 메시지를 화면이나 파일 또는 네트워크 소켓 등 어디에 기록할 것인지와 같은 로그 동작을 정의한다. 얘도 로거랑 마찬가지로 로그 레벨을 가지고 있다. 로그 레코드의 로그 레벨이 핸들러의 로그 레벨보다 더 낮으면 핸들러는 메시지를 마찬가지로 무시한다. 로거는 핸들러를 여러개 가질수 있고, 각 핸들러는 서로 다른 로그 레벨을 가질 수 있다.
로그 레코드가 뭐였을까!!? 로거에 저장되는 메시지를 로그 레코드라고 한다고 했다. 이 로그 레코드가 로서에서 핸들러로 넘겨질 때, 필터를 사용해서 로그 레코드에 추가적인 제어를 할 수 있다. 예를 들면, ERROR 메시지 중에서 특정 소스로부터 오는 메시지만 핸들러로 넘겨버릴 수 있다. 필터는 로거 또는핸들러 어디에나 적용이 가능하고, 여러 개의 필터를 체인 방식으로 동작시킬 수도 있다.
로그 레코드는 최종적으로 텍스트로 표현되는데, 포맷터는 텍스트로 표현 시 사용할 포맷을 지정해주는 역할을 한다.
여기까지만 정리할까 했지만 마지막으로 장고의 디폴트 로깅 설정과 디폴트 설정을 무시할 수 있는 부분까지만 정리하겠음!!!
위에 열심히 설명한 것들이 로깅 메소드를 호출했을 때 로그 메시지를 원하는 대로 기록하기 위해서 사용되는 녀석들이었다. 파이썬의 로깅 라이브러리는 다양한 설정 방식을 제공하고 있는데 장고는 그중에서 사전형 설정(dictConfig)방식을 사용한다. 이 방식은 settings.py파일의 LOGGING항목에 로깅 속성을 사전 형식으로 정의하게 된다.
setting.py파일에 LOGGING항목을 따로 지정하지 않으면 장고는 디폴트 로깅 설정을 한다. 근데 대부분 디폴트 설정을 쓰기 보다는 기존의 디폴트로 설정된 로거들을 오버라이딩하여 핸들러, 필터, 포맷터 등의 동작을 변경하는 방법을 많이 사용한다. 디폴트는 장고 패키지의 구성에 맞춰 사용되고 있는 것이지 우리가 개발하고 있는 애플리케이션에 사용할 로거는 따로 필요하기 때문에 내 입맛에 맞게 만져줘야 한다는 것!
디폴트 설정을 무시하고 나만의 로깅 방식을 설정해버릴 수도 있다. 또는 장고의 디폴트 로깅 설정이 장황하다고 생각해서 간단하게 내맴대로 설정해버릴 수도 있다. 그럴 때는
LOGGING_CONFIG = None
LOGGING = {
(원하는 내용으로 로깅 컴포넌트들을 설정)
}
import logging.config
logging.config.dictConfig(LOGGING)