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

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

Models


장고의 models.Model 클래스를 활용하면 직접 SQL을 사용하지 않고도 파이썬에서 객체 타입을 선언하고 DB와 연결시킬 수 있다. models.Model을 상속한 모델 클래스를 만들면 장고가 제공하는 ORM(object-relational mapping)을 이용할 수 있다.
models.Model을 상속한 클래스 내부에는 필드를 정의할 수 있고, 이 필드들도 models 라이브러리의 세부 속성으로 선언할 수 있다.
DB와 연결되었을 때 하나의 클래스는 하나의 테이블이 되고, 클래스 내부의 필드들은 각 테이블 내부의 컬럼들이 된다.

다음은 장고 모델 클래스의 예시이다.

from django.db import models

class User(models.Model):
    id = models.AutoField()
    name = models.CharField()
    age = models.IntegerField()


User 클래스는 장고와 연결된 DB에서 user 테이블이 되고, id, name, age 속성들은 user 테이블 내부의 id, name, age라는 이름의 컬럼들이 된다. 참고로 실제 테이블 이름은 user이 아닐 수 있다.
장고에서는 테이블 이름을 지을 때 기본으로 사용되는 규칙이 있어서 별도의 이름을 지정하지 않으면 그 규칙대로 이름이 지정되고, 원하는 이름이 있으면 커스텀 속성으로 지정할 수 있다.

보통은 'app이름_model이름' 으로 테이블 이름이 지정된다. 만약 User 클래스가 account라는 app 내부에서 정의되었다면, 기본적으로 해당 테이블은 account_user이 된다. 직접 테이블 이름을 지정할 수도 있다. 각 모델 클래스의 내부에는 Meta 클래스를 선언하여 테이블 자체와 관련된 정보를 설정할 수 있다.
db_table 속성에다가 지정하고 싶은 테이블 이름을 입력하면 된다. 아래와 같은 경우 DB에 저장되는 테이블 이름도 User가 된다.

from django.db import models

class User(models.Model):
    id = models.AutoField()
    name = models.CharField()
    age = models.IntegerField()
    
    class Meta:
        db_table = "User"


장고에서 모델 클래스(models.Model을 상속한 클래스)를 정의하면 기본적으로는 DB에 테이블이 생기고, 변경되는 점들이 있으면 마이그레이션(migration)을 통해 연결되는 것이 대부분이다. 그러나 Meta 클래스에서 다른 옵션을 선택하면 이 규칙들을 바꿀 수 있다.


1) abstract = True

장고에는 추상 모델(abstract model)이 있다. 이렇게 선언하면 장고 내부에서는 해당 클래스를 모델로 인식하지만, 연동된 데이터베이스에 테이블이 생기지는 않는다.
보통은 추상 모델 클래스를 만들어 놓고, 다른 모델에서 해당 클래스를 상속하는 방식으로 사용한다. 한 모델 클래스를 상속받는 다른 모델 클래스는 그 모델 클래스에 정의된 필드를 사용할 수 있다.


2) managed = False

장고에서 모델 클래스에 변경사항이 있으면 마이그레이션 커맨드(makemigrations, migrate 등)를 통해 반영할 수 있는 이유는 테이블에서는 이 옵션이 기본으로 managed=True로 되어 있기 때문이다. 그러나 이 옵션을 False로 저장하면 해당 모델을 추가하거나 내부 필드를 변경해도 장고와 연결된 DB에 변경사항이 반영되지 않는다. 변경사항을 반영하려면 직접 해당 DB에 SQL문을 입력해야 한다.
이 옵션은 주로 해당 모델이 장고 프로젝트 내에서만 사용되는 모델이 아닐 때 등에 사용된다.


3) proxy = True

이 속성을 지정한 모델은 기존에 있던 다른 모델의 프록시 모델이 된다. 즉 다른 모델과 똑같이 작용한다고 보면 된다.

from django.db import models

class User(models.Model):
    # contents
    
class Person(User):
    # contents
    
    class Meta:
        proxy = True


