Repository: Check-Data-Out/velog-dashboard-v2-back-office
Pull request #27: [25.05.12 / TASK-179] Refactor - 타임존 이슈를 위한 local DBMS 관련 수정 사항
Author: login: Nuung
Labels: name: bug
description: Something isn't working
name: documentation
description: Improvements or additions to documentation
name: enhancement
description: New feature or request
Created: 2025-05-11T15:51:16Z | Merged: 2025-05-16T15:36:41Z

## 🔥 변경 사항
해당 변경사항은 local 에서도 timezone 을 모두 UTC 로 얼라인하기 위한 업데이트 입니다.
***적용 시키기 위해서는 "기본 도커 컨테이너와 볼륨을 모두 삭제하고 재실행" 해주세요!***

## 🏷 관련 이슈
- 관련 이슈: 노션 참조!!

## 📌 체크리스트
- [x] 기능이 정상적으로 동작하는지 테스트 완료
- [X] 코드 스타일 가이드 준수 여부 확인
- [X] 관련 문서 업데이트 완료 (필요 시)


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Chores**
    - `.env.sample`에 `DATABASE_URL` 환경 변수를 추가했습니다.
    - 데이터베이스 서비스의 환경 변수 및 볼륨 마운트 구성을 업데이트했습니다.
    - `.gitignore`에서 `timescale_data` 디렉터리 무시 규칙을 수정했습니다.
    - `init.sql`에 PostgreSQL 타임존 설정(UTC)을 추가했습니다.
    - `macwinua` 패키지 의존성을 추가했습니다. (`pyproject.toml`, `requirements.txt`)
- **Style**
    - 주석 내 공백 스타일을 개선했습니다.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.env.sample b/.env.sample
index f0c4ccc..ed94086 100644
--- a/.env.sample
+++ b/.env.sample
@@ -12,6 +12,9 @@ AES_KEY_7=01a8b93c17f8d6e4fba3c5d91e7a6f45
 AES_KEY_8=01a8b93c17f8d6e4fba3c5d91e7a6f45
 AES_KEY_9=01a8b93c17f8d6e4fba3c5d91e7a6f45
 
+# for data migration from pord db
+DATABASE_URL=postgres://postgres..../postgres
+
 # Database default info
 DATABASE_ENGINE=timescale.db.backends.postgresql
 DATABASE_NAME=postgres
diff --git a/.gitignore b/.gitignore
index 960bc46..a603459 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
 # CUSTOM
