Spaces:
Running
Running
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Any | |
| CONTENT_STATUSES = ["generated", "reviewed", "approved", "rejected"] | |
| class RoadmapStep: | |
| title: str | |
| description: str | |
| level: str = "๊ธฐ์ด" | |
| class LessonCard: | |
| id: str | |
| track: str | |
| certification: str | |
| title: str | |
| summary: str | |
| example: str | |
| common_mistake: str | |
| keywords: list[str] | |
| source_id: str | |
| status: str = "approved" | |
| details: tuple[str, ...] = () | |
| level: str = "์ ๋ฌธ" | |
| related_practices: tuple[str, ...] = () | |
| class LabQuiz: | |
| id: str | |
| lesson_id: str | |
| track: str | |
| question_type: str | |
| question: str | |
| options: list[str] | |
| answer: str | |
| explanation: str | |
| difficulty: str = "easy" | |
| source_id: str = "manual-linux-basic" | |
| status: str = "approved" | |
| class PracticeTask: | |
| id: str | |
| track: str | |
| title: str | |
| task_description: str | |
| expected_command: str | |
| grading_conditions: list[str] | |
| hint: str | |
| explanation: str | |
| difficulty: str = "easy" | |
| status: str = "approved" | |
| takeaway: str = "" | |
| TRACKS = [ | |
| { | |
| "id": "linux", | |
| "name": "Linux", | |
| "description": "ํฐ๋ฏธ๋, ํ์ผ, ๊ถํ, ํ๋ก์ธ์ค, ์๋น์ค ๊ด๋ฆฌ ๊ธฐ์ด", | |
| "priority": 1, | |
| "is_active": True, | |
| }, | |
| { | |
| "id": "azure", | |
| "name": "Azure", | |
| "description": "Azure ๊ด๋ฆฌ์ ์ญํ ๊ณผ AZ-104 ์ํ ๋๋น", | |
| "priority": 2, | |
| "is_active": True, | |
| }, | |
| { | |
| "id": "tool_docs", | |
| "name": "Tool Docs", | |
| "description": "Ollama, LangChain, Streamlit, Hugging Face ๊ณต์ ๋ฌธ์ ๊ธฐ๋ฐ ํ์ต", | |
| "priority": 3, | |
| "is_active": True, | |
| }, | |
| {"id": "git", "name": "Git", "description": "๋ฒ์ ๊ด๋ฆฌ ๊ธฐ๋ณธ๊ธฐ", "priority": 4, "is_active": False}, | |
| {"id": "python", "name": "Python", "description": "๊ธฐ์ด ๋ฌธ๋ฒ๊ณผ ์๋ํ", "priority": 5, "is_active": False}, | |
| {"id": "sql", "name": "SQL", "description": "์กฐํ์ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง ๊ธฐ์ด", "priority": 6, "is_active": False}, | |
| {"id": "docker", "name": "Docker", "description": "์ปจํ ์ด๋ ์ด๋ฏธ์ง์ ์คํ ํ๊ฒฝ", "priority": 7, "is_active": False}, | |
| {"id": "kubernetes", "name": "Kubernetes", "description": "์ปจํ ์ด๋ ์ค์ผ์คํธ๋ ์ด์ ๊ธฐ์ด", "priority": 8, "is_active": False}, | |
| ] | |
| CERTIFICATIONS = { | |
| "azure": [ | |
| { | |
| "id": "az-104", | |
| "name": "AZ-104", | |
| "description": "Microsoft Azure Administrator", | |
| "readiness": "ready_with_questions", | |
| "study_mode": "๋ฌธ์ ์ํ ๊ธฐ๋ฐ", | |
| }, | |
| ], | |
| "linux": [ | |
| { | |
| "id": "lfcs", | |
| "name": "LFCS", | |
| "description": "Linux Foundation Certified Systems Administrator", | |
| "readiness": "practice_based", | |
| "study_mode": "์ค์ต ๊ณผ์ ๊ธฐ๋ฐ", | |
| }, | |
| { | |
| "id": "linux-master", | |
| "name": "๋ฆฌ๋ ์ค๋ง์คํฐ", | |
| "description": "๋ฆฌ๋ ์ค ๋ช ๋ น์ด์ ์ด์์ฒด์ ๊ฐ๋ ์ค์ฌ ๊ตญ๋ด ์๊ฒฉ์ฆ", | |
| "readiness": "concept_quiz_based", | |
| "study_mode": "๊ฐ๋ /ํ์ธ ํด์ฆ ๊ธฐ๋ฐ", | |
| }, | |
| ], | |
| "tool_docs": [ | |
| { | |
| "id": "tool-docs", | |
| "name": "Docs Study", | |
| "description": "๊ณต์ ๋ฌธ์ ๊ธฐ๋ฐ ๋๊ตฌ ํ์ต", | |
| "readiness": "docs_based", | |
| "study_mode": "๊ณต์ ๋ฌธ์ ๊ธฐ๋ฐ", | |
| }, | |
| ], | |
| } | |
| ROADMAPS = { | |
| "linux": [ | |
| RoadmapStep("Essential Commands", "ํ์ผ ํ์, ๊ฒ์, ์์ถ, ๋ฆฌ๋ค์ด๋ ์ , ๊ธฐ๋ณธ ํ ์คํธ ์ฒ๋ฆฌ๋ฅผ ์ตํ๋๋ค.", "LFCS ํต์ฌ"), | |
| RoadmapStep("Users & Groups", "์ฌ์ฉ์, ๊ทธ๋ฃน, sudo, ๊ณ์ ์ ์ฑ ์ ๊ด๋ฆฌํฉ๋๋ค.", "LFCS ํต์ฌ"), | |
| RoadmapStep("Permissions", "rwx, chmod, chown, umask, ํน์ ๊ถํ์ ์ ๋ฆฌํฉ๋๋ค.", "LFCS ํต์ฌ"), | |
| RoadmapStep("Processes & Services", "ps, kill, systemctl, journalctl๋ก ํ๋ก์ธ์ค์ ์๋น์ค๋ฅผ ๋ค๋ฃน๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("Networking", "ip, ss, ping, curl, DNS, ๋ฐฉํ๋ฒฝ ๊ธฐ์ด๋ฅผ ํ์ตํฉ๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("Storage", "ํํฐ์ , ํ์ผ์์คํ , mount, swap, LVM ๊ฐ๋ ์ ์ ๋ฆฌํฉ๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("Package & Software", "apt, dnf/yum, repository, ์ ๋ฐ์ดํธ ํ๋ฆ์ ์ตํ๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("Logs & Troubleshooting", "๋ก๊ทธ ์์น์ ์ฅ์ ํ์ธ ๋ฃจํด์ ์ฐ์ตํฉ๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("Security Basics", "SSH, ๊ถํ, ์๋น์ค ๋ ธ์ถ, ๊ธฐ๋ณธ ๋ณด์ ์ ๊ฒ์ ์ ๋ฆฌํฉ๋๋ค.", "LFCS ์ค๋ฌด"), | |
| RoadmapStep("LFCS Style Labs", "๋ช ๋ น์ด๋ฅผ ์ง์ ์ ๋ ฅํด ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธํฉ๋๋ค.", "์ค์ต"), | |
| ], | |
| "azure": [ | |
| RoadmapStep("Identity & Governance", "Entra ID, RBAC, Policy, Resource Group, Management Group์ ์ ๋ฆฌํฉ๋๋ค.", "AZ-104 20-25%"), | |
| RoadmapStep("Storage", "Storage Account, Blob, Files, redundancy, lifecycle๋ฅผ ํ์ตํฉ๋๋ค.", "AZ-104 15-20%"), | |
| RoadmapStep("Compute", "VM, Scale Set, App Service, Container ๋ฆฌ์์ค๋ฅผ ํ์ตํฉ๋๋ค.", "AZ-104 20-25%"), | |
| RoadmapStep("Networking", "VNet, Subnet, NSG, Peering, Load Balancer, DNS๋ฅผ ํ์ตํฉ๋๋ค.", "AZ-104 15-20%"), | |
| RoadmapStep("Monitor & Recovery", "Azure Monitor, Log Analytics, Backup, Site Recovery๋ฅผ ์ ๋ฆฌํฉ๋๋ค.", "AZ-104 10-15%"), | |
| RoadmapStep("AZ-104 ๋ฌธ์ ํ์ด", "๊ธฐ์กด ๋ฌธ์ ์ํ์ผ๋ก ์ํ ์คํ์ผ์ ์ฐ์ตํฉ๋๋ค.", "์ํ ๋๋น"), | |
| ], | |
| "tool_docs": [ | |
| RoadmapStep("Ollama", "๋ก์ปฌ ๋ชจ๋ธ ์คํ, ๋ชจ๋ธ ๊ด๋ฆฌ, API ํธ์ถ ํ๋ฆ์ ํ์ตํฉ๋๋ค.", "๊ณต์ Docs"), | |
| RoadmapStep("LangChain", "Chain, Retriever, Tool, Agent์ ๊ธฐ๋ณธ ์ฐ์์ ์ ๋ฆฌํฉ๋๋ค.", "๊ณต์ Docs"), | |
| RoadmapStep("Streamlit", "์ํ ๊ด๋ฆฌ, ์ ๋ ฅ ์์ ฏ, ๋ฐฐํฌ ํ๋ฆ์ ํ์ตํฉ๋๋ค.", "๊ณต์ Docs"), | |
| RoadmapStep("Hugging Face", "Spaces, Models, Datasets, Secrets ๊ด๋ฆฌ ํ๋ฆ์ ์ตํ๋๋ค.", "๊ณต์ Docs"), | |
| ], | |
| } | |
| LESSONS = [] | |
| QUIZZES = [ | |
| LabQuiz( | |
| "linux-q1", | |
| "linux-pwd", | |
| "linux", | |
| "multiple_choice", | |
| "ํ์ฌ ๋๋ ํฐ๋ฆฌ ์์น๋ฅผ ํ์ธํ๋ ๋ช ๋ น์ด๋?", | |
| ["ls", "pwd", "cd", "mkdir"], | |
| "pwd", | |
| "`pwd`๋ print working directory์ ์ฝ์๋ก ํ์ฌ ์์ ์ค์ธ ๋๋ ํฐ๋ฆฌ์ ๊ฒฝ๋ก๋ฅผ ์ถ๋ ฅํฉ๋๋ค.", | |
| ), | |
| LabQuiz( | |
| "linux-q2", | |
| "linux-chmod", | |
| "linux", | |
| "multiple_choice", | |
| "ํ์ผ ๊ถํ์ ๋ณ๊ฒฝํ๋ ๋ช ๋ น์ด๋?", | |
| ["chmod", "chown", "grep", "kill"], | |
| "chmod", | |
| "`chmod`๋ ํ์ผ์ด๋ ๋๋ ํฐ๋ฆฌ์ ๊ถํ์ ๋ณ๊ฒฝํ ๋ ์ฌ์ฉํฉ๋๋ค.", | |
| ), | |
| LabQuiz( | |
| "linux-q3", | |
| "linux-grep", | |
| "linux", | |
| "multiple_choice", | |
| "ํ ์คํธ์์ ํน์ ๋ฌธ์์ด์ ๊ฒ์ํ๋ ๋ช ๋ น์ด๋?", | |
| ["cat", "grep", "touch", "mv"], | |
| "grep", | |
| "`grep`์ ํ์ผ์ด๋ ์ถ๋ ฅ ๊ฒฐ๊ณผ์์ ํน์ ๋ฌธ์์ด ํจํด์ ๊ฒ์ํฉ๋๋ค.", | |
| source_id="linux-grep-manual", | |
| ), | |
| LabQuiz( | |
| "linux-q4", | |
| "linux-mkdir", | |
| "linux", | |
| "command", | |
| "`practice` ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง๋๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "mkdir practice", | |
| "`mkdir practice`๋ ํ์ฌ ์์น์ practice ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค.", | |
| ), | |
| LabQuiz( | |
| "linux-q5", | |
| "linux-touch", | |
| "linux", | |
| "command", | |
| "`hello.txt` ๋น ํ์ผ์ ๋ง๋๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "touch hello.txt", | |
| "`touch hello.txt`๋ ๋น ํ์ผ์ ๋ง๋ค๊ฑฐ๋ ๊ธฐ์กด ํ์ผ์ ์์ ์๊ฐ์ ๊ฐฑ์ ํฉ๋๋ค.", | |
| ), | |
| LabQuiz( | |
| "linux-q6", | |
| "linux-apt", | |
| "linux", | |
| "command", | |
| "Ubuntu์์ ํจํค์ง ๋ชฉ๋ก์ ๊ฐฑ์ ํ๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "sudo apt update", | |
| "`sudo apt update`๋ ์ ์ฅ์์ ํจํค์ง ๋ชฉ๋ก์ ๊ฐฑ์ ํฉ๋๋ค. ์ค์ ํจํค์ง ์ ๊ทธ๋ ์ด๋๋ `sudo apt upgrade`์ ๋๋ค.", | |
| source_id="ubuntu-package-management", | |
| ), | |
| LabQuiz( | |
| "linux-q7", | |
| "linux-ufw", | |
| "linux", | |
| "command", | |
| "Ubuntu์์ SSH ์ ์ ํฌํธ 22๋ฒ์ ํ์ฉํ๋ ufw ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "sudo ufw allow 22", | |
| "`sudo ufw allow 22`๋ 22๋ฒ ํฌํธ๋ฅผ ํ์ฉํฉ๋๋ค. ์๊ฒฉ ์๋ฒ์์๋ ufw enable ์ ์ SSH ํฌํธ ํ์ฉ ์ฌ๋ถ๋ฅผ ํ์ธํด์ผ ํฉ๋๋ค.", | |
| source_id="ubuntu-firewall", | |
| ), | |
| LabQuiz( | |
| "azure-q1", | |
| "azure-rg", | |
| "azure", | |
| "multiple_choice", | |
| "AZ-104์์ Resource Group์ ๊ฐ์ฅ ์ค์ํ ํน์ง์ผ๋ก ๋ง๋ ๊ฒ์?", | |
| ["๊ฐ์ ๋คํธ์ํฌ ์ฃผ์ ๊ณต๊ฐ์ ์ ์ํ๋ค", "Azure ๋ฆฌ์์ค๋ฅผ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ฌถ์ด ๊ด๋ฆฌํ๋ค", "์ฌ์ฉ์ ์ํธ๋ฅผ ์ฌ์ค์ ํ๋ค", "VM ํฌ๊ธฐ๋ฅผ ์๋์ผ๋ก ์กฐ์ ํ๋ค"], | |
| "Azure ๋ฆฌ์์ค๋ฅผ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ฌถ์ด ๊ด๋ฆฌํ๋ค", | |
| "Resource Group์ ์ฌ๋ฌ Azure ๋ฆฌ์์ค๋ฅผ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ฌถ๊ณ ์๋ช ์ฃผ๊ธฐ์ ๊ถํ ๊ด๋ฆฌ๋ฅผ ๋๋ ๋จ์์ ๋๋ค.", | |
| source_id="manual-az104-basic", | |
| ), | |
| LabQuiz( | |
| "azure-q2", | |
| "azure-storage", | |
| "azure", | |
| "multiple_choice", | |
| "Storage Account์์ Blob Storage๊ฐ ์ฃผ๋ก ์ ์ฅํ๋ ๋ฐ์ดํฐ ์ ํ์?", | |
| ["๊ฐ์ฒด ๋ฐ์ดํฐ", "๊ฐ์ ๋คํธ์ํฌ ๋ผ์ฐํ ํ ์ด๋ธ", "Entra ID ์ฌ์ฉ์", "Azure Policy ์ ์๋ง"], | |
| "๊ฐ์ฒด ๋ฐ์ดํฐ", | |
| "Blob Storage๋ ๋ก๊ทธ, ์ด๋ฏธ์ง, ๋ฐฑ์ ํ์ผ ๊ฐ์ ๋น์ ํ ๊ฐ์ฒด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.", | |
| source_id="azure-storage-account", | |
| ), | |
| LabQuiz( | |
| "azure-q3", | |
| "azure-vnet", | |
| "azure", | |
| "multiple_choice", | |
| "VNet์ ์ค๊ณํ ๋ ๋จผ์ ํ์ธํด์ผ ํ๋ ๊ฒ์?", | |
| ["์ฃผ์ ๊ณต๊ฐ์ด ๊ฒน์น์ง ์๋์ง", "๋ชจ๋ VM์ด ๊ฐ์ ์ด๋ฆ์ธ์ง", "๋ชจ๋ ๋ฆฌ์์ค๊ฐ ๊ฐ์ ํ๊ทธ์ธ์ง", "Storage Account access tier๊ฐ Archive์ธ์ง"], | |
| "์ฃผ์ ๊ณต๊ฐ์ด ๊ฒน์น์ง ์๋์ง", | |
| "VNet ์ฃผ์ ๊ณต๊ฐ์ด ๋ค๋ฅธ ๋คํธ์ํฌ์ ๊ฒน์น๋ฉด ํผ์ด๋ง์ด๋ ํ์ด๋ธ๋ฆฌ๋ ์ฐ๊ฒฐ ์ค๊ณ์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ต๋๋ค.", | |
| source_id="azure-vnet-overview", | |
| ), | |
| LabQuiz( | |
| "docs-q1", | |
| "docs-streamlit-state", | |
| "tool_docs", | |
| "multiple_choice", | |
| "Streamlit์์ rerun ์ดํ์๋ ๊ฐ์ ์ ์งํ๋ ๋ฐ ๊ฐ์ฅ ์ ํฉํ ๊ฒ์?", | |
| ["์ผ๋ฐ ์ง์ญ ๋ณ์", "st.session_state", "print", "requirements.txt"], | |
| "st.session_state", | |
| "Streamlit์ ์ํธ์์ฉ ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ค์ ์คํํ๋ฏ๋ก ์ง์ ์ํ๋ session_state์ ์ ์ฅํ๋ ๊ฒ์ด ์ ํฉํฉ๋๋ค.", | |
| source_id="official-docs-streamlit", | |
| ), | |
| LabQuiz( | |
| "docs-q3", | |
| "docs-langchain-rag", | |
| "tool_docs", | |
| "multiple_choice", | |
| "LangChain RAG์์ ์ง๋ฌธ๊ณผ ๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์ LLM์ ๋๊ธฐ๋ ์ญํ ์ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ฒ์?", | |
| ["Retriever", "Theme config", "Space Secret", "chmod"], | |
| "Retriever", | |
| "Retriever๋ ๋ฒกํฐ ๊ฒ์์ด๋ ํค์๋ ๊ฒ์ ๋ฑ์ ํตํด ์ง๋ฌธ๊ณผ ๊ด๋ จ ์๋ ๋ฌธ์๋ฅผ ์ฐพ์ ์ฒด์ธ์ ์ ๋ฌํฉ๋๋ค.", | |
| source_id="official-docs-langchain", | |
| ), | |
| LabQuiz( | |
| "docs-q2", | |
| "docs-hf-spaces", | |
| "tool_docs", | |
| "multiple_choice", | |
| "Hugging Face Space์์ API ํค ๊ฐ์ ๋น๋ฐ๊ฐ์ ์ ์ฅํด์ผ ํ๋ ์์น๋?", | |
| ["README ๋ณธ๋ฌธ", "Git ์ปค๋ฐ", "Space Secrets", "์ฑ ํ๋ฉด ์บก์ "], | |
| "Space Secrets", | |
| "๋ฐฐํฌ ํ๊ฒฝ์ ๋น๋ฐ๊ฐ์ Space Settings์ Secrets์ ์ ์ฅํ๊ณ ์ฝ๋์๋ ํฌํจํ์ง ์๋ ๊ฒ์ด ์์ ํฉ๋๋ค.", | |
| source_id="official-docs-huggingface", | |
| ), | |
| ] | |
| QUIZZES.extend( | |
| [ | |
| LabQuiz( | |
| "linux-q8", | |
| "linux-chown", | |
| "linux", | |
| "multiple_choice", | |
| "`chmod`์ ๋น๊ตํ์ ๋ `chown`์ ์ฃผ๋ ์ญํ ์?", | |
| ["ํ์ผ ๋ด์ฉ ์ถ๋ ฅ", "์์ ์/๊ทธ๋ฃน ๋ณ๊ฒฝ", "ํ๋ก์ธ์ค ์ข ๋ฃ", "ํจํค์ง ์ค์น"], | |
| "์์ ์/๊ทธ๋ฃน ๋ณ๊ฒฝ", | |
| "`chown`์ owner/group์ ๋ฐ๊พธ๊ณ , `chmod`๋ ๊ถํ ๋นํธ๋ฅผ ๋ฐ๊ฟ๋๋ค.", | |
| source_id="manual-linux-basic", | |
| ), | |
| LabQuiz( | |
| "linux-q9", | |
| "linux-umask", | |
| "linux", | |
| "multiple_choice", | |
| "`umask 022`์ ์๋ฏธ๋ก ๊ฐ์ฅ ๊ฐ๊น์ด ๊ฒ์?", | |
| ["๋ชจ๋ ํ์ผ์ 022 ๊ถํ์ผ๋ก ๋ง๋ ๋ค", "๊ทธ๋ฃน/๊ธฐํ ์ฌ์ฉ์ ์ฐ๊ธฐ ๊ถํ์ ๊ธฐ๋ณธ ์ ๊ฑฐํ๋ค", "root๋ง ๋ก๊ทธ์ธํ๊ฒ ํ๋ค", "๋์คํฌ๋ฅผ ๋ง์ดํธํ๋ค"], | |
| "๊ทธ๋ฃน/๊ธฐํ ์ฌ์ฉ์ ์ฐ๊ธฐ ๊ถํ์ ๊ธฐ๋ณธ ์ ๊ฑฐํ๋ค", | |
| "`umask`๋ ์ ํ์ผ/๋๋ ํฐ๋ฆฌ์์ ์ ๊ฑฐํ ๊ถํ์ ๋ปํฉ๋๋ค.", | |
| source_id="manual-linux-basic", | |
| ), | |
| LabQuiz( | |
| "linux-q10", | |
| "linux-find", | |
| "linux", | |
| "command", | |
| "`/var/log` ์๋์ `.log` ํ์ผ์ ์ฐพ๋ find ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "find /var/log -type f -name '*.log'", | |
| "`find ๊ฒฝ๋ก -type f -name ํจํด` ํํ๋ก ํ์ผ ํ์ ๊ณผ ์ด๋ฆ ํจํด์ ํจ๊ป ์ง์ ํ ์ ์์ต๋๋ค.", | |
| source_id="manual-linux-basic", | |
| ), | |
| LabQuiz( | |
| "linux-q11", | |
| "linux-pipe-redirect", | |
| "linux", | |
| "multiple_choice", | |
| "`>>` ๋ฆฌ๋ค์ด๋ ์ ์ ์๋ฏธ๋?", | |
| ["ํ์ผ ๋ฎ์ด์ฐ๊ธฐ", "ํ์ผ ๋์ ์ด์ด์ฐ๊ธฐ", "ํ์ค ์๋ฌ ๋ฌด์", "๋ช ๋ น์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์คํ"], | |
| "ํ์ผ ๋์ ์ด์ด์ฐ๊ธฐ", | |
| "`>`๋ ๋ฎ์ด์ฐ๊ธฐ, `>>`๋ ์ด์ด์ฐ๊ธฐ์ ๋๋ค.", | |
| source_id="manual-linux-basic", | |
| ), | |
| LabQuiz( | |
| "linux-q12", | |
| "linux-tar", | |
| "linux", | |
| "command", | |
| "`logs` ๋๋ ํฐ๋ฆฌ๋ฅผ gzip ์์ถ tar ํ์ผ `logs.tar.gz`๋ก ๋ง๋๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "tar -czf logs.tar.gz logs", | |
| "`-c`๋ ์์ฑ, `-z`๋ gzip, `-f`๋ ํ์ผ๋ช ์ ์ง์ ํฉ๋๋ค.", | |
| source_id="manual-linux-basic", | |
| ), | |
| LabQuiz( | |
| "linux-q13", | |
| "linux-systemctl", | |
| "linux", | |
| "multiple_choice", | |
| "`systemctl enable nginx`์ ์๋ฏธ๋?", | |
| ["์ง๊ธ ํ ๋ฒ๋ง ์คํ", "๋ถํ ์ ์๋ ์์ ๋ฑ๋ก", "ํจํค์ง ์ ๊ฑฐ", "๋ก๊ทธ ์ญ์ "], | |
| "๋ถํ ์ ์๋ ์์ ๋ฑ๋ก", | |
| "`start`๋ ์ฆ์ ์คํ, `enable`์ ๋ถํ ์ ์๋ ์์ ์ค์ ์ ๋๋ค.", | |
| source_id="linux-systemd-systemctl", | |
| ), | |
| LabQuiz( | |
| "linux-q14", | |
| "linux-journalctl", | |
| "linux", | |
| "command", | |
| "nginx ์๋น์ค ๋ก๊ทธ๋ฅผ ํ์ธํ๋ journalctl ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "journalctl -u nginx", | |
| "`journalctl -u ์๋น์ค๋ช `์ ํน์ systemd unit ๋ก๊ทธ๋ฅผ ํ์ธํฉ๋๋ค.", | |
| source_id="linux-systemd-systemctl", | |
| ), | |
| LabQuiz( | |
| "linux-q15", | |
| "linux-ip-ss", | |
| "linux", | |
| "multiple_choice", | |
| "ํ์ฌ ์์คํ ์ listening ํฌํธ๋ฅผ ํ์ธํ๋ ๋ฐ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ช ๋ น์?", | |
| ["ss -tulpen", "touch port", "chmod 755", "tar -czf"], | |
| "ss -tulpen", | |
| "`ss -tulpen`์ TCP/UDP listening ์์ผ๊ณผ ํ๋ก์ธ์ค ์ ๋ณด๋ฅผ ํ์ธํ ๋ ์ ์ฉํฉ๋๋ค.", | |
| source_id="linux-man7-ip", | |
| ), | |
| LabQuiz( | |
| "linux-q16", | |
| "linux-df-du-mount", | |
| "linux", | |
| "multiple_choice", | |
| "`df -h`์ `du -sh /var/log`์ ์ฐจ์ด๋ก ๋ง๋ ๊ฒ์?", | |
| ["๋ ๋ค ํจํค์ง ์ค์น ๋ช ๋ น์ด๋ค", "`df`๋ ํ์ผ์์คํ , `du`๋ ๊ฒฝ๋ก๋ณ ์ฌ์ฉ๋ ํ์ธ์ ๊ฐ๊น๋ค", "`du`๋ง ๋คํธ์ํฌ ์ํ๋ฅผ ๋ณธ๋ค", "`df`๋ ์ฌ์ฉ์ ์ํธ ๋ณ๊ฒฝ ๋ช ๋ น์ด๋ค"], | |
| "`df`๋ ํ์ผ์์คํ , `du`๋ ๊ฒฝ๋ก๋ณ ์ฌ์ฉ๋ ํ์ธ์ ๊ฐ๊น๋ค", | |
| "`df`๋ ๋ง์ดํธ๋ ํ์ผ์์คํ ๋จ์, `du`๋ ํน์ ํ์ผ/๋๋ ํฐ๋ฆฌ ์ฉ๋ ํ์ธ์ ์ ํฉํฉ๋๋ค.", | |
| source_id="ubuntu-storage", | |
| ), | |
| LabQuiz( | |
| "azure-q4", | |
| "azure-rbac", | |
| "azure", | |
| "multiple_choice", | |
| "Azure RBAC์์ ๊ถํ์ด ์ ์ฉ๋๋ ๋ฒ์๋ฅผ ๋ปํ๋ ๋ง์?", | |
| ["Scope", "Access tier", "Subnet mask", "Archive"], | |
| "Scope", | |
| "RBAC role assignment๋ management group, subscription, resource group, resource ๊ฐ์ scope์ ์ ์ฉ๋ฉ๋๋ค.", | |
| source_id="azure-rbac-overview", | |
| ), | |
| LabQuiz( | |
| "azure-q5", | |
| "azure-policy", | |
| "azure", | |
| "multiple_choice", | |
| "Azure Policy์ ์ฃผ๋ ๋ชฉ์ ์?", | |
| ["VM CPU๋ฅผ ์ฆ์ ๋๋ฆฐ๋ค", "๋ฆฌ์์ค ์ํ๋ฅผ ์กฐ์ง ๊ท์น์ ๋ง๊ฒ ํ๊ฐ/์ ์ดํ๋ค", "Blob์ ๋ค์ด๋ก๋ํ๋ค", "DNS ๋ ์ฝ๋๋ง ์์ฑํ๋ค"], | |
| "๋ฆฌ์์ค ์ํ๋ฅผ ์กฐ์ง ๊ท์น์ ๋ง๊ฒ ํ๊ฐ/์ ์ดํ๋ค", | |
| "Azure Policy๋ deny/audit ๊ฐ์ ํจ๊ณผ๋ก ๋ฆฌ์์ค ๊ท์ ์ค์๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.", | |
| source_id="azure-policy-overview", | |
| ), | |
| LabQuiz( | |
| "azure-q6", | |
| "azure-vm", | |
| "azure", | |
| "multiple_choice", | |
| "Azure VM์ ์ดํดํ ๋ ํจ๊ป ๋ด์ผ ํ ๋ฆฌ์์ค๋ก ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ด ์กฐํฉ์?", | |
| ["Disk, NIC, VNet, NSG", "Only DNS TXT record", "Only Policy initiative", "Only Blob tier"], | |
| "Disk, NIC, VNet, NSG", | |
| "VM์ compute๋ฟ ์๋๋ผ ๋์คํฌ์ ๋คํธ์ํฌ ๋ฆฌ์์ค๊ฐ ํจ๊ป ๊ตฌ์ฑ๋ฉ๋๋ค.", | |
| source_id="azure-vm-overview", | |
| ), | |
| LabQuiz( | |
| "azure-q7", | |
| "azure-nsg", | |
| "azure", | |
| "multiple_choice", | |
| "NSG ๊ท์น ํ๊ฐ์์ ๋จผ์ ์ ์ฉ๋๋ ๊ท์น์?", | |
| ["์ฐ์ ์์ ์ซ์๊ฐ ๋ฎ์ ๊ท์น", "์ด๋ฆ์ด ๊ธด ๊ท์น", "๋์ค์ ๋ง๋ ๊ท์น", "ํญ์ Deny ๊ท์น"], | |
| "์ฐ์ ์์ ์ซ์๊ฐ ๋ฎ์ ๊ท์น", | |
| "NSG๋ priority ์ซ์๊ฐ ๋ฎ์์๋ก ๋จผ์ ํ๊ฐ๋ฉ๋๋ค.", | |
| source_id="azure-nsg-overview", | |
| ), | |
| LabQuiz( | |
| "azure-q8", | |
| "azure-app-gateway", | |
| "azure", | |
| "multiple_choice", | |
| "Application Gateway๊ฐ Load Balancer์ ๋น๊ตํด ๋ ์ ๋ค๋ฃจ๋ ์์ญ์?", | |
| ["HTTP path ๊ธฐ๋ฐ L7 ๋ผ์ฐํ ", "ํ์ผ ๊ถํ ๋ณ๊ฒฝ", "ํจํค์ง ๋ชฉ๋ก ๊ฐฑ์ ", "ํ๋ก์ธ์ค ์ข ๋ฃ"], | |
| "HTTP path ๊ธฐ๋ฐ L7 ๋ผ์ฐํ ", | |
| "Application Gateway๋ HTTP/HTTPS L7 ๋ผ์ฐํ ๊ณผ WAF ๊ฐ์ ์น ๊ณ์ธต ๊ธฐ๋ฅ์ ์ ํฉํฉ๋๋ค.", | |
| source_id="azure-app-gateway-overview", | |
| ), | |
| LabQuiz( | |
| "azure-q9", | |
| "azure-blob-tier", | |
| "azure", | |
| "multiple_choice", | |
| "Archive tier์ ํน์ง์ผ๋ก ๊ฐ์ฅ ๋ง๋ ๊ฒ์?", | |
| ["ํญ์ ์ฆ์ ์ฝ๊ธฐ ๊ฐ๋ฅ", "์คํ๋ผ์ธ ๊ณ์ธต์ด๋ผ ์ฝ๊ธฐ ์ ์ rehydrate๊ฐ ํ์ํ ์ ์์", "VM ํฌ๊ธฐ ์ค์ ", "RBAC ์ญํ ์ด๋ฆ"], | |
| "์คํ๋ผ์ธ ๊ณ์ธต์ด๋ผ ์ฝ๊ธฐ ์ ์ rehydrate๊ฐ ํ์ํ ์ ์์", | |
| "Archive๋ ์ฅ๊ธฐ ๋ณด๊ด ๋น์ฉ์ ๋ฎ์ถ๋ ๋์ ์ฆ์ ์ ๊ทผ์ฑ์ด ๋จ์ด์ง ์ ์์ต๋๋ค.", | |
| source_id="azure-blob-access-tiers", | |
| ), | |
| LabQuiz( | |
| "azure-q10", | |
| "azure-log-analytics", | |
| "azure", | |
| "multiple_choice", | |
| "Log Analytics์์ ๋ก๊ทธ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ ์ธ์ด๋?", | |
| ["KQL", "Bash", "YAML only", "Markdown"], | |
| "KQL", | |
| "Log Analytics๋ KQL๋ก ๋ก๊ทธ๋ฅผ ํํฐ๋งํ๊ณ ๋ถ์ํฉ๋๋ค.", | |
| source_id="azure-log-analytics-overview", | |
| ), | |
| LabQuiz( | |
| "docs-q4", | |
| "docs-lcel", | |
| "tool_docs", | |
| "multiple_choice", | |
| "LCEL์์ `prompt | llm | parser`์ฒ๋ผ ์ฐ๊ฒฐํ๋ ๋ฐฉ์์ ์ฅ์ ์?", | |
| ["์ฒด์ธ ํ๋ฆ์ ๋ช ์์ ์ผ๋ก ์กฐํฉํ ์ ์๋ค", "์๋ฒ ๋ฐฉํ๋ฒฝ์ ์ฐ๋ค", "ํ์ผ ๊ถํ์ ๋ฐ๊พผ๋ค", "Blob tier๋ฅผ ๋ณ๊ฒฝํ๋ค"], | |
| "์ฒด์ธ ํ๋ฆ์ ๋ช ์์ ์ผ๋ก ์กฐํฉํ ์ ์๋ค", | |
| "LCEL์ Runnable์ ํ์ดํ์ฒ๋ผ ์ฐ๊ฒฐํด ์ ๋ ฅ/์ถ๋ ฅ ํ๋ฆ์ ๊ตฌ์ฑํฉ๋๋ค.", | |
| source_id="official-docs-langchain", | |
| ), | |
| LabQuiz( | |
| "docs-q5", | |
| "docs-vectorstore", | |
| "tool_docs", | |
| "multiple_choice", | |
| "VectorStore๋ฅผ ์ฌ์ฉํ ๋ ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ๋ฐ๊พธ๋ฉด ์ฃผ์ํ ์ ์?", | |
| ["๊ธฐ์กด ๋ฒกํฐ์ ์ฐจ์์ด ๋ฌ๋ผ์ง ์ ์๋ค", "ํญ์ ๊ฐ์ ์ปฌ๋ ์ ์ ์์ด์ผ ํ๋ค", "DB ์ํธ๊ฐ ์๋ ๊ณต๊ฐ๋๋ค", "UI ๋ฒํผ์ด ์ฌ๋ผ์ง๋ค"], | |
| "๊ธฐ์กด ๋ฒกํฐ์ ์ฐจ์์ด ๋ฌ๋ผ์ง ์ ์๋ค", | |
| "์๋ฒ ๋ฉ ๋ชจ๋ธ๋ณ๋ก ์ปฌ๋ ์ ์ ๋ถ๋ฆฌํ๋ฉด ์ฐจ์ ๋ถ์ผ์น ๋ฌธ์ ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.", | |
| source_id="official-docs-langchain", | |
| ), | |
| LabQuiz( | |
| "docs-q6", | |
| "docs-streamlit-cache", | |
| "tool_docs", | |
| "multiple_choice", | |
| "Streamlit์์ ๋ชจ๋ธ/DB ์ฐ๊ฒฐ ๊ฐ์ ๋ฆฌ์์ค ์ด๊ธฐํ์ ๋ ์ ํฉํ ์บ์๋?", | |
| ["st.cache_resource", "st.write", "st.button", "st.caption"], | |
| "st.cache_resource", | |
| "`st.cache_resource`๋ ๋ชจ๋ธ, DB connection ๊ฐ์ ๊ณต์ ๋ฆฌ์์ค ์บ์์ ์ ํฉํฉ๋๋ค.", | |
| source_id="official-docs-streamlit", | |
| ), | |
| LabQuiz( | |
| "docs-q7", | |
| "docs-ollama-api", | |
| "tool_docs", | |
| "multiple_choice", | |
| "Ollama API๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ํ์ํ ์กฐ๊ฑด์ผ๋ก ๊ฐ์ฅ ๋ง๋ ๊ฒ์?", | |
| ["Ollama ์๋ฒ๊ฐ ์คํ ์ค์ด๊ณ ๋ชจ๋ธ์ด ์ค๋น๋์ด ์์ด์ผ ํ๋ค", "ufw๊ฐ ๋ฐ๋์ ๊บผ์ ธ ์์ด์ผ ํ๋ค", "Azure VMSS๊ฐ ์์ด์ผ ํ๋ค", "README์ API ํค๋ฅผ ์ ์ด์ผ ํ๋ค"], | |
| "Ollama ์๋ฒ๊ฐ ์คํ ์ค์ด๊ณ ๋ชจ๋ธ์ด ์ค๋น๋์ด ์์ด์ผ ํ๋ค", | |
| "๋ก์ปฌ Ollama ์๋ฒ์ ๋ชจ๋ธ์ด ์ค๋น๋์ง ์์ผ๋ฉด generate/chat API ํธ์ถ์ด ์คํจํฉ๋๋ค.", | |
| source_id="official-docs-ollama", | |
| ), | |
| ] | |
| ) | |
| PRACTICE_TASKS = [ | |
| PracticeTask( | |
| "linux-lab-1", | |
| "linux", | |
| "ํ์ฌ ์์น ํ์ธ", | |
| "ํ์ฌ ์์ ์ค์ธ ๋๋ ํฐ๋ฆฌ๋ฅผ ํ์ธํ์ธ์.", | |
| "pwd", | |
| ["pwd"], | |
| "ํ์ฌ ์์น๋ working directory๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.", | |
| "`pwd`๋ฅผ ์ ๋ ฅํ๋ฉด ํ์ฌ ๋๋ ํฐ๋ฆฌ์ ์ ๋ ๊ฒฝ๋ก๋ฅผ ๋ณผ ์ ์์ต๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-2", | |
| "linux", | |
| "๋๋ ํฐ๋ฆฌ ์์ฑ", | |
| "`practice` ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฑํ์ธ์.", | |
| "mkdir practice", | |
| ["mkdir", "practice"], | |
| "๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง๋ค ๋๋ mkdir์ ์ฌ์ฉํฉ๋๋ค.", | |
| "`mkdir practice`๋ practice๋ผ๋ ์ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง๋ญ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-3", | |
| "linux", | |
| "ํ์ผ ์์ฑ", | |
| "`hello.txt` ํ์ผ์ ์์ฑํ์ธ์.", | |
| "touch hello.txt", | |
| ["touch", "hello.txt"], | |
| "๋น ํ์ผ์ ๋น ๋ฅด๊ฒ ๋ง๋ค ๋ touch๋ฅผ ์ธ ์ ์์ต๋๋ค.", | |
| "`touch hello.txt`๋ hello.txt ํ์ผ์ ๋ง๋ญ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-4", | |
| "linux", | |
| "ํ์ผ ๋ด์ฉ ์ถ๋ ฅ", | |
| "`hello.txt` ๋ด์ฉ์ ์ถ๋ ฅํ์ธ์.", | |
| "cat hello.txt", | |
| ["cat", "hello.txt"], | |
| "ํ์ผ ๋ด์ฉ์ ํ์ค ์ถ๋ ฅ์ผ๋ก ๋ณผ ๋ cat์ ์ฌ์ฉํฉ๋๋ค.", | |
| "`cat hello.txt`๋ ํ์ผ ๋ด์ฉ์ ํ๋ฉด์ ์ถ๋ ฅํฉ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-5", | |
| "linux", | |
| "์์ธํ ๋ชฉ๋ก ํ์ธ", | |
| "ํ์ฌ ๋๋ ํฐ๋ฆฌ์ ํ์ผ ๋ชฉ๋ก์ ์์ธํ ํ์ธํ์ธ์.", | |
| "ls -al", | |
| ["ls", "-"], | |
| "`-a`์ `-l`์ ํจ๊ป ์ฐ๋ฉด ์จ๊น ํ์ผ๊ณผ ์์ธํ ์ ๋ณด๋ฅผ ๋ณผ ์ ์์ต๋๋ค.", | |
| "`ls -al` ๋๋ `ls -la`๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.", | |
| ), | |
| ] | |
| PRACTICE_TASKS.extend( | |
| [ | |
| PracticeTask( | |
| "linux-lab-6", | |
| "linux", | |
| "์์ ์ ๋ณ๊ฒฝ", | |
| "`app.log` ํ์ผ์ ์์ ์๋ฅผ `app` ์ฌ์ฉ์์ `app` ๊ทธ๋ฃน์ผ๋ก ๋ณ๊ฒฝํ์ธ์.", | |
| "sudo chown app:app app.log", | |
| ["chown", "app:app", "app.log"], | |
| "์์ ์์ ๊ทธ๋ฃน์ `user:group` ํํ๋ก ํจ๊ป ์ง์ ํ ์ ์์ต๋๋ค.", | |
| "`sudo chown app:app app.log`๋ app.log์ ์์ ์์ ๊ทธ๋ฃน์ app์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-7", | |
| "linux", | |
| "๋ก๊ทธ ํ์ผ ๊ฒ์", | |
| "`/var/log` ์๋์์ `.log` ํ์ผ์ ๊ฒ์ํ์ธ์.", | |
| "find /var/log -type f -name '*.log'", | |
| ["find", "/var/log", "-type", "f", "-name"], | |
| "ํ์ผ ์กฐ๊ฑด ๊ฒ์์ find๊ฐ ์ ํฉํฉ๋๋ค.", | |
| "`find /var/log -type f -name '*.log'`๋ /var/log ์๋์ .log ํ์ผ์ ์ฐพ์ต๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-8", | |
| "linux", | |
| "์ถ๋ ฅ ์ด์ด์ฐ๊ธฐ", | |
| "`hello` ๋ฌธ์์ด์ `note.txt` ํ์ผ ๋์ ์ด์ด ์ฐ์ธ์.", | |
| "echo hello >> note.txt", | |
| ["echo", "hello", ">>", "note.txt"], | |
| "`>>`๋ ํ์ผ ๋์ ์ด์ด ์๋๋ค.", | |
| "`echo hello >> note.txt`๋ note.txt ๋ค์ hello ํ ์ค์ ์ถ๊ฐํฉ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-9", | |
| "linux", | |
| "tar ์์นด์ด๋ธ ์์ฑ", | |
| "`logs` ๋๋ ํฐ๋ฆฌ๋ฅผ `logs.tar.gz` ํ์ผ๋ก ์์ถํ์ธ์.", | |
| "tar -czf logs.tar.gz logs", | |
| ["tar", "-czf", "logs.tar.gz", "logs"], | |
| "`-c` ์์ฑ, `-z` gzip, `-f` ํ์ผ๋ช ์ง์ ์ ๋๋ค.", | |
| "`tar -czf logs.tar.gz logs`๋ logs ๋๋ ํฐ๋ฆฌ๋ฅผ gzip ์์ถ tar ํ์ผ๋ก ๋ง๋ญ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-10", | |
| "linux", | |
| "์๋น์ค ์ํ ํ์ธ", | |
| "`nginx` ์๋น์ค ์ํ๋ฅผ ํ์ธํ์ธ์.", | |
| "systemctl status nginx", | |
| ["systemctl", "status", "nginx"], | |
| "์๋น์ค ์ํ๋ systemctl status๋ก ๋จผ์ ํ์ธํฉ๋๋ค.", | |
| "`systemctl status nginx`๋ nginx ์๋น์ค์ ์คํ ์ํ์ ์ต๊ทผ ๋ก๊ทธ ํํธ๋ฅผ ๋ณด์ฌ์ค๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-11", | |
| "linux", | |
| "์๋น์ค ๋ก๊ทธ ํ์ธ", | |
| "`nginx` ์๋น์ค ๋ก๊ทธ๋ฅผ ํ์ธํ์ธ์.", | |
| "journalctl -u nginx", | |
| ["journalctl", "-u", "nginx"], | |
| "ํน์ unit ๋ก๊ทธ๋ `journalctl -u`๋ก ํ์ธํฉ๋๋ค.", | |
| "`journalctl -u nginx`๋ nginx systemd unit ๋ก๊ทธ๋ฅผ ๋ณด์ฌ์ค๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-12", | |
| "linux", | |
| "ํจํค์ง ๋ชฉ๋ก ๊ฐฑ์ ", | |
| "Ubuntu ํจํค์ง ๋ชฉ๋ก์ ๊ฐฑ์ ํ์ธ์.", | |
| "sudo apt update", | |
| ["apt", "update"], | |
| "`update`์ `upgrade`๋ฅผ ๊ตฌ๋ถํ์ธ์.", | |
| "`sudo apt update`๋ ์ ์ฅ์ ํจํค์ง ๋ชฉ๋ก์ ๊ฐฑ์ ํฉ๋๋ค.", | |
| ), | |
| PracticeTask( | |
| "linux-lab-13", | |
| "linux", | |
| "SSH ํฌํธ ํ์ฉ", | |
| "ufw์์ SSH 22๋ฒ ํฌํธ๋ฅผ ํ์ฉํ์ธ์.", | |
| "sudo ufw allow 22", | |
| ["ufw", "allow", "22"], | |
| "์๊ฒฉ ์๋ฒ์์๋ ๋ฐฉํ๋ฒฝ ํ์ฑํ ์ ์ SSH ํ์ฉ ์ฌ๋ถ๋ฅผ ํ์ธํ์ธ์.", | |
| "`sudo ufw allow 22`๋ 22๋ฒ ํฌํธ๋ฅผ ํ์ฉํฉ๋๋ค.", | |
| ), | |
| ] | |
| ) | |
| QUIZZES.extend( | |
| [ | |
| LabQuiz( | |
| "linux-q17", | |
| "linux-useradd", | |
| "linux", | |
| "command", | |
| "`devuser` ์ฌ์ฉ์๋ฅผ ํ ๋๋ ํฐ๋ฆฌ์ ํจ๊ป ์์ฑํ๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", | |
| [], | |
| "sudo useradd -m devuser", | |
| "`useradd -m`์ ํ ๋๋ ํฐ๋ฆฌ ์์ฑ์ ํจ๊ป ์ฒ๋ฆฌํฉ๋๋ค.", | |
| source_id="ubuntu-user-management", | |
| ), | |
| LabQuiz("linux-q18", "linux-usermod", "linux", "command", "`devuser`๋ฅผ sudo ๊ทธ๋ฃน์ ์ถ๊ฐํ๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", [], "sudo usermod -aG sudo devuser", "`-aG`๋ ๊ธฐ์กด ๋ณด์กฐ ๊ทธ๋ฃน์ ์ ์งํ๋ฉด์ ์ ๊ทธ๋ฃน์ ์ถ๊ฐํฉ๋๋ค.", source_id="ubuntu-user-management"), | |
| LabQuiz("linux-q19", "linux-sudoers", "linux", "multiple_choice", "sudoers ํ์ผ์ ์์ ํ๊ฒ ํธ์งํ๋ ๋ช ๋ น์?", ["visudo", "cat", "touch", "tar"], "visudo", "`visudo`๋ sudoers ๋ฌธ๋ฒ ๊ฒ์ฌ๋ฅผ ๋์ sudo ์ค์ ์ค๋ฅ๋ฅผ ์ค์ ๋๋ค.", source_id="ubuntu-user-management"), | |
| LabQuiz("linux-q20", "linux-special-perms", "linux", "multiple_choice", "๊ณต์ฉ ๋๋ ํฐ๋ฆฌ์์ ํ์ธ ํ์ผ ์ญ์ ๋ฅผ ์ ํํ๋ ํน์ ๊ถํ์?", ["sticky bit", "setuid", "umask", "swap"], "sticky bit", "sticky bit๋ /tmp ๊ฐ์ ๊ณต์ฉ ๋๋ ํฐ๋ฆฌ์์ ์์ฃผ ์ฐ์ ๋๋ค.", source_id="manual-linux-basic"), | |
| LabQuiz("linux-q21", "linux-ssh-key", "linux", "multiple_choice", "SSH ํค ์ธ์ฆ์์ ์๋ฒ์ authorized_keys์ ๋ค์ด๊ฐ๋ ๊ฒ์?", ["๊ณต๊ฐํค", "๊ฐ์ธํค", "root ์ํธ", "swap ํ์ผ"], "๊ณต๊ฐํค", "๊ฐ์ธํค๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๊ดํ๊ณ ๊ณต๊ฐํค๋ฅผ ์๋ฒ์ ๋ฑ๋กํฉ๋๋ค.", source_id="ubuntu-server-docs"), | |
| LabQuiz("linux-q22", "linux-dns-resolve", "linux", "multiple_choice", "IP ํต์ ์ ๋๋๋ฐ ๋๋ฉ์ธ ์ ์๋ง ์คํจํ ๋ ๋จผ์ ์์ฌํ ์์ญ์?", ["DNS", "chmod", "tar", "umask"], "DNS", "๋๋ฉ์ธ ์ด๋ฆ ํด์ ์คํจ๋ DNS ์ค์ ๋ฌธ์ ์ผ ์ ์์ต๋๋ค.", source_id="ubuntu-networking"), | |
| LabQuiz("linux-q23", "linux-route", "linux", "command", "๊ธฐ๋ณธ ๋ผ์ฐํ ์ ๋ณด๋ฅผ ํ์ธํ๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ธ์.", [], "ip route", "`ip route`๋ ๊ธฐ๋ณธ ๊ฒ์ดํธ์จ์ด์ ๋ผ์ฐํ ํ ์ด๋ธ์ ํ์ธํฉ๋๋ค.", source_id="linux-man7-ip"), | |
| LabQuiz("linux-q24", "linux-cron", "linux", "multiple_choice", "์ฌ์ฉ์๋ณ ๋ฐ๋ณต ์์ ์ ํธ์งํ๋ ๋ช ๋ น์?", ["crontab -e", "journalctl -u", "lsblk", "swapon"], "crontab -e", "`crontab -e`๋ ์ฌ์ฉ์ cron ์์ ์ ํธ์งํฉ๋๋ค.", source_id="ubuntu-server-docs"), | |
| LabQuiz("linux-q25", "linux-lsblk", "linux", "multiple_choice", "๋ธ๋ก ์ฅ์น์ ํํฐ์ ๊ตฌ์กฐ๋ฅผ ํ์ธํ๋ ๋ช ๋ น์?", ["lsblk", "grep", "passwd", "curl"], "lsblk", "`lsblk`๋ ๋์คํฌ/ํํฐ์ /๋ง์ดํธ ๊ด๊ณ๋ฅผ ๋ณด๊ธฐ ์ข์ต๋๋ค.", source_id="ubuntu-storage"), | |
| LabQuiz("linux-q26", "linux-fstab", "linux", "multiple_choice", "๋ถํ ์ ์๋ ๋ง์ดํธ ์ค์ ์ ๋ด๋ ํ์ผ์?", ["/etc/fstab", "/etc/hosts", "/etc/passwd", "/var/log/auth.log"], "/etc/fstab", "`/etc/fstab`์ ํ์ผ์์คํ ์๋ ๋ง์ดํธ ์ค์ ์ ๋ด์ต๋๋ค.", source_id="ubuntu-storage"), | |
| LabQuiz("linux-q27", "linux-exit-code", "linux", "multiple_choice", "์ง์ ๋ช ๋ น์ exit code๋ฅผ ํ์ธํ๋ ํํ์?", ["echo $?", "echo $PATH", "pwd", "id"], "echo $?", "`$?`๋ ์ง์ ๋ช ๋ น์ ์ข ๋ฃ ์ฝ๋๋ฅผ ๋ด์ต๋๋ค.", source_id="linux-bash-cd"), | |
| LabQuiz("linux-q28", "linux-remove-purge", "linux", "multiple_choice", "`apt purge`๊ฐ `apt remove`์ ๋น๊ตํด ๋ ์ ๋ฆฌํ๋ ๊ฒ์?", ["์ค์ ํ์ผ", "CPU core", "VNet", "DNS zone"], "์ค์ ํ์ผ", "`purge`๋ ํจํค์ง ์ค์ ํ์ผ๊น์ง ์ ๊ฑฐํ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค.", source_id="ubuntu-package-management"), | |
| LabQuiz("azure-q11", "azure-entra-id", "azure", "multiple_choice", "์ธ์ฆ๊ณผ ์ฌ์ฉ์/๊ทธ๋ฃน ID ๊ด๋ฆฌ์ ์ค์ฌ ์๋น์ค๋?", ["Microsoft Entra ID", "Azure Blob Tier", "Load Balancer Probe", "Recovery Point"], "Microsoft Entra ID", "Entra ID๋ ID์ ์ธ์ฆ์ ์ค์ฌ์ด๊ณ RBAC๋ Azure ๋ฆฌ์์ค ๊ถํ ์ ์ด์ ์ฐ๊ฒฐ๋ฉ๋๋ค.", source_id="manual-az104-basic"), | |
| LabQuiz("azure-q12", "azure-management-group", "azure", "multiple_choice", "์ฌ๋ฌ subscription์ ์ ์ฑ ์ ๊ณ์ธต์ ์ผ๋ก ์ ์ฉํ๊ธฐ ์ข์ ๋ฒ์๋?", ["Management Group", "NIC", "Blob", "VM disk"], "Management Group", "Management Group์ subscription์ ๋ฌถ์ด ๊ฑฐ๋ฒ๋์ค๋ฅผ ์ ์ฉํ๋ ์์ ๋ฒ์์ ๋๋ค.", source_id="manual-az104-basic"), | |
| LabQuiz("azure-q13", "azure-resource-lock", "azure", "multiple_choice", "์ค์ ๋ฆฌ์์ค์ ์ค์ ์ญ์ ๋ฅผ ๋ง๋ ๋ฐ ์ ํฉํ ๊ธฐ๋ฅ์?", ["Resource Lock", "Access tier", "NAT rule", "KQL"], "Resource Lock", "CanNotDelete lock์ ์ญ์ ๋ฐฉ์ง์ ์ฌ์ฉ๋ฉ๋๋ค.", source_id="manual-az104-basic"), | |
| LabQuiz("azure-q14", "azure-sas", "azure", "multiple_choice", "Storage์ ์ ํ๋ ๊ธฐ๊ฐ/๊ถํ์ผ๋ก ์ ๊ทผํ๊ฒ ํ๋ URL ํ ํฐ์?", ["SAS", "NSG", "VMSS", "KQL"], "SAS", "Shared Access Signature๋ Storage ์ ๊ทผ ๊ถํ์ ์ ํ์ ์ผ๋ก ์์ํฉ๋๋ค.", source_id="azure-storage-account"), | |
| LabQuiz("azure-q15", "azure-storage-redundancy", "azure", "multiple_choice", "์ง์ญ ๊ฐ ๋ณต์ ๋ฅผ ๊ณ ๋ คํ๋ Storage redundancy ์ต์ ์ผ๋ก ๊ฐ์ฅ ๊ฐ๊น์ด ๊ฒ์?", ["GRS", "LRS only", "NSG", "UFW"], "GRS", "GRS๋ geo-redundant storage๋ก ์ง์ญ ๊ฐ ๋ณต์ ๋ฅผ ์ ๊ณตํฉ๋๋ค.", source_id="azure-storage-account"), | |
| LabQuiz("azure-q16", "azure-availability-set", "azure", "multiple_choice", "Availability Set์์ VM ์ฅ์ ๋ถ์ฐ์ ์ฐ์ด๋ ๊ฐ๋ ์?", ["Fault domain", "Blob container", "SAS token", "Private DNS record"], "Fault domain", "Fault domain/update domain์ Availability Set์ ํต์ฌ ๊ฐ๋ ์ ๋๋ค.", source_id="azure-vm-overview"), | |
| LabQuiz("azure-q17", "azure-vnet-peering", "azure", "multiple_choice", "VNet Peering์ด ์คํจํ ์ ์๋ ๋ํ ์์ธ์?", ["์ฃผ์ ๊ณต๊ฐ ๊ฒน์นจ", "ํ์ผ ๊ถํ 755", "cron ๋ฏธ์ค์ ", "Archive tier ์ฌ์ฉ"], "์ฃผ์ ๊ณต๊ฐ ๊ฒน์นจ", "VNet ์ฃผ์ ๊ณต๊ฐ์ด ๊ฒน์น๋ฉด peeringํ ์ ์์ต๋๋ค.", source_id="azure-vnet-overview"), | |
| LabQuiz("azure-q18", "azure-load-balancer-probe", "azure", "multiple_choice", "Backend VM์ด ํธ๋ํฝ์ ๋ฐ์ ์ ์๋์ง ํ๋จํ๋ Load Balancer ๊ตฌ์ฑ์?", ["Health probe", "Tag", "SAS", "Key Vault secret"], "Health probe", "Probe๊ฐ ์คํจํ๋ฉด backend pool VM์ ํธ๋ํฝ ๋์์์ ์ ์ธ๋ ์ ์์ต๋๋ค.", source_id="azure-load-balancer"), | |
| LabQuiz("azure-q19", "azure-private-dns", "azure", "multiple_choice", "Private Endpoint ์ ์ ๋ฌธ์ ์์ ํจ๊ป ํ์ธํด์ผ ํ ๋ํ ์์ญ์?", ["Private DNS", "umask", "tar", "cron"], "Private DNS", "Private Endpoint๋ DNS๊ฐ ์ฌ๋ฐ๋ฅธ private IP๋ก ํด์๋๋์ง ์ค์ํฉ๋๋ค.", source_id="azure-dns-overview"), | |
| LabQuiz("azure-q20", "azure-alert", "azure", "multiple_choice", "Azure Monitor Alert์์ ์ค์ ์๋ฆผ ๋์์ ์ ์ํ๋ ๊ตฌ์ฑ์?", ["Action Group", "Availability Set", "Storage tier", "Subnet mask"], "Action Group", "Alert rule์ ์กฐ๊ฑด, action group์ ์๋ฆผ/์กฐ์น ๋์์ ์ ์ํฉ๋๋ค.", source_id="azure-monitor-overview"), | |
| LabQuiz("azure-q21", "azure-backup-policy", "azure", "multiple_choice", "๋ฐฑ์ ๋น๋์ ๋ณด์กด ๊ธฐ๊ฐ์ ์ ์ํ๋ ๊ฒ์?", ["Backup Policy", "NSG Rule", "VNet Peering", "Access Tier"], "Backup Policy", "Backup Policy๋ ๋ฐฑ์ ์ผ์ ๊ณผ retention์ ์ ์ํฉ๋๋ค.", source_id="azure-recovery-services-vault"), | |
| LabQuiz("azure-q22", "azure-key-vault", "azure", "multiple_choice", "๋น๋ฐ๊ฐ๊ณผ ์ธ์ฆ์๋ฅผ ์์ ํ๊ฒ ์ ์ฅํ๋ Azure ์๋น์ค๋?", ["Key Vault", "Load Balancer", "VMSS", "Activity Log"], "Key Vault", "Key Vault๋ secret/key/certificate ๊ด๋ฆฌ๋ฅผ ์ํ ์๋น์ค์ ๋๋ค.", source_id="manual-az104-basic"), | |
| LabQuiz("azure-q23", "azure-bastion", "azure", "multiple_choice", "๊ณต์ธ IP ์์ด VM์ ์์ ํ๊ฒ RDP/SSH ์ ์ํ ๋ ์ ํฉํ ์๋น์ค๋?", ["Azure Bastion", "Blob lifecycle", "UFW", "Cron"], "Azure Bastion", "Azure Bastion์ VM public IP ์์ด ํฌํธ/๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ ์ ์์ ์ ๊ณตํฉ๋๋ค.", source_id="azure-bastion-overview"), | |
| LabQuiz("azure-q24", "azure-nat-gateway", "azure", "multiple_choice", "Private subnet์ ์์๋ฐ์ด๋ ์ธํฐ๋ท ์ฐ๊ฒฐ์ ๊ณ ์ ๊ณต์ธ IP๋ก ์ ๊ณตํ๋ ์๋น์ค๋?", ["NAT Gateway", "Private DNS", "Recovery Vault", "Policy Initiative"], "NAT Gateway", "NAT Gateway๋ subnet outbound ์ฐ๊ฒฐ์ ์ ํฉํฉ๋๋ค.", source_id="azure-nat-gateway"), | |
| LabQuiz("azure-q25", "azure-deployment-slot", "azure", "multiple_choice", "App Service์์ staging ๊ฒ์ฆ ํ production์ผ๋ก ์ ํํ๋ ๊ธฐ๋ฅ์?", ["Deployment slot swap", "VNet peering", "SAS rotation", "Fault domain"], "Deployment slot swap", "Deployment Slot์ App Service ๋ฐฐํฌ ์์ ํ์ ์ฌ์ฉ๋ฉ๋๋ค.", source_id="azure-app-service-overview"), | |
| LabQuiz("azure-q26", "azure-managed-identity", "azure", "multiple_choice", "์ฝ๋์ ๋น๋ฐ๊ฐ ์์ด Azure ๋ฆฌ์์ค๊ฐ Key Vault์ ์ ๊ทผํ๊ฒ ํ๋ ๋ฐฉ์์?", ["Managed Identity", "Access tier", "Sticky bit", "Archive restore"], "Managed Identity", "Managed Identity๋ Azure ๋ฆฌ์์ค์ Entra ID ๊ธฐ๋ฐ ID๋ฅผ ๋ถ์ฌํฉ๋๋ค.", source_id="azure-key-vault-overview"), | |
| LabQuiz("azure-q27", "azure-template-deployment", "azure", "multiple_choice", "Bicep/ARM ๋ฐฐํฌ์์ resource group/subscription ๊ฐ์ ๋ฐฐํฌ ๋ฒ์๋ฅผ ๋ปํ๋ ๊ฐ๋ ์?", ["target scope", "health probe", "authorized_keys", "umask"], "target scope", "๋ฐฐํฌ scope์ ๋ฐ๋ผ ์ฌ์ฉํ ์ ์๋ ํจ์์ ๋ฆฌ์์ค ๋ฒ์๊ฐ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.", source_id="manual-az104-basic"), | |
| ] | |
| ) | |
| PRACTICE_TASKS.extend( | |
| [ | |
| PracticeTask("linux-lab-14", "linux", "์ฌ์ฉ์ ์์ฑ", "`devuser` ์ฌ์ฉ์๋ฅผ ํ ๋๋ ํฐ๋ฆฌ์ ํจ๊ป ์์ฑํ์ธ์.", "sudo useradd -m devuser", ["useradd", "-m", "devuser"], "ํ ๋๋ ํฐ๋ฆฌ ์์ฑ ์ต์ ์ ํจ๊ป ์จ์ผ ํฉ๋๋ค.", "`sudo useradd -m devuser`๊ฐ ๋ํ ๋ช ๋ น์ ๋๋ค."), | |
| PracticeTask("linux-lab-15", "linux", "sudo ๊ทธ๋ฃน ์ถ๊ฐ", "`devuser`๋ฅผ sudo ๊ทธ๋ฃน์ ์ถ๊ฐํ์ธ์.", "sudo usermod -aG sudo devuser", ["usermod", "-aG", "sudo", "devuser"], "`-aG`๋ฅผ ํจ๊ป ์จ ๊ธฐ์กด ๊ทธ๋ฃน์ ์ ์งํ์ธ์.", "`sudo usermod -aG sudo devuser`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-16", "linux", "๊ทธ๋ฃน ํ์ธ", "`devuser`์ UID/GID์ ๊ทธ๋ฃน์ ํ์ธํ์ธ์.", "id devuser", ["id", "devuser"], "`id`๋ UID, GID, groups๋ฅผ ํจ๊ป ๋ณด์ฌ์ค๋๋ค.", "`id devuser`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-17", "linux", "๋ผ์ฐํ ํ์ธ", "ํ์ฌ ๋ผ์ฐํ ํ ์ด๋ธ์ ํ์ธํ์ธ์.", "ip route", ["ip", "route"], "์ธ๋ถ ํต์ ์ฅ์ ๋ default route๋ฅผ ํ์ธํ์ธ์.", "`ip route`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-18", "linux", "ํฌํธ ํ์ธ", "ํ์ฌ listening ํฌํธ๋ฅผ ํ์ธํ์ธ์.", "ss -tulpen", ["ss", "-"], "listening TCP/UDP ์์ผ์ ํ์ธํฉ๋๋ค.", "`ss -tulpen`์ ์ฌ์ฉํ ์ ์์ต๋๋ค."), | |
| PracticeTask("linux-lab-19", "linux", "cron ํธ์ง", "ํ์ฌ ์ฌ์ฉ์์ cron ์์ ์ ํธ์งํ์ธ์.", "crontab -e", ["crontab", "-e"], "์ฌ์ฉ์๋ณ ๋ฐ๋ณต ์์ ์ crontab์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค.", "`crontab -e`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-20", "linux", "๋์คํฌ ๊ตฌ์กฐ ํ์ธ", "๋ธ๋ก ์ฅ์น์ ํํฐ์ ๊ตฌ์กฐ๋ฅผ ํ์ธํ์ธ์.", "lsblk", ["lsblk"], "๋์คํฌ/ํํฐ์ /๋ง์ดํธ ๊ตฌ์กฐ๋ฅผ ๋ด ๋๋ค.", "`lsblk`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-21", "linux", "swap ํ์ธ", "ํ์ฌ ํ์ฑํ๋ swap์ ํ์ธํ์ธ์.", "swapon --show", ["swapon", "--show"], "swap ์ฌ์ฉ ์ฌ๋ถ๋ฅผ ํ์ธํฉ๋๋ค.", "`swapon --show`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-22", "linux", "์ง์ ๋ช ๋ น ๊ฒฐ๊ณผ ํ์ธ", "์ง์ ๋ช ๋ น์ exit code๋ฅผ ํ์ธํ์ธ์.", "echo $?", ["echo", "$?"], "`$?`๋ ์ง์ ๋ช ๋ น์ ์ข ๋ฃ ์ฝ๋์ ๋๋ค.", "`echo $?`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-23", "linux", "ํจํค์ง ํ๋ณด ํ์ธ", "`nginx` ํจํค์ง ํ๋ณด ๋ฒ์ ์ ํ์ธํ์ธ์.", "apt-cache policy nginx", ["apt-cache", "policy", "nginx"], "repository ๋ฌธ์ ๋ฅผ ๋ณผ ๋ ํจํค์ง ํ๋ณด๋ฅผ ํ์ธํฉ๋๋ค.", "`apt-cache policy nginx`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-24", "linux", "SSH ํค ์์ฑ", "ed25519 ๋ฐฉ์ SSH ํค๋ฅผ ์์ฑํ์ธ์.", "ssh-keygen -t ed25519", ["ssh-keygen", "-t", "ed25519"], "ํค ๊ธฐ๋ฐ ์ธ์ฆ์ ๊ณต๊ฐํค/๊ฐ์ธํค ์์ ๋ง๋ญ๋๋ค.", "`ssh-keygen -t ed25519`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-25", "linux", "DNS ์ํ ํ์ธ", "ํ์ฌ DNS resolver ์ํ๋ฅผ ํ์ธํ์ธ์.", "resolvectl status", ["resolvectl", "status"], "DNS ๋ฌธ์ ๋ ๋คํธ์ํฌ ์ฅ์ ์ ๊ตฌ๋ถํด์ ๋ด์ผ ํฉ๋๋ค.", "`resolvectl status`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-26", "linux", "HTTP ํค๋ ํ์ธ", "example.com์ HTTP ํค๋๋ง ํ์ธํ์ธ์.", "curl -I https://example.com", ["curl", "-I", "example.com"], "์๋น์ค ์๋ต ํ์ธ์ curl์ด ํธํฉ๋๋ค.", "`curl -I https://example.com`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-27", "linux", "์คํ ๋ฆฌ์ง ์ฌ์ฉ๋ ํ์ธ", "`/var/log` ๋๋ ํฐ๋ฆฌ ์ฌ์ฉ๋์ ์์ฝํด์ ํ์ธํ์ธ์.", "du -sh /var/log", ["du", "-sh", "/var/log"], "๋๋ ํฐ๋ฆฌ๋ณ ์ฌ์ฉ๋์ du๊ฐ ์ ํฉํฉ๋๋ค.", "`du -sh /var/log`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-28", "linux", "fstab ํ์ธ", "์๋ ๋ง์ดํธ ์ค์ ํ์ผ์ ์ถ๋ ฅํ์ธ์.", "cat /etc/fstab", ["cat", "/etc/fstab"], "๋ถํ ์ ๋ง์ดํธ ๋ฌธ์ ๋ fstab์ ํ์ธํฉ๋๋ค.", "`cat /etc/fstab`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-29", "linux", "ํ๊ฒฝ ๋ณ์ PATH ํ์ธ", "ํ์ฌ PATH ํ๊ฒฝ ๋ณ์๋ฅผ ์ถ๋ ฅํ์ธ์.", "echo $PATH", ["echo", "$PATH"], "๋ช ๋ น ๊ฒ์ ๊ฒฝ๋ก ํ์ธ์ ํ์ํฉ๋๋ค.", "`echo $PATH`๋ฅผ ์ฌ์ฉํฉ๋๋ค."), | |
| PracticeTask("linux-lab-30", "linux", "์ปค๋ ๋ก๊ทธ ํ์ธ", "์ปค๋ ๋ก๊ทธ ๋ง์ง๋ง ๋ถ๋ถ์ ํ์ธํ์ธ์.", "dmesg | tail", ["dmesg", "tail"], "๋ถํ /ํ๋์จ์ด ํํธ๋ dmesg์์ ๋ณผ ์ ์์ต๋๋ค.", "`dmesg | tail`์ ์ฌ์ฉํฉ๋๋ค."), | |
| ] | |
| ) | |
| def active_tracks() -> list[dict[str, Any]]: | |
| return [track for track in TRACKS if track["is_active"]] | |
| def normalize_track_id(track_id: str) -> str: | |
| if track_id == "azure-az104": | |
| return "azure" | |
| return track_id or "linux" | |
| def track_by_id(track_id: str) -> dict[str, Any]: | |
| normalized = normalize_track_id(track_id) | |
| return next((track for track in TRACKS if track["id"] == normalized), TRACKS[0]) | |
| def certifications_for_track(track_id: str) -> list[dict[str, str]]: | |
| return CERTIFICATIONS.get(normalize_track_id(track_id), []) | |
| def certification_by_id(certification_id: str) -> dict[str, str]: | |
| for certifications in CERTIFICATIONS.values(): | |
| for certification in certifications: | |
| if certification["id"] == certification_id: | |
| return certification | |
| return {"id": "", "name": "๋ฏธ์ ", "description": "", "readiness": "unknown", "study_mode": "๋ฏธ์ "} | |
| def certification_for_track(track_id: str) -> dict[str, str]: | |
| certifications = certifications_for_track(track_id) | |
| return certifications[0] if certifications else {"id": "", "name": "๋ฏธ์ ", "description": "", "readiness": "unknown", "study_mode": "๋ฏธ์ "} | |
| def lessons_for_track(track_id: str) -> list[LessonCard]: | |
| track_id = normalize_track_id(track_id) | |
| return [lesson for lesson in LESSONS if lesson.track == track_id and lesson.status == "approved"] | |
| def quizzes_for_track(track_id: str) -> list[LabQuiz]: | |
| track_id = normalize_track_id(track_id) | |
| return [quiz for quiz in QUIZZES if quiz.track == track_id and quiz.status == "approved"] | |
| def roadmap_for_track(track_id: str) -> list[RoadmapStep]: | |
| return ROADMAPS.get(normalize_track_id(track_id), []) | |
| def evaluate_lab_quiz(quiz: LabQuiz, answer: str) -> bool: | |
| normalized_answer = _normalize(answer) | |
| expected = _normalize(quiz.answer) | |
| if quiz.question_type == "command": | |
| return all(part in normalized_answer for part in expected.split()) | |
| return normalized_answer == expected | |
| def evaluate_lab_quiz_detail(quiz: LabQuiz, answer: str) -> tuple[bool, list[tuple[str, bool]]]: | |
| """(์ ์ฒด_์ ๋ต, [(ํ ํฐ, ์ผ์น์ฌ๋ถ), ...]) โ command ํ์ ๋ง ํ ํฐ ๋ถํด, ๋๋จธ์ง๋ ๋จ์ผ ํญ๋ชฉ.""" | |
| normalized_answer = _normalize(answer) | |
| expected = _normalize(quiz.answer) | |
| if quiz.question_type == "command": | |
| tokens = expected.split() | |
| results = [(tok, tok in normalized_answer) for tok in tokens] | |
| return all(ok for _, ok in results), results | |
| matched = normalized_answer == expected | |
| return matched, [(quiz.answer, matched)] | |
| def evaluate_practice(task: PracticeTask, command: str) -> bool: | |
| normalized = _normalize(command) | |
| return all(_normalize(condition) in normalized for condition in task.grading_conditions) | |
| def evaluate_practice_detail(task: PracticeTask, command: str) -> tuple[bool, list[tuple[str, bool]]]: | |
| """(์ ์ฒด_์ ๋ต, [(์กฐ๊ฑด๋ฌธ์์ด, ์ถฉ์กฑ์ฌ๋ถ), ...]).""" | |
| normalized = _normalize(command) | |
| results = [(_normalize(c), _normalize(c) in normalized) for c in task.grading_conditions] | |
| return all(ok for _, ok in results), results | |
| def track_progress(track_id: str, completed_lessons: set[str], completed_quizzes: set[str], completed_practices: set[str]) -> dict[str, int]: | |
| lessons = lessons_for_track(track_id) | |
| quizzes = quizzes_for_track(track_id) | |
| practices = [task for task in PRACTICE_TASKS if task.track == track_id and task.status == "approved"] | |
| total = len(lessons) + len(quizzes) + len(practices) | |
| completed = ( | |
| len({lesson.id for lesson in lessons} & completed_lessons) | |
| + len({quiz.id for quiz in quizzes} & completed_quizzes) | |
| + len({task.id for task in practices} & completed_practices) | |
| ) | |
| return {"completed": completed, "total": total, "percent": int((completed / total) * 100) if total else 0} | |
| def _normalize(value: str) -> str: | |
| return " ".join(str(value or "").strip().lower().split()) | |
| # โโ JSON content files (cert_study_app/content/{track}/*.json) โโโโโโโโโโโโโโโ | |
| def _lessons_from_content(track_id: str) -> list[LessonCard]: | |
| try: | |
| from cert_study_app.content.loader import load_lesson_dicts | |
| return [ | |
| LessonCard( | |
| d["id"], track_id, | |
| d.get("certification", ""), | |
| d["title"], d["summary"], | |
| d.get("example", ""), d.get("common_mistake", ""), | |
| d.get("keywords", []), d.get("source_id", "manual"), | |
| details=tuple(d.get("details", [])), | |
| level=d.get("level", "์ ๋ฌธ"), | |
| related_practices=tuple(d.get("related_practices", [])), | |
| ) | |
| for d in load_lesson_dicts(track_id) | |
| ] | |
| except Exception: | |
| return [] | |
| def _quizzes_from_content(track_id: str) -> list[LabQuiz]: | |
| try: | |
| from cert_study_app.content.loader import load_quiz_dicts | |
| return [ | |
| LabQuiz( | |
| d["id"], d.get("lesson_id", ""), track_id, | |
| d.get("question_type", "multiple_choice"), | |
| d["question"], d.get("options", []), | |
| d["answer"], d.get("explanation", ""), | |
| difficulty=d.get("difficulty", "easy"), | |
| source_id=d.get("source_id", "manual"), | |
| ) | |
| for d in load_quiz_dicts(track_id) | |
| ] | |
| except Exception: | |
| return [] | |
| def _practices_from_content(track_id: str) -> list[PracticeTask]: | |
| try: | |
| from cert_study_app.content.loader import load_practice_dicts | |
| return [ | |
| PracticeTask( | |
| d["id"], track_id, d["title"], | |
| d["task_description"], d["expected_command"], | |
| d.get("grading_conditions", []), | |
| d.get("hint", ""), d.get("explanation", ""), | |
| difficulty=d.get("difficulty", "easy"), | |
| takeaway=d.get("takeaway", ""), | |
| ) | |
| for d in load_practice_dicts(track_id) | |
| ] | |
| except Exception: | |
| return [] | |
| for _tid in ("linux", "azure", "tool_docs"): | |
| LESSONS.extend(_lessons_from_content(_tid)) | |
| QUIZZES.extend(_quizzes_from_content(_tid)) | |
| PRACTICE_TASKS.extend(_practices_from_content(_tid)) | |