모델 Person이 모델 User의 프록시 모델이라고 해 보자. User.objects.all()과 Person.objects.all()은 모두 같은 결과를 리턴한다.
프록시 모델을 선언하는 이유 중 하나는 DB와 장고 모델과의 연결은 그대로 두고, User 모델이 장고 내부에서 작동하는 방식이나 메소드를 바꾸고 싶을 때이다. 기본 모델인 User와 달리 Person에서는 정렬(ordering) 방식을 다르게 한다던가, 내부 메소드를 추가로 정의하고 싶을 때 프록시 모델을 사용한다.

 

Managers

 

쿼리 등으로 장고의 모델과 데이터베이스 사이에 통신이 일어날 때, 모델과 데이터베이스가 직접적으로 통신하지 않는다. 그 사이에는 Manager 클래스가 있다.
이 manager 클래스는 한 모델마다 최소 1개 이상이 있다. 즉 1:M의 관계다. 기본적으로는 1개이지만 원한다면 하나의 모델에 여러 개의 매니저 클래스를 둘 수 있다.
우리가 알고 있는 기본 매니저 클래스가 바로 objects이다. 항상 쿼리를 날릴 때 Model.objects.all() 이라고 썼었는데, 그 objects가 바로 매니저 클래스였던 것이다.

매니저 클래스는 쿼리셋을 만드는 방식이나 쿼리셋의 결과를 다르게 하고 싶을 때에도 사용한다.
예를 들면 Person 모델에는 employee_type이 crew, chef, administrator인 세 종류의 직원이 있다고 해 보자. 만약 employee_type이 crew인 직원들 안에서만 쿼리를 수행하고 싶다면 매번 filter() 메소드를 거는 방법도 있지만, 아예 crew_members 라는 매니저 클래스를 생성하고 필터링된 쿼리를 적용한다면 더 편하고 직관적으로 쿼리를 작성할 수도 있다.

# models.py
from django.db import models

class Person(models.Model):
    # contents
    objects = models.Manager()
    crew_objects = PersonCrewManager()
    administrator_objects = PersonAdministratorManager()
    chef_objects = PersonChefManager()
    
class PersonCrewManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(employee_type="crew")
    
class PersonAdministratorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(employee_type="administrator")
        
class PersonChefManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(employee_type="chef")
# example.py
from models import Person

Person.objects.all()
Person.crew_objects.all()
Person.administrator_objects.all()
Person.chef_objects.all()

 

 

QuerySet

 

장고 모델에서 DB에 연결하여 정보를 가져오기 위해서 사용하는 것이 쿼리셋이다. 장고 문법으로 쿼리를 작성하면 해당 쿼리셋은 DB의 SQL로 변환되어 DB에 전달되고, DB에서 나온 데이터는 다시 장고에서 쿼리셋으로 변환된다. 

 

예를 들어서 다음과 같은 쿼리를 작성하면, DB에는 이에 해당하는 SQL문이 입력된다. 

Person.objects.get(id=1)
SELECT * FROM myapp_person WHERE id = 1;

 

사실 쿼리셋은 코드가 실행되자마자 SQL에서 데이터를 가져오지는 않는다. 즉 쿼리셋은 작성되는 시점과 데이터를 가져오는 시점(evaluate 되는 시점)이 다르다. 그래서 'querysets are lazy'라는 말이 있다. 

변수에 쿼리셋을 할당할 때는 DB를 거치지 않고, 결과물을 콘솔 등으로 print()해서 보여줄 때 DB를 거쳐서 결과를 가져오게 된다. 

 

또한 쿼리는 캐싱을 사용한다. 한 번 evaluate 된 결과를 다음에 사용할 때는 또 데이터베이스를 거치지 않고(no hit), 캐싱해둔 데이터를 사용한다는 것이다. 그런데 그 결과값을 변수에 할당해 두지 않으면 같은 쿼리 결과를 사용할 때도 데이터베이스를 계속 hit 하게 된다. 

 

