인프런 강의를 들으면서 내가 이해한 내용들과 궁금한 점들을 정리해 보았다.

(중간중간 이해되는 내용이 많아질 경우 계속 내용이 추가될 예정이다)

 

 웹 어플리케이션 서버(WAS)와 웹 서버(WS)의 차이점은 무엇일까

WS는 정적인 컨텐츠에 대한 요청을 처리하며, WAS는 웹 어플리케이션 로직을 통해 동적인 요청을 처리할 수 있다는 점이 가장 큰 차이이다. 그러나 요즘은 WS도 설정하기에 따라 꼭 정적 컨텐츠에 대한 요청만 처리하는 것은 아니라고 해서... 무 자르듯이 아주 정교하게 구분하기는 어렵다고 한다. 또한 예전 멘토링에서 말씀하신 것처럼 WAS 안에 WS가 있는 구조라고 한다. WS는 예를 들면 apache, nginx 등이 있고, WAS는 사용하는 프레임워크 별로 다양하다. 가령 django WAS, tomcat 등이 있다. 

 

그러면 WAS만 사용하면 되는 게 아닌가? 굳이 정적인 요청만 처리할 수 있는 WS는 왜 사용할까

요청별로 처리하는 데 드는 비용이 다르다. 가령 주문과 결제 로직을 사용하는 요청의 경우 처리하는 데 드는 시간과 드는 리소스가 더 크고, 반면 단순 이미지를 요청하는 경우는 리소스가 더 작다. 그런데 이 모든 요청을 WAS에게 요청할 경우 값싼 요청을 처리하는 데에도 똑같이 WAS의 리소스를 할당해야 하기 때문에, 이 경우 요청을 먼저 WS가 처리하게 하여서 정적 컨텐츠 요청의 경우는 WAS의 리소스를 아예 사용하지 않도록 할 수 있다. 

 

 WAS는 어떤 기능을 제공하는가

WAS의 큰 장점 중 하나는 서블릿을 지원한다는 것이다. 서블릿을 사용하게 되면 개발자는 비즈니스 로직에만 더 집중할 수 있다. 서블릿에서는 가령 요청이 들어왔을 때 그 요청에서 헤더를 분리하고, 요청의 경로를 확인하고, 파라미터들을 일일이 다 파싱하고... 등등의 작업을 해 준다. 만약 서블릿이 없다면 개발자는 비즈니스 로직 외에도 위의 작업에 필요한 로직을 다 만들어야 했을 것이다. 이럴 경우 작업 효율성이 떨어지고, 코드도 훨씬 더 복잡해졌을 수 있다. 

 

WAS에서는 서블릿을 지원한다. 요청이 오면 서블릿에서 그 요청을 먼저 처리해서, 위에서 언급한 url 경로를 확인하거나 파라미터 및 헤더를 파싱하는 등의 공통적이고 반복적인 작업을 먼저 처리한다. 그 다음 요청이나 응답을 뷰(MVC 패턴을 쓰는 경우)에서 처리하기 쉽게 요청 및 응답 객체를 새로 생성하여 리턴해 준다. 이는 모든 웹 서버 프레임워크에서 공통적인 것으로 보인다. 지금까지 주로 사용해온 django의 경우도 마찬가지로, view 함수에서는 항상 request 라는 객체를 받았고, 그 안에서 request.headers 등의 기본으로 제공되는 메소드를 통해 요청의 헤더나 파라미터 값들을 쉽게 확인할 수 있었다. 

 

또한 WAS는 멀티스레딩 기능도 제공한다. 싱글 스레드로 클라이언트의 요청을 처리하게 될 경우, 앞선 요청이 지연되는 경우 뒤의 요청들까지 전부 밀리게 된다. 그렇다고 해서 요청이 들어올 때마다 스레드를 생성하게 된다면 스레드를 생성하는 비용이 비싸고, 그렇게 만든 스레드를 재사용할 수 없어서 비효율적이며, 스레드 간 context switching 비용이 발생하며, 요청을 무한대로 제한 없이 받을 수 있어 과도한 요청이 들어오면 WAS가 다운될 수 있다는 단점이 있다. 그래서 보통은 스레드 여러 개를 모아 둔 스레드 풀을 만들어 두고, 요청이 들어오면 스레드 풀에서 필요한 스레드를 꺼내 쓰는 방식으로 사용한다. 

 

✅ 궁금한 점들

django WAS는 다른 WAS와는 어떻게 다를까. 서블릿은 어떻게 구현되어 있는지, 또 멀티스레딩은 파이썬에서 어떻게 구현했을지 궁금하다. 

톰캣에서 사용하는 스레드 풀의 기본 max thread 값은 200개라는데, 현재 개발하고 있는 서비스(onestep)의 경우는 그렇게 많은 스레드가 필요해 보이지는 않는다. 얼마 정도가 적당할지 궁금한데, 아마도 테스트를 통해서 알 수 있을 것 같다. 그러니 테스트도 해 보자.

 

'server-side > server' 카테고리의 다른 글

Mac 환경설정  (0) 2024.07.15
Software Release Life Cycle  (0) 2023.07.15
OAuth 2.0 기본원리  (0) 2022.09.26
인증(Authentication)  (0) 2022.07.14
linux: cron 사용해서 자동으로 스케줄 실행하기  (0) 2022.07.09

