%%
주니어 백엔드 개발자에게 필요한 기초 지식이랄게 무엇이 존재할까?
- 장고를 사용한 프로젝트를 진행하며 겪은 다양한 에피소드를 엮어보자.
- 그래서 장고가 뭘 해주는데
- 가상환경, 패키지, 프로젝트 시작
- 장고로 만드는 App, Form, View, Model, Template
- 데이터 모델링
- 데이터 모델링이니까 개념, 논리 모델링 중점적으로 다루자.
- 장고 CRUD 코드와 비교 가능?
- REST 기본 개념
- URI 설계
- Request & Response
- DRF
- 인증과 권한 (Authentication & Authorization)
- OAuth: allauth 활용예시 위주로 + 에러 트래킹
- JWT
- 보안
- CSRF
- SSL/TLS
- Input Validation
- CORS: 에러처리 챕터 참조
- HTTPS
- DevOps: 뭐가 뭔지 대충 아는 식으로
- ec2
- s3: S3 에러트래킹
- rds
- docker
- nginx
이게 보니까 순서가 완전 뒤죽박죽이네. 이대로 두고 비선형 방식으로 책을 그때그때 찾아 읽기 위해 작성했다고 의도를 설명하자.
Top Down Approach
%%
Before start#
note:
저 링크들을 오가면서 발표를 진행할 것임. 라운지에 링크 올리고 발표 시작하자.
주백개그이?#
note:
- "주니어 백엔드 개발자, 그 이상으로" 라는 뜻
- 구체적으로 예상독자의 수준을 제목에서부터 함의 → 비전공자 X, 초보자 X
django#
- 장고가 뭘 해주는데
- Model Template View
- App, Form, Routing
Data Modeling#
- 개념, 논리, 물리 모델링 (ER 다이어그램)
- Django CRUD 예제와 같이
note:
CRUD 예제는 ER 다이어그램을 토대로 admin site에 가서 확인해 보는 것이 좋겠다.
REST API#
+ RESTful한 URI 설계 + Request & Response + DRF 리팩토링한 결과 함께 보기
note:
bookstore 깃허브 페이지 가서 DRF
- users/serializers.py
- users/view.py
Authentication & Authorization#
+ 인증? 인가?
+ Django의 인증, 인가
+ OAuth: allauth
모듈 활용 중심 소셜 로그인
+ JSON Web Token
Securities#
+ CSRF + SSL/TLS + Input Validation + CORS + HTTPS
note:
이런게 있다 정도만 가볍게
DevOps#
+ EC2 + S3 + RDS + Docker + Nginx
note:
척 보면 척 하게 되는 과목이라 그렇게 심각하게 다룰 필요는 없음. 다만 내용이 너무 많으니까
잠깐! ✋#
note:
책 순서와 발표 순서가 뒤죽박죽이라는 것을 눈치챘을거다. 괜찮다. 이 책은 비선형으로 진행이 되기 때문에 그때그때 찾아가며 읽으면 되는 책으로 만들었다. 따라서 이 책을 완독할 의무또한 없다.
Framework#
as a web server
note: - 장고는 웹 서버이고, 웹 애플리케이션임. 프레임워크로써 이미 설계가 된 대로 장고 백엔드 (서버 백엔드 X)와 통신을 해야함.
note:
장고는 주어진 설계대로 App을 제작하여야 하며, App 별로 MVT 패턴을 적용하여야 함.
MVT#
Model, View, Template
note:
장고 백엔드와의 통신규약.
- Model: DB관리, ORM 기능 지원
- View: 비즈니스 로직 구현, MVC 패턴의 View와 Controller를 모두 View가 담당함.
- Template: HTML + Django Template Backend => 동적으로 HTML 생성
Application#
models.py
views.py
urls.py
- ...
note: - MTV 패턴을 구현하는 하나의 단위인 APP, 장고는 다음과 같은 파일로 구성되어 있다.
class SignupForm(UserCreationForm):
class Meta:
model = User
fields = ["email", "nickname", "password1", "password2"]
note: - 백엔드 개발자가 프론트도 겸해야 할 때 쓰는 Form,
note:
RESTful한 리소스 구분을 위한 Routing.
urlpatterns
가 라우팅을 수행함. 프로젝트에도 있고, 각 앱에도 있어서 들어오는 요청에 따라 구체적인 App의 urlpatterns
에게 요청을 전달하는 일종의 ==게이트웨이==와 같은 기능을 하고 있음을 보여준다.
라우팅이 성공적으로 진행되면 view에서 로직을 처리하고 웹에 응답을 보낸다.
GET http://13.209.22.220/products/book/
core/urls.py
urlpatterns = [
path("products/", include("products.urls")),
]
GET http://13.209.22.220/products/book/
products/urls.py
urlpatterns = [
path('book/', book_list, name='book_list'),
]
GET http://13.209.22.220/products/book/
proejcts/views.py
def book_list(request):
books = Product.objects.all()
for book in books:
url = str(book.image)
url_without_query = url.split('?')[0]
book.image = url_without_query
return render(request, 'book_list.html', {'books': books})
Database#
Relational Database#
Data Modeling #
Entity Relationships
note:
Data Modeling 챕터는 바로 들어가기에 앞서 이 개념의 위치 파악에 힘을 써야 한다. 데이터베이스는 사실 윈도우 파일 시스템도 데이터베이스라고 불리울 수 있을 정도로 일반적인 개념.
Relational database(RDB)는 Key와 Value의 관계를 테이블화한 데이터베이스
Key 덕분에 서로 연관성 있는 테이블을 연결할 수 있게 되는데, 여기에서 ==데이터 모델링==이 튀어나오는 것.
데이터 모델링에는 개념 / 논리 / 물리 모델링이 있는데, 우리가 흔히 "DB 다이어그램"이라고 부르는 단계는 개념 + 논리 모델링을 아우른다. 우리는 이 단계에만 집중한다.
Entity#
erDiagram
User {
id
email
nickname
hash
}
note:
엔티티는 고유하게 식별할 수 있고 독립적으로 존재 가능한 개체이다.
Attribute#
Relationship#
Django Models#
products/models.py
RESTful?#
note:
REST는 웹을 망가뜨리지 않으면서 설계를 변경하기 위해 고안된 규약. HTML 문서, 엔티티 등의 리소스를 접근하기 위해 계층적인 경로를 가짐.
메서드(동사)를 통해 리소스 명시와 이에 따른 동작을 분리할 수 있게 됐다. 덕분에 쌩판 처음 보는 요청을 접해도 직관적으로 유추할 수 있게 되었다.
GET /books
POST /books
/deleteBooks
note:
책을 추가하는 동작(동사)가 URI에 정의
/books/getAllBooks
note:
book 이라는 단어 중복사용
Request & Response#
note:
ch.10 요청과 응답 참고바람.
REST는 웹과 함께 성장했기에, HTTP는 REST이다.
request header
note:
우리가 눈여겨 볼 것은 바로 헤더와 바디. 일단 헤더 먼저 보자.
위의 이미지는 요청의 헤더이다.
response header
note:
다음은 응답 헤더를 의미.
note:
바디는 Content-Type
이 정의한 대로 간다. 이미지 왼쪽에서부터 각각 HTML, JS, JSON
DRF#
note:
모든 구성을 DRF으로 변경할 수는 없었다. 우리 프로젝트 중 user, cart 부분만 DRF으로 작업했다.
DRF는 장고 앱 중의 하나로, Serialization, Deserialization을 지원한다.
DRF는 객체, dictionary, QuerySet 등등 장고에서 다룰 수 있는 모든 데이터를 직렬화, 역직렬화 할 수 있다. 직렬화의 결과로 JSON을 채택했으며 (JSON만 있는 건 절대로 아님) JSON은 데이터를 바이트 스트림으로 인코드, 디코드할 수 있다.
그러면 일반 오브젝트는 바이트 스트림으로 바로 변환이 안되냐? ㅇㅇ
users/serializers.py
class UserSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(required=True, write_only=True)
password2 = serializers.CharField(required=True, write_only=True)
class Meta:
model = User
fields = ['email', 'nickname', 'password1', 'password2']
extra_kwargs = {
'password1': {'write_only': True},
'password2': {'write_only': True}
}
note:
다음은 UserSerializer
의 코드 일부를 가져왔다. 모양이 상당히 Form
과 닮았다. 맞다. Form처럼 사용할 수도 있다. 사용자가 <form>
태그에 이메일, 닉네임, 비밀번호를 적어 회원가입을 할 때 Form이 아니라 Serializer
에 의한 JSON이 날아온다.
users/serializers.py
class UserSerializer(serializers.ModelSerializer):
# continue
def validate(self, attrs):
attrs = super().validate(attrs)
password1 = attrs.get('password1')
try:
password_validation.validate_password(password1)
except DjangoValidationError as error:
raise serializers.ValidationError({'password1': error})
return attrs
users/views.py
def signup(request):
if request.method == 'POST':
serializer = UserSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({'success': True, 'message': '회원가입이 완료되었습니다.', 'redirect': '/users/signin/'})
except serializers.ValidationError as error:
errors=''
if 'email' in error.detail and 'password1' not in error.detail:
errors = error.detail.get('email', [])
errors = errors[0] if len(errors) > 0 else ''
if 'password1' in error.detail and 'email' not in error.detail:
errors = error.detail.get('password1', [])
errors = errors[0] if len(errors) > 0 else ''
return Response({'success': False, 'errors': errors}, status=status.HTTP_400_BAD_REQUEST)
return render(request, 'signup.html')
note:
validate
라는 메서드는 우리 form.is_valid
할 때와 같은 API를 가지고 있다.
Authentication#
Authorization#
note:
인증과 인가.
인증은 사용자 신원을 확인하는 절차, 인가는 인증된 사용자에게 적절한 권한을 부여.
# 사용자 객체 반환
user = authenticate(request, username=username, password=password)
# 로그인 실패 시 HttpResponse 반환
login(request, user)
logout(request)
# Boolean 반환
if request.user.is_authenticated:
note:
사용자 모델을 AbstractUser
나 AbstractBaseUser
를 사용한 경우, 기본 내장 인증함수를 손쉽게 사용할 수 있다.
django-allauth#
note:
구글 로그인 구현을 해보자. ch 9.4를 참조
settings.py
설정- 클라이언트 등록 및 발급
- admin-site에서 사이트 등록
- Sites
- Social applications
JWT#
JSON Web Token
note:
JWT는 쿠키와 같이 인증인가를 할 수 있는 여러 방법 중 하나임. 각각의 장단점이 명확하게 있어 무엇이 더 좋은지 논의하는 건 의미가 없음.
Header#
{
"alg": "SHA256",
"typ": "JWT"
}
Payload#
{
"iss": true,
"admin": true,
"name": "Choi Wheatley"
}
Signature#
Security#
- OAuth
- SSL/TLS
- Input Validation
- HTTPS
- CORS
EC2#
S3#
RDS#
Docker#
내 컴퓨터에서는 되는데?
note:
컨테이너 기반의 오픈소스 가상화 플랫폼. 가상머신이 아니라 프로세스로 작동하기 때문에 가벼움.
컨테이너란, 파일 시스템, 시스템 자원, 가상 네트워크를 아우르는 독립된 공간을 의미함.
Nginx#
web server, not web application
note:
웹 서버로 사용되는 nginx는 정적 HTML 문서들을 매우 빠른 속도로 호스팅 하는 용도 외적으로도 리버스 프록시, 부하분산, 캐싱, SSL 오프로딩, HTTP/2 지원 등 다양한 역할을 수행할 수 있는 다재다능한 서버이다.
Gateway#
nginx/default.conf
note:
우리팀은 nginx를 게이트웨이로 사용하고 있다. default.conf
파일을 보면 http 프로토콜의 기본 리스닝 포트인 80을 받아다 장고 웹앱으로 보내는 작업을 하고 있다.
entrypoint.sh
note:
배포단계의 장고앱은 runserver
를 호출하지 않는다! nginx가 장고 앱을 호출한다고 했지만 그 사이에 gunicorn이라는 미들웨어가 runserver
같은 역할을 수행하는 걸로 알고 있지만 책에는 넣지 않았다.