전반적인 장고 핵심 설계 철학
- 마감시간이 있는 완벽 주의자를 위한 웹 프레임워크
- 바퀴를 다시 발명하지 마라 (초기 학습비용 존재, 계속 학습하지 않아도 됨, 백엔드 개발에 필요한 거의 모든 기능을 장고 중심으로 제공)
- 고유한 개념 및 데이터는 한 곳에, 하나만 존재해야 한다.
- 다양한 정규화를 통해 중복을 줄인다.
장고의 설계 철학: 일반
1. 신속한 개발: 21세기 웹 프레임워크의 핵심은 지루한 부분을 빠르게 만드는 것
- 장고는 쾌속 웹 개발을 가능케 한다.
- 대개의 경우 프로젝트 개발에서 시간이 가장 큰 비용, 중복을 줄여 높은 생산성을 통해 서비스 개발에 집중할 수 있도록 도와준다.
- 하지만, 규모 큰 회사는 시간보다 더 중요하게 고려해야할 사항들이 있을 수 있다.
2. 느슨한 결합: 장고 스택의 근본적인 목표는 “느슨한 결합, 탄탄한 응집”
- 프레임워크의 각 계층은 필요하기 전에는 서로 결합이 없어야 한다.
- 장고 best practice 제안 구현도 있지만, 이는 제안일 뿐 강제되지 않는다. 임의의 아키텍처로 개발 가능
- Flask/FastAPI: 기능의 수가 적지만 배우기 쉽고 빠름, 하지만 규모가 커지면 기능이 부족하고, 관리주체가 달라 기능추가/개선 유지보수가 유기적으로 이루어지지 않는다.
3. 적은 코드: 반복을 줄이고, 가능한 최소한의 코드를 사용, 다른 언어/프레임워크의 틀에 박힌 코드를 배제
- Introspection(객체의 메타데이터를 조사하는 과정)과 같은 python의 동적인 기능을 최대한 활용
4. 반복하지 않기 1: 중복은 줄이고, 정규화를 지향
- 고유한 개념 및 데이터는 단 한 번, 단 한 곳에 존재하는 것으로 충분하다. 그러한 이유로, 장고는 절제된 구현으로 완벽히 동작하는 기능을 만들 수 있다.
예) 클래스 기반 뷰, ModelForm, ModelSerialiezer 등
- 유저로부터 새로운 포스팅을 입력받으려 하거나 기존 포스팅에 대한 수정 요청을 받으려 할 때 유저에게 html 입력폼을 보여줘야한다. 유저가 제목과 내용을 입력하고, 작성 완료를 클릭하면 서버로 그 요청이 전달 되고, 서버에서 유저가 입력한 값에 대한 유효성 검사를 해야 한다. 서버에서 원하는 길이나 포맷에 맞지 않는 값을 받으면 거부할 수 있어야 한다(오류 메시지).
- modelform은 중복 코드를 줄여주는 하나의 선택지(meta클래스에 지정된 옵션), 상황에 따라 form과 modelform을 사용한다.
5. 반복하지 않기 2: 파이썬의 특성을 활용하여, 반복을 줄일 수 있는 다양한 기능을 제공한다.
- 강제되는 기능이 아니라 선택이며, 다양한 커스터마이징이 가능하다.
- 클래스 기반 뷰를 사용하여 반복을 줄일 수 있는데, 무조건 클래스 기반 뷰로 구현 해야하는건 아니다. 함수 기반 뷰도 많이 사용한다.
6. 반복하지 않기 3: 초기 뷰 구현이나 스페셜한 구현은 “함수 기반 뷰”로 구현하고, 반복이 보이면 이를 CBV로 반복을 제거
- .get: 사전에서 가져온다.
- icontains의 i는 ignore case, 대소문자 구별하지 않는다는 의미
- Mixin(믹스인): 반복이 되는 부분을 별도의 클래스로 뽑아서 사용, 다중 상속을 통해 반복을 줄이고 재사용성을 높일 수 있다. 앞 뒤가 바뀌면 안됨, 앞에 있어야 한다.
7. 명시적인 것이 묵시적인 것보다 낫다.
- 장고가 마법을 많이 부려서는 안 된다. – 코드에 의도가 드러나지 않는 묵시적인 상황
- 정당한 이유 없이 마법이 일어나서는 안된다. 마법은 매우 큰 편익이 있을 때만 허용, 기능의 사용법을 배우고자 하는 개발자에게 혼란을 일으키는 방식으로 구현되어서는 안됨.
- 장고는 명시적으로 중복을 줄여주고 개발 시간을 단축해주는 많은 기능이 있다.
장고 설계철학: 뷰
단순성: 뷰를 작성하는 것은 함수를 작성하는 것만큼 단순하고 직관적이어야 한다.
- 뷰는 요청 처리의 지휘자이다 → 뷰에서 모든 처리를 하지 않는다.
- 뷰는 비지니스 처리를 위임할뿐, 직접 비즈니스 로직을 구현하지는 않는다.
- Model/Form/Serializer를 적절히 활용한다.
- 개발자는 함수로 처리할 수 있는 일을 하기 위해 클래스의 인스턴스를 굳이 생성하지 않아도 된다. // 물론 함수로 복잡한 처리를 할 수 있고, 클래스를 활용한 처리도 가능하다.
요청의 모든 메타 정보를 담고 있는 요청 객체: 요청 객체(HttpRequest 타입)은 장고에서 자동으로 생성하여, 뷰에 명시적으로 전달한다.
- 뷰 함수의 첫번째 인자는 HttpRequest 객체이다. // 요청 헤더, 요청 인자(GET/POST/FILES), 세션, 쿠키, 로그인 유저 등
- 뷰를 테스트할 때에는 가짜 요청 객체를 전달하는 방식으로 가볍고 깔끔한 테스트를 만들 수 있다.
템플릿 시스템과의 느슨한 결합: 명시적으로 템플릿 시스템을 호출하여, 필요한 문자열을 렌더링한다.
http에서의 GET/POST method: 일반적으로 조회 목적으로 GET 방식을, 생성/수정/삭제 목적으로 POST 방식을 사용한다.
웹 스펙 상, 요청 패킷에 헤더만 존재하고, body가 없다.
GET 요청에 대한 부가적인 데이터는 QueryString과 헤더로만 전달 가능
파일 업로드 불가: QueryString 포맷에는 파일을 담을 수 없다.
요청에 대한 부가적인 데이터는 QueryString과 헤더 및 body로 전달 가능
요청 패킷에 body가 있기에 파일 업로드 가능
조회에 사용할 수는 있지만, POST 요청은 캐싱할 수 없기에 매번 DB조회가 발생한다. 이는 조회가 아주 많은 페이지 (ex:이벤트 페이지)에서 비효율적이다.
요청객체에서의 요청 인자
GET요청과 POST요청처리 로직을 패턴화: 패턴화하여 클래스기반 뷰를 제공하고 있다.
- Form 처리에서의 패턴
GET 요청을 받으면 : 생성/수정 HTML 폼을 노출
POST 요청을 받으면: 생성/수정 HTML 폼에 대한 저장 요청을 처리하고 유효성 검사하여, 오류가 발생하면 오류내역을 표현하고 재입력을 요구, 오류가 없으면 저장하고 성공 페이지로 이동
장고 설계철학: URL 설계
뷰와 URL의 느슨한 결합: 뷰 구현에 직접 URL을 매핑하지 않고, URL 매핑을 뷰 구현과 따로 처리하여 URL 정의의 유연함
- 같은 뷰를 쓰더라도 상황에 따라 다른 URL 매핑을 부여할 수 있다.
장고앱 시스템은 앱의 재사용성에 목적: 같은 장고앱을 다양한 프로젝트에서 요구되어지는 URL Prefix로 손쉽게 지정할 수 있다.
- 장고앱 폴더만 다른 프로젝트로 복사하면, 복잡한 설정없이 사용할 수 있다.
- 뷰에 대한 URL 문자열이 매번 바뀌어도 애플리케이션 구동에는 괜찮다. // 강력한 URL Reverse 기능 때문이다.
엄격하게 URL 패턴을 지정할 수 있다: URL 문자열 패턴에 정규표현식을 지원하기에 보다 엄격한 URL 패턴이 가능
- EX) /123/과 /about/ 주소를 서로 다른 주소에 매칭할 수 있다.
모범 사례를 장려: 보다 가독성 높게 URL 패턴을 정의할 수 있도록 도와준다
- path converter를 통해 간결하게 URL 패턴을 지정할 수 있다.
- 웹페이지 URL에서 <파일 확장자>는 가급적 피한다. // EX) /blog/posts/100/html
html 응답에서는 굳이 지정할 이유가 없다.
명확한 URL: 검색엔진이 혼동하지 않도록 URL을 정규화하는 옵션이 기본 활성화
- foo.com/bar와 foo.com/bar/는 서로 다른 URL
검색엔진 봇과 웹트래픽 분석 툴에서는 별개의 페이지로 처리한다.
- settings.APPEND_SLASH 설정이 디폴트로 True
장고 설계철학: 템플릿
템플릿 언어는 간결해야한다: 프로그래머가 아닌 디자이너가 작성하는 것을 전제로 한다
- 디자이너가 파이썬 지식을 갖고 있을 것으로 간주해서는 안된다.
- 분기와 반복같이 표현 계층에 꼭 필요한 프로그래밍 기능을 제공하는 것이 목표이다.
HTML 조합에서 자주 사용
- 웹 프레임워크이니 대부분 브라우저의 요청이며, 브라우저 요청에서는 HTML 문자열 응답이 대부분
- HTML 포맷의 문자열은 복잡한 문자열이지만 템플릿 엔진을 활용하면 그 문자열 조합 로직이 간결해 진다.
- render API를 통해 손쉽게 템플릿 엔진을 활용하여, HttpResponse 응답을 만들어 낸다.
다양한 문자열을 조합할 수 있도록 설계: 복잡한 문자열을 단순히 언어의 기본 기능 만으로 조합하는 것은 어렵다
- 활용 예시 : SMS 문자 메세지, 이메일 제목/내용, 푸쉬 메세지 내용 등
템플릿 파일은 HTML 파일이 아니다: HTML 포맷 뿐만 아니라, 어떠한 문자열 조합에도 활용 될 수 있다.
- 장고 템플릿 파일은 HTML 응답에 사용되는 최종 HTML 결과물이 아니라, HTML 응답 생성에 사용되는 소스파일
서버 측에서 HTML 템플릿 코드를 로딩하여, HTML 문자열을 생성 SSR의 템플릿으로 활용한다.
장고 템플릿 HTML 파일을 브라우저에 그대로 로딩하면, 장고 템플릿 태그/필터 문법의 코드가 그대로 노출돼버린다.
- 드림위버와 같은 위지웍 편집기는 장고를 거치지 않고, 그 파일 내용을 그대로 미리보기 하기에 장고 템플릿 파일 편집에 전절하지 않다.
템플릿 파일 작성자가 HTML을 직접적으로 편집하고, 웹브라우저-장고뷰를 통해 렌더링된 HTML 문자열을 받아서 확인해야 한다.
스파게티 개발을 원천적으로 봉쇄한다: Stupid 템플릿 시스템
- 템플릿 시트템이 표현을 제어하는 도구이자 표현에 관련된 로직일 뿐, 기본 목표를 넘어서는 기능을 지원하면 안됨
표현 외의 로직을 템플릿에서 쓸 수 없도록 제약된 문법 제공
안전과 보안
- 템플릿 시스템은 데이터베이스의 레코드를 삭제하는 명령과 같은 악의적 코드를 포함할 수 없게 되어야한다.
템플릿 시스템에 의해, 벡엔드 단에서 임의의 Python 코드를 실행할 수 없는 또 다른 이유이다.
중복을 배제
- 대다수의 동적 웹사이트는 공통 헤더, 푸터, 네비게이션 바와 같은 공통 디자인을 가진다. -> 각 템플릿 파일마다 필연적으로 중복이 발생
- 템플릿 상속을 통해 중복을 제거할 수 있다. 상속은 여러 계층으로 이뤄질 수 있다.
확장성
- 템플릿 태그/필터 문법은 단순하기에 HTML을 읽고 쓸 수 있다면 손쉽게 활용할 수 있다.
또한, 커스텀 템플릿 태그/필터를 통해 템플릿 시스템을 확장할 수 있다.
- 써드파티 템플릿/필터 라이브러리를 통해, 템플릿 시스템은 더욱 확장된다.
장고 설계철학: 모델
명시적인 것이 묵시적인 것보다 낫다: 어떤 필드가 그 이름에 전적으로 의존하여 특이한 성질을 가질 것으로 가정하면 안됨
- 가정은 시스템에 대한 지식을 너무 많이 필요로 하며 오류를 양산한다. 필드의 성직은 키워드 인자에 근거해야며, 경우에 따라서는 필드의 유형(타입)에 근거해야 한다.
- 명시적으로 기능을 확장한다.
모든 관련 도메인 로직을 포함하기: 모델은 마틴 파울러의 Active Record 디자인 패턴을 따른다
- 데이터를 모델로 표현하고, 그에 대한 정보(사람이 읽을 수 있는 이름, 기본 순서 같은 선택 사항등)를 모델 클래스에 정의한다. 모델을 이해하는데 요구되는 모든 정보가 모델 내에 있어야 한다.
장고 설계철학: 데이터베이스 API
간결하고 강력한 구문
- 가능한 적은 구문으로 풍부하게 표현력이 뛰어난 쿼리를 생성할 수 있어야 한다.
- 모델 객체는 관련된 모든 객체에 양방향으로 접근할 수 있어야 한다.
SQL 효율성
- 성능높게 관련 객체를 함께 쿼리하는 방법을 제공
필요한 경우 SQL 실행도 쉬워야 한다
- Raw SQL 쿼리로 QuerySet을 생성 할수 있다.
- 모델의 개입없이 SQL 쿼리를 실행할 수 있어야 한다.
- 장고는 다중 데이터베이스를 지원하며, 원하는 데이터베이스를 지정해서 쿼리할 수 있다. (Router 구현 필요)
- settings.DATABASES에 지정한 alias로 원하는 데이터베이스를 지정해서 쿼리할 수 있다.
파이썬 문법
함수: 코드 블럭을 함수 단위로 묶어서 재사용할 수 있다.
- 함수의 주요 구성요소: 이름(필수), 인자, 반환 // 예외) lamda 함수(익명함수)
- 파이썬은 이름으로만 개체를 식별한다. 클래스도 동일
- 타입을 지정하는 습관을 들이면, 빠르게 인자 타입 확인이 가능하다. //타입힌팅
- docstring에 함수 스펙을 작성하면, 빠르게 스펙 확인이 가능하고, 이 후 자동으로 문서로 생성할 수 있다.
위치/키워드 인자, 디폴트 인자: 함수의 인자를 정의하고, 호출 시에 인자를 넘기는 다양한 방법
- 위치 인자 (positional arguments): 함수 호출 시에 위치(index)로 각 인자를 식별//인자의 개수 적으면
- 키워드 인자(keyword arguments): 함수 호출 시에 키워드(keyword)로 각 인자를 식별//인자의 개수 많으면
- 디폴트 인자: 함수 인자 정의 시에 디폴트 값이 지정된 인자는 해당 인자가 지정되지 않으면 디폴트 값으로 할
클로저 (Closure): 함수가 선언될 당시의 환경을 기억(포섭)했다가, 차후 호출되 떄 기억한 환경을 이용하는 함수
- 지원하는 언어: 파이썬, 자바스크립트, 자바 등
- 장고에서는 주로 뷰와 장식자에서 사용
- 클래스 안의 함수는 클래스의 인스턴스 메드가 스태틱/ 클래스 메소드를 뜻한다. 하지만 함수의 안의 함수는 바깥 함수가 호출이 되어 수행이 될 떄마다 매번 안의 함수가 새롭게 정의가 된다. //local, global 개념
일급 (first-class) 함수/ 클래스: 함수/클래스를 비롯한 모든 대상이 객체
- 런타임에 새로운 변수를 생성할 수 있듯이, 런타임에 새로운 함수/클래스를 생성할 수 있다.
- 함수/클래스를 변수에 할당하고, 인수로 사용하고, 함수 반환값으로 사용할 수 있다.
타입 힌팅(Type Hinting): 힌트이기에 타입에 맞지 않게 값을 지정하더라도 실행 오류가 발생하지는 않는다.
- 엉뚱한 타입의 값을 지정하는 경우를 줄이고, 정확한 속성 자동완성을 제공받을 수 있다.
- 개발툴이 타입을 추론할 수는 있지만, 추론이 오래 걸리거나 엉뚱한 타입을 추론할 가능성 있다. 직접 지정한다면 코드의 가독성을 높이고, 보다 정확하고 빠릿 하게 동작한다.
관점 지향 프로그래밍: AOP(Aspect Oriented Programming)
- 횡단 관심사의 분리 -> 모듈성 증대
- 기능의 핵심 구현부를 어수선하게 수정하지 않고도, 비지니스 로직에 핵심적이지 않은 동작들을 프로그램에 추가 // @ 장식자 문법
Helper(특정 로직을 재사용 가능하도록 독립적인 함수로 제공하는 방식)으로도 구현할 수 있지만 핵심 구현부를 건드리게 됨
AOP의 일반적인 활용
1. 대상 함수 호출전에 고통 기능을 실행
2. 예외 발생 여부에 상관없이 대상 함수 수행 후에 공통 기능을 수행
3. 대상 함수 수행 중에 예외가 발생하지 않았을 경우 고통 기능을 실행
4. 대상 함수 수행 중에 예외가 발생한 경우 고통 기능을 실행
장식자(Decorators)
- 특정 함수/클래스를 래핑하여, 그 함수에 대한 인자변환/반환값변환/호출유무 등을 제어할 수 있다.
- 장식자는 래핑한 함수를 같은 이름으로 지정하는 문법
개발 환경 구축
장고 프로젝트 생성
- mkdir -p : 폴더 경로 중에 없는 폴더도 생성
- pwd : 현재 작업 디렉토리 경로
- ~ : 현재 유저의 홈 디렉토리
- m venv : venv 모듈 실행(가상환경)
- start . : 윈도우 탐색기 (맥은 open .)
- python -m pip install Django==5.1.7 : 장고 설치
- python -m django startproject mysite . : 새로운 장고 프로젝트 현재경로에
- python manage.py
migrate : 현 프로젝트에 활성화된 장고앱 내에, 데이터베이스 테이블 생성
createsuperuser: 슈퍼유저 계정 생성
unsever: 개발서버 구동
- python manage.py startapp (앱이름)
- python manage.py startapp catube : catube 장고앱 생성
장고와 스프링의 비교
스프링의 일반적인 포스팅 목록 조회 페이지 (DB를 통해 HTML응답을 만듬)
//컨트롤러: 외부 요청을 받아서, 서비스를 통해 로직을 수행하고, 응답
//서비스: 실제 비즈니스 로직을 처리
//엔티티
//저장소
//DTO(data transfer object) : 응답 포맷을 정의
//index 템플릿
장고의 일반적인 포스팅 목록 조회 페이지
//URL Patterns: 장고는 뷰 정의와 URL 매핑을 따로 한다.
//뷰(함수로 만든 뷰- 가장 기본이 되고 핵심): 앞선 스프링의 컨트롤러의 역할. 서비스의 일부 역할
//모델: 앞선 스프링의 엔티티와 유사
//템플릿: 복잡한 문자열 조합의 틀
장고에서의 요청 처리
일반적인 웹 요청 처리 프로세스
- Load Balancer(Nginx, API Gateway) : 부하를 분산시켜줌, 여러 서버로)
- URL에 따라 요청 분기 → 어떤 뷰에서 처리할 것인지 결정
- 처리할 뷰가 없다면 404 Page not found 같은 오류를 돌려 줄 수 있다.
- 뷰에서 다양하게 처리하고 응답한다.
- Worker 프로세스에서 일을 하고, 사용자에겐 이미 처리되었다고 응답해 기다리는 시간을 줄일 수 있음.
View 함수를 통한 클라이언트 요청 처리
//문자열 응답하기
//템플릿 엔진을 활용한 HTML 문자열 응답
//이미지 생성하여 응답하기
urlpatterns 리스트: View 함수에 대한 라우팅(Routing) 테이블
- 일반적으로 웹서버로 유입되는 클라이언트로부터의 요청을 식별하는 1차 기준은 URL
- settings.ROOT_URLCONF에 지정된 urlpatterns으로부터 하위 urlpatterns들을 가질 수 있다.
스프링의 주된 역할은 IoC 컨테이너: 수많은 웹프레임워크(스프링, Nest.js, ASP.NET 등)들은 객체지향 방식으로 접근
- 모든 구현에서 클래스가 필수: 자바는 클래스 단위로만 개발이 가능
- 각 인스턴스의 생성/지정을 우리가 직접 수행할 수 없고, IoC 컨테이너(스프링)에게 위임: IoC 컨테이너가 인스턴스의 LifeCycle을 관리하고, 필요한 인스턴스를 주입
- IoC(Inversion of Control): 제어의 역전
- DI(Dependency Injection): 의존성 주입
장고는 모든 http 요청 주체는 Callable Object
- 클라이언트로부터의 요청을 함수로 처리하기에 구현이 단순해지고, 확장이 용이하다.(ex: 클래스 기반 뷰)
- 매 요청을 처리 시마다 관련 함수가 격리되어 호출되며, 원천적으로 뷰에서 상태를 저장할 수 없고, 가질 수 없다.