캐싱이란?
캐싱은 서버가 동일한 요청에 대해 매번 데이터를 생성하는 대신, 한번 생성된 데이터를 메모리/디스트에 저장해 두고 이후 요청에 재사용하는 방식으로 성능을 높일 수 있다.
응답 속도 향상, 백 단의 서버 부하 감소등의 장점이 있지만, 데이터 일관성의 문제, 민감한 데이터를 캐시에 저장할 경우 보안상 문제가 생길 수 있다.
데이터 베이스는 디스크에 데이터를 저장/조회, 캐시는 메모리에 데이터를 저장/조회한다.
메모리가 디스크보다 입출력 속도가 빠르지만 용량이 작고, 가격이 비싸다.
Django의 기본 캐싱 프레임워크
Django의 캐싱 프레임워크는 'memcached', 'redis', 'database caching', 'file-based caching'등 다양한 캐시 백엔드를 지원하지만 그중, memcached와 redis에 대해서 다뤄 보려고 한다.
memcached는 pymemcache, pylibmc 라이브러리에 의존하고,
Redis는 redis, django-redis 라이브러리에 의존한다.
만약 Redis를 사용한다면 django-redis를 사용하면 캐시 API 외에 레디스의 기본 기능을 활용하는 커스텀 명령어를 지원하기 때문에 유용하다.
멀티 캐싱 지원
멀티 캐싱은 여러 계층의 캐시 시스템을 사용하는 것을 의미한다. 예를 들어 메모리 기반 캐시와 파일 기반 캐시를 조합해서 사용하는 방식을 모두 사용하여, 성능과 신뢰성을 동시에 고려할 수 있게 되는 것이다.
django에서는 이러한 멀티 캐싱을 지원해 준다.
Django에서 멀티 캐싱 설정 예시
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
},
'file_cache': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
},
}
디폴트로 Memcached를 사용하고, 추가적인 파일 기반 캐시를 설정한 것이다. 각 캐시를 필요에 따라 아래와 같이 선택하여 사용할 수 있다.
from django.core.cache import caches
# 'default' 캐시 백엔드 사용 (Memcached)
default_cache = caches['default']
default_cache.set('my_key', 'my_value', timeout=60*15)
my_value = default_cache.get('my_key')
# 'file_cache' 백엔드 사용 (File-based cache)
file_cache = caches['file_cache']
file_cache.set('my_file_key', 'my_file_value', timeout=60*15)
my_file_value = file_cache.get('my_file_key')
low level 캐싱 명령어
Low-level 캐싱은 캐시 저장소와 직접적으로 상호작용하는 캐싱 방식으로, 세부적인 캐시 제어가 가능하다.
Django의 'cache' 모듈을 이용하여 low-level 캐싱 명령어를 사용할 수 있다.
아래 low-level 예시는 공식 문서를 참고한 내용이다.
Django의 캐시 프레임워크 | Django 문서
The web framework for perfectionists with deadlines.
docs.djangoproject.com
cache.set()과 cache.get()
캐싱 시스템에서 가장 기본적인 인터페이스로 cache.set()과 cache.get()이 있다.
from django.core.cache import cache
# 캐시 설정
cache.set("my_key", "hello, world!", 30)
# 캐시에서 데이터 가져오기
print(cache.get("my_key")) # 'hello, world!'
key는 문자열이 여야 하며, value는 직렬화 가능한 모든 파이썬 객체일 수 있다.
timeout은 캐시의 데이터 유효 기간으로, 기본값은 설정된 캐시 백엔드의 설정에 따르며, 초단위로 지정되며 None를 설정하면 영구적으로 0을 입력하면 캐시를 사용하지 않음을 나타낸다.
캐시가 만료될 경우, cache.get()은 기본적으로 None를 반환한다.
# 30초 후
print(cache.get("my_key")) # None
cache.add()
키가 없을 때 만 값을 추가한다. 이미 존재하는 키라면 아무런 작업도 하지 않는다.
cache.set("add_key", "Initial value")
cache.add("add_key", "New value")
print(cache.get("add_key")) # 'Initial value'
cache.get_or_set()
키가 존재하면 해당 값을 반환하고, 존재하지 않으면 새로운 값을 설정한 후 반환한다.
print(cache.get_or_set("my_new_key", "my new value", 100)) # 'my new value'
함수나 다른 호출 가능한 객체를 'default' 값으로 전달할 수도 있다.
import datetime
print(cache.get_or_set("some-timestamp-key", datetime.datetime.now)) # 현재 시간 반환
cache.get_many()와 cache.set_many()
여러 개의 키를 동시에 처리할 때 효율적이다.
# 여러 개의 키를 한 번에 설정
cache.set_many({"a": 1, "b": 2, "c": 3})
# 여러 개의 키를 한 번에 가져오기
print(cache.get_many(["a", "b", "c"])) # {'a': 1, 'b': 2, 'c': 3}
cache.delete()와 cache.delect_many()
특정 키나 여러 키를 캐시에서 삭재할 때 사용한다.
cache.delete("a") # True 반환
cache.delete_many(["a", "b", "c"])
cache.clear()
모든 캐시 데이터를 삭제하려면 cache.clear()을 사용할 수 있다. 모든 캐시를 삭제하므로 사용에 주의해야 한다.
cache.clear()
cache.touch()
캐시 된 데이터의 만료시간을 연장할 수 있다.
cache.touch("a", 10) # 10초 후 만료
cache.incr()과 cache.decr()
캐시된 숫자 값을 증가, 감소시킨다. 모든 캐시 백엔드에서 원자적이지 않을 수 있다.
cache.set("num", 1)
cache.incr("num") # 2 반환
cache.incr("num", 10) # 12 반환
cache.decr("num") # 11 반환
cache.decr("num", 5) # 6 반환
cache.close()
캐시 백엔드에서 연결을 종료할 필요가 있을 경우, 호출할 수 있다.
모든 캐시 백엔드에서 구현된 것은 아니므로 지원하지 않을 수도 있다.
비동기 캐시 메서드
Django 4.1부터 비동기 메서드가 지원된다. 기본 캐시 메서드 이름 앞에 a를 붙임으로써 비동기 메서드를 사용할 수 있다.
예를 들어, cache.aadd()처럼 사용할 수 있다.
high-level API
high-level API에는 뷰 함수와, 템플릿 지정 block 캐싱이 있다.
뷰 단위 캐싱
특정 뷰만 캐시 하려면 cache_page 데코레이터를 사용한다. 이 데코레이터를 사용하여 뷰의 결과를 자동으로 캐싱해 준다.
첫 번째 인자로 만료 시간을 넘겨주면 되고, 모든 유저에게 동일한 캐싱 응답을 한다.
따라서 유저 별로 다른 응답이 있으면 사용하지 않는 것이 좋다. 요청 URL + LANGUAGE_CODE + TIMEZON 조합으로 캐싱 키를 생성한다.
이벤트 페이지에 적합하다.
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 15분 동안 캐싱
def my_view(request):
# 뷰 로직
...
캐시 템플릿 태그
뷰나 사이트 전체가 아닌 템플릿의 일부만 캐시 하려면 {% cache %} 템플릿 태그를 사용할 수 있다.
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
'Django > DRF' 카테고리의 다른 글
[Django] 랜덤 객체를 가지고 오는 방법 (1) | 2024.09.13 |
---|---|
[DRF] Django REST framework의 권한 관리 (0) | 2024.09.12 |
[Django] 장고에서의 페이징 처리 (0) | 2024.08.12 |
[Django] 배포 시 에러 로그와 관리자 이메일 발송 (0) | 2024.08.07 |
[DRF] nested serializer 사용 방법 (0) | 2024.07.03 |