일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- ws
- Q objects
- Bruteforce
- Git
- Stack
- algorithm
- SQL
- dictionary
- Programmers
- greedy
- stack&que
- codecov
- utils
- was
- Django
- Python
- Gunicorn
- AWS
- Query
- 백준
- TDD
- combinations
- Unit Testing
- stateless
- pytest
- ORM
- postreSQL
- HTTP 완벽 가이드
- permutations
- stateful
- Today
- Total
해피 코딩!
Django의 쿼리와 데이터베이스 레이어 본문
본 내용은 Two Scoops of Django
의 내용을 기반으로 작성하였습니다.
소감
이전에는 장고에서 SQL 을 사용하지 않아도 ORM을 사용해 SQL을 대신 할 수 있다 생각하며 SQL에 소흘하였었습니다. 이것은 면접을 다니는 과정 속에서 SQL을 사용하지 않아왔던 것을 설명하는 과정속에서 타당한 이유를 찾아보게 만들었습니다.
Django도 분명 SQL이 필요한 부분이 있으며, debug toolbar에서도 최적화 된 orm의 sql query를 확인할 수 있어 왔었습니다. 과연 그 쿼리가 최적화 되었다고 본인이 판단할 수 있을까라는 의문이 들게 되었고 이에 대해 답변할 수 있도록 SQL을 공부해야 겠다는 동기부여가 생기게 되었습니다.
ORM
장고의 ORM은 객체와 RDBMS를 자동으로 매핑해주는 것을 말한다. 객체를 통해 간접적으로 데이터베이스에 접근을 할 수 있다.
ORM을 사용하는 이유
객체 지향적인 코드로 SQL을 사용하지 않아도 DB를 사용할 수 있다.
데이터베이스 함수들은 데이터베이스별로 다르게 구현되어 있지만, 장고는 ORM을 통해 이를 하나로 통합했다. 따라서 PostgreSQL에서 쓰인 우리의 코드는 MySQL이나, SQLite에서도 잘 동작한다.
ORM을 사용하지 않는 이유
ORM으로만 서비스를 구성할 수 없다.
ORM으로 가져올 때보다 직접 로우 SQL 질의문을 통해 성능을 올린 쿼리를 사용할 수 있다.
1. 단일 객체에서 get_object_or_404
사용하기 - 장고 문서로 연결됩니다.
단일 객체를 가져와서 작업하는 세부 페이지 같은 뷰에서는 get() 대신에, get_object_or_404()
를 이용한다.
단, View
에서만 이용해야 한다.
get_object_or_404
는 try-except
를 이용한 예외처리를 하지 않도록 도와준다.
2. 쿼리를 좀 더 명확하게 하기 위해 지연 연산을 이용 - 장고 문서로 연결됩니다.
한 쿼리 안에서 여러 필터를 추가하는 행동은 자제하도록 하자.
가독성이 떨어지는 단점이 있다.
Django는 지연연산 - lazy evalution
을 이용하여 가독성이 증가한 쿼리문을 작성할 수 있다.
lazy loading은 데이터가 정말로 필요하기 전까지 장고가 SQL을 호출하지 않도록 해준다.
따라서 우리는 ORM 메서드와 함수를 얼마든지 우리가 원하는 만큼 연결해서 코드를 써내려 갈 수 있다.
우리가 결과를 실행하기 전까지 장고는 실제 데이터베이스에서 데이터를 가져오지 않는다.
3. 필수 불가결한 상황이 아니라면 로우 SQL은 지향하자.
사실 우리가 쓰는 쿼리의 대부분은 단순한 것 들이다.
ORM이라는 관계형 매핑은 매우높은 생산성을 제공하는데, 우리가 처리하는 다양한 환경에서의 단순한 쿼리 작성뿐만이 아니라, 모델에 대한 접근과 업데이트를 할 때 유효성 검사와 보안을 제공하기 때문이다.
따라서 이용하려는 쿼리를 ORM으로 표현할 수 있다면 반드시 ORM을 이용하기 바란다.
그렇다면 어떤 경우에 로우 SQL을 써야 할까? 로우 sql을 직접 이용함으로써 파이썬 코드나 ORM을 통해 생성된 코드가 월등히 간결해지고 단축되는 경우에만 로우쿼리를 이용하자 예를 들어 큰 데이터 세트에 적용되는 다수의 쿼리세트가 연동되는 경우라면 로우 SQL을 이용함으로써 더욱 효과적으로 처리하는 방법을 찾을 수 있다.
4. 장고에서 SQL을 이용하는 것에 조언
장고 코어 개발자인 말콤 트레데닉의 말을 직접 인용하면
ORM이 많은 일을 대신 해 준다는 것은 사실이다. 하지만 떄때로 SQL이 정답인 경우도 있다.
장고의 ORM에 대한 대략적인 개념을 이야기해보면 SQL을 기능적으로 이용하기 위해 만들어놓은 저장소라는 것이다. 복잡한 SQL이 필요하다면 복잡한 SQL을 이용해야 한다. 단 raw()와 extra() 메서드들을 과용하지 않는 범위에서 균형을 맞추어야 한다.
장고의 프로젝트 리더 중 한 명인 제이콥 캐플런모스의 말을 인용하자면
SQL로 쿼리를 짜는게 더 쉽다면, 이용하라 단 extra()는 자제하며 raw()더 낫다.
5. 필요에 따라 인덱스를 이용하자.
모델에 db_index=True
를 추가하면 인덱스가 추가된다.
하지만 언제 추가해야 하는지 판단이 필요하다. 처음에는 인덱스 없이 시작하며 이후 필요에 따라 하나하나 추가하는 방법이 있다.
다음과 같은 상황에 인덱스 이용을 추천한다.
- 인덱스가 빈번하게( 모든 쿼리의 10~25% 사이에) 이용될 때
- 실제 데이터 또는 실제와 비슷한 데이터가 존재해서 인덱싱 결과에 대한 분석이 가능할 때
- 인덱싱을 통해 성능이 향상되는 테스트를 할 수 있을 때
- 선택할 수 있다면 문자열보다는 정수형을 이용
6. 트랜잭션의 필요성
장고는 기본적은로 ORM이 모든 쿼리를 호출할 때 마다 자동으로 커밋을 하게 되어있다.
이 말은 데이터를 수정할 때마다, 즉 매번 .create()나 .update()가 호출될 때 마다 SQL 데이터베이스들 안의 값들이 실제로 변한다는 의미다.
이로 인한 장점은 초보 개발자들이 ORM을 이해하기 한결 수월하다는 것이며 단점은 뷰(혹은 다른 처리 과정)에서 둘 또는 그 이상의 데이터베이스 수정이 요구될 때 첫 번째 수정은 문제가 없었지만 두 번째 수정에서 문제가 발생해 데이터베이스상의 충돌이 일어날 위험이 존재하게 되었다는 것이다.
6.1 트랜잭션이란?
이러한 데이터베이스 충돌을 해결하기 위해 데이터베이스 트랜잭션을 이용하는 방법이 있다. 데이터베이스 트랜잭션이란 둘 또는 그이상의 데이터베이스 업데이트를 단일화된 작업으로 처리하는 기법을 말한다.
이 경우 하나의 수정 작업이 실패하면 트랜잭션 상의 모든 업데이트가 실패 이전 상태로 복구된다. 이를 제대로 이용하기 위해서는 데이터베이스 트랜잭션이 원자성(atomic), 일관성(consistent), 독립성(isolated), 지속성(durable)을 가져야 한다.
데이터베이스 전문가들은 데이터베이스 트랜잭션의 이런 특성을 ACID라고 줄여서 이야기한다.
장고는 1.8 릴리즈부터 현대화되고 잘 정비된, 강력하면서도 동시에 상대적으로 이용하기 쉬운 트랜잭션 매커니즘을 제공한다.
6.2 장고에서 트랜잭션을 사용하는 방법
프로젝트에서 직관적인 패턴의 데코레이터와 콘텍스트 매니저를 이용하여 데이터베이스의 일관성을 유지하기 매우 쉬워졌다.
명시적인 트랜잭션 선언은 사이트 성능을 개선하는 방법 중 하나다. 트랜잭션에서 어떤 뷰와 비즈니스 로직이 하나로 엮여있고, 어떤 것이 그렇지 않은지 명시해 주는 것이다. 개발할 때 더 ㅁ낳은 시간을 요구하는 것이 단점이다.
ATOMIC_REQUESTS와 명시적인 트랜잭션 선언에 대한 에머릭 어거스틴의 의견
/
애머릭 어거스틴의 의견에 따르면,
성능문제가 정말 심각하지 않는 한 ATOMIC_REUQEUSTS를 이용하라.
대부분의 사이트에서는 그것만으로도 충분하다.
트랜잭션에 관한 몇 가지 가이드라인을 제시하면 다음과 같다.
- 데이터베이스에 변경이 생기지 않는 데이터베이스 작업은 트랜잭션으로 처리하지 않는다.
- 데이터베이스에 변경이 생기는 데이터베이스 작업은 반드시 트랜잭션으로 처리한다.
- 데이터베이스 읽기 작업을 수반하는 데이터베이스 변경 작업 또는 데이터베이스 성능에 관련된 특별한 경우는 앞의 두 가이드라인을 고려한다.
앞의 이야기가 명확하게 와 닿지 않는다면
데이터 생성 : .create(), .bulk_create(), get_or_create()는 트랜잭션을 이용해라.
데이터 가져오기 : .get(), .filter(), .count(), .iterate(), .exsts(), .exclude(), .in_bulk() 등은 트랜잭션을 이용하지 마라.
데이터 수정하기 : .update() 는 트랜잭션을 이용해라
데이터 지우기 : .delete()는 트랜잭션을 이용하라.
단, 장고의 ORM은 데이터의 일관성을 위해 내부적으로 트랜잭션을 이용하고 있다. 예를 들어 접합 상속(concrete ingeritance)로 인해 업데이트가 여러 테이블에 걸쳐 영향을 준다고 할 때 장고는 이를 트랜잭션으로 처리한다.
따라서 독립적인 ORM 메서드를 트랜잭션으로 처리하는 것은 그다지 유용하지 않다.
ORM에서의 .raw(), .extra()
raw()
raw queryset을 반환하며 RawQuerySet을 반환합니다.
User.objects.raw('select * from members_user')
실제 적용한 코드로는
if request.method == 'POST':
user = User.objects.get(username=request.POST['username'])
username = request.POST['username']
user = User.objects.raw('select * from system_user where username=%s', [username])[0]
login_data = user.check_password(request.POST['password'])
login(request, user)
UserLog.objects.create(user=user, login=datetime.now())
이렇게 구현을 해 보았습니다.
extra()
때로는 Django 쿼리 구문 자체만으로는 복잡한 WHERE절을 쉽게 표현할 수 없습니다 . 이러한 엣지 케이스를 위해 Django는 extra() QuerySet수정자를 제공합니다.
User.objects.extra(where=["username='admin'"])
두 메서드의 차이점
아쉽게도 참고한 서적에서 이유가 설명되어있지 않고, 검색을 해 보아도 저는 찾기가 쉽지 않네요 ㅠㅠ.
다만, extra 메서드에 대한 설명으로 참고한 참고문서1
의 내용을 인용하자면 DB와의 호환성이 좋지 않으며, 장고의 철학인 DRY 원칙을 위반하는 문제가 있다고 설명이 되어 있습니다만 저는 로우쿼리를 사용해본 경험이 없어서 이해가 바로 되지 않았습니다. 부족한 설명은 죄송합니다 ㅠㅠ
raw queryset 참고 문서
'Django' 카테고리의 다른 글
Django 유틸리티 (0) | 2021.03.11 |
---|---|
Django form 정리 (0) | 2021.01.14 |
Django를 사용하게 된 이유 (0) | 2020.12.15 |
pytest를 사용하는 이유 (0) | 2020.11.23 |
Django 구성에 대한 이해 (0) | 2020.11.23 |