프로그램을 진행하면서 새 맥북을 받았다! 너무 기뻤지만, 항상 사용하던 윈도우 노트북에서 새 맥북으로 바꾸려니 초기 설정부터 다시 해야 한다는 문제점이 있었다. 

 

혹시라도 내가 다음에 또 노트북을 바꾸게 되거나, 비슷한 개발자들이 있다면 참고할 수 있도록 하기 위해서 간략하게나마 글을 적어본다. 

 

설치할 프로그램들

1. brew

이 사이트에서 homebrew를 설치할 수 있다. 나는 homebrew는 mac에서 사용하는 일종의 패키지라고 알고 있고, 실제로 앞으로 설치할 많은 프로그램들(git, docker 등)이 brew install ___으로 간단히 설치가 가능하므로, 맥 사용자라면 homebrew 설치는 거의 필수적이라고 볼 수 있다. 

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

 

2. git

brew install git

 

잘 설치되었는지 확인해준다. 

git --version

 

3. docker

brew install docker

 

잘 설치되었는지 확인해준다. 

docker --version

 

4. iterm

필수 사항은 아니지만, 맥에서 기본으로 제공하는 터미널이 예쁘지 않다는 의견이 많아서 내 주변 맥 사용자들도 다 iterm을 사용하는 것 같아서 깔아봤다. 공식 사이트에서 설치할 수 있다. 

 

5. python

지금 개발중인 프로젝트에서는 python, django를 사용하므로 파이썬도 깔아줬다. 공식 홈페이지에서 맞는 버전을 사용하면 된다. 

 

6. java

지금 개발에서 사용하는 언어는 아니지만, 앱 개발을 하려면 android studio가 필요하고 android studio에서는 java를 필요로 하기 때문에 결론적으로 java도 필요했다. 주의할 점은, java를 설치하는 방법이 꽤나 여러가지라는 것이다. 그리고 android studio에서 빌드를 실행시키다 보면 jdk와 gradle의 버전이 맞지 않아서 나는 오류가 있다. 

이 사이트에서는 여러 자바 버전을 설치할 수가 있어서 여러 블로그들을 찾아보다 이 사이트에서 특정 jdk 버전을 다운받아서 오류를 해결하였다. 

 

자바가 잘 설치되었는지 확인하는 명령어는 다음과 같은데, 당연하게도 처음에 이 명령어를 입력하면 java, javac를 인식하지 못한다. 

java -version
javac -version

 

그 이유는 java에 대해서 환경변수 등록을 해주지 않았기 때문이다. 

맥에서는 루트 폴더 기준으로 .zshrc 라는 파일에 여러 환경변수 경로를 설정할 수 있는데, 이 안에 다음과 같이 입력해 주자. 

 

참고로 맥에서 파일을 읽거나 쓰기 위해서는 vim 이나 nano 명령어를 사용한다. 그러니까 iterm의 초기 디렉토리에서 다음과 같이 입력하면 아마도 .zshrc 창이 열릴 거다. 

vim .zshrc

 

여기서 -v 다음에 있는 17의 경우, 나의 경우는 jdk 17을 사용하고 싶어서 제한을 둔 것이다. 만약 다른 버전의 jdk를 명시적으로 사용하고 싶다면 17 대신 다른 숫자를 입력하면 된다. 

export JAVA_HOME=$(/usr/libexec/java_home -v 17)
export PATH=$JAVA_HOME/bin:$PATH

 

아래와 같은 명령어로 .zshrc 파일에서 변경된 내용을 반영해 준 다음, 다시 자바의 버전을 확인해보면 될 것이다. 

source .zshrc

 

아 그리고 자바 버전이 여러 개 깔려있을 때에도 중요한 내용인데, android studio에서 사용하는 gradle에서 특정 jdk를 사용하도록 설정하고 싶을 수 있다. 

gradle과 관련된 설정 파일은 보통 iterm의 초기 디렉토리에서(iterm을 열면 맨 먼저 보이는 기본 경로) .gradle > gradle.properties 파일 안에 있다(없으면 그 파일을 만들면 된다). 

 

파일을 만들고 싶다면 touch 명령어를 사용해 보자. 

touch gradle.properties

 

이후 vim이나 nano로 해당 파일을 열어준 뒤, 다음과 같이 입력했다. 나의 경우는 다음과 같다. 

org.gradle.java.home=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home

 

org.gradle.java.home 이라는 속성의 값을 java가 위치한 경로로 설정하는 것이 중요하다. 만약 이 경로를 모르겠다면 다음과 같이 입력해서 java가 어디 설치되어 있는지 찾아볼 수 있다. 

which java

 

나의 경우 이렇게 입력하면

/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/bin/java

 

이렇게 나오는데, 이 중에서 뒤에 /bin/java를 뺀 값을 경로로 지정해 주면 된다. 

 

7. visual studio code

공식 사이트에서 설치할 수 있다. 

 

8. android studio

공식 사이트에서 설치할 수 있다. 

 

 

 

 

 

 

 

 

 

'server-side > server' 카테고리의 다른 글

웹 어플리케이션 서버(WAS)  (0) 2024.08.05
Software Release Life Cycle  (0) 2023.07.15
OAuth 2.0 기본원리  (0) 2022.09.26
인증(Authentication)  (0) 2022.07.14
linux: cron 사용해서 자동으로 스케줄 실행하기  (0) 2022.07.09