-timesacle_data/*
+**/timescale_data/**
 
 
 # Created by https://www.toptal.com/developers/gitignore/api/macos,python,pycharm,django
diff --git a/backoffice/settings/local.py b/backoffice/settings/local.py
index 567f42a..2bcc9cf 100644
--- a/backoffice/settings/local.py
+++ b/backoffice/settings/local.py
@@ -9,4 +9,4 @@
 
 CORS_ALLOW_ALL_ORIGINS = True
 
-# DATABASES["prod"] = env.db() # noqa: F405
+# DATABASES["prod"] = env.db()  # noqa: F405
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 5feab76..69807d0 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -6,12 +6,11 @@ services:
       POSTGRES_USER: ${POSTGRES_USER}
       POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
       POSTGRES_DB: ${DATABASE_NAME}
+      PGDATA: /home/postgres/pgdata
+      TZ: 'UTC'
     ports:
       - "${POSTGRES_PORT}:${POSTGRES_PORT}"
     platform: linux/arm64
     volumes:
-      - timescale_data:/var/lib/postgresql/data
-      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
-
-volumes:
-  timescale_data:
+      - ./timescale_data:/home/postgres/pgdata
+      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
\ No newline at end of file
diff --git a/init.sql b/init.sql
index 8c04f01..b6c5c09 100644
--- a/init.sql
+++ b/init.sql
@@ -1,3 +1,4 @@
+-- 기본 user 생성
 DO
 $do$
 BEGIN
@@ -7,3 +8,10 @@ BEGIN
    END IF;
 END
 $do$;
+
+-- 시스템 설정 변경
+ALTER SYSTEM SET timezone TO 'UTC';
+-- 설정 즉시 적용
+SELECT pg_reload_conf();
+-- 현재 세션에도 적용
+SET timezone TO 'UTC';
\ No newline at end of file
diff --git a/poetry.lock b/poetry.lock
index dbebcac..8dd8476 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
 
 [[package]]
 name = "aiohappyeyeballs"
@@ -6,7 +6,6 @@ version = "2.4.6"
 description = "Happy Eyeballs for asyncio"
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1"},
     {file = "aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"},
@@ -18,7 +17,6 @@ version = "3.11.12"
 description = "Async http client/server framework (asyncio)"
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"},
     {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"},
@@ -113,7 +111,7 @@ propcache = ">=0.2.0"
 yarl = ">=1.17.0,<2.0"
 
 [package.extras]
-speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
+speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
 
 [[package]]
 name = "aiohttp-retry"
@@ -121,7 +119,6 @@ version = "2.9.1"
 description = "Simple retry client for aiohttp"
 optional = false
 python-versions = ">=3.7"
-groups = ["main"]
 files = [
     {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"},
     {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"},
@@ -136,7 +133,6 @@ version = "1.3.2"
 description = "aiosignal: a list of registered asynchronous callbacks"
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"},
     {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"},
@@ -151,7 +147,6 @@ version = "3.8.1"
 description = "ASGI specs, helper code, and adapters"
 optional = false
 python-versions = ">=3.8"
-groups = ["main", "dev"]
 files = [
     {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
     {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
@@ -166,7 +161,6 @@ version = "3.0.0"
 description = "Annotate AST trees with source code positions"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
     {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
@@ -182,7 +176,6 @@ version = "5.0.1"
 description = "Timeout context manager for asyncio programs"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
 files = [
     {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
     {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
@@ -194,19 +187,18 @@ version = "25.1.0"
 description = "Classes Without Boilerplate"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
 files = [
     {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"},
     {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"},
 ]
 
 [package.extras]
-benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
 docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
-tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
+tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
 
 [[package]]
 name = "certifi"
@@ -214,7 +206,6 @@ version = "2025.1.31"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
-groups = ["main"]
 files = [
     {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
     {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
@@ -226,8 +217,6 @@ version = "1.17.1"
 description = "Foreign Function Interface for Python calling C code."
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
-markers = "platform_python_implementation != \"PyPy\""
 files = [
     {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
     {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -307,7 +296,6 @@ version = "3.4.0"
 description = "Validate configuration and produce human readable error messages."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
     {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
@@ -319,7 +307,6 @@ version = "8.1.8"
 description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
-groups = ["main"]
 files = [
     {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
     {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
@@ -334,12 +321,10 @@ version = "0.4.6"
 description = "Cross-platform colored terminal text."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-groups = ["main", "dev"]
 files = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 ]
-markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""}
 
 [[package]]
 name = "coverage"
@@ -347,7 +332,6 @@ version = "7.6.10"
 description = "Code coverage measurement for Python"
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"},
     {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"},
@@ -414,7 +398,7 @@ files = [
 ]
 
 [package.extras]
-toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
+toml = ["tomli"]
 
 [[package]]
 name = "cryptography"
@@ -422,7 +406,6 @@ version = "43.0.3"
 description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
 optional = false
 python-versions = ">=3.7"
-groups = ["main"]
 files = [
     {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"},
     {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"},
@@ -472,7 +455,6 @@ version = "5.1.1"
 description = "Decorators for Humans"
 optional = false
 python-versions = ">=3.5"
-groups = ["dev"]
 files = [
     {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
     {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
@@ -484,7 +466,6 @@ version = "0.3.9"
 description = "Distribution utilities"
 optional = false
 python-versions = "*"
-groups = ["dev"]
 files = [
     {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
     {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
@@ -496,7 +477,6 @@ version = "5.1.6"
 description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
 optional = false
 python-versions = ">=3.10"
-groups = ["main", "dev"]
 files = [
     {file = "Django-5.1.6-py3-none-any.whl", hash = "sha256:8d203400bc2952fbfb287c2bbda630297d654920c72a73cc82a9ad7926feaad5"},
     {file = "Django-5.1.6.tar.gz", hash = "sha256:1e39eafdd1b185e761d9fab7a9f0b9fa00af1b37b25ad980a8aa0dac13535690"},
@@ -517,7 +497,6 @@ version = "4.7.0"
 description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)."
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "django_cors_headers-4.7.0-py3-none-any.whl", hash = "sha256:f1c125dcd58479fe7a67fe2499c16ee38b81b397463cf025f0e2c42937421070"},
     {file = "django_cors_headers-4.7.0.tar.gz", hash = "sha256:6fdf31bf9c6d6448ba09ef57157db2268d515d94fc5c89a0a1028e1fc03ee52b"},
@@ -533,7 +512,6 @@ version = "4.4.6"
 description = "A configurable set of panels that display various debug information about the current request/response."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "django_debug_toolbar-4.4.6-py3-none-any.whl", hash = "sha256:3beb671c9ec44ffb817fad2780667f172bd1c067dbcabad6268ce39a81335f45"},
     {file = "django_debug_toolbar-4.4.6.tar.gz", hash = "sha256:36e421cb908c2f0675e07f9f41e3d1d8618dc386392ec82d23bcfcd5d29c7044"},
@@ -549,7 +527,6 @@ version = "0.11.2"
 description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application."
 optional = false
 python-versions = ">=3.6,<4"
-groups = ["main"]
 files = [
     {file = "django-environ-0.11.2.tar.gz", hash = "sha256:f32a87aa0899894c27d4e1776fa6b477e8164ed7f6b3e410a62a6d72caaf64be"},
     {file = "django_environ-0.11.2-py2.py3-none-any.whl", hash = "sha256:0ff95ab4344bfeff693836aa978e6840abef2e2f1145adff7735892711590c05"},
@@ -566,7 +543,6 @@ version = "3.2.3"
 description = "Extensions for Django"
 optional = false
 python-versions = ">=3.6"
-groups = ["dev"]
 files = [
     {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"},
     {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
@@ -581,7 +557,6 @@ version = "0.2.13"
 description = "A Django database backend for integration with TimescaleDB"
 optional = false
 python-versions = "*"
-groups = ["main"]
 files = [
     {file = "django-timescaledb-0.2.13.tar.gz", hash = "sha256:3a2dcdf224af318c6c813c7fbf323fa73ae9bd12d33cdf7540e34344ae62f739"},
 ]
@@ -592,14 +567,13 @@ version = "2.2.0"
 description = "Get the currently executing AST node of a frame, and other information"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"},
     {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"},
 ]
 
 [package.extras]
-tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
+tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
 
 [[package]]
 name = "filelock"
@@ -607,7 +581,6 @@ version = "3.17.0"
 description = "A platform independent file lock."
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"},
     {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"},
@@ -616,7 +589,7 @@ files = [
 [package.extras]
 docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
 testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
-typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
+typing = ["typing-extensions (>=4.12.2)"]
 
 [[package]]
 name = "frozenlist"
@@ -624,7 +597,6 @@ version = "1.5.0"
 description = "A list-like structure which implements collections.abc.MutableSequence"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
 files = [
     {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"},
     {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"},
@@ -726,7 +698,6 @@ version = "23.0.0"
 description = "WSGI HTTP Server for UNIX"
 optional = false
 python-versions = ">=3.7"
-groups = ["main"]
 files = [
     {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
     {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
@@ -748,7 +719,6 @@ version = "0.14.0"
 description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
 optional = false
 python-versions = ">=3.7"
-groups = ["main"]
 files = [
     {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
     {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -760,7 +730,6 @@ version = "2.6.6"
 description = "File identification library for Python"
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"},
     {file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"},
@@ -775,7 +744,6 @@ version = "3.10"
 description = "Internationalized Domain Names in Applications (IDNA)"
 optional = false
 python-versions = ">=3.6"
-groups = ["main"]
 files = [
     {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
     {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
@@ -790,7 +758,6 @@ version = "2.0.0"
 description = "brain-dead simple config-ini parsing"
 optional = false
 python-versions = ">=3.7"
-groups = ["dev"]
 files = [
     {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -802,7 +769,6 @@ version = "8.32.0"
 description = "IPython: Productive Interactive Computing"
 optional = false
 python-versions = ">=3.10"
-groups = ["dev"]
 files = [
     {file = "ipython-8.32.0-py3-none-any.whl", hash = "sha256:cae85b0c61eff1fc48b0a8002de5958b6528fa9c8defb1894da63f42613708aa"},
     {file = "ipython-8.32.0.tar.gz", hash = "sha256:be2c91895b0b9ea7ba49d33b23e2040c352b33eb6a519cca7ce6e0c743444251"},
@@ -822,7 +788,7 @@ traitlets = ">=5.13.0"
 [package.extras]
 all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
 black = ["black"]
-doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
+doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"]
 kernel = ["ipykernel"]
 matplotlib = ["matplotlib"]
 nbconvert = ["nbconvert"]
@@ -839,7 +805,6 @@ version = "0.19.2"
 description = "An autocompletion tool for Python that can be used for text editors."
 optional = false
 python-versions = ">=3.6"
-groups = ["dev"]
 files = [
     {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
     {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
@@ -853,13 +818,23 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab
 qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
 testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
 
+[[package]]
+name = "macwinua"
+version = "0.1.2"
+description = "A library for generating realistic browser headers for macOS and Windows platforms — always the freshest Chrome headers."
+optional = false
+python-versions = "<4.0,>=3.10"
+files = [
+    {file = "macwinua-0.1.2-py3-none-any.whl", hash = "sha256:ebda05e2e6525ec8163ca3a071bfe63c44c13f18c4fb637ba299e4cd19560424"},
+    {file = "macwinua-0.1.2.tar.gz", hash = "sha256:d889b97ebd45ec57b2df1011e301e81f037c5c42fd11b98a08739ec9a3fcffb7"},
+]
+
 [[package]]
 name = "matplotlib-inline"
 version = "0.1.7"
 description = "Inline Matplotlib backend for Jupyter"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
     {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
@@ -874,7 +849,6 @@ version = "6.1.0"
 description = "multidict implementation"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
 files = [
     {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
     {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"},
@@ -976,7 +950,6 @@ version = "1.15.0"
 description = "Optional static typing for Python"
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
     {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
@@ -1029,7 +1002,6 @@ version = "1.0.0"
 description = "Type system extensions for programs checked with the mypy type checker."
 optional = false
 python-versions = ">=3.5"
-groups = ["dev"]
 files = [
     {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
     {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
@@ -1041,7 +1013,6 @@ version = "1.9.1"
 description = "Node.js virtual environment builder"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-groups = ["dev"]
 files = [
     {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
     {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
@@ -1053,7 +1024,6 @@ version = "24.2"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.8"
-groups = ["main", "dev"]
 files = [
     {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
     {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
@@ -1065,7 +1035,6 @@ version = "0.8.4"
 description = "A Python Parser"
 optional = false
 python-versions = ">=3.6"
-groups = ["dev"]
 files = [
     {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
     {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
@@ -1081,8 +1050,6 @@ version = "4.9.0"
 description = "Pexpect allows easy control of interactive console applications."
 optional = false
 python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
 files = [
     {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
     {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
@@ -1097,7 +1064,6 @@ version = "4.3.6"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
     {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
@@ -1114,7 +1080,6 @@ version = "1.5.0"
 description = "plugin and hook calling mechanisms for python"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
     {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@@ -1130,7 +1095,6 @@ version = "4.1.0"
 description = "A framework for managing and maintaining multi-language pre-commit hooks."
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"},
     {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"},
@@ -1149,7 +1113,6 @@ version = "3.0.50"
 description = "Library for building powerful interactive command lines in Python"
 optional = false
 python-versions = ">=3.8.0"
-groups = ["dev"]
 files = [
     {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"},
     {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"},
@@ -1164,7 +1127,6 @@ version = "0.2.1"
 description = "Accelerated property cache"
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"},
     {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"},
@@ -1256,7 +1218,6 @@ version = "3.2.4"
 description = "PostgreSQL database adapter for Python"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
 files = [
     {file = "psycopg-3.2.4-py3-none-any.whl", hash = "sha256:43665368ccd48180744cab26b74332f46b63b7e06e8ce0775547a3533883d381"},
     {file = "psycopg-3.2.4.tar.gz", hash = "sha256:f26f1346d6bf1ef5f5ef1714dd405c67fb365cfd1c6cea07de1792747b167b92"},
@@ -1267,8 +1228,8 @@ psycopg-binary = {version = "3.2.4", optional = true, markers = "implementation_
 tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 
 [package.extras]
-binary = ["psycopg-binary (==3.2.4) ; implementation_name != \"pypy\""]
-c = ["psycopg-c (==3.2.4) ; implementation_name != \"pypy\""]
+binary = ["psycopg-binary (==3.2.4)"]
+c = ["psycopg-c (==3.2.4)"]
 dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
 docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
 pool = ["psycopg-pool"]
@@ -1280,8 +1241,6 @@ version = "3.2.4"
 description = "PostgreSQL database adapter for Python -- C optimisation distribution"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
-markers = "implementation_name != \"pypy\""
 files = [
     {file = "psycopg_binary-3.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c716f75b5c0388fc5283b5124046292c727511dd8c6aa59ca2dc644b9a2ed0cd"},
     {file = "psycopg_binary-3.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e2e8050347018f596a63f5dccbb92fb68bca52b13912cb8fc40184b24c0e534f"},
@@ -1356,8 +1315,6 @@ version = "0.7.0"
 description = "Run a subprocess in a pseudo terminal"
 optional = false
 python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
 files = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
     {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@@ -1369,7 +1326,6 @@ version = "0.2.3"
 description = "Safely evaluate AST nodes without side effects"
 optional = false
 python-versions = "*"
-groups = ["dev"]
 files = [
     {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
     {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
@@ -1384,8 +1340,6 @@ version = "2.22"
 description = "C parser in Python"
 optional = false
 python-versions = ">=3.8"
-groups = ["main"]
-markers = "platform_python_implementation != \"PyPy\""
 files = [
     {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
     {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -1397,7 +1351,6 @@ version = "2.19.1"
 description = "Pygments is a syntax highlighting package written in Python."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
     {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
@@ -1412,7 +1365,6 @@ version = "8.3.4"
 description = "pytest: simple powerful testing with Python"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
     {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
@@ -1433,7 +1385,6 @@ version = "0.25.3"
 description = "Pytest support for asyncio"
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"},
     {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"},
@@ -1452,7 +1403,6 @@ version = "6.0.0"
 description = "Pytest plugin for measuring coverage."
 optional = false
 python-versions = ">=3.9"
-groups = ["dev"]
 files = [
     {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
     {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
@@ -1471,7 +1421,6 @@ version = "4.9.0"
 description = "A Django plugin for pytest."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99"},
     {file = "pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314"},
@@ -1490,7 +1439,6 @@ version = "6.0.2"
 description = "YAML parser and emitter for Python"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -1553,7 +1501,6 @@ version = "0.7.4"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
-groups = ["dev"]
 files = [
     {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"},
     {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"},
@@ -1581,7 +1528,6 @@ version = "2.24.1"
 description = "Python client for Sentry (https://sentry.io)"
 optional = false
 python-versions = ">=3.6"
-groups = ["main"]
 files = [
     {file = "sentry_sdk-2.24.1-py2.py3-none-any.whl", hash = "sha256:36baa6a1128b9d98d2adc5e9b2f887eff0a6af558fc2b96ed51919042413556d"},
     {file = "sentry_sdk-2.24.1.tar.gz", hash = "sha256:8ba3c29990fa48865b908b3b9dc5ae7fa7e72407c7c9e91303e5206b32d7b8b1"},
@@ -1638,7 +1584,6 @@ version = "0.5.3"
 description = "A non-validating SQL parser."
 optional = false
 python-versions = ">=3.8"
-groups = ["main", "dev"]
 files = [
     {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"},
     {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"},
@@ -1654,7 +1599,6 @@ version = "0.6.3"
 description = "Extract data from python stack frames and tracebacks for informative displays"
 optional = false
 python-versions = "*"
-groups = ["dev"]
 files = [
     {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
     {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
@@ -1674,7 +1618,6 @@ version = "5.14.3"
 description = "Traitlets Python configuration system"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
     {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
@@ -1690,7 +1633,6 @@ version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@@ -1702,8 +1644,6 @@ version = "2025.1"
 description = "Provider of IANA time zone data"
 optional = false
 python-versions = ">=2"
-groups = ["main", "dev"]
-markers = "sys_platform == \"win32\""
 files = [
     {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"},
     {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"},
@@ -1715,14 +1655,13 @@ version = "2.3.0"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
     {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
 ]
 
 [package.extras]
-brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
 h2 = ["h2 (>=4,<5)"]
 socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 zstd = ["zstandard (>=0.18.0)"]
@@ -1733,7 +1672,6 @@ version = "0.34.0"
 description = "The lightning-fast ASGI server."
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"},
     {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"},
@@ -1744,7 +1682,7 @@ click = ">=7.0"
 h11 = ">=0.8"
 
 [package.extras]
-standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"]
+standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
 
 [[package]]
 name = "virtualenv"
@@ -1752,7 +1690,6 @@ version = "20.29.1"
 description = "Virtual Python Environment builder"
 optional = false
 python-versions = ">=3.8"
-groups = ["dev"]
 files = [
     {file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"},
     {file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"},
@@ -1765,7 +1702,7 @@ platformdirs = ">=3.9.1,<5"
 
 [package.extras]
 docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
 
 [[package]]
 name = "wcwidth"
@@ -1773,7 +1710,6 @@ version = "0.2.13"
 description = "Measures the displayed width of unicode strings in a terminal"
 optional = false
 python-versions = "*"
-groups = ["dev"]
 files = [
     {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
     {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
@@ -1785,7 +1721,6 @@ version = "1.18.3"
 description = "Yet another URL library"
 optional = false
 python-versions = ">=3.9"
-groups = ["main"]
 files = [
     {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"},
     {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"},
@@ -1877,6 +1812,6 @@ multidict = ">=4.0"
 propcache = ">=0.2.0"
 
 [metadata]
-lock-version = "2.1"
+lock-version = "2.0"
 python-versions = "^3.13"
-content-hash = "3343378d6ab7ca69696902ac4b32e5ce5d5a2ae86041288879880ec7e4d302bf"
+content-hash = "47adf4a48844a63bacf973be9cbc806470e3069095dfe731794c98a0b696d080"
diff --git a/pyproject.toml b/pyproject.toml
index d1d2f58..513a7db 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,6 +19,7 @@ uvicorn = "^0.34.0"
 gunicorn = "^23.0.0"
 async-timeout = "^5.0.1"
 sentry-sdk = "^2.24.1"
+macwinua = "^0.1.2"
 
 [tool.poetry.group.dev.dependencies]
 ruff = "^0.7.4"
diff --git a/requirements.txt b/requirements.txt
index 229ef3d..cde7ca3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -30,6 +30,7 @@ idna==3.10
 iniconfig==2.0.0
 ipython==8.32.0
 jedi==0.19.2
+macwinua==0.1.2
 matplotlib-inline==0.1.7
 multidict==6.1.0
 mypy==1.15.0


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>docker-compose.yaml (2)</summary><blockquote>

`15-15`: **데이터 볼륨 바인드 마운트 권한 확인**  
로컬 디렉토리 `./timescale_data`를 바인드 마운트로 사용하면 호스트와 컨테이너 간 UID/GID 불일치로 인한 쓰기 권한 문제가 발생할 수 있습니다. `postgres` 유저가 해당 디렉토리에 쓰기 가능한지 확인하거나, 초기화 단계에서 `chown` 명령을 추가하는 방안을 고려하세요.

---

`16-16`: **Init 스크립트 마운트 및 파일 끝 개행**  
`./init.sql`을 `/docker-entrypoint-initdb.d/init.sql`에 마운트하여 초기화 스크립트를 적용하신 부분은 적절합니다. 다만, 스크립트 실행 순서에 영향을 줄 수 있으므로 `init.sql` 내 명령이 의도한 순서로 실행되는지 확인하는 테스트를 권장하며, YAML 파일 마지막에 누락된 개행 문자를 추가해주세요.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 16-16: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9ffde97895035464a5f144434e196eae1df87af9 and a4c0e1ee3a1f1169bfc75bfb48a17a3aff34dee5.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `poetry.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (7)</summary>

* `.env.sample` (1 hunks)
* `.gitignore` (1 hunks)
* `backoffice/settings/local.py` (1 hunks)
* `docker-compose.yaml` (1 hunks)
* `init.sql` (2 hunks)
* `pyproject.toml` (1 hunks)
* `requirements.txt` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>docker-compose.yaml</summary>

[error] 16-16: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: Test CI</summary>

<details>
<summary>backoffice/settings/local.py</summary>

[error] 12-12: django.core.exceptions.ImproperlyConfigured: Set the DATABASE_URL environment variable. The migration command failed because the DATABASE_URL environment variable is missing.

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>pyproject.toml (1)</summary>

`22-22`: **`macwinua` 의존성 추가 검증**  
`macwinua = "^0.1.2"` 패키지가 PyPI에 실제로 존재하고, 프로젝트의 Python 버전 및 다른 의존성과 호환되는지 확인해주세요. 또한, 보안 취약점이나 라이선스 제약이 없는지도 검토가 필요합니다.

</details>
<details>
<summary>requirements.txt (1)</summary>

`33-33`: **`macwinua` 버전 일관성 점검**  
`pyproject.toml`에서 사용한 `^0.1.2` 제약 조건과 `requirements.txt`에 고정된 `==0.1.2`가 일치하는지 확인하고, 두 파일 간 버전 불일치로 인한 설치 문제를 방지해주세요.

</details>
<details>
<summary>.env.sample (1)</summary>

`15-16`: **프로덕션 데이터베이스 연결을 위한 환경변수 추가**

데이터 마이그레이션을 위한 프로덕션 DB 연결 정보를 명시적으로 추가했습니다. 이는 backoffice/settings/local.py에서 활성화된 DATABASES["prod"] 설정과 함께 작동하여 프로덕션 데이터베이스에서 데이터를 마이그레이션할 수 있도록 합니다.

</details>
<details>
<summary>.gitignore (1)</summary>

`2-2`: **타임스케일 데이터 디렉토리 무시 패턴 개선**

타임스케일 데이터 디렉토리의 무시 패턴을 `**/timescale_data/**`로 변경하여 프로젝트 내 모든 깊이에서 타임스케일 데이터 디렉토리와 그 내용을 무시하도록 개선했습니다. 이는 도커 컴포즈 설정에서 로컬 디렉토리를 바인드 마운트로 사용하는 변경사항과 일치합니다.

</details>
<details>
<summary>init.sql (1)</summary>

`12-17`: **PostgreSQL 타임존 설정을 UTC로 통일**

데이터베이스 서버의 타임존을 UTC로 설정하는 명령을 추가했습니다. 이는 PR의 주요 목적인 "모든 타임존을 UTC로 통일"과 일치합니다. 시스템 레벨 설정을 변경하고, 설정을 즉시 적용하며, 현재 세션에도 적용하는 세 단계의 명령을 통해 일관된 타임존 처리를 보장합니다.


이 변경으로 인해 기존 사용자들은 도커 컨테이너와 볼륨을 삭제하고 재시작해야 함을 안내해야 합니다.

</details>
<details>
<summary>docker-compose.yaml (2)</summary>

`9-9`: **PGDATA 경로 변경 검토**  
`PGDATA`를 `/home/postgres/pgdata`로 지정하여 기본 데이터 디렉토리 경로를 오버라이드하셨습니다. 이미지 엔트리포인트가 해당 환경 변수를 정상 인식하는지, 그리고 컨테이너가 해당 경로에 접근 권한을 갖는지 공식 문서를 확인하고 실제 동작 환경에서 테스트를 권장합니다.

---

`10-10`: **타임존 설정 적절**  
`TZ: 'UTC'` 환경 변수를 추가하여 컨테이너 레벨 타임존을 UTC로 통일하신 것은 로컬 환경과 DB 타임존 일관성 유지에 도움이 됩니다.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-11T15:55:01Z
user: login: ooheunda
body: 대부분 타임존 관련한 설정 변경이라 리뷰 드릴 부분은 크게 없어 보이네요.
고생 많으셨습니다~! 👍 🔥 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-12T11:18:41Z
user: login: Nuung
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:24:11Z
user: login: ooheunda
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T11:47:15Z
user: login: six-standard
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T00:12:12Z
user: login: six-standard
body: 이번에도 코드 잘 읽었습니다!
API 서버에 비해 비교적 수정 내용이 적은 것 같은데, 그래도 필요한 건 다 적용되어있네요~

### 좋았던 점
- 이전에 말씀드렸던 도커 볼륨 경로 문제가 해결된 것 같아보입니다. (다만 해결되긴 했어도, 이전에 왜 저 경로로 설정하셨었는지가 궁금하긴 하네요.)
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-13T00:17:52Z
user: login: Jihyun3478
body: 백오피스 서버는 타임존 관련 수정이 많지 않아 크게 다른 이견 없습니다! 고생하셨습니다!!

### 좋았던 점

- init.sql 파일에서 데이터베이스 타임존을 UTC로 설정하신 부분이 좋았습니다! 애플리케이션과 DB 간의 시간 일관성을 유지할 수 있겠네요!!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T00:34:08Z
user: login: Nuung
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T14:39:20Z

Review comments:
diff_hunk: @@ -9,4 +9,4 @@
 
 CORS_ALLOW_ALL_ORIGINS = True
 
-# DATABASES["prod"] = env.db() # noqa: F405
+DATABASES["prod"] = env.db()  # noqa: F405
path: backoffice/settings/local.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**프로덕션 데이터베이스 설정 활성화 및 환경변수 필수화**

프로덕션 데이터베이스 설정을 활성화했습니다. 이 변경으로 인해 DATABASE_URL 환경변수가 필수가 되었습니다. 파이프라인 실패 로그에서 보이듯이 이 환경변수가 누락되면 마이그레이션 명령이 실패합니다.


개발 및 테스트 환경에서 이 설정을 사용하려면 .env 파일에 DATABASE_URL을 반드시 설정해야 합니다. 이 점을 프로젝트 문서에 명확히 기록하고, 모든 팀원에게 이 변경사항에 대해 알려주세요.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: Test CI</summary>

[error] 12-12: django.core.exceptions.ImproperlyConfigured: Set the DATABASE_URL environment variable. The migration command failed because the DATABASE_URL environment variable is missing.

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In backoffice/settings/local.py at line 12, the production database setting is
now activated, making the DATABASE_URL environment variable mandatory. Ensure
that the project documentation is updated to clearly state this requirement and
communicate to all team members that the .env file must include DATABASE_URL for
development and testing environments to prevent migration failures.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 4324741
created_at: 2025-05-11T15:55:00Z
author_association: NONE
diff_hunk: @@ -30,6 +30,7 @@ idna==3.10
 iniconfig==2.0.0
 ipython==8.32.0
 jedi==0.19.2
+macwinua==0.1.2
path: requirements.txt
user: login: ooheunda
body: 이 패키지 추가는 의도하신게 맞을까요?! 지금 PR이랑 큰 연관은 없어 보여서요!
created_at: 2025-05-12T11:18:18Z
author_association: MEMBER
diff_hunk: @@ -30,6 +30,7 @@ idna==3.10
 iniconfig==2.0.0
 ipython==8.32.0
 jedi==0.19.2
+macwinua==0.1.2
path: requirements.txt
user: login: Nuung
body: ㅋㅋ맞아요 user agent 값 바꾸는 건데, http request 던질때 bot 감지 우회하려고 바꿔요! 사실 제가 만든거라 추가해씀.. ㅎ
created_at: 2025-05-12T11:24:11Z
author_association: MEMBER
diff_hunk: @@ -30,6 +30,7 @@ idna==3.10
 iniconfig==2.0.0
 ipython==8.32.0
 jedi==0.19.2
+macwinua==0.1.2
path: requirements.txt
user: login: ooheunda
body: 대박~~~ 봇 감지 우회라니 좋아보이네요!!! 👍 💪 
created_at: 2025-05-12T11:47:15Z
author_association: MEMBER
diff_hunk: @@ -9,4 +9,4 @@
 
 CORS_ALLOW_ALL_ORIGINS = True
 
-# DATABASES["prod"] = env.db() # noqa: F405
+# DATABASES["prod"] = env.db()  # noqa: F405
path: backoffice/settings/local.py
user: login: six-standard
body: 이 부분은 단순 오타나 코드 컨벤션 맞춤일까요?
created_at: 2025-05-13T00:12:12Z
author_association: MEMBER
diff_hunk: @@ -9,4 +9,4 @@
 
 CORS_ALLOW_ALL_ORIGINS = True
 
-# DATABASES["prod"] = env.db() # noqa: F405
+# DATABASES["prod"] = env.db()  # noqa: F405
path: backoffice/settings/local.py
user: login: Nuung
body: @six-standard 주석 없앴다가 그냥 optional 하게 가는걸로 바꿧어요 흐흐
created_at: 2025-05-13T14:39:20Z
author_association: MEMBER

PR comments:
user: login: notion-workspace[bot]
created_at: 2025-05-11T15:51:18Z
body: <a href="https://www.notion.so/table-1e26299fd66680eca8d7e43e191dbd96?pvs=8&n=github_linkback">🚨 [긴급] table 관련 이슈 & 타임존 이슈 모두 해결하기</a>
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-11T15:51:22Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

"""
## Walkthrough

이 변경사항은 데이터베이스 환경변수 추가, 도커 컴포즈 내 볼륨 마운트 경로 및 환경변수 수정, PostgreSQL 타임존 설정 추가, `.gitignore` 패턴 수정, 그리고 `macwinua` 패키지 의존성 추가를 포함합니다. 일부 주석 스타일도 소폭 조정되었습니다.

## Changes

| 파일/경로                                      | 변경 요약                                                                                                 |
|------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| .env.sample                                   | 데이터 마이그레이션용 `DATABASE_URL` 환경 변수 추가 (PostgreSQL 연결 문자열 포함).                          |
| .gitignore                                    | `timescale_data` 디렉토리 무시 패턴 오타 수정 및 재귀적 무시 패턴으로 확장.                                |
| backoffice/settings/local.py                   | 주석 처리된 `DATABASES["prod"] = env.db()` 라인에 공백 추가, 기능적 변경 없음.                            |
| docker-compose.yaml                           | DB 서비스 환경변수 `PGDATA`, `TZ` 추가, 볼륨을 명명된 볼륨에서 로컬 디렉토리 바인드 마운트로 변경, 불필요한 볼륨 선언 제거. |
| init.sql                                      | PostgreSQL 서버 및 세션 타임존을 'UTC'로 설정하는 명령 추가.                                              |
| pyproject.toml, requirements.txt              | `macwinua` 패키지 버전 0.1.2 의존성 추가.                                                                |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Developer
    participant EnvFile
    participant DjangoApp
    participant Docker
    participant Postgres

    Developer->>EnvFile: DATABASE_URL 추가
    Developer->>Docker: docker-compose.yaml 수정 및 실행
    Docker->>Postgres: 환경변수(TZ, PGDATA) 전달
    Docker->>Postgres: init.sql로 타임존 UTC 설정
    Developer->>DjangoApp: prod DB 환경설정 활성화(local.py)
    DjangoApp->>EnvFile: DATABASE_URL 읽기
    DjangoApp->>Postgres: prod DB 연결
```

## Possibly related PRs

- Check-Data-Out/velog-dashboard-v2-back-office#4: `.env.sample`에서 `DATABASE_URL` 관련 환경변수를 제거하고 상세 PostgreSQL 연결 변수로 대체하는 변경을 포함, 본 PR과 환경설정 관련 충돌 가능성 있음.

## Suggested reviewers

- six-standard
- BDlhj

## Poem

> 🐇  
> UTC로 시간 맞추고,  
> 데이터 길도 뚜렷해졌네.  
> macwinua 새 친구도 왔고,  
> 바인드 마운트로 데이터 지키고,  
> 깃이 타임스케일 숨겨주네.  
> 코드토끼 오늘도 힘차게,  
> 뛰어가며 코드를 완성해!  
>
"""

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> - CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> 
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a4c0e1ee3a1f1169bfc75bfb48a17a3aff34dee5 and 4324741522f5effd293b968e13b819f40ed89ed8.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `backoffice/settings/local.py` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* backoffice/settings/local.py

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Check-Data-Out/velog-dashboard-v2-back-office&utm_content=27):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE