{"id":5406,"date":"2025-12-21T15:52:08","date_gmt":"2025-12-21T06:52:08","guid":{"rendered":"https:\/\/secondlife.lol\/?p=5406"},"modified":"2025-12-21T15:52:09","modified_gmt":"2025-12-21T06:52:09","slug":"opencode-django-nextjs-code-examples","status":"publish","type":"post","link":"https:\/\/secondlife.lol\/en\/opencode-django-nextjs-code-examples\/","title":{"rendered":"OpenCode \uc2e4\uc804 \uc608\uc2dc: Django\u00b7Next.js \ucf54\ub4dc\uc640 \ud504\ub86c\ud504\ud2b8 \uc138\ud2b8"},"content":{"rendered":"<style>.kb-image5406_1ec3bc-0e .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<div class=\"wp-block-kadence-image kb-image5406_1ec3bc-0e\"><figure class=\"aligncenter size-large\"><img decoding=\"async\" width=\"600\" height=\"414\" src=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-600x414.jpg\" alt=\"opencode django next.js project image\" class=\"kb-img wp-image-5698\" srcset=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-600x414.jpg 600w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-300x207.jpg 300w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-768x530.jpg 768w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-18x12.jpg 18w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1.jpg 988w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><figcaption>(OpenCode Project QHSE System)<\/figcaption><\/figure><\/div>\n\n\n\n<p><a href=\"https:\/\/secondlife.lol\/en\/opencode-sme-qhse-system\/\">\uc55e\uc120 \uae00<\/a>\ub4e4\uc5d0\uc11c \uac1c\ub150\u00b7\uc124\uc815\u00b7\uc6cc\ud06c\ud50c\ub85c\uc6b0\ub97c \ucda9\ubd84\ud788 \uc0b4\ud3b4\ubd24\uc73c\ub2c8,<br>\uc774\ubc88 \ud3b8\ubd80\ud130\ub294 \uc815\ub9d0 \u201c\ubc14\ub85c \ubd99\uc5ec \ub123\uace0 \ub3cc\ub824\ubcfc \uc218 \uc788\ub294 \uc218\uc900\u201d\uc73c\ub85c \ub0b4\ub824\uac00 \ubcf4\uaca0\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p>\uc624\ub298 \ubaa9\ud45c\ub294 \ub2e8\uc21c\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/www.djangoproject.com\/\" target=\"_blank\" rel=\"noopener\">Django<\/a> Backend<\/strong>\n<ul class=\"wp-block-list\">\n<li>QHSE\uc6a9 \ud575\uc2ec \ubaa8\ub378 2\uac1c<\/li>\n\n\n\n<li>REST API \ubf08\ub300<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Next.js \ud504\ub860\ud2b8\uc5d4\ub4dc<\/strong>\n<ul class=\"wp-block-list\">\n<li>\ub9e4\uc6b0 \ub2e8\uc21c\ud55c QHSE \ub300\uc2dc\ubcf4\ub4dc \ud398\uc774\uc9c0<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\uc774 \ub458\uc744 <strong>\ud130\ubbf8\ub110\uc5d0\uc11c AI\uc640 \uac19\uc774 \ub9cc\ub4dc\ub294 \ud504\ub86c\ud504\ud2b8 \uc138\ud2b8<\/strong>\uae4c\uc9c0 \uc81c\uacf5<\/li>\n<\/ol>\n\n\n\n<p>\uadf8\ub7ec\ub2c8\uae4c \uc774 \uae00\uc740<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u201c\ucf54\ub4dc + \ud504\ub86c\ud504\ud2b8\ub97c \uadf8\ub300\ub85c \uac00\uc838\uac00\uc11c \ub0b4 \ud504\ub85c\uc81d\ud2b8 \uc774\ub984\ub9cc \ubc14\uafd4\uc11c \ub3cc\ub824\ubcf4\ub294\u201d<br>\uc2e4\uc2b5 \uc2a4\ud15d\uc774\ub77c\uace0 \ubcf4\uc2dc\uba74 \ub429\ub2c8\ub2e4 \ud83d\ude0a<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">1) OpenCode-Django: QHSE \ud575\uc2ec \ubaa8\ub378 + API \ubf08\ub300<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 \uc608\uc2dc \ub3c4\uba54\uc778 \uc815\ub9ac<\/h3>\n\n\n\n<p>\ud611\ub825\uc0ac(SME) \uc785\uc7a5\uc5d0\uc11c <strong>\ucd5c\uc18c \ub2e8\uc704 \ub370\uc774\ud130<\/strong>\ub97c \ub450 \uac1c\ub9cc \uc7a1\uaca0\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>Vendor<\/code> \u2013 \ud611\ub825\uc5c5\uccb4(\uc790\uae30 \ud68c\uc0ac \ud639\uc740 \ud558\ub3c4\uae09\uc0ac)<\/li>\n\n\n\n<li><code>QhseRecord<\/code> \u2013 \ud488\uc9c8\/\uc548\uc804\/\ud658\uacbd \uad00\ub828 \uc774\ubca4\ud2b8\u00b7\uc870\uce58 \uae30\ub85d<\/li>\n<\/ol>\n\n\n\n<p>\uc2e4\uc81c\ub85c\ub294 \ud6e8\uc52c \ub354 \ucabc\uac1c\uc57c \ud558\uc9c0\ub9cc, <strong>MVP \ud559\uc2b5\uc6a9 \uc608\uc2dc<\/strong>\ub85c\ub294 \ucda9\ubd84\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 Django \ubaa8\ub378 \uc608\uc2dc (<code>vendors\/models.py<\/code>)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.db import models\n\n\nclass Vendor(models.Model):\n    \"\"\"\n    \uc6d0\uc804 \uacf5\uae09\ub9dd\uc5d0 \ucc38\uc5ec\ud558\ub294 \ud611\ub825\uc5c5\uccb4 \uc815\ubcf4\n    \"\"\"\n    name = models.CharField(max_length=255)\n    business_id = models.CharField(\n        max_length=50,\n        unique=True,\n        help_text=\"\uc0ac\uc5c5\uc790\ub4f1\ub85d\ubc88\ud638 \ub610\ub294 \ub0b4\ubd80 \ucf54\ub4dc\"\n    )\n    iso_19443_certified = models.BooleanField(default=False)\n    iso_9001_certified = models.BooleanField(default=True)\n    country = models.CharField(max_length=100, default=\"Korea\")\n    contact_person = models.CharField(max_length=100, blank=True)\n    contact_email = models.EmailField(blank=True)\n    note = models.TextField(blank=True)\n\n    def __str__(self):\n        return f\"{self.name} ({self.business_id})\"\n\n\nclass QhseRecord(models.Model):\n    \"\"\"\n    \ud488\uc9c8\/\ubcf4\uac74\/\uc548\uc804\/\ud658\uacbd \uad00\ub828 \uc774\ubca4\ud2b8\u00b7\uc870\uce58 \uae30\ub85d\n    \"\"\"\n    QHSE_TYPE_CHOICES = &#91;\n        (\"Q\", \"Quality\"),\n        (\"H\", \"Health &amp; Safety\"),\n        (\"E\", \"Environment\"),\n    ]\n\n    vendor = models.ForeignKey(\n        Vendor, on_delete=models.CASCADE,\n        related_name=\"qhse_records\"\n    )\n    record_type = models.CharField(max_length=1, choices=QHSE_TYPE_CHOICES)\n    title = models.CharField(max_length=255)\n    description = models.TextField()\n    occurred_at = models.DateField()\n    corrective_action = models.TextField(blank=True)\n    is_closed = models.BooleanField(default=False)\n    created_at = models.DateTimeField(auto_now_add=True)\n\n    class Meta:\n        ordering = &#91;\"-occurred_at\", \"-created_at\"]\n\n    def __str__(self):\n        return f\"&#91;{self.get_record_type_display()}] {self.title}\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 DRF Serializer &amp; View \uc608\uc2dc (<code>vendors\/api.py<\/code>)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>from rest_framework import serializers, viewsets\nfrom .models import Vendor, QhseRecord\n\n\nclass VendorSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Vendor\n        fields = \"__all__\"\n\n\nclass QhseRecordSerializer(serializers.ModelSerializer):\n    vendor_name = serializers.CharField(\n        source=\"vendor.name\", read_only=True\n    )\n\n    class Meta:\n        model = QhseRecord\n        fields = \"__all__\"\n\n\nclass VendorViewSet(viewsets.ModelViewSet):\n    queryset = Vendor.objects.all()\n    serializer_class = VendorSerializer\n\n\nclass QhseRecordViewSet(viewsets.ModelViewSet):\n    queryset = QhseRecord.objects.select_related(\"vendor\")\n    serializer_class = QhseRecordSerializer<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.4 URL \ub77c\uc6b0\ud305 \uc608\uc2dc (<code>core\/urls.py<\/code>)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.contrib import admin\nfrom django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\nfrom vendors.api import VendorViewSet, QhseRecordViewSet\n\nrouter = DefaultRouter()\nrouter.register(r\"vendors\", VendorViewSet)\nrouter.register(r\"qhse-records\", QhseRecordViewSet)\n\nurlpatterns = &#91;\n    path(\"admin\/\", admin.site.urls),\n    path(\"api\/\", include(router.urls)),\n]<\/code><\/pre>\n\n\n\n<p>\uc5ec\uae30\uae4c\uc9c0\uac00 <strong>\ubc31\uc5d4\ub4dc \ucd5c\uc18c \ubf08\ub300<\/strong>.<br>\uc774\uc81c \ud504\ub860\ud2b8\uc5d0\uc11c \uc774 API\ub97c \uc368\uc11c \uac04\ub2e8\ud55c \ub300\uc2dc\ubcf4\ub4dc\ub97c \ub9cc\ub4e4\uc5b4 \ubcfc\uac8c\uc694.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2) Next.js: QHSE \ub300\uc2dc\ubcf4\ub4dc \ud398\uc774\uc9c0 \ubf08\ub300<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\uae30\uc900: <code>app<\/code> router, TypeScript \uc0ac\uc6a9, \uc544\uc8fc \uc2ec\ud50c\ud55c UI<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">2.1 \uac04\ub2e8\ud55c fetch \uc720\ud2f8 (<code>lib\/api.ts<\/code>)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ lib\/api.ts\nexport async function fetchVendors() {\n  const res = await fetch(\"http:\/\/localhost:8000\/api\/vendors\/\");\n  if (!res.ok) {\n    throw new Error(\"Failed to fetch vendors\");\n  }\n  return res.json();\n}\n\nexport async function fetchQhseRecords() {\n  const res = await fetch(\"http:\/\/localhost:8000\/api\/qhse-records\/\");\n  if (!res.ok) {\n    throw new Error(\"Failed to fetch QHSE records\");\n  }\n  return res.json();\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.2 QHSE \ub300\uc2dc\ubcf4\ub4dc \ud398\uc774\uc9c0 (<code>app\/qhse\/page.tsx<\/code>)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ app\/qhse\/page.tsx\nimport { fetchVendors, fetchQhseRecords } from &quot;@\/lib\/api&quot;;\n\nexport default async function QhseDashboardPage() {\n  const [vendors, records] = await Promise.all([\n    fetchVendors(),\n    fetchQhseRecords(),\n  ]);\n\n  return (\n    &lt;main classname=&quot;px-6 py-8 space-y-8&quot;&gt;\n      &lt;section&gt;\n        &lt;h1 classname=&quot;text-2xl font-bold&quot;&gt;QHSE Dashboard&lt;\/h1&gt;\n        &lt;p classname=&quot;text-sm text-gray-600 mt-2&quot;&gt;\n          \uc6d0\uc804 \ud611\ub825\uc5c5\uccb4\uc758 \ud488\uc9c8&middot;\uc548\uc804&middot;\ud658\uacbd \uc774\uc288 \ud604\ud669\uc744 \ud55c\ub208\uc5d0 \ubcfc \uc218 \uc788\ub294 \uac04\ub2e8\ud55c \ub300\uc2dc\ubcf4\ub4dc\uc785\ub2c8\ub2e4.\n        &lt;\/p&gt;\n      &lt;\/section&gt;\n\n      &lt;section classname=&quot;grid gap-4 md:grid-cols-3&quot;&gt;\n        &lt;div classname=&quot;rounded-xl border p-4&quot;&gt;\n          &lt;h2 classname=&quot;text-sm font-semibold mb-2&quot;&gt;\ucd1d \ud611\ub825\uc5c5\uccb4 \uc218&lt;\/h2&gt;\n          &lt;p classname=&quot;text-3xl font-bold&quot;&gt;{vendors.length}&lt;\/p&gt;\n        &lt;\/div&gt;\n        &lt;div classname=&quot;rounded-xl border p-4&quot;&gt;\n          &lt;h2 classname=&quot;text-sm font-semibold mb-2&quot;&gt;QHSE \uae30\ub85d \uc218&lt;\/h2&gt;\n          &lt;p classname=&quot;text-3xl font-bold&quot;&gt;{records.length}&lt;\/p&gt;\n        &lt;\/div&gt;\n        &lt;div classname=&quot;rounded-xl border p-4&quot;&gt;\n          &lt;h2 classname=&quot;text-sm font-semibold mb-2&quot;&gt;\ubbf8\uc885\uacb0 \uc774\uc288 \uc218&lt;\/h2&gt;\n          &lt;p classname=&quot;text-3xl font-bold&quot;&gt;\n            {records.filter((r: any) =&gt; !r.is_closed).length}\n          &lt;\/p&gt;\n        &lt;\/div&gt;\n      &lt;\/section&gt;\n\n      &lt;section classname=&quot;space-y-3&quot;&gt;\n        &lt;h2 classname=&quot;text-lg font-semibold&quot;&gt;\ucd5c\uadfc QHSE \uae30\ub85d&lt;\/h2&gt;\n        &lt;div classname=&quot;space-y-2&quot;&gt;\n          {records.slice(0, 5).map((r: any) =&gt; (\n            &lt;div\n              key=&quot;{r.id}&quot;              classname=&quot;flex flex-col md:flex-row md:items-center justify-between rounded-lg border p-3&quot;\n            &gt;\n              &lt;div&gt;\n                &lt;p classname=&quot;text-sm font-semibold&quot;&gt;\n                  [{r.record_type}] {r.title}\n                &lt;\/p&gt;\n                &lt;p classname=&quot;text-xs text-gray-600 mt-1&quot;&gt;\n                  {r.vendor_name} &middot; {r.occurred_at}\n                &lt;\/p&gt;\n              &lt;\/div&gt;\n              &lt;span\n                classname=&quot;{`mt-2&quot; md:mt-0 inline-flex items-center rounded-full px-3 py-1 text-xs font-medium ${\n r.is_closed ? &quot;bg-green-100 text-green-700&quot; : &quot;bg-amber-100 text-amber-700&quot;\n }`}\n&gt;\n                {r.is_closed ? &quot;\uc870\uce58 \uc644\ub8cc&quot; : &quot;\uc9c4\ud589 \uc911&quot;}\n              &lt;\/span&gt;\n            &lt;\/div&gt;\n          ))}\n        &lt;\/div&gt;\n      &lt;\/section&gt;\n    &lt;\/main&gt;\n  );\n}\n<\/code><\/pre>\n\n\n\n<p>\uc774 \uc815\ub3c4\uba74<br>\u201cQHSE \uc2dc\uc2a4\ud15c \ub300\uc2dc\ubcf4\ub4dc\uc758 <strong>\uc544\uc8fc \uc791\uc740 \ud504\ub85c\ud1a0\ud0c0\uc785<\/strong>\u201d\uc744<br>\ube60\ub974\uac8c \ub9cc\ub4e4\uc5b4 \ubcf4\ub294 \ub370 \ucda9\ubd84\ud55c \ubf08\ub300\uac00 \ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3) \ud130\ubbf8\ub110\uc5d0\uc11c \uc774\uac78 \uac19\uc774 \ub9cc\ub4dc\ub294 \ud504\ub86c\ud504\ud2b8 \uc138\ud2b8<\/h2>\n\n\n<style>.kb-image5406_499958-b4 .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<div class=\"wp-block-kadence-image kb-image5406_499958-b4\"><figure class=\"aligncenter size-large\"><img decoding=\"async\" width=\"600\" height=\"458\" src=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3-600x458.jpg\" alt=\"OpenCode Project QHSE System \uac1c\ubc1c \uc774\ubbf8\uc9c0\" class=\"kb-img wp-image-5701\" srcset=\"https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3-600x458.jpg 600w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3-300x229.jpg 300w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3-768x586.jpg 768w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3-16x12.jpg 16w, https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-3.jpg 986w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure><\/div>\n\n\n\n<p>\uc774\uc81c \uc9c4\uc9dc \ud575\uc2ec\uc778 \u201c\uc5b4\ub5bb\uac8c \ub9d0\ud558\uba74\uc11c \uac19\uc774 \ub9cc\ub4e4 \uac83\uc778\uac00\u201d\ub97c \uc815\ub9ac\ud574 \ubcf4\uaca0\uc2b5\ub2c8\ub2e4.<br>\uc544\ub798 \ud504\ub86c\ud504\ud2b8\ub4e4\uc740 \uadf8\ub300\ub85c \ubcf5\ubd99\ud574\uc11c \uc368\ub3c4 \ub418\uace0,<br>\ub2f9\uc2e0 \uc0c1\ud669\uc5d0 \ub9de\uac8c \uc870\uae08\ub9cc \uace0\uccd0\uc11c \uc4f0\uba74 \ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 \uc124\uacc4 \ub2e8\uacc4 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ask \uc911\uc18c\uae30\uc5c5\uc744 \uc704\ud55c Nuclear QHSE &amp; DocHub \uc2dc\uc2a4\ud15c\uc744 \ub9cc\ub4e4\uace0 \uc2f6\uc5b4.\n\ubaa8\ub378\uc740 Vendor\uc640 QhseRecord \ub450 \uac1c\ub85c \uc2dc\uc791\ud558\ub824\uace0 \ud558\ub294\ub370,\n\uac01 \ud544\ub4dc\uc5d0 \uc5b4\ub5a4 \uac12\uc774 \ub4e4\uc5b4\uac00\ub294\uc9c0 \uc608\uc2dc\uc640 \ud568\uaed8 \uc124\uba85\ud574\uc918.\nDjango \ubaa8\ub378 \uad00\uc810\uc5d0\uc11c \uc124\uacc4\ud574\uc918.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 Django \ubaa8\ub378\/\uc2dc\ub9ac\uc5bc\ub77c\uc774\uc800 \uc0dd\uc131 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ask \ubc29\uae08 \uc124\uba85\ud55c \ub0b4\uc6a9\uc744 \uae30\ubc18\uc73c\ub85c\nvendors\/models.py \uc548\uc5d0 Vendor\uc640 QhseRecord \ubaa8\ub378\uc744 \ub9cc\ub4e4\uc5b4\uc918.\nISO 19443 \uc778\uc99d \uc5ec\ubd80\ub97c \ub098\ud0c0\ub0b4\ub294 \ud544\ub4dc\ub3c4 \ud3ec\ud568\ud574\uc918.\n\uadf8\ub9ac\uace0 DRF Serializer\uc640 ViewSet \ucf54\ub4dc \ucd08\uc548\ub3c4 \uac19\uc774 \uc791\uc131\ud574\uc918.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.3 URL \ub77c\uc6b0\ud305 \uc0dd\uc131 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ask DefaultRouter\ub97c \uc0ac\uc6a9\ud558\ub294 Django REST Framework \ub77c\uc6b0\ud305 \uc608\uc2dc\ub97c\ncore\/urls.py \uae30\uc900\uc73c\ub85c \uc791\uc131\ud574\uc918.\napi\/vendors\/, api\/qhse-records\/ \ub450 \uc5d4\ub4dc\ud3ec\uc778\ud2b8\ub97c \ub178\ucd9c\ud558\uace0 \uc2f6\uc5b4.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.4 Next.js fetch \uc720\ud2f8 \uc0dd\uc131 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ask Django API\uc5d0\uc11c vendors\uc640 qhse-records\ub97c \uac00\uc838\uc624\ub294\nNext.js\uc6a9 fetch \uc720\ud2f8 \ud568\uc218\ub97c \ub9cc\ub4e4\uc5b4\uc918.\nTypeScript\ub85c \uc791\uc131\ud558\uace0, lib\/api.ts\uc5d0 \ub4e4\uc5b4\uac08 \uc608\uc2dc\ub85c \ub9cc\ub4e4\uc5b4\uc918.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.5 QHSE \ub300\uc2dc\ubcf4\ub4dc \ud398\uc774\uc9c0 \uc0dd\uc131 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@frontend-ui-ux-engineer\n\uac04\ub2e8\ud55c QHSE Dashboard \ud398\uc774\uc9c0\ub97c \ub9cc\ub4e4\uace0 \uc2f6\uc5b4.\napp\/qhse\/page.tsx \ud30c\uc77c\uc744 \uae30\uc900\uc73c\ub85c,\n\ucd1d \ud611\ub825\uc5c5\uccb4 \uc218, QHSE \uae30\ub85d \uc218, \ubbf8\uc885\uacb0 \uc774\uc288 \uc218\ub97c \ubcf4\uc5ec\uc8fc\ub294\n\uc2ec\ud50c\ud55c \uce74\ub4dc UI\uc640 \ucd5c\uadfc 5\uac1c \uae30\ub85d \ub9ac\uc2a4\ud2b8\ub97c \ud3ec\ud568\ud574\uc918.\nTailwind\ub97c \uc0ac\uc6a9\ud574\uc11c \uae54\ub054\ud558\uac8c \uc791\uc131\ud574\uc918.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.6 \ub9ac\ud329\ud130\ub9c1 &amp; \uac1c\uc120 \uc694\uccad \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ask \uc9c0\uae08 \uc791\uc131\ub41c QHSE \ub300\uc2dc\ubcf4\ub4dc \ucf54\ub4dc \uc804\uccb4\ub97c \ub9ac\ubdf0\ud574\uc918.\n\ucd08\uae09 \uac1c\ubc1c\uc790\uac00 \uc54c\uc544\ub450\uba74 \uc88b\uc740 \ub9ac\ud329\ud130\ub9c1 \ud3ec\uc778\ud2b8\ub97c 3\uac1c\ub9cc \ubf51\uc544\uc11c \uc124\uba85\ud574\uc8fc\uace0,\n\uadf8 \uc911 1\ubc88 \ud56d\ubaa9\uc744 \uc2e4\uc81c \ucf54\ub4dc\ub85c \ubc18\uc601\ud55c \ubc84\uc804\uc744 \uc81c\uc548\ud574\uc918.<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.7 \ubb38\uc11c\ud654 \ud504\ub86c\ud504\ud2b8<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@document\nNuclear QHSE &amp; DocHub\uc758 \uccab \ubc84\uc804\uc744 \uc704\ud55c README.md \ucd08\uc548\uc744 \uc791\uc131\ud574\uc918.\n- \ud504\ub85c\uc81d\ud2b8 \uc18c\uac1c\n- \uc0ac\uc6a9 \uae30\uc220 \uc2a4\ud0dd\n- Django\/Next.js \uc2e4\ud589 \ubc29\ubc95\n- QHSE \ub300\uc2dc\ubcf4\ub4dc \uae30\ub2a5 \uc124\uba85\n\uc704 4\uac00\uc9c0\ub97c \ud3ec\ud568\ud574\uc918.<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Organize<\/h2>\n\n\n\n<p>\uc774\ubc88 \ud3ec\uc2a4\ud2b8\uc740 \ub9d0 \uadf8\ub300\ub85c \u201c\ucf54\ub4dc+\ud504\ub86c\ud504\ud2b8 \ud328\ud0a4\uc9c0\u201d\uc600\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Django\ub85c <strong>Vendor \/ QhseRecord \ubaa8\ub378\uacfc REST API<\/strong>\ub97c \ub9cc\ub4e4\uace0<\/li>\n\n\n\n<li>Next.js\ub85c <strong>QHSE \ub300\uc2dc\ubcf4\ub4dc \ud398\uc774\uc9c0 \ubf08\ub300<\/strong>\ub97c \uad6c\uc131\ud55c \ub4a4<\/li>\n\n\n\n<li>\ud130\ubbf8\ub110\uc5d0\uc11c AI\uc640 \ud568\uaed8 \uc804\uccb4\ub97c \uc124\uacc4\u00b7\ub9ac\ud329\ud130\ub9c1\u00b7\ubb38\uc11c\ud654\ud558\ub294 <strong>\ub300\ud654 \ud328\ud134<\/strong>\uae4c\uc9c0 \ubd99\uc5ec\ubcf4\uc558\uc8e0.<\/li>\n<\/ul>\n\n\n\n<p>\uc774\uc81c \uc5ec\uae30\uc11c\ubd80\ud130\ub294<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ubaa8\ub378\uc744 \ub354 \uc138\ubd84\ud654\ud558\uace0 (\uad50\uc721, \uc778\uc99d\uc11c, \ud504\ub85c\uc81d\ud2b8, \uac10\uc0ac \ub4f1)<\/li>\n\n\n\n<li>\ub300\uc2dc\ubcf4\ub4dc\ub97c \ub354 \ud48d\uc131\ud558\uac8c \ub9cc\ub4e4\uace0<\/li>\n\n\n\n<li>\ub9ac\ud3ec\ud2b8(PDF\/Zip) \uc0dd\uc131 \uae30\ub2a5\uae4c\uc9c0 \ud655\uc7a5\ud574 \ub098\uac00\uba74<\/li>\n<\/ul>\n\n\n\n<p>\uc2e4\uc81c\ub85c SME\ub4e4\uc774 \u201c\ub3c8 \ub0b4\uace0 \uc4f8 \ubc95\ud55c\u201d \uc2dc\uc2a4\ud15c\uc73c\ub85c\ub3c4 \ucda9\ubd84\ud788 \uc131\uc7a5\uc2dc\ud0ac \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p>\uc6d0\ud558\uc2dc\uba74 <strong>12\ud3b8<\/strong>\uc5d0\uc11c\ub294 \u201cQHSE \ub9ac\ud3ec\ud2b8 \uc790\ub3d9 \uc0dd\uc131\u201d\uc744 \uc8fc\uc81c\ub85c \uacc4\uc18d \uc774\uc5b4\uac00 \ubcfc\uac8c\uc694.<\/p>","protected":false},"excerpt":{"rendered":"<p>\uc55e\uc120 \uae00\ub4e4\uc5d0\uc11c \uac1c\ub150\u00b7\uc124\uc815\u00b7\uc6cc\ud06c\ud50c\ub85c\uc6b0\ub97c \ucda9\ubd84\ud788 \uc0b4\ud3b4\ubd24\uc73c\ub2c8,\uc774\ubc88 \ud3b8\ubd80\ud130\ub294 \uc815\ub9d0 \u201c\ubc14\ub85c \ubd99\uc5ec \ub123\uace0&#8230;<\/p>","protected":false},"author":3,"featured_media":5698,"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],"tags":[],"class_list":["post-5406","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"taxonomy_info":{"category":[{"value":1,"label":"Uncategorized"}]},"featured_image_src_large":["https:\/\/secondlife.lol\/wp-content\/uploads\/2025\/12\/image-40-1-600x414.jpg",600,414,true],"author_info":{"display_name":"TERE","author_link":"https:\/\/secondlife.lol\/en\/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":1,"filter":"raw","cat_ID":1,"category_count":1,"category_description":"","cat_name":"Uncategorized","category_nicename":"uncategorized","category_parent":0}],"tag_info":false,"_links":{"self":[{"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/posts\/5406","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/comments?post=5406"}],"version-history":[{"count":4,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/posts\/5406\/revisions"}],"predecessor-version":[{"id":5703,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/posts\/5406\/revisions\/5703"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/media\/5698"}],"wp:attachment":[{"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/media?parent=5406"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/categories?post=5406"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secondlife.lol\/en\/wp-json\/wp\/v2\/tags?post=5406"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}