또한 한 모델 테이블과 연결된 여러 모델의 데이터도 select_related()prefetch_related()를 통해서 한 번에 불러올 수 있다. 이는 특히 for loop 등으로 반복적인 작업을 계속할 때 효율적이다. 

해당 모델이 참조하는 모델 데이터를 불러올 때는 select_related(), 해당 모델을 참조하는 모델 데이터를 불러올 때는 prefetch_related()를 불러온다. many-to-many 관계의 모델 데이터도 prefetch_related()로 불러올 수 있다. 

 

캐싱이 사용되는 경우 -> 효율적

people = Person.objects.select_related("company")		# Person, Company 데이터를 모두 가져옴
for person in people:
    person.company.is_valid = True		# 여기서는 database hit 발생 X

캐싱이 사용되지 않는 경우 -> 비효율적

people = Person.objects.all()		# 여기서 Person 모델 데이터만 가져옴
for person in people:
    person.company.is_valid = True		# 한 번 실행될 때마다 database hit

 

 

복잡한 쿼리셋

 

쿼리셋에서 filter(), exclude() 등으로 여러 개의 조건을 걸 수 있는데, 간혹 복잡하거나 구체적인 조건도 설정할 수 있다. 

 

1) field lookups

말 그대로 필드의 값과 동일한 값만 찾는 게 아니라, 숫자일 경우 gt, gte, lt, lte 등으로 작거나 큰 값을 조회할 수 있다. 문자열인 경우는 contains, iexact 등으로 특정 문자열을 포함하거나 대소문자를 구분하지 않은 결과들도 조회할 수 있다. 이런 field lookup등의 경우 필드 이름 뒤에 '__'을 붙인다. 

더 복잡한 field lookup도 있다. '__'을 붙이면 해당 모델과 관계 있는 다른 모델에서의 검색도 가능하다. 이 FK 검색은 연쇄적으로 계속 일어날 수 있는데, 실제로 이렇게 쿼리를 작성할 경우 SQL문에서는 JOIN 문을 사용해서 결과를 가져온다고 한다. 

# Person의 employee_type 필드값에 'c'를 포함한 모든 레코드 조회
Person.objects.filter(employee_type__contains="c")

# Person의 foreignkey인 Company 모델의 name 필드값에 'a'를 포함한 모든 레코드 조회
Person.objects.filter(company__name__contains="a")

 

2) Q()

Q() 안에는 filter(), exclude()에 사용하는 것처럼 조건문이 온다. 다만 Q()를 여러 개 사용해서 AND, OR, XOR 등 다양한 조건이 교차하는 필터링을 할 수 있다. 기존의 chaining 방법으로는 모든 조건들을 전부 만족하는 쿼리만 불러올 수 있지만 Q()를 사용하면 조건들을 다양하게 선언할 수 있다. 

# Person 중 name에 'peter'이 포함되고 27세 이상인 레코드 조회
Person.objects.filter(Q(name__contains="peter") | Q(age__gte=27))

 

3) F()

F()를 사용하면 기존의 다른 쿼리와는 달리 여러 필드들의 값을 비교할 수 있다. 기존에는 한 필드의 값에만 조건을 걸 수 있었다. 하지만 F()를 사용하면, 예를 들면 두 필드값을 비교하는 등의 필터링도 가능하다. 또한 필드값을 가져와서 연산 후에 조건을 거는 것도 가능하다. 

# House 레코드 중 number_of_people 필드와 number_of_cars 필드값이 같은 레코드만 조회
House.objects.filter(F("number_of_people") == F("number_of_cars"))

# House 레코드 중 price가 annual_people_income 필드값의 2배 이상인 레코드만 조회
House.objects.filter(price__gt=2*F("annual_people_income"))

 

4) Aggregation

또한 aggregate()를 사용하면 레코드의 총 개수, 특정 필드의 총합 등 특정 값을 추가해서 계산할 수 있다. Count, Avg, Max, Sum 등 다양한 기본 함수들을 사용할 수 있다. 

# 전체 Person 레코드의 개수 조회
Person.objects.aggregate(Count("id"))

 

5) Annotate