⚠️

아래 공식문서를 읽고 인증 관련된 내용 중 일부만 발췌해서 정리했습니다.

자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/auth/customizing/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

✅custom user model 설정하기

 

settings.py 파일의 AUTH_USER_MODEL 변수의 값을 "app이름.Model이름" 으로 설정하자. 

AUTH_USER_MODEL = "account.CustomUser"

 

또한 공식문서에서는 인증 모델의 정보에 접근할 때는 쿼리셋을 작성할 때 흔히 사용하는 방식(CustomUser.objects....) 보다는 django.contrib.auth 모듈의 get_user_model() 사용을 권장한다. 

 

이유는 다른 auth user model을 사용하는 다른 프로젝트에서 코드를 사용할 때 충돌이 날 수 있어서라고 한다. 

 

확장성 있는 코드를 작성하고 싶다면 공식문서대로 하는 것이 좋겠고, 다른 프로젝트에서 코드를 사용할 일이 없다면 크게 상관없지 않을까 싶다. 

 

✅인증 정보 구분하기: profile model, proxy model

공식 문서에서는 인증 관련 정보는 user 모델에, 유저와 관련된 그 외 다른 정보는 다른 모델(profile model)에 저장하는 것을 권장한다. 

 

예를 들어 profile model이 Employee라고 하면, Employee와 User 모델의 관계를 1:1 또는 외래키로 설정하는 것이다. 

class Employee(models.Model):
	user = models.OneToOneField(User)
	...

 

반면 인증 관련 정보 외에 추가로 정보를 저장할 필요는 없지만 User 모델이 동작하는 방식을 바꾸고 싶다면 proxy model을 사용하는 것도 좋다. 

 

프록시 모델(proxy model)을 사용하면 유저 모델에서 사용 가능한 메소드나 쿼리셋의 정렬 등을 유저 모델과 다르게 설정할 수 있다. 

class Member(CustomUser):

	class Meta:
		proxy = True

 

✅User model Customizing

공식문서에서는 유저 모델을 정의할 때 django.contrib.auth.models 모듈의 AbstractBaseUser를 상속해서 사용하는 것을 권장한다. 

 

AbstractUser는 장고의 기본 유저 모델인 만큼 인증에서 사용할 수 있는 여러 메소드가 정의되어 있기 때문이다. 

 

물론 AbstractUser를 상속하지 않고도 유저 모델을 새로 정의해서 사용하는 것도 가능하다. 

 

하지만 이 경우 AbstractUser에 있던 기본 메소드들을 따로 정의해 주어야 하기 때문에 불편할 수도 있다. 

 

AbstractUser 모델을 상속해서 커스터마이징 할 경우, 다음과 같은 필드를 설정할 때는 주의해야 한다. 

 

USERNAME_FIELD

각 유저를 unique하게 인식하기 위해 필요한 필드이다. 이 값으로 선언한 필드는 반드시 unique 해야 한다. 

 

REQUIRED_FIELDS

createsuperuser 커맨드를 호출할 때는 이 필드들의 값을 모두 입력해야 한다. 이 필드값들은 unique 할 필요는 없지만 blank=False 이어야 한다. 

class CustomUser(AbstractUser):
	...
	email = models.CharField(max_length=40, unique=True)
    
	USERNAME_FIELD = "email"
	REQUIRED_FIELDS = ["email", "address"]

 

☑️blank와 null의 차이

null은 DB에 값을 저장할 때 null을 허용할지를 결정하고, blank은 DB와는 관련 없는 validation의 영역이다. null=True이어도 blank=False라면 DB에 넣을 값을 검사할 때 허용되지 않는다. 

 

그 외에도 AbstractBaseUser에는 세션이나 비밀번호 설정 등과 관련한 여러 메소드를 제공한다. 

 

is_authenticated

요청을 한 유저가 인증된 유저인지를 boolean 값으로 리턴한다. 

다만 이 값이 True라고 해도 이 유저가 어떤 특정 권한이 있거나 이 유저에 대한 세션이 있는지는 보장하지 않는다. 

 

set_password()

장고에서는 유저의 비밀번호 값을 그대로 저장하지 않고 암호화하여 저장하기 때문에 password의 값을 그대로 바꾸면 로그인이 되지 않는다. 

그 대신 set_password() 함수를 사용하면 해당 값이 암호화되어 저장된다. 

 

set_unusable_password()

장고에서 비밀번호를 사용하지 않을 때도 이 메소드를 사용하면 좋다. 

비밀번호에 공백 값을 넣는 것과 비밀번호를 사용하지 않는 것은 다른데, 이 메소드는 후자일 경우에 사용하자. 

 

get_session_auth_hash()

유저의 비밀번호를 HMAC 방식으로 암호화한 값을 리턴한다. (정확히 어떤 데 쓰이는지는 아직 모르겠다...)

 

 

☑️AbstractUser

만약 유저 모델의 특성을 거의 바꿀 것 같지 않다면 AbstractBaseUser를 상속한 AbstractUser 모델을 그대로 사용하는 것도 가능하다. 

 

이 모델에서는 USERNAME_FIELDREQUIRED_FIELDS 옵션도 이미 정의되어 있다.

 

