ORM(Object Relational Mapping**)**은 관계 지향형인 데이터베이스와 객체를 연결해 주는 것으로 ORM을 사용하게 되면 개발자는 데이터베이스를 객체 지향 코드로 다룰 수 있습니다. 이는 SQL 쿼리문을 직접 작성할 필요가 없기 때문에 코드가 직관적이며 개발자의 생산성을 향상할 수 있습니다. 또 코드의 일관성을 유지하고 복잡성을 줄이는 데 도움이 됩니다. 또한 SQL Injection 공격을 방어하는 데도 도움이 됩니다. 그리고 여러 데이터베이스를 지원하기 때문에 데이터베이스를 이전하기도 비교적 쉽습니다. SQL Injection은 공격자가 악의적으로 입력값을 조작해 SQL 코드를 주입하고 데이터베이스에서 원하는 SQL 코드를 실행시키는 공격으로 데이터베이스의 값을 삭제하거나 데이터 유출이 발생할 수 있습니다. Django 에서는 사용자의 입력값을 SQL 쿼리에 직접 삽입하지 않고 매개변수(parameter) 화해서 SQL Injection 공격을 방어합니다.
ORM은 자동으로 생성된 SQL을 사용하기 때문에 최적화되지 않은 SQL이 사용될 경우 성능 저하가 발생할 수 있습니다. 또 특정한 데이터베이스 쿼리를 ORM에서 지원하지 않을 경우 개발자가 직접 SQL을 작성해야 할 수 있습니다. 그렇기 때문에 ORM에만 의존하는 것은 좋지 않으며 SQL에 대한 이해도 필요합니다.
Django ORM 외에도 Python에는 SQLAlchemy, Peewee, Tortoise ORM 등의 ORM이 있으며 Java의 Hibernate 그리고 JavaScript의 Prisma, TypeORM 등이 있습니다.
이번 장에서는 설명의 편의를 위해 아래의 Model과 데이터를 기반으로 예제 코드를 작성하였습니다. 데이터베이스는 PostgreSQL 15.4를 사용하였습니다.
from django.db import models
class Zoo(models.Model):
zoo_name = models.CharField()
def __str__(self):
return f'{self.zoo_name}'
class Animal(models.Model):
name = models.CharField()
info = models.TextField()
zoo = models.ForeignKey(Zoo, on_delete=models.CASCADE)
medical_check = models.BooleanField()
birth_at = models.DateTimeField()
def __str__(self):
return f'{self.name}'
쿼리셋은 데이터베이스 쿼리 발생을 최소화하기 위해 데이터가 필요한 시점까지 쿼리를 최대한 지연시키고 데이터가 필요한 순간에 쿼리셋을 평가해 쿼리를 발생시킵니다. 또 쿼리셋이 평가되었다면 캐싱을 사용해 데이터를 재사용합니다. 하지만 캐싱 되는 데이터가 많다면 메모리 성능 저하가 발생할 수 있고 쿼리를 지연시키는 특징 때문에 N+1 문제가 발생할 수 있습니다. 이를 해결하기 위해 Django 에서는 쿼리를 지연시키지 않고 즉시 쿼리 하는 **select_related()**와 prefetch_related() 메소드를 제공하고 있습니다. 앞으로 배울 쿼리셋 평가 시기와 다양한 메소드, 쿼리셋 결합 방법 등을 잘 이해하고 효과적으로 쿼리셋을 최적화할 수 있는 능력을 갖출 수 있습니다.
다음은 쿼리셋이 바로 평가되는 경우들입니다. SQL 쿼리가 발생하기 때문에 유의해야 합니다.
lteration (Asynchronous iteration)
>>> for i in Animal.objects.all():
... print(i.name)
Tiger
Cat
Cheetah
Penguin
Dog
반복문을 사용할 경우 쿼리셋이 평가됩니다. 비동기 반복문의 경우에도 동일하게 쿼리셋이 평가됩니다. 만약 결값의 존재 여부를 확인하기 위해 반복문을 사용할 경우 **exist()**를 사용하는 것이 더 효율적입니다.
Slicing
>>> Animal.objects.all()[:4:2]
[<Animal: Tiger>, <Animal: Cheetah>]
슬라이싱을 사용할 경우 음수 인덱싱은 지원하지 않으며 위와 같이 step 파라미터를 사용하는 경우 쿼리셋이 바로 평가됩니다. 하지만 step 파라미터 없이 평가되지 않은 쿼리셋을 슬라이싱 한다면 새로운 쿼리셋이 반환됩니다. 이때 슬라이싱 된 쿼리셋을 정렬하거나 추가로 필터링하는 것은 금지됩니다.