annotate()를 사용하면 쿼리셋에 포함되는 각 인스턴스 별로 aggregate()와 같은 쿼리를 생성할 수 있다. 

# 피자에 들어가는 토핑의 개수를 같이 조회
pizzas = Pizza.objects.annotate(number_of_toppings=Count("toppings")

# 첫 번째 피자에 들어간 토핑의 개수
pizzas[0].number_of_toppings

 

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

django routers  (1) 2023.12.21
django apps  (0) 2023.12.20
python - poetry 사용하기  (0) 2023.07.12
signals  (0) 2023.06.21
Model.select_related() vs Model.prefetch_related()  (0) 2022.07.11

이 포스트는 인프런의 스프링부트 시큐리티 강의를 참고하여 작성되었습니다. 

 

 

프로젝트 구조

구글 로그인은 소셜로그인의 일종이기 때문에 스프링 시큐리티를 사용할 때 OAuth와 함께 사용해야 한다. 홈페이지에서는 일반 로그인과 구글로그인을 같이 구현하는 것이 목표이므로, 일반 로그인의 코드를 따로 건드리지 않고 구글 로그인 코드를 작성해 주어야 하겠다. 

 

일반 로그인 관련 코드는 auth라는 패키지에, 소셜 로그인 관련 코드는 oauth 패키지에 따로 묶어두자. 

 

스프링 시큐리티를 사용할 때 가장 기본이 되는 클래스는 SecurityConfig 클래스다. 이 클래스 내부에는 SecurityFilterChain 인스턴스를 리턴하는 filterChain() 메소드가 재정의된다. filterChain() 메소드는 스프링 시큐리티가 개입할 때 가장 먼저 호출되는 메소드이다.

 

이 클래스에 @EnableWebSecurity 어노테이션을 붙여서 내부에 정의된 filterChain() 메소드를 스프링 필터체인(filterChain)에 등록한다. 

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {

    @Autowired
    public PrincipalOauth2UserService principalOauth2UserService;

    @Bean
    public BCryptPasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.csrf(csrf -> csrf.disable());
        http
                .cors(cors -> cors.disable())
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers("/login", "/loginForm", "/loginProc", "/join", "/joinProc", "/user").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin((formLogin) ->
                        formLogin
                                .loginPage("/loginForm")
                                .loginProcessingUrl("/loginProc")
                                .defaultSuccessUrl("/"))
                .oauth2Login((oauth2Login) ->
                        oauth2Login
                                .loginPage("/loginForm")
                                .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig.userService(principalOauth2UserService))
                )
                .httpBasic(withDefaults());
        return http.build();
    }

}

 

HttpSecurity.cors(), HttpSecurity.csrf()의 내부에서는 CORS, CSRF 처리를 어떻게 할지를 정의할 수 있다. 

 

HttpSecurity.authorizeHttpRequests() 내부에서는 프로젝트의 각 엔드포인트에 대해 접근권한을 허용할지 말지를 결정할 수 있다. requestMatchers() 안에 url 패턴을 선언해서 해당 패턴들을 가진 엔드포인트들을 한 번에 처리할 수도 있다. 

 

뒤에 authenticated()를 붙이면 해당 엔드포인트들에 대해서는 인증을 실행하고, permitAll()을 붙이면 해당 엔드포인트에 대한 요청들은 별도의 인증을 안 하고 통과시킨다. 

 

HttpSecurity.formLogin() 내부에서는 시큐리티 기본 로그인을 어떻게 할지를 설정한다. 기본 로그인이란 아이디, 비밀번호를 입력해서 사용자를 인증하는 로그인이다. 메소드 내부에서는 .loginPage()로 어떤 엔드포인트를 기본 로그인 페이지로 설정할지를 정할 수 있다. 이 경우 인증되지 않은 사용자가 인증이 필요한 페이지를 요청할 경우 기본 로그인 페이지로 리다이렉트 된다. 

 

