ORM?
- Object Relational Mapping
- 객체와 관계형 DB를 자동으로 매핑해 주는것.
- SQL 쿼리문 없어 DB의 데이터를 다룰 수 있게 해줌.
1
SLECT * FROM User u WHERE u.first_name='kim'
1
User.objects.filter(first_name='kim')
특징
Cahcing(캐싱)
- 한번 가져온 데이터는 변화가 없다면 계속 사용
1 2 3 4
users = User.objects.filter(first_name='kim') first_user = users[0].full_name # 데이터 가져옴 user_list = list(users) # 데이터 가져옴2 # 총 2번 가져옴
1 2 3 4
users = User.objects.filter(first_name='kim') user_list = list(users) # 데이터 가져옴 first_user = user_list[0].full_name # 캐싱된 데이터 이용 # 총 1번 가져옴
- 위 예제처럼 캐싱을 활용하면 효율적인 코드 작성이 가능
Lazy-loading(지연로딩)
- ORM을 사용할 때 데이터가 바로 로딩되지 않음.
1 2 3
users = User.objects.filter(first_name='kim') # 이때는 데이터를 가져오지 않음 for user in users: print(user.full_name) # 이때 데이터를 가져옴
- 실제로 데이터가 필요한 시점에 데이터를 가져옴.
- Django ORM은 기본적으로 Lazy-loading으로 동작.
- 코드의 순서에 따라 비효율적일 수도 있음. (캐싱 참고)
Eager-loading(즉시로딩)
- Lazy-loading에 반대되는 개념
- ORM을 실행한 즉시 데이터를 가져옴
- Lazy-loading의 대표적인 문제인 N+1 Problem을 해결하기 위해 사용
N+1 Problem
- 외래키로 연결된 데이터를 가져오는 과정에서 생기는 문제
1 2 3 4 5 6 7 8 9 10 11 12
# User와 UserProfile은 1:1 관계 class User(AbstractBaseUser): # Data fields username = models.CharField(max_length=200, null=False, blank=True, unique=True) first_name = models.CharField(max_length=200, null=False, blank=True) last_name = models.CharField(max_length=200, null=False, blank=True) class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile') phone_number = models.CharField(max_length=50, default='') zip_code = models.CharField(max_length=20, default='') home_address = models.CharField(max_length=200,default='')
1 2 3 4 5 6
kims_users = User.objects.filter(first_name='kim') for user in kims_users: # kim_user를 for문에 사용하기 위해 데이터 가져옴 user_address = user.user_profile.home_address # user와 연결된 userprofile 데이터를 가져옴 print(f'user address :: {user_address}') # 총 1 + N(kims_users 개수)
해결책
selecte_related
1
2
3
4
5
6
# 첫 줄에서 User, UserProfile까지 join하여 모든 데이터를 가져옴.
kims_users = User.objects.select_related('user_profile').filter(first_name='kim')
for user in kims_users:
user_address = user.user_profile.home_address
print(f'user address :: {user_address}')
- select_related를 사용하면 모든 데이터를 한번에 가져옴
-> 데이터를 한번만 가져오는 효과 - 1:1 관계, 정참조 관계에만 사용 가능
prefetch_related
1
2
3
4
5
6
# 첫 줄에서 User, UserProfile까지 모든 데이터를 가져온 뒤, 장고에서 내부적으로 데이터를 합침.
kims_users = User.objects.prefetch_related('user_profile').filter(first_name='kim')
for user in kims_users:
user_address = user.user_profile.home_address
print(f'user address :: {user_address}')
- prefetch_related역시 모든 데이터를 한번에 가져옴
- select_related와 달리 join으로 데이터를 합치는게 아닌, 두 테이블의 데이터를 따로 가져와서 Django가 합침
- select_related보다 추가적인 쿼리 발생
-> select_related를 사용하지 못하는 상황에서 사용
장점
- 생산성 증가
- 가독성 증가
- 유지보수 용이
단점
- ORM을 따로 배워야함
- 복잡한 참조일경우 SQL문이 더 좋을수도 있음
- 정확한 이해없이 사용하면 성능저하등의 문제 발생