{"id":6451,"date":"2026-06-03T22:57:10","date_gmt":"2026-06-03T13:57:10","guid":{"rendered":"https:\/\/secondlife.lol\/?p=6451"},"modified":"2026-06-03T22:57:12","modified_gmt":"2026-06-03T13:57:12","slug":"django-beta-tester-bulk-creation-hmac-fernet","status":"publish","type":"post","link":"https:\/\/secondlife.lol\/ja\/django-beta-tester-bulk-creation-hmac-fernet\/","title":{"rendered":"Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131 \uc644\ubcbd \uac00\uc774\ub4dc (HMAC + Fernet \uc554\ud638\ud654 \uc801\uc6a9 + Admin 500 \uc5d0\ub7ec \ud2b8\ub7ec\ube14\uc288\ud305)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.djangoproject.com\/\" target=\"_blank\" rel=\"noopener\">Django<\/a> \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131\uc740 \uc5b4\ub5bb\uac8c \ud560 \uc218 \uc788\uc744\uae4c\uc694?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c \ub0b4\ubd80 \uc0ac\uc6a9\uc790 \ub300\uc0c1 \ubca0\ud0c0 \ud14c\uc2a4\ud2b8\ub97c \uc9c4\ud589\ud560 \ub54c, <strong>50\uba85, 100\uba85, \uc2ec\uc9c0\uc5b4 \uc218\ubc31 \uba85\uc758 \ud14c\uc2a4\ud2b8 \uacc4\uc815\uc744 \uc218\ub3d9\uc73c\ub85c \uc0dd\uc131\ud558\ub294 \uac83\uc740 \ud604\uc2e4\uc801\uc73c\ub85c \ubd88\uac00\ub2a5<\/strong>\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uad00\ub9ac\uc790 \ud55c \uba85\uc774 \uc77c\uc77c\uc774 \ud68c\uc6d0\uac00\uc785\uc744 \ubc1b\uace0 \uc2b9\uc778\ud558\ub294 \uacfc\uc815\uc740 \uc2dc\uac04 \ub0ad\ube44\uc77c \ubfd0 \uc544\ub2c8\ub77c, <strong>\uc0ac\ubc88(employee_no)\uacfc \uc774\ub984 \uac19\uc740 \ubbfc\uac10 \uc815\ubcf4\ub97c \ud3c9\ubb38\uc73c\ub85c \uc800\uc7a5<\/strong>\ud558\uba74 \ubcf4\uc548 \uc0ac\uace0\ub85c \uc9c1\uacb0\ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uadf8\ub798\uc11c \uc6b0\ub9ac\ub294 <strong>Django shell \uc2a4\ud06c\ub9bd\ud2b8 \ud55c \ubc29<\/strong>\uc73c\ub85c \ud574\uacb0\ud588\uc2b5\ub2c8\ub2e4.<br>HMAC-SHA256 \ud574\uc2dc + Fernet(AES-128) \uc554\ud638\ud654\ub97c \uc801\uc6a9\ud558\uace0, <strong>\uc989\uc2dc \uc2b9\uc778(approved)<\/strong> \uc0c1\ud0dc\ub85c \uc0dd\uc131\ud558\ub294 \uc2e4\uc804\uc6a9 Django \ubca0\ud0c0\ud14c\uc2a4\ud130 <strong>\uc77c\uad04 \uc0dd\uc131 \uc2a4\ud06c\ub9bd\ud2b8<\/strong>\ub97c \uacf5\uac1c\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uc774 \ud3ec\uc2a4\ud2b8\uc5d0\uc11c\ub294 <strong>\ucd08\uae30 \uc2a4\ud06c\ub9bd\ud2b8\uc758 \ubb38\uc81c\uc810<\/strong>, <strong>\uc218\uc815\ub41c \uc644\uc131 \uc2a4\ud06c\ub9bd\ud2b8<\/strong>, <strong>Admin\uc5d0\uc11c 500 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud55c \ud2b8\ub7ec\ube14\uc288\ud305 \uacfc\uc815<\/strong>, \uadf8\ub9ac\uace0 <strong>\uc548\uc804\ud558\uac8c \uc6b4\uc601\ud558\ub294 \ubc29\ubc95<\/strong>\uae4c\uc9c0 \ubaa8\ub450 \ub2e4\ub8f9\ub2c8\ub2e4.<\/p>\n\n\n<style>.kb-image6451_914cc1-cd .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<div class=\"wp-block-kadence-image kb-image6451_914cc1-cd\"><figure class=\"aligncenter size-medium\"><img decoding=\"async\" width=\"300\" height=\"447\" src=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11-300x447.jpg\" alt=\"Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131 \uc2a4\ud06c\ub9bd\ud2b8 \uc2e4\ud589 \uc911 IntegrityError \ubc0f \ud3c9\ubb38 \uc800\uc7a5 \ubb38\uc81c \ud130\ubbf8\ub110 \ub85c\uadf8\" class=\"kb-img wp-image-6452\" srcset=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11-300x447.jpg 300w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11-600x894.jpg 600w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11-768x1144.jpg 768w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11-8x12.jpg 8w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-11.jpg 784w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">\uc65c \ubca0\ud0c0\ud14c\uc2a4\ud130\ub97c \uc77c\uad04 \uc0dd\uc131\ud574\uc57c \ud558\ub294\uac00?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\ubca0\ud0c0 \ud14c\uc2a4\ud2b8 \ub2e8\uacc4\uc5d0\uc11c\ub294 \ucd5c\uc18c 50\uba85 \uc774\uc0c1\uc758 \uc2e4\uc81c \uc0ac\uc6a9\uc790(\uc0ac\ubc88 \ubcf4\uc720 \uc9c1\uc6d0)\ub85c \ud14c\uc2a4\ud2b8\ub97c \ud574\uc57c \ud569\ub2c8\ub2e4.<br>\uc218\ub3d9\uc73c\ub85c 50\uba85\uc744 \uc0dd\uc131\ud558\ub824\uba74:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>50\ubc88\uc758 \ud68c\uc6d0\uac00\uc785 + \uc774\uba54\uc77c \ud655\uc778<\/li>\n\n\n\n<li>\uad00\ub9ac\uc790 \uc2b9\uc778 \uc791\uc5c5<\/li>\n\n\n\n<li>\ube44\ubc00\ubc88\ud638 \ucd08\uae30\ud654 \ubc0f \uc804\ub2ec<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\uc774 \uacfc\uc815\ub9cc\uc73c\ub85c\ub3c4 <strong>\ubc18\ub098\uc808 \uc774\uc0c1<\/strong>\uc774 \uc18c\uc694\ub429\ub2c8\ub2e4.<br>\uac8c\ub2e4\uac00 \uc0ac\ubc88\uc744 \ud3c9\ubb38\uc73c\ub85c \uc800\uc7a5\ud558\uba74 <strong>DB \uc720\ucd9c \uc2dc 1\ucc28 \ud53c\ud574<\/strong>\uac00 \ubc1c\uc0dd\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud574\uacb0\ucc45<\/strong>: Django shell + \uc554\ud638\ud654 \ub85c\uc9c1\uc744 \uacb0\ud569\ud55c <strong>\uc790\ub3d9\ud654 \uc2a4\ud06c\ub9bd\ud2b8<\/strong>\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ubb38\uc81c \ubc1c\uc0dd: \ucd08\uae30 \uc2a4\ud06c\ub9bd\ud2b8\uc758 \uce58\uba85\uc801 \ubc84\uadf8<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\ucc98\uc74c\uc5d0 \uc791\uc131\ud55c \uc2a4\ud06c\ub9bd\ud2b8\ub294 \ub2e4\uc74c\uacfc \uac19\uc558\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># create_beta_users.py (\ubb38\uc81c \ubc84\uc804)\nfrom django.contrib.auth import get_user_model\nfrom django.db import IntegrityError\n\nUser = get_user_model()\n\nprefixes = &#91;10000000, 20000000, 30000000, 40000000, 50000000]\ncount_per_prefix = 10\ncreated_accounts = &#91;]\n\nfor prefix in prefixes:\n    for i in range(count_per_prefix):\n        emp_id = str(prefix + i)\n        try:\n            user_kwargs = {\n                User.USERNAME_FIELD: emp_id,   # \u2190 \uc5ec\uae30\uc11c \ud3c9\ubb38 \uc0ac\ubc88\uc774 \uadf8\ub300\ub85c \uc800\uc7a5\ub428\n            }\n            # name \ud544\ub4dc\uac00 NOT NULL\uc774\uba74 \uc8fc\uc11d \ud574\uc81c \ud544\uc694\n            # user_kwargs&#91;'name'] = f'\ubca0\ud0c0\ud14c\uc2a4\ud130_{emp_id}'\n\n            user = User(**user_kwargs)\n            user.set_password(emp_id)          # \ube44\ubc00\ubc88\ud638 = \uc0ac\ubc88 (\ud3c9\ubb38)\n            user.save()\n\n            created_accounts.append(emp_id)\n            print(f\" \uc0dd\uc131 \uc644\ub8cc: \uc0ac\ubc88 {emp_id} \/ \ube44\ubc88 {emp_id}\")\n\n        except IntegrityError:\n            print(f\" \uc774\ubbf8 \uc874\uc7ac\ud558\ub294 \uc0ac\ubc88\uc785\ub2c8\ub2e4: {emp_id}\")\n        except Exception as e:\n            print(f\" \uc0dd\uc131 \uc2e4\ud328 ({emp_id}): {e}\")\n\nprint(f\" \ucd1d {len(created_accounts)}\uac1c\uc758 \ubca0\ud0c0\ud14c\uc2a4\ud130 \uacc4\uc815 \uc0dd\uc131 \uc644\ub8cc\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\uc774 \uc2a4\ud06c\ub9bd\ud2b8\uc758 \ubb38\uc81c\uc810<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>employee_no<\/code> \ud544\ub4dc\uc5d0 <strong>\ud3c9\ubb38 \uc0ac\ubc88<\/strong>\uc774 \uadf8\ub300\ub85c \uc800\uc7a5\ub428 (HMAC \ud574\uc2dc\uac00 \uc544\ub2d8)<\/li>\n\n\n\n<li><code>CustomUser<\/code> \ubaa8\ub378\uc758 <code>EncryptedCharField<\/code>\uac00 \uc801\uc6a9\ub418\uc9c0 \uc54a\uc544 <code>employee_no_plain<\/code>\uacfc <code>name<\/code>\uc774 \uc81c\ub300\ub85c \uc554\ud638\ud654\ub418\uc9c0 \uc54a\uc74c<\/li>\n\n\n\n<li>Admin\uc5d0\uc11c \uc0ad\uc81c \uc2dc <strong>500 Internal Server Error<\/strong> \ubc1c\uc0dd (\uc554\ud638\ud654 \ud544\ub4dc \ucc98\ub9ac \uc911 \uc608\uc678)<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\ud574\uacb0: \uc644\uc131\ub41c fix_beta_users.py \uc2a4\ud06c\ub9bd\ud2b8<\/h3>\n\n\n<style>.kb-image6451_32c674-0d .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<div class=\"wp-block-kadence-image kb-image6451_32c674-0d\"><figure class=\"aligncenter size-medium\"><img decoding=\"async\" width=\"300\" height=\"447\" src=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12-300x447.jpg\" alt=\"Django shell\uc5d0\uc11c fix_beta_users.py \uc2e4\ud589 \ud6c4 50\uac1c \ubca0\ud0c0\ud14c\uc2a4\ud130 \uacc4\uc815 \uc0dd\uc131 \uc644\ub8cc \ub85c\uadf8 \ud654\uba74\" class=\"kb-img wp-image-6453\" srcset=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12-300x447.jpg 300w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12-600x894.jpg 600w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12-768x1144.jpg 768w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12-8x12.jpg 8w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-12.jpg 784w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">\uc544\ub798\ub294 <strong>\uc2e4\uc81c \uc6b4\uc601 \uc911\uc778 \uac80\uc99d\ub41c \uc2a4\ud06c\ub9bd\ud2b8<\/strong>\uc785\ub2c8\ub2e4.<br>\ub450 \ub2e8\uacc4\ub85c \uad6c\uc131\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># fix_beta_users.py (\ucd5c\uc885 \uac80\uc99d \uc644\ub8cc \ubc84\uc804)\nfrom apps.accounts.models import CustomUser, AccountStatus\nfrom apps.accounts.encryption import hmac_of\nfrom django.utils import timezone\n\n# ============================================\n# 1\ub2e8\uacc4: \uc798\ubabb \uc0dd\uc131\ub41c \uacc4\uc815(\ud3c9\ubb38 \uc800\uc7a5) \uc0ad\uc81c\n# ============================================\n# employee_no\uac00 64\uc790\ub9ac hex \ubb38\uc790\uc5f4(0-9a-f)\uc774 \uc544\ub2cc \uacbd\uc6b0 = \ud3c9\ubb38\uc73c\ub85c \uc798\ubabb \uc800\uc7a5\ub41c \uacc4\uc815\nbad = CustomUser.objects.exclude(employee_no__regex=r'^&#91;0-9a-f]{64}$')\nprint(f\"\uc798\ubabb \uc0dd\uc131\ub41c \uacc4\uc815 {bad.count()}\uac1c \uc0ad\uc81c \uc911...\")\nbad.delete()\nprint(\"\uc0ad\uc81c \uc644\ub8cc\\n\")\n\n# ============================================\n# 2\ub2e8\uacc4: \uc62c\ubc14\ub978 \ubc29\uc2dd\uc73c\ub85c \uc7ac\uc0dd\uc131 (HMAC + Fernet + \uc989\uc2dc \uc2b9\uc778)\n# ============================================\nprefixes = &#91;10000000, 20000000, 30000000, 40000000, 50000000]\ncreated = &#91;]\n\nfor prefix in prefixes:\n    for i in range(10):\n        emp_id = str(prefix + i)           # \uc608: 10000000, 10000001 ...\n        hashed = hmac_of(emp_id)           # HMAC-SHA256 \ud574\uc2dc \uc0dd\uc131 (64\uc790 hex)\n\n        # \uc774\ubbf8 \uc874\uc7ac\ud558\ub294 \uacbd\uc6b0 \uac74\ub108\ub6f0\uae30\n        if CustomUser.objects.filter(employee_no=hashed).exists():\n            print(f\"\uc774\ubbf8 \uc874\uc7ac: {emp_id}\")\n            continue\n\n        # CustomUser \uc0dd\uc131 (\uc554\ud638\ud654 \ud544\ub4dc \uc790\ub3d9 \ucc98\ub9ac)\n        user = CustomUser(\n            employee_no=hashed,                    # HMAC \ud574\uc2dc \uc800\uc7a5 (\uc870\ud68c\uc6a9)\n            role='employee',                       # \uae30\ubcf8 \uc5ed\ud560\n            status=AccountStatus.APPROVED,         # \uc989\uc2dc \uc2b9\uc778 \uc0c1\ud0dc\n            approved_at=timezone.now(),            # \uc2b9\uc778 \uc77c\uc2dc \uae30\ub85d\n        )\n        user.employee_no_plain = emp_id   # EncryptedCharField \u2192 \uc790\ub3d9 Fernet \uc554\ud638\ud654\n        user.name = f'\ubca0\ud0c0\ud14c\uc2a4\ud130'         # EncryptedCharField \u2192 \uc790\ub3d9 Fernet \uc554\ud638\ud654\n        user.set_password(emp_id)         # \ube44\ubc00\ubc88\ud638 = \uc0ac\ubc88 (PBKDF2 \ud574\uc2dc)\n        user.save()\n\n        created.append(emp_id)\n        print(f\"\uc0dd\uc131 \uc644\ub8cc: \uc0ac\ubc88 {emp_id} \/ \ube44\ubc88 {emp_id}\")\n\nprint(f\"\\n\ucd1d {len(created)}\uac1c \uacc4\uc815 \uc0dd\uc131 \uc644\ub8cc\")\nprint(\"=\" * 50)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\uc774 \uc2a4\ud06c\ub9bd\ud2b8\uc758 \ud575\uc2ec \ud3ec\uc778\ud2b8<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>hmac_of(emp_id)<\/code> \u2192 <strong>\ubcf5\ud638\ud654 \ubd88\uac00\ub2a5\ud55c \ud574\uc2dc<\/strong>\ub85c \uc800\uc7a5 (\ubcf4\uc548 \ud575\uc2ec)<\/li>\n\n\n\n<li><code>user.employee_no_plain = emp_id<\/code> \u2192 <code>EncryptedCharField<\/code>\uac00 <strong>\uc790\ub3d9\uc73c\ub85c Fernet \uc554\ud638\ud654<\/strong><\/li>\n\n\n\n<li><code>status=AccountStatus.APPROVED<\/code> \u2192 \uad00\ub9ac\uc790 \uc2b9\uc778 \uc5c6\uc774 <strong>\uc989\uc2dc \ub85c\uadf8\uc778 \uac00\ub2a5<\/strong><\/li>\n\n\n\n<li>\uc608\uc678 \ucc98\ub9ac\uc640 \uc911\ubcf5 \uccb4\ud06c\ub85c <strong>\uc548\uc815\uc131 \ud655\ubcf4<\/strong><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\uc2e4\ud589 \ubc29\ubc95 (3\ub2e8\uacc4)<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\uc2a4\ud06c\ub9bd\ud2b8 \ud30c\uc77c \uc0dd\uc131<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   cat &lt;&lt;'EOF' > fix_beta_users.py\n   # \uc704 \uc2a4\ud06c\ub9bd\ud2b8 \uc804\uccb4 \ubd99\uc5ec\ub123\uae30\n   EOF<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Django shell\uc5d0\uc11c \uc2e4\ud589<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   cd \/home\/\n   python manage.py shell &lt; fix_beta_users.py<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\uc0dd\uc131 \ud655\uc778<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   python manage.py shell -c \"\n   from apps.accounts.models import CustomUser\n   print(f'\ucd1d \uacc4\uc815 \uc218: {CustomUser.objects.count()}')\n   print('\uc0d8\ud50c 5\uba85:')\n   for u in CustomUser.objects.all()&#91;:5]:\n       print(f'  {u.employee_no_plain} \/ {u.name} \/ {u.status}')\n   \"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\ud2b8\ub7ec\ube14\uc288\ud305: Admin\uc5d0\uc11c 500 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud560 \ub54c<\/h3>\n\n\n<style>.kb-image6451_026890-bd .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<div class=\"wp-block-kadence-image kb-image6451_026890-bd\"><figure class=\"aligncenter size-large\"><img decoding=\"async\" width=\"600\" height=\"403\" src=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-600x403.jpg\" alt=\"Django Admin 500 Internal Server Error \ud654\uba74\uacfc shell \uba85\ub839\uc5b4\ub85c \ud574\uacb0\ud558\ub294 \uc804\ud6c4 \ube44\uad50 \uc778\ud3ec\uadf8\ub798\ud53d\" class=\"kb-img wp-image-6454\" srcset=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-600x403.jpg 600w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-300x201.jpg 300w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-768x516.jpg 768w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-18x12.jpg 18w, https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13.jpg 1168w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\uc99d\uc0c1<\/strong>:<br>Django Admin (<code>\/admin\/accounts\/customuser\/<\/code>)\uc5d0\uc11c \uacc4\uc815\uc744 \uc120\ud0dd\ud558\uace0 \uc0ad\uc81c \ubc84\ud2bc\uc744 \ub204\ub974\uba74 <strong>500 Internal Server Error<\/strong>\uac00 \ubc1c\uc0dd\ud558\uace0, \ube0c\ub77c\uc6b0\uc800 \ucf58\uc194\uc5d0 <code>POST ... 500<\/code>\uc774 \ucc0d\ud798.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\uc6d0\uc778 \ubd84\uc11d<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Admin\uc758 <code>delete_selected<\/code> \uc561\uc158\uc774 \uc2e4\ud589\ub420 \ub54c, <code>CustomUser<\/code> \ubaa8\ub378\uc758 <code>save()<\/code> \ub610\ub294 \uad00\ub828 <strong>signal<\/strong>\uc774 \ud2b8\ub9ac\uac70\ub428<\/li>\n\n\n\n<li><code>employee_no<\/code>\uac00 \ud3c9\ubb38\uc73c\ub85c \uc800\uc7a5\ub41c \uc0c1\ud0dc\uc5d0\uc11c <code>hmac_of()<\/code>\ub098 \uc554\ud638\ud654 \ub85c\uc9c1\uc774 \uc2e4\ud589\ub418\uba74\uc11c \uc608\uc678 \ubc1c\uc0dd<\/li>\n\n\n\n<li><code>EncryptedCharField<\/code>\uc758 <code>from_db_value<\/code> \/ <code>get_prep_value<\/code>\uc5d0\uc11c \ubcf5\ud638\ud654 \uc2e4\ud328<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud574\uacb0 \ubc29\ubc95<\/strong> (\uac00\uc7a5 \ube60\ub974\uace0 \uc548\uc804):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. \uc798\ubabb \uc0dd\uc131\ub41c \uacc4\uc815\ub9cc \uc815\ud655\ud788 \uc0ad\uc81c (\uc815\uaddc\uc2dd\uc73c\ub85c 64\uc790 hex\uac00 \uc544\ub2cc \uac83\ub9cc)\npython manage.py shell -c \"\nfrom apps.accounts.models import CustomUser\nbad = CustomUser.objects.exclude(employee_no__regex=r'^&#91;0-9a-f]{64}$')\ncount = bad.count()\nprint(f'\uc0ad\uc81c \ub300\uc0c1: {count}\uac1c')\nbad.delete()\nprint(f'{count}\uac1c \uc0ad\uc81c \uc644\ub8cc')\nprint(f'\ub0a8\uc740 \uacc4\uc815: {CustomUser.objects.count()}\uac1c')\n\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\uc774 \uba85\ub839\uc5b4\ub294 <strong>Admin\uc744 \uac70\uce58\uc9c0 \uc54a\uace0<\/strong> ORM\uc73c\ub85c \uc9c1\uc811 \uc0ad\uc81c\ud558\uae30 \ub54c\ubb38\uc5d0 500 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uc0ad\uc81c \ud6c4 \uc704 <code>fix_beta_users.py<\/code>\ub97c \ub2e4\uc2dc \uc2e4\ud589\ud558\uba74 \uae54\ub054\ud558\uac8c \uc7ac\uc0dd\uc131\ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ub85c\uadf8\uc778 \ud14c\uc2a4\ud2b8 \ubc29\ubc95<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\uc0dd\uc131\ub41c \ubca0\ud0c0\ud14c\uc2a4\ud130 \uacc4\uc815 \uc815\ubcf4:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\uc0ac\ubc88 \ubc94\uc704<\/th><th>\ube44\ubc00\ubc88\ud638<\/th><th>\uc0c1\ud0dc<\/th><\/tr><\/thead><tbody><tr><td>10000000 ~ 10000009<\/td><td>\ub3d9\uc77c<\/td><td>APPROVED<\/td><\/tr><tr><td>20000000 ~ 20000009<\/td><td>\ub3d9\uc77c<\/td><td>APPROVED<\/td><\/tr><tr><td>\u2026<\/td><td>\u2026<\/td><td>\u2026<\/td><\/tr><tr><td>50000000 ~ 50000009<\/td><td>\ub3d9\uc77c<\/td><td>APPROVED<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ub85c\uadf8\uc778 \ud654\uba74<\/strong>\uc5d0\uc11c:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\uc0ac\ubc88: <code>10000000<\/code><\/li>\n\n\n\n<li>\ube44\ubc00\ubc88\ud638: <code>10000000<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\ubc14\ub85c \ub85c\uadf8\uc778\ub418\uc5b4 \ud504\ub85c\uc81d\ud2b8\uc758 \ubaa8\ub4e0 \uae30\ub2a5\uc744 \ud14c\uc2a4\ud2b8\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131 \uc2dc \ubcf4\uc548 \uace0\ub824\uc0ac\ud56d \ubc0f \ud655\uc7a5 \ud301<\/h2>\n\n\n<style>.wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f, .wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f[data-kb-block=\"kb-adv-heading6451_2a5b96-5f\"]{font-style:normal;}.wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f[data-kb-block=\"kb-adv-heading6451_2a5b96-5f\"] mark.kt-highlight{font-style:normal;color:#f76a0c;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading6451_2a5b96-5f[data-kb-block=\"kb-adv-heading6451_2a5b96-5f\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h3 class=\"kt-adv-heading6451_2a5b96-5f wp-block-kadence-advancedheading\" data-kb-block=\"kb-adv-heading6451_2a5b96-5f\">\ube44\ubc00\ubc88\ud638 \uc815\ucc45 \uac15\ud654 (\uc2e4\uc81c \uc6b4\uc601 \uc2dc)<\/h3>\n\n\n\n<ol class=\"wp-block-list\"><\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ubca0\ud0c0 \ub2e8\uacc4\uc5d0\uc11c\ub294 <code>\uc0ac\ubc88 = \ube44\ubc00\ubc88\ud638<\/code>\ub85c \ud3b8\uc758\uc131\uc744 \uc6b0\uc120\ud588\uc9c0\ub9cc,<\/li>\n\n\n\n<li>\uc2e4\uc81c \uc11c\ube44\uc2a4\uc5d0\uc11c\ub294 <strong>\uac15\ub825\ud55c \ucd08\uae30 \ube44\ubc00\ubc88\ud638 + \uc774\uba54\uc77c \uc804\uc1a1<\/strong> \ubc29\uc2dd\uc73c\ub85c \ubcc0\uacbd\ud558\uc138\uc694.<\/li>\n<\/ul>\n\n\n<style>.wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46, .wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46[data-kb-block=\"kb-adv-heading6451_f74c80-46\"]{font-style:normal;}.wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46 mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46[data-kb-block=\"kb-adv-heading6451_f74c80-46\"] mark.kt-highlight{font-style:normal;color:#f76a0c;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46 img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading6451_f74c80-46[data-kb-block=\"kb-adv-heading6451_f74c80-46\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h3 class=\"kt-adv-heading6451_f74c80-46 wp-block-kadence-advancedheading\" data-kb-block=\"kb-adv-heading6451_f74c80-46\">\ub300\ub7c9 \uc0dd\uc131 \uc2dc \uc131\ub2a5<\/h3>\n\n\n\n<ol class=\"wp-block-list\"><\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>1000\uba85 \uc774\uc0c1 \uc0dd\uc131\ud560 \ub54c\ub294 <code>bulk_create<\/code> + <code>set_password<\/code>\ub97c \ubcc4\ub3c4 \ucc98\ub9ac\ud558\ub294 \uac83\uc774 \uc88b\uc2b5\ub2c8\ub2e4.<\/li>\n\n\n\n<li><code>django.db.transaction.atomic()<\/code>\uc73c\ub85c \ubb36\uc5b4\uc11c DB \ubd80\ud558\ub97c \uc904\uc774\uc138\uc694.<\/li>\n<\/ul>\n\n\n<style>.wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46, .wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46[data-kb-block=\"kb-adv-heading6451_8c2dc8-46\"]{font-style:normal;}.wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46 mark.kt-highlight, .wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46[data-kb-block=\"kb-adv-heading6451_8c2dc8-46\"] mark.kt-highlight{font-style:normal;color:#f76a0c;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46 img.kb-inline-image, .wp-block-kadence-advancedheading.kt-adv-heading6451_8c2dc8-46[data-kb-block=\"kb-adv-heading6451_8c2dc8-46\"] img.kb-inline-image{width:150px;vertical-align:baseline;}<\/style>\n<h3 class=\"kt-adv-heading6451_8c2dc8-46 wp-block-kadence-advancedheading\" data-kb-block=\"kb-adv-heading6451_8c2dc8-46\">\ub85c\uadf8 \uae30\ub85d<\/h3>\n\n\n\n<ol class=\"wp-block-list\"><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>   import logging\n   logger = logging.getLogger(__name__)\n   logger.info(f\"\ubca0\ud0c0\ud14c\uc2a4\ud130 \uc0dd\uc131 \uc644\ub8cc: {emp_id}\")<\/code><\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>CSV \ud30c\uc77c\ub85c\ubd80\ud130 \uc77d\uc5b4\uc624\uae30<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\uc0ac\ubc88 \ubaa9\ub85d\uc744 Excel\/CSV\ub85c \ubc1b\uc544\uc11c \ucc98\ub9ac\ud558\ub294 \ubc84\uc804\uc73c\ub85c \ud655\uc7a5 \uac00\ub2a5\ud569\ub2c8\ub2e4.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\uacb0\ub860<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131<\/strong>\uc740 \ub2e8\uc21c\ud55c \ud3b8\uc758 \uae30\ub2a5\uc774 \uc544\ub2d9\ub2c8\ub2e4.<br>\ubcf4\uc548(\uc554\ud638\ud654)\uacfc \uc6b4\uc601 \ud6a8\uc728\uc131(\uc790\ub3d9\ud654)\uc744 \ub3d9\uc2dc\uc5d0 \uc7a1\ub294 <strong>\ud544\uc218 \uae30\uc220<\/strong>\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ub0b4\ubd80 \ubcf4\uc548\uc774 \uc911\uc694\ud55c \uc2dc\uc2a4\ud15c\uc5d0\uc11c\ub294<br><strong>HMAC + Fernet<\/strong> \uc870\ud569\uc73c\ub85c <strong>&#8220;DB\uac00 \uc720\ucd9c\ub418\uc5b4\ub3c4 \uc548\uc804\ud55c&#8221;<\/strong> \uad6c\uc870\ub97c \ub9cc\ub4dc\ub294 \uac83\uc774 \ud575\uc2ec\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uc704 \uc2a4\ud06c\ub9bd\ud2b8\ub97c \uadf8\ub300\ub85c \ubcf5\uc0ac\ud574\uc11c \uc0ac\uc6a9\ud558\uc2dc\uace0,<br>Admin 500 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uba74 <strong>shell \uba85\ub839\uc5b4\ub85c \uc9c1\uc811 \uc0ad\uc81c<\/strong>\ud558\ub294 \ud2b8\ub7ec\ube14\uc288\ud305\uc744 \uae30\uc5b5\ud558\uc138\uc694.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\uc774\uc81c 50\uba85\uc758 \ubca0\ud0c0\ud14c\uc2a4\ud130\uac00 \uc900\ube44\ub418\uc5c8\uc2b5\ub2c8\ub2e4.<br>\uc2e4\uc81c \uc0ac\uc6a9\uc790 \ud53c\ub4dc\ubc31\uc744 \ubc1b\uc544 \ud504\ub85c\uc81d\ud2b8\ub97c \ub354 \uc644\uc131\ub3c4 \uc788\uac8c \ub9cc\ub4e4\uc5b4 \ubcf4\uc138\uc694!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ucd9c\ucc98 \ubc0f \ucc38\uace0 \uc790\ub8cc<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Django \uacf5\uc2dd \ubb38\uc11c: Customizing authentication (AbstractBaseUser, USERNAME_FIELD)<\/li>\n\n\n\n<li>Django \uacf5\uc2dd \ubb38\uc11c: django.db.models.query.QuerySet.exclude() \ubc0f regex lookup<\/li>\n\n\n\n<li>cryptography \ub77c\uc774\ube0c\ub7ec\ub9ac \ubb38\uc11c: Fernet (symmetric encryption)<\/li>\n\n\n\n<li>OWASP: Password Storage Cheat Sheet (PBKDF2 \uad8c\uc7a5)<\/li>\n\n\n\n<li>Django Admin actions \uc18c\uc2a4 \ucf54\ub4dc \ubd84\uc11d (delete_selected \ub3d9\uc791 \ubc29\uc2dd)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131\uc740 \uc5b4\ub5bb\uac8c \ud560 \uc218 \uc788\uc744\uae4c\uc694? \ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c&#8230;<\/p>","protected":false},"author":3,"featured_media":6454,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[1,3],"tags":[1573,1571,1574,1570,1572,1575,1576],"class_list":["post-6451","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","category-python-coding","tag-admin-500-","tag-django-customuser-","tag-django-shell-","tag-django---","tag-hmac-fernet","tag-karis-","tag-1576"],"taxonomy_info":{"category":[{"value":1,"label":"Uncategorized"},{"value":3,"label":"\ud30c\uc774\uc36c(Python)"}],"post_tag":[{"value":1573,"label":"Admin 500 \uc5d0\ub7ec"},{"value":1571,"label":"Django CustomUser \uc554\ud638\ud654"},{"value":1574,"label":"Django shell \uc2a4\ud06c\ub9bd\ud2b8"},{"value":1570,"label":"Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131"},{"value":1572,"label":"HMAC Fernet"},{"value":1575,"label":"KARIS \ud504\ub85c\uc81d\ud2b8"},{"value":1576,"label":"\ubca0\ud0c0\ud14c\uc2a4\ud2b8 \uacc4\uc815 \uc0dd\uc131"}]},"featured_image_src_large":["https:\/\/secondlife.lol\/wp-content\/uploads\/2026\/06\/image-13-600x403.jpg",600,403,true],"author_info":{"display_name":"TERE","author_link":"https:\/\/secondlife.lol\/ja\/author\/tere\/"},"comment_info":0,"category_info":[{"term_id":1,"name":"Uncategorized","slug":"uncategorized","term_group":0,"term_taxonomy_id":1,"taxonomy":"category","description":"","parent":0,"count":2,"filter":"raw","cat_ID":1,"category_count":2,"category_description":"","cat_name":"Uncategorized","category_nicename":"uncategorized","category_parent":0},{"term_id":3,"name":"\ud30c\uc774\uc36c(Python)","slug":"python-coding","term_group":0,"term_taxonomy_id":3,"taxonomy":"category","description":"","parent":20,"count":119,"filter":"raw","cat_ID":3,"category_count":119,"category_description":"","cat_name":"\ud30c\uc774\uc36c(Python)","category_nicename":"python-coding","category_parent":20}],"tag_info":[{"term_id":1573,"name":"Admin 500 \uc5d0\ub7ec","slug":"admin-500-%ec%97%90%eb%9f%ac","term_group":0,"term_taxonomy_id":1573,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1571,"name":"Django CustomUser \uc554\ud638\ud654","slug":"django-customuser-%ec%95%94%ed%98%b8%ed%99%94","term_group":0,"term_taxonomy_id":1571,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1574,"name":"Django shell \uc2a4\ud06c\ub9bd\ud2b8","slug":"django-shell-%ec%8a%a4%ed%81%ac%eb%a6%bd%ed%8a%b8","term_group":0,"term_taxonomy_id":1575,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1570,"name":"Django \ubca0\ud0c0\ud14c\uc2a4\ud130 \uc77c\uad04 \uc0dd\uc131","slug":"django-%eb%b2%a0%ed%83%80%ed%85%8c%ec%8a%a4%ed%84%b0-%ec%9d%bc%ea%b4%84-%ec%83%9d%ec%84%b1","term_group":0,"term_taxonomy_id":1570,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1572,"name":"HMAC Fernet","slug":"hmac-fernet","term_group":0,"term_taxonomy_id":1572,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1575,"name":"KARIS \ud504\ub85c\uc81d\ud2b8","slug":"karis-%ed%94%84%eb%a1%9c%ec%a0%9d%ed%8a%b8","term_group":0,"term_taxonomy_id":1574,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":1576,"name":"\ubca0\ud0c0\ud14c\uc2a4\ud2b8 \uacc4\uc815 \uc0dd\uc131","slug":"%eb%b2%a0%ed%83%80%ed%85%8c%ec%8a%a4%ed%8a%b8-%ea%b3%84%ec%a0%95-%ec%83%9d%ec%84%b1","term_group":0,"term_taxonomy_id":1576,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"}],"_links":{"self":[{"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/posts\/6451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/comments?post=6451"}],"version-history":[{"count":2,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/posts\/6451\/revisions"}],"predecessor-version":[{"id":6456,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/posts\/6451\/revisions\/6456"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/media\/6454"}],"wp:attachment":[{"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/media?parent=6451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/categories?post=6451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secondlife.lol\/ja\/wp-json\/wp\/v2\/tags?post=6451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}