.loginProcessingUrl()은 로그인 과정을 처리하는 엔드포인트다. 보통 로그인 폼에서 '로그인'을 눌렀을 때 로그인 과정을 처리하는 엔드포인트로 이동하고, 해당 엔드포인트에서 로그인 인증이 진행된다. 

 

.defaultSuccessUrl()은 로그인이 성공했을 때 리다이렉트 될 엔드포인트이다. 

 

HttpSecurity.oauth2Login() 내부에서는 OAuth 로그인을 어떻게 설정할지를 작성한다. .loginPage()에서 설정한 엔드포인트로부터 들어오는 요청들에 대해서 oauth provider(구글, 네이버 등등)가 인증을 진행한다. 로그인 페이지 말고도 여러 엔드포인트를 설정할 수 있다. 

 

그 전에, OAuth에서는 3가지의 주체가 있다: Client, Resource Owner, Resource Server

client는 소셜로그인을 이용하려는 웹사이트의 서버, resource owner는 사용자, resource server는 구글 등 소셜로그인 서비스를 제공하는 서버이다. 

 

 

스프링 시큐리티 OAuth 2.0에서 지정한 엔드포인트(protocol endpoint)

1. Authorization Endpoint

client가 resource owner로부터 권한(authorization)을 획득하는 부분이다. 

즉 사용자(resource owner)의 로그인이 진행되는 부분이다. 사용자가 로그인을 진행하면 그 과정에서 앱 서비스(client)는 권한 코드(authorization code)를 얻을 수 있다. 사용자의 로그인이 성공적이었다면 1번에서 권한 코드를 얻을 수 있다. 

 

2. Token Endpoint

1번에서 얻은 권한 코드를 액세스 토큰으로 교환하는 지점이다. 

 

3. Redirection Endpoint

인증 서버(resource server)가 앱 서비스(클라이언트)에게 권한 정보(authorization)를 리턴하는 지점이다. 

 

4. UserInfo Endpoint

클라이언트가 인증된 사용자의 정보(claims)를 받기 위해서는 이 엔드포인트로 요청하면 된다. 

 

코드에서는 .userInfoEndpoint()로 사용자의 인증 정보를 받아서 어떻게 처리할지를 설정했다. 

.userInfoEndpoint().userService() 에서는 사용자의 인증 정보를 엔드포인트로 받아서 어떤 UserDetailsService 클래스에 넘겨줄지를 설정한다. 여기서는 앞에서 미리 작성한 principalOauth2UserService 클래스에게 유저 정보를 넘겨주기로 설정했다. 

 

 

OAuth 서비스 클래스 작성 - 기존 로그인 로직과 연동

principalOauth2UserService 클래스는 DefaultOAuth2UserService 클래스를 상속하고, DefaultOAuth2UserService 클래스는 OAuth2Service 인터페이스를 구현한다. 

OAuth2Service 인터페이스는 UserInfo Endpoint에서 유저 정보를 얻은 뒤 그에 맞는 OAuth2User 인터페이스 타입의 인스턴스를 리턴한다. 즉 우리 클래스는 이 인터페이스를 구현받은 클래스를 상속받고 있으므로 OAuth2Service 인터페이스의 역할 + a를 한다고 볼 수 있다. 

 

해당 클래스는 앞서 구현 및 상속받은 인터페이스, 클래스가 있기 때문에 loadUser() 라는 메소드를 재정의해야 한다. loadUser() 메소드는 UserInfo Endpoint에서 유저 정보를 얻은 뒤 그에 맞는 OAuth2User 인터페이스 타입의 인스턴스를 리턴한다. 인터페이스 타입을 리턴할 수는 없으므로, OAuth2User 인터페이스를 구현한 클래스를 하나 만든 뒤 그 클래스 타입의 인스턴스를 리턴시켜야 하겠다. 

 

(예시 코드)

@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

        OAuth2User oauth2User = super.loadUser(userRequest);
        // code
        return new PrincipalDetails(userEntity, oauth2User.getAttributes());
    }
}

 

여기서 PrincipalDetails라는 클래스는 강의에서 정의한 클래스로, 기존 로그인에서 필요했던 UserDetails 인터페이스와, loadUser()의 리턴 타입인 OAuth2User 인터페이스를 구현한 클래스이다. 

 

앞서 기본 로그인을 진행하기 위해서는 PrincipalOauth2UserService와 같은 역할을 하는 서비스 클래스로 PrincipalDetailsService 클래스를 정의했었고, 해당 클래스는 UserDetailsService 라는 인터페이스를 구현하고 있었다. UserDetailsService 인터페이스에서는 loadUserByUsername() 메소드를 오버라이딩 해야 했는데, 이 메소드의 리턴 타입이 UserDetails 이었다.

 

즉 PrincipalDetailsService와 PrincipalOauth2UserService의 관계는 매우 비슷하다. 각각 UserDetailsService와 OAuth2Service 인터페이스를 구현하고, 그 인터페이스에 선언된 메소드에서는 각각 UserDetails와 OAuth2User 인터페이스 타입을 리턴한다. 

 

그러므로 PrincipalDetails 클래스에서 두 인터페이스(UserDetails, OAuth2User)를 모두 구현해버리면 하나의 클래스가 일반 로그인 서비스인 PrincipalDetailsService와 소셜로그인 서비스인 PrincipalOAuth2UserService 모두에서 쓰일 수 있게 된다. 

 

대신 일반 로그인과 소셜로그인의 처리 절차가 다를 수 있으므로, UserDetailsService의 loadUserByUsername() 메소드와 OAuth2UserService의 loadUser() 메소드를 각각 다르게 오버라이딩 해 주면 같은 엔드포인트와 같은 클래스 타입을 사용해서 두 가지의 로그인을 한번에 진행할 수 있다. 

 

구조 설명

 

해당 그림은 스프링 시큐리티 구조를 간단히 나타낸 것이다. 

 

앞서 언급한 PrincipalOauth2UserService와 PrincipalDetailsService는 그림에서 'UserDetailsService' 부분에 해당한다. 이 클래스들은 AuthenticationProvider에서 authentication 객체를 건네받은 뒤, 해당 객체를 통해 UserDetails 인터페이스에 접근해서 유저 정보를 가져온 뒤 다시 AuthenticationProvider에게 그 정보를 리턴하는 역할을 한다. 

 

또한 앞서 나왔던 PrincipalDetails는 UserDetails 부분에 해당한다. 보통 모델에서 사용하는 유저 클래스가 이 인터페이스를 구현한다. 이 인터페이스에는 스프링 시큐리티에서 해당 유저 모델을 사용하기 위해서 필요한 여러 필드와 메소드(개별 유저의 권한 리턴하기, 유저가 유효한지 확인하는 메소드 등)가 선언되어 있다. 

 

Software Release Life Cycle(SRLC)는 소프트웨어를 개발, 테스트 및 배포하는 과정이다. SRLC에는 Pre-alpha, Alpha, Beta, Release Candidate, RTM, GA, Gold 등 여러 단계가 있지만 크게는 소프트웨어를 개발 및 테스트하는 단계와 배포하는 단계로 나눌 수 있다.

 

Pre-Alpha

 

SRLC의 첫 단계로, 직접적인 개발을 하기 전 필요한 사항들을 분석한다. 예를 들면 소비자의 요구사항 파악, 필요한 개발 기능 파악이 있다.

 

 

Alpha

 

alpha 배포 단계에서는 앞 단계에서 정의한 문제를 해결하는 잠재적 솔루션(소프트웨어)을 만든다. 프로토타입을 제작하는 것과 같다. 오픈소스가 아닌 이상 대중에게 프로덕트를 공개하지는 않으며, 프로덕트도 서비스의 모든 기능이 추가된 상태는 아니다.

 

서비스의 일부 기능을 구현하였으나 버그가 있을 수 있으며 데이터 손실 등의 버그도 있을 수 있다. 즉 실제 배포에서 일어나서는 안 되는 종류의 버그들이 있을 수 있다. 그래서 내부적으로 테스트 과정을 거친다.

 

 

Beta

 