AbstractBaseUser의 설정을 대부분 따르되 그 중 일부만 변경하고 싶다면 AbstractUser를 사용하면 코드를 간결하게 작성할 수 있다. 

class CustomUser(AbstractUser):
	pass

 

✅User manager Customizing

기본적으로, 각 모델에는 objects라는 이름을 가진 manager 클래스가 있다. 

 

이 manager 클래스는 모델이 어떤 쿼리를 통해서 데이터베이스와 어떻게 상호작용할지를 결정하는 일종의 인터페이스 역할을 한다. 

 

 

☑️BaseManager

 

유저 모델의 manager 클래스는 django.contrib.auth.models 모듈의 BaseManager 클래스를 상속한다. 

 

get_by_natural_key()

BaseManager에서 유저를 찾는 데 사용하는 메소드이다. 

createsuperuser 등의 커맨드에서도 호출된다. 

유저에서 USERNAME_FIELD로 선언한 필드의 값을 사용해서 유저를 찾는다.

 

 

☑️CustomManager

 

이 클래스를 상속해서 커스텀 manager 클래스를 만들고 싶다면 두 가지 메소드를 재정의해야 한다. 

 

create_user()

유저를 생성할 때 호출되는 메소드이다.

유저 모델에서 USERNAME_FIELDREQUIRED_FIELDS로 선언한 필드들은 모두 매개변수로 받아야 한다. 

 

create_superuser()

createsuperuser 커맨드를 사용할 때 호출된다. 

마찬가지로 USERNAME_FIELDREQUIRED_FIELDS로 선언한 필드들은 모두 매개변수로 받아야 한다. 

 

 

참고한 포스트

https://docs.djangoproject.com/en/5.0/ref/models/fields/#:~:text=null%20is%20purely%20database%2Drelated,the%20field%20will%20be%20required.

https://docs.djangoproject.com/en/5.0/topics/db/managers/

 

'server-side > Django' 카테고리의 다른 글

django customizing authentication  (0) 2024.01.07
django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

⚠️

아래 공식문서를 읽고 인증 관련된 내용 중 일부만 발췌해서 정리했습니다.

자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/auth/customizing/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

✅django authentication

장고에서는 인증을 수행하기 위한 여러 백엔드 클래스(authentication backends)들이 있다. 

 

settings.py 설정 파일에서 AUTHENTICATION_BACKENDS 라는 변수 값으로 이 인증 백엔드 클래스를 어떻게 설정할지를 지정할 수 있다. 

 

인증 백엔드가 여러 개일 경우, 맨 위에 선언한 클래스부터 순차적으로 인증이 실행된다. 상위 클래스에서 인증이 완료되면 하위 클래스는 실행되지 않는다. 

 

기본값은 ModelBackend이다. 

AUTHENTICATION_BACKENDS = [
	"django.contrib.auth.backends.ModelBackend"
]

 

인증 백엔드는 세션과도 연관이 있다. 하나의 세션에서는 인증에 사용하는 백엔드를 캐싱해 두고 인증할 때 그 클래스를 사용한다. 

 

django.contrib.auth 모듈의 authenticate()를 호출하면 장고의 모든 인증 백엔드에서 순차적으로 인증을 시도한다. 

 

☑️login() vs authenticate()

인증을 수행한다는 공통점이 있다.

authenticate(): 인증 정보가 유효한지를 한 번 확인한다.

login(): 인증 정보가 유효한지를 확인하고 유효하다면 세션을 만드는 등의 후처리를 한다. 그 결과 한 번 인증된 유저를 여러 번 인증할 필요가 없게 한다. 

 

✅Custom Authentication

기본 인증을 사용하지 않고 새로운 인증 클래스를 만들어서 사용해야 할 때도 있다.

 

예를 들면 서로 다른 두 앱(django app)의 유저를 통합하거나 할 때, 두 앱에서 모두 사용할 수 있는 인증 클래스가 필요할 것이다. 

 

새로운 인증 백엔드 클래스(Authentication Backend)를 만들기 위해서는 크게 두 가지가 필요하다. 

  1. django.contrib.auth.backends 모듈의 BaseBackend 클래스를 상속하는 새 클래스 만들기
  2. 해당 클래스에서 두 가지 메소드 구현하기
  • authenticate: 요청 및 필요시 추가 정보(credentials)를 받아서 인증 되면 user, 아니면 None을 리턴한다. 
  • get_user: user 인스턴스의 pk 값을 받아서 user를 리턴한다. 
from django.contrib.auth.backends import BaseBackend

class CustomBackend(BaseBackend):

	def authenticate(self, request, credentials=None):
		...
        
	def get_user(self, user_id):
		...
        
   ...

 

 

✅Authentication Backend에서 권한(Permissions) 부여하기

BaseBackend 클래스에서는 User 모델 및 UserManager 클래스에서 정의하는 여러 권한 조회 함수들(permission lookup functions)을 사용할 수 있다. 

  • get_user_permissions: 해당 유저가 직접적으로 가진 권한들을 리턴한다. 
  • get_group_permissions: 해당 유저가 속한 그룹이 가진 권한들을 리턴한다. 
  • get_all_permissions: 위의 두 메소드의 합집합을 리턴한다. 
  • has_perm(perm): 유저가 해당 권한을 가지고 있는지를 boolean 값으로 리턴한다. 
  • has_module_perms(module_name): 유저가 해당 모듈(django app)에 해당하는 권한을 하나라도 갖고 있다면 True를 리턴한다. 

