현대에 대ㅡㄹ어서 웹 시스템의 규모가 더욱더 커지고 무엇보다 처리해야 하는 동시 요청 수와 데이터 규모가 기하학적으로 증가하면서 웹 시스템들의 구조 또한 더욱 방대해지고 복잡해지게 되었다.
현대 백엔드 개발자의 영역은 일반적인 백엔드 API 시스템 개발부터 Data Pipeline 시스템, Machine Learning 시스템, Big Data 분석 시스템 등 비실시간 대규모 데이터 수집 및 분석 시스템까지 넓어지고 있다.
ch. 3 - 첫 API 개발 시작
Flask는 파이썬 웹 애플리케이션을 구현할 때 사용되는 프레임워크이며, Django와 다르게 웹 애플리케이션을 구현할 때 꼭 필요한 기능만을 제공하는 프레임워크다. 그러므로 학습 곡선이 비교적 낮다.
파이썬 개발을 할 때에는 먼저 파이썬 가상 환경을 생성한 후 항상 활성화(conda activate [가상환경]) 시킨 상태에서 개발, 실행, 테스트를 해야 한다. 파이썬 가상 환경을 생성하는 방법은 여러 가지가 있지만, 콘다를 사용하여 파이썬 개발 환경을 생성하는 것이 선호된다.
Flask에서는 일반적으로 route 데코레이터를 사용해서 함수들을 엔드포인트로 등록하는 방식이 사용된다. 즉, Flask에서 엔드포인트를 구현한다는 것은 결국은 일반 함수를 구현하는 것과 마찬가지다. 그러므로 백엔드 API 개발도 구조적으로는 크게 어렵거나 복잡할 것이 없다. 해당 API가 제공하는 서비스, 즉 비즈니스 로직을 구현하는 함수들을 개발하는 것이 백엔드 API에서 차지하는 가장 큰 부분이다.
백엔드 API 개발 입문에서 중요한 것은 먼저 기본적인 개념을 먼저 잘 이해하고, 그러고 난 후 API 코드의 전체적인 구조에 대해서 이해하는 것이 핵심이라고 할 수 있다. API의 개념을 잘 이해해서 구조를 잘 잡고 나면 그다음은 필요한 비즈니스 로직을 함수를 통해 구현하기만 하면 된다. API 코드의 전체적인 구조가 일단 잡히면 그다음부터는 엔드포인트들, 즉 함수들을 구현하는 것이 개발의 대부분이다. 함수를 구현하는 것은 개념적이나 구조적으로 어려울 것이 없다.
API를 개발하기 위해 필수적인 기본 개념들 중 가장 중요한 것 하나가 바로 HTTP다. 왜냐햐면 API는 기본적으로 HTTP 통신에 기반을 두고 있기 때문이다.
ch. 4 - HTTP의 구조 및 핵심 요소
HTTP 통신은 요청과 응답으로 이루어져 있다. 클라이언트가 HTTP 요청을 보내면 서버는 해당 요청에 대한 응답을 보내는 것이 하나의 HTTP 통신이다.
HTTP 통신은 stateless다. 클라이언트와 서버는 HTTP 통신을 여러 번 주고받는 것이 일반적인데, HTTP 프로토콜에서는 동일한 클라이언트와 서버가 주고받은 HTTP 통신들이라도 서로 연결되어 있지 않다. 즉, 각각의 HTTP 통신은 독립적이며, 그 전에 처리된 HTTP 통신에 대해서 전혀 알지 못한다.
HTTP 요청 메시지 구성요소
Start line
Header
Body
HTTP 응답 메시지 구성요소
Status line
Header
Body
자주 사용되는 HTTP 메소드에는 GET, POST, OPTIONS, PUT, DELETE 등이 있다.
자주 사용되는 HTTP 응답 코드와 응답 텍스트에는 200 OK, 301 Moved Permanently, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error 등이 있다.
API 엔드포인트 아키텍처 패턴 중 가장 널리 사용되는 패턴은 REST다. REST는 엔드포인트의 고유 주소(URI)와 허용하는 HTTP 메소드를 통해서 제공하는 리소스와 기능을 알 수 있게 해 줌으로써 클라이언트가 API를 더 쉽게 이해하고 사용할 수 있게 해준다.
GraphQL은 REST 방식의 API를 구현할 때 생기는 문제를 해결하기 위해 만들어진 기술로, REST보다 더 유연한 엔드포인트 구조를 구현할 수 있지만, REST보다는 아직 널리 사용되고 있지 않다.
ch. 5 - 본격젹으로 API 개발하기
트위터의 축소된 버전인 '미니터' API 구현. 회원가입부터 팔로우, 타임라인 엔드포인트 기능 구현.
데이터를 수정하는 기능의 엔드포인트는 POST를 사용한다.
데이터를 읽어 들이는 기능의 엔드포인트를 GET을 사용한다.
POST 엔드포인트에 데이터를 전송할 때는 body에 JSON 형식으로 데이터를 전송한다.
URL에 인자(parameter)를 전송하고 싶을 때는 type:value 형식으로 URL을 구성한다. 예를 들어, int 값의 사용자 아이디를 URL에 포함시켜 받고 싶을 때는 다음과 같이 주소를 구성하면 된다.
/timeline/int:user_id
중복된 값이 없어야 하는 데이터라면 set을 사용하고 순서나 순차가 중요하다면 list를 사용하자. 키와 값을 표현해야 하는 데이터의 경우는 딕셔너리를 사용하도록 하자.
ch. 6 - 데이터베이스
데이터베이스 시스템(database system)은 이름 그대로 데이터를 저장 및 보존하는 시스템이다. 데이터베이스에 저장되어 있는 데이터를 읽어 들일 수 있으며, 새로운 데이터를 저장할 수도 있고, 기존의 데이터를 업데이트할 수도 있다.
일반적으로 데이터베이스 시스템은 크게 2가지 종류로 분류된다.
관계형 데이터베이스 시스템(RDBMS, Relational Database Management System)
비관계형 데이터베이스(NoSQL)
관계형 데이터베이스에서 테이블들의 상호 관련성 종류에는 다음의 3가지가 있다.
one to one
one to many
many to many
관계형 데이터베이스에서 외부 키(foreign key)를 사용해서 테이블들을 연결하여 데이터 값의 중복을 최소화하게 데이터를 구조화하는 프로세스를 **정규화(normalization)**라 한다.
관계형 데이터베이스에서 트랜잭션은 일련의 작업들이 마치 하나의 작업처럼 취급되어 모두 다 성공하거나 아니면 모두 다 실패하는 것을 말한다.
SQL(Structured Query Language)은 MySQL 같은 관계형 데이터베이스에서 데이터를 읽거나 생성 및 수정하기 위해 사용하는 언어다.
SELECT 구문 : 데이터 가져오기
INSERT 구문 : 데이터 생성
UPDATE 구문 : 데이터 수정
DELETE 구문 : 데이터 삭제
JOIN 구문 : 데이터 연결
sqlalchemy 라이브러리를 사용하여 파이썬 코드에서 데이터베이스에 연결하여 SQL을 실행시킬 수 있다.
sqlalchemy의 create_engine 함수를 사용하여 데이터베이스에 연결하고 text 함수를 사용하여 실행할 SQL을 만든다.
Flask가 create_app이라는 이름의 함수를 자동으로 팩토리(factory) 함수로 인식해서 해당 함수를 통해서 Flask를 실행시킨다.
ch. 7 - 인증
인증 절차는 대부분의 API들이 필요로 하는 기능이다.
사용자 비밀번호는 꼭 단방향 해시 암호화를 이용해서 저장해야 한다. 그리고 bcrypt와 같이 rainbow attack을 막아 주는 단방향 암호 알고리즘을 사용하는 것이 더욱 안전하다.
HTTP 통신은 stateless이므로 해당 통신 전에 인증 절차를 거치는 통신이 있었는지에 대해서 알 수 없다. 그러므로 모든 HTTP 통신에 인증 여부 정보를 첨부해야 한다. 이러한 것을 가능케 해주는 것이 access token이다.
access token은 JWT(JSON Web Token)을 사용해서 생성될 수 있다. JWT를 생성할 때 조심해야 할 점은 민감한 사용자의 개인정보는 포함시키지 말아햐 한다는 것이다.
인증 절차를 엔드포인트들에 적용시키기 위해서 decorator를 사용할 수 있다.
ch. 8 - unit test
시스템을 테스트하는 방법에는 크게 3가지가 있다.
UI test(End-To-End test) : 이름 그대로 시스템의 UI(User Interface)를 통해서 테스트하는 것.
장점 : 사용자가 실제로 시스템을 사용하는 방식과 가장 동일하게 테스트하게 된다. 가장 확실하게 테스트 가능.
단점 : 시간이 가장 많이 소요된다. 프론트엔드부터 백엔드까지 모든 시스템을 실행시키고 연결해야 할 수 있기 때문.
integration test : 테스트하고자 하는 서버를 실제로 실행시키고, 테스트 HTTP 요청(혹은 HTTP가 아니더라도 해당 서버를 작동시킬 수 있는 요청이나 명령어)을 실행하여 테스트해 보는 방식.
장점 : 하나의 시스템만 실행해서 테스트하므로 UI test에 비해 테스트 설정이나 실행 시간이 더 짧고 간단하며 자동화하기에도 용이함.
단점 : unit test에 비해 자동화에 걸리는 공수가 더 크고 실행 속도도 더 느림.
unit test : 코드 자체를 테스트하는 것.
장점 : 실행하기 쉽고 속도도 빠르다. 디버깅이 비교적 쉽다. 함수 단위로 테스트를 함으로써 문제가 있을 경우 파악이 비교적 쉬울 수밖에 없다.
단점 : 함수 단위로 테스트를 하다 보니 전체적인 부분을 테스트하기에는 제한적. 모든 함수가 제대로 작동하는 것을 테스트했다고 하더라도 전체적인 하나의 서비스로 연결되어 실행되었을 때도 정상적으로 작동하는지에 대해서는 확신하기 힘들다.
전체 테스트 중 UI test는 10%, integration test는 20%, 나머지 70%는 unit test로 구성하는 것을 권장한다.
파이썬에서 unit test 구현을 도와주는 라이브러리 중 하나는 pytest다.
test 파일 이름은 test_ 로 시작해야 하며, 마찬가지로 test 함수도 test_로 시작되어야 한다.
pytest.fixture decorator를 사용하여 test 함수에 필요한 인자들을 제공할 수 있다.
setup_function과 teardown_function을 사용하여 각 test 함수들이 실행 되기 전에 필요한 test 데이터들을 생성할 수 있고, 또한 각 test 함수들이 실행 종료된 후 test 데이터들을 삭제해 줄 수 있다.
각 test 함수들은 서로에게 영향받지 않고 독립적으로 실행되어야 하기 때문에 test 데이터들을 삭제해 주는 것이 중요하다.
Flask의 test_client 함수를 통해 엔드포인트 함수들에 가상의 HTTP 요청(request)을 보내고 응답(response)를 받을 수 있다. 그러므로 test_client 함수를 사용해 unit test를 구현하면 unit test이지만 어느 정도 integration test를 실행하는 효과도 볼 수 있다.
ch. 9 - AWS에 배포하기
미니터 API를 실제로 AWS에 배포했다. EC2를 통해 서버를 구축하고 RDS MySQL을 통해 DB를 연결했다.