- 보다 나은 웹 애플리케이션과 행복한 개발자를 만들기 위한 웹 개발 방법론을 전달하는 것
- '안정적으로 동작하는 깔끔한 코드 작성'을 위한 TDD 사용법에 대해 다룸
- Django, 셀레늄, jQuery, 모크 등의 툴을 사용해서 실제 웹 애플리케이션을 개발해가면서 실용적인 예제 코드들을 다룸
- 1부(1장~6장): 기초편
- TDD를 이용해서 간단한 웹 애플리케이션 구축
- 셀레눔 기반으로 기능 테스트 작성
- 철저한 단위 테스트를 기반으로 하는 Django(모델, 뷰, 템플릿) 기초를 다룸
- 2부(7장~14장): 웹 개발 핵심편
- 웹 개발의 필수 기술에 대해 다룸
- 테스트가 어떻게 웹 개발에 도움이 되는지 학습
- 정적 파일, 배포, 폼 데이터 검증, 데이터베이스 마이그레이션, 자바스크립트 테스트를 중점적으로 다룸
- 3부(15장~20장): 고급편
- 모킹(Mocking), 외부 인증 시스템 통합, AJAX, 테스트 픽스처(fixtures), TDD 상호작용, 지속적 통합(CI) 등에 대해 다룸
- python 3.4
- django 1.7
- selenium
- unittest (온라인 문서)
TDD is an awareness of the gap between decision and feedback during programming, and techniques to control that gap. --Test Driven Development by Example, p xxi
TDD란 프로그래밍 의사결정과 피드백 사이의 간극에 대한 인식이자, 이 간극을 제어하기 위한 기술이다.
- 프로그래밍을 하는 과정에서 일어나는 의사결정들(변수 이름, 함수 길이 등)
- 그러한 의사결정을 내린 후에 돌아오는 피드백(컴파일 에러, 버그, 깔끔한 코드, 금전적 손실 등)
- 그리고 이 둘 사이의 간극
즉, 이 세가지를 잘 의식하면서 적극적으로 제어하려는 노력, 이것이 테스트 주도 개발이라는 것이다.
- 애플리케이션이 어떻게 **동작(functions)**하는지 사용자 관점에서 확인하는 것
- 승인 테스트, 종단간 테스트라고 부르기도 함
- 블랙박스 테스트라고 부르기도 함
- 시스템 내부에 대해선 전혀 알지 못하기 때문
- 중요한 것은 이 테스트들이 전체 애플리케이션이 어떻게 동작하는지를 외부 사용자 관점에서 확인하는 테스트라는 것
- 제대로 된 기능성을 갖춘 애플리케이션을 구축하도록 돕고, 그 기능성이 망가지지 않도록 보장
- 프로그래머 관점에서 그 내부를 테스트하는 것
- 자동화된 테스트 실행자에 의해 실행됨
- 단위 테스트는 기능 테스트에 의해 파생되며 더 실제 코드에 가깝다.
- 단위 테스트의 전형적인 구조
- 설정(Setup): 테스트 설정을 위한 코드 그룹
- 처리(Exercise): 실제 함수를 호출하는 코드 부분
- 어설션(Assert)
- 각각의 단위 테스트가 한 가지만 테스트 하는 것이 좋은 단위 테스트이다.
- 테스트에 많은 어설션이 있는 경우, 앞에 있는 어설션이 실패하면 뒤에 있는 어설션 상태를 파악할 수 없음
- 사용자 스토리 (User Story)
- 사용자 관점에서 어떻게 애플리케이션이 동작해야 하는지 기술한 것
- 기능 테스트 구조화를 위해 사용
- 예측된 실패(Expected failure)
- 의도적으로 구현한 테스트 실패를 의미
- TDD 주기
- 실패 테스트를 작성한 후 테스트를 통과할 수 있는 코드를 작성하는 과정
- 터미널에서 단위 테스트를 실행해서 어떻게 실패하는지 확인
- 편집기상에서 현재 실패 테스트를 수정하기 위한 최소한의 코드 변경
- 코드 품질을 높이고 싶다면 코드 변경을 최소화 -> 변경된 코드를 하나하나 테스트에 의해 검증
- 레드(Red), 그린(Green), 리팩터(Refactor)
- 레드: 실패할 단위 테스트를 작성함으로써 작업 시작
- 그린: 이 테스트를 통과할 최소 코드 작성, 편법이라도 상관없음
- 리팩터: 코드를 리팩터링해서 이해할 수 있는 코드로 만든다.
- 중복 제거: 테스트 코드와 애플리케이션 코드 간 중복 제거
- 삼각법: 테스트가 상수 같은 편법 코드를 허용하지 않음 -> 다른 테스트를 작성해서 더 나은 코드를 작성하도록 함
- 기능(결과물)은 바꾸지 않고 코드 자체를 개선하는 작업을 일컫는다.
- 리팩터링은 테스트와 함께!
- 앱 코드와 테스트 코드를 한 번에 동시 수정하는 것이 아니라 하나씩 수정해야 함
- 리팩터링 전에는 반드시 커밋하는 습관 갖기
- 템플릿은 Django의 매우 강력한 기능 중 하나
- 파이썬 변수를 HTML 텍스트로 변경해주는 기능이 있음
- render 함수
- 첫 번째 인수: 요청(request)
- 두 번째 인수: 렌더링할 템플릿 명
- Django는 앱 폴더 내에 있는 templates 폴더를 자동으로 검색
- 템플릿 콘텐츠 기반으로 HttpResponse를 만들어줌
- 데이터베이스의 테이블, 레코드, 칼럼 형태로 저장되어 있는 데이터를 추상화
- ORM을 이용하면 익숙한 객체 지향 코딩 방식을 이용해서 데이터베이스 조작, 처리 가능
- 데이터베이스는 클래스로 표현하고, 칼럼은 속성, 레코드는 각 클래스의 인스턴스로 표현
- 클래스 속성인 .objects을 통해 데이터베이스 쿼리 가능
- model.py 파일에 적용된 내용을 기반으로, 사용자가 테이블과 칼럼을 삭제 및 추가할 수 있도록 함
- 데이터베이스를 위한 버전 관리 시스템
- 상용 서버에 배포한 데이터베이스를 업그레이드할 때 특히 유용
- 퇴행(Regression)
- 동작하고 있던 애플리케이션 처리가 새롭게 추가된 코드에 의해 망가지는 것을 의미
- 예상하지 못한 실패(Unexpected failure)
- 생각하지 못한 방법으로 테스트가 실패하는 것을 의미
- 이는 테스트를 잘못 작성했거나, 테스트 자체가 코드 퇴행을 발견했다는 것, 애플리케이션 코드 수정이 필요하다는 의미
- 삼각법(Triangulation)
- 기존 코드에 구체적인 테스트 케이스를 추가해서 일반화(편법이 될 수도 있는)한 처리를 정당화하는 것
- 스트라이크 세 개면 리팩터(Three strikes and refactor)
- 중복 코드를 제거해야할 시기를 말해주는 일반적인 규칙
- 세 번째 동일 코드가 사용된다면 -> 리팩터링 확신
- 자동으로 테스트용 데이터베이스를 생성하고(단위 테스트와 마찬가지로), 기능 테스트를 위한 개발 서버 가동
- LiveServerTestCase는 Django의 manage.py를 이용해 실행
- Django 1.6 부터는 테스트 실행자가 'test'로 시작하는 파일을 자동 탐색
- 개발 초기 단계에 요구사항 분석과 설계에 많은 시간을 할애하는 전통적 소프트웨어 공학과 상반된 방법론
- 애자일은 이론보다는 실제 상황을 통해 문제를 해결하려는 것을 근간으로 하고 있음
- 가능한 빨리 사용자가 애플리케이션을 접할 수 있도록 하는 것이 특징
- 긴 설계 과정 대신에, "동작하는 최소한의 애플리케이션"을 빠르게 만들고, 이를 이용해서 얻은 실제 사용자 의견을 설계에 점진적으로 반영해가는 방식
- YAGNI: "You ain't gonna need it"
- (REST 정리 글)
- 웹 설계 방법 중 하나
- 일반적으로 웹 기반 API를 이용해서 설계하도록 유도
- 데이터 구조를 URL 구조에 일치시키는 방식
- 각각의 테스트가 다른 테스트에 영향을 끼쳐서는 안된다는 것을 의미
- 각 테스트 마지막에는 영구적인 상태를 초기화해야 하다는 것
- Django의 테스트 실행자는 각 테스트 결과물을 제거해주는 테스트 데이터베이스를 생성함으로써 이 문제를 해결
- 다수의 템플릿 각각에 전체 상용구 코드(Boilerplate)를 추가하는 것은 바람직하지 않음
- 템플릿 간 공통 부분을 추출해서 base.html을 생성하고, 'blocks'이라는 연속 영역 정의 -> 이는 자식 템플릿의 콘텐츠를 추가하거나 연동할 수 있는 영역
- 다른 템플릿에서 base.html 상속
- 자식 템플릿은 커스터마이징을 위해 'block'설정
- Django의 개발 서버를 이용하고 있는 동안은(manage.py runserver), Django가 제공하는 솔루션을 이용해서 정적 파일을 찾을 수 있다.
- 즉, Django가 앱의 서브 폴더를 모두 검색해서 static이라는 폴더를 찾는다.
- Django/runserver는 요청 url이 settings.py의 'STATIC_URL' 접두사로 시작되면, 이 요청이 정적 파일을 위한 것이라 생각하고, 해당 파일을 검색하여 서빙
- 기능 테스트 실행시
- runserver와 달리 LiveServerTestCase는 자동으로 정적 파일을 찾을 수 없음
- StaticLiveServerCase를 이용해 테스트 함
- 실제 운영 중인 서버에서 Django가 정적 콘텐츠를 제공하도록 하는 것은 매우 느리며 비효율적이다.
- 아파치(Apache)나 Nginx 같은 웹 서버도 같은 역할을 할 수 있다.
- 직접 정적 파일을 호스팅하는 대신에 CDN(Content Delivery Network)에 업로드해서 호스팅하는 방법도 있다.
- 따라서, 여러 앱에 존재하는 모든 정적 파일을 한 곳에 모아서 배포용으로 만들어 둘 필요가 있고, 이 작업을 collectstatic 명령으로 수행함
- collectstatic명령으로 수집된 정적 파일들은 settings.py의 STATIC_ROOT에서 설정된 위치에 모임
- 디자인과 레이아웃용 테스트는 작성할 필요 없음
- 최소한의 '스모크 테스트'를 이용해서 CSS와 정적 파일이 동작하는지만 확인하는 것이 좋다.
- 작은 스타일링 코드를 적용하기 위해 다량의 클라이언트 측 자바스크립트가 요구되는 경우, 이를 위한 테스트도 반드시 필요
- 디자인과 레이아웃이 동작한다는 것을 확신할 수 있게 하는 최소한의 테스트만 작성
- 디자인과 레이아웃을 자유롭게 변경할 수 있도록 하고, 변경 시마다 테스트 하거나 이전 상태로 돌리는 등의 작업은 배제
- 이 파일들을 제공하기 위해 웹 서버에 특수한 설정 필요
- 권한이나 경로 문제가 있을 수 있으며, 배포 시 테스트 데이터 관리에 유의해야 함
- 개발한 소프트웨어와 연계돼 있는 패키지를 서버에 설치해야 하며, 패키지 버전도 확인해야
- 신제 운영 사이트에서 사용하는 환경과 동일한 환경의 스테이징(staging) 사이트이용
- 이를 통해 '실제' 사이트에 배포하기 전에 모든 것이 제대로 동작하는지 테스트할 수 있음
- 스테이징 사이트에 대해 기능 테스트 실행
- 이를 통해 서버상에 있는 코드와 패키지가 제대로 된 것임을 다시 확인할 수 있음
- 레이아웃용 '스모크 테스트'를 통해 CSS가 정상적으로 로딩되는지 알 수 있음
- Virtualenv를 이용해 하나 이상의 파이썬 애플리케이션이 동작하고 있는 장비에서 패키지 및 패키지 의존 관계를 관리할 수 있음
- 자동화
- 자동화된 스크립트를 이용해서 신규 버전을 배포하고, 이를 스테이징 서버와 운영 서버에 동시에 배포
- 운영 서버와 스테이징 서버를 가능한 동일 상태로 유지
- AWS(Amazon Web Services)
- 아마존 웹 서비스는 아마존닷컴이 제공하는 각종 원격 컴퓨팅 서비스(웹 서비스)이다.
- 아마존 웹 서비스는 다른 웹 사이트나 클라이언트 측 응용 프로그램에 대해 온라인 서비스를 제공하고 있다.
- 아마존 웹 서비스의 상당수는 최종 사용자에 직접 공개되는 것이 아니고, 다른 개발자가 사용 가능한 기능을 제공하는 플랫폼을 제공하는 Paas다.
- 아마존 웹 서비스의 각종 서비스는 REST 프로토콜 및 SOAP 프로토콜을 통해 접근, 이용 및 관리가 가능하다.
- 비용은 실제 사용량에 따라 결정되며, 일부 서비스의 경우 미리 고정된 금액을 지불하는 형태도 있음
- 아키텍처(서버 구성)
- Nginx와 uWSGI는 유직스 유닉스 도메인 소켓으로 통신
- Supervisor을 이용해 Nginx와 uWSGI 프로세스가 다운되면, 자동으로 재시작해줄 수 있도록 함
클라이언트 - Nginx - uWSGI - Django
- Fabric
- 페브릭은 파이썬 스크립트를 이용ㅎ해서 서버상에서 명령어를 실행하게 해준다.
- 서버 관리 자동화를 위한 훌륭한 툴이다.
- fabfile에 있는 함수들은 상단부터 차례대로 커맨드라인에서 호출되기 때문에, 밑줄(_)을 이용해서 공용 API와 섞이지 않도록 한다.
- mkdir -p 를 이용
- 여러 단계로 이루어진 디렉터리의 경우 상위 디렉터리까지 작성(예: mkdir -p /tmp/foo/bar는 bar뿐만 아니라 부모 디렉터리인 foo도 생성)
- mkdir 명령어로 만드려는 디렉터리 이름이 이미 존재하더라도 오류를 발생시키지 않음(예: 이미 bar 폴더가 있어도 에러가 발생하지 않는다.)
- 멱등성(Idempotency)
- 멱등성이란 몇 번을 실행해도 결과가 같은 것을 의미한다.
- 자동화 스크립트는 신규 사이트뿐만이 아니라 기존 사이트에서도 동작하는 것을 의미
- 기존 서버에 배포 스크립트를 실행한다면, 스크립트 설계 시 두 가지 측면을 고려해야 한다.
- 서버에 신규로 설치하는 경우와 이미 설치 버전이 있는 경우
- 설정 파일 버전 관리
- 설정 파일이 서버에만 존재하는 일은 없어야 한다.
- 애플리케이션 핵심 파일이기 때문에 다른 코드와 같이 버전 관리를 해야 한다.
- 프로비저닝 자동ㅎ화
- 신규 서버 가동과 필요 소프트웨어 설치까지 모두 자동화되어야 한다.
- 또한 호스팅 제공자의 API연계성도 확인해야 한다.
- 관리 툴 설정
- 페브릭은 매우 유연한 툴이긴 하지만 그 로직이 스크립트에 기반하고 있다.
- 선언적인 작성이 가능해서 작업을 더 쉽게 해주는 툴들도 있다.
- Ansible, Vagrant, Chef, Puppet 등등
- 기존에 하나의 파일에 모든 기능 테스트 함수를 정의했지만, 각 테스트 기능 단위로 새로은 class를 만들고 테스트를 나눈다.
- 초기 세팅을 위한 클래스를 다른 테스트 클래스들이 상속받도록한다.
- 나누어진 클래스들은 각각 다른 파일에 존재한다.
- 기능 테스트를 기능 단위로 여러 파일로 분할하게 되면, 특정 테스트에만 관심이 있다면 모든 테스트를 실행해서 해당 테스트가 실행될 때까지 기다릴 필요가 없다.