pre-alpha에서 기획했던 기능은 다 완성된 상태이나 여전히 버그 가능성은 있다. alpha에서 언급한 데이터 손실, 충돌 등의 문제가 여전히 발생할 수 있다. Beta 단계에서의 목표는 사용성 테스트(usability test)를 통해 실제 유저가 서비스를 사용할 수 있도록 하는 것이다. Beta 단계부터는 테스터를 모집하고, 정식 출시를 하는 소프트웨어도 있다. 인터넷 같은 일부 서비스는 beta 단계에서 계속 머무르기도 한다.

 

 

Release Candidate

 

stable release 버전이 될 가능성이 있는 beta 버전이다. 모든 기능들이 개발된 버전이다. beta 버전 사이클을 1번 이상 거치고, 중대한 버그(데이터 손실, 충돌 등)가 발생하지 않은 상태이지만 자잘한 버그의 수정은 있을 수 있다. 보통 이 단계까지를 개발 단계로 보고, 그 다음부터는 배포 단계로 본다.

 

 

GA(General Availability)

 

프로덕트가 상업적으로 이용 가능한 상태가 된다. 이 단계에서도 프로덕트가 수정될 수는 있는데, 버그 수정 등의 오류가 아니라 더 상품성을 높이기 위해서 수정된다. 예를 들면 보안 기능이나 여러 지역에 서비스를 배포하는 것 등이 해당한다.

 

 

Production Release(Stable Release)

 

정식 배포가 된 상태이다.

 

 

참고한 포스트

https://ausdto.github.io/service-handbook/alpha/1-introduction/1-1-what.html

https://en.wikipedia.org/wiki/Software_release_life_cycle

https://theproductmanager.com/topics/software-release-life-cycle/

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

웹 어플리케이션 서버(WAS)  (0) 2024.08.05
Mac 환경설정  (0) 2024.07.15
OAuth 2.0 기본원리  (0) 2022.09.26
인증(Authentication)  (0) 2022.07.14
linux: cron 사용해서 자동으로 스케줄 실행하기  (0) 2022.07.09

poetry는 파이썬에서 패키지를 관리하는 도구이다. 이전에는 requirements.txt, setup.py 등 여러 파일에 의존성 관련 정보를 두고 사용했는데 poetry를 사용하면 pyproject.toml 파일 하나로 의존성을 관리할 수 있다. 또한 기존 requirements.txt를 이용한 방식은 pip라는 패키지 관리 프로그램을 같이 사용해야 했지만, poetry는 다른 패키지 관리 프로그램이나 파일을 더 사용하지 않고 poetry 가상환경을 따로 만들어서 관리할 수 있다.

 

poetry 환경 설정하기

우선은 poetry를 설치하는 방법부터 알아보자.

curl -sSL <https://install.python-poetry.org> | python3 -

 

기존에는 다른 url이었는데 바뀐 듯 하다.

이 명령어를 실행하기 전에 poetry 공식문서를 보고 명령어가 바뀐 게 있는지를 참고하면 좋을 것 같다.

poetry --version

 

터미널에 검색했을 때 버전이 정상적으로 나온다면 잘 설치된 것이다.

 

poetry를 실행하려면 pyproject.toml 파일을 작성해야 한다. 해당 문서를 참고하면 좋을 것 같다.

pyproject.toml 파일에 의존성 패키지 리스트를 작성할 때는 []를 사용해서 여러 상황마다 사용할 패키지를 다르게 설정할 수 있다. 기본값은 [tool.poetry]이고 이 뒤에 조건을 추가한다.

 

예를 들어 테스트 환경에서만 필요한 패키지들이 있다면 다음과 같이 적어주면 된다.

[tool.poetry.group.test]  # This part can be left out

[tool.poetry.group.test.dependencies]
pytest = "^6.0.0"
pytest-mock = "*"

 

상황이나 환경별로 의존성 패키지들을 따로 적을 필요가 없다면, 보통은 [tool.poetry.dependencies]로 선언하고 그 아래 필요한 패키지들을 적는다.

[tool.poetry.dependencies]  # main dependency group
httpx = "*"
pendulum = "*"
[tool.poetry.group.test.dependencies]
pytest = "^6.0.0"
pytest-mock = "*"

 

만약 실제 서버 환경과 배포 환경이 나눠져 있다면 [tool.poetry.dev.dependencies]로 선언하면 된다.

 

poetry 가상환경 만들기

앞서 poetry에서 가상환경을 만들 수 있다고 했었다. poetry 가상환경을 만들고 나면 pyproject.toml 파일의 의존성과 poetry 가상환경의 패키지들을 명령어로 동기화시킬 수 있다.

poetry shell

 

그러면 poetry 가상환경이 터미널에서 실행되고, 가상환경 관련 캐시는 Library/Caches/pypoetry/virtualenvs/ … 이 디렉토리에 저장된다.

deactivate

 

가상환경을 끄고 싶다면 이렇게 입력하자. 다시 켜고 싶으면 poetry shell을 다시 입력하면 된다.

 

만약 중간에 실행을 하다가 파이썬 인터프리터가 poetry 가상환경을 인식 못 해서 패키지 의존성 관련 에러가 난다면, 파이썬 인터프리터에 poetry 가상환경 디렉토리가 제대로 설정되지 않았을 수 있다. 즉 현재 실행중인 환경의 디렉토리가 poetry 가상환경 정보가 들어있는 디렉토리랑 다를 수 있다.

 

맥의 경우, File>Manage IDE Settings>Settings Sync.. 로 설정으로 들어간 뒤 왼쪽 탭에서 Project를 누르고, python interpreter를 누른다. 여기서 Add Interpreter를 누르고 왼쪽 탭에서 poetry environment를 누른다. 여기서 Existing environment를 누른 뒤 경로를 다시 설정해야 한다.

 

경로는 poetry shell을 입력했을 때 “Spawning shell within …. ”하면서 경로가 나오는데, 그 경로에서 뒤에다 /bin/python 을 붙여준 경로를 입력하면 실행이 잘 될 것이다.

 

poetry 기타 커맨드

poetry 삭제하기

 

poetry를 삭제할 때는 poetry를 어떻게 설치했는지에 따라 삭제 방법이 다르다. 공식 사이트의 명령어를 통해 설치하지 않은 경우 버전 업데이트 등 다른 쪽에서 에러가 날 수 있는데, 이 경우는 기존에 poetry를 설치했던 방법으로 삭제해줘야 정상적으로 삭제된다.

 

-pip을 통해 설치한 경우

pip uninstall poetry

 

-brew를 통해 설치한 경우

brew uninstall poetry

 

그리고 poetry가 제거되었는지 확인하려면 반드시 poetry —version을 입력해서 poetry가 없다는 말이 나와야 한다. 그렇지 않으면 poetry 파일이나 메타 데이터가 컴퓨터 어딘가에 남아 있는 것이다.

 

만약 poetry —version 커맨드에서 버전이 나온다면, 맥의 경우는 최상위 폴더로 이동한 뒤 /.local/bin/poetry 파일이 있는지 확인해보자. 있다면 지우고 다시 설치하자.

 

공식 사이트의 poetry 설치 커맨드를 통해 설치한 경우에는 위와 관련된 문제는 아마 없지 않을까 싶다. 이 경우에는 이렇게 제거할 수 있다.

curl -sSL <https://install.python-poetry.org> | python3 - --uninstall

 

poetry python 버전 바꾸기

poetry env use {version}

 

poetry 버전 바꾸기

poetry self update {version}

 

poetry 환경 리스트 보기

poetry env list

 

참고한 포스트

https://python-poetry.org/docs/

https://python-poetry.org/docs/managing-dependencies/

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

django apps  (0) 2023.12.20
models and databases  (0) 2023.09.16
signals  (0) 2023.06.21
Model.select_related() vs Model.prefetch_related()  (0) 2022.07.11
Model.objects.filter() vs Model.objects.get()  (0) 2022.07.11

+ Recent posts