만약 시행 중 Permission Denied 에러가 리턴되면, 미들웨어처럼 이후의 인증 백엔드를 거치지 않고 에러를 리턴한다. 

 

✅특정 모델 인스턴스에 대해 권한 생성하기

모델 클래스의 Meta 옵션에 permissions 속성을 추가하고, 그 값으로 추가하고 싶은 권한들을 작성한다. 

권한들도 DB를 사용하는 경우 하나의 테이블로 맵핑되기 때문에, 이렇게 추가한 권한 값들은 migrate 명령어를 실행한 이후에 생성되므로 그 이후에 사용할 수 있다. 

class Task(models.Model):
	...
	class Meta:
		permissions = [
			("change_task_status", "Can change the status of tasks"),
			("close_task", "Can close the status of tasks"),
		]

 

 

참고한 포스트

https://stackoverflow.com/questions/28249276/whats-the-difference-between-authenticate-and-login

 

'server-side > Django' 카테고리의 다른 글

django customizing user  (0) 2024.01.07
django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

아래 공식문서를 읽고 세션에 관련된 기본 내용만 발췌하여 정리했습니다. 자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/http/sessions/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

Session

  • 세션은 인증에서 사용되는 방법 중 하나로, 서버에 접속한 개별 클라이언트마다 인증에 필요한 정보를 서버에 저장해 두고 관리하는 방식이다.
  • 인증 정보를 서버 측에서 관리하기 때문에 보안 면에서 더 안전하고 정보가 탈취될 가능성이 더 적다.
  • 반면 사용자가 많아질수록 그 많은 사용자의 정보를 서버에서 모두 관리해야 하기 때문에 서버의 부하가 커질 수 있고, DB 등 정보를 저장할 곳이 필요하기 때문에 확장성이 떨어진다는 단점도 있다. 

 

Django Session

장고에서는 세션을 미들웨어(middleware)로 구현한다.

장고의 설정 파일(settings.py)의 middleware 변수에는 django.contrib.sessions.middleware.SessionMiddleware 클래스가 기본으로 추가되어 있는데, 이 미들웨어가 장고에서 기본 세션을 구현하는 역할을 한다. 

만약 장고에서 세션을 사용하고 싶지 않다면 이 미들웨어를 middleware 변수에서 빼 주자. 

 

앞서 세션은 인증에 필요한 정보를 서버 측에 저장하는 방식이라고 했다. 

장고에서는 이 정보를 어디에 저장하는지에 따라서 다양한 세션 엔진(session engine)의 종류가 있다. 

 

DB

  • 장고 세션에서 사용하는 기본 설정이다.
  • 사용하는 DB에 세션 정보를 저장하는 전용 테이블을 만들어서 그 안에 세션 정보를 저장한다.
  • 이 경우 Session 객체를 통해 개별 세션의 정보에 접근할 수 있다. 

Cache

  • 공식문서에서는 Redis나 MemCached 같은 인메모리 데이터 저장소를 사용하는 경우에만 사용하는 것을 추천한다. 
  • 로컬 메모리 캐시를 사용할 경우 데이터를 오랫동안 저장해두지 못할 수 있고, 로컬 메모리 캐시는 멀티 프로세스 환경에 적합하지 않기 때문이다(not multi-process safe, 멀티 프로세스 환경에서 발생하는 동시성 에러 등으로 데이터가 변경될 수 있다). 
  • 인메모리 데이터 저장소에서는 데이터를 key-value 쌍으로 저장하기 때문에, 이 경우 key는 각 세션을 구분하는 session key가 될 것이고 세션에 저장할 데이터가 value 값이 된다. 

Cookie

  • http 통신에 사용하는 쿠키 형태로 세션 데이터를 저장하는 방식이다. 
  • 다만 쿠키 데이터를 cryptography 방식으로 암호화하여 저장한다. 

File

  • 파일 형태로 로컬 디렉토리에 저장한다.
  • 기본 값으로는 /tmp 디렉토리에 저장되고, 기본 디렉토리를 바꾸고 싶다면 설정 파일의 SESSION_FILE_PATH 변수 값을 따로 지정하면 된다. 

 

Session object 특징

  • session_key: 이 값으로 각 세션을 구분한다. session key는 40글자 이내의 문자열이다. 
  • expiry_date:  각 세션이 언제 만료되는지를 초 값으로 저장한다. 
  • session_data: dictionary 형태로 인증에 필요한 정보를 저장한다.

 

Sesssion 특징

서버에서 브라우저와 통신할 때는 쿠키를 사용하는데, 이 쿠키에 현재 세션을 unique하게 구분하기 위한 session key 값이 들어 있다. 만약 session key 값이 잘못되었거나 해당 세션의 만료 시점이 지났다면 인증이 정상적으로 시행되지 않는다. 

 

세션 데이터는 사용하는 세션 엔진에 따라서, 또는 커스텀 세션의 경우는 직접 정의한 encoder 및 decoder에 따라서 값 그대로 저장되지 않고 인코딩 되어 저장된다. 

 

세션 데이터의 값이 수정될 때마다 세션 값이 새로 저장되는 것이 기본적이다. 만약 그렇게 하지 않고 매번 요청할 때만 값을 새로 저장하고 싶다면, 설정 파일에서 SESSION_SAVE_EVERY_REQUEST 변수 값을 True로 바꿔주자. 

 

세션은 세션 정보를 저장하는 쿠키의 만료 시점까지 지속되는 것이 기본 설정이다. 그러나 브라우저를 닫을 때 관련 세션이 닫히게 하고 싶다면, 설정 파일의 SESSION_EXPIRE_AT_BROWSER_CLOSE 변수 값을 True로 바꿔주자. 

 

세션이 만료된 경우에도 DB 등에 저장된 세션 데이터는 자동으로 사라지지 않고 남아있다. 이 경우 clearsessions 명령어를 통해 만료된 세션 데이터를 주기적으로 지워 주어야 한다. 

 

 

그 외 참고한 포스트들

https://docs.djangoproject.com/en/5.0/topics/http/sessions/

https://zangzangs.tistory.com/72

https://superfastpython.com/process-safe-in-python/

 

'server-side > Django' 카테고리의 다른 글

django customizing user  (0) 2024.01.07
django customizing authentication  (0) 2024.01.07
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

migration(마이그레이션)이란

장고에서 모델과 연결된 DB의 버전을 관리하는 방법이다. migration은 파이썬 파일 형식으로 관리된다. 

 

migration의 특징

  1. 실제 DB에서 변경되는 내용이 없어도 migration 파일이 만들어질 수 있다. 
  2. 어떤 DB에는 적용 가능한 내용이 다른 DB에는 적용 불가능할 수 있다. (MySQL의 경우 PostgreSQL보다 컬럼에 사용하는 max_length 속성의 최댓값이 작다)
  3. 직접 migration 파일을 수정할 수 있고 빈 migration 파일을 만들 수도 있다. 
  4. transaction 개념이 있는 DB(postgresql, sqlite)의 경우, migration은 하나의 transaction 안에서 실행된다. 그렇지 않은 경우는 transaction 없이 실행되고, 이 경우 migration 도중 failure가 발생하면 rollback이 되지 않는다. 
  5. 맨 처음 만들어진 migration(initial migration)이 아닌 경우, migration은 다른 migration에 의존한다. 

 

migration이 만들어질 때

python manage.py makemigrations 커맨드를 입력하면 migration 파일이 만들어진다. 

migration 파일에는 여러 속성들이 있지만, 그 중에서도 dependencies와 operations가 대표적이다. 

dependencies는 해당 migration이 다른 어떤 migration에 의존하고 있는지를 나타낸다. 

operations는 해당 migration이 해당 앱의 모델을 어떻게 변형할지를 나타낸다. 

 

makemigrations 명령어에 따라 migration 파일이 만들어지는 원리

  1. 장고는 이전에 있던 migration들을 순서대로 실행한다. 
  2. 현재 모델의 상태와 1번의 과정에서 얻은 모델의 상태를 비교한다. 만약 변화된 점이 없다면 'detected no changes'와 유사한 문구가 뜬다. 
  3. 2번에서 현재 모델과 migration 파일들을 순차적으로 실행하면서 얻은 모델의 상태에 차이가 있다면, 그 차이점을 바탕으로 migration 파일을 만든다. 

migration 관련 명령어들

  • makemigrations: 위의 과정으로 migration 파일을 만든다. 
  • migrate: makemigrations으로 만들어진 파일이 있는 경우 해당 내용을 실행해서 DB 스키마를 바꾼다. 만약 모델에 변화가 있는데 makemigrations 없이 migrate를 실행한 경우, migration 파일이 만들어지고 그 파일에 대해서 바로 migrate 명령어가 이어서 실행된다. 
  • sqlmigrate: migrate와 똑같이 동작하는데, 다만 동작 과정에서 어떤 SQL문이 실행되는지를 보여준다. 
  • showmigrations: 현재 어떤 migration들이 있는지를 보여준다. migration 파일은 있는데 DB에 반영되지 않은 경우에는 체크 표시가 없고, migration 파일도 있고 DB에 정상적으로 반영된 경우 체크 표시가 있다. 
  • squashmigrations: 기존에 있던 여러 개의 migration 파일을 압축하는 커맨드이다. 

 

apply migration

python manage.py migrate {app_label}

특정 앱에 대해서만 migration을 적용하고 싶을 때 사용한다. 

 

reverse migration 

python manage.py migrate {app_label} {number/migration name}

특정 앱에 대해서 이전 migration으로 진행 상태를 돌리고 싶을 때 사용한다. migration의 번호를 입력하면 해당 번호까지의 migration이 실행된 상태로 돌려 준다. 

물론 항상 가능한 것은 아니고, 되돌리지 못하는 migration의 경우는 IrreversibleError가 발생할 수 있다. 

 

squash migration

python manage.py squashmigrations {app_label} {number/migration name}

기존 여러 개의 migration 파일을 1개 혹은 몇 개로 줄이는 작업이다. 최초 migration부터 입력한 번호까지의 migration을 압축한다고 볼 수 있다. 기존 migration보다 파일의 개수나 operation의 개수는 줄어들지만 DB에 적용되는 내용은 똑같다. 

 

작업 원리

  1. squash 대상이 되는 모든 migration에서 operation을 추출해서 순서대로 정렬한다. 
  2. 1번 결과에 대해 optimizer를 실행해서, 중복되는 operation은 생략하는 등 operation의 개수를 줄인다. (ex. CreateModel과 DeleteModel이 같이 있다면 두 operation 모두 삭제하기)
  3. 2번의 결과를 migration 파일로 만든다. 

 

squashed migration -> normal migration

squashed migration(squashmigration의 결과로 생성된 파일)은 해당 파일의 replace 속성이 있어서 일반 migration과는 달리 "기존의 migration을 대체한" 파일이다. 

이 파일이 일반 migration처럼 동작하게 하려면 추가 작업이 필요하다. 

  1. squashmigrations 명령어 실행하기
  2. 1번의 migration 파일이 대체한 기존 migration 파일들 삭제하기
  3. 2번에서 삭제한 migration 파일에 의존하는 다른 파일들의 dependencies 속성을 1번에서 새로 생성된 squashed migration 파일로 수정하기
  4. 1번 파일에 표시된 replaces 속성을 삭제하기

 

fake migration

이미 DB에는 SQL문이 실행되어 테이블과 컬럼이 있으나 장고 migration에서는 반영되지 않았을 때 --fake 옵션을 사용한다. 

즉 장고에서 관리하는 migration의 진행 상태만 진행한 것으로 변화시키고, 실제로 DB에 SQL문은 실행하지 않는 것을 fake migration이라고 한다. 

 

initial migration

맨 처음 실행되는 mgiration으로, 다른 migration에 의존하지 않는다. 

보통 initial migration은 1개이지만 2개 이상의 initial migration을 만들 수도 있다. 

--fake-initial 옵션을 붙이면 이미 DB에서 테이블과 컬럼이 만들어진 경우 초기 migration을 적용하지 않고 스킵할 수 있다. 다만 이 옵션은 적용하려고 했던 initial migration에 선언된 모델이 DB에 존재하는지가 확인되어야 적용할 수 있다. (적용하려는 모델이 없는데 --fake-initial 옵션을 쓸 수는 없다.)

 

RunPython

기존의 migration 파일을 작성하는 방법이 아니라, python 코드로 DB 내부의 내용을 변경할 수 있다. 

(RunPython과 비슷하게는 RunSQL도 있다. 이 경우 SQL문을 직접 장고에서 실행할 수 있다.)

 

주의 사항

1. RunPython으로 현재 앱이 아닌 다른 앱에 있는 모델에 접근할 경우, migration 파일의 dependencies 속성에다 다음 2가지를 꼭 추가해야 한다. 

  • 현재 app의 initial migration
  • 접근하는 다른 앱의 가장 최신 migration

그렇지 않으면 LookupError 에러가 발생한다. 

 

2. RunPython으로 data migration 작업을 할 수 있다. 

  1. python manage.py migrate {app_label} --empty 커맨드로 빈 migration 파일을 만든다. 
  2. 해당 파일의 dependencies 속성에는 해당 앱의 initial migration만 추가한다. 
  3. operations에는 migrations.RunPython()을 명시하고, 괄호 안에는 callable(실행 가능한 함수의 이름)을 적는다. data migration 작업을 하는 함수를 만들고 그 함수의 이름을 callable로 적으면 된다. 

 

'server-side > Django' 카테고리의 다른 글

django customizing authentication  (0) 2024.01.07
django sessions  (0) 2024.01.06
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20
models and databases  (0) 2023.09.16

Routing(라우팅)

서버로 들어온 요청에 맞는 리소스나 페이지로 요청을 이동시키는 것이다. 

 

Routing 방법들

1. path 사용

path("경로", Viewset)

from django.urls import path

urlpatterns = [
	path("/path", ViewSet.as_view()),
]

 

2. router 사용

router.register("경로", Viewset)

from rest_framework import routers

router = routers.SimpleRouter()
router.register(r"path", ViewSet)
urlpatterns = router.urls

 

3. @action decorator 사용

path, router과는 조금 다른 방법으로, 기본적인 url mapping을 따르지 않거나, view의 역할이 기본적인 CRUD가 아닐 때, 또는 추가적인 permission 등을 지정하고 싶을 때 사용한다. 

class TempViewSet(ModelViewSet):
	
    @action(methods=['post'], detail=True, permission_classes=[IsAdminOnly], url_path="tempapi")
    def temp_view(self, request):
		...

methods: 해당 view에 접근 가능한 http method를 리스트 형식으로 지정한다. 

detail: 해당 view가 모델 개별 인스턴스에 대한 정보를 다루는지, 모델 전체 인스턴스들에 대한 정보를 다루는지를 boolean 값으로 표현한다. 

permission_classes: 해당 view에 어떤 permission 클래스에 해당해야 접근할 수 있는지를 리스트 형식으로 지정한다. 

url_path: @action을 사용하는 경우 보통 url 경로는 메소드의 이름으로 정해진다. 만약 url 경로를 메소드의 이름과 다르게 설정하고 싶다면 설정해주면 된다. 

 

Router: SimpleRouter와 DefaultRouter

1. 차이점

- DefaultRouter는 프로젝트의 모든 url이 전부 명시되어 있는 기본 페이지를 제공한다. 

- DefaultRouter는 api를 제공할 때 .json 형식으로도 제공한다. 

 

2. url path, http method, action에 따라 url을 mapping 하는 방식

url style http method action
기본 path/ GET LIST
기본 path/ POST CREATE
기본 path/해당 url path/ 지정 X @action(detail=False)
기본 path/pk/ GET RETRIEVE
기본 path/pk/ PUT UPDATE
기본 path/pk/ PATCH PARTIAL-UPDATE
기본 path/pk/ DELETE DESTROY
기본 path/pk/해당 url path/ 지정 X @action(detail=True)

 

+

또한 django에서 제공하는 router들은 기본적으로 모든 url 뒤에 '/'를 붙이는 것이 기본이다. 

그러나 router를 선언할 때 trailing_slash 옵션을 False로 선언하면 url의 맨 뒤에 '/'가 붙지 않도록 할 수 있다. 

router = routers.SimpleRouter(trailing_slash=False)

 

'server-side > Django' 카테고리의 다른 글

django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django apps  (0) 2023.12.20
models and databases  (0) 2023.09.16
python - poetry 사용하기  (0) 2023.07.12

Application(앱)이란

django-admin startproject myproject

이 커맨드로 장고 프로젝트(project)를 처음 만들면 기본 디렉토리에 asgi, wsgi, settings 등의 파일이 생성된다.

이 파일들은 프로젝트 전역에서 사용되는 파일로, 하나의 앱(application)에 소속되어 있지 않다. 

 

django-admin startapp myapp

여기서 이 커맨드로 myproject에 소속된 myapp 앱을 만들면 프로젝트 디렉토리 기준으로 /myapp 이라는 디렉토리가 생기고, 이 디렉토리 안에는 apps, models, views, tests 등 여러 파일이 생긴다. 이 파일들은 해당 myapp 앱 안에 소속되어 있다. 

 

장고 앱은 하나의 프로젝트 안에서도 여러 역할이나 기능이 나누어질 수 있기 때문에, 그 기능이나 역할별로 코드를 효율적으로 작성하는 데 도움이 된다. 

 

App 선언하고 사용하기

startapp 커맨드로 앱을 만든 뒤에는 이 앱을 장고가 인식할 수 있게 해야 한다. 

프로젝트 디렉토리의 settings.py의 INSTALLED_APPS라는 리스트 형식의 변수에다가 해당 앱 디렉토리 까지의 경로를 추가해 준다. 

 

이때 앱 디렉토리까지의 경로만 입력해도 장고가 앱을 인식할 수 있는 이유는, 앱 안의 apps.py에 기본적으로 AppConfig 클래스가 정의되어 있기 때문이다. 

이 클래스는 AppConfig라는 클래스를 상속하여야 하고, 한 앱당 최소 1개 이상은 있어야 한다. 

다만 꼭 apps.py 파일 안에 있을 필요는 없다. AppConfig 클래스는 해당 앱의 바깥에다가 정의해도 상관없다. 대신에 이 경우 INSTALLED_APPS에는 해당 AppConfig 클래스까지의 경로를 정확히 명시해 주어야 한다. 

 

AppConfig는 각 장고 앱에 관련된 메타 데이터를 저장하는 클래스이다. 장고에서는 각 앱마다 있는 AppConfig 클래스를 상속한 이 AppConfig 객체에 메타 데이터를 저장한다. 

반면 Application 객체는 없다. 그냥 AppConfig 객체에 각 앱의 실행과 관리에 필요한 여러 정보를 저장할 뿐이다. 

 

AppConfig class

name: 프로젝트 디렉토리에서 해당 앱까지의 경로이며, 모든 앱은 이 경로가 서로 달라야 한다. 

label: 각 앱을 구분하는 이름이고, 이 이름도 서로 달라야 한다. 

verbose_name: human-readable 이름이다. 이 이름은 서로 겹칠 수도 있다. 

path: 파일 시스템에서 해당 앱까지의 경로이다. 

default: 하나의 앱에 여러 AppConfig 클래스가 있을 때, 어떤 것을 기본으로 사용할지 지정하는 boolean 필드이다. 

default_auto_field: 만약 앱 내에서 모델을 생성한다면, 그 모델의 AutoField를 어떤 타입으로 할지를 지정한다. 기본값은 BigAutoField이다. 

 

앱이 로딩되는 순서

1. 맨 처음 장고가 시작된다. (즉 django.setup() 이 실행된다.)

2. settings.py 파일에 선언된 변수들을 import 한다. 

3. 이때 INSTALLED_APPS 안에 선언된 앱들을 위에서 아래로 차례대로 import 한다. 이 과정에서 각 앱의 AppConfig 인스턴스가 생성된다. 

4. 각 앱에 있는 모델이 있다면 그 모델들을 import 한다. 

5. 각 앱의 AppConfig의 메소드인 ready()를 호출한다. 이 ready()가 호출되면 비로소 앱은 실행을 위해 준비된 상태가 된다. ready() 메소드는 override해서 각 AppConfig 클래스마다 새로 정의할 수 있다. 이 메소드가 실행 중일 때는 앱이 아직 준비되지 않은 상태이기 때문에, DB에 접근하는 코드 등은 지양해야 한다. 이 경우 AppRegistryNotReady 에러가 발생할 수 있다. 

 

'server-side > Django' 카테고리의 다른 글

django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
models and databases  (0) 2023.09.16
python - poetry 사용하기  (0) 2023.07.12
signals  (0) 2023.06.21

+ Recent posts