Kentlo commited on
Commit
28677d8
ยท
verified ยท
1 Parent(s): 8b7e28b

Sync from GitHub 37f011465a0aa2abdfdd94d8bd5f31cfe932c669

Browse files
CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
  # Changelog
2
 
 
 
 
 
 
 
 
 
 
 
3
  ## 2026-07-04
4
 
5
  - Fixed AI quiz assistant crashing the page when ChromaDB collection is empty (added `min(k, count)` guard).
 
1
  # Changelog
2
 
3
+ ## 2026-07-04 (2)
4
+
5
+ - Set difficulty (easy/medium/hard) on 49 Linux quizzes that were previously unclassified.
6
+ - Added `level` (์ž…๋ฌธ/์ค‘๊ธ‰/๊ณ ๊ธ‰) to all 50 Azure lessons (was None for all).
7
+ - Linked `related_practices` on 20 Azure lessons to their corresponding practice task IDs.
8
+ - Added cumulative lesson/quiz/practice progress count (N/์ด๊ฐœ์ˆ˜) to home mode cards.
9
+ - Added overall quiz progress bar (ํ€ด์ฆˆ N/์ด๊ฐœ์ˆ˜ ์™„๋ฃŒ) to the top of the `ํ™•์ธ ํ€ด์ฆˆ` screen.
10
+ - Added overall practice progress bar (์‹ค์Šต N/์ด๊ฐœ์ˆ˜ ์™„๋ฃŒ) to the top of the `์‹ค์Šตํ•˜๊ธฐ` screen.
11
+ - Added "๊ด€๋ จ ์ด๋ก  ๋‹ค์‹œ ๋ณด๊ธฐ" button on practice failure, routing to the linked lesson (reverse `related_practices` lookup).
12
+
13
  ## 2026-07-04
14
 
15
  - Fixed AI quiz assistant crashing the page when ChromaDB collection is empty (added `min(k, count)` guard).
cert_study_app/content/__init__.py ADDED
File without changes
cert_study_app/content/azure/lessons.json ADDED
@@ -0,0 +1,1213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "azure-rg",
4
+ "certification": "AZ-104",
5
+ "title": "Resource Group โ€” ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน",
6
+ "summary": "Azure ๋ฆฌ์†Œ์Šค๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ฌถ๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค. ๋™์ผ RG์˜ ๋ฆฌ์†Œ์Šค๋Š” ํ•จ๊ป˜ ๋ฐฐํฌยท๊ด€๋ฆฌยท์‚ญ์ œ๋˜๋ฉฐ, ์œ„์น˜(region)๊ฐ€ ์„œ๋กœ ๋‹ฌ๋ผ๋„ ๊ฐ™์€ RG์— ์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
7
+ "example": "az group create --name myRG --location koreacentral\naz group list --output table\naz group delete --name myRG --yes",
8
+ "common_mistake": "RG๋ฅผ ์‚ญ์ œํ•˜๋ฉด ์•ˆ์— ์†ํ•œ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๊ฐ€ ํ•จ๊ป˜ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. RG ์œ„์น˜์™€ ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค ์œ„์น˜๋Š” ๋ณ„๊ฐœ์ž…๋‹ˆ๋‹ค.",
9
+ "keywords": [
10
+ "resource group",
11
+ "container",
12
+ "lifecycle",
13
+ "region",
14
+ "az group"
15
+ ],
16
+ "source_id": "azure-vm-overview",
17
+ "details": [
18
+ "RG๋Š” ๋ฆฌ์†Œ์Šค์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๋ณ„, ํ™˜๊ฒฝ๋ณ„(dev/prod), ๋˜๋Š” ํŒ€๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.",
19
+ "RG ์ž์ฒด์˜ ์œ„์น˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ ์œ„์น˜์ด๋ฉฐ, ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค๋Š” ๋‹ค๋ฅธ region์— ์žˆ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.",
20
+ "์‹œํ—˜ ํŒจํ„ด: RG ์‚ญ์ œ ์‹œ ์˜ํ–ฅ ๋ฒ”์œ„, RG์— RBACยทLockยทTag๋ฅผ ์ ์šฉํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค, RG ์ด๋™ ์ œํ•œ ์‚ฌํ•ญ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
21
+ ],
22
+ "level": "์ž…๋ฌธ",
23
+ "related_practices": [
24
+ "azure-lab-1"
25
+ ]
26
+ },
27
+ {
28
+ "id": "azure-storage",
29
+ "certification": "AZ-104",
30
+ "title": "Storage Account โ€” ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ •",
31
+ "summary": "Blob, File, Queue, Table 4๊ฐ€์ง€ ์Šคํ† ๋ฆฌ์ง€ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ตœ์ƒ์œ„ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. SKU(Standard/Premium)์™€ ์ค‘๋ณต์„ฑ(LRS/GRS ๋“ฑ)์„ ์ƒ์„ฑ ์‹œ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.",
32
+ "example": "az storage account create \\\n --name mystorageacct \\\n --resource-group myRG \\\n --sku Standard_LRS \\\n --kind StorageV2",
33
+ "common_mistake": "Storage Account ์ด๋ฆ„์€ ์ „ ์„ธ๊ณ„์—์„œ ์œ ์ผํ•ด์•ผ ํ•˜๋ฉฐ ์†Œ๋ฌธ์žยท์ˆซ์ž๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค(3~24์ž). SKU๋Š” ์ƒ์„ฑ ํ›„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
34
+ "keywords": [
35
+ "storage account",
36
+ "Blob",
37
+ "Files",
38
+ "LRS",
39
+ "GRS",
40
+ "SKU",
41
+ "StorageV2"
42
+ ],
43
+ "source_id": "azure-storage-account",
44
+ "details": [
45
+ "StorageV2(๋ฒ”์šฉ v2)๊ฐ€ ํ˜„์žฌ ๊ถŒ์žฅ ํ˜•์‹์œผ๋กœ ๋ชจ๋“  ์Šคํ† ๋ฆฌ์ง€ ์„œ๋น„์Šค์™€ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
46
+ "์ค‘๋ณต์„ฑ: LRS(๋‹จ์ผ datacenter 3๋ณต์ œ), ZRS(3 AZ), GRS(๋‹ค๋ฅธ region์œผ๋กœ ๋น„๋™๊ธฐ ๋ณต์ œ), GZRS(ZRS+GRS).",
47
+ "์‹œํ—˜ ํŒจํ„ด: ์ค‘๋ณต์„ฑ ์ˆ˜์ค€๋ณ„ ํŠน์ง•, ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ • ์ข…๋ฅ˜(Blob์ „์šฉ/StorageV2 ๋“ฑ), ๋ณด์•ˆ ์„ค์ •(HTTPS ๊ฐ•์ œ, ๋ฐฉํ™”๋ฒฝ) ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
48
+ ],
49
+ "level": "์ž…๋ฌธ",
50
+ "related_practices": [
51
+ "azure-lab-2"
52
+ ]
53
+ },
54
+ {
55
+ "id": "azure-vnet",
56
+ "certification": "AZ-104",
57
+ "title": "Virtual Network โ€” ๊ฐ€์ƒ ๋„คํŠธ์›Œํฌ",
58
+ "summary": "Azure์—์„œ ๋ฆฌ์†Œ์Šค๊ฐ€ ํ†ต์‹ ํ•˜๋Š” ๊ฒฉ๋ฆฌ๋œ ์‚ฌ์„ค ๋„คํŠธ์›Œํฌ์ž…๋‹ˆ๋‹ค. ์„œ๋ธŒ๋„ท์œผ๋กœ ๋‚˜๋‰˜๋ฉฐ, ์˜จํ”„๋ ˆ๋ฏธ์Šค ์—ฐ๊ฒฐ(VPN Gateway, ExpressRoute)๊ณผ ํ”ผ์–ด๋ง์„ ํ†ตํ•ด ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.",
59
+ "example": "az network vnet create \\\n --name myVNet \\\n --address-prefix 10.0.0.0/16 \\\n --subnet-name default \\\n --subnet-prefix 10.0.0.0/24",
60
+ "common_mistake": "VNet ์ฃผ์†Œ ๊ณต๊ฐ„์ด ๊ฒน์น˜๋ฉด ํ”ผ์–ด๋ง์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์„ค๊ณ„ ์ดˆ๊ธฐ์— IP ๋ฒ”์œ„๋ฅผ ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š๊ฒŒ ์žก์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
61
+ "keywords": [
62
+ "VNet",
63
+ "subnet",
64
+ "address space",
65
+ "CIDR",
66
+ "peering",
67
+ "NSG"
68
+ ],
69
+ "source_id": "azure-vnet-overview",
70
+ "details": [
71
+ "VNet ๋‚ด ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋กœ ํ†ต์‹  ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์ธํ„ฐ๋„ท ํ†ต์‹ ์€ Public IP ๋˜๋Š” NAT Gateway๋ฅผ ํ†ตํ•ฉ๋‹ˆ๋‹ค.",
72
+ "์„œ๋ธŒ๋„ท์€ VNet ์ฃผ์†Œ ๊ณต๊ฐ„์˜ ์ผ๋ถ€. Azure๊ฐ€ ๊ฐ ์„œ๋ธŒ๋„ท์—์„œ 5๊ฐœ IP๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค(์ฒซ 4๊ฐœ + ๋งˆ์ง€๋ง‰).",
73
+ "์‹œํ—˜ ํŒจํ„ด: VNet/์„œ๋ธŒ๋„ท ์ฃผ์†Œ ๋ฒ”์œ„ ๊ณ„์‚ฐ, NSG์™€ ์„œ๋ธŒ๋„ท ์—ฐ๊ฒฐ, ํ”ผ์–ด๋ง ์กฐ๊ฑด(์ฃผ์†Œ ๋น„์ค‘์ฒฉ) ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
74
+ ],
75
+ "level": "์ž…๋ฌธ",
76
+ "related_practices": [
77
+ "azure-lab-3"
78
+ ]
79
+ },
80
+ {
81
+ "id": "azure-rbac",
82
+ "certification": "AZ-104",
83
+ "title": "Azure RBAC โ€” ์—ญํ•  ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์ œ์–ด",
84
+ "summary": "๋ˆ„๊ฐ€(Who), ์–ด๋–ค ๋ฆฌ์†Œ์Šค์—(Scope), ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€(Role)๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ญํ•  ํ• ๋‹น์€ ๊ตฌ๋…ยทRGยท๋ฆฌ์†Œ์Šค ๋ ˆ๋ฒจ์—์„œ ์ƒ์†๋ฉ๋‹ˆ๋‹ค.",
85
+ "example": "az role assignment create \\\n --assignee alice@example.com \\\n --role 'Contributor' \\\n --scope /subscriptions/SUB_ID/resourceGroups/myRG",
86
+ "common_mistake": "RBAC๋Š” ๊ด€๋ฆฌ ํ‰๋ฉด(Azure Portal/API) ์ ‘๊ทผ ์ œ์–ด์ž…๋‹ˆ๋‹ค. VM ๋‚ด๋ถ€ OS ์ ‘๊ทผ์ด๋‚˜ Storage ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์€ ๋ณ„๋„ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
87
+ "keywords": [
88
+ "RBAC",
89
+ "role assignment",
90
+ "Owner",
91
+ "Contributor",
92
+ "Reader",
93
+ "scope",
94
+ "principal"
95
+ ],
96
+ "source_id": "azure-rbac-overview",
97
+ "details": [
98
+ "๋‚ด์žฅ ์—ญํ•  3๊ฐ€์ง€: Owner(๋ชจ๋“  ๊ถŒํ•œ+์—ญํ•  ํ• ๋‹น), Contributor(๋ฆฌ์†Œ์Šค CRUD, ์—ญํ•  ํ• ๋‹น ๋ถˆ๊ฐ€), Reader(์ฝ๊ธฐ๋งŒ).",
99
+ "์—ญํ•  ํ• ๋‹น ๋ฒ”์œ„: ๊ด€๋ฆฌ ๊ทธ๋ฃน > ๊ตฌ๋… > RG > ๋ฆฌ์†Œ์Šค. ์ƒ์œ„ ๋ฒ”์œ„ ์—ญํ• ์€ ํ•˜์œ„๋กœ ์ƒ์†๋ฉ๋‹ˆ๋‹ค.",
100
+ "์‹œํ—˜ ํŒจํ„ด: ์ตœ์†Œ ๊ถŒํ•œ ์›์น™์— ๋”ฐ๋ฅธ ์—ญํ•  ์„ ํƒ, ๋ฒ”์œ„๋ณ„ ์—ญํ•  ์ƒ์† ๊ฒฐ๊ณผ, Owner vs Contributor ์ฐจ์ด ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
101
+ ],
102
+ "level": "์ž…๋ฌธ",
103
+ "related_practices": [
104
+ "azure-lab-5"
105
+ ]
106
+ },
107
+ {
108
+ "id": "azure-policy",
109
+ "certification": "AZ-104",
110
+ "title": "Azure Policy โ€” ์ •์ฑ…",
111
+ "summary": "๋ฆฌ์†Œ์Šค๊ฐ€ ์กฐ์ง์˜ ๊ทœ์ •์„ ์ค€์ˆ˜ํ•˜๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • region๋งŒ ํ—ˆ์šฉ, ํƒœ๊ทธ ๊ฐ•์ œ, SKU ์ œํ•œ ๋“ฑ์„ ์ •์ฑ… ์ •์˜๋กœ ๋งŒ๋“ค์–ด ๊ตฌ๋…ยทRG์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.",
112
+ "example": "# ํ—ˆ์šฉ location ์ œํ•œ ์ •์ฑ… ํ• ๋‹น ์˜ˆ\naz policy assignment create \\\n --name 'allowed-locations' \\\n --policy 'e56962a6-4747-49cd-b67b-bf8b01975c4f' \\\n --params '{\"listOfAllowedLocations\":{\"value\":[\"koreacentral\"]}}'",
113
+ "common_mistake": "Policy๋Š” ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋น„์ค€์ˆ˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ˆ˜์ •์ด ํ•„์š”ํ•˜๋ฉด remediation task๋ฅผ ๋ณ„๋„๋กœ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
114
+ "keywords": [
115
+ "Azure Policy",
116
+ "policy definition",
117
+ "initiative",
118
+ "compliance",
119
+ "deny",
120
+ "audit",
121
+ "remediation"
122
+ ],
123
+ "source_id": "azure-policy-overview",
124
+ "details": [
125
+ "ํšจ๊ณผ(effect) ์œ ํ˜•: Deny(๊ฑฐ๋ถ€), Audit(๊ฐ์‚ฌ๋งŒ), AuditIfNotExists, DeployIfNotExists(์ž๋™ ๋ณด์™„ ๋ฐฐํฌ), Disabled.",
126
+ "Initiative(์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ)๋Š” ์—ฌ๋Ÿฌ Policy๋ฅผ ๋ฌถ์€ ์ง‘ํ•ฉ. NIST, CIS ๋“ฑ ๊ทœ์ • ์ค€์ˆ˜ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.",
127
+ "์‹œํ—˜ ํŒจํ„ด: Policy์™€ RBAC ์ฐจ์ด(RBAC๋Š” ํ–‰์œ„ ์ œ์–ด, Policy๋Š” ๋ฆฌ์†Œ์Šค ์†์„ฑ ์ œ์–ด), ํšจ๊ณผ ์œ ํ˜•๋ณ„ ๋™์ž‘, ์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ vs ๋‹จ๋… ์ •์ฑ… ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
128
+ ],
129
+ "level": "์ž…๋ฌธ"
130
+ },
131
+ {
132
+ "id": "azure-vm",
133
+ "certification": "AZ-104",
134
+ "title": "Virtual Machine โ€” ๊ฐ€์ƒ ๋จธ์‹ ",
135
+ "summary": "Azure์—์„œ ์ œ๊ณตํ•˜๋Š” IaaS ์ปดํ“จํŒ…์ž…๋‹ˆ๋‹ค. VM ํฌ๊ธฐ(SKU), OS ์ด๋ฏธ์ง€, ๊ฐ€์šฉ์„ฑ ๊ตฌ์„ฑ, ๋””์Šคํฌ ์œ ํ˜•์„ ์„ ํƒํ•ด ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.",
136
+ "example": "az vm create \\\n --name myVM \\\n --resource-group myRG \\\n --image Ubuntu2204 \\\n --size Standard_B2s \\\n --admin-username azureuser \\\n --generate-ssh-keys",
137
+ "common_mistake": "VM์„ '์ค‘์ง€(Stop)'ํ•ด๋„ ํ• ๋‹น ํ•ด์ œ(deallocated)๊ฐ€ ์•„๋‹ˆ๋ฉด ์ปดํ“จํŒ… ์š”๊ธˆ์ด ๊ณ„์† ์ฒญ๊ตฌ๋ฉ๋‹ˆ๋‹ค. ๋น„์šฉ ์ ˆ๊ฐ์„ ์œ„ํ•ด '์ค‘์ง€(ํ• ๋‹น ํ•ด์ œ)'๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
138
+ "keywords": [
139
+ "VM",
140
+ "SKU",
141
+ "availability set",
142
+ "managed disk",
143
+ "deallocate",
144
+ "NSG",
145
+ "public IP"
146
+ ],
147
+ "source_id": "azure-vm-overview",
148
+ "details": [
149
+ "VM ํฌ๊ธฐ ์‹œ๋ฆฌ์ฆˆ: B(๋ฒ„์ŠคํŠธ, ์ €๋น„์šฉ), D(๋ฒ”์šฉ), E(๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”), F(์ปดํ“จํŒ… ์ตœ์ ํ™”), N(GPU). ์ˆซ์ž๋Š” vCPU ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.",
150
+ "VM ์ƒ์„ฑ ์‹œ ์ž๋™์œผ๋กœ NIC, OS ๋””์Šคํฌ, Public IP(์„ ํƒ)๊ฐ€ ํ•จ๊ป˜ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
151
+ "์‹œํ—˜ ํŒจํ„ด: ์ค‘์ง€ vs ํ• ๋‹น ํ•ด์ œ ์š”๊ธˆ ์ฐจ์ด, VM ํฌ๊ธฐ ๋ณ€๊ฒฝ ์‹œ ์žฌ์‹œ์ž‘ ํ•„์š” ์—ฌ๋ถ€, ๊ฐ€์šฉ์„ฑ SLA 99.9/99.95/99.99% ๊ตฌ์„ฑ ๋ฐฉ๋ฒ•์ด ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
152
+ ],
153
+ "level": "์ž…๋ฌธ"
154
+ },
155
+ {
156
+ "id": "azure-vmss",
157
+ "certification": "AZ-104",
158
+ "title": "Virtual Machine Scale Sets โ€” ๊ฐ€์ƒ ๋จธ์‹  ํ™•์žฅ ์ง‘ํ•ฉ",
159
+ "summary": "๋™์ผํ•œ VM์„ ์ž๋™์œผ๋กœ ํ™•์žฅยท์ถ•์†Œํ•˜๋Š” ๊ทธ๋ฃน์ž…๋‹ˆ๋‹ค. ๋ถ€ํ•˜์— ๋”ฐ๋ผ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ์ž๋™ ์กฐ์ •(Autoscale)ํ•˜๊ณ , ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์žฅ์น˜์™€ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
160
+ "example": "az vmss create \\\n --name myVMSS \\\n --resource-group myRG \\\n --image Ubuntu2204 \\\n --instance-count 2 \\\n --vm-sku Standard_B2s",
161
+ "common_mistake": "VMSS์˜ ๊ฐœ๋ณ„ ์ธ์Šคํ„ด์Šค๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ ์ •์ฑ…(์ž๋™/์ˆ˜๋™/๋กค๋ง)์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. ์ž๋™ ์ •์ฑ…์€ ์ธ์Šคํ„ด์Šค๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
162
+ "keywords": [
163
+ "VMSS",
164
+ "scale set",
165
+ "autoscale",
166
+ "instance count",
167
+ "upgrade policy",
168
+ "load balancer"
169
+ ],
170
+ "source_id": "azure-vmss-overview",
171
+ "details": [
172
+ "Orchestration ๋ชจ๋“œ: Uniform(๋™์ผ VM, ์ž๋™ ํ™•์žฅ ์ตœ์ ํ™”) vs Flexible(ํ˜ผํ•ฉ VM ํฌ๊ธฐ ๊ฐ€๋Šฅ, Availability Zone ์ง€์› ๊ฐ•ํ™”).",
173
+ "Autoscale ๊ทœ์น™: CPU%, ๋ฉ”๋ชจ๋ฆฌ, ํ ๊ธธ์ด ๋“ฑ ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ฐ˜์œผ๋กœ scale-out/in ์ž„๊ณ„๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.",
174
+ "์‹œํ—˜ ํŒจํ„ด: VMSS vs Availability Set ์ฐจ์ด(AS๋Š” ์ž๋™ ํ™•์žฅ ์—†์Œ), ์—…๊ทธ๋ ˆ์ด๋“œ ์ •์ฑ… ์„ ํƒ, ๋ถ€ํ•˜ ๋ถ„์‚ฐ๊ธฐ ํ†ตํ•ฉ ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
175
+ ],
176
+ "level": "์ค‘๊ธ‰"
177
+ },
178
+ {
179
+ "id": "azure-nsg",
180
+ "certification": "AZ-104",
181
+ "title": "Network Security Group โ€” ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ๊ทธ๋ฃน",
182
+ "summary": "์ธ๋ฐ”์šด๋“œ/์•„์›ƒ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ์„ ํฌํŠธยทํ”„๋กœํ† ์ฝœยท์›๋ณธIP๋กœ ํ—ˆ์šฉ/๊ฑฐ๋ถ€ํ•˜๋Š” ๊ทœ์น™ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ์„œ๋ธŒ๋„ท ๋˜๋Š” NIC์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
183
+ "example": "az network nsg rule create \\\n --nsg-name myNSG \\\n --name allow-ssh \\\n --priority 100 \\\n --access Allow \\\n --protocol Tcp \\\n --destination-port-ranges 22",
184
+ "common_mistake": "์šฐ์„ ์ˆœ์œ„ ์ˆซ์ž๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋จผ์ € ์ ์šฉ๋ฉ๋‹ˆ๋‹ค(100์ด 200๋ณด๋‹ค ๋†’์€ ์šฐ์„ ์ˆœ์œ„). ๊ทœ์น™ ์ถฉ๋Œ ์‹œ ๋‚ฎ์€ ๋ฒˆํ˜ธ ๊ทœ์น™์ด ์ด๊น๋‹ˆ๋‹ค.",
185
+ "keywords": [
186
+ "NSG",
187
+ "inbound",
188
+ "outbound",
189
+ "priority",
190
+ "allow",
191
+ "deny",
192
+ "subnet",
193
+ "NIC"
194
+ ],
195
+ "source_id": "azure-nsg-overview",
196
+ "details": [
197
+ "NSG์—๋Š” ๊ธฐ๋ณธ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค: AllowVNetInbound(65000), AllowAzureLoadBalancerInbound(65001), DenyAllInbound(65500).",
198
+ "NSG๋ฅผ ์„œ๋ธŒ๋„ท๊ณผ NIC ์–‘์ชฝ์— ์—ฐ๊ฒฐํ•˜๋ฉด ํŠธ๋ž˜ํ”ฝ์ด ๋‘ NSG๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. ์ธ๋ฐ”์šด๋“œ๋Š” ์„œ๋ธŒ๋„ทNSGโ†’NIC NSG ์ˆœ์„œ.",
199
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํฌํŠธ ํ—ˆ์šฉ/์ฐจ๋‹จ ๊ทœ์น™ ์„ค๊ณ„, ์šฐ์„ ์ˆœ์œ„ ์ถฉ๋Œ ๊ฒฐ๊ณผ, ์„œ๋ธŒ๋„ท vs NIC ์—ฐ๊ฒฐ ์ฐจ์ด ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
200
+ ],
201
+ "level": "์ž…๋ฌธ",
202
+ "related_practices": [
203
+ "azure-lab-4"
204
+ ]
205
+ },
206
+ {
207
+ "id": "azure-app-gateway",
208
+ "certification": "AZ-104",
209
+ "title": "Application Gateway โ€” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฒŒ์ดํŠธ์›จ์ด",
210
+ "summary": "HTTP/HTTPS ๋ ˆ์ด์–ด(L7) ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์žฅ์น˜์ž…๋‹ˆ๋‹ค. URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…, SSL ์ข…๋ฃŒ, WAF(์›น ๋ฐฉํ™”๋ฒฝ)๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
211
+ "example": "# ๊ตฌ์„ฑ ์š”์†Œ: Frontend IP โ†’ Listener โ†’ Rule โ†’ Backend Pool โ†’ HTTP Settings\n# URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ์˜ˆ:\n# /images/* โ†’ image-pool\n# /api/* โ†’ api-pool",
212
+ "common_mistake": "Application Gateway๋Š” L7, Azure Load Balancer๋Š” L4์ž…๋‹ˆ๋‹ค. URL ๊ฒฝ๋กœ๋‚˜ ํ˜ธ์ŠคํŠธ ํ—ค๋” ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์ด ํ•„์š”ํ•˜๋ฉด App Gateway๋ฅผ ์”๋‹ˆ๋‹ค.",
213
+ "keywords": [
214
+ "Application Gateway",
215
+ "L7",
216
+ "WAF",
217
+ "SSL termination",
218
+ "URL routing",
219
+ "backend pool"
220
+ ],
221
+ "source_id": "azure-app-gateway-overview",
222
+ "details": [
223
+ "๊ตฌ์„ฑ ํ๋ฆ„: Frontend IP โ†’ Listener(ํฌํŠธ/ํ”„๋กœํ† ์ฝœ) โ†’ Routing Rule โ†’ Backend Pool(VM/VMSS/IP) โ†’ HTTP Settings.",
224
+ "WAF SKU๋Š” OWASP ๊ทœ์น™์…‹์œผ๋กœ SQL injection, XSS ๋“ฑ ์›น ๊ณต๊ฒฉ์„ ํƒ์ง€ยท์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.",
225
+ "์‹œํ—˜ ํŒจํ„ด: App Gateway vs Load Balancer ์„ ํƒ ๊ธฐ์ค€, URL ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๊ตฌ์„ฑ, SSL ์˜คํ”„๋กœ๋”ฉ ๊ฐœ๋…์ด ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
226
+ ],
227
+ "level": "์ค‘๊ธ‰"
228
+ },
229
+ {
230
+ "id": "azure-dns",
231
+ "certification": "AZ-104",
232
+ "title": "Azure DNS โ€” ๋„๋ฉ”์ธ ์ด๋ฆ„ ์„œ๋น„์Šค",
233
+ "summary": "Azure์—์„œ DNS ์˜์—ญ์„ ํ˜ธ์ŠคํŒ…ํ•ฉ๋‹ˆ๋‹ค. Public DNS Zone์€ ์ธํ„ฐ๋„ท์šฉ, Private DNS Zone์€ VNet ๋‚ด๋ถ€ ์ด๋ฆ„ ํ•ด์„์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
234
+ "example": "az network dns zone create --name example.com --resource-group myRG\naz network dns record-set a add-record \\\n --zone-name example.com \\\n --record-set-name www \\\n --ipv4-address 1.2.3.4",
235
+ "common_mistake": "Azure DNS๋Š” ๋„๋ฉ”์ธ ๋“ฑ๋ก ์„œ๋น„์Šค๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ์€ ๋ณ„๋„ ๋“ฑ๋ก ๊ธฐ๊ด€์—์„œ ๊ตฌ๋งค ํ›„ ๋„ค์ž„์„œ๋ฒ„๋ฅผ Azure๋กœ ์œ„์ž„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
236
+ "keywords": [
237
+ "Azure DNS",
238
+ "Public Zone",
239
+ "Private Zone",
240
+ "A record",
241
+ "CNAME",
242
+ "NS delegation"
243
+ ],
244
+ "source_id": "azure-dns-overview",
245
+ "details": [
246
+ "Public DNS Zone ์ƒ์„ฑ ํ›„ Azure๊ฐ€ ์ œ๊ณตํ•˜๋Š” NS ๋ ˆ์ฝ”๋“œ 4๊ฐœ๋ฅผ ๋„๋ฉ”์ธ ๋“ฑ๋ก ๊ธฐ๊ด€์—์„œ ์„ค์ •ํ•ด ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค.",
247
+ "Private DNS Zone์€ VNet์— ์—ฐ๊ฒฐ(link)ํ•˜๋ฉด ํ•ด๋‹น VNet์˜ VM์ด ์‚ฌ์„ค ๋„๋ฉ”์ธ ์ด๋ฆ„์„ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
248
+ "์‹œํ—˜ ํŒจํ„ด: Public vs Private Zone ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค, ๋„ค์ž„์„œ๋ฒ„ ์œ„์ž„ ์ ˆ์ฐจ, Azure ์„œ๋น„์Šค์— DNS๋ณ„์นญ(CNAME/alias) ์—ฐ๊ฒฐ ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
249
+ ],
250
+ "level": "์ค‘๊ธ‰"
251
+ },
252
+ {
253
+ "id": "azure-blob-tier",
254
+ "certification": "AZ-104",
255
+ "title": "Blob Access Tier โ€” Blob ์ ‘๊ทผ ๊ณ„์ธต",
256
+ "summary": "๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๋นˆ๋„์— ๋”ฐ๋ผ HotยทCoolยทColdยทArchive ๊ณ„์ธต์„ ์„ ํƒํ•ด ์Šคํ† ๋ฆฌ์ง€ ๋น„์šฉ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ €์žฅ ๋น„์šฉ๊ณผ ์ ‘๊ทผ ๋น„์šฉ์€ ๋ฐ˜๋น„๋ก€ํ•ฉ๋‹ˆ๋‹ค.",
257
+ "example": "az storage blob set-tier \\\n --account-name myaccount \\\n --container-name mycontainer \\\n --name myblob.log \\\n --tier Cool",
258
+ "common_mistake": "Archive ๊ณ„์ธต์˜ Blob์€ ์ฆ‰์‹œ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ฝ์œผ๋ ค๋ฉด Hot ๋˜๋Š” Cool๋กœ rehydration(๋ช‡ ์‹œ๊ฐ„~15์‹œ๊ฐ„)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
259
+ "keywords": [
260
+ "blob tier",
261
+ "Hot",
262
+ "Cool",
263
+ "Cold",
264
+ "Archive",
265
+ "rehydration",
266
+ "lifecycle"
267
+ ],
268
+ "source_id": "azure-blob-access-tiers",
269
+ "details": [
270
+ "๊ณ„์ธต๋ณ„ ํŠน์ง•: Hot(์ž์ฃผ ์ ‘๊ทผ, ๋†’์€ ์ €์žฅ ๋น„์šฉ ๋‚ฎ์€ ์ ‘๊ทผ ๋น„์šฉ), Cool(30์ผ ์ด์ƒ ๋ณด๊ด€), Cold(90์ผ ์ด์ƒ), Archive(180์ผ ์ด์ƒ, ์ ‘๊ทผ ๋น„์šฉ ์ตœ๊ณ ).",
271
+ "Archive์—์„œ ๊บผ๋‚ผ ๋•Œ ์šฐ์„ ์ˆœ์œ„: Standard(์ตœ๋Œ€ 15์‹œ๊ฐ„), High Priority(1์‹œ๊ฐ„ ์ด๋‚ด, ๋น„์Œˆ).",
272
+ "์‹œํ—˜ ํŒจํ„ด: ๊ณ„์ธต๋ณ„ ์ตœ์†Œ ๋ณด๊ด€ ๊ธฐ๊ฐ„(Cool=30์ผ, Cold=90์ผ, Archive=180์ผ), ์กฐ๊ธฐ ์‚ญ์ œ ์‹œ ์š”๊ธˆ ๋ถ€๊ณผ, rehydration ์‹œ๊ฐ„ ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
273
+ ],
274
+ "level": "์ž…๋ฌธ",
275
+ "related_practices": [
276
+ "azure-lab-7"
277
+ ]
278
+ },
279
+ {
280
+ "id": "azure-log-analytics",
281
+ "certification": "AZ-104",
282
+ "title": "Log Analytics Workspace โ€” ๋กœ๊ทธ ๋ถ„์„ ์ž‘์—… ์˜์—ญ",
283
+ "summary": "Azure Monitor์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค. VMยท์„œ๋น„์Šค์˜ ๋กœ๊ทธ์™€ ๋ฉ”ํŠธ๋ฆญ์„ ์ˆ˜์ง‘ํ•˜๊ณ  KQL(Kusto Query Language)๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.",
284
+ "example": "# KQL ์˜ˆ: ์ตœ๊ทผ 1์‹œ๊ฐ„ ์˜ค๋ฅ˜ ๋กœ๊ทธ\nEvent\n| where TimeGenerated > ago(1h)\n| where EventLevelName == 'Error'\n| summarize count() by Source",
285
+ "common_mistake": "Log Analytics๋Š” ์ €์žฅ์†Œ์ด๊ณ  Azure Monitor๊ฐ€ ์ˆ˜์ง‘ ์—์ด์ „ํŠธยท๊ฒฝ๋ณด๋ฅผ ํฌํ•จํ•œ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ๋‘˜์€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋ฉฐ ๋ณ„๊ฐœ์ž…๋‹ˆ๋‹ค.",
286
+ "keywords": [
287
+ "Log Analytics",
288
+ "KQL",
289
+ "workspace",
290
+ "diagnostic settings",
291
+ "MMA",
292
+ "AMA"
293
+ ],
294
+ "source_id": "azure-log-analytics-overview",
295
+ "details": [
296
+ "๋ฐ์ดํ„ฐ ์ˆ˜์ง‘: VM์— Azure Monitor Agent(AMA) ์„ค์น˜ ๋˜๋Š” Diagnostic Settings๋กœ Azure ์„œ๋น„์Šค ๋กœ๊ทธ๋ฅผ Workspace๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.",
297
+ "KQL ํ•ต์‹ฌ: `| where`, `| project`(์ปฌ๋Ÿผ ์„ ํƒ), `| summarize`(์ง‘๊ณ„), `| order by`, `| take`(์ƒ์œ„ N๊ฐœ).",
298
+ "์‹œํ—˜ ํŒจํ„ด: Diagnostic Settings ์„ค์ • ์ ˆ์ฐจ, VM์— ์—์ด์ „ํŠธ ์—ฐ๊ฒฐ, KQL ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ฟผ๋ฆฌํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
299
+ ],
300
+ "level": "์ค‘๊ธ‰"
301
+ },
302
+ {
303
+ "id": "azure-recovery-vault",
304
+ "certification": "AZ-104",
305
+ "title": "Recovery Services Vault โ€” ๋ณต๊ตฌ ์„œ๋น„์Šค ์ž๊ฒฉ ์ฆ๋ช… ๋ชจ์Œ",
306
+ "summary": "Azure Backup๊ณผ Site Recovery ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค. VM, SQL, Files ๋“ฑ์˜ ๋ฐฑ์—… ์ •์ฑ…์„ ์ •์˜ํ•˜๊ณ  ๋ณต์› ์ง€์ ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
307
+ "example": "az backup vault create \\\n --name myVault \\\n --resource-group myRG \\\n --location koreacentral\naz backup protection enable-for-vm \\\n --vault-name myVault \\\n --vm myVM \\\n --policy-name DefaultPolicy",
308
+ "common_mistake": "Recovery Services Vault๋Š” ๋ฐฑ์—… ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์™€ ๊ฐ™์€ region์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ region์˜ VM์€ ๋ฐฑ์—…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
309
+ "keywords": [
310
+ "Recovery Services Vault",
311
+ "Azure Backup",
312
+ "backup policy",
313
+ "restore point",
314
+ "soft delete"
315
+ ],
316
+ "source_id": "azure-recovery-services-vault",
317
+ "details": [
318
+ "๋ฐฑ์—… ์ •์ฑ…: ์Šค์ผ€์ค„(์ผ๋ณ„/์ฃผ๋ณ„), ๋ณด์กด ๊ธฐ๊ฐ„(์ผ/์ฃผ/์›”/๋…„)์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. VM์€ ๊ธฐ๋ณธ ์ •์ฑ…(์ผ 1ํšŒ, 30์ผ ๋ณด์กด)์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
319
+ "Soft Delete: ๋ฐฑ์—… ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œ ํ›„ 14์ผ๊ฐ„ ๋ณต์› ๊ฐ€๋Šฅ ์ƒํƒœ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋žœ์„ฌ์›จ์–ด ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค.",
320
+ "์‹œํ—˜ ํŒจํ„ด: ๋ฐฑ์—… ์ •์ฑ… ๊ตฌ์„ฑ, ๋ณต์› ์œ ํ˜•(์›๋ž˜ ์œ„์น˜/์ƒˆ ์œ„์น˜), ์ง€์—ญ ๊ฐ„ ๋ณต์›(CRR) ๊ธฐ๋Šฅ, Soft Delete ํ™œ์„ฑํ™” ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
321
+ ],
322
+ "level": "์ค‘๊ธ‰",
323
+ "related_practices": [
324
+ "azure-lab-8"
325
+ ]
326
+ },
327
+ {
328
+ "id": "azure-entra-id",
329
+ "certification": "AZ-104",
330
+ "title": "Microsoft Entra ID โ€” ID ๊ด€๋ฆฌ",
331
+ "summary": "Azure์˜ ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ ID ๋ฐ ์ ‘๊ทผ ๊ด€๋ฆฌ(IAM) ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค(๊ตฌ Azure Active Directory). ์‚ฌ์šฉ์žยท๊ทธ๋ฃนยท์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ID๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  MFA, ์กฐ๊ฑด๋ถ€ ์ ‘๊ทผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
332
+ "example": "az ad user create \\\n --display-name 'Alice Kim' \\\n --user-principal-name alice@mydomain.com \\\n --password 'TempPass123!'\naz ad group create --display-name 'Developers' --mail-nickname 'devs'",
333
+ "common_mistake": "Entra ID์™€ ์˜จํ”„๋ ˆ๋ฏธ์Šค Active Directory๋Š” ๋‹ค๋ฅธ ์ œํ’ˆ์ž…๋‹ˆ๋‹ค. Entra ID๋Š” ํด๋ผ์šฐ๋“œ LDAP/Kerberos๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ๋„๋ฉ”์ธ ์กฐ์ธ์€ Entra Domain Services๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
334
+ "keywords": [
335
+ "Entra ID",
336
+ "AAD",
337
+ "tenant",
338
+ "user",
339
+ "group",
340
+ "MFA",
341
+ "SSO",
342
+ "B2B",
343
+ "B2C"
344
+ ],
345
+ "source_id": "azure-rbac-overview",
346
+ "details": [
347
+ "ํ…Œ๋„ŒํŠธ(Tenant)๋Š” Entra ID์˜ ์ธ์Šคํ„ด์Šค์ด๋ฉฐ ์กฐ์ง ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ๊ตฌ๋…์€ ๋ฐ˜๋“œ์‹œ ํ•˜๋‚˜์˜ ํ…Œ๋„ŒํŠธ์— ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
348
+ "์‚ฌ์šฉ์ž ์œ ํ˜•: Member(๋‚ด๋ถ€ ์‚ฌ์šฉ์ž) vs Guest(B2B ์ดˆ๋Œ€ ์‚ฌ์šฉ์ž). ๊ทธ๋ฃน ์œ ํ˜•: Security(RBAC ํ• ๋‹น์šฉ) vs Microsoft 365.",
349
+ "์‹œํ—˜ ํŒจํ„ด: ์‚ฌ์šฉ์ž/๊ทธ๋ฃน ์ƒ์„ฑยท๊ด€๋ฆฌ, ๊ฒŒ์ŠคํŠธ ์‚ฌ์šฉ์ž ์ดˆ๋Œ€(B2B), MFA ๋“ฑ๋ก, SSPR ์„ค์ •, Entra ID vs ์˜จํ”„๋ ˆ๋ฏธ์Šค AD ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
350
+ ],
351
+ "level": "์ž…๋ฌธ"
352
+ },
353
+ {
354
+ "id": "azure-management-group",
355
+ "certification": "AZ-104",
356
+ "title": "Management Group โ€” ๊ด€๋ฆฌ ๊ทธ๋ฃน",
357
+ "summary": "์—ฌ๋Ÿฌ ๊ตฌ๋…์„ ๊ณ„์ธต์ ์œผ๋กœ ๋ฌถ๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค. ์ตœ์ƒ์œ„ ๋ฃจํŠธ ๊ด€๋ฆฌ ๊ทธ๋ฃน์—์„œ RBAC์™€ Policy๋ฅผ ํ• ๋‹นํ•˜๋ฉด ํ•˜์œ„ ๊ตฌ๋…์œผ๋กœ ์ƒ์†๋ฉ๋‹ˆ๋‹ค.",
358
+ "example": "# ๊ณ„์ธต ๊ตฌ์กฐ ์˜ˆ:\n# Root Management Group\n# โ””โ”€ Production MG\n# โ”œโ”€ Sub-A\n# โ””โ”€ Sub-B\naz account management-group create --name 'Production'",
359
+ "common_mistake": "๊ด€๋ฆฌ ๊ทธ๋ฃน์€ ์ตœ๋Œ€ 6๋‹จ๊ณ„(๋ฃจํŠธ ํฌํ•จ)๊นŒ์ง€ ์ค‘์ฒฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋…์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ๊ด€๋ฆฌ ๊ทธ๋ฃน์—๋งŒ ์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
360
+ "keywords": [
361
+ "management group",
362
+ "hierarchy",
363
+ "policy inheritance",
364
+ "RBAC",
365
+ "root",
366
+ "subscription"
367
+ ],
368
+ "source_id": "azure-rbac-overview",
369
+ "details": [
370
+ "๋ฃจํŠธ ๊ด€๋ฆฌ ๊ทธ๋ฃน์— ํ• ๋‹นํ•œ PolicyยทRBAC๋Š” ํ…Œ๋„ŒํŠธ์˜ ๋ชจ๋“  ๊ตฌ๋…์— ์ƒ์†๋ฉ๋‹ˆ๋‹ค. ์ „์‚ฌ ๊ทœ์ • ์ ์šฉ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.",
371
+ "๊ด€๋ฆฌ ๊ทธ๋ฃน ์ด๋™: ๊ตฌ๋…์ด๋‚˜ ํ•˜์œ„ ๊ด€๋ฆฌ ๊ทธ๋ฃน์„ ๋‹ค๋ฅธ ๊ด€๋ฆฌ ๊ทธ๋ฃน์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
372
+ "์‹œํ—˜ ํŒจํ„ด: ๊ด€๋ฆฌ ๊ทธ๋ฃน ๊ณ„์ธต ์„ค๊ณ„, Policy/RBAC ์ƒ์† ํ๋ฆ„, ๊ด€๋ฆฌ ๊ทธ๋ฃน vs ๊ตฌ๋… vs RG ๋ฒ”์œ„ ์ฐจ์ด ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
373
+ ],
374
+ "level": "์ž…๋ฌธ"
375
+ },
376
+ {
377
+ "id": "azure-subscription",
378
+ "certification": "AZ-104",
379
+ "title": "Subscription โ€” ๊ตฌ๋…",
380
+ "summary": "Azure ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ๋น„์šฉ์„ ์ฒญ๊ตฌํ•˜๋Š” ๊ธฐ๋ณธ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ๊ตฌ๋…๋งˆ๋‹ค ๋ฆฌ์†Œ์Šค ํ•œ๋„(quota)๊ฐ€ ์žˆ์œผ๋ฉฐ ํ•˜๋‚˜์˜ Entra ID ํ…Œ๋„ŒํŠธ์— ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
381
+ "example": "az account list --output table\naz account set --subscription 'My Subscription'\naz account show",
382
+ "common_mistake": "๊ตฌ๋…๊ณผ ํ…Œ๋„ŒํŠธ๋Š” 1:1์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ํ…Œ๋„ŒํŠธ์— ์—ฌ๋Ÿฌ ๊ตฌ๋…์ด ์†ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ตฌ๋…์„ ๋‹ค๋ฅธ ํ…Œ๋„ŒํŠธ๋กœ ์ด์ „ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.",
383
+ "keywords": [
384
+ "subscription",
385
+ "tenant",
386
+ "quota",
387
+ "billing",
388
+ "az account"
389
+ ],
390
+ "source_id": "azure-rbac-overview",
391
+ "details": [
392
+ "๊ตฌ๋… ์œ ํ˜•: Pay-As-You-Go, Enterprise Agreement(EA), CSP(ํŒŒํŠธ๋„ˆ ์ œ๊ณต) ๋“ฑ. ๋น„์šฉ ์ฒญ๊ตฌ ๊ณ„์ •๊ณผ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
393
+ "๋ฆฌ์†Œ์Šค ํ•œ๋„(Quota)๋Š” ๊ตฌ๋… ๋‹จ์œ„๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ: ํŠน์ • region์˜ vCPU ์ตœ๋Œ€ ์ˆ˜. ํ•œ๋„ ์ฆ๊ฐ€๋Š” Microsoft์— ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.",
394
+ "์‹œํ—˜ ํŒจํ„ด: ๊ตฌ๋… ์ด๋™(ํ…Œ๋„ŒํŠธ ๊ฐ„), ๊ตฌ๋… ๋ ˆ๋ฒจ RBAC, Cost Management์—์„œ ๊ตฌ๋…๋ณ„ ๋น„์šฉ ๋ถ„์„ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
395
+ ],
396
+ "level": "์ž…๋ฌธ"
397
+ },
398
+ {
399
+ "id": "azure-resource-lock",
400
+ "certification": "AZ-104",
401
+ "title": "Resource Lock โ€” ๋ฆฌ์†Œ์Šค ์ž ๊ธˆ",
402
+ "summary": "์‹ค์ˆ˜๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์ˆ˜์ •ยท์‚ญ์ œํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค. ReadOnly(์ˆ˜์ •ยท์‚ญ์ œ ์ฐจ๋‹จ)์™€ CanNotDelete(์‚ญ์ œ๋งŒ ์ฐจ๋‹จ) ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.",
403
+ "example": "az lock create \\\n --name 'prod-lock' \\\n --resource-group myRG \\\n --lock-type CanNotDelete\naz lock list --resource-group myRG",
404
+ "common_mistake": "Lock์€ RBAC๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค. Owner๋ผ๋„ Lock์ด ๊ฑธ๋ฆฐ ๋ฆฌ์†Œ์Šค๋Š” ์ž ๊ธˆ์„ ๋จผ์ € ํ•ด์ œํ•ด์•ผ ์ˆ˜์ •ยท์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
405
+ "keywords": [
406
+ "resource lock",
407
+ "CanNotDelete",
408
+ "ReadOnly",
409
+ "RBAC override"
410
+ ],
411
+ "source_id": "azure-vm-overview",
412
+ "details": [
413
+ "ReadOnly Lock: ์ฝ๊ธฐ๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์“ฐ๊ธฐยท์‚ญ์ œ ๋ชจ๋‘ ์ฐจ๋‹จ. VM์— ๊ฑธ๋ฉด ์žฌ์‹œ์ž‘๋„ ์ฐจ๋‹จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
414
+ "Lock์€ RG์— ์„ค์ •ํ•˜๋ฉด ํ•˜์œ„ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ์ƒ์†๋ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ณ„ ๋ฆฌ์†Œ์Šค์—๋งŒ ๊ฑธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.",
415
+ "์‹œํ—˜ ํŒจํ„ด: Lock๊ณผ RBAC ์ƒํ˜ธ์ž‘์šฉ, CanNotDelete vs ReadOnly ์‹œ๋‚˜๋ฆฌ์˜ค, ์ž ๊ธˆ ํ•ด์ œ ์—†์ด ์‚ญ์ œ ์‹œ๋„ ๊ฒฐ๊ณผ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
416
+ ],
417
+ "level": "์ž…๋ฌธ",
418
+ "related_practices": [
419
+ "azure-lab-6"
420
+ ]
421
+ },
422
+ {
423
+ "id": "azure-tags",
424
+ "certification": "AZ-104",
425
+ "title": "Tags โ€” ํƒœ๊ทธ",
426
+ "summary": "๋ฆฌ์†Œ์Šค์— ํ‚ค-๊ฐ’ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ ๋ถ„๋ฅ˜ยท๋น„์šฉ ์ถ”์ ยท์ž๋™ํ™”์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. RG์˜ ํƒœ๊ทธ๋Š” ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค์— ์ž๋™ ์ƒ์†๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
427
+ "example": "az resource tag \\\n --tags Environment=Production CostCenter=IT \\\n --ids /subscriptions/SUB/resourceGroups/myRG\n# Policy๋กœ ํƒœ๊ทธ ๊ฐ•์ œ:\n# 'Require a tag on resources'",
428
+ "common_mistake": "ํƒœ๊ทธ๋Š” RG์—์„œ ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค๋กœ ์ž๋™ ์ƒ์†๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒ์†์ด ํ•„์š”ํ•˜๋ฉด Azure Policy๋กœ ํƒœ๊ทธ ์ƒ์† ๊ทœ์น™์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
429
+ "keywords": [
430
+ "tags",
431
+ "key-value",
432
+ "cost center",
433
+ "environment",
434
+ "policy",
435
+ "inheritance"
436
+ ],
437
+ "source_id": "azure-vm-overview",
438
+ "details": [
439
+ "๋ฆฌ์†Œ์Šค๋‹น ์ตœ๋Œ€ 50๊ฐœ์˜ ํƒœ๊ทธ. ์ด๋ฆ„ ์ตœ๋Œ€ 512์ž, ๊ฐ’ ์ตœ๋Œ€ 256์ž(์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ •์€ ์ด๋ฆ„ 128์ž).",
440
+ "๋น„์šฉ ๋ถ„์„์—์„œ ํƒœ๊ทธ๋กœ ํ•„ํ„ฐ๋งํ•˜๋ฉด ๋ถ€์„œ๋ณ„ยทํ”„๋กœ์ ํŠธ๋ณ„ ๋น„์šฉ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
441
+ "์‹œํ—˜ ํŒจํ„ด: ํƒœ๊ทธ ์ƒ์† ์ œํ•œ, Policy๋ฅผ ํ†ตํ•œ ํƒœ๊ทธ ๊ฐ•์ œ ์ ์šฉ, ๋น„์šฉ ๋ถ„์„์—์„œ ํƒœ๊ทธ ํ™œ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
442
+ ],
443
+ "level": "์ž…๋ฌธ"
444
+ },
445
+ {
446
+ "id": "azure-custom-role",
447
+ "certification": "AZ-104",
448
+ "title": "Custom Role โ€” ์‚ฌ์šฉ์ž ์ง€์ • ์—ญํ• ",
449
+ "summary": "๋‚ด์žฅ ์—ญํ• ์ด ๋„ˆ๋ฌด ๊ด‘๋ฒ”์œ„ํ•˜๊ฑฐ๋‚˜ ๋ถ€์กฑํ•  ๋•Œ ํŠน์ • Actions๋งŒ ํ—ˆ์šฉํ•˜๋Š” ์—ญํ• ์„ ์ง์ ‘ ๋งŒ๋“ญ๋‹ˆ๋‹ค. JSON์œผ๋กœ ActionsยทNotActionsยทDataActions๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
450
+ "example": "{\n \"Name\": \"VM Operator\",\n \"Actions\": [\n \"Microsoft.Compute/virtualMachines/start/action\",\n \"Microsoft.Compute/virtualMachines/restart/action\",\n \"Microsoft.Compute/virtualMachines/read\"\n ],\n \"AssignableScopes\": [\"/subscriptions/SUB_ID\"]\n}",
451
+ "common_mistake": "Custom Role์€ ํ…Œ๋„ŒํŠธ ๋‚ด์—์„œ ๋งŒ๋“ค์ง€๋งŒ, AssignableScopes์— ์ง€์ •ํ•œ ๊ตฌ๋…/RG ๋ฒ”์œ„์—์„œ๋งŒ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
452
+ "keywords": [
453
+ "custom role",
454
+ "Actions",
455
+ "NotActions",
456
+ "DataActions",
457
+ "AssignableScopes",
458
+ "JSON"
459
+ ],
460
+ "source_id": "azure-rbac-overview",
461
+ "details": [
462
+ "Actions: ๊ด€๋ฆฌ ํ‰๋ฉด ์ž‘์—… ํ—ˆ์šฉ. NotActions: Actions์—์„œ ์ œ์™ธํ•  ์ž‘์—…. DataActions: ๋ฐ์ดํ„ฐ ํ‰๋ฉด ์ž‘์—…(Blob ์ฝ๊ธฐ ๋“ฑ).",
463
+ "๊ธฐ์กด ๋‚ด์žฅ ์—ญํ• ์„ ๋ณต์‚ฌํ•ด ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ์‹์ด ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค: `az role definition list --name 'Contributor'`๋กœ ์‹œ์ž‘.",
464
+ "์‹œํ—˜ ํŒจํ„ด: ์ตœ์†Œ ๊ถŒํ•œ ์›์น™์— ๋”ฐ๋ฅธ Custom Role Actions ์„ค๊ณ„, DataActions vs Actions ์ฐจ์ด, AssignableScopes ์„ค์ •์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
465
+ ],
466
+ "level": "์ค‘๊ธ‰"
467
+ },
468
+ {
469
+ "id": "azure-sas",
470
+ "certification": "AZ-104",
471
+ "title": "Shared Access Signature โ€” SAS",
472
+ "summary": "์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ • ์ „์ฒด๋ฅผ ๋…ธ์ถœํ•˜์ง€ ์•Š๊ณ  ํŠน์ • ๋ฆฌ์†Œ์Šค์— ์ œํ•œ๋œ ๊ถŒํ•œ๊ณผ ์‹œ๊ฐ„ ๋ฒ”์œ„์˜ ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜๋Š” ์„œ๋ช…๋œ URL์ž…๋‹ˆ๋‹ค.",
473
+ "example": "az storage blob generate-sas \\\n --account-name myaccount \\\n --container-name data \\\n --name report.pdf \\\n --permissions r \\\n --expiry 2024-12-31",
474
+ "common_mistake": "SAS URL์ด ๋…ธ์ถœ๋˜๋ฉด ๋งŒ๋ฃŒ ์ „๊นŒ์ง€ ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋Š” ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๊ณ , SAS๋ฅผ ์ €์žฅํ•œ Stored Access Policy๋ฅผ ์‚ฌ์šฉํ•ด ์ฆ‰์‹œ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
475
+ "keywords": [
476
+ "SAS",
477
+ "Service SAS",
478
+ "Account SAS",
479
+ "User Delegation SAS",
480
+ "expiry",
481
+ "stored access policy"
482
+ ],
483
+ "source_id": "azure-storage-account",
484
+ "details": [
485
+ "SAS ์œ ํ˜•: Account SAS(์ „์ฒด ์Šคํ† ๋ฆฌ์ง€ ์„œ๋น„์Šค), Service SAS(ํŠน์ • ์„œ๋น„์Šค), User Delegation SAS(Entra ID ์ž๊ฒฉ ์ฆ๋ช… ๊ธฐ๋ฐ˜, ๊ฐ€์žฅ ์•ˆ์ „).",
486
+ "Stored Access Policy: ์„œ๋น„์Šค SAS๋ฅผ ์ •์ฑ…์— ์—ฐ๊ฒฐํ•˜๋ฉด URL์„ ์žฌ๋ฐœ๊ธ‰ํ•˜์ง€ ์•Š๊ณ  ์ •์ฑ… ์‚ญ์ œ๋งŒ์œผ๋กœ ์ ‘๊ทผ์„ ์ฆ‰์‹œ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
487
+ "์‹œํ—˜ ํŒจํ„ด: SAS ์œ ํ˜• ์„ ํƒ, ๊ถŒํ•œยท๋งŒ๋ฃŒ ์„ค์ •, Stored Access Policy๋ฅผ ํ†ตํ•œ ์ทจ์†Œ ๋ฐฉ๋ฒ•, ํ‚ค ๊ธฐ๋ฐ˜ vs Entra ID ๊ธฐ๋ฐ˜ SAS ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
488
+ ],
489
+ "level": "์ค‘๊ธ‰"
490
+ },
491
+ {
492
+ "id": "azure-storage-redundancy",
493
+ "certification": "AZ-104",
494
+ "title": "Storage Redundancy โ€” ์Šคํ† ๋ฆฌ์ง€ ์ค‘๋ณต์„ฑ",
495
+ "summary": "๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œํ•ด ๊ฐ€์šฉ์„ฑ๊ณผ ๋‚ด๊ตฌ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ณต์ œ ๋ฒ”์œ„(๋‹จ์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐโ†’AZโ†’๋‹ค๋ฅธ region)์— ๋”ฐ๋ผ LRSยทZRSยทGRSยทGZRS ์ค‘ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
496
+ "example": "# ์ƒ์„ฑ ์‹œ SKU๋กœ ๊ฒฐ์ •\naz storage account create --sku Standard_GRS ...\n# ๋ณ€๊ฒฝ (์ผ๋ถ€๋งŒ ๊ฐ€๋Šฅ)\naz storage account update --sku Standard_RAGRS --name myaccount",
497
+ "common_mistake": "GRS๋Š” ๋ณด์กฐ ์ง€์—ญ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œํ•˜์ง€๋งŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฝ๊ธฐ ๋ถˆ๊ฐ€์ž…๋‹ˆ๋‹ค. ๋ณด์กฐ ์ง€์—ญ ์ฝ๊ธฐ๋ฅผ ํ—ˆ์šฉํ•˜๋ ค๋ฉด RA-GRS(Read-Access GRS)๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
498
+ "keywords": [
499
+ "LRS",
500
+ "ZRS",
501
+ "GRS",
502
+ "GZRS",
503
+ "RA-GRS",
504
+ "redundancy",
505
+ "replication"
506
+ ],
507
+ "source_id": "azure-storage-account",
508
+ "details": [
509
+ "LRS: ๋‹จ์ผ datacenter ๋‚ด 3๋ณต์ œ. ZRS: ๋™์ผ region 3๊ฐœ AZ์— ๋ณต์ œ. GRS: ๋‹ค๋ฅธ region์— ๋น„๋™๊ธฐ ๋ณต์ œ(LRSร—2). GZRS: ZRS+GRS.",
510
+ "๋‚ด๊ตฌ์„ฑ(nines): LRS=11 9s, ZRS=12 9s, GRS=16 9s. ๋น„์šฉ์€ LRS<ZRS<GRS<GZRS ์ˆœ์ž…๋‹ˆ๋‹ค.",
511
+ "์‹œํ—˜ ํŒจํ„ด: ์š”๊ตฌ์‚ฌํ•ญ(AZ ์žฅ์•  ๋ณดํ˜ธ, region ์žฅ์•  ๋ณดํ˜ธ)์— ๋”ฐ๋ฅธ ์ค‘๋ณต์„ฑ ์„ ํƒ, RA-GRS์—์„œ ๋ณด์กฐ ์ง€์—ญ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
512
+ ],
513
+ "level": "์ค‘๊ธ‰"
514
+ },
515
+ {
516
+ "id": "azure-lifecycle",
517
+ "certification": "AZ-104",
518
+ "title": "Storage Lifecycle Management โ€” ์Šคํ† ๋ฆฌ์ง€ ์ˆ˜๋ช… ์ฃผ๊ธฐ",
519
+ "summary": "Blob์„ ์ผ์ • ๊ธฐ๊ฐ„ ํ›„ ์ž๋™์œผ๋กœ ๊ณ„์ธต ์ „ํ™˜(Hotโ†’Coolโ†’Archive)ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” ์ •์ฑ…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋น„์šฉ ์ž๋™ ์ตœ์ ํ™”์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
520
+ "example": "# ๊ทœ์น™ ์˜ˆ: 30์ผ ํ›„ Cool, 90์ผ ํ›„ Archive, 365์ผ ํ›„ ์‚ญ์ œ\n{\n \"rules\": [{\n \"action\": {\"baseBlob\": {\n \"tierToCool\": {\"daysAfterModificationGreaterThan\": 30},\n \"tierToArchive\": {\"daysAfterModificationGreaterThan\": 90},\n \"delete\": {\"daysAfterModificationGreaterThan\": 365}\n }}\n }]\n}",
521
+ "common_mistake": "์ˆ˜๋ช… ์ฃผ๊ธฐ ์ •์ฑ…์€ Blob ๋ ˆ๋ฒจ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ Archive ๊ณ„์ธต์ธ Blob์€ ์ •์ฑ…์ด ๋‹ค์‹œ Archive๋กœ ์ „ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
522
+ "keywords": [
523
+ "lifecycle policy",
524
+ "tier transition",
525
+ "Hot",
526
+ "Cool",
527
+ "Archive",
528
+ "delete",
529
+ "daysAfterModification"
530
+ ],
531
+ "source_id": "azure-blob-access-tiers",
532
+ "details": [
533
+ "์กฐ๊ฑด ๊ธฐ์ค€: ๋งˆ์ง€๋ง‰ ์ˆ˜์ • ๋‚ ์งœ, ๋งˆ์ง€๋ง‰ ์ ‘๊ทผ ๋‚ ์งœ(์ ‘๊ทผ ์ถ”์  ํ™œ์„ฑํ™” ํ•„์š”), blob ์ƒ์„ฑ ๋‚ ์งœ ์ค‘ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
534
+ "ํ•„ํ„ฐ๋กœ ํŠน์ • ์ปจํ…Œ์ด๋„ˆ, ์ ‘๋‘์‚ฌ, blob ์œ ํ˜•(blockBlob/appendBlob)์—๋งŒ ์ •์ฑ…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
535
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ๋น„์šฉ ์ตœ์ ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ lifecycle ์ •์ฑ… ๊ทœ์น™ ์ž‘์„ฑ, ์กฐ๊ฑด๋ณ„ ๊ณ„์ธต ์ „ํ™˜ ์ˆœ์„œ, ์‚ญ์ œ ํฌํ•จ ์ „์ฒด ํ๋ฆ„์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
536
+ ],
537
+ "level": "์ค‘๊ธ‰"
538
+ },
539
+ {
540
+ "id": "azure-files",
541
+ "certification": "AZ-104",
542
+ "title": "Azure Files โ€” ํŒŒ์ผ ๊ณต์œ ",
543
+ "summary": "SMB/NFS ํ”„๋กœํ† ์ฝœ๋กœ ๋งˆ์šดํŠธํ•˜๋Š” ์™„์ „ ๊ด€๋ฆฌํ˜• ํด๋ผ์šฐ๋“œ ํŒŒ์ผ ๊ณต์œ ์ž…๋‹ˆ๋‹ค. VMยท์˜จํ”„๋ ˆ๋ฏธ์Šคยท์ปจํ…Œ์ด๋„ˆ์—์„œ ๋„คํŠธ์›Œํฌ ๋“œ๋ผ์ด๋ธŒ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
544
+ "example": "# Windows์—์„œ ๋งˆ์šดํŠธ\nnet use Z: \\\\myaccount.file.core.windows.net\\myshare /user:myaccount [key]\n# Linux์—์„œ ๋งˆ์šดํŠธ\nmount -t cifs //myaccount.file.core.windows.net/myshare /mnt -o user=myaccount,password=[key]",
545
+ "common_mistake": "Azure Files SMB๋Š” ํฌํŠธ 445๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€ ISP์™€ ๊ธฐ์—… ๋ฐฉํ™”๋ฒฝ์ด 445๋ฅผ ์ฐจ๋‹จํ•ด ๋งˆ์šดํŠธ๊ฐ€ ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.",
546
+ "keywords": [
547
+ "Azure Files",
548
+ "SMB",
549
+ "NFS",
550
+ "file share",
551
+ "port 445",
552
+ "Azure File Sync"
553
+ ],
554
+ "source_id": "azure-storage-account",
555
+ "details": [
556
+ "์„ฑ๋Šฅ ๊ณ„์ธต: Standard(HDD ๊ธฐ๋ฐ˜), Premium(SSD ๊ธฐ๋ฐ˜). Premium์€ ๋‚ฎ์€ ์ง€์—ฐ์‹œ๊ฐ„์ด ํ•„์š”ํ•œ ์›Œํฌ๋กœ๋“œ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
557
+ "Azure File Sync: ์˜จํ”„๋ ˆ๋ฏธ์Šค ํŒŒ์ผ ์„œ๋ฒ„์™€ Azure Files๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ , ์ž์ฃผ ์“ฐ์ง€ ์•Š๋Š” ํŒŒ์ผ์„ Cloud Tiering์œผ๋กœ Azure์— ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค.",
558
+ "์‹œํ—˜ ํŒจํ„ด: Blob vs Files ์„ ํƒ ๊ธฐ์ค€(ํŒŒ์ผ ์‹œ์Šคํ…œ ๋งˆ์šดํŠธ ํ•„์š” ์—ฌ๋ถ€), Azure File Sync ์•„ํ‚คํ…์ฒ˜, ํฌํŠธ 445 ๋ฌธ์ œ ํ•ด๊ฒฐ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
559
+ ],
560
+ "level": "์ค‘๊ธ‰"
561
+ },
562
+ {
563
+ "id": "azure-managed-disk",
564
+ "certification": "AZ-104",
565
+ "title": "Managed Disk โ€” ๊ด€๋ฆฌ ๋””์Šคํฌ",
566
+ "summary": "VM์— ์—ฐ๊ฒฐํ•˜๋Š” ๋ธ”๋ก ์Šคํ† ๋ฆฌ์ง€์ž…๋‹ˆ๋‹ค. OS ๋””์Šคํฌ์™€ ๋ฐ์ดํ„ฐ ๋””์Šคํฌ๋กœ ๋‚˜๋‰˜๋ฉฐ, ์„ฑ๋Šฅ ์š”๊ตฌ์— ๋”ฐ๋ผ Standard HDDยทStandard SSDยทPremium SSDยทUltra Disk๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
567
+ "example": "az disk create \\\n --name myDataDisk \\\n --resource-group myRG \\\n --size-gb 128 \\\n --sku Premium_LRS\naz vm disk attach --vm-name myVM --name myDataDisk",
568
+ "common_mistake": "๊ด€๋ฆฌ ๋””์Šคํฌ ํฌ๊ธฐ๋Š” ๋Š˜๋ฆด ์ˆ˜๋งŒ ์žˆ๊ณ  ์ค„์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํฌ๊ธฐ๋ฅผ ์ค„์ด๋ ค๋ฉด ์ƒˆ ์ž‘์€ ๋””์Šคํฌ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ด์ „ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
569
+ "keywords": [
570
+ "managed disk",
571
+ "OS disk",
572
+ "data disk",
573
+ "Premium SSD",
574
+ "Ultra Disk",
575
+ "snapshot",
576
+ "disk encryption"
577
+ ],
578
+ "source_id": "azure-storage-account",
579
+ "details": [
580
+ "SKU ์„ ํƒ: Standard HDD(๋น„์šฉ ์ตœ์ €, ๋ฐฑ์—…์šฉ), Standard SSD(์ผ๋ฐ˜ ์›Œํฌ๋กœ๋“œ), Premium SSD(I/O ์ง‘์•ฝ ์•ฑ), Ultra Disk(DB, ์ตœ์ € ์ง€์—ฐ).",
581
+ "์Šค๋ƒ…์ƒท: ํŠน์ • ์‹œ์ ์˜ ๋””์Šคํฌ ์ƒํƒœ๋ฅผ ์บก์ฒ˜. ๋‹ค๋ฅธ region์— ๋ณต์‚ฌํ•ด ์žฌํ•ด ๋ณต๊ตฌ์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
582
+ "์‹œํ—˜ ํŒจํ„ด: ์›Œํฌ๋กœ๋“œ๋ณ„ ๋””์Šคํฌ SKU ์„ ํƒ, ๋””์Šคํฌ ํ™•์žฅ ํ›„ OS์—์„œ ํŒŒํ‹ฐ์…˜ ํ™•์žฅ ํ•„์š” ์—ฌ๋ถ€, ์Šค๋ƒ…์ƒท vs ๋ฐฑ์—… ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
583
+ ],
584
+ "level": "์ž…๋ฌธ"
585
+ },
586
+ {
587
+ "id": "azure-availability-set",
588
+ "certification": "AZ-104",
589
+ "title": "Availability Set โ€” ๊ฐ€์šฉ์„ฑ ์ง‘ํ•ฉ",
590
+ "summary": "๋™์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋‚ด์—์„œ VM์„ ๋‹ค๋ฅธ ๋ฌผ๋ฆฌ ๋ž™(์žฅ์•  ๋„๋ฉ”์ธ)๊ณผ ์—…๋ฐ์ดํŠธ ๊ทธ๋ฃน(์—…๋ฐ์ดํŠธ ๋„๋ฉ”์ธ)์— ๋ฐฐ์น˜ํ•ด ๋‹จ์ผ ์žฅ์• ์ ์„ ์—†์•ฑ๋‹ˆ๋‹ค.",
591
+ "example": "az vm availability-set create \\\n --name myAS \\\n --resource-group myRG \\\n --platform-fault-domain-count 2 \\\n --platform-update-domain-count 5",
592
+ "common_mistake": "Availability Set์€ ๋‹จ์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋‚ด ๋ณดํ˜ธ์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์„ผํ„ฐ ์ „์ฒด ์žฅ์• ๋‚˜ AZ ์žฅ์• ์— ๋Œ€์‘ํ•˜๋ ค๋ฉด Availability Zones๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
593
+ "keywords": [
594
+ "availability set",
595
+ "fault domain",
596
+ "update domain",
597
+ "UD",
598
+ "FD",
599
+ "SLA 99.95%"
600
+ ],
601
+ "source_id": "azure-vm-overview",
602
+ "details": [
603
+ "์žฅ์•  ๋„๋ฉ”์ธ(FD): ๊ณต์œ  ์ „์›ยท๋„คํŠธ์›Œํฌ ์Šค์œ„์น˜ ๊ธฐ์ค€ ๋ฌผ๋ฆฌ ๋ž™ ๋ถ„๋ฆฌ. ๊ธฐ๋ณธ ์ตœ๋Œ€ 3๊ฐœ.",
604
+ "์—…๋ฐ์ดํŠธ ๋„๋ฉ”์ธ(UD): ๊ณ„ํš๋œ ์œ ์ง€ ๊ด€๋ฆฌ ์‹œ ๋™์‹œ์— ์žฌ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ทธ๋ฃน. ๊ธฐ๋ณธ 5๊ฐœ(์ตœ๋Œ€ 20๊ฐœ).",
605
+ "์‹œํ—˜ ํŒจํ„ด: AS vs AZ ๋ณดํ˜ธ ์ˆ˜์ค€ ์ฐจ์ด, SLA(AS=99.95%, ๋‹จ์ผVM-Premium=99.9%, AZ=99.99%), AS์— VM ์ถ”๊ฐ€ ์‹œ๊ธฐ ์ œํ•œ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
606
+ ],
607
+ "level": "์ž…๋ฌธ"
608
+ },
609
+ {
610
+ "id": "azure-autoscale",
611
+ "certification": "AZ-104",
612
+ "title": "Autoscale โ€” ์ž๋™ ํฌ๊ธฐ ์กฐ์ •",
613
+ "summary": "๋ฉ”ํŠธ๋ฆญ(CPU, ๋ฉ”๋ชจ๋ฆฌ, ํ ๊ธธ์ด ๋“ฑ)์ด๋‚˜ ์ผ์ •์— ๋”ฐ๋ผ VM Scale Set ๋˜๋Š” App Service์˜ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ๋Š˜๋ฆฌ๊ณ  ์ค„์ž…๋‹ˆ๋‹ค.",
614
+ "example": "# Autoscale ๊ทœ์น™ ์˜ˆ:\n# CPU > 70% 5๋ถ„ ์œ ์ง€ โ†’ +1 ์ธ์Šคํ„ด์Šค ์ถ”๊ฐ€\n# CPU < 30% 5๋ถ„ ์œ ์ง€ โ†’ -1 ์ธ์Šคํ„ด์Šค ์ œ๊ฑฐ\n# ์ตœ์†Œ 2, ์ตœ๋Œ€ 10 ์ธ์Šคํ„ด์Šค",
615
+ "common_mistake": "Scale-out(์ถ”๊ฐ€)๊ณผ Scale-in(์ œ๊ฑฐ) ๊ทœ์น™์„ ๋ชจ๋‘ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Scale-out๋งŒ ์„ค์ •ํ•˜๋ฉด ์ธ์Šคํ„ด์Šค๊ฐ€ ์ค„์–ด๋“ค์ง€ ์•Š์•„ ๋น„์šฉ๏ฟฝ๏ฟฝ ๊ณ„์† ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.",
616
+ "keywords": [
617
+ "autoscale",
618
+ "scale-out",
619
+ "scale-in",
620
+ "metric",
621
+ "cooldown",
622
+ "min/max instances"
623
+ ],
624
+ "source_id": "azure-vmss-overview",
625
+ "details": [
626
+ "๋ƒ‰๊ฐ ๊ธฐ๊ฐ„(Cooldown): Scale ๋™์ž‘ ํ›„ ๋‹ค์Œ ๋™์ž‘๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„. ๋„ˆ๋ฌด ์งง์œผ๋ฉด ์ง„๋™(flapping) ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.",
627
+ "์ผ์ • ๊ธฐ๋ฐ˜ Autoscale: ํŠน์ • ์š”์ผยท์‹œ๊ฐ„๋Œ€์— ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ ๋Š˜๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
628
+ "์‹œํ—˜ ํŒจํ„ด: Scale-in/out ๊ทœ์น™ ์„ค๊ณ„, ๋ƒ‰๊ฐ ๊ธฐ๊ฐ„ ๊ฐœ๋…, ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ฐ˜ vs ์ผ์ • ๊ธฐ๋ฐ˜ ์„ ํƒ, VMSS์™€ App Service์˜ Autoscale ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
629
+ ],
630
+ "level": "์ค‘๊ธ‰"
631
+ },
632
+ {
633
+ "id": "azure-vnet-peering",
634
+ "certification": "AZ-104",
635
+ "title": "VNet Peering โ€” ๊ฐ€์ƒ ๋„คํŠธ์›Œํฌ ํ”ผ์–ด๋ง",
636
+ "summary": "๋‘ VNet์„ Microsoft ๋ฐฑ๋ณธ ๋„คํŠธ์›Œํฌ๋กœ ์ง์ ‘ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋™์ผ region(Local Peering)๊ณผ ๋‹ค๋ฅธ region(Global Peering) ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
637
+ "example": "az network vnet peering create \\\n --name VNet1-to-VNet2 \\\n --vnet-name VNet1 \\\n --remote-vnet VNet2 \\\n --resource-group myRG\n# ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ๋„ ์ƒ์„ฑํ•ด์•ผ ํ•จ",
638
+ "common_mistake": "ํ”ผ์–ด๋ง์€ ๋น„์ „์ด์ (non-transitive)์ž…๋‹ˆ๋‹ค. Aโ†”B, Bโ†”C๋กœ ํ”ผ์–ด๋งํ•ด๋„ A์—์„œ C๋กœ ์ง์ ‘ ํ†ต์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Aโ†”C๋ฅผ ๋ณ„๋„๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
639
+ "keywords": [
640
+ "VNet peering",
641
+ "non-transitive",
642
+ "local peering",
643
+ "global peering",
644
+ "transit"
645
+ ],
646
+ "source_id": "azure-vnet-overview",
647
+ "details": [
648
+ "ํ”ผ์–ด๋ง ์กฐ๊ฑด: ๋‘ VNet์˜ ์ฃผ์†Œ ๊ณต๊ฐ„์ด ๊ฒน์น˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”ผ์–ด๋ง์€ ์–‘๋ฐฉํ–ฅ ์„ค์ •์ด ๊ฐ๊ฐ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
649
+ "๊ฒŒ์ดํŠธ์›จ์ด ์ „์†ก(Gateway Transit): ํ”ผ์–ด๋ง๋œ VNet์ด ํ—ˆ๋ธŒ์˜ VPN Gateway๋ฅผ ๊ณต์œ ํ•ด ์˜จํ”„๋ ˆ๋ฏธ์Šค ์—ฐ๊ฒฐ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
650
+ "์‹œํ—˜ ํŒจํ„ด: ๋น„์ „์ด์„ฑ์œผ๋กœ ์ธํ•œ ์—ฐ๊ฒฐ ๋ถˆ๊ฐ€ ์‹œ๋‚˜๋ฆฌ์˜ค, ์ฃผ์†Œ ๊ณต๊ฐ„ ์ค‘์ฒฉ ์˜ค๋ฅ˜, Gateway Transit ์„ค์ • ๋ฐฉ๋ฒ•์ด ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
651
+ ],
652
+ "level": "์ค‘๊ธ‰",
653
+ "related_practices": [
654
+ "azure-lab-10"
655
+ ]
656
+ },
657
+ {
658
+ "id": "azure-route-table",
659
+ "certification": "AZ-104",
660
+ "title": "Route Table โ€” ๊ฒฝ๋กœ ํ…Œ์ด๋ธ”",
661
+ "summary": "์„œ๋ธŒ๋„ท์˜ ํŠธ๋ž˜ํ”ฝ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•ฉ๋‹ˆ๋‹ค(UDR: User-Defined Route). ๊ธฐ๋ณธ ์‹œ์Šคํ…œ ๊ฒฝ๋กœ๋ฅผ ์žฌ์ •์˜ํ•ด ํŠธ๋ž˜ํ”ฝ์„ NVA, VPN GW, VNet Gateway ๋“ฑ์œผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.",
662
+ "example": "az network route-table create --name myRT --resource-group myRG\naz network route-table route create \\\n --route-table-name myRT \\\n --name force-to-firewall \\\n --address-prefix 0.0.0.0/0 \\\n --next-hop-type VirtualAppliance \\\n --next-hop-ip-address 10.0.1.4",
663
+ "common_mistake": "๊ฒฝ๋กœ ํ…Œ์ด๋ธ”์€ ์„œ๋ธŒ๋„ท์— ์—ฐ๊ฒฐํ•ด์•ผ ํšจ๊ณผ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. Route Table์„ ๋งŒ๋“ค๊ธฐ๋งŒ ํ•˜๊ณ  ์„œ๋ธŒ๋„ท์— ์—ฐ๊ฒฐ(association)ํ•˜์ง€ ์•Š์œผ๋ฉด ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
664
+ "keywords": [
665
+ "route table",
666
+ "UDR",
667
+ "next hop",
668
+ "NVA",
669
+ "VirtualAppliance",
670
+ "subnet association"
671
+ ],
672
+ "source_id": "azure-vnet-overview",
673
+ "details": [
674
+ "Next hop ์œ ํ˜•: VirtualAppliance(๋ฐฉํ™”๋ฒฝ NVA), VirtualNetworkGateway(VPN), Internet, VnetLocal, None(ํŠธ๋ž˜ํ”ฝ ๋“œ๋กญ).",
675
+ "๊ฐ•์ œ ํ„ฐ๋„๋ง(Force Tunneling): 0.0.0.0/0์„ VPN Gateway๋กœ ๋ณด๋‚ด ์ธํ„ฐ๋„ท ํŠธ๋ž˜ํ”ฝ๋„ ์˜จํ”„๋ ˆ๋ฏธ์Šค ๊ฒฝ์œ ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.",
676
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํŠธ๋ž˜ํ”ฝ์„ NVA๋กœ ์šฐํšŒํ•˜๋Š” UDR ์„ค๊ณ„, ๊ฐ•์ œ ํ„ฐ๋„๋ง ๊ตฌ์„ฑ, ์‹œ์Šคํ…œ ๊ฒฝ๋กœ vs ์‚ฌ์šฉ์ž ๊ฒฝ๋กœ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
677
+ ],
678
+ "level": "์ค‘๊ธ‰"
679
+ },
680
+ {
681
+ "id": "azure-private-endpoint",
682
+ "certification": "AZ-104",
683
+ "title": "Private Endpoint โ€” ํ”„๋ผ์ด๋น— ์—”๋“œํฌ์ธํŠธ",
684
+ "summary": "Azure PaaS ์„œ๋น„์Šค(Storage, SQL, Key Vault ๋“ฑ)์— VNet ๋‚ด ์‚ฌ์„ค IP๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท์„ ํ†ตํ•˜์ง€ ์•Š์•„ ๋ฐ์ดํ„ฐ ๋…ธ์ถœ ์œ„ํ—˜์„ ์ค„์ž…๋‹ˆ๋‹ค.",
685
+ "example": "az network private-endpoint create \\\n --name myPE \\\n --resource-group myRG \\\n --vnet-name myVNet \\\n --subnet default \\\n --private-connection-resource-id /subscriptions/.../storageAccounts/myaccount \\\n --group-ids blob",
686
+ "common_mistake": "Private Endpoint ์ƒ์„ฑ ํ›„ DNS ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Private DNS Zone๊ณผ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š์œผ๋ฉด ๊ณต์šฉ DNS๊ฐ€ Public IP๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ธํ„ฐ๋„ท ๊ฒฝ๋กœ๋กœ ์ ‘์†๋ฉ๋‹ˆ๋‹ค.",
687
+ "keywords": [
688
+ "private endpoint",
689
+ "private link",
690
+ "private IP",
691
+ "DNS zone",
692
+ "Service Endpoint ์ฐจ์ด"
693
+ ],
694
+ "source_id": "azure-private-endpoint",
695
+ "details": [
696
+ "Private Endpoint vs Service Endpoint: Private Endpoint๋Š” VNet์— ์‚ฌ์„ค IP๋ฅผ ๋ถ€์—ฌ(๋” ์•ˆ์ „), Service Endpoint๋Š” Public IP ์œ ์ง€ํ•˜๋˜ VNet์—์„œ๋งŒ ํ—ˆ์šฉ.",
697
+ "Private DNS Zone ์ž๋™ ํ†ตํ•ฉ: Private Endpoint ์ƒ์„ฑ ์‹œ DNS Zone Group์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ DNS ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
698
+ "์‹œํ—˜ ํŒจํ„ด: Private Endpoint vs Service Endpoint ์ฐจ์ด, DNS ์„ค์ • ์—†์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ๏ฟฝ๏ฟฝ, ์˜จํ”„๋ ˆ๋ฏธ์Šค์—์„œ Private Endpoint ์ ‘๊ทผ ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
699
+ ],
700
+ "level": "์ค‘๊ธ‰",
701
+ "related_practices": [
702
+ "azure-lab-11"
703
+ ]
704
+ },
705
+ {
706
+ "id": "azure-load-balancer-probe",
707
+ "certification": "AZ-104",
708
+ "title": "Load Balancer โ€” ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์žฅ์น˜",
709
+ "summary": "L4(TCP/UDP) ์ˆ˜์ค€์—์„œ ์ธ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐฑ์—”๋“œ VM ํ’€๋กœ ๋ถ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. Public LB๋Š” ์ธํ„ฐ๋„ท ํŠธ๋ž˜ํ”ฝ, Internal LB๋Š” VNet ๋‚ด๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
710
+ "example": "az network lb create \\\n --name myLB \\\n --resource-group myRG \\\n --frontend-ip-name frontendIP \\\n --backend-pool-name backendPool\n# ์ƒํƒœ ํ”„๋กœ๋ธŒ ์ถ”๊ฐ€\naz network lb probe create --lb-name myLB --name myProbe --protocol Http --port 80 --path /health",
711
+ "common_mistake": "์ƒํƒœ ํ”„๋กœ๋ธŒ(Health Probe)๊ฐ€ ์‹คํŒจํ•˜๋ฉด ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋ธŒ ๊ฒฝ๋กœ(/health)๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์œผ๋ฉด ์ •์ƒ VM๋„ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.",
712
+ "keywords": [
713
+ "load balancer",
714
+ "health probe",
715
+ "backend pool",
716
+ "frontend IP",
717
+ "SKU Basic/Standard"
718
+ ],
719
+ "source_id": "azure-load-balancer",
720
+ "details": [
721
+ "SKU: Basic(๋ฌด๋ฃŒ, AZ ๋ฏธ์ง€์›, ๊ณต๊ฐœ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ) vs Standard(์œ ๋ฃŒ, AZ ์ง€์›, HTTPS ํ”„๋กœ๋ธŒ, SLA 99.99%). ์ƒˆ ๊ตฌ์ถ•์€ Standard ๊ถŒ์žฅ.",
722
+ "๋ถ€ํ•˜ ๋ถ„์‚ฐ ๊ทœ์น™: ํฌํŠธยทํ”„๋กœํ† ์ฝœยท์„ธ์…˜ ์ง€์†์„ฑ(Client IP/Protocol)์„ ์„ค์ •. Floating IP๋กœ SQL AlwaysOn ๋“ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
723
+ "์‹œํ—˜ ํŒจํ„ด: ํ”„๋กœ๋ธŒ ์‹คํŒจ ์‹œ ๋™์ž‘, Public vs Internal LB ์„ ํƒ, Basic vs Standard SKU ์ฐจ์ด, ์„ธ์…˜ ์ง€์†์„ฑ ์˜ต์…˜์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
724
+ ],
725
+ "level": "์ค‘๊ธ‰"
726
+ },
727
+ {
728
+ "id": "azure-private-dns",
729
+ "certification": "AZ-104",
730
+ "title": "Private DNS Zone โ€” ์‚ฌ์„ค DNS ์˜์—ญ",
731
+ "summary": "VNet ๋‚ด๋ถ€์—์„œ๋งŒ ํ•ด์„๋˜๋Š” ์‚ฌ์„ค ๋„๋ฉ”์ธ์„ ํ˜ธ์ŠคํŒ…ํ•ฉ๋‹ˆ๋‹ค. Private Endpoint, ๋‚ด๋ถ€ ์„œ๋น„์Šค ๊ฒ€์ƒ‰, ์˜จํ”„๋ ˆ๋ฏธ์Šค DNS ํ†ตํ•ฉ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
732
+ "example": "az network private-dns zone create \\\n --name 'privatelink.blob.core.windows.net' \\\n --resource-group myRG\naz network private-dns link vnet create \\\n --zone-name 'privatelink.blob.core.windows.net' \\\n --virtual-network myVNet \\\n --registration-enabled false",
733
+ "common_mistake": "Private DNS Zone ์ƒ์„ฑ๋งŒ์œผ๋กœ๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. VNet์— ๋งํฌ(link)๋ฅผ ์—ฐ๊ฒฐํ•ด์•ผ ํ•ด๋‹น VNet์˜ VM์ด ์‚ฌ์„ค DNS๋ฅผ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
734
+ "keywords": [
735
+ "private DNS zone",
736
+ "VNet link",
737
+ "registration",
738
+ "privatelink",
739
+ "custom DNS"
740
+ ],
741
+ "source_id": "azure-dns-overview",
742
+ "details": [
743
+ "์ž๋™ ๋“ฑ๋ก(Auto-registration): VNet ๋งํฌ์—์„œ ํ™œ์„ฑํ™”ํ•˜๋ฉด ํ•ด๋‹น VNet์˜ VM์ด ์ž๋™์œผ๋กœ A ๋ ˆ์ฝ”๋“œ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.",
744
+ "Private Endpoint DNS: `privatelink.blob.core.windows.net` ๊ฐ™์€ ํŠน์ˆ˜ ์˜์—ญ์„ ์‚ฌ์šฉํ•ด PaaS ์„œ๋น„์Šค์˜ ์‚ฌ์„ค IP๋ฅผ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค.",
745
+ "์‹œํ—˜ ํŒจํ„ด: Private Endpoint์™€ Private DNS Zone ์—ฐ๋™ ์ˆœ์„œ, ์ž๋™ ๋“ฑ๋ก vs ์ˆ˜๋™ ๋ ˆ์ฝ”๋“œ, VNet ๋งํฌ ์—†์„ ๋•Œ DNS ํ•ด์„ ์‹คํŒจ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
746
+ ],
747
+ "level": "์ค‘๊ธ‰"
748
+ },
749
+ {
750
+ "id": "azure-monitor-metrics",
751
+ "certification": "AZ-104",
752
+ "title": "Azure Monitor Metrics โ€” ๋ชจ๋‹ˆํ„ฐ ๋ฉ”ํŠธ๋ฆญ",
753
+ "summary": "Azure ๋ฆฌ์†Œ์Šค๊ฐ€ ์ž๋™์œผ๋กœ ์ˆ˜์ง‘ํ•˜๋Š” ์ˆ˜์น˜ ๋ฐ์ดํ„ฐ(CPU%, ๋„คํŠธ์›Œํฌ In/Out, ๋””์Šคํฌ IOPS ๋“ฑ)๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•˜๊ณ  ๊ฒฝ๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.",
754
+ "example": "az monitor metrics list \\\n --resource /subscriptions/.../virtualMachines/myVM \\\n --metric 'Percentage CPU' \\\n --interval PT1M",
755
+ "common_mistake": "๊ธฐ๋ณธ ํ”Œ๋žซํผ ๋ฉ”ํŠธ๋ฆญ์€ 93์ผ ๋ณด๊ด€๋ฉ๋‹ˆ๋‹ค. ๋” ์˜ค๋ž˜ ๋ณด๊ด€ํ•˜๊ฑฐ๋‚˜ ์ปค์Šคํ…€ ๋ฉ”ํŠธ๋ฆญ์„ ์ €์žฅํ•˜๋ ค๋ฉด Log Analytics Workspace๋กœ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
756
+ "keywords": [
757
+ "metrics",
758
+ "Azure Monitor",
759
+ "CPU",
760
+ "latency",
761
+ "alert",
762
+ "93 days retention"
763
+ ],
764
+ "source_id": "azure-monitor-overview",
765
+ "details": [
766
+ "ํ”Œ๋žซํผ ๋ฉ”ํŠธ๋ฆญ์€ ๋ณ„๋„ ์„ค์ • ์—†์ด ์ž๋™ ์ˆ˜์ง‘๋ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ŠคํŠธ OS ๋‚ด๋ถ€ ๋ฉ”ํŠธ๋ฆญ(๋ฉ”๋ชจ๋ฆฌ ๋“ฑ)์€ Azure Monitor Agent๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
767
+ "๋ฉ”ํŠธ๋ฆญ ํƒ์ƒ‰๊ธฐ(Metrics Explorer)์—์„œ ์—ฌ๋Ÿฌ ๋ฉ”ํŠธ๋ฆญ์„ ๋น„๊ตํ•˜๊ณ  ์ฐจํŠธ๋ฅผ ๋Œ€์‹œ๋ณด๋“œ์— ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
768
+ "์‹œํ—˜ ํŒจํ„ด: ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ฐ˜ ๊ฒฝ๋ณด ์„ค์ •, ํ”Œ๋žซํผ ๋ฉ”ํŠธ๋ฆญ vs ๊ฒŒ์ŠคํŠธ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘ ๋ฐฉ๋ฒ•, ๋ณด๊ด€ ๊ธฐ๊ฐ„ ํ•œ๊ณ„์™€ Log Analytics ์—ฐ๋™์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
769
+ ],
770
+ "level": "์ค‘๊ธ‰",
771
+ "related_practices": [
772
+ "azure-lab-15"
773
+ ]
774
+ },
775
+ {
776
+ "id": "azure-activity-log",
777
+ "certification": "AZ-104",
778
+ "title": "Activity Log โ€” ํ™œ๋™ ๋กœ๊ทธ",
779
+ "summary": "๊ตฌ๋… ๋ ˆ๋ฒจ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ด€๋ฆฌ ์ž‘์—…(๋ฆฌ์†Œ์Šค ์ƒ์„ฑยท์‚ญ์ œยท๋ณ€๊ฒฝ)์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ฐ€, ์–ธ์ œ, ๋ฌด์—‡์„ ํ–ˆ๋Š”์ง€ ๊ฐ์‚ฌ(Audit)์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
780
+ "example": "az monitor activity-log list \\\n --resource-group myRG \\\n --start-time 2024-01-01 \\\n --end-time 2024-01-31\n# Portal: Monitor โ†’ Activity Log",
781
+ "common_mistake": "Activity Log๋Š” ๊ธฐ๋ณธ 90์ผ ๋ณด๊ด€๋ฉ๋‹ˆ๋‹ค. ์žฅ๊ธฐ ๋ณด๊ด€์„ ์œ„ํ•ด Storage Account์— ์•„์นด์ด๋ธŒํ•˜๊ฑฐ๋‚˜ Log Analytics๋กœ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
782
+ "keywords": [
783
+ "activity log",
784
+ "audit",
785
+ "management plane",
786
+ "90 days",
787
+ "diagnostic settings"
788
+ ],
789
+ "source_id": "azure-monitor-overview",
790
+ "details": [
791
+ "๊ธฐ๋ก ๋‚ด์šฉ: ๋ˆ„๊ฐ€(caller), ์–ธ์ œ(eventTimestamp), ๋ฌด์—‡์„(operation), ์„ฑ๊ณต/์‹คํŒจ(status). ๋ฐ์ดํ„ฐ ํ‰๋ฉด ์ž‘์—…(Blob ์ฝ๊ธฐ ๋“ฑ)์€ ๊ธฐ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
792
+ "์ง„๋‹จ ์„ค์ •(Diagnostic Settings)์œผ๋กœ Activity Log๋ฅผ Log Analytics, Storage Account, Event Hub๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
793
+ "์‹œํ—˜ ํŒจํ„ด: ๋ฆฌ์†Œ์Šค ์‚ญ์ œ ์ฑ…์ž„์ž ์ถ”์ , 90์ผ ์ด์ƒ ๋ณด๊ด€ ๋ฐฉ๋ฒ•, Activity Log vs Diagnostic Log(๋ฆฌ์†Œ์Šค ๋ ˆ๋ฒจ) ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
794
+ ],
795
+ "level": "์ค‘๊ธ‰"
796
+ },
797
+ {
798
+ "id": "azure-alert",
799
+ "certification": "AZ-104",
800
+ "title": "Alert Rule โ€” ๊ฒฝ๋ณด ๊ทœ์น™",
801
+ "summary": "๋ฉ”ํŠธ๋ฆญยท๋กœ๊ทธยทํ™œ๋™ ๋กœ๊ทธ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋ฉด ์ด๋ฉ”์ผยทSMSยทwebhookยทITSM ๋“ฑ์œผ๋กœ ์•Œ๋ฆฝ๋‹ˆ๋‹ค. Action Group์— ์•Œ๋ฆผ ๋Œ€์ƒ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
802
+ "example": "az monitor alert create \\\n --name highCPU \\\n --resource /subscriptions/.../myVM \\\n --condition 'avg Percentage CPU > 80' \\\n --action email alice@example.com",
803
+ "common_mistake": "๊ฒฝ๋ณด๋Š” Action Group ์—†์ด๋Š” ๋ฐœ์ƒํ•ด๋„ ์•„๋ฌด๋„ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ Action Group์— ์•Œ๋ฆผ ์ฑ„๋„์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
804
+ "keywords": [
805
+ "alert rule",
806
+ "action group",
807
+ "metric alert",
808
+ "log alert",
809
+ "severity",
810
+ "signal"
811
+ ],
812
+ "source_id": "azure-monitor-overview",
813
+ "details": [
814
+ "์‹ ํ˜ธ ์œ ํ˜•: ๋ฉ”ํŠธ๋ฆญ(์ˆ˜์น˜ ์ž„๊ณ„๊ฐ’), ๋กœ๊ทธ(KQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ), ํ™œ๋™ ๋กœ๊ทธ(ํŠน์ • ์ž‘์—… ๋ฐœ์ƒ), ์„œ๋น„์Šค ์ƒํƒœ.",
815
+ "Action Group: ์ด๋ฉ”์ผ, SMS, ์ „ํ™”, Azure Function, Logic App, webhook, ITSM ํ†ตํ•ฉ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ฒฝ๋ณด์—์„œ ๊ณต์œ  ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
816
+ "์‹œํ—˜ ํŒจํ„ด: ๋ฉ”ํŠธ๋ฆญ ๊ฒฝ๋ณด ๊ตฌ์„ฑ ์ ˆ์ฐจ, Action Group ์ƒ์„ฑ ๋ฐ ์—ฐ๊ฒฐ, ๊ฒฝ๋ณด ์‹ฌ๊ฐ๋„(0~4) ๋ ˆ๋ฒจ, ์ •์  vs ๋™์  ์ž„๊ณ„๊ฐ’์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
817
+ ],
818
+ "level": "์ค‘๊ธ‰",
819
+ "related_practices": [
820
+ "azure-lab-15"
821
+ ]
822
+ },
823
+ {
824
+ "id": "azure-backup-policy",
825
+ "certification": "AZ-104",
826
+ "title": "Backup Policy โ€” ๋ฐฑ์—… ์ •์ฑ…",
827
+ "summary": "๋ฐฑ์—… ์Šค์ผ€์ค„(์ผ๋ณ„/์ฃผ๋ณ„)๊ณผ ๋ณด์กด ๊ธฐ๊ฐ„(์ผ/์ฃผ/์›”/๋…„)์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. Recovery Services Vault์—์„œ ์›Œํฌ๋กœ๋“œ๋ณ„๋กœ ์ •์ฑ…์„ ๋งŒ๋“ค์–ด ๋ฆฌ์†Œ์Šค์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
828
+ "example": "az backup policy create \\\n --vault-name myVault \\\n --resource-group myRG \\\n --name myPolicy \\\n --policy @policy.json\n# policy.json์— ์Šค์ผ€์ค„๊ณผ ๋ณด์กด ๊ธฐ๊ฐ„ ์ •์˜",
829
+ "common_mistake": "๋ฐฑ์—… ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•ด๋„ ๊ธฐ์กด ๋ณต์› ์ง€์  ๋ณด์กด ๊ธฐ๊ฐ„์€ ์†Œ๊ธ‰ ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ์ดํ›„ ์ƒ์„ฑ๋˜๋Š” ๋ณต์› ์ง€์ ์—๋งŒ ์ƒˆ ์ •์ฑ…์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
830
+ "keywords": [
831
+ "backup policy",
832
+ "schedule",
833
+ "retention",
834
+ "RPO",
835
+ "Recovery Services Vault"
836
+ ],
837
+ "source_id": "azure-recovery-services-vault",
838
+ "details": [
839
+ "Enhanced Policy(ํ–ฅ์ƒ๋œ ์ •์ฑ…): ์‹œ๊ฐ„๋‹น ๋ฐฑ์—…, AZ ์ค‘๋ณต ๋ณต์› ์ง€์ , Trusted Launch VM ์ง€์› ๋“ฑ ๋” ๋งŽ์€ ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
840
+ "๋ณด์กด ๊ณ„์ธต: ์ผ๋ณ„(์ตœ๋Œ€ 180์ผ), ์ฃผ๋ณ„(์ตœ๋Œ€ 520์ฃผ), ์›”๋ณ„(์ตœ๋Œ€ 120๊ฐœ์›”), ์—ฐ๋ณ„(์ตœ๋Œ€ 10๋…„).",
841
+ "์‹œํ—˜ ํŒจํ„ด: RPO(๋ณต๊ตฌ ์‹œ์  ๋ชฉํ‘œ)์— ๋”ฐ๋ฅธ ๋ฐฑ์—… ์Šค์ผ€์ค„ ์„ ํƒ, ์žฅ๊ธฐ ๋ณด์กด ์„ค์ •, GRS ๋ฐฑ์—…์œผ๋กœ ์ง€์—ญ ๊ฐ„ ๋ณต์› ๋ฐฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
842
+ ],
843
+ "level": "์ค‘๊ธ‰",
844
+ "related_practices": [
845
+ "azure-lab-8"
846
+ ]
847
+ },
848
+ {
849
+ "id": "azure-restore",
850
+ "certification": "AZ-104",
851
+ "title": "Restore โ€” ๋ณต์›",
852
+ "summary": "Recovery Services Vault์˜ ๋ณต์› ์ง€์ ์—์„œ VM, ๋””์Šคํฌ, ํŒŒ์ผ์„ ์›๋ž˜ ์œ„์น˜ ๋˜๋Š” ์ƒˆ ์œ„์น˜๋กœ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค.",
853
+ "example": "az backup restore restore-disks \\\n --vault-name myVault \\\n --resource-group myRG \\\n --container-name myVM \\\n --item-name myVM \\\n --rp-name 'RecoveryPointName' \\\n --storage-account myStorageAcct",
854
+ "common_mistake": "VM ์ „์ฒด ๋ณต์›(Restore VM)๊ณผ ๋””์Šคํฌ๋งŒ ๋ณต์›(Restore Disks)์€ ๋‹ค๋ฅธ ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๋””์Šคํฌ ๋ณต์› ํ›„์—๋Š” VM์„ ๋ณ„๋„๋กœ ์žฌ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
855
+ "keywords": [
856
+ "restore",
857
+ "recovery point",
858
+ "original location",
859
+ "alternate location",
860
+ "cross-region restore"
861
+ ],
862
+ "source_id": "azure-recovery-services-vault",
863
+ "details": [
864
+ "๋ณต์› ์˜ต์…˜: ์›๋ž˜ ์œ„์น˜(๊ธฐ์กด VM ๋ฎ์–ด์“ฐ๊ธฐ), ์ƒˆ ์œ„์น˜(์ƒˆ VM ์ƒ์„ฑ), ๋””์Šคํฌ๋งŒ ๋ณต์›(Storage Account์— VHD ์ €์žฅ), ํŒŒ์ผ ๋ณต์›(ํŒŒ์ผ ๋‹จ์œ„).",
865
+ "์ง€์—ญ ๊ฐ„ ๋ณต์›(CRR): GRS Vault์—์„œ ๋ณด์กฐ region์˜ ๋ณต์› ์ง€์ ์œผ๋กœ DR ๋ณต์›. CRR ๊ธฐ๋Šฅ์„ Vault์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
866
+ "์‹œํ—˜ ํŒจํ„ด: ๋ณต์› ์œ ํ˜• ์„ ํƒ(VM vs ํŒŒ์ผ), ์ง€์—ญ ๊ฐ„ ๋ณต์› ์š”๊ฑด, Soft Delete ์ƒํƒœ์—์„œ ๋ณต์› ๊ฐ€๋Šฅ ์—ฌ๋ถ€๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
867
+ ],
868
+ "level": "์ค‘๊ธ‰",
869
+ "related_practices": [
870
+ "azure-lab-8"
871
+ ]
872
+ },
873
+ {
874
+ "id": "azure-app-service-plan",
875
+ "certification": "AZ-104",
876
+ "title": "App Service Plan โ€” ์•ฑ ์„œ๋น„์Šค ํ”Œ๋žœ",
877
+ "summary": "App Service ์•ฑ์ด ์‹คํ–‰๋˜๋Š” ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค(CPU, ๋ฉ”๋ชจ๋ฆฌ, ์Šคํ† ๋ฆฌ์ง€)๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋™์ผ ํ”Œ๋žœ์˜ ์•ฑ์€ ๊ฐ™์€ VM ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.",
878
+ "example": "az appservice plan create \\\n --name myPlan \\\n --resource-group myRG \\\n --sku P1v3 \\\n --is-linux\naz webapp create --name myApp --plan myPlan --runtime 'NODE:18-lts'",
879
+ "common_mistake": "Free/Shared ํ”Œ๋žœ์€ Autoscale๊ณผ ์ปค์Šคํ…€ ๋„๋ฉ”์ธ SSL์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—๋Š” Standard(S1) ์ด์ƒ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
880
+ "keywords": [
881
+ "App Service Plan",
882
+ "SKU",
883
+ "Free",
884
+ "Basic",
885
+ "Standard",
886
+ "Premium",
887
+ "Isolated",
888
+ "autoscale"
889
+ ],
890
+ "source_id": "azure-app-service-overview",
891
+ "details": [
892
+ "SKU ๊ณ„์ธต: Free/Shared(๊ณต์œ  ์ธํ”„๋ผ, SLA ์—†์Œ), Basic(์ „์šฉ VM), Standard(Autoscale, SSL), Premium(๊ณ ์„ฑ๋Šฅ), Isolated(VNet ์ „์šฉ ํ™˜๊ฒฝ).",
893
+ "๊ฐ™์€ ํ”Œ๋žœ์— ์—ฌ๋Ÿฌ ์•ฑ์„ ๋„ฃ์œผ๋ฉด ์ปดํ“จํŒ…์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์ด ๋งŽ์•„์ง€๋ฉด ํ”Œ๋žœ์„ ์Šค์ผ€์ผ์—…(ํฌ๊ธฐ ๋ณ€๊ฒฝ)ํ•˜๊ฑฐ๋‚˜ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
894
+ "์‹œํ—˜ ํŒจํ„ด: ํ”Œ๋žœ SKU๋ณ„ ์ง€์› ๊ธฐ๋Šฅ(Autoscale, Deployment Slots, VNet ํ†ตํ•ฉ ์กฐ๊ฑด), ํ”Œ๋žœ ๋ณ€๊ฒฝ ์‹œ ์•ฑ ์˜ํ–ฅ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
895
+ ],
896
+ "level": "๊ณ ๊ธ‰",
897
+ "related_practices": [
898
+ "azure-lab-13"
899
+ ]
900
+ },
901
+ {
902
+ "id": "azure-container-registry",
903
+ "certification": "AZ-104",
904
+ "title": "Azure Container Registry โ€” ์ปจํ…Œ์ด๋„ˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ",
905
+ "summary": "Docker ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€์™€ Helm ์ฐจํŠธ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์‚ฌ์„ค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์ž…๋‹ˆ๋‹ค. AKS, Container Instances, App Service์™€ ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค.",
906
+ "example": "az acr create \\\n --name myRegistry \\\n --resource-group myRG \\\n --sku Standard\naz acr build --registry myRegistry --image myapp:v1 .\ndocker pull myregistry.azurecr.io/myapp:v1",
907
+ "common_mistake": "ACR์—์„œ ์ด๋ฏธ์ง€๋ฅผ pullํ•˜๋ ค๋ฉด ์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. AKS์—์„œ ์‚ฌ์šฉ ์‹œ Managed Identity ๋˜๋Š” Service Principal๋กœ pull ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
908
+ "keywords": [
909
+ "ACR",
910
+ "container registry",
911
+ "docker",
912
+ "image",
913
+ "geo-replication",
914
+ "SKU Basic/Standard/Premium"
915
+ ],
916
+ "source_id": "azure-app-service-overview",
917
+ "details": [
918
+ "SKU: Basic(๊ฐœ๋ฐœ์šฉ), Standard(์ผ๋ฐ˜, geo-replication ๋ฏธ์ง€์›), Premium(geo-replication, Private Link ์ง€์›).",
919
+ "ACR Tasks: ์ด๋ฏธ์ง€ ๋นŒ๋“œยทํ…Œ์ŠคํŠธยทํŒจ์น˜๋ฅผ Azure์—์„œ ์ง์ ‘ ์‹คํ–‰. Docker ์—†์ด ํด๋ผ์šฐ๋“œ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค.",
920
+ "์‹œํ—˜ ํŒจํ„ด: ACR SKU๋ณ„ ๊ธฐ๋Šฅ ์ฐจ์ด, AKS์—์„œ ACR ์ด๋ฏธ์ง€ pull ๊ถŒํ•œ ์„ค์ •(Managed Identity ์—ฐ๊ฒฐ), geo-replication ์„ค์ •์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
921
+ ],
922
+ "level": "๊ณ ๊ธ‰"
923
+ },
924
+ {
925
+ "id": "azure-key-vault",
926
+ "certification": "AZ-104",
927
+ "title": "Key Vault โ€” ํ‚ค ์ž๊ฒฉ ์ฆ๋ช… ๋ชจ์Œ",
928
+ "summary": "๋น„๋ฐ€(Secret), ํ‚ค(Key), ์ธ์ฆ์„œ(Certificate)๋ฅผ ์ค‘์•™์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅยท๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์•ฑ ์ฝ”๋“œ์—์„œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ œ๊ฑฐํ•˜๋Š” ํ•ต์‹ฌ ๋ณด์•ˆ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.",
929
+ "example": "az keyvault create --name myKV --resource-group myRG --location koreacentral\naz keyvault secret set --vault-name myKV --name 'db-password' --value 'MyP@ss'\naz keyvault secret show --vault-name myKV --name 'db-password'",
930
+ "common_mistake": "Key Vault์— ์ ‘๊ทผํ•˜๋ ค๋ฉด Access Policy ๋˜๋Š” RBAC๋กœ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Managed Identity๋ฅผ ํ†ตํ•ด ์•ฑ์ด ์ž๊ฒฉ ์ฆ๋ช… ์—†์ด Key Vault์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.",
931
+ "keywords": [
932
+ "Key Vault",
933
+ "secret",
934
+ "key",
935
+ "certificate",
936
+ "soft delete",
937
+ "RBAC",
938
+ "access policy"
939
+ ],
940
+ "source_id": "azure-key-vault-overview",
941
+ "details": [
942
+ "์ ‘๊ทผ ์ œ์–ด: ๊ตฌ์‹ Access Policy ๋ฐฉ์‹๊ณผ ์ตœ์‹  Azure RBAC ๋ฐฉ์‹ ๋‘ ๊ฐ€์ง€. ์ƒˆ ๊ตฌ์ถ•์€ RBAC ๋ฐฉ์‹ ๊ถŒ์žฅ.",
943
+ "Soft Delete์™€ Purge Protection: ์‹ค์ˆ˜๋กœ ์‚ญ์ œ๋œ ๋น„๋ฐ€์„ 7~90์ผ ๋‚ด ๋ณต์› ๊ฐ€๋Šฅ. Purge Protection ํ™œ์„ฑํ™” ์‹œ ์‚ญ์ œ๋œ ํ•ญ๋ชฉ ์˜๊ตฌ ์ œ๊ฑฐ ๋ถˆ๊ฐ€.",
944
+ "์‹œํ—˜ ํŒจํ„ด: Managed Identity๋กœ ์•ฑ์—์„œ ๋น„๋ฐ€ ์ ‘๊ทผ, Soft Delete/Purge Protection ๊ฐœ๋…, HSM(ํ•˜๋“œ์›จ์–ด ๋ณด์•ˆ ๋ชจ๋“ˆ) ์ง€์› SKU ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
945
+ ],
946
+ "level": "์ค‘๊ธ‰",
947
+ "related_practices": [
948
+ "azure-lab-9"
949
+ ]
950
+ },
951
+ {
952
+ "id": "azure-cost-management",
953
+ "certification": "AZ-104",
954
+ "title": "Cost Management โ€” ๋น„์šฉ ๊ด€๋ฆฌ",
955
+ "summary": "Azure ์„œ๋น„์Šค ์‚ฌ์šฉ ๋น„์šฉ์„ ๋ถ„์„ํ•˜๊ณ  ์˜ˆ์‚ฐ์„ ์„ค์ •ํ•ด ์ดˆ๊ณผ ์‹œ ์•Œ๋ฆผ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ํƒœ๊ทธยท๊ตฌ๋…ยทRG๋ณ„๋กœ ๋น„์šฉ์„ ์„ธ๋ถ„ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
956
+ "example": "az consumption budget create \\\n --budget-name monthlyBudget \\\n --amount 1000 \\\n --time-grain Monthly \\\n --start-date 2024-01-01 \\\n --end-date 2024-12-31",
957
+ "common_mistake": "์˜ˆ์‚ฐ ๊ฒฝ๋ณด๋Š” ์ง€์ถœ์ด ์ž„๊ณ„๊ฐ’์— ๋„๋‹ฌํ–ˆ์„ ๋•Œ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ์ด์ง€, ์ž๋™์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜ ๋น„์šฉ์„ ์ฐจ๋‹จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
958
+ "keywords": [
959
+ "Cost Management",
960
+ "budget",
961
+ "alert",
962
+ "cost analysis",
963
+ "tags",
964
+ "billing"
965
+ ],
966
+ "source_id": "azure-monitor-overview",
967
+ "details": [
968
+ "๋น„์šฉ ๋ถ„์„(Cost Analysis): ๊ธฐ๊ฐ„ยท์„œ๋น„์Šคยทlocationยทํƒœ๊ทธ ๋“ฑ์œผ๋กœ ํ•„ํ„ฐ๋งํ•ด ๋น„์šฉ ๋‚ด์—ญ์„ ํŒŒ์ด/๋ˆ„์  ์ฐจํŠธ๋กœ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.",
969
+ "์˜ˆ์‚ฐ ๊ฒฝ๋ณด: ์‹ค์ œ ๋น„์šฉ ๋˜๋Š” ์˜ˆ์ธก ๋น„์šฉ์ด ์„ค์ • ๋น„์œจ(์˜ˆ: 80%, 100%)์— ๋„๋‹ฌํ•˜๋ฉด Action Group์œผ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.",
970
+ "์‹œํ—˜ ํŒจํ„ด: ์˜ˆ์‚ฐ ๊ฒฝ๋ณด ์„ค์ • ์ ˆ์ฐจ, ํƒœ๊ทธ๋ฅผ ํ†ตํ•œ ๋น„์šฉ ๋ถ„๋ฅ˜, Cost Management vs Azure Advisor ์—ญํ•  ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
971
+ ],
972
+ "level": "์ค‘๊ธ‰"
973
+ },
974
+ {
975
+ "id": "azure-bastion",
976
+ "certification": "AZ-104",
977
+ "title": "Azure Bastion โ€” ์•ˆ์ „ํ•œ ์›๊ฒฉ ์ ‘์†",
978
+ "summary": "๊ณต์šฉ IP ์—†์ด Azure Portal์—์„œ ์ง์ ‘ VM์— RDP/SSH๋กœ ์ ‘์†ํ•ฉ๋‹ˆ๋‹ค. TLS 443 ํฌํŠธ๋งŒ ์‚ฌ์šฉํ•ด VM์˜ ๊ด€๋ฆฌ ํฌํŠธ๋ฅผ ์ธํ„ฐ๋„ท์— ๋…ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
979
+ "example": "az network bastion create \\\n --name myBastion \\\n --public-ip-address myBastionIP \\\n --resource-group myRG \\\n --vnet-name myVNet \\\n --location koreacentral",
980
+ "common_mistake": "Bastion์€ VNet์— `AzureBastionSubnet`์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์„œ๋ธŒ๋„ท(/27 ์ด์ƒ)์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
981
+ "keywords": [
982
+ "Azure Bastion",
983
+ "RDP",
984
+ "SSH",
985
+ "AzureBastionSubnet",
986
+ "port 443",
987
+ "no public IP"
988
+ ],
989
+ "source_id": "azure-bastion-overview",
990
+ "details": [
991
+ "Bastion SKU: Basic(RDP/SSH ๊ธฐ๋ณธ), Standard(ํŒŒ์ผ ์ „์†ก, ๋ฉ€ํ‹ฐํƒญ, ๊ธฐ๋ณธ ํ˜ธ์ŠคํŠธ ํฌํŠธ ์™ธ ํฌํŠธ, VM Auth ๋“ฑ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ).",
992
+ "VM์— Public IP์™€ NSG์— RDP/SSH ํ—ˆ์šฉ ๊ทœ์น™์ด ์—†์–ด๋„ Bastion์œผ๋กœ ์ ‘์† ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ณต๊ฒฉ ํ‘œ๋ฉด์ด ํฌ๊ฒŒ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.",
993
+ "์‹œํ—˜ ํŒจํ„ด: Bastion ์„œ๋ธŒ๋„ท ์ด๋ฆ„ยทํฌ๊ธฐ ์š”๊ตฌ์‚ฌํ•ญ, Public IP ์—†๋Š” VM ์ ‘์† ๋ฐฉ๋ฒ•, JIT(Just-In-Time) VM Access์™€ ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
994
+ ],
995
+ "level": "์ค‘๊ธ‰",
996
+ "related_practices": [
997
+ "azure-lab-12"
998
+ ]
999
+ },
1000
+ {
1001
+ "id": "azure-nat-gateway",
1002
+ "certification": "AZ-104",
1003
+ "title": "NAT Gateway โ€” ์•„์›ƒ๋ฐ”์šด๋“œ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ",
1004
+ "summary": "์„œ๋ธŒ๋„ท์˜ ๋ชจ๋“  VM์ด ๋™์ผํ•œ ๊ณ ์ • ๊ณต์šฉ IP๋กœ ์ธํ„ฐ๋„ท์— ๋‚˜๊ฐ€๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์•„์›ƒ๋ฐ”์šด๋“œ ์—ฐ๊ฒฐ ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ณ  SNAT ํฌํŠธ ๊ณ ๊ฐˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
1005
+ "example": "az network public-ip create --name myNatIP --resource-group myRG --sku Standard\naz network nat gateway create \\\n --name myNatGW \\\n --resource-group myRG \\\n --public-ip-addresses myNatIP\naz network vnet subnet update --nat-gateway myNatGW ...",
1006
+ "common_mistake": "NAT Gateway๋Š” ์•„์›ƒ๋ฐ”์šด๋“œ ์ „์šฉ์ž…๋‹ˆ๋‹ค. ์ธ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›์œผ๋ ค๋ฉด ์—ฌ์ „ํžˆ Public IP ๋˜๋Š” Load Balancer๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
1007
+ "keywords": [
1008
+ "NAT Gateway",
1009
+ "outbound",
1010
+ "SNAT",
1011
+ "public IP",
1012
+ "port exhaustion",
1013
+ "static IP"
1014
+ ],
1015
+ "source_id": "azure-nat-gateway",
1016
+ "details": [
1017
+ "SNAT(Source NAT): VM์˜ ์‚ฌ์„ค IP๋ฅผ NAT Gateway์˜ ๊ณต์šฉ IP๋กœ ๋ณ€ํ™˜. ์—ฌ๋Ÿฌ VM์ด ๊ฐ™์€ ๊ณต์šฉ IP๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.",
1018
+ "Load Balancer ์•„์›ƒ๋ฐ”์šด๋“œ ๊ทœ์น™ vs NAT Gateway: NAT Gateway๊ฐ€ ๋” ๋งŽ์€ SNAT ํฌํŠธ(64,512 ร— IP ์ˆ˜)๋ฅผ ์ œ๊ณตํ•ด ๊ณ ๋ถ€ํ•˜์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
1019
+ "์‹œํ—˜ ํŒจํ„ด: NAT Gateway ์„œ๋ธŒ๋„ท ์—ฐ๊ฒฐ ๋ฐฉ๋ฒ•, ๊ณ ์ • ์•„์›ƒ๋ฐ”์šด๋“œ IP ์š”๊ตฌ ์‹œ๋‚˜๋ฆฌ์˜ค, Load Balancer์™€ ๋™์‹œ ์‚ฌ์šฉ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1020
+ ],
1021
+ "level": "์ค‘๊ธ‰"
1022
+ },
1023
+ {
1024
+ "id": "azure-app-service",
1025
+ "certification": "AZ-104",
1026
+ "title": "App Service โ€” ์›น ์•ฑ ํ˜ธ์ŠคํŒ…",
1027
+ "summary": "์ฝ”๋“œ ๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ์™„์ „ ๊ด€๋ฆฌํ˜• PaaS ์›น ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. .NET, Node.js, Python, Java, PHP ๋“ฑ ๋‹ค์–‘ํ•œ ๋Ÿฐํƒ€์ž„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
1028
+ "example": "az webapp create \\\n --name myWebApp \\\n --resource-group myRG \\\n --plan myPlan \\\n --runtime 'PYTHON:3.11'\naz webapp deployment source config-zip \\\n --name myWebApp --src app.zip",
1029
+ "common_mistake": "App Service์˜ ์•ฑ ์„ค์ •(Application Settings)์€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์•ฑ์— ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์„ค์ •๋ณด๋‹ค ์•ฑ ์„ค์ •์— ๋„ฃ๋Š” ๊ฒƒ์ด ๋ณด์•ˆ์— ์ข‹์Šต๋‹ˆ๋‹ค.",
1030
+ "keywords": [
1031
+ "App Service",
1032
+ "web app",
1033
+ "deployment slot",
1034
+ "custom domain",
1035
+ "SSL",
1036
+ "VNet integration"
1037
+ ],
1038
+ "source_id": "azure-app-service-overview",
1039
+ "details": [
1040
+ "๋ฐฐํฌ ๋ฐฉ๋ฒ•: ZIP ๋ฐฐํฌ, Git(๋กœ์ปฌ/GitHub Actions), Docker ์ด๋ฏธ์ง€, Azure DevOps. CI/CD ์—ฐ๋™์ด ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.",
1041
+ "VNet ํ†ตํ•ฉ: Standard ์ด์ƒ ํ”Œ๋žœ์—์„œ App Service๋ฅผ VNet์— ์—ฐ๊ฒฐํ•ด ์‚ฌ์„ค ๋ฆฌ์†Œ์Šค(DB, ์บ์‹œ)์— ์ ‘๏ฟฝ๏ฟฝ๏ฟฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1042
+ "์‹œํ—˜ ํŒจํ„ด: ์ปค์Šคํ…€ ๋„๋ฉ”์ธยทSSL ๋ฐ”์ธ๋”ฉ, ๋ฐฐํฌ ์Šฌ๋กฏ ์Šค์™‘, VNet ํ†ตํ•ฉ ์„ค์ •, App Settings vs Connection Strings ์ฐจ์ด๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1043
+ ],
1044
+ "level": "๊ณ ๊ธ‰",
1045
+ "related_practices": [
1046
+ "azure-lab-13"
1047
+ ]
1048
+ },
1049
+ {
1050
+ "id": "azure-deployment-slot",
1051
+ "certification": "AZ-104",
1052
+ "title": "Deployment Slot โ€” ๋ฐฐํฌ ์Šฌ๋กฏ",
1053
+ "summary": "์•ฑ์˜ ์Šคํ…Œ์ด์ง• ๋ฒ„์ „์„ ๋ณ„๋„ ์Šฌ๋กฏ์— ๋ฐฐํฌํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ›„ ํ”„๋กœ๋•์…˜ ์Šฌ๋กฏ๊ณผ ์ฆ‰์‹œ ๊ต์ฒด(Swap)ํ•ฉ๋‹ˆ๋‹ค. Standard ์ด์ƒ ํ”Œ๋žœ์—์„œ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
1054
+ "example": "az webapp deployment slot create \\\n --name myWebApp \\\n --resource-group myRG \\\n --slot staging\n# ํ…Œ์ŠคํŠธ ํ›„ swap\naz webapp deployment slot swap \\\n --name myWebApp --slot staging",
1055
+ "common_mistake": "Swap ์‹œ ์Šฌ๋กฏ ๊ณ ์œ  ์„ค์ •(Slot Settings๋กœ ํ‘œ์‹œ๋œ ํ•ญ๋ชฉ)์€ ๊ต์ฒด๋˜์ง€ ์•Š๊ณ  ๊ฐ ์Šฌ๋กฏ์— ๊ณ ์ •๋ฉ๋‹ˆ๋‹ค. ์˜ˆ: ์Šคํ…Œ์ด์ง• DB ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์€ ํ”„๋กœ๋•์…˜์œผ๋กœ ๋”ฐ๋ผ๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
1056
+ "keywords": [
1057
+ "deployment slot",
1058
+ "staging",
1059
+ "swap",
1060
+ "slot settings",
1061
+ "traffic routing",
1062
+ "warm-up"
1063
+ ],
1064
+ "source_id": "azure-app-service-overview",
1065
+ "details": [
1066
+ "Swap ๋™์ž‘: ์Šฌ๋กฏ์ด ์›Œ๋ฐ์—…๋œ ํ›„ ๋ผ์šฐํ„ฐ ์ˆ˜์ค€์—์„œ ํŠธ๋ž˜ํ”ฝ์ด ์ฆ‰์‹œ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. ๋‹ค์šดํƒ€์ž„ ์—†์ด ์ƒˆ ๋ฒ„์ „ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
1067
+ "ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ…: % ๊ธฐ๋ฐ˜์œผ๋กœ ์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ๋งŒ ์Šคํ…Œ์ด์ง• ์Šฌ๋กฏ์œผ๋กœ ๋ณด๋‚ด A/B ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1068
+ "์‹œํ—˜ ํŒจํ„ด: Slot Settings(์Šฌ๋กฏ ๊ณ ์ • ์„ค์ •) ๊ฐœ๋…, Swap ์‹œ ์„ค์ • ๊ต์ฒด ๊ทœ์น™, Auto Swap ๊ธฐ๋Šฅ, ์Šฌ๋กฏ ์ˆ˜ ์ œํ•œ(Standard=5) ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1069
+ ],
1070
+ "level": "๊ณ ๊ธ‰",
1071
+ "related_practices": [
1072
+ "azure-lab-14"
1073
+ ]
1074
+ },
1075
+ {
1076
+ "id": "azure-aci",
1077
+ "certification": "AZ-104",
1078
+ "title": "Azure Container Instances โ€” ์ปจํ…Œ์ด๋„ˆ ์ธ์Šคํ„ด์Šค",
1079
+ "summary": "VM์ด๋‚˜ ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ ์—†์ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ดˆ ๋‹จ์œ„๋กœ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ ์ž‘์—…, ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ, ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
1080
+ "example": "az container create \\\n --name myapp \\\n --resource-group myRG \\\n --image nginx:latest \\\n --cpu 1 --memory 1.5 \\\n --ports 80 \\\n --dns-name-label myapp-dns",
1081
+ "common_mistake": "ACI๋Š” ๋‹จ๊ธฐ ์‹คํ–‰ ์ปจํ…Œ์ด๋„ˆ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ง€์†์ ์ธ ๊ณ ๊ฐ€์šฉ์„ฑ ์›น ์•ฑ์ด๋‚˜ ๋ณต์žกํ•œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์—๋Š” AKS(Kubernetes)๊ฐ€ ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
1082
+ "keywords": [
1083
+ "ACI",
1084
+ "container",
1085
+ "serverless",
1086
+ "container group",
1087
+ "CPU/memory",
1088
+ "VNet"
1089
+ ],
1090
+ "source_id": "azure-container-instances",
1091
+ "details": [
1092
+ "Container Group: ๋™์ผ ํ˜ธ์ŠคํŠธ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ ๋ชจ์Œ. Kubernetes Pod ๊ฐœ๋…๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.",
1093
+ "VNet ๋ฐฐํฌ: ACI๋ฅผ VNet์— ๋ฐฐํฌํ•˜๋ฉด ์‚ฌ์„ค IP๋ฅผ ๋ถ€์—ฌ๋ฐ›์•„ ๋‹ค๋ฅธ Azure ๋ฆฌ์†Œ์Šค์™€ ์‚ฌ์„ค๋ง์—์„œ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.",
1094
+ "์‹œํ—˜ ํŒจํ„ด: ACI vs AKS ์„ ํƒ ๊ธฐ์ค€, ์ปจํ…Œ์ด๋„ˆ ๊ทธ๋ฃน ๋‚ด ์‚ฌ์ด๋“œ์นด ํŒจํ„ด, ์žฌ์‹œ์ž‘ ์ •์ฑ…(Always/OnFailure/Never) ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1095
+ ],
1096
+ "level": "๊ณ ๊ธ‰"
1097
+ },
1098
+ {
1099
+ "id": "azure-keyvault-secret",
1100
+ "certification": "AZ-104",
1101
+ "title": "Key Vault Secret โ€” ๋น„๋ฐ€ ๊ด€๋ฆฌ",
1102
+ "summary": "Key Vault์—์„œ API ํ‚ค, ๋น„๋ฐ€๋ฒˆํ˜ธ, ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ๋ฒ„์ „ ๊ด€๋ฆฌ์™€ ํ•จ๊ป˜ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์—์„œ Managed Identity๋กœ ์ฝ”๋“œ์— ์ž๊ฒฉ ์ฆ๋ช… ์—†์ด ๋น„๋ฐ€์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.",
1103
+ "example": "az keyvault secret set --vault-name myKV --name 'APIKey' --value 'secret123'\naz keyvault secret show --vault-name myKV --name 'APIKey' --query 'value'\n# ์•ฑ์—์„œ: ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋˜๋Š” Key Vault ์ฐธ์กฐ๋กœ ์ฃผ์ž…",
1104
+ "common_mistake": "๋น„๋ฐ€์„ ์‚ญ์ œํ•˜๋ฉด ๋ชจ๋“  ๋ฒ„์ „์ด ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. Soft Delete๊ฐ€ ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ณต์› ๊ธฐ๊ฐ„(90์ผ ๊ธฐ๋ณธ) ๋‚ด์— ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
1105
+ "keywords": [
1106
+ "secret",
1107
+ "version",
1108
+ "managed identity",
1109
+ "Key Vault reference",
1110
+ "rotation",
1111
+ "soft delete"
1112
+ ],
1113
+ "source_id": "azure-key-vault-overview",
1114
+ "details": [
1115
+ "๋น„๋ฐ€ ๋ฒ„์ „: ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ ๋ฒ„์ „์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์•ฑ์€ ๋ฒ„์ „ ์—†์ด ์ฐธ์กฐํ•˜๋ฉด ํ˜„์žฌ ํ™œ์„ฑ ๋ฒ„์ „์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.",
1116
+ "Key Vault ์ฐธ์กฐ: App Service ์•ฑ ์„ค์ •์—์„œ `@Microsoft.KeyVault(SecretUri=...)` ํ˜•์‹์œผ๋กœ ๋น„๋ฐ€์„ ์ง์ ‘ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.",
1117
+ "์‹œํ—˜ ํŒจํ„ด: App Service์—์„œ Key Vault ์ฐธ์กฐ๋กœ ๋น„๋ฐ€ ์ฃผ์ž…, ๋น„๋ฐ€ ์ˆœํ™˜(rotation) ์„ค์ •, Managed Identity์™€ Key Vault ์—ฐ๋™ ์ ˆ์ฐจ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1118
+ ],
1119
+ "level": "๊ณ ๊ธ‰",
1120
+ "related_practices": [
1121
+ "azure-lab-9"
1122
+ ]
1123
+ },
1124
+ {
1125
+ "id": "azure-managed-identity",
1126
+ "certification": "AZ-104",
1127
+ "title": "Managed Identity โ€” ๊ด€๋ฆฌํ˜• ID",
1128
+ "summary": "Azure ๋ฆฌ์†Œ์Šค(VM, App Service ๋“ฑ)์— Entra ID ID๋ฅผ ๋ถ€์—ฌํ•ด ์ž๊ฒฉ ์ฆ๋ช… ์—†์ด ๋‹ค๋ฅธ Azure ์„œ๋น„์Šค์— ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์— ๋น„๋ฐ€์„ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.",
1129
+ "example": "# VM์— System-Assigned MI ํ™œ์„ฑํ™”\naz vm identity assign --name myVM --resource-group myRG\n# MI์— ์—ญํ•  ํ• ๋‹น\naz role assignment create \\\n --assignee $(az vm show --name myVM --query identity.principalId -o tsv) \\\n --role 'Key Vault Secrets User' \\\n --scope /subscriptions/.../vaults/myKV",
1130
+ "common_mistake": "System-Assigned MI๋Š” ๋ฆฌ์†Œ์Šค ์‚ญ์ œ ์‹œ ํ•จ๊ป˜ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์—์„œ ๊ณต์œ ํ•˜๋Š” ID๊ฐ€ ํ•„์š”ํ•˜๋ฉด User-Assigned MI๋ฅผ ์”๋‹ˆ๋‹ค.",
1131
+ "keywords": [
1132
+ "managed identity",
1133
+ "system-assigned",
1134
+ "user-assigned",
1135
+ "principal ID",
1136
+ "service principal"
1137
+ ],
1138
+ "source_id": "azure-rbac-overview",
1139
+ "details": [
1140
+ "์œ ํ˜•: System-Assigned(๋ฆฌ์†Œ์Šค์— ์ข…์†, 1:1), User-Assigned(๋…๋ฆฝ์ ์œผ๋กœ ์ƒ์„ฑ, ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์— ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ).",
1141
+ "๋™์ž‘ ์›๋ฆฌ: ๋ฆฌ์†Œ์Šค๊ฐ€ Entra ID์—์„œ ํ† ํฐ์„ ์ž๋™ ํš๋“ โ†’ ํ•ด๋‹น ํ† ํฐ์œผ๋กœ Key Vault, Storage ๋“ฑ์— ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค.",
1142
+ "์‹œํ—˜ ํŒจํ„ด: System vs User-Assigned ์„ ํƒ ์‹œ๋‚˜๋ฆฌ์˜ค, MI์— RBAC ์—ญํ•  ๋ถ€์—ฌ ์ ˆ์ฐจ, App Service์—์„œ MI๋ฅผ ํ†ตํ•œ Key Vault ์ ‘๊ทผ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1143
+ ],
1144
+ "level": "๊ณ ๊ธ‰"
1145
+ },
1146
+ {
1147
+ "id": "azure-network-watcher",
1148
+ "certification": "AZ-104",
1149
+ "title": "Network Watcher โ€” ๋„คํŠธ์›Œํฌ ์ง„๋‹จ",
1150
+ "summary": "Azure ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•˜๋Š” ๋„๊ตฌ ๋ชจ์Œ์ž…๋‹ˆ๋‹ค. IP ํ๋ฆ„ ํ™•์ธ, ์—ฐ๊ฒฐ ๋ฌธ์ œ ํ•ด๊ฒฐ, NSG ์ง„๋‹จ, ํŒจํ‚ท ์บก์ฒ˜ ๋“ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
1151
+ "example": "# NSG๊ฐ€ ํŒจํ‚ท์„ ํ—ˆ์šฉํ•˜๋Š”์ง€ ํ™•์ธ\naz network watcher test-ip-flow \\\n --vm myVM \\\n --direction Inbound \\\n --protocol TCP \\\n --local 10.0.0.4:80 \\\n --remote 0.0.0.0:*",
1152
+ "common_mistake": "Network Watcher๋Š” region๋ณ„๋กœ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. VNet์„ ์ƒ์„ฑํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„ ์ž๋™ ํ™œ์„ฑํ™”๋˜์ง€๋งŒ, ๋น„ํ™œ์„ฑํ™”๋œ region์—์„œ๋Š” ์ง„๋‹จ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
1153
+ "keywords": [
1154
+ "Network Watcher",
1155
+ "IP flow verify",
1156
+ "connection troubleshoot",
1157
+ "NSG flow log",
1158
+ "packet capture"
1159
+ ],
1160
+ "source_id": "azure-vnet-overview",
1161
+ "details": [
1162
+ "IP ํ๋ฆ„ ํ™•์ธ: ํŠน์ • VM์—์„œ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€ IPยทํฌํŠธ๋กœ NSG๊ฐ€ ํ—ˆ์šฉ/๊ฑฐ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ทœ์น™์„ ์ฐพ์•„์ค๋‹ˆ๋‹ค.",
1163
+ "NSG Flow Log: ์„œ๋ธŒ๋„ทยทNIC๋ฅผ ํ†ต๊ณผํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์˜ ํ—ˆ์šฉ/๊ฑฐ๋ถ€ ํ๋ฆ„์„ Storage Account์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. Traffic Analytics์™€ ์—ฐ๋™ํ•ด ์‹œ๊ฐํ™”.",
1164
+ "์‹œํ—˜ ํŒจํ„ด: IP ํ๋ฆ„ ํ™•์ธ์œผ๋กœ NSG ๊ทœ์น™ ์ง„๋‹จ, Connection Troubleshoot์œผ๋กœ VM ๊ฐ„ ์—ฐ๊ฒฐ ๊ฒ€์ฆ, NSG Flow Log ์„ค์ •์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1165
+ ],
1166
+ "level": "์ค‘๊ธ‰"
1167
+ },
1168
+ {
1169
+ "id": "azure-move-resource",
1170
+ "certification": "AZ-104",
1171
+ "title": "๋ฆฌ์†Œ์Šค ์ด๋™ โ€” Resource Move",
1172
+ "summary": "๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ค๋ฅธ Resource Group์ด๋‚˜ ๋‹ค๋ฅธ Subscription์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ์ด๋™ ์ค‘ ๋ฆฌ์†Œ์Šค๋Š” ์ž ๊ธฐ๋ฉฐ, ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๊ฐ€ ์ด๋™์„ ์ง€์›ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.",
1173
+ "example": "az resource move \\\n --destination-group targetRG \\\n --ids /subscriptions/SUB/resourceGroups/sourceRG/providers/Microsoft.Compute/virtualMachines/myVM",
1174
+ "common_mistake": "์ด๋™ ํ›„ ๋ฆฌ์†Œ์Šค ID(ARM ID)๊ฐ€ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ์ด ID๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉํ•œ ์Šคํฌ๋ฆฝํŠธยท๋ชจ๋‹ˆํ„ฐ๋งยทRole Assignment๋Š” ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
1175
+ "keywords": [
1176
+ "resource move",
1177
+ "RG move",
1178
+ "subscription move",
1179
+ "ARM ID change",
1180
+ "limitations"
1181
+ ],
1182
+ "source_id": "azure-vm-overview",
1183
+ "details": [
1184
+ "์ด๋™ ์ „ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ: `az resource invoke-action --action validate-move`๋กœ ์ด๋™ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ๋ฏธ๋ฆฌ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
1185
+ "์ด๋™์ด ๋ถˆ๊ฐ€ํ•œ ๊ฒฝ์šฐ: ํŠน์ • ๋ฆฌ์†Œ์Šค ์œ ํ˜•, ๊ตฌ๋… ๊ฐ„ ์ด๋™ ์‹œ ์„œ๋น„์Šค๋ณ„ ์ œํ•œ, Key Vault(๊ตฌ๋… ์ด๋™ ์‹œ ์ผ๋ถ€ ์ œํ•œ) ๋“ฑ.",
1186
+ "์‹œํ—˜ ํŒจํ„ด: ์ด๋™ ํ›„ ๋ฆฌ์†Œ์Šค ID ๋ณ€๊ฒฝ ์˜ํ–ฅ, ์ด๋™ ๋ถˆ๊ฐ€ ๋ฆฌ์†Œ์Šค ์˜ˆ์‹œ, ๊ตฌ๋… ๊ฐ„ ์ด๋™ ์‹œ ์ถ”๊ฐ€ ์กฐ๊ฑด(Entra ID ํ…Œ๋„ŒํŠธ ์ผ์น˜ ๋“ฑ)์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1187
+ ],
1188
+ "level": "๊ณ ๊ธ‰"
1189
+ },
1190
+ {
1191
+ "id": "azure-template-deployment",
1192
+ "certification": "AZ-104",
1193
+ "title": "ARM/Bicep โ€” ์ธํ”„๋ผ ์ฝ”๋“œ ๋ฐฐํฌ",
1194
+ "summary": "ARM Template(JSON) ๋˜๋Š” Bicep(DSL)์œผ๋กœ Azure ๋ฆฌ์†Œ์Šค๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. IaC(Infrastructure as Code)์˜ ํ‘œ์ค€ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.",
1195
+ "example": "# Bicep ๋ฐฐํฌ\naz deployment group create \\\n --resource-group myRG \\\n --template-file main.bicep \\\n --parameters environment=prod\n# ๊ตฌ๋… ๋ ˆ๋ฒจ ๋ฐฐํฌ\naz deployment sub create --template-file rg.bicep --location koreacentral",
1196
+ "common_mistake": "ARM ๋ฐฐํฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Incremental ๋ชจ๋“œ(๊ธฐ์กด ๋ฆฌ์†Œ์Šค ์œ ์ง€, ์ƒˆ ๋ฆฌ์†Œ์Šค ์ถ”๊ฐ€)์ž…๋‹ˆ๋‹ค. Complete ๋ชจ๋“œ๋Š” ํ…œํ”Œ๋ฆฟ์— ์—†๋Š” RG ๋‚ด ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ญ์ œํ•˜๋ฏ€๋กœ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
1197
+ "keywords": [
1198
+ "ARM template",
1199
+ "Bicep",
1200
+ "IaC",
1201
+ "incremental",
1202
+ "complete",
1203
+ "deployment scope"
1204
+ ],
1205
+ "source_id": "azure-vm-overview",
1206
+ "details": [
1207
+ "๋ฐฐํฌ ๋ฒ”์œ„: Resource Group(๋Œ€๋ถ€๋ถ„), Subscription(RG ์ƒ์„ฑ ํฌํ•จ), Management Group(Policy/RBAC), Tenant ๋ ˆ๋ฒจ ์ค‘ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
1208
+ "Bicep์€ ARM JSON๋ณด๋‹ค ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ƒ…๋‹ˆ๋‹ค. `bicep build`๋กœ ARM JSON์œผ๋กœ ์ปดํŒŒ์ผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
1209
+ "์‹œํ—˜ ํŒจํ„ด: Incremental vs Complete ๋ชจ๋“œ ์ฐจ์ด, ๋ฐฐํฌ ๋ฒ”์œ„๋ณ„ ๋ช…๋ น(`deployment group/sub/mg create`), What-If ๊ฒ€์ฆ ์‚ฌ์šฉ๋ฒ•์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1210
+ ],
1211
+ "level": "๊ณ ๊ธ‰"
1212
+ }
1213
+ ]
cert_study_app/content/azure/practices.json ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "azure-lab-1",
4
+ "title": "๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน ์ƒ์„ฑ",
5
+ "task_description": "Korea Central ์ง€์—ญ์— `myRG`๋ผ๋Š” ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์„ ๋งŒ๋“œ์„ธ์š”.",
6
+ "expected_command": "az group create --name myRG --location koreacentral",
7
+ "grading_conditions": ["az group create", "--name myRG", "--location koreacentral"],
8
+ "hint": "`az group create --name <์ด๋ฆ„> --location <์ง€์—ญ>` ํ˜•์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
9
+ "explanation": "๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์€ Azure ๋ฆฌ์†Œ์Šค๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ฌถ๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค. ์ง€์—ญ์€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ ์œ„์น˜์ด๋ฉฐ ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค์™€ ๋‹ฌ๋ผ๋„ ๋ฉ๋‹ˆ๋‹ค.",
10
+ "difficulty": "easy"
11
+ },
12
+ {
13
+ "id": "azure-lab-2",
14
+ "title": "์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ • ์ƒ์„ฑ (Standard LRS)",
15
+ "task_description": "`myRG` ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์— Standard_LRS ์ค‘๋ณต์„ฑ์„ ๊ฐ€์ง„ `mystorageacct` ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ •์„ ์ƒ์„ฑํ•˜์„ธ์š”.",
16
+ "expected_command": "az storage account create --name mystorageacct --resource-group myRG --sku Standard_LRS --kind StorageV2",
17
+ "grading_conditions": ["az storage account create", "--name mystorageacct", "--resource-group myRG", "--sku Standard_LRS"],
18
+ "hint": "`az storage account create`์— `--sku`์™€ `--kind` ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.",
19
+ "explanation": "StorageV2(๋ฒ”์šฉ v2)๋Š” ํ˜„์žฌ ๊ถŒ์žฅ ์Šคํ† ๋ฆฌ์ง€ ์ข…๋ฅ˜์ž…๋‹ˆ๋‹ค. LRS๋Š” ๋‹จ์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋‚ด 3๋ณต์ œ๋กœ ๊ฐ€์žฅ ์ €๋ ดํ•œ ์ค‘๋ณต์„ฑ์ž…๋‹ˆ๋‹ค.",
20
+ "difficulty": "easy"
21
+ },
22
+ {
23
+ "id": "azure-lab-3",
24
+ "title": "๊ฐ€์ƒ ๋„คํŠธ์›Œํฌ์™€ ์„œ๋ธŒ๋„ท ์ƒ์„ฑ",
25
+ "task_description": "`myRG`์— ์ฃผ์†Œ ๊ณต๊ฐ„ `10.0.0.0/16`, ์„œ๋ธŒ๋„ท `default`(`10.0.0.0/24`)๋ฅผ ๊ฐ€์ง„ VNet `myVNet`์„ ์ƒ์„ฑํ•˜์„ธ์š”.",
26
+ "expected_command": "az network vnet create --name myVNet --resource-group myRG --address-prefix 10.0.0.0/16 --subnet-name default --subnet-prefix 10.0.0.0/24",
27
+ "grading_conditions": ["az network vnet create", "--name myVNet", "--address-prefix 10.0.0.0/16", "--subnet-name default", "--subnet-prefix 10.0.0.0/24"],
28
+ "hint": "`az network vnet create`์— `--address-prefix`์™€ `--subnet-name`, `--subnet-prefix`๋ฅผ ํ•จ๊ป˜ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
29
+ "explanation": "VNet ์ฃผ์†Œ ๊ณต๊ฐ„(/16)๊ณผ ์„œ๋ธŒ๋„ท ๋ฒ”์œ„(/24)๋Š” ํฌํ•จ ๊ด€๊ณ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. Azure๋Š” ๊ฐ ์„œ๋ธŒ๋„ท์—์„œ 5๊ฐœ IP๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.",
30
+ "difficulty": "easy"
31
+ },
32
+ {
33
+ "id": "azure-lab-4",
34
+ "title": "NSG ๊ทœ์น™์œผ๋กœ SSH ํ—ˆ์šฉ",
35
+ "task_description": "NSG `myNSG`์— ์šฐ์„ ์ˆœ์œ„ 100์œผ๋กœ TCP ํฌํŠธ 22 ์ธ๋ฐ”์šด๋“œ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๊ทœ์น™ `allow-ssh`๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.",
36
+ "expected_command": "az network nsg rule create --nsg-name myNSG --resource-group myRG --name allow-ssh --priority 100 --access Allow --protocol Tcp --destination-port-ranges 22",
37
+ "grading_conditions": ["az network nsg rule create", "--nsg-name myNSG", "--name allow-ssh", "--priority 100", "--access Allow", "--destination-port-ranges 22"],
38
+ "hint": "`az network nsg rule create`์— `--priority`, `--access`, `--protocol`, `--destination-port-ranges`๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
39
+ "explanation": "NSG ๊ทœ์น™์€ ์šฐ์„ ์ˆœ์œ„ ์ˆซ์ž๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋จผ์ € ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. 100์€ ๊ธฐ๋ณธ ๊ทœ์น™(65000~65500)๋ณด๋‹ค ํ›จ์”ฌ ๋†’์€ ์šฐ์„ ์ˆœ์œ„์ž…๋‹ˆ๋‹ค.",
40
+ "difficulty": "easy"
41
+ },
42
+ {
43
+ "id": "azure-lab-5",
44
+ "title": "RBAC ์—ญํ•  ํ• ๋‹น (Contributor)",
45
+ "task_description": "`alice@example.com`์—๊ฒŒ `myRG` ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน ๋ฒ”์œ„์˜ `Contributor` ์—ญํ• ์„ ํ• ๋‹นํ•˜์„ธ์š”.",
46
+ "expected_command": "az role assignment create --assignee alice@example.com --role Contributor --scope /subscriptions/SUB_ID/resourceGroups/myRG",
47
+ "grading_conditions": ["az role assignment create", "--assignee alice@example.com", "--role Contributor", "--scope"],
48
+ "hint": "`az role assignment create`์— `--assignee`, `--role`, `--scope`๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. scope๋Š” ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์˜ ์ „์ฒด ARM ID์ž…๋‹ˆ๋‹ค.",
49
+ "explanation": "Contributor๋Š” ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์—ญํ•  ํ• ๋‹น์€ ๋ถˆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. Owner๋งŒ ์—ญํ•  ํ• ๋‹น ๊ถŒํ•œ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.",
50
+ "difficulty": "medium"
51
+ },
52
+ {
53
+ "id": "azure-lab-6",
54
+ "title": "๋ฆฌ์†Œ์Šค ์ž ๊ธˆ ์„ค์ • (CanNotDelete)",
55
+ "task_description": "`myRG` ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์— `CanNotDelete` ์ž ๊ธˆ `prod-lock`์„ ์„ค์ •ํ•˜์„ธ์š”.",
56
+ "expected_command": "az lock create --name prod-lock --resource-group myRG --lock-type CanNotDelete",
57
+ "grading_conditions": ["az lock create", "--name prod-lock", "--resource-group myRG", "--lock-type CanNotDelete"],
58
+ "hint": "`az lock create`์— `--lock-type`์œผ๋กœ `CanNotDelete` ๋˜๋Š” `ReadOnly`๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
59
+ "explanation": "CanNotDelete ์ž ๊ธˆ์€ ์ฝ๊ธฐ์™€ ์ˆ˜์ •์€ ํ—ˆ์šฉํ•˜์ง€๋งŒ ์‚ญ์ œ๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. Owner๋ผ๋„ ์ž ๊ธˆ์„ ๋จผ์ € ํ•ด์ œํ•ด์•ผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
60
+ "difficulty": "easy"
61
+ },
62
+ {
63
+ "id": "azure-lab-7",
64
+ "title": "Blob ์ ‘๊ทผ ๊ณ„์ธต ๋ณ€๊ฒฝ (Cool)",
65
+ "task_description": "`mycontainer`์˜ `report.log` Blob์„ Cool ๊ณ„์ธต์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.",
66
+ "expected_command": "az storage blob set-tier --account-name mystorageacct --container-name mycontainer --name report.log --tier Cool",
67
+ "grading_conditions": ["az storage blob set-tier", "--account-name mystorageacct", "--container-name mycontainer", "--name report.log", "--tier Cool"],
68
+ "hint": "`az storage blob set-tier`์— `--tier`๋กœ Hot/Cool/Cold/Archive๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
69
+ "explanation": "Cool ๊ณ„์ธต์€ 30์ผ ์ด์ƒ ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ €์žฅ ๋น„์šฉ์€ ๋‚ฎ์ง€๋งŒ ์ ‘๊ทผ ๋น„์šฉ์ด Hot๋ณด๋‹ค ๋†’์Šต๋‹ˆ๋‹ค.",
70
+ "difficulty": "easy"
71
+ },
72
+ {
73
+ "id": "azure-lab-8",
74
+ "title": "VM ๋ฐฑ์—… ํ™œ์„ฑํ™”",
75
+ "task_description": "`myVault` Recovery Services Vault์—์„œ `myVM` ๊ฐ€์ƒ ๋จธ์‹ ์˜ ๋ฐฑ์—…์„ `DefaultPolicy`๋กœ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”.",
76
+ "expected_command": "az backup protection enable-for-vm --vault-name myVault --resource-group myRG --vm myVM --policy-name DefaultPolicy",
77
+ "grading_conditions": ["az backup protection enable-for-vm", "--vault-name myVault", "--vm myVM", "--policy-name DefaultPolicy"],
78
+ "hint": "`az backup protection enable-for-vm`์— vault, vm, policy ์ด๋ฆ„์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
79
+ "explanation": "VM ๋ฐฑ์—… ํ™œ์„ฑํ™” ํ›„ ์ตœ์ดˆ ๋ฐฑ์—…์€ ์ฆ‰์‹œ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋‹ค์Œ ์˜ˆ์•ฝ ์‹œ๊ฐ„๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. Vault์™€ VM์€ ๊ฐ™์€ region์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
80
+ "difficulty": "medium"
81
+ },
82
+ {
83
+ "id": "azure-lab-9",
84
+ "title": "Key Vault ๋น„๋ฐ€ ์ €์žฅ",
85
+ "task_description": "`myKV` Key Vault์— `db-password`๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ `MyP@ss123` ๋น„๋ฐ€์„ ์ €์žฅํ•˜์„ธ์š”.",
86
+ "expected_command": "az keyvault secret set --vault-name myKV --name db-password --value MyP@ss123",
87
+ "grading_conditions": ["az keyvault secret set", "--vault-name myKV", "--name db-password", "--value"],
88
+ "hint": "`az keyvault secret set`์— vault ์ด๋ฆ„, ๋น„๋ฐ€ ์ด๋ฆ„, ๊ฐ’์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
89
+ "explanation": "๋น„๋ฐ€์€ ๋ฒ„์ „ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๋‹ค์‹œ ์ €์žฅํ•˜๋ฉด ์ƒˆ ๋ฒ„์ „์ด ์ƒ์„ฑ๋˜๊ณ  ์ด์ „ ๋ฒ„์ „์€ ๋น„ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.",
90
+ "difficulty": "easy"
91
+ },
92
+ {
93
+ "id": "azure-lab-10",
94
+ "title": "VNet ํ”ผ์–ด๋ง ์ƒ์„ฑ",
95
+ "task_description": "`VNet1`์—์„œ `VNet2`๋กœ ํ–ฅํ•˜๋Š” ํ”ผ์–ด๋ง `VNet1-to-VNet2`๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”. (resource group: myRG)",
96
+ "expected_command": "az network vnet peering create --name VNet1-to-VNet2 --vnet-name VNet1 --remote-vnet VNet2 --resource-group myRG",
97
+ "grading_conditions": ["az network vnet peering create", "--name VNet1-to-VNet2", "--vnet-name VNet1", "--remote-vnet VNet2"],
98
+ "hint": "ํ”ผ์–ด๋ง์€ ์–‘๋ฐฉํ–ฅ์„ ๊ฐ๊ฐ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ช…๋ น์€ VNet1โ†’VNet2 ๋ฐฉํ–ฅ์ž…๋‹ˆ๋‹ค.",
99
+ "explanation": "VNet ํ”ผ์–ด๋ง์€ ๋น„์ „์ด์ (non-transitive)์ž…๋‹ˆ๋‹ค. Aโ†”B, Bโ†”C๊ฐ€ ์žˆ์–ด๋„ A์—์„œ C๋กœ๋Š” ์ง์ ‘ ํ†ต์‹  ๋ถˆ๊ฐ€์ด๋ฉฐ Aโ†”C๋ฅผ ๋ณ„๋„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
100
+ "difficulty": "medium"
101
+ },
102
+ {
103
+ "id": "azure-lab-11",
104
+ "title": "Private Endpoint ์ƒ์„ฑ (Blob)",
105
+ "task_description": "`myVNet`์˜ `default` ์„œ๋ธŒ๋„ท์— ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ • `mystorageacct`์˜ blob ์„œ๋น„์Šค์šฉ Private Endpoint `myPE`๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.",
106
+ "expected_command": "az network private-endpoint create --name myPE --resource-group myRG --vnet-name myVNet --subnet default --private-connection-resource-id /subscriptions/SUB_ID/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/mystorageacct --group-ids blob --connection-name myPEConnection",
107
+ "grading_conditions": ["az network private-endpoint create", "--name myPE", "--vnet-name myVNet", "--group-ids blob"],
108
+ "hint": "`--private-connection-resource-id`์— ์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ •์˜ ์ „์ฒด ARM ID๋ฅผ ๋„ฃ๊ณ , `--group-ids blob`์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
109
+ "explanation": "Private Endpoint๋Š” PaaS ์„œ๋น„์Šค์— VNet ๋‚ด ์‚ฌ์„ค IP๋ฅผ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ ํ›„ Private DNS Zone ์—ฐ๊ฒฐ์ด ์žˆ์–ด์•ผ DNS ํ•ด์„์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.",
110
+ "difficulty": "hard"
111
+ },
112
+ {
113
+ "id": "azure-lab-12",
114
+ "title": "Azure Bastion ์ƒ์„ฑ",
115
+ "task_description": "`myVNet`์— Public IP `myBastionIP`๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Azure Bastion `myBastion`์„ Korea Central์— ์ƒ์„ฑํ•˜์„ธ์š”.",
116
+ "expected_command": "az network bastion create --name myBastion --public-ip-address myBastionIP --resource-group myRG --vnet-name myVNet --location koreacentral",
117
+ "grading_conditions": ["az network bastion create", "--name myBastion", "--public-ip-address myBastionIP", "--vnet-name myVNet"],
118
+ "hint": "Bastion ์ƒ์„ฑ ์ „ VNet์— `AzureBastionSubnet`(/27 ์ด์ƒ)์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
119
+ "explanation": "Azure Bastion์€ VM์— Public IP ์—†์ด ํฌํ„ธ์—์„œ RDP/SSH ์ ‘๊ทผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. AzureBastionSubnet ์ด๋ฆ„์€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
120
+ "difficulty": "medium"
121
+ },
122
+ {
123
+ "id": "azure-lab-13",
124
+ "title": "App Service ์›น์•ฑ ์ƒ์„ฑ",
125
+ "task_description": "`myPlan` App Service ํ”Œ๋žœ์„ ์‚ฌ์šฉํ•˜๋Š” `myWebApp` ์›น์•ฑ์„ Node.js 18 LTS ๋Ÿฐํƒ€์ž„์œผ๋กœ ์ƒ์„ฑํ•˜์„ธ์š”.",
126
+ "expected_command": "az webapp create --name myWebApp --resource-group myRG --plan myPlan --runtime NODE:18-lts",
127
+ "grading_conditions": ["az webapp create", "--name myWebApp", "--resource-group myRG", "--plan myPlan", "--runtime"],
128
+ "hint": "`az webapp create`์— `--plan`๊ณผ `--runtime`์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„ ๋ชฉ๋ก์€ `az webapp list-runtimes`๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
129
+ "explanation": "App Service๋Š” ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ๊ณผ ๋ฐฐํฌ ๋ฐฉ๋ฒ•์„ ์œ ์—ฐํ•˜๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” PaaS์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ํ”Œ๋žœ์˜ ์•ฑ๋“ค์€ ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.",
130
+ "difficulty": "easy"
131
+ },
132
+ {
133
+ "id": "azure-lab-14",
134
+ "title": "๋ฐฐํฌ ์Šฌ๋กฏ ์ƒ์„ฑ (staging)",
135
+ "task_description": "`myWebApp` ์›น์•ฑ์— `staging` ๋ฐฐํฌ ์Šฌ๋กฏ์„ ์ƒ์„ฑํ•˜์„ธ์š”.",
136
+ "expected_command": "az webapp deployment slot create --name myWebApp --resource-group myRG --slot staging",
137
+ "grading_conditions": ["az webapp deployment slot create", "--name myWebApp", "--slot staging"],
138
+ "hint": "`az webapp deployment slot create`์— `--slot`์œผ๋กœ ์Šฌ๋กฏ ์ด๋ฆ„์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. Standard ์ด์ƒ ํ”Œ๋žœ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
139
+ "explanation": "๋ฐฐํฌ ์Šฌ๋กฏ์€ ๋‹ค์šดํƒ€์ž„ ์—†์ด ์Šคํ…Œ์ด์ง• ๋ฒ„์ „์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  swap์œผ๋กœ ์ฆ‰์‹œ ๊ต์ฒดํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.",
140
+ "difficulty": "easy"
141
+ },
142
+ {
143
+ "id": "azure-lab-15",
144
+ "title": "๊ฒฝ๋ณด ๊ทœ์น™ ์ƒ์„ฑ (CPU 80%)",
145
+ "task_description": "`myVM`์˜ ํ‰๊ท  CPU ์‚ฌ์šฉ๋ฅ ์ด 80%๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด `alice@example.com`์œผ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๊ฒฝ๋ณด ๊ทœ์น™์„ ์ƒ์„ฑํ•˜์„ธ์š”.",
146
+ "expected_command": "az monitor alert create --name highCPU --resource-group myRG --resource myVM --resource-type Microsoft.Compute/virtualMachines --condition avg Percentage CPU > 80 --action email alice@example.com",
147
+ "grading_conditions": ["az monitor alert create", "--name highCPU", "--condition", "Percentage CPU", "> 80", "--action email alice@example.com"],
148
+ "hint": "`az monitor alert create`์— `--condition`์œผ๋กœ ๋ฉ”ํŠธ๋ฆญ ์ด๋ฆ„๊ณผ ์ž„๊ณ„๊ฐ’์„, `--action`์œผ๋กœ ์•Œ๋ฆผ ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
149
+ "explanation": "๊ฒฝ๋ณด๋Š” Action Group ์—†์ด๋Š” ๋ฐœ์ƒํ•ด๋„ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. --action email๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ง€์ •ํ•˜๊ฑฐ๋‚˜, ๋ณ„๋„ Action Group์„ ์ƒ์„ฑํ•ด ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
150
+ "difficulty": "medium"
151
+ }
152
+ ]
cert_study_app/content/azure/quizzes.json ADDED
@@ -0,0 +1,1058 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "azure-q28",
4
+ "lesson_id": "azure-subscription",
5
+ "question_type": "multiple_choice",
6
+ "question": "Azure์—์„œ ๊ณผ๊ธˆ๊ณผ ๋ฆฌ์†Œ์Šค ๋ฐฐํฌ์˜ ์ฃผ์š” ๊ฒฝ๊ณ„ ๋‹จ์œ„๋Š”?",
7
+ "options": [
8
+ "Subscription",
9
+ "Resource Group",
10
+ "Management Group",
11
+ "Tenant"
12
+ ],
13
+ "answer": "Subscription",
14
+ "explanation": "Subscription์€ Azure ๊ณผ๊ธˆ, ๋ฆฌ์†Œ์Šค ํ• ๋‹น๋Ÿ‰, RBAC/Policy ์ ์šฉ์˜ ์ฃผ์š” ๊ฒฝ๊ณ„์ž…๋‹ˆ๋‹ค. Resource Group์€ Subscription ์•„๋ž˜, Management Group์€ ์—ฌ๋Ÿฌ Subscription์„ ๋ฌถ๋Š” ์ƒ์œ„ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.",
15
+ "difficulty": "easy",
16
+ "source_id": "manual-az104-basic"
17
+ },
18
+ {
19
+ "id": "azure-q29",
20
+ "lesson_id": "azure-tags",
21
+ "question_type": "multiple_choice",
22
+ "question": "Azure Tags์˜ ์ฃผ๋œ ์šฉ๋„๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
23
+ "options": [
24
+ "๋ฆฌ์†Œ์Šค์— key/value ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ ๋น„์šฉ๊ณผ ์†Œ์œ ์ž๋ฅผ ๋ถ„๋ฅ˜ํ•œ๋‹ค",
25
+ "๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ง์ ‘ ์ œ์–ดํ•œ๋‹ค",
26
+ "VM ์ธ์Šคํ„ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ์žฌ์‹œ์ž‘ํ•œ๋‹ค",
27
+ "NSG ์ธ๋ฐ”์šด๋“œ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค"
28
+ ],
29
+ "answer": "๋ฆฌ์†Œ์Šค์— key/value ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ ๋น„์šฉ๊ณผ ์†Œ์œ ์ž๋ฅผ ๋ถ„๋ฅ˜ํ•œ๋‹ค",
30
+ "explanation": "Tags๋Š” ๋น„์šฉ ๋ถ„์„, ์†Œ์œ ์ž ์‹๋ณ„, ํ™˜๊ฒฝ ๊ตฌ๋ถ„์„ ์œ„ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ถŒํ•œ ์ œ์–ด๋Š” RBAC๊ฐ€ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.",
31
+ "difficulty": "easy",
32
+ "source_id": "manual-az104-basic"
33
+ },
34
+ {
35
+ "id": "azure-q30",
36
+ "lesson_id": "azure-custom-role",
37
+ "question_type": "multiple_choice",
38
+ "question": "Azure Custom Role์˜ `AssignableScopes`๋ฅผ ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋Š”?",
39
+ "options": [
40
+ "ํ•„์š”ํ•œ ๋ฒ”์œ„์— ์—ญํ• ์„ ํ• ๋‹นํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค",
41
+ "์—ญํ• ์— ์ •์˜๋œ actions์ด ๋ชจ๋‘ ๋ฌด์‹œ๋œ๋‹ค",
42
+ "VM์ด ์ž๋™์œผ๋กœ ์ค‘์ง€๋œ๋‹ค",
43
+ "Blob ์ ‘๊ทผ์ด ์ฐจ๋‹จ๋œ๋‹ค"
44
+ ],
45
+ "answer": "ํ•„์š”ํ•œ ๋ฒ”์œ„์— ์—ญํ• ์„ ํ• ๋‹นํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค",
46
+ "explanation": "`AssignableScopes`๋Š” ์ด ์—ญํ• ์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ์ข๊ฒŒ ์„ค์ •ํ•˜๋ฉด ํ•„์š”ํ•œ subscription์ด๋‚˜ resource group์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
47
+ "difficulty": "medium",
48
+ "source_id": "azure-rbac-overview"
49
+ },
50
+ {
51
+ "id": "azure-q31",
52
+ "lesson_id": "azure-lifecycle",
53
+ "question_type": "multiple_choice",
54
+ "question": "Azure Storage Lifecycle ๊ด€๋ฆฌ์˜ ์ฃผ๋œ ๋ชฉ์ ์€?",
55
+ "options": [
56
+ "Blob ์ ‘๊ทผ ๋นˆ๋„์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ tier๋ฅผ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•ด ๋น„์šฉ์„ ์ตœ์ ํ™”ํ•œ๋‹ค",
57
+ "VM์„ ์ž๋™์œผ๋กœ ๋ฐฑ์—…ํ•œ๋‹ค",
58
+ "NSG ๊ทœ์น™์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ฐฑ์‹ ํ•œ๋‹ค",
59
+ "DNS ๋ ˆ์ฝ”๋“œ๋ฅผ ์ž๋™ ์—…๋ฐ์ดํŠธํ•œ๋‹ค"
60
+ ],
61
+ "answer": "Blob ์ ‘๊ทผ ๋นˆ๋„์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ tier๋ฅผ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•ด ๋น„์šฉ์„ ์ตœ์ ํ™”ํ•œ๋‹ค",
62
+ "explanation": "Lifecycle ์ •์ฑ…์€ ๋งˆ์ง€๋ง‰ ์ˆ˜์ •ยท์ ‘๊ทผ ์ดํ›„ ๊ฒฝ๊ณผ ์ผ์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ Blob์„ ์ž๋™์œผ๋กœ Cool/Archive๋กœ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.",
63
+ "difficulty": "easy",
64
+ "source_id": "azure-blob-access-tiers"
65
+ },
66
+ {
67
+ "id": "azure-q32",
68
+ "lesson_id": "azure-files",
69
+ "question_type": "multiple_choice",
70
+ "question": "Azure Files์™€ Blob Storage์˜ ์ฃผ์š” ์ฐจ์ด๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
71
+ "options": [
72
+ "Azure Files๋Š” SMB/NFS ํŒŒ์ผ ๊ณต์œ , Blob์€ REST API ๊ธฐ๋ฐ˜ ๊ฐœ์ฒด ์ €์žฅ์ด๋‹ค",
73
+ "๋‘ ์„œ๋น„์Šค๋Š” ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋งˆ์šดํŠธ๋œ๋‹ค",
74
+ "Blob์ด ํŒŒ์ผ ๊ณต์œ ์— ์ ํ•ฉํ•˜๊ณ  Azure Files๊ฐ€ ๊ฐœ์ฒด ์ €์žฅ์— ์ ํ•ฉํ•˜๋‹ค",
75
+ "Azure Files๋Š” ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„œ๋น„์Šค๋‹ค"
76
+ ],
77
+ "answer": "Azure Files๋Š” SMB/NFS ํŒŒ์ผ ๊ณต์œ , Blob์€ REST API ๊ธฐ๋ฐ˜ ๊ฐœ์ฒด ์ €์žฅ์ด๋‹ค",
78
+ "explanation": "Azure Files๋Š” ๋„คํŠธ์›Œํฌ ๋“œ๋ผ์ด๋ธŒ์ฒ˜๋Ÿผ ๋งˆ์šดํŠธํ•ด ์‚ฌ์šฉํ•˜๊ณ , Blob Storage๋Š” REST API๋กœ ๋น„์ •ํ˜• ๋ฐ์ดํ„ฐ(์ด๋ฏธ์ง€, ๋กœ๊ทธ, ๋ฐฑ์—…)๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
79
+ "difficulty": "easy",
80
+ "source_id": "azure-storage-account"
81
+ },
82
+ {
83
+ "id": "azure-q33",
84
+ "lesson_id": "azure-managed-disk",
85
+ "question_type": "multiple_choice",
86
+ "question": "Azure VM์—์„œ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š” ๋””์Šคํฌ ์œ ํ˜•์€?",
87
+ "options": [
88
+ "Temporary disk",
89
+ "OS disk",
90
+ "Premium SSD data disk",
91
+ "Ultra disk"
92
+ ],
93
+ "answer": "Temporary disk",
94
+ "explanation": "์ž„์‹œ ๋””์Šคํฌ(temporary disk)๋Š” VM ์žฌ์‹œ์ž‘ ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. OS disk์™€ data disk๋Š” ์˜๊ตฌ ์ €์žฅ์†Œ๋กœ ์žฌ์‹œ์ž‘์—๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณด์กด๋ฉ๋‹ˆ๋‹ค.",
95
+ "difficulty": "easy",
96
+ "source_id": "azure-vm-overview"
97
+ },
98
+ {
99
+ "id": "azure-q34",
100
+ "lesson_id": "azure-autoscale",
101
+ "question_type": "multiple_choice",
102
+ "question": "Autoscale์ด ์—ฐ๊ฒฐ๋˜์–ด ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์†Œ์Šค๋Š”?",
103
+ "options": [
104
+ "Virtual Machine Scale Sets (VMSS)",
105
+ "Recovery Services Vault",
106
+ "Private DNS Zone",
107
+ "Activity Log"
108
+ ],
109
+ "answer": "Virtual Machine Scale Sets (VMSS)",
110
+ "explanation": "Autoscale์€ VMSS๋‚˜ App Service Plan ๊ฐ™์€ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๋ฆฌ์†Œ์Šค์™€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. CPU/๋ฉ”๋ชจ๋ฆฌ ๋ฉ”ํŠธ๋ฆญ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์ค„์ž…๋‹ˆ๋‹ค.",
111
+ "difficulty": "easy",
112
+ "source_id": "azure-vmss-overview"
113
+ },
114
+ {
115
+ "id": "azure-q35",
116
+ "lesson_id": "azure-route-table",
117
+ "question_type": "multiple_choice",
118
+ "question": "UDR(User Defined Route, ์‚ฌ์šฉ์ž ์ •์˜ ๊ฒฝ๋กœ)์˜ ์ฃผ๋œ ์—ญํ• ์€?",
119
+ "options": [
120
+ "Subnet ํŠธ๋ž˜ํ”ฝ์˜ next hop์„ ๊ธฐ๋ณธ Azure ๋ผ์šฐํŒ… ๋Œ€์‹  NVA๋‚˜ ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ์ œ์–ดํ•œ๋‹ค",
121
+ "VM์˜ ํฌ๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•œ๋‹ค",
122
+ "Blob ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์—ญ ๊ฐ„ ๋ณต์ œํ•œ๋‹ค",
123
+ "DNS ์ด๋ฆ„์„ IP๋กœ ํ•ด์„ํ•œ๋‹ค"
124
+ ],
125
+ "answer": "Subnet ํŠธ๋ž˜ํ”ฝ์˜ next hop์„ ๊ธฐ๋ณธ Azure ๋ผ์šฐํŒ… ๋Œ€์‹  NVA๋‚˜ ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ์ œ์–ดํ•œ๋‹ค",
126
+ "explanation": "UDR์€ ๊ธฐ๋ณธ Azure ๋ผ์šฐํŒ…์„ ์žฌ์ •์˜ํ•ด ๋ฐฉํ™”๋ฒฝ(NVA)์ด๋‚˜ ํŠน์ • ์žฅ์น˜๋ฅผ ํ†ตํ•ด ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. NSG์™€ ์—ญํ• ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.",
127
+ "difficulty": "medium",
128
+ "source_id": "azure-vnet-overview"
129
+ },
130
+ {
131
+ "id": "azure-q36",
132
+ "lesson_id": "azure-private-endpoint",
133
+ "question_type": "multiple_choice",
134
+ "question": "Private Endpoint ์—ฐ๊ฒฐ ๋ฌธ์ œ์—์„œ DNS๋ฅผ ํ•จ๊ป˜ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?",
135
+ "options": [
136
+ "Private Endpoint๊ฐ€ private IP๋ฅผ ์“ฐ๋ฏ€๋กœ DNS๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ IP๋กœ ํ•ด์„ํ•ด์•ผ ์—ฐ๊ฒฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค",
137
+ "DNS๊ฐ€ Blob ์ ‘๊ทผ tier๋ฅผ ๊ฒฐ์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค",
138
+ "DNS๊ฐ€ NSG ๊ทœ์น™์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค",
139
+ "DNS๊ฐ€ VM ํฌ๊ธฐ๋ฅผ ์ œ์–ดํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค"
140
+ ],
141
+ "answer": "Private Endpoint๊ฐ€ private IP๋ฅผ ์“ฐ๋ฏ€๋กœ DNS๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ IP๋กœ ํ•ด์„ํ•ด์•ผ ์—ฐ๊ฒฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค",
142
+ "explanation": "Private Endpoint๋Š” VNet ๋‚ด๋ถ€ private IP๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. DNS๊ฐ€ ํ•ด๋‹น ์„œ๋น„์Šค๋ฅผ public IP ๋Œ€์‹  ์˜ฌ๋ฐ”๋ฅธ private IP๋กœ ํ•ด์„ํ•˜์ง€ ๋ชปํ•˜๋ฉด ์—ฐ๊ฒฐ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.",
143
+ "difficulty": "medium",
144
+ "source_id": "azure-private-endpoint"
145
+ },
146
+ {
147
+ "id": "azure-q37",
148
+ "lesson_id": "azure-monitor-metrics",
149
+ "question_type": "multiple_choice",
150
+ "question": "Azure Monitor Metrics์™€ Logs์˜ ์ฐจ์ด๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
151
+ "options": [
152
+ "Metrics๋Š” ์ˆ˜์น˜ํ˜• ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ, Logs๋Š” KQL๋กœ ์กฐํšŒํ•˜๋Š” ๊ตฌ์กฐํ™” ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋‹ค",
153
+ "๋‘ ์„œ๋น„์Šค ๋ชจ๋‘ KQL๋กœ๋งŒ ์กฐํšŒํ•œ๋‹ค",
154
+ "Metrics๋Š” ํ…์ŠคํŠธ ๋กœ๊ทธ, Logs๋Š” ์ˆซ์ž ๊ฐ’๋งŒ ์ €์žฅํ•œ๋‹ค",
155
+ "Metrics๋Š” ๋ฐฑ์—…, Logs๋Š” ๋ณต๊ตฌ์— ์‚ฌ์šฉํ•œ๋‹ค"
156
+ ],
157
+ "answer": "Metrics๋Š” ์ˆ˜์น˜ํ˜• ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ, Logs๋Š” KQL๋กœ ์กฐํšŒํ•˜๋Š” ๊ตฌ์กฐํ™” ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋‹ค",
158
+ "explanation": "Metrics๋Š” CPU%, ๋„คํŠธ์›Œํฌ ๋ฐ”์ดํŠธ์ฒ˜๋Ÿผ ์ˆซ์ž ๊ธฐ๋ฐ˜ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ์ด๊ณ , Logs๋Š” Log Analytics workspace์—์„œ KQL๋กœ ์กฐํšŒํ•˜๋Š” ๊ตฌ์กฐํ™”๋œ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.",
159
+ "difficulty": "medium",
160
+ "source_id": "azure-monitor-overview"
161
+ },
162
+ {
163
+ "id": "azure-q38",
164
+ "lesson_id": "azure-activity-log",
165
+ "question_type": "multiple_choice",
166
+ "question": "Azure Activity Log์—์„œ ํ™•์ธํ•˜๊ธฐ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์ •๋ณด๋Š”?",
167
+ "options": [
168
+ "๋ˆ„๊ฐ€ ์–ด๋–ค ๋ฆฌ์†Œ์Šค๋ฅผ ์–ธ์ œ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ–ˆ๋Š”์ง€ ๊ด€๋ฆฌ ์ด๋ฒคํŠธ",
169
+ "VM ๋‚ด๋ถ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ค๋ฅ˜ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค",
170
+ "Blob ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๋‚ด์—ญ",
171
+ "๋„คํŠธ์›Œํฌ ํŒจํ‚ท์˜ ํŽ˜์ด๋กœ๋“œ"
172
+ ],
173
+ "answer": "๋ˆ„๊ฐ€ ์–ด๋–ค ๋ฆฌ์†Œ์Šค๋ฅผ ์–ธ์ œ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ–ˆ๋Š”์ง€ ๊ด€๋ฆฌ ์ด๋ฒคํŠธ",
174
+ "explanation": "Activity Log๋Š” subscription ์ˆ˜์ค€์˜ ๊ด€๋ฆฌ ์ž‘์—…(๋ˆ„๊ฐ€ ๋ฌด์—‡์„ ์–ธ์ œ ํ–ˆ๋Š”์ง€)์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€ ์˜ค๋ฅ˜ ๋กœ๊ทธ๋Š” Diagnostic Settings๋‚˜ Application Insights์—์„œ ๋ด…๋‹ˆ๋‹ค.",
175
+ "difficulty": "easy",
176
+ "source_id": "azure-monitor-overview"
177
+ },
178
+ {
179
+ "id": "azure-q39",
180
+ "lesson_id": "azure-restore",
181
+ "question_type": "multiple_choice",
182
+ "question": "Recovery Services Vault์—์„œ ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•œ ์‹œ์ ์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์€?",
183
+ "options": [
184
+ "Backup Policy์˜ ๋ฐฑ์—… ๋นˆ๋„์™€ ๋ณด์กด ๊ธฐ๊ฐ„",
185
+ "VM์˜ ํฌ๊ธฐ(size)",
186
+ "NSG ๊ทœ์น™ ์ˆ˜",
187
+ "VNet ์ฃผ์†Œ ๊ณต๊ฐ„"
188
+ ],
189
+ "answer": "Backup Policy์˜ ๋ฐฑ์—… ๋นˆ๋„์™€ ๋ณด์กด ๊ธฐ๊ฐ„",
190
+ "explanation": "๋ณต๊ตฌ ์ง€์ (recovery point)์€ backup policy์—์„œ ์ •์˜ํ•œ ์ผ์ •๊ณผ ๋ณด์กด ๊ธฐ๊ฐ„์— ๋”ฐ๋ผ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋ณด์กด ๊ธฐ๊ฐ„์ด ์ง€๋‚œ ๋ณต๊ตฌ ์ง€์ ์€ ์ž๋™ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.",
191
+ "difficulty": "easy",
192
+ "source_id": "azure-recovery-services-vault"
193
+ },
194
+ {
195
+ "id": "azure-q40",
196
+ "lesson_id": "azure-app-service-plan",
197
+ "question_type": "multiple_choice",
198
+ "question": "App Service Plan์ด Web App๊ณผ์˜ ๊ด€๊ณ„์—์„œ ์ •์˜ํ•˜๋Š” ๊ฒƒ์€?",
199
+ "options": [
200
+ "Web App์ด ์‚ฌ์šฉํ•  ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค์™€ ๊ฐ€๊ฒฉ ํ‹ฐ์–ด",
201
+ "Web App์˜ ๋„๋ฉ”์ธ ์ด๋ฆ„",
202
+ "Web App์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ",
203
+ "Web App์˜ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐฉ์‹"
204
+ ],
205
+ "answer": "Web App์ด ์‚ฌ์šฉํ•  ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค์™€ ๊ฐ€๊ฒฉ ํ‹ฐ์–ด",
206
+ "explanation": "App Service Plan์€ CPU, ๋ฉ”๋ชจ๋ฆฌ, ์ธ์Šคํ„ด์Šค ์ˆ˜, ๊ฐ€๊ฒฉ ํ‹ฐ์–ด๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ Web App์ด ํ•˜๋‚˜์˜ Plan์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์Šค์ผ€์ผ ์„ค์ •์€ Plan ์ˆ˜์ค€์—์„œ ํ•ฉ๋‹ˆ๋‹ค.",
207
+ "difficulty": "easy",
208
+ "source_id": "azure-vm-overview"
209
+ },
210
+ {
211
+ "id": "azure-q41",
212
+ "lesson_id": "azure-container-registry",
213
+ "question_type": "multiple_choice",
214
+ "question": "Azure Container Registry(ACR)์˜ ์ฃผ๋œ ์—ญํ• ์€?",
215
+ "options": [
216
+ "์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋ผ์ด๋น— ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ",
217
+ "์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๋Š” ์„œ๋ฒ„๋ฆฌ์Šค ์„œ๋น„์Šค",
218
+ "VM์„ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋„๊ตฌ",
219
+ "Kubernetes ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค"
220
+ ],
221
+ "answer": "์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋ผ์ด๋น— ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ",
222
+ "explanation": "ACR์€ Docker Hub ๊ฐ™์€ ์ด๋ฏธ์ง€ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ ์‹คํ–‰์€ ACI, AKS, App Service๊ฐ€ ๋‹ด๋‹นํ•˜๊ณ  ACR์€ ์ด๋ฏธ์ง€ ์ €์žฅ/๋ฐฐํฌ์— ํŠนํ™”๋ฉ๋‹ˆ๋‹ค.",
223
+ "difficulty": "easy",
224
+ "source_id": "azure-vm-overview"
225
+ },
226
+ {
227
+ "id": "azure-q42",
228
+ "lesson_id": "azure-cost-management",
229
+ "question_type": "multiple_choice",
230
+ "question": "Azure Tags๊ฐ€ ๋น„์šฉ ๊ด€๋ฆฌ์—์„œ ๋„์›€์„ ์ฃผ๋Š” ๋ฐฉ์‹์€?",
231
+ "options": [
232
+ "๋ฆฌ์†Œ์Šค๋ฅผ ํ™˜๊ฒฝยทํŒ€ยทํ”„๋กœ์ ํŠธ๋ณ„๋กœ ๋ถ„๋ฅ˜ํ•ด Cost Management์—์„œ ํ•„ํ„ฐ๋ง์„ ์‰ฝ๊ฒŒ ํ•ด์ค€๋‹ค",
233
+ "๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•ด ๋น„์šฉ์„ ์ค„์ธ๋‹ค",
234
+ "VM์„ ์ž๋™์œผ๋กœ ์Šค์ผ€์ผ๋‹ค์šดํ•ด ์ ˆ์•ฝํ•œ๋‹ค",
235
+ "Blob tier๋ฅผ ์ž๋™์œผ๋กœ ๋‚ฎ์ถฐ ์ ˆ์•ฝํ•œ๋‹ค"
236
+ ],
237
+ "answer": "๋ฆฌ์†Œ์Šค๋ฅผ ํ™˜๊ฒฝยทํŒ€ยทํ”„๋กœ์ ํŠธ๋ณ„๋กœ ๋ถ„๋ฅ˜ํ•ด Cost Management์—์„œ ํ•„ํ„ฐ๋ง์„ ์‰ฝ๊ฒŒ ํ•ด์ค€๋‹ค",
238
+ "explanation": "Tags ์ž์ฒด๋Š” ๋น„์šฉ์„ ์ค„์ด์ง€ ์•Š์ง€๋งŒ, Cost Management์—์„œ tag๋ณ„๋กœ ํ•„ํ„ฐ๋งํ•˜๋ฉด ๋ถ€์„œ/ํ”„๋กœ์ ํŠธ๋ณ„ ์ง€์ถœ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
239
+ "difficulty": "easy",
240
+ "source_id": "manual-az104-basic"
241
+ },
242
+ {
243
+ "id": "azure-q43",
244
+ "lesson_id": "azure-app-service",
245
+ "question_type": "multiple_choice",
246
+ "question": "Azure App Service์˜ ํŠน์ง•์œผ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
247
+ "options": [
248
+ "VM์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์›น์•ฑ๊ณผ API๋ฅผ ๋ฐฐํฌํ•˜๋Š” PaaS ์„œ๋น„์Šค",
249
+ "OS์™€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋Š” IaaS ์„œ๋น„์Šค",
250
+ "์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ",
251
+ "๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค"
252
+ ],
253
+ "answer": "VM์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์›น์•ฑ๊ณผ API๋ฅผ ๋ฐฐํฌํ•˜๋Š” PaaS ์„œ๋น„์Šค",
254
+ "explanation": "App Service๋Š” ์ธํ”„๋ผ ๊ด€๋ฆฌ ์—†์ด ์ฝ”๋“œ๋ฅผ ๋ฐฐํฌํ•˜๋Š” PaaS์ž…๋‹ˆ๋‹ค. OS ํŒจ์น˜, ์„œ๋ฒ„ ๊ด€๋ฆฌ๋Š” Azure๊ฐ€ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.",
255
+ "difficulty": "easy",
256
+ "source_id": "azure-app-service-overview"
257
+ },
258
+ {
259
+ "id": "azure-q44",
260
+ "lesson_id": "azure-aci",
261
+ "question_type": "multiple_choice",
262
+ "question": "Azure Container Instances(ACI)๊ฐ€ AKS์™€ ๋น„๊ตํ•ด ๊ฐ€์ง„ ์ฃผ์š” ํŠน์ง•์€?",
263
+ "options": [
264
+ "ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ ์—†์ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ฆ‰์‹œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„๋ฆฌ์Šค ๋ฐฉ์‹์ด๋‹ค",
265
+ "๋Œ€๊ทœ๋ชจ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์— ์ตœ์ ํ™”๋๋‹ค",
266
+ "VM Scale Set๊ณผ ๋™์ผํ•œ ์„œ๋น„์Šค๋‹ค",
267
+ "์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋‹ค"
268
+ ],
269
+ "answer": "ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ ์—†์ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ฆ‰์‹œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„๋ฆฌ์Šค ๋ฐฉ์‹์ด๋‹ค",
270
+ "explanation": "ACI๋Š” ํด๋Ÿฌ์Šคํ„ฐ ์šด์˜ ์—†์ด ๊ฐ„๋‹จํ•œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•˜๋Š” ์„œ๋ฒ„๋ฆฌ์Šค ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์€ AKS๊ฐ€ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
271
+ "difficulty": "easy",
272
+ "source_id": "azure-container-instances"
273
+ },
274
+ {
275
+ "id": "azure-q45",
276
+ "lesson_id": "azure-keyvault-secret",
277
+ "question_type": "multiple_choice",
278
+ "question": "Key Vault Secret์˜ ์ฃผ๋œ ์‚ฌ์šฉ ๋ชฉ์ ์€?",
279
+ "options": [
280
+ "DB password, API ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ๊ฐ’์„ ์ฝ”๋“œ๋‚˜ ์„ค์ • ํŒŒ์ผ ๋Œ€์‹  ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•œ๋‹ค",
281
+ "VM ๋””์Šคํฌ๋ฅผ ์•”ํ˜ธํ™”ํ•œ๋‹ค",
282
+ "RBAC ์—ญํ•  ์ •์˜๋ฅผ ์ €์žฅํ•œ๋‹ค",
283
+ "NSG ๋ณด์•ˆ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค"
284
+ ],
285
+ "answer": "DB password, API ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ๊ฐ’์„ ์ฝ”๋“œ๋‚˜ ์„ค์ • ํŒŒ์ผ ๋Œ€์‹  ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•œ๋‹ค",
286
+ "explanation": "Key Vault Secret์€ ๋น„๋ฐ€๊ฐ’์„ ์ฝ”๋“œ๋‚˜ Git์— ๋„ฃ์ง€ ์•Š๊ณ  ์ค‘์•™์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Managed Identity์™€ ๊ฒฐํ•ฉํ•˜๋ฉด ์ฝ”๋“œ์— ๋น„๋ฐ€๊ฐ’์ด ์ „ํ˜€ ์—†์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.",
287
+ "difficulty": "easy",
288
+ "source_id": "azure-key-vault-overview"
289
+ },
290
+ {
291
+ "id": "azure-q46",
292
+ "lesson_id": "azure-network-watcher",
293
+ "question_type": "multiple_choice",
294
+ "question": "Azure Network Watcher์˜ ์ฃผ๋œ ์—ญํ• ์€?",
295
+ "options": [
296
+ "๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ง„๋‹จ, ํ๋ฆ„ ๋กœ๊ทธ, ํŒจํ‚ท ์บก์ฒ˜ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค",
297
+ "VM์„ ์ž๋™์œผ๋กœ ์‹œ์ž‘ํ•˜๊ณ  ์ค‘์ง€ํ•œ๋‹ค",
298
+ "Blob ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ์ž๋™ ๊ด€๋ฆฌํ•œ๋‹ค",
299
+ "NSG ๊ทœ์น™์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค"
300
+ ],
301
+ "answer": "๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ง„๋‹จ, ํ๋ฆ„ ๋กœ๊ทธ, ํŒจํ‚ท ์บก์ฒ˜ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค",
302
+ "explanation": "Network Watcher๋Š” Connection troubleshoot, NSG flow log, IP flow verify, packet capture ๊ฐ™์€ ์ง„๋‹จ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. NSG ๊ทœ์น™๋งŒ์œผ๋กœ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์šด ์‹ค์ œ ํŠธ๋ž˜ํ”ฝ ๊ฒฝ๋กœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
303
+ "difficulty": "easy",
304
+ "source_id": "azure-vnet-overview"
305
+ },
306
+ {
307
+ "id": "azure-q47",
308
+ "lesson_id": "azure-move-resource",
309
+ "question_type": "multiple_choice",
310
+ "question": "Azure ๋ฆฌ์†Œ์Šค ์ด๋™(Move)์—์„œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์‚ฌํ•ญ์œผ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
311
+ "options": [
312
+ "๋ชจ๋“  ๋ฆฌ์†Œ์Šค ์œ ํ˜•์ด ์ด๋™์„ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ข…์† ๋ฆฌ์†Œ์Šค๋„ ํ•จ๊ป˜ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค",
313
+ "์ด๋™ ์ค‘์—๋„ ์„œ๋น„์Šค๊ฐ€ ํ•ญ์ƒ ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค",
314
+ "์ด๋™ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ƒˆ subscription ๊ณผ๊ธˆ์œผ๋กœ ์ „ํ™˜๋œ๋‹ค",
315
+ "Resource Lock์ด ์žˆ์–ด๋„ ํ•ญ์ƒ ์ด๋™ ๊ฐ€๋Šฅํ•˜๋‹ค"
316
+ ],
317
+ "answer": "๋ชจ๋“  ๋ฆฌ์†Œ์Šค ์œ ํ˜•์ด ์ด๋™์„ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ข…์† ๋ฆฌ์†Œ์Šค๋„ ํ•จ๊ป˜ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค",
318
+ "explanation": "์ผ๋ถ€ ๋ฆฌ์†Œ์Šค ์œ ํ˜•์€ ์ด๋™์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. NIC, ๋””์Šคํฌ ๊ฐ™์€ ์ข…์† ๋ฆฌ์†Œ์Šค๋ฅผ ํ•จ๊ป˜ ์ด๋™ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฉฐ, ์ด๋™ ์ค‘์—๋Š” ์ผ์‹œ์ ์œผ๋กœ ์ž ๊ธˆ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.",
319
+ "difficulty": "medium",
320
+ "source_id": "manual-az104-basic"
321
+ },
322
+ {
323
+ "id": "azure-q48",
324
+ "lesson_id": "azure-vmss",
325
+ "question_type": "multiple_choice",
326
+ "question": "VMSS์™€ Availability Set์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€?",
327
+ "options": [
328
+ "VMSS๋Š” ๋ถ€ํ•˜์— ๋”ฐ๋ผ VM ์ˆ˜๋ฅผ ์ž๋™ ์กฐ์ •ํ•œ๋‹ค",
329
+ "Availability Set์€ SLA๊ฐ€ ๋” ๋†’๋‹ค",
330
+ "VMSS๋Š” ๋‹จ์ผ VM๋งŒ ์ง€์›ํ•œ๋‹ค",
331
+ "Availability Set์ด ๋” ์ €๋ ดํ•˜๋‹ค"
332
+ ],
333
+ "answer": "VMSS๋Š” ๋ถ€ํ•˜์— ๋”ฐ๋ผ VM ์ˆ˜๋ฅผ ์ž๋™ ์กฐ์ •ํ•œ๋‹ค",
334
+ "explanation": "VMSS๋Š” Autoscale๋กœ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ๋™์ ์œผ๋กœ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. Availability Set์€ HA๋ฅผ ์œ„ํ•œ ๋ฐฐ์น˜ ๊ตฌ์„ฑ์ด๋ฉฐ ์ž๋™ ํ™•์žฅ ๊ธฐ๋Šฅ์ด ์—†์Šต๋‹ˆ๋‹ค.",
335
+ "source_id": "azure-vmss-overview"
336
+ },
337
+ {
338
+ "id": "azure-q49",
339
+ "lesson_id": "azure-dns",
340
+ "question_type": "multiple_choice",
341
+ "question": "Azure DNS์—์„œ Public Zone ์ƒ์„ฑ ํ›„ ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•˜๋Š” ์ถ”๊ฐ€ ์ž‘์—…์€?",
342
+ "options": [
343
+ "๋„๋ฉ”์ธ ๋“ฑ๋ก ๊ธฐ๊ด€์—์„œ NS ๋ ˆ์ฝ”๋“œ๋ฅผ Azure๋กœ ์œ„์ž„",
344
+ "VNet์— DNS Zone ๋งํฌ ์—ฐ๊ฒฐ",
345
+ "Private Endpoint ์ƒ์„ฑ",
346
+ "SSL ์ธ์ฆ์„œ ๋ฐ”์ธ๋”ฉ"
347
+ ],
348
+ "answer": "๋„๋ฉ”์ธ ๋“ฑ๋ก ๊ธฐ๊ด€์—์„œ NS ๋ ˆ์ฝ”๋“œ๋ฅผ Azure๋กœ ์œ„์ž„",
349
+ "explanation": "Azure DNS๋Š” ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค์ด๋ฏ€๋กœ ๋„๋ฉ”์ธ ๋“ฑ๋ก ๊ธฐ๊ด€์—์„œ Azure๊ฐ€ ์ œ๊ณตํ•œ NS ๋ ˆ์ฝ”๋“œ 4๊ฐœ๋ฅผ ์„ค์ •ํ•ด ์œ„์ž„ํ•ด์•ผ ์‹ค์ œ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
350
+ "source_id": "azure-dns-overview"
351
+ },
352
+ {
353
+ "id": "azure-q50",
354
+ "lesson_id": "azure-recovery-vault",
355
+ "question_type": "multiple_choice",
356
+ "question": "Recovery Services Vault์˜ Soft Delete ๊ธฐ๋Šฅ์ด ํ•˜๋Š” ์—ญํ• ์€?",
357
+ "options": [
358
+ "์‚ญ์ œ๋œ ๋ฐฑ์—… ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ • ๊ธฐ๊ฐ„ ๋ณต์› ๊ฐ€๋Šฅํ•˜๊ฒŒ ์œ ์ง€ํ•œ๋‹ค",
359
+ "VM์„ ์ž๋™์œผ๋กœ ๋ฐฑ์—…ํ•œ๋‹ค",
360
+ "๋ฐฑ์—… ์ •์ฑ…์„ ์ž๋™ ์ ์šฉํ•œ๋‹ค",
361
+ "๋‹ค๋ฅธ ์ง€์—ญ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œํ•œ๋‹ค"
362
+ ],
363
+ "answer": "์‚ญ์ œ๋œ ๋ฐฑ์—… ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ • ๊ธฐ๊ฐ„ ๋ณต์› ๊ฐ€๋Šฅํ•˜๊ฒŒ ์œ ์ง€ํ•œ๋‹ค",
364
+ "explanation": "Soft Delete๋Š” ๋ฐฑ์—… ํ•ญ๋ชฉ ์‚ญ์ œ ํ›„ ์ตœ๋Œ€ 14์ผ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธ ์ƒํƒœ๋กœ ์œ ์ง€ํ•ด ์‹ค์ˆ˜๋‚˜ ๋žœ์„ฌ์›จ์–ด๋กœ ์ธํ•œ ์‚ญ์ œ์—์„œ ๋ณต์›ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.",
365
+ "source_id": "azure-recovery-services-vault"
366
+ },
367
+ {
368
+ "id": "azure-q51",
369
+ "lesson_id": "azure-rg",
370
+ "question_type": "command",
371
+ "question": "`Korea Central` ์ง€์—ญ์— `myRG` ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์„ ์ƒ์„ฑํ•˜๋Š” Azure CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
372
+ "options": [],
373
+ "answer": "az group create --name myRG --location koreacentral",
374
+ "explanation": "`az group create --name <์ด๋ฆ„> --location <์ง€์—ญ>`. ์ง€์—ญ ์ด๋ฆ„์€ ๊ณต๋ฐฑ ์—†์ด ์†Œ๋ฌธ์ž๋กœ ์”๋‹ˆ๋‹ค (koreacentral, eastus ๋“ฑ).",
375
+ "difficulty": "easy",
376
+ "source_id": "manual-az104-basic"
377
+ },
378
+ {
379
+ "id": "azure-q52",
380
+ "lesson_id": "azure-storage",
381
+ "question_type": "multiple_choice",
382
+ "question": "Azure Storage ๊ณ„์ •์˜ ๊ธฐ๋ณธ ์ค‘๋ณต์„ฑ SKU๋กœ ๋™์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋‚ด 3๊ฐœ ๋ณต์ œ๋ณธ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€?",
383
+ "options": ["LRS", "ZRS", "GRS", "GZRS"],
384
+ "answer": "LRS",
385
+ "explanation": "LRS(Locally Redundant Storage)๋Š” ๋‹จ์ผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ๋‚ด 3๊ฐœ ๋ณต์ œ๋ณธ์œผ๋กœ ๊ฐ€์žฅ ์ €๋ ดํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ์ „์ฒด ์žฅ์• ์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.",
386
+ "difficulty": "easy",
387
+ "source_id": "azure-storage-account"
388
+ },
389
+ {
390
+ "id": "azure-q53",
391
+ "lesson_id": "azure-vnet",
392
+ "question_type": "multiple_choice",
393
+ "question": "Azure VNet์—์„œ ์„œ๋ธŒ๋„ท๋งˆ๋‹ค ์˜ˆ์•ฝ๋˜์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” IP ์ฃผ์†Œ ์ˆ˜๋Š”?",
394
+ "options": ["5๊ฐœ", "3๊ฐœ", "2๊ฐœ", "1๊ฐœ"],
395
+ "answer": "5๊ฐœ",
396
+ "explanation": "Azure๋Š” ์„œ๋ธŒ๋„ท๋งˆ๋‹ค ๋„คํŠธ์›Œํฌ ๏ฟฝ๏ฟฝ์†Œ, ๊ฒŒ์ดํŠธ์›จ์ด, DHCP 2๊ฐœ, ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋“ฑ 5๊ฐœ IP๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. /28 ์„œ๋ธŒ๋„ท์ด๋ฉด 16-5=11๊ฐœ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
397
+ "difficulty": "medium",
398
+ "source_id": "manual-az104-basic"
399
+ },
400
+ {
401
+ "id": "azure-q54",
402
+ "lesson_id": "azure-rbac",
403
+ "question_type": "multiple_choice",
404
+ "question": "Owner, Contributor, Reader ์ค‘ ์—ญํ•  ํ• ๋‹น(RBAC ๋ถ€์—ฌ) ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๊ฒƒ์€?",
405
+ "options": ["Owner๋งŒ", "Owner์™€ Contributor", "์„ธ ์—ญํ•  ๋ชจ๋‘", "Reader๋งŒ"],
406
+ "answer": "Owner๋งŒ",
407
+ "explanation": "Owner๋งŒ ์—ญํ•  ํ• ๋‹น ๊ถŒํ•œ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. Contributor๋Š” ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ๋Š” ๋˜์ง€๋งŒ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ ์—ญํ• ์„ ์ค„ ์ˆ˜ ์—†๊ณ , Reader๋Š” ์กฐํšŒ๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
408
+ "difficulty": "easy",
409
+ "source_id": "azure-rbac-overview"
410
+ },
411
+ {
412
+ "id": "azure-q55",
413
+ "lesson_id": "azure-policy",
414
+ "question_type": "multiple_choice",
415
+ "question": "Azure Policy effect ์ค‘ ๊ทœ์ •์— ๋งž์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค ๋ฐฐํฌ ์ž์ฒด๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์€?",
416
+ "options": ["Deny", "Audit", "Append", "Modify"],
417
+ "answer": "Deny",
418
+ "explanation": "Deny๋Š” ๋น„์ค€์ˆ˜ ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ์„ ๋ง‰์Šต๋‹ˆ๋‹ค. Audit๋Š” ํ—ˆ์šฉํ•˜๋˜ ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๊ณ , Append๋Š” ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉฐ, Modify๋Š” ํƒœ๊ทธ ๊ฐ™์€ ์†์„ฑ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.",
419
+ "difficulty": "easy",
420
+ "source_id": "azure-policy-overview"
421
+ },
422
+ {
423
+ "id": "azure-q56",
424
+ "lesson_id": "azure-vm",
425
+ "question_type": "command",
426
+ "question": "`myRG` ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์— Ubuntu 22.04 LTS ์ด๋ฏธ์ง€๋กœ `myVM`์„ ์ƒ์„ฑํ•˜๋Š” ์ตœ์†Œํ•œ์˜ Azure CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
427
+ "options": [],
428
+ "answer": "az vm create --resource-group myRG --name myVM --image Ubuntu2204",
429
+ "explanation": "`az vm create --resource-group <RG> --name <์ด๋ฆ„> --image <์ด๋ฏธ์ง€>`. ์ด๋ฏธ์ง€ ์ด๋ฆ„์€ `az vm image list` ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
430
+ "difficulty": "medium",
431
+ "source_id": "azure-vm-overview"
432
+ },
433
+ {
434
+ "id": "azure-q57",
435
+ "lesson_id": "azure-vmss",
436
+ "question_type": "multiple_choice",
437
+ "question": "VMSS์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ตœ์†Œ 2๊ฐœ ์ตœ๋Œ€ 10๊ฐœ๋กœ ์œ ์ง€ํ•˜๊ณ  CPU 75% ์ดˆ๊ณผ ์‹œ ๋Š˜๋ฆฌ๋Š” ๊ทœ์น™์€ ์–ด๋””์— ์ •์˜ํ•˜๋Š”๊ฐ€?",
438
+ "options": ["Autoscale ์„ค์ •", "NSG ๊ทœ์น™", "Backup Policy", "Alert Rule"],
439
+ "answer": "Autoscale ์„ค์ •",
440
+ "explanation": "VMSS์˜ ์ธ์Šคํ„ด์Šค ์ˆ˜ ์ž๋™ ์กฐ์ •์€ Autoscale ์„ค์ •์—์„œ ๋ฉ”ํŠธ๋ฆญ ์กฐ๊ฑด(CPU %), ์ตœ์†Œ/์ตœ๋Œ€ ์ธ์Šคํ„ด์Šค, ์ฆ๊ฐ ๋‹จ์œ„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
441
+ "difficulty": "easy",
442
+ "source_id": "azure-vmss-overview"
443
+ },
444
+ {
445
+ "id": "azure-q58",
446
+ "lesson_id": "azure-nsg",
447
+ "question_type": "command",
448
+ "question": "NSG `myNSG`์— ์šฐ์„ ์ˆœ์œ„ 200์œผ๋กœ TCP 443 ์ธ๋ฐ”์šด๋“œ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๊ทœ์น™ `allow-https`๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
449
+ "options": [],
450
+ "answer": "az network nsg rule create --nsg-name myNSG --resource-group myRG --name allow-https --priority 200 --access Allow --protocol Tcp --destination-port-ranges 443",
451
+ "explanation": "`az network nsg rule create`์— `--nsg-name`, `--priority`, `--access Allow`, `--protocol Tcp`, `--destination-port-ranges`๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
452
+ "difficulty": "medium",
453
+ "source_id": "manual-az104-basic"
454
+ },
455
+ {
456
+ "id": "azure-q59",
457
+ "lesson_id": "azure-app-gateway",
458
+ "question_type": "multiple_choice",
459
+ "question": "Application Gateway์™€ Azure Load Balancer์˜ ์ฃผ์š” ์ฐจ์ด๋Š”?",
460
+ "options": [
461
+ "App Gateway๋Š” L7(HTTP/HTTPS) ๋ผ์šฐํŒ…, Load Balancer๋Š” L4(TCP/UDP) ๋ถ„์‚ฐ์ด๋‹ค",
462
+ "๋‘ ์„œ๋น„์Šค ๋ชจ๋‘ L4๋งŒ ์ง€์›ํ•œ๋‹ค",
463
+ "Load Balancer๊ฐ€ URL ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์„ ์ง€์›ํ•œ๋‹ค",
464
+ "App Gateway๋Š” VM์„ ์ง์ ‘ ๊ด€๋ฆฌํ•œ๋‹ค"
465
+ ],
466
+ "answer": "App Gateway๋Š” L7(HTTP/HTTPS) ๋ผ์šฐํŒ…, Load Balancer๋Š” L4(TCP/UDP) ๋ถ„์‚ฐ์ด๋‹ค",
467
+ "explanation": "App Gateway๋Š” URL ๊ฒฝ๋กœ, ํ˜ธ์ŠคํŠธ ํ—ค๋” ๊ธฐ๋ฐ˜ L7 ๋ผ์šฐํŒ…๊ณผ WAF๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Load Balancer๋Š” TCP/UDP ๋ ˆ๋ฒจ L4 ๋ถ„์‚ฐ์œผ๋กœ ๋” ๋‹จ์ˆœํ•˜๊ณ  ๋น ๋ฆ…๋‹ˆ๋‹ค.",
468
+ "difficulty": "medium",
469
+ "source_id": "azure-vnet-overview"
470
+ },
471
+ {
472
+ "id": "azure-q60",
473
+ "lesson_id": "azure-dns",
474
+ "question_type": "multiple_choice",
475
+ "question": "Azure Private DNS Zone๊ณผ Public DNS Zone์˜ ์ฐจ์ด๋Š”?",
476
+ "options": [
477
+ "Private Zone์€ VNet ๋‚ด๋ถ€์—์„œ๋งŒ ํ•ด์„๋˜๊ณ , Public Zone์€ ์ธํ„ฐ๋„ท์—์„œ ์กฐํšŒ๋œ๋‹ค",
478
+ "Private Zone์€ ๋” ๋น ๋ฅธ TTL์„ ๊ฐ€์ง„๋‹ค",
479
+ "Public Zone์€ VNet์— ๋งํฌํ•ด์•ผ๋งŒ ๋™์ž‘ํ•œ๋‹ค",
480
+ "๋‘ Zone ๋ชจ๋‘ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค"
481
+ ],
482
+ "answer": "Private Zone์€ VNet ๋‚ด๋ถ€์—์„œ๋งŒ ํ•ด์„๋˜๊ณ , Public Zone์€ ์ธํ„ฐ๋„ท์—์„œ ์กฐํšŒ๋œ๋‹ค",
483
+ "explanation": "Private DNS Zone์€ VNet ๋งํฌ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น VNet ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค์—๋งŒ DNS ์ด๋ฆ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Public Zone์€ ์ธํ„ฐ๋„ท ์ „์ฒด์—์„œ ์กฐํšŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
484
+ "difficulty": "easy",
485
+ "source_id": "azure-dns-overview"
486
+ },
487
+ {
488
+ "id": "azure-q61",
489
+ "lesson_id": "azure-blob-tier",
490
+ "question_type": "multiple_choice",
491
+ "question": "Archive tier์— ์žˆ๋Š” Blob์— ์ฆ‰์‹œ ์ ‘๊ทผํ•˜๋ ค๋ฉด ๋จผ์ € ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์€?",
492
+ "options": [
493
+ "rehydration(๋ฆฌํ•˜์ด๋“œ๋ ˆ์ด์…˜)์œผ๋กœ Hot ๋˜๋Š” Cool๋กœ ์ด๋™",
494
+ "์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ •์„ ์žฌ์ƒ์„ฑ",
495
+ "Blob์„ ์‚ญ์ œ ํ›„ ๋‹ค์‹œ ์—…๋กœ๋“œ",
496
+ "SAS ํ† ํฐ์„ ์ƒˆ๋กœ ๋ฐœ๊ธ‰"
497
+ ],
498
+ "answer": "rehydration(๋ฆฌํ•˜์ด๋“œ๋ ˆ์ด์…˜)์œผ๋กœ Hot ๋˜๋Š” Cool๋กœ ์ด๋™",
499
+ "explanation": "Archive tier์˜ Blob์€ ์˜คํ”„๋ผ์ธ ์ƒํƒœ๋ผ ์ง์ ‘ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Hot/Cool๋กœ rehydration ํ›„ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ‘œ์ค€ ์šฐ์„ ์ˆœ์œ„๋กœ ์ˆ˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.",
500
+ "difficulty": "medium",
501
+ "source_id": "azure-blob-access-tiers"
502
+ },
503
+ {
504
+ "id": "azure-q62",
505
+ "lesson_id": "azure-log-analytics",
506
+ "question_type": "multiple_choice",
507
+ "question": "Log Analytics workspace์—์„œ ์ฟผ๋ฆฌ ์–ธ์–ด๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€?",
508
+ "options": ["KQL (Kusto Query Language)", "SQL", "Bash", "PowerShell"],
509
+ "answer": "KQL (Kusto Query Language)",
510
+ "explanation": "Azure Log Analytics๋Š” KQL(Kusto Query Language)๋กœ ๋กœ๊ทธ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. `| where`, `| summarize`, `| project` ๊ฐ™์€ ํŒŒ์ดํ”„๋ผ์ธ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
511
+ "difficulty": "easy",
512
+ "source_id": "azure-monitor-overview"
513
+ },
514
+ {
515
+ "id": "azure-q63",
516
+ "lesson_id": "azure-entra-id",
517
+ "question_type": "multiple_choice",
518
+ "question": "Microsoft Entra ID์™€ ์˜จํ”„๋ ˆ๋ฏธ์Šค Active Directory์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€?",
519
+ "options": [
520
+ "Entra ID๋Š” ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ ID ์„œ๋น„์Šค๋กœ OAuth/OIDC๋ฅผ ์ง€์›, AD๋Š” Kerberos/LDAP ๊ธฐ๋ฐ˜์ด๋‹ค",
521
+ "๋‘ ์„œ๋น„์Šค ๋ชจ๋‘ Kerberos๋งŒ ์‚ฌ์šฉํ•œ๋‹ค",
522
+ "AD๋Š” ํด๋ผ์šฐ๋“œ ์ „์šฉ์ด๋‹ค",
523
+ "Entra ID๋Š” ๊ทธ๋ฃน ์ •์ฑ…์„ ์ง์ ‘ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค"
524
+ ],
525
+ "answer": "Entra ID๋Š” ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ ID ์„œ๋น„์Šค๋กœ OAuth/OIDC๋ฅผ ์ง€์›, AD๋Š” Kerberos/LDAP ๊ธฐ๋ฐ˜์ด๋‹ค",
526
+ "explanation": "Entra ID๋Š” SaaS/ํด๋ผ์šฐ๋“œ ์•ฑ ์ธ์ฆ์— OAuth2, OIDC, SAML์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜จํ”„๋ ˆ๋ฏธ์Šค AD๋Š” Kerberos/LDAP ๊ธฐ๋ฐ˜์œผ๋กœ ๋„๋ฉ”์ธ ์กฐ์ธ, ๊ทธ๋ฃน ์ •์ฑ…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
527
+ "difficulty": "medium",
528
+ "source_id": "azure-entra-id-overview"
529
+ },
530
+ {
531
+ "id": "azure-q64",
532
+ "lesson_id": "azure-management-group",
533
+ "question_type": "multiple_choice",
534
+ "question": "Management Group ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ Policy์™€ RBAC ์ƒ์† ๋ฐฉํ–ฅ์€?",
535
+ "options": [
536
+ "์ƒ์œ„์—์„œ ํ•˜์œ„๋กœ ์ƒ์† (Management Group โ†’ Subscription โ†’ Resource Group โ†’ Resource)",
537
+ "ํ•˜์œ„์—์„œ ์ƒ์œ„๋กœ ์ƒ์†",
538
+ "๋™์ผ ๋ ˆ๋ฒจ ๊ฐ„์—๋งŒ ์ƒ์†",
539
+ "์ƒ์† ์—†์ด ๊ฐ ๋ ˆ๋ฒจ์— ๋…๋ฆฝ ์ ์šฉ"
540
+ ],
541
+ "answer": "์ƒ์œ„์—์„œ ํ•˜์œ„๋กœ ์ƒ์† (Management Group โ†’ Subscription โ†’ Resource Group โ†’ Resource)",
542
+ "explanation": "Policy์™€ RBAC๋Š” ๊ณ„์ธต ์ƒ์œ„์—์„œ ํ•˜์œ„๋กœ ์ƒ์†๋ฉ๋‹ˆ๋‹ค. Management Group์— ์ •์ฑ…์„ ์ ์šฉํ•˜๋ฉด ํ•˜์œ„์˜ ๋ชจ๋“  Subscription๊ณผ Resource Group์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
543
+ "difficulty": "easy",
544
+ "source_id": "azure-management-group-overview"
545
+ },
546
+ {
547
+ "id": "azure-q65",
548
+ "lesson_id": "azure-resource-lock",
549
+ "question_type": "multiple_choice",
550
+ "question": "ReadOnly Lock์ด ๊ฑธ๋ฆฐ ๋ฆฌ์†Œ์Šค์—์„œ ํ•  ์ˆ˜ ์—†๋Š” ์ž‘์—…์€?",
551
+ "options": [
552
+ "์„ค์ • ์ˆ˜์ • ๋ฐ ์‚ญ์ œ ๋ชจ๋‘ ๋ถˆ๊ฐ€",
553
+ "์‚ญ์ œ๋งŒ ๋ถˆ๊ฐ€, ์ˆ˜์ •์€ ๊ฐ€๋Šฅ",
554
+ "์ฝ๊ธฐ๋งŒ ๋ถˆ๊ฐ€",
555
+ "๋ฐฐํฌ๋งŒ ๋ถˆ๊ฐ€"
556
+ ],
557
+ "answer": "์„ค์ • ์ˆ˜์ • ๋ฐ ์‚ญ์ œ ๋ชจ๋‘ ๋ถˆ๊ฐ€",
558
+ "explanation": "ReadOnly Lock์€ ์ฝ๊ธฐ๋งŒ ํ—ˆ์šฉํ•˜๊ณ  ์ˆ˜์ •ยท์‚ญ์ œ๋ฅผ ๋ชจ๋‘ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. CanNotDelete Lock์€ ์ˆ˜์ •์€ ํ—ˆ์šฉํ•˜์ง€๋งŒ ์‚ญ์ œ๋งŒ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.",
559
+ "difficulty": "medium",
560
+ "source_id": "manual-az104-basic"
561
+ },
562
+ {
563
+ "id": "azure-q66",
564
+ "lesson_id": "azure-tags",
565
+ "question_type": "command",
566
+ "question": "`myVM` VM์— `env=prod` ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” Azure CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
567
+ "options": [],
568
+ "answer": "az resource tag --tags env=prod --resource-group myRG --name myVM --resource-type Microsoft.Compute/virtualMachines",
569
+ "explanation": "`az resource tag --tags key=value`๋กœ ๋ฆฌ์†Œ์Šค์— ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. `--resource-type`์€ ์ „์ฒด provider ๊ฒฝ๋กœ๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
570
+ "difficulty": "hard",
571
+ "source_id": "manual-az104-basic"
572
+ },
573
+ {
574
+ "id": "azure-q67",
575
+ "lesson_id": "azure-custom-role",
576
+ "question_type": "multiple_choice",
577
+ "question": "Custom Role ์ƒ์„ฑ ์‹œ `NotActions`์˜ ์—ญํ• ์€?",
578
+ "options": [
579
+ "Actions์—์„œ ํ—ˆ์šฉ๋œ ์ž‘์—… ์ค‘ ํŠน์ • ์ž‘์—…์„ ์ œ์™ธํ•œ๋‹ค",
580
+ "๋ชจ๋“  ์ž‘์—…์„ ์ฐจ๋‹จํ•œ๋‹ค",
581
+ "Actions์— ์—†๋Š” ์ž‘์—…์„ ์ถ”๊ฐ€ํ•œ๋‹ค",
582
+ "์—ญํ• ์„ ํ• ๋‹นํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค"
583
+ ],
584
+ "answer": "Actions์—์„œ ํ—ˆ์šฉ๋œ ์ž‘์—… ์ค‘ ํŠน์ • ์ž‘์—…์„ ์ œ์™ธํ•œ๋‹ค",
585
+ "explanation": "NotActions๋Š” Actions ์™€์ผ๋“œ์นด๋“œ๋กœ ํ—ˆ์šฉ๋œ ์ž‘์—…์—์„œ ํŠน์ • ์ž‘์—…์„ ๋บ„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ: `Actions: [\"*\"]`, `NotActions: [\"Microsoft.Authorization/*/Delete\"]`.",
586
+ "difficulty": "medium",
587
+ "source_id": "azure-rbac-overview"
588
+ },
589
+ {
590
+ "id": "azure-q68",
591
+ "lesson_id": "azure-sas",
592
+ "question_type": "multiple_choice",
593
+ "question": "SAS(Shared Access Signature) ํ† ํฐ์—์„œ ๋ฐ˜๋“œ์‹œ ์ง€์ •ํ•ด์•ผ ํ•˜๋Š” ๋ณด์•ˆ ์š”์†Œ 2๊ฐ€์ง€๋Š”?",
594
+ "options": [
595
+ "๋งŒ๋ฃŒ ์‹œ๊ฐ„(se)๊ณผ ํ—ˆ์šฉ ๊ถŒํ•œ(sp)",
596
+ "IP ์ฃผ์†Œ์™€ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„",
597
+ "์Šคํ† ๋ฆฌ์ง€ ๊ณ„์ • ์ด๋ฆ„๊ณผ ๊ตฌ๋… ID",
598
+ "๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน๊ณผ ์ง€์—ญ"
599
+ ],
600
+ "answer": "๋งŒ๋ฃŒ ์‹œ๊ฐ„(se)๊ณผ ํ—ˆ์šฉ ๊ถŒํ•œ(sp)",
601
+ "explanation": "SAS์—์„œ `se`(signed expiry)๋Š” ์œ ํšจ ๊ธฐ๊ฐ„, `sp`(signed permission)๋Š” r(read)/w(write)/d(delete) ๋“ฑ ํ—ˆ์šฉ ์ž‘์—…์„ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. ์œ ํšจ ๊ธฐ๊ฐ„ ์—†์ด ๋ฐœ๊ธ‰ํ•˜๋ฉด ๋ณด์•ˆ ์œ„ํ—˜์ด ํฝ๋‹ˆ๋‹ค.",
602
+ "difficulty": "medium",
603
+ "source_id": "azure-storage-account"
604
+ },
605
+ {
606
+ "id": "azure-q69",
607
+ "lesson_id": "azure-storage-redundancy",
608
+ "question_type": "multiple_choice",
609
+ "question": "์žฌํ•ด๋ณต๊ตฌ๋ฅผ ์œ„ํ•ด ์ง€์—ญ ๊ฐ„ ๋ณต์ œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์„ ํƒํ•ด์•ผ ํ•˜๋Š” ์ค‘๋ณต์„ฑ ์˜ต์…˜์€?",
610
+ "options": ["GRS ๋˜๋Š” GZRS", "LRS", "ZRS", "Premium LRS"],
611
+ "answer": "GRS ๋˜๋Š” GZRS",
612
+ "explanation": "GRS(Geo-Redundant Storage)์™€ GZRS๋Š” ์ˆ˜๋ฐฑ km ๋–จ์–ด์ง„ ํŽ˜์–ด ์ง€์—ญ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œํ•ด ์ง€์—ญ ์ „์ฒด ์žฌํ•ด์— ๋Œ€๋น„ํ•ฉ๋‹ˆ๋‹ค. LRS/ZRS๋Š” ๋‹จ์ผ ์ง€์—ญ ๋‚ด์—์„œ๋งŒ ๋ณต์ œํ•ฉ๋‹ˆ๋‹ค.",
613
+ "difficulty": "easy",
614
+ "source_id": "azure-storage-account"
615
+ },
616
+ {
617
+ "id": "azure-q70",
618
+ "lesson_id": "azure-lifecycle",
619
+ "question_type": "multiple_choice",
620
+ "question": "Lifecycle ์ •์ฑ…์—์„œ ๋งˆ์ง€๋ง‰ ์ˆ˜์ • ํ›„ 30์ผ ๋’ค Cool๋กœ, 90์ผ ๋’ค Archive๋กœ ์ด๋™ํ•˜๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ธฐ์ค€ ๊ฐ’์€?",
621
+ "options": [
622
+ "daysAfterModificationGreaterThan",
623
+ "daysAfterCreationGreaterThan",
624
+ "daysAfterLastAccessTimeGreaterThan",
625
+ "daysUntilExpiration"
626
+ ],
627
+ "answer": "daysAfterModificationGreaterThan",
628
+ "explanation": "Lifecycle ์ •์ฑ…์˜ ์กฐ๊ฑด ํ•„๋“œ `daysAfterModificationGreaterThan: 30`๊ณผ `90`์œผ๋กœ ๋‹จ๊ณ„์  tier ์ด๋™์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ ‘๊ทผ ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜์€ `daysAfterLastAccessTimeGreaterThan`์ž…๋‹ˆ๋‹ค.",
629
+ "difficulty": "medium",
630
+ "source_id": "azure-blob-access-tiers"
631
+ },
632
+ {
633
+ "id": "azure-q71",
634
+ "lesson_id": "azure-files",
635
+ "question_type": "command",
636
+ "question": "`myshare` Azure ํŒŒ์ผ ๊ณต์œ ๋ฅผ Linux์—์„œ `/mnt/azfiles`์— ๋งˆ์šดํŠธํ•˜๋Š” ํ”„๋กœํ† ์ฝœ๊ณผ ๋งˆ์šดํŠธ ๋ช…๋ น์–ด ์‹œ์ž‘ ๋ถ€๋ถ„์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
637
+ "options": [],
638
+ "answer": "mount -t cifs",
639
+ "explanation": "Azure Files๋Š” SMB ํ”„๋กœํ† ์ฝœ(CIFS)๋กœ Linux์— ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค: `mount -t cifs //<์Šคํ† ๋ฆฌ์ง€>.file.core.windows.net/<๊ณต์œ ๋ช…> /mnt/azfiles -o ...`",
640
+ "difficulty": "medium",
641
+ "source_id": "azure-storage-account"
642
+ },
643
+ {
644
+ "id": "azure-q72",
645
+ "lesson_id": "azure-managed-disk",
646
+ "question_type": "multiple_choice",
647
+ "question": "VM์— ๋ฐ์ดํ„ฐ ๋””์Šคํฌ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ Premium SSD์™€ Standard HDD์˜ ์„ ํƒ ๊ธฐ์ค€์œผ๋กœ ๋งž๋Š” ๊ฒƒ์€?",
648
+ "options": [
649
+ "I/O ์ง‘์•ฝ์  DB ์›Œํฌ๋กœ๋“œ๋Š” Premium SSD, ๋กœ๊ทธ ๋ฐฑ์—… ๋ณด๊ด€์€ Standard HDD",
650
+ "Standard HDD๊ฐ€ ๋ชจ๋“  ๊ฒฝ์šฐ์— ๋” ๋น ๋ฅด๋‹ค",
651
+ "Premium SSD๋Š” OS ๋””์Šคํฌ์—๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค",
652
+ "๋ฐ์ดํ„ฐ ๋””์Šคํฌ์—๋Š” SSD๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค"
653
+ ],
654
+ "answer": "I/O ์ง‘์•ฝ์  DB ์›Œํฌ๋กœ๋“œ๋Š” Premium SSD, ๋กœ๊ทธ ๋ฐฑ์—… ๋ณด๊ด€์€ Standard HDD",
655
+ "explanation": "Premium SSD๋Š” ๋‚ฎ์€ ์ง€์—ฐ์‹œ๊ฐ„๊ณผ ๋†’์€ IOPS๋กœ DB๋‚˜ ์›น์„œ๋ฒ„ ์Šคํ† ๋ฆฌ์ง€์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. Standard HDD๋Š” ์ ‘๊ทผ ๋นˆ๋„๊ฐ€ ๋‚ฎ์€ ๋ฐฑ์—…, ์•„์นด์ด๋ธŒ์— ๋น„์šฉ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.",
656
+ "difficulty": "easy",
657
+ "source_id": "azure-vm-overview"
658
+ },
659
+ {
660
+ "id": "azure-q73",
661
+ "lesson_id": "azure-availability-set",
662
+ "question_type": "multiple_choice",
663
+ "question": "Availability Set์˜ Fault Domain๊ณผ Update Domain์˜ ์ฐจ์ด๋Š”?",
664
+ "options": [
665
+ "Fault Domain์€ ์ „์›/๋„คํŠธ์›Œํฌ ๊ณต์œ  ๊ทธ๋ฃน, Update Domain์€ OS ํŒจ์น˜ ์ˆœ์„œ ๊ทธ๋ฃน์ด๋‹ค",
666
+ "๋‘ ๋„๋ฉ”์ธ ๋ชจ๋‘ ๋™์ผํ•œ ์—ญํ• ์ด๋‹ค",
667
+ "Update Domain์€ ์ง€์—ญ ๊ฐ„ ๋ถ„์‚ฐ์„ ์˜๋ฏธํ•œ๋‹ค",
668
+ "Fault Domain์€ ์†Œํ”„ํŠธ์›จ์–ด ์—…๋ฐ์ดํŠธ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•œ๋‹ค"
669
+ ],
670
+ "answer": "Fault Domain์€ ์ „์›/๋„คํŠธ์›Œํฌ ๊ณต์œ  ๊ทธ๋ฃน, Update Domain์€ OS ํŒจ์น˜ ์ˆœ์„œ ๊ทธ๋ฃน์ด๋‹ค",
671
+ "explanation": "Fault Domain์€ ๋ฌผ๋ฆฌ์  ์ „์›๊ณผ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์„œ๋ฒ„ ๋ž™ ๊ทธ๋ฃน์ž…๋‹ˆ๋‹ค. Update Domain์€ OS ํŒจ์น˜ ์žฌ์‹œ์ž‘ ์‹œ ๋™์‹œ์— ๋‚ด๋ ค๊ฐ€์ง€ ์•Š๋„๋ก ๋ถ„๋ฆฌํ•œ ๋…ผ๋ฆฌ ๊ทธ๋ฃน์ž…๋‹ˆ๋‹ค.",
672
+ "difficulty": "medium",
673
+ "source_id": "azure-vm-overview"
674
+ },
675
+ {
676
+ "id": "azure-q74",
677
+ "lesson_id": "azure-autoscale",
678
+ "question_type": "multiple_choice",
679
+ "question": "Autoscale ๊ทœ์น™์—์„œ scale-in ์‹œ ์ฃผ์˜ํ•ด์•ผ ํ•˜๋Š” ์„ค์ •์€?",
680
+ "options": [
681
+ "scale-in์€ minimum ์ธ์Šคํ„ด์Šค ์ˆ˜ ์•„๋ž˜๋กœ๋Š” ์ค„์–ด๋“ค์ง€ ์•Š๋Š”๋‹ค",
682
+ "scale-in์€ maximum ์ธ์Šคํ„ด์Šค๊นŒ์ง€๋งŒ ์ค„์–ด๋“ ๋‹ค",
683
+ "scale-in๊ณผ scale-out์ด ๋™์‹œ์— ํŠธ๋ฆฌ๊ฑฐ๋  ์ˆ˜ ์žˆ๋‹ค",
684
+ "scale-in์€ CPU 100%์ผ ๋•Œ๋งŒ ์ž‘๋™ํ•œ๋‹ค"
685
+ ],
686
+ "answer": "scale-in์€ minimum ์ธ์Šคํ„ด์Šค ์ˆ˜ ์•„๋ž˜๋กœ๋Š” ์ค„์–ด๋“ค์ง€ ์•Š๋Š”๋‹ค",
687
+ "explanation": "Autoscale์€ minimum/maximum ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. scale-in ๊ทœ์น™์ด ํŠธ๋ฆฌ๊ฑฐ๋ผ๋„ minimum ์•„๋ž˜๋กœ ์ค„์–ด๋“ค์ง€ ์•Š์•„ ํ•ญ์ƒ ์ตœ์†Œํ•œ์˜ ์šฉ๋Ÿ‰์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.",
688
+ "difficulty": "easy",
689
+ "source_id": "azure-vmss-overview"
690
+ },
691
+ {
692
+ "id": "azure-q75",
693
+ "lesson_id": "azure-vnet-peering",
694
+ "question_type": "multiple_choice",
695
+ "question": "VNet Peering์ด ๋น„์ „์ด์ (non-transitive)์ด๋ผ๋Š” ์˜๋ฏธ๋Š”?",
696
+ "options": [
697
+ "Aโ†”B, Bโ†”C ํ”ผ์–ด๋ง์ด ์žˆ์–ด๋„ Aโ†”C๋Š” ์ง์ ‘ ํ†ต์‹  ๋ถˆ๊ฐ€",
698
+ "ํ”ผ์–ด๋ง๋œ VNet๋ผ๋ฆฌ๋Š” ํ•ญ์ƒ ๋ผ์šฐํŒ…์ด ์ž๋™์œผ๋กœ ๋œ๋‹ค",
699
+ "ํ”ผ์–ด๋ง์€ ๋™์ผ ์ง€์—ญ์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค",
700
+ "A์—์„œ B๋ฅผ ํ†ตํ•ด C๋กœ ํŠธ๋ž˜ํ”ฝ์ด ์ž๋™ ์ „๋‹ฌ๋œ๋‹ค"
701
+ ],
702
+ "answer": "Aโ†”B, Bโ†”C ํ”ผ์–ด๋ง์ด ์žˆ์–ด๋„ Aโ†”C๋Š” ์ง์ ‘ ํ†ต์‹  ๋ถˆ๊ฐ€",
703
+ "explanation": "VNet Peering์€ ๋น„์ „์ด์ ์ž…๋‹ˆ๋‹ค. A-B, B-C๊ฐ€ ํ”ผ์–ด๋ง๋˜์–ด๋„ A-C ํŠธ๋ž˜ํ”ฝ์€ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. A-C ํ†ต์‹ ์ด ํ•„์š”ํ•˜๋ฉด ๋ณ„๋„์˜ A-C ํ”ผ์–ด๋ง์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ Hub-Spoke + VPN Gateway๋ฅผ ์”๋‹ˆ๋‹ค.",
704
+ "difficulty": "medium",
705
+ "source_id": "azure-vnet-overview"
706
+ },
707
+ {
708
+ "id": "azure-q76",
709
+ "lesson_id": "azure-route-table",
710
+ "question_type": "command",
711
+ "question": "`myRouteTable`์„ `myRG`์— ์ƒ์„ฑํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
712
+ "options": [],
713
+ "answer": "az network route-table create --name myRouteTable --resource-group myRG",
714
+ "explanation": "`az network route-table create`๋กœ Route Table์„ ๋งŒ๋“ค๊ณ , `az network route-table route create`๋กœ ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•œ ๋’ค ์„œ๋ธŒ๋„ท์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
715
+ "difficulty": "easy",
716
+ "source_id": "azure-vnet-overview"
717
+ },
718
+ {
719
+ "id": "azure-q77",
720
+ "lesson_id": "azure-private-endpoint",
721
+ "question_type": "multiple_choice",
722
+ "question": "Private Endpoint ์ƒ์„ฑ ํ›„ VNet ๋‚ด๋ถ€์—์„œ ์„œ๋น„์Šค ์ด๋ฆ„์ด public IP๋กœ ํ•ด์„๋œ๋‹ค๋ฉด ์›์ธ์œผ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
723
+ "options": [
724
+ "Private DNS Zone์ด VNet์— ๋งํฌ๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค",
725
+ "NSG ๊ทœ์น™์ด ์ž˜๋ชป๋๋‹ค",
726
+ "Subnet ์ฃผ์†Œ ๋ฒ”์œ„๊ฐ€ ๋ถ€์กฑํ•˜๋‹ค",
727
+ "Private Endpoint๊ฐ€ ์Šน์ธ๋˜์ง€ ์•Š์•˜๋‹ค"
728
+ ],
729
+ "answer": "Private DNS Zone์ด VNet์— ๋งํฌ๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค",
730
+ "explanation": "Private Endpoint๋Š” private IP๊ฐ€ ์ƒ๊ธฐ์ง€๋งŒ, DNS๊ฐ€ ์—ฌ์ „ํžˆ public IP๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—ฐ๊ฒฐ์ด ๊ณต๊ฐœ ์ธํ„ฐ๋„ท์„ ํ†ตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Private DNS Zone ๋งํฌ์™€ A ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
731
+ "difficulty": "hard",
732
+ "source_id": "azure-private-endpoint"
733
+ },
734
+ {
735
+ "id": "azure-q78",
736
+ "lesson_id": "azure-load-balancer-probe",
737
+ "question_type": "multiple_choice",
738
+ "question": "Azure Load Balancer์˜ Health Probe ์‹คํŒจ ์‹œ ์–ด๋–ค ๋™์ž‘์ด ์ผ์–ด๋‚˜๋Š”๊ฐ€?",
739
+ "options": [
740
+ "ํ•ด๋‹น ๋ฐฑ์—”๋“œ ์ธ์Šคํ„ด์Šค๋กœ์˜ ํŠธ๋ž˜ํ”ฝ ์ „๋‹ฌ์„ ์ค‘๋‹จํ•œ๋‹ค",
741
+ "๋ฐฑ์—”๋“œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ์žฌ์‹œ์ž‘ํ•œ๋‹ค",
742
+ "์ „์ฒด Load Balancer๋ฅผ ์ค‘์ง€ํ•œ๋‹ค",
743
+ "์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์ž๋™ ์ถ”๊ฐ€ํ•œ๋‹ค"
744
+ ],
745
+ "answer": "ํ•ด๋‹น ๋ฐฑ์—”๋“œ ์ธ์Šคํ„ด์Šค๋กœ์˜ ํŠธ๋ž˜ํ”ฝ ์ „๋‹ฌ์„ ์ค‘๋‹จํ•œ๋‹ค",
746
+ "explanation": "Health Probe๊ฐ€ ์„ค์ •๋œ ํšŸ์ˆ˜ ์ด์ƒ ์‹คํŒจํ•˜๋ฉด Load Balancer๋Š” ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ unhealthy๋กœ ํ‘œ์‹œํ•˜๊ณ  ํŠธ๋ž˜ํ”ฝ ์ „๋‹ฌ์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์‹œ ์„ฑ๊ณตํ•˜๋ฉด ์ž๋™ ๋ณต๊ท€ํ•ฉ๋‹ˆ๋‹ค.",
747
+ "difficulty": "easy",
748
+ "source_id": "azure-vnet-overview"
749
+ },
750
+ {
751
+ "id": "azure-q79",
752
+ "lesson_id": "azure-private-dns",
753
+ "question_type": "multiple_choice",
754
+ "question": "Private DNS Zone์„ VNet์— `enableAutoRegistration: true`๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด?",
755
+ "options": [
756
+ "ํ•ด๋‹น VNet์˜ VM์ด ์ƒ์„ฑ/์‚ญ์ œ๋  ๋•Œ A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ๋“ฑ๋ก/์‚ญ์ œ๋œ๋‹ค",
757
+ "DNS Zone์ด ์ž๋™์œผ๋กœ ๋ฐฑ์—…๋œ๋‹ค",
758
+ "์™ธ๋ถ€ ์ธํ„ฐ๋„ท์—์„œ๋„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค",
759
+ "Private Endpoint๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค"
760
+ ],
761
+ "answer": "ํ•ด๋‹น VNet์˜ VM์ด ์ƒ์„ฑ/์‚ญ์ œ๋  ๋•Œ A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ๋“ฑ๋ก/์‚ญ์ œ๋œ๋‹ค",
762
+ "explanation": "Auto-registration ๋งํฌ๋Š” VNet ๋‚ด VM์˜ ํ”„๋ผ์ด๋น— IP๋ฅผ ํ•ด๋‹น DNS Zone์— ์ž๋™ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜๋™์œผ๋กœ A ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง‘๋‹ˆ๋‹ค.",
763
+ "difficulty": "medium",
764
+ "source_id": "azure-dns-overview"
765
+ },
766
+ {
767
+ "id": "azure-q80",
768
+ "lesson_id": "azure-monitor-metrics",
769
+ "question_type": "multiple_choice",
770
+ "question": "Azure Monitor์—์„œ ํŠน์ • ๋ฉ”ํŠธ๋ฆญ์ด ์ž„๊ณ„๊ฐ’์„ ์ดˆ๊ณผํ•  ๋•Œ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๋ ค๋ฉด ๋ฌด์—‡์ด ํ•„์š”ํ•œ๊ฐ€?",
771
+ "options": [
772
+ "Alert Rule + Action Group",
773
+ "Diagnostic Settings๋งŒ",
774
+ "Log Analytics Workspace๋งŒ",
775
+ "NSG Flow Log"
776
+ ],
777
+ "answer": "Alert Rule + Action Group",
778
+ "explanation": "Alert Rule์€ ์กฐ๊ฑด(์ž„๊ณ„๊ฐ’)์„ ์ •์˜ํ•˜๊ณ , Action Group์€ ์ด๋ฉ”์ผยทSMSยท์›นํ›… ๋“ฑ ์•Œ๋ฆผ ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€๊ฐ€ ์—ฐ๊ฒฐ๋˜๏ฟฝ๏ฟฝ๏ฟฝ์•ผ ์‹ค์ œ๋กœ ์•Œ๋ฆผ์ด ๋ฐœ์†ก๋ฉ๋‹ˆ๋‹ค.",
779
+ "difficulty": "easy",
780
+ "source_id": "azure-monitor-overview"
781
+ },
782
+ {
783
+ "id": "azure-q81",
784
+ "lesson_id": "azure-activity-log",
785
+ "question_type": "command",
786
+ "question": "์ง€๋‚œ 24์‹œ๊ฐ„ ๋™์•ˆ `myRG`์—์„œ ๋ฐœ์ƒํ•œ Activity Log๋ฅผ CLI๋กœ ์กฐํšŒํ•˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
787
+ "options": [],
788
+ "answer": "az monitor activity-log list --resource-group myRG --offset 24h",
789
+ "explanation": "`az monitor activity-log list --resource-group <RG> --offset 24h` ๋กœ ์ตœ๊ทผ 24์‹œ๊ฐ„ ํ™œ๋™ ๋กœ๊ทธ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. `--offset`์€ ํ˜„์žฌ ์‹œ๊ฐ ๊ธฐ์ค€ ์ด์ „ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.",
790
+ "difficulty": "medium",
791
+ "source_id": "azure-monitor-overview"
792
+ },
793
+ {
794
+ "id": "azure-q82",
795
+ "lesson_id": "azure-alert",
796
+ "question_type": "multiple_choice",
797
+ "question": "Alert Rule์—์„œ `Severity 0`๊ณผ `Severity 4`์˜ ์ฐจ์ด๋Š”?",
798
+ "options": [
799
+ "Severity 0์ด ๊ฐ€์žฅ ์‹ฌ๊ฐ(Critical), Severity 4๋Š” ๊ฐ€์žฅ ๋‚ฎ์Œ(Verbose)",
800
+ "Severity 4๊ฐ€ ๊ฐ€์žฅ ์‹ฌ๊ฐํ•˜๋‹ค",
801
+ "๋‘ ๊ฐ’์€ ๋™์ผํ•˜๋‹ค",
802
+ "Severity๋Š” ๊ฒฝ๋ณด ์กฐ๊ฑด ์ž„๊ณ„๊ฐ’์„ ์˜๋ฏธํ•œ๋‹ค"
803
+ ],
804
+ "answer": "Severity 0์ด ๊ฐ€์žฅ ์‹ฌ๊ฐ(Critical), Severity 4๋Š” ๊ฐ€์žฅ ๋‚ฎ์Œ(Verbose)",
805
+ "explanation": "Azure Alert Severity๋Š” 0(Critical)๋ถ€ํ„ฐ 4(Verbose)๊นŒ์ง€ 5๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์‹ฌ๊ฐ๋„๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋” ์œ„ํ—˜ํ•œ ์ƒํ™ฉ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.",
806
+ "difficulty": "easy",
807
+ "source_id": "azure-monitor-overview"
808
+ },
809
+ {
810
+ "id": "azure-q83",
811
+ "lesson_id": "azure-backup-policy",
812
+ "question_type": "multiple_choice",
813
+ "question": "VM Backup Policy์—์„œ ์„ค์ •ํ•˜๋Š” ํ•ต์‹ฌ ๋‘ ๊ฐ€์ง€ ์š”์†Œ๋Š”?",
814
+ "options": [
815
+ "๋ฐฑ์—… ๋นˆ๋„(์ผ๋ณ„/์ฃผ๋ณ„)์™€ ๋ณด์กด ๊ธฐ๊ฐ„",
816
+ "๋ฐฑ์—… ์œ„์น˜์™€ VM ํฌ๊ธฐ",
817
+ "NSG ๊ทœ์น™๊ณผ subnet",
818
+ "์ด๋ฉ”์ผ ์•Œ๋ฆผ ์ฃผ์†Œ์™€ SKU"
819
+ ],
820
+ "answer": "๋ฐฑ์—… ๋นˆ๋„(์ผ๋ณ„/์ฃผ๋ณ„)์™€ ๋ณด์กด ๊ธฐ๊ฐ„",
821
+ "explanation": "Backup Policy๋Š” ์–ธ์ œ ๋ฐฑ์—…ํ• ์ง€(๋นˆ๋„)์™€ ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ๋ณด์กดํ• ์ง€(๋ณด์กด ๊ธฐ๊ฐ„)๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณด์กด ๊ธฐ๊ฐ„์ด ๊ธธ์ˆ˜๋ก ๋” ๋งŽ์€ ๋ณต๊ตฌ ์ง€์ ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
822
+ "difficulty": "easy",
823
+ "source_id": "azure-recovery-services-vault"
824
+ },
825
+ {
826
+ "id": "azure-q84",
827
+ "lesson_id": "azure-restore",
828
+ "question_type": "multiple_choice",
829
+ "question": "VM ๋ณต์› ์‹œ '๊ธฐ์กด VM ๊ต์ฒด' ๋Œ€์‹  '์ƒˆ VM์œผ๋กœ ๋ณต์›'์„ ์„ ํƒํ•˜๋Š” ์ด์œ ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
830
+ "options": [
831
+ "๊ธฐ์กด VM์„ ๋ณด์กดํ•˜๋ฉด์„œ ๋ณต์› ๊ฒฐ๊ณผ๋ฅผ ๋จผ์ € ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด",
832
+ "์ƒˆ VM์ด ํ•ญ์ƒ ๋” ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ",
833
+ "๊ธฐ์กด VM ๊ต์ฒด๊ฐ€ ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ",
834
+ "์ƒˆ VM์€ ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ"
835
+ ],
836
+ "answer": "๊ธฐ์กด VM์„ ๋ณด์กดํ•˜๋ฉด์„œ ๋ณต์› ๊ฒฐ๊ณผ๋ฅผ ๋จผ์ € ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด",
837
+ "explanation": "ํ”„๋กœ๋•์…˜ VM์— ์ง์ ‘ ๋ณต์›ํ•˜๋ฉด ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ƒˆ VM์œผ๋กœ ๋ณต์›ํ•ด ๋ฐ์ดํ„ฐ์™€ ์•ฑ ์ •์ƒ ๋™์ž‘์„ ํ™•์ธํ•œ ๋’ค DNS๋‚˜ IP๋ฅผ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.",
838
+ "difficulty": "medium",
839
+ "source_id": "azure-recovery-services-vault"
840
+ },
841
+ {
842
+ "id": "azure-q85",
843
+ "lesson_id": "azure-app-service-plan",
844
+ "question_type": "multiple_choice",
845
+ "question": "App Service Plan์˜ Free ํ‹ฐ์–ด์™€ Standard ํ‹ฐ์–ด์˜ ๊ฐ€์žฅ ํฐ ๊ธฐ๋Šฅ ์ฐจ์ด๋Š”?",
846
+ "options": [
847
+ "Standard ์ด์ƒ์—์„œ๋งŒ Custom Domain, SSL, ๋ฐฐํฌ ์Šฌ๋กฏ, ์ž๋™ ํฌ๊ธฐ ์กฐ์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค",
848
+ "Free ํ‹ฐ์–ด๊ฐ€ ๋” ๋น ๋ฅธ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์ œ๊ณตํ•œ๋‹ค",
849
+ "Standard๋Š” Linux๋งŒ ์ง€์›ํ•œ๋‹ค",
850
+ "Free ํ‹ฐ์–ด์—์„œ๋งŒ VNet ํ†ตํ•ฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค"
851
+ ],
852
+ "answer": "Standard ์ด์ƒ์—์„œ๋งŒ Custom Domain, SSL, ๋ฐฐํฌ ์Šฌ๋กฏ, ์ž๋™ ํฌ๊ธฐ ์กฐ์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค",
853
+ "explanation": "Free/Shared ํ‹ฐ์–ด๋Š” ๊ฐœ๋ฐœยทํ…Œ์ŠคํŠธ์šฉ์ž…๋‹ˆ๋‹ค. Custom Domain ๋ฐ”์ธ๋”ฉ, SSL ์ธ์ฆ์„œ, ๋ฐฐํฌ ์Šฌ๋กฏ(Blue-Green), Autoscale์€ Standard ์ด์ƒ์—์„œ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
854
+ "difficulty": "medium",
855
+ "source_id": "azure-app-service-overview"
856
+ },
857
+ {
858
+ "id": "azure-q86",
859
+ "lesson_id": "azure-container-registry",
860
+ "command": "command",
861
+ "question_type": "command",
862
+ "question": "ACR `myACR`์—์„œ ์ด๋ฏธ์ง€ `myapp:v1`์„ ๋นŒ๋“œํ•ด ํ‘ธ์‹œํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
863
+ "options": [],
864
+ "answer": "az acr build --registry myACR --image myapp:v1 .",
865
+ "explanation": "`az acr build`๋Š” ๋กœ์ปฌ Dockerfile์„ ACR ๋นŒ๋“œ ์—์ด์ „ํŠธ์— ๋ณด๋‚ด ๋นŒ๋“œ ํ›„ ์ž๋™์œผ๋กœ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ์— Docker ์„ค์น˜๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.",
866
+ "difficulty": "medium",
867
+ "source_id": "azure-vm-overview"
868
+ },
869
+ {
870
+ "id": "azure-q87",
871
+ "lesson_id": "azure-key-vault",
872
+ "question_type": "multiple_choice",
873
+ "question": "Key Vault์—์„œ ๋น„๋ฐ€(Secret), ํ‚ค(Key), ์ธ์ฆ์„œ(Certificate)์˜ ์ฐจ์ด๋Š”?",
874
+ "options": [
875
+ "Secret์€ ํŒจ์Šค์›Œ๋“œ ๊ฐ™์€ ์ž„์˜ ๋ฌธ์ž์—ด, Key๋Š” ์•”ํ˜ธํ™”์šฉ ํ‚ค ์†Œ์žฌ, Certificate๋Š” X.509 ์ธ์ฆ์„œ๋‹ค",
876
+ "์„ธ ๊ฐ€์ง€ ๋ชจ๋‘ ๋™์ผํ•œ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค",
877
+ "Key๋Š” ํŒจ์Šค์›Œ๋“œ, Secret์€ ์•”ํ˜ธํ™” ํ‚ค๋‹ค",
878
+ "Certificate๋Š” ๋„คํŠธ์›Œํฌ ๋ฐฉํ™”๋ฒฝ ๊ทœ์น™์ด๋‹ค"
879
+ ],
880
+ "answer": "Secret์€ ํŒจ์Šค์›Œ๋“œ ๊ฐ™์€ ์ž„์˜ ๋ฌธ์ž์—ด, Key๋Š” ์•”ํ˜ธํ™”์šฉ ํ‚ค ์†Œ์žฌ, Certificate๋Š” X.509 ์ธ์ฆ์„œ๋‹ค",
881
+ "explanation": "Secret: DB ๋น„๋ฐ€๋ฒˆํ˜ธยทAPI ํ‚ค ๊ฐ™์€ ๋ฌธ์ž์—ด. Key: HSM์—์„œ ๋ณดํ˜ธ๋˜๋Š” ์•”ํ˜ธํ™”/์„œ๋ช…์šฉ ํ‚ค ์†Œ์žฌ. Certificate: TLS ์ธ์ฆ์„œ๋กœ ์ž๋™ ๊ฐฑ์‹  ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
882
+ "difficulty": "medium",
883
+ "source_id": "azure-key-vault-overview"
884
+ },
885
+ {
886
+ "id": "azure-q88",
887
+ "lesson_id": "azure-cost-management",
888
+ "question_type": "multiple_choice",
889
+ "question": "Azure Cost Management์—์„œ ์›”๋ณ„ ์˜ˆ์‚ฐ ์ดˆ๊ณผ ์‹œ ์ž๋™์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์ข…๋ฃŒํ•˜๋ ค๋ฉด?",
890
+ "options": [
891
+ "์˜ˆ์‚ฐ ๊ฒฝ๋ณด(Budget Alert)๋Š” ์•Œ๋ฆผ๋งŒ ๋ณด๋‚ด๊ณ  ์ž๋™ ์ข…๋ฃŒ๋Š” Action Group + Automation์œผ๋กœ ๋ณ„๋„ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค",
892
+ "์˜ˆ์‚ฐ ์„ค์ •๋งŒ ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ข…๋ฃŒ๋œ๋‹ค",
893
+ "Cost Management๊ฐ€ ์ง์ ‘ VM์„ ์ค‘์ง€ํ•œ๋‹ค",
894
+ "Policy๋ฅผ ํ†ตํ•ด ์ž๋™ ์ข…๋ฃŒ๋œ๋‹ค"
895
+ ],
896
+ "answer": "์˜ˆ์‚ฐ ๊ฒฝ๋ณด(Budget Alert)๋Š” ์•Œ๋ฆผ๋งŒ ๋ณด๋‚ด๊ณ  ์ž๋™ ์ข…๋ฃŒ๋Š” Action Group + Automation์œผ๋กœ ๋ณ„๋„ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค",
897
+ "explanation": "Budget Alert์€ ์˜ˆ์‚ฐ ์ž„๊ณ„๊ฐ’ ๋„๋‹ฌ ์‹œ ์ด๋ฉ”์ผ๋งŒ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฆฌ์†Œ์Šค ์ข…๋ฃŒ๋Š” Automation Account์˜ Runbook์„ Action Group์— ์—ฐ๊ฒฐํ•ด ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
898
+ "difficulty": "hard",
899
+ "source_id": "manual-az104-basic"
900
+ },
901
+ {
902
+ "id": "azure-q89",
903
+ "lesson_id": "azure-bastion",
904
+ "question_type": "multiple_choice",
905
+ "question": "Azure Bastion์„ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด VNet์— ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•˜๋Š” ์„œ๋ธŒ๋„ท ์ด๋ฆ„์€?",
906
+ "options": ["AzureBastionSubnet", "BastionSubnet", "GatewaySubnet", "PrivateEndpointSubnet"],
907
+ "answer": "AzureBastionSubnet",
908
+ "explanation": "Azure Bastion์€ ์ •ํ™•ํžˆ `AzureBastionSubnet`์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์„œ๋ธŒ๋„ท์—๋งŒ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ๋‹ค๋ฅด๋ฉด ๋ฐฐํฌ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ํฌ๊ธฐ๋Š” ์ตœ์†Œ /27 ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
909
+ "difficulty": "easy",
910
+ "source_id": "azure-vnet-overview"
911
+ },
912
+ {
913
+ "id": "azure-q90",
914
+ "lesson_id": "azure-nat-gateway",
915
+ "question_type": "multiple_choice",
916
+ "question": "NAT Gateway๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ๋œ ์ด์œ ๋Š”?",
917
+ "options": [
918
+ "VNet ๋‚ด Private VM์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์— ๊ณ ์ • Public IP๋ฅผ ์ œ๊ณตํ•œ๋‹ค",
919
+ "์ธ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ์„ VM์œผ๋กœ ๋ถ„์‚ฐํ•œ๋‹ค",
920
+ "VNet ๊ฐ„ ๋ผ์šฐํŒ…์„ ๋‹ด๋‹นํ•œ๋‹ค",
921
+ "DNS ์ด๋ฆ„์„ ํ•ด์„ํ•œ๋‹ค"
922
+ ],
923
+ "answer": "VNet ๋‚ด Private VM์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์— ๊ณ ์ • Public IP๋ฅผ ์ œ๊ณตํ•œ๋‹ค",
924
+ "explanation": "NAT Gateway๋Š” Public IP๊ฐ€ ์—†๋Š” VM์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ์„ ๊ณ ์ • Public IP๋กœ SNATํ•ฉ๋‹ˆ๋‹ค. ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์˜ SNAT ํฌํŠธ ์†Œ์ง„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ๋„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
925
+ "difficulty": "medium",
926
+ "source_id": "azure-vnet-overview"
927
+ },
928
+ {
929
+ "id": "azure-q91",
930
+ "lesson_id": "azure-app-service",
931
+ "question_type": "command",
932
+ "question": "`myWebApp`์— GitHub Actions๋กœ Node.js ์•ฑ์„ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด ๊ฒŒ์‹œ ํ”„๋กœํ•„์„ ๋‚ด๋ ค๋ฐ›๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
933
+ "options": [],
934
+ "answer": "az webapp deployment list-publishing-profiles --name myWebApp --resource-group myRG",
935
+ "explanation": "`az webapp deployment list-publishing-profiles`๋กœ ๊ฒŒ์‹œ ํ”„๋กœํ•„(FTP, WebDeploy, Git URL ๋“ฑ)์„ ๋‚ด๋ ค๋ฐ›์•„ GitHub Actions ์‹œํฌ๋ฆฟ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.",
936
+ "difficulty": "hard",
937
+ "source_id": "azure-app-service-overview"
938
+ },
939
+ {
940
+ "id": "azure-q92",
941
+ "lesson_id": "azure-deployment-slot",
942
+ "question_type": "multiple_choice",
943
+ "question": "๋ฐฐํฌ ์Šฌ๋กฏ swap ํ›„ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋กค๋ฐฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
944
+ "options": [
945
+ "๋‹ค์‹œ swap์„ ์‹คํ–‰ํ•˜๋ฉด production๊ณผ staging์ด ๊ต์ฒด๋˜์–ด ๋กค๋ฐฑ๋œ๋‹ค",
946
+ "git revert ํ›„ ์žฌ๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค",
947
+ "VM์„ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋œ๋‹ค",
948
+ "์Šฌ๋กฏ์„ ์‚ญ์ œํ•˜๊ณ  ์žฌ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค"
949
+ ],
950
+ "answer": "๋‹ค์‹œ swap์„ ์‹คํ–‰ํ•˜๋ฉด production๊ณผ staging์ด ๊ต์ฒด๋˜์–ด ๋กค๋ฐฑ๋œ๋‹ค",
951
+ "explanation": "Swap์€ production๊ณผ staging์˜ ์—ญํ• ์„ ๊ตํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค์‹œ swap์œผ๋กœ ์ฆ‰์‹œ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
952
+ "difficulty": "easy",
953
+ "source_id": "azure-app-service-overview"
954
+ },
955
+ {
956
+ "id": "azure-q93",
957
+ "lesson_id": "azure-aci",
958
+ "question_type": "command",
959
+ "question": "`myACR.azurecr.io/myapp:v1` ์ด๋ฏธ์ง€๋กœ `myContainer` ์ปจํ…Œ์ด๋„ˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
960
+ "options": [],
961
+ "answer": "az container create --resource-group myRG --name myContainer --image myACR.azurecr.io/myapp:v1",
962
+ "explanation": "`az container create --image <์ด๋ฏธ์ง€>` ๋กœ ACI๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ACR์˜ ํ”„๋ผ์ด๋น— ์ด๋ฏธ์ง€๋Š” `--registry-login-server`, `--registry-username`, `--registry-password` ์˜ต์…˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
963
+ "difficulty": "medium",
964
+ "source_id": "azure-container-instances"
965
+ },
966
+ {
967
+ "id": "azure-q94",
968
+ "lesson_id": "azure-keyvault-secret",
969
+ "question_type": "command",
970
+ "question": "`myKV` Key Vault์—์„œ `db-password` ๋น„๋ฐ€์˜ ํ˜„์žฌ ๊ฐ’์„ ์กฐํšŒํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
971
+ "options": [],
972
+ "answer": "az keyvault secret show --vault-name myKV --name db-password",
973
+ "explanation": "`az keyvault secret show --vault-name <KV> --name <๋น„๋ฐ€์ด๋ฆ„>` ์œผ๋กœ ํ˜„์žฌ ํ™œ์„ฑ ๋ฒ„์ „์˜ ๋น„๋ฐ€ ๊ฐ’์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์ถœ๋ ฅ์—์„œ `value` ํ•„๋“œ์— ์‹ค์ œ ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค.",
974
+ "difficulty": "easy",
975
+ "source_id": "azure-key-vault-overview"
976
+ },
977
+ {
978
+ "id": "azure-q95",
979
+ "lesson_id": "azure-managed-identity",
980
+ "question_type": "multiple_choice",
981
+ "question": "System-assigned Managed Identity์™€ User-assigned Managed Identity์˜ ์ฐจ์ด๋Š”?",
982
+ "options": [
983
+ "System-assigned๋Š” ๋ฆฌ์†Œ์Šค์™€ ์ˆ˜๋ช…์ด ๊ฐ™๊ณ , User-assigned๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋งŒ๋“ค์–ด ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์— ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๋‹ค",
984
+ "User-assigned๋Š” ์ž๋™์œผ๋กœ ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋œ๋‹ค",
985
+ "System-assigned๋Š” ์—ฌ๋Ÿฌ VM์— ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๋‹ค",
986
+ "๋‘ ํƒ€์ž…์€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค"
987
+ ],
988
+ "answer": "System-assigned๋Š” ๋ฆฌ์†Œ์Šค์™€ ์ˆ˜๋ช…์ด ๊ฐ™๊ณ , User-assigned๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋งŒ๋“ค์–ด ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์— ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๋‹ค",
989
+ "explanation": "System-assigned๋Š” VM/App Service ์ƒ์„ฑ ์‹œ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๊ณ  ๋ฆฌ์†Œ์Šค ์‚ญ์ œ ์‹œ ํ•จ๊ป˜ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. User-assigned๋Š” ๋ณ„๋„๋กœ ๋งŒ๋“ค์–ด ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์–ด ๊ณต์œ  ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
990
+ "difficulty": "medium",
991
+ "source_id": "azure-key-vault-overview"
992
+ },
993
+ {
994
+ "id": "azure-q96",
995
+ "lesson_id": "azure-network-watcher",
996
+ "question_type": "multiple_choice",
997
+ "question": "Network Watcher์˜ IP flow verify ๊ธฐ๋Šฅ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€?",
998
+ "options": [
999
+ "ํŠน์ • IP์™€ ํฌํŠธ ์กฐํ•ฉ์ด NSG์— ์˜ํ•ด ํ—ˆ์šฉ๋˜๋Š”์ง€ ์ฐจ๋‹จ๋˜๋Š”์ง€",
1000
+ "VM์˜ CPU ์‚ฌ์šฉ๋ฅ ",
1001
+ "Blob ์ ‘๊ทผ ์ด๋ ฅ",
1002
+ "Key Vault ์ ‘๊ทผ ๋กœ๊ทธ"
1003
+ ],
1004
+ "answer": "ํŠน์ • IP์™€ ํฌํŠธ ์กฐํ•ฉ์ด NSG์— ์˜ํ•ด ํ—ˆ์šฉ๋˜๋Š”์ง€ ์ฐจ๋‹จ๋˜๋Š”์ง€",
1005
+ "explanation": "IP flow verify๋Š” VM NIC์—์„œ ํŠน์ • ์†Œ์Šค IP, ๋ชฉ์ ์ง€ IP, ํฌํŠธ, ๋ฐฉํ–ฅ(์ธ๋ฐ”์šด๋“œ/์•„์›ƒ๋ฐ”์šด๋“œ)์„ ์ž…๋ ฅํ•˜๋ฉด ์–ด๋–ค NSG ๊ทœ์น™์ด ์ ์šฉ๋˜๋Š”์ง€ ์ฆ‰์‹œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1006
+ "difficulty": "medium",
1007
+ "source_id": "azure-vnet-overview"
1008
+ },
1009
+ {
1010
+ "id": "azure-q97",
1011
+ "lesson_id": "azure-move-resource",
1012
+ "question_type": "command",
1013
+ "question": "`myRG`์˜ VM `myVM`์„ `targetRG`๋กœ ์ด๋™ํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
1014
+ "options": [],
1015
+ "answer": "az resource move --destination-group targetRG --ids $(az vm show --resource-group myRG --name myVM --query id -o tsv)",
1016
+ "explanation": "`az resource move --destination-group <๋Œ€์ƒRG> --ids <๋ฆฌ์†Œ์ŠคID>` ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ID๋Š” `az vm show --query id`๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ข…์† ๋ฆฌ์†Œ์Šค(NIC, ๋””์Šคํฌ)๋„ ํ•จ๊ป˜ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1017
+ "difficulty": "hard",
1018
+ "source_id": "manual-az104-basic"
1019
+ },
1020
+ {
1021
+ "id": "azure-q99",
1022
+ "lesson_id": "azure-recovery-vault",
1023
+ "question_type": "command",
1024
+ "question": "`myRG`์˜ `myVM`์„ `myVault` Recovery Services Vault์— `DefaultPolicy`๋กœ ๋ฐฑ์—… ํ™œ์„ฑํ™”ํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
1025
+ "options": [],
1026
+ "answer": "az backup protection enable-for-vm --vault-name myVault --resource-group myRG --vm myVM --policy-name DefaultPolicy",
1027
+ "explanation": "`az backup protection enable-for-vm`์œผ๋กœ VM ๋ฐฑ์—…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. Vault์™€ VM์€ ๊ฐ™์€ region์— ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, ์ตœ์ดˆ ๋ฐฑ์—…์€ ํ™œ์„ฑํ™” ์งํ›„ ๋˜๋Š” ์˜ˆ์•ฝ ์‹œ๊ฐ„์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.",
1028
+ "difficulty": "medium",
1029
+ "source_id": "azure-recovery-services-vault"
1030
+ },
1031
+ {
1032
+ "id": "azure-q100",
1033
+ "lesson_id": "azure-subscription",
1034
+ "question_type": "multiple_choice",
1035
+ "question": "ํ•˜๋‚˜์˜ Azure Tenant ์•„๋ž˜ ์—ฌ๋Ÿฌ Subscription์„ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ๋œ ์ด์œ ๋Š”?",
1036
+ "options": [
1037
+ "ํ™˜๊ฒฝ(๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง•/์šด์˜)์ด๋‚˜ ๋ถ€์„œ๋ณ„ ๊ณผ๊ธˆ๊ณผ ์ •์ฑ…์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด",
1038
+ "Subscription์ด ๋งŽ์„์ˆ˜๋ก ์š”๊ธˆ์ด ํ• ์ธ๋˜๊ธฐ ๋•Œ๋ฌธ",
1039
+ "ํ•œ Subscription์— VM์„ 2๊ฐœ ์ด์ƒ ๋งŒ๋“ค ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ",
1040
+ "Subscription์„ ๋Š˜๋ฆฌ๋ฉด ๊ฐ€์šฉ์„ฑ์ด ๋†’์•„์ง€๊ธฐ ๋•Œ๋ฌธ"
1041
+ ],
1042
+ "answer": "ํ™˜๊ฒฝ(๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง•/์šด์˜)์ด๋‚˜ ๋ถ€์„œ๋ณ„ ๊ณผ๊ธˆ๊ณผ ์ •์ฑ…์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด",
1043
+ "explanation": "Subscription์€ ๊ณผ๊ธˆ, ๋ฆฌ์†Œ์Šค ํ• ๋‹น๋Ÿ‰, RBAC/Policy์˜ ๊ฒฝ๊ณ„์ž…๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ๊ณผ ์šด์˜์„ ๋ถ„๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋ถ€์„œ๋ณ„ ๋น„์šฉ์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ๋•Œ ์—ฌ๋Ÿฌ Subscription์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1044
+ "difficulty": "easy",
1045
+ "source_id": "manual-az104-basic"
1046
+ },
1047
+ {
1048
+ "id": "azure-q98",
1049
+ "lesson_id": "azure-template-deployment",
1050
+ "question_type": "command",
1051
+ "question": "`myRG`์— `main.bicep` ํŒŒ์ผ์„ Bicep CLI๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
1052
+ "options": [],
1053
+ "answer": "az deployment group create --resource-group myRG --template-file main.bicep",
1054
+ "explanation": "`az deployment group create --template-file <ํŒŒ์ผ>`๋กœ Bicep ๋˜๋Š” ARM JSON ํ…œํ”Œ๋ฆฟ์„ ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. `--parameters`๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ํŒŒ์ผ๋„ ํ•จ๊ป˜ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1055
+ "difficulty": "easy",
1056
+ "source_id": "azure-arm-bicep-overview"
1057
+ }
1058
+ ]
cert_study_app/content/linux/lessons.json ADDED
@@ -0,0 +1,1448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "linux-pwd",
4
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
5
+ "title": "pwd โ€” ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ ํ™•์ธ",
6
+ "summary": "ํ˜„์žฌ ์‰˜์˜ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์กฐ์ž‘ ๋ช…๋ น์€ ๋ชจ๋‘ ์ด ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‹คํ–‰๋˜๋ฏ€๋กœ ์ž‘์—… ์ „ ํ™•์ธ ์Šต๊ด€์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.",
7
+ "example": "pwd\n# /home/ubuntu\ncd /var/log && pwd\n# /var/log",
8
+ "common_mistake": "์ƒ๋Œ€๊ฒฝ๋กœ(../dir)๋Š” pwd ๊ฒฐ๊ณผ ๊ธฐ์ค€์œผ๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค. ํ—ท๊ฐˆ๋ฆฌ๋ฉด ์ ˆ๋Œ€๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
9
+ "keywords": [
10
+ "pwd",
11
+ "working directory",
12
+ "absolute path"
13
+ ],
14
+ "source_id": "linux-bash-manual",
15
+ "details": [
16
+ "`pwd`๋Š” ํ˜„์žฌ ์œ„์น˜๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ์— ํ‘œ์‹œ๋œ ๊ฒฝ๋กœ๊ฐ€ ์ž˜๋ฆด ๋•Œ ์ •ํ™•ํ•œ ์œ„์น˜๋ฅผ ํ™•์ธํ•˜๋Š” ์šฉ๋„๋กœ ์”๋‹ˆ๋‹ค.",
17
+ "์Šคํฌ๋ฆฝํŠธ์—์„œ `DIR=$(pwd)`๋กœ ํ˜„์žฌ ๊ฒฝ๋กœ๋ฅผ ์ €์žฅํ•ด๋‘๋ฉด `cd` ํ›„ `cd $DIR`๋กœ ๋ณต๊ท€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
18
+ "์‹œํ—˜์—์„œ๋Š” `cd`๋กœ ์ด๋™ ํ›„ `pwd` ๊ฒฐ๊ณผ๋ฅผ ๋ฌป๊ฑฐ๋‚˜, ์ƒ๋Œ€๊ฒฝ๋กœ ๋ช…๋ น์ด ์–ด๋А ์œ„์น˜์— ์ ์šฉ๋˜๋Š”์ง€ ์ถ”๋ก ํ•˜๋Š” ํ˜•ํƒœ๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
19
+ ]
20
+ },
21
+ {
22
+ "id": "linux-ls",
23
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
24
+ "title": "ls โ€” ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ชฉ๋ก ํ™•์ธ",
25
+ "summary": "๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ํŒŒ์ผ๊ณผ ํ•˜์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œยท์†Œ์œ ์žยทํฌ๊ธฐยท์ˆ˜์ •์‹œ๊ฐ„๊นŒ์ง€ ๋ณด๋ ค๋ฉด `-l`, ์ˆจ๊น€ํŒŒ์ผ ํฌํ•จ์€ `-a`๋ฅผ ์”๋‹ˆ๋‹ค.",
26
+ "example": "ls -al /etc\nls -lh /var/log\nls -lt # ์ˆ˜์ •์‹œ๊ฐ„ ์—ญ์ˆœ ์ •๋ ฌ",
27
+ "common_mistake": "`ls -l`์˜ ์ฒซ ์ปฌ๋Ÿผ์€ ๊ถŒํ•œ ๋น„ํŠธ(rwxrwxrwx)์ž…๋‹ˆ๋‹ค. d๋กœ ์‹œ์ž‘ํ•˜๋ฉด ๋””๋ ‰ํ„ฐ๋ฆฌ, -๋ฉด ์ผ๋ฐ˜ ํŒŒ์ผ, l์ด๋ฉด ์‹ฌ๋ณผ๋ฆญ ๋งํฌ.",
28
+ "keywords": [
29
+ "ls",
30
+ "-l",
31
+ "-a",
32
+ "-h",
33
+ "hidden files",
34
+ "permissions"
35
+ ],
36
+ "source_id": "linux-bash-manual",
37
+ "details": [
38
+ "`ls -l` ์ถœ๋ ฅ: `๊ถŒํ•œ ๋งํฌ์ˆ˜ ์†Œ์œ ์ž ๊ทธ๋ฃน ํฌ๊ธฐ ๋‚ ์งœ ์ด๋ฆ„` ์ˆœ์„œ. ๊ถŒํ•œ ๋น„ํŠธ ์ฝ๊ธฐ๋ฅผ ๋ฐ˜๋“œ์‹œ ์ตํ˜€์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
39
+ "`-a`๋Š” `.`์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ˆจ๊น€ํŒŒ์ผ ํฌํ•จ. `-h`๋Š” ์šฉ๋Ÿ‰์„ KB/MB๋กœ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.",
40
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ํŒŒ์ผ ๊ถŒํ•œยท์†Œ์œ ์ž๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜, ์ตœ๊ทผ ์ˆ˜์ • ํŒŒ์ผ์„ ์ฐพ๋Š” ๋ฌธ์ œ์—์„œ `ls -lt` ์กฐํ•ฉ์ด ์ž์ฃผ ๋‚˜์˜ต๋‹ˆ๋‹ค."
41
+ ]
42
+ },
43
+ {
44
+ "id": "linux-cd",
45
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
46
+ "title": "cd โ€” ๋””๋ ‰ํ„ฐ๋ฆฌ ์ด๋™",
47
+ "summary": "์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์‹คํ–‰ํ•˜๋Š” ๋ชจ๋“  ์ƒ๋Œ€๊ฒฝ๋กœ ๋ช…๋ น์˜ ๊ธฐ์ค€์ด ๋ฐ”๋€๋‹ˆ๋‹ค.",
48
+ "example": "cd /etc/nginx # ์ ˆ๋Œ€๊ฒฝ๋กœ ์ด๋™\ncd .. # ๋ถ€๋ชจ ๋””๋ ‰ํ„ฐ๋ฆฌ\ncd ~ # ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ\ncd - # ์ด์ „ ๋””๋ ‰ํ„ฐ๋ฆฌ",
49
+ "common_mistake": "`cd ..`์€ ํ•œ ๋‹จ๊ณ„ ์œ„, `cd /`๋Š” ๋ฃจํŠธ๋กœ ์ด๋™. `cd -`๋Š” ๋ฐ”๋กœ ์ง์ „ ์œ„์น˜๋กœ ๋Œ์•„์˜ต๋‹ˆ๋‹ค.",
50
+ "keywords": [
51
+ "cd",
52
+ "relative path",
53
+ "absolute path",
54
+ "home"
55
+ ],
56
+ "source_id": "linux-bash-cd",
57
+ "details": [
58
+ "`cd ์ ˆ๋Œ€๊ฒฝ๋กœ`๋Š” ์–ด๋””์„œ๋“  ๋™์ผ ์œ„์น˜๋กœ ์ด๋™. `cd ์ƒ๋Œ€๊ฒฝ๋กœ`๋Š” ํ˜„์žฌ ์œ„์น˜ ๊ธฐ์ค€์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.",
59
+ "`cd ~user`๋Š” ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ์ž‘์—… ์‹œ ์ž์ฃผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
60
+ "์‹œํ—˜ ํŒจํ„ด: ์—ฌ๋Ÿฌ ๋ฒˆ `cd`๋กœ ์ด๋™ ํ›„ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๋ฌป๊ฑฐ๋‚˜, ์˜ฌ๋ฐ”๋ฅธ ๊ฒฝ๋กœ ํ‘œํ˜„์‹์„ ๊ณ ๋ฅด๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
61
+ ]
62
+ },
63
+ {
64
+ "id": "linux-mkdir",
65
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
66
+ "title": "mkdir โ€” ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ",
67
+ "summary": "์ƒˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. `-p` ์˜ต์…˜์œผ๋กœ ์ค‘๊ฐ„ ๊ฒฝ๋กœ๊ฐ€ ์—†์–ด๋„ ํ•œ ๋ฒˆ์— ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
68
+ "example": "mkdir logs\nmkdir -p /srv/app/config # ์ค‘๊ฐ„ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ž๋™ ์ƒ์„ฑ\nmkdir -m 750 secure_dir # ๊ถŒํ•œ ์ง€์ •ํ•ด ์ƒ์„ฑ",
69
+ "common_mistake": "๋ถ€๋ชจ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์—†๋Š” ๊ฒฝ๋กœ์— `mkdir`๋งŒ ์“ฐ๋ฉด ์—๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค. `-p`๋ฅผ ๋ถ™์ด๋ฉด ๋ถ€๋ชจ๊นŒ์ง€ ์ž๋™ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
70
+ "keywords": [
71
+ "mkdir",
72
+ "-p",
73
+ "directory",
74
+ "create"
75
+ ],
76
+ "source_id": "linux-bash-manual",
77
+ "details": [
78
+ "`mkdir -p a/b/c`๋Š” a, a/b, a/b/c๋ฅผ ํ•œ ๋ฒˆ์— ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ์—์„œ ๊ฒฝ๋กœ ์ค€๋น„ ์‹œ ํ•„์ˆ˜ ์˜ต์…˜์ž…๋‹ˆ๋‹ค.",
79
+ "`-m ๋ชจ๋“œ`๋กœ ์ƒ์„ฑ๊ณผ ๋™์‹œ์— ๊ถŒํ•œ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: `mkdir -m 700 private`.",
80
+ "์‹œํ—˜ ํŒจํ„ด: ์‹ค์Šต์—์„œ ํŠน์ • ๊ฒฝ๋กœ๋ฅผ ๋งŒ๋“ค๊ณ  ์ด์–ด์„œ ๊ถŒํ•œยท์†Œ์œ ์ž๋ฅผ ์„ค์ •ํ•˜๋Š” ํ๋ฆ„์œผ๋กœ ์ž์ฃผ ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค."
81
+ ]
82
+ },
83
+ {
84
+ "id": "linux-touch",
85
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
86
+ "title": "touch โ€” ํŒŒ์ผ ์ƒ์„ฑ / ํƒ€์ž„์Šคํƒฌํ”„ ๊ฐฑ์‹ ",
87
+ "summary": "๋นˆ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, ์ด๋ฏธ ์žˆ๋Š” ํŒŒ์ผ์˜ ์ˆ˜์ • ์‹œ๊ฐ„์„ ํ˜„์žฌ ์‹œ๊ฐ์œผ๋กœ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.",
88
+ "example": "touch newfile.txt\ntouch -t 202401010000 old.txt # ํŠน์ • ์‹œ๊ฐ์œผ๋กœ ์„ค์ •",
89
+ "common_mistake": "์ด๋ฏธ ์žˆ๋Š” ํŒŒ์ผ์— `touch`๋ฅผ ์“ฐ๏ฟฝ๏ฟฝ ๋‚ด์šฉ์„ ์ง€์šฐ์ง€ ์•Š๊ณ  ์‹œ๊ฐ„ ์ •๋ณด๋งŒ ๊ฐฑ์‹ ๋ฉ๋‹ˆ๋‹ค.",
90
+ "keywords": [
91
+ "touch",
92
+ "timestamp",
93
+ "create",
94
+ "mtime"
95
+ ],
96
+ "source_id": "linux-bash-manual",
97
+ "details": [
98
+ "ํŒŒ์ผ์ด ์—†์œผ๋ฉด ๋นˆ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ์žˆ์œผ๋ฉด atime(์ ‘๊ทผ)๊ณผ mtime(์ˆ˜์ •) ์‹œ๊ฐ์„ ํ˜„์žฌ๋กœ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.",
99
+ "`-t YYYYMMDDhhmm` ํ˜•ํƒœ๋กœ ์›ํ•˜๋Š” ์‹œ๊ฐ์„ ์ง์ ‘ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.",
100
+ "์‹œํ—˜ ํŒจํ„ด: ํŒŒ์ผ ์ƒ์„ฑ ํ›„ `ls -l`๋กœ ์กด์žฌยท์‹œ๊ฐ ํ™•์ธ, ๋˜๋Š” `find -newer`๋กœ ์ตœ๊ทผ ํŒŒ์ผ ๊ฒ€์ƒ‰ํ•˜๋Š” ํ๋ฆ„๊ณผ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค."
101
+ ]
102
+ },
103
+ {
104
+ "id": "linux-cat",
105
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
106
+ "title": "cat โ€” ํŒŒ์ผ ๋‚ด์šฉ ์ถœ๋ ฅ",
107
+ "summary": "ํŒŒ์ผ ๋‚ด์šฉ์„ ํ‘œ์ค€ ์ถœ๋ ฅ์œผ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์œผ๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค ๋•Œ๋„ ์”๋‹ˆ๋‹ค.",
108
+ "example": "cat /etc/hostname\ncat file1.txt file2.txt > merged.txt\ncat -n file.txt # ์ค„ ๋ฒˆํ˜ธ ํ‘œ์‹œ",
109
+ "common_mistake": "ํฐ ํŒŒ์ผ์€ `cat` ๋Œ€์‹  `less`(์Šคํฌ๋กค), `head`/`tail`(์ผ๋ถ€๋งŒ)์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
110
+ "keywords": [
111
+ "cat",
112
+ "stdout",
113
+ "concatenate",
114
+ "-n"
115
+ ],
116
+ "source_id": "linux-bash-manual",
117
+ "details": [
118
+ "`cat`์€ ํŒŒ์ผ ์ „์ฒด๋ฅผ ํ•œ ๋ฒˆ์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `/etc/passwd`, `/etc/hosts` ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ ๋น ๋ฅด๊ฒŒ ๋ณผ ๋•Œ ์”๋‹ˆ๋‹ค.",
119
+ "`cat file1 file2 > combined`๋กœ ๋‘ ํŒŒ์ผ์„ ํ•ฉ์นฉ๋‹ˆ๋‹ค. `>>` ์‚ฌ์šฉ ์‹œ ๊ธฐ์กด ํŒŒ์ผ์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.",
120
+ "์‹œํ—˜ ํŒจํ„ด: `cat > file << EOF โ€ฆ EOF` ํ˜•ํƒœ์˜ here-doc์œผ๋กœ ํŒŒ์ผ์„ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š” ํŒจํ„ด๋„ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
121
+ ]
122
+ },
123
+ {
124
+ "id": "linux-grep",
125
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
126
+ "title": "grep โ€” ํ…์ŠคํŠธ ํŒจํ„ด ๊ฒ€์ƒ‰",
127
+ "summary": "ํŒŒ์ผ์ด๋‚˜ ํ‘œ์ค€ ์ž…๋ ฅ์—์„œ ํŒจํ„ด์ด ์žˆ๋Š” ์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ ๋ถ„์„๊ณผ ์„ค์ • ํŒŒ์ผ ๊ฒ€์ƒ‰์— ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค.",
128
+ "example": "grep 'ERROR' /var/log/syslog\ngrep -rn 'PermitRootLogin' /etc/ssh/\ngrep -v '^#' /etc/ssh/sshd_config # ์ฃผ์„ ์ œ์™ธ",
129
+ "common_mistake": "`grep`์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋ถ„ ์—†์ด ์ฐพ์œผ๋ ค๋ฉด `-i`๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.",
130
+ "keywords": [
131
+ "grep",
132
+ "-r",
133
+ "-n",
134
+ "-i",
135
+ "-v",
136
+ "pattern",
137
+ "regex"
138
+ ],
139
+ "source_id": "linux-grep-manual",
140
+ "details": [
141
+ "์ฃผ์š” ์˜ต์…˜: `-r`(์žฌ๊ท€), `-n`(์ค„๋ฒˆํ˜ธ), `-i`(๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œ), `-v`(์ œ์™ธ), `-l`(ํŒŒ์ผ๋ช…๋งŒ), `-c`(๊ฐœ์ˆ˜).",
142
+ "`grep -r 'failed' /var/log/`๋กœ ๋กœ๊ทธ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ „์ฒด์—์„œ ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
143
+ "์‹œํ—˜ ํŒจํ„ด: `ps aux | grep nginx`์ฒ˜๋Ÿผ ํŒŒ์ดํ”„์™€ ํ•จ๊ป˜, ๋˜๋Š” ์„ค์ • ํŒŒ์ผ์—์„œ ํŠน์ • ๊ฐ’์„ ์ฐพ๋Š” ๋ณตํ•ฉ ๋ฌธ์ œ๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
144
+ ]
145
+ },
146
+ {
147
+ "id": "linux-chmod",
148
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
149
+ "title": "chmod โ€” ํŒŒ์ผ ๊ถŒํ•œ ๋ณ€๊ฒฝ",
150
+ "summary": "ํŒŒ์ผยท๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ์ฝ๊ธฐ(r=4), ์“ฐ๊ธฐ(w=2), ์‹คํ–‰(x=1) ๊ถŒํ•œ์„ ์†Œ์œ ์žยท๊ทธ๋ฃนยท๊ธฐํƒ€ ์‚ฌ์šฉ์ž๋ณ„๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.",
151
+ "example": "chmod 755 script.sh # rwxr-xr-x\nchmod 644 config.conf # rw-r--r--\nchmod u+x,g-w file.txt # ์‹ฌ๋ณผ๋ฆญ ๋ฐฉ์‹",
152
+ "common_mistake": "8์ง„์ˆ˜ ๋ฐฉ์‹: 7=rwx, 6=rw-, 5=r-x, 4=r--. `chmod 777`์€ ๋ชจ๋“  ์‚ฌ์šฉ์ž์— ๋ชจ๋“  ๊ถŒํ•œ โ€” ๋ณด์•ˆ์ƒ ์œ„ํ—˜ํ•ฉ๋‹ˆ๋‹ค.",
153
+ "keywords": [
154
+ "chmod",
155
+ "permissions",
156
+ "rwx",
157
+ "octal",
158
+ "symbolic"
159
+ ],
160
+ "source_id": "ubuntu-server-docs",
161
+ "details": [
162
+ "8์ง„์ˆ˜ ํ‘œ๊ธฐ: ์ฒซ์งธ ์ž๋ฆฌ=์†Œ์œ ์ž(u), ๋‘˜์งธ=๊ทธ๋ฃน(g), ์…‹์งธ=๊ธฐํƒ€(o). ๊ฐ ์ž๋ฆฌ๋Š” r(4)+w(2)+x(1) ํ•ฉ์‚ฐ์ž…๋‹ˆ๋‹ค.",
163
+ "์‹ฌ๋ณผ๋ฆญ ํ‘œ๊ธฐ: `u+x`(์†Œ์œ ์ž์— ์‹คํ–‰ ์ถ”๊ฐ€), `go-w`(๊ทธ๋ฃนยท๊ธฐํƒ€ ์“ฐ๊ธฐ ์ œ๊ฑฐ), `a=r`(๋ชจ๋‘ ์ฝ๊ธฐ๋งŒ).",
164
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ๊ถŒํ•œ ๋น„ํŠธ๋ฅผ ๋ณด๊ณ  8์ง„์ˆ˜ ์ˆซ์ž๋ฅผ ๋งž์ถ”๊ฑฐ๋‚˜, ์š”๊ตฌ ์กฐ๊ฑด์— ๋งž๋Š” chmod ๋ช…๋ น์„ ๊ณ ๋ฅด๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
165
+ ]
166
+ },
167
+ {
168
+ "id": "linux-chown",
169
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
170
+ "title": "chown โ€” ์†Œ์œ ์žยท๊ทธ๋ฃน ๋ณ€๊ฒฝ",
171
+ "summary": "ํŒŒ์ผยท๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ์†Œ์œ ์ž์™€ ๊ทธ๋ฃน์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. `chmod`๋Š” ๊ถŒํ•œ ๋น„ํŠธ, `chown`์€ ์†Œ์œ ์ž๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ์œผ๋กœ ์—ญํ• ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.",
172
+ "example": "sudo chown ubuntu:ubuntu /srv/app\nchown -R www-data:www-data /var/www/html\nchown :developers project/ # ๊ทธ๋ฃน๋งŒ ๋ณ€๊ฒฝ",
173
+ "common_mistake": "`chown user file`์€ ์†Œ์œ ์ž๋งŒ, `chown user:group file`์€ ์†Œ์œ ์ž์™€ ๊ทธ๋ฃน์„ ํ•จ๊ป˜ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.",
174
+ "keywords": [
175
+ "chown",
176
+ "owner",
177
+ "group",
178
+ "-R",
179
+ "sudo"
180
+ ],
181
+ "source_id": "ubuntu-user-management",
182
+ "details": [
183
+ "`chown ์‚ฌ์šฉ์ž:๊ทธ๋ฃน ํŒŒ์ผ` ํ˜•์‹. `:๊ทธ๋ฃน`๋งŒ ์“ฐ๋ฉด ๊ทธ๋ฃน๋งŒ ๋ณ€๊ฒฝ. `-R`์€ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.",
184
+ "์›น ์„œ๋ฒ„ ์„ค์ •์—์„œ `/var/www/html`์˜ ์†Œ์œ ์ž๋ฅผ `www-data`๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ํŒจํ„ด์ด ์ž์ฃผ ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค.",
185
+ "์‹œํ—˜ ํŒจํ„ด: `chown`๊ณผ `chmod`๋ฅผ ์กฐํ•ฉํ•ด ํŠน์ • ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•˜๋„๋ก ์„ค์ •ํ•˜๋Š” ์‹ค์Šต ๋ฌธ์ œ์— ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
186
+ ]
187
+ },
188
+ {
189
+ "id": "linux-umask",
190
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
191
+ "title": "umask โ€” ๊ธฐ๋ณธ ๊ถŒํ•œ ๋งˆ์Šคํฌ",
192
+ "summary": "์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผยท๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ๊ธฐ๋ณธ ๊ถŒํ•œ์—์„œ ์ฐจ๊ฐํ•  ๋น„ํŠธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ umask 022๋Š” ํŒŒ์ผ 644, ๋””๋ ‰ํ„ฐ๋ฆฌ 755๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค.",
193
+ "example": "umask # ํ˜„์žฌ ๋งˆ์Šคํฌ ํ™•์ธ\numask 027 # ํŒŒ์ผ 640, ๋””๋ ‰ํ„ฐ๋ฆฌ 750\numask -S # ์‹ฌ๋ณผ๋ฆญ ํ‘œ๊ธฐ๋กœ ํ™•์ธ",
194
+ "common_mistake": "umask ๊ฐ’์€ ๊ถŒํ•œ์ด ์•„๋‹ˆ๋ผ '๋นผ๋Š” ๊ฐ’'์ž…๋‹ˆ๋‹ค. umask 022 โ†’ ํŒŒ์ผ ๊ธฐ๋ณธ 666-022=644, ๋””๋ ‰ํ„ฐ๋ฆฌ 777-022=755.",
195
+ "keywords": [
196
+ "umask",
197
+ "default permissions",
198
+ "mask",
199
+ "022"
200
+ ],
201
+ "source_id": "ubuntu-server-docs",
202
+ "details": [
203
+ "ํŒŒ์ผ ๊ธฐ๋ณธ ์ตœ๋Œ€ ๊ถŒํ•œ์€ 666, ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” 777. ์—ฌ๊ธฐ์„œ umask๋ฅผ ๋นผ๋ฉด ์‹ค์ œ ์ƒ์„ฑ ๊ถŒํ•œ์ด ๋ฉ๋‹ˆ๋‹ค.",
204
+ "์‚ฌ์šฉ์ž๋ณ„ umask๋Š” `~/.bashrc` ๋˜๋Š” `/etc/profile`์—์„œ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ์ „์ฒด๋Š” `/etc/login.defs`.",
205
+ "์‹œํ—˜ ํŒจํ„ด: umask ๊ฐ’์„ ์ฃผ๊ณ  ํŒŒ์ผ ์ƒ์„ฑ ์‹œ ๊ถŒํ•œ์ด ๋ฌด์—‡์ธ์ง€, ๋˜๋Š” ์›ํ•˜๋Š” ๊ธฐ๋ณธ ๊ถŒํ•œ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ umask ๊ฐ’์„ ๋ฌป์Šต๋‹ˆ๋‹ค."
206
+ ]
207
+ },
208
+ {
209
+ "id": "linux-find",
210
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
211
+ "title": "find โ€” ํŒŒ์ผ ๊ฒ€์ƒ‰",
212
+ "summary": "์ด๋ฆ„, ํฌ๊ธฐ, ์ˆ˜์ • ์‹œ๊ฐ, ๊ถŒํ•œ, ์†Œ์œ ์ž ๋“ฑ ๋‹ค์–‘ํ•œ ์กฐ๊ฑด์œผ๋กœ ํŒŒ์ผ์‹œ์Šคํ…œ์—์„œ ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.",
213
+ "example": "find /etc -name '*.conf'\nfind /var/log -mtime -1 # ํ•˜๋ฃจ ์ด๋‚ด ์ˆ˜์ •\nfind / -perm /4000 -type f # SUID ํŒŒ์ผ",
214
+ "common_mistake": "`find -name '*.conf'`์—์„œ ๋”ฐ์˜ดํ‘œ๋ฅผ ๋น ๋œจ๋ฆฌ๋ฉด ์‰˜์ด glob์„ ๋จผ์ € ํ•ด์„ํ•ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.",
215
+ "keywords": [
216
+ "find",
217
+ "-name",
218
+ "-type",
219
+ "-mtime",
220
+ "-perm",
221
+ "-exec"
222
+ ],
223
+ "source_id": "linux-bash-manual",
224
+ "details": [
225
+ "์ฃผ์š” ์กฐ๊ฑด: `-name`(์ด๋ฆ„), `-type f/d`(ํŒŒ์ผ/๋””๋ ‰ํ„ฐ๋ฆฌ), `-size +10M`(ํฌ๊ธฐ), `-mtime -7`(7์ผ ์ด๋‚ด ์ˆ˜์ •), `-user`(์†Œ์œ ์ž).",
226
+ "`-exec ๋ช…๋ น {} \\;`๋กœ ์ฐพ์€ ํŒŒ์ผ์— ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค: `find . -name '*.tmp' -exec rm {} \\;`.",
227
+ "์‹œํ—˜ ํŒจํ„ด: SUID/SGID ํŒŒ์ผ ์ฐพ๊ธฐ(`-perm /4000`), ํŠน์ • ์‚ฌ์šฉ์ž ํŒŒ์ผ ์ฐพ๊ธฐ(`-user`), ์˜ค๋ž˜๋œ ํŒŒ์ผ ์ •๋ฆฌ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
228
+ ]
229
+ },
230
+ {
231
+ "id": "linux-pipe-redirect",
232
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
233
+ "title": "ํŒŒ์ดํ”„์™€ ๋ฆฌ๋‹ค์ด๋ ‰์…˜",
234
+ "summary": "ํŒŒ์ดํ”„(`|`)๋Š” ๋ช…๋ น์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ๋ช…๋ น์˜ ์ž…๋ ฅ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๊ณ , ๋ฆฌ๋‹ค์ด๋ ‰์…˜(`>`, `>>`, `<`)์€ ์ถœ๋ ฅยท์ž…๋ ฅ์„ ํŒŒ์ผ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
235
+ "example": "cat /etc/passwd | grep root\nls -la > filelist.txt\necho 'append' >> log.txt\ncommand 2>&1 | tee output.log",
236
+ "common_mistake": "`>`๋Š” ํŒŒ์ผ์„ ๋ฎ์–ด์“ฐ๊ณ , `>>`๋Š” ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ํŒŒ์ผ์— ์‹ค์ˆ˜๋กœ `>`๋ฅผ ์“ฐ๋ฉด ๋‚ด์šฉ์ด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.",
237
+ "keywords": [
238
+ "pipe",
239
+ "redirect",
240
+ ">",
241
+ ">>",
242
+ "2>&1",
243
+ "stdin",
244
+ "stdout"
245
+ ],
246
+ "source_id": "linux-bash-cd",
247
+ "details": [
248
+ "stdout ๋ฆฌ๋‹ค์ด๋ ‰์…˜: `>` ๋ฎ์–ด์“ฐ๊ธฐ, `>>` ์ถ”๊ฐ€. stderr ๋ฆฌ๋‹ค์ด๋ ‰์…˜: `2>`. `2>&1`์€ stderr๋ฅผ stdout์œผ๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค.",
249
+ "`tee` ๋ช…๋ น์€ stdout์„ ํ™”๋ฉด๊ณผ ํŒŒ์ผ์— ๋™์‹œ์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค: `command | tee log.txt`.",
250
+ "์‹œํ—˜ ํŒจํ„ด: ๋ช…๋ น ์ถœ๋ ฅ์„ ํŒŒ์ผ์— ์ €์žฅํ•˜๊ฑฐ๋‚˜, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ์— ๊ธฐ๋กํ•˜๋Š” ๋ณตํ•ฉ ํŒŒ์ดํ”„๋ผ์ธ ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
251
+ ]
252
+ },
253
+ {
254
+ "id": "linux-tar",
255
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
256
+ "title": "tar โ€” ์•„์นด์ด๋ธŒ์™€ ์••์ถ•",
257
+ "summary": "์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ํ•˜๋‚˜์˜ ์•„์นด์ด๋ธŒ๋กœ ๋ฌถ๊ฑฐ๋‚˜ ์••์ถ•ํ•ฉ๋‹ˆ๋‹ค. `-c`(์ƒ์„ฑ), `-x`(ํ•ด์ œ), `-z`(gzip), `-j`(bzip2)๋ฅผ ์กฐํ•ฉํ•ด ์”๋‹ˆ๋‹ค.",
258
+ "example": "tar czf backup.tar.gz /etc\ntar xzf backup.tar.gz -C /tmp\ntar tvf archive.tar.gz # ๋‚ด์šฉ ๋ชฉ๋ก",
259
+ "common_mistake": "`c`(create)์™€ `x`(extract)๋ฅผ ํ˜ผ๋™ํ•˜์ง€ ๋งˆ์„ธ์š”. `-C`๋กœ ์••์ถ• ํ•ด์ œ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ํ˜„์žฌ ์œ„์น˜์— ํ’€๋ฆฝ๋‹ˆ๋‹ค.",
260
+ "keywords": [
261
+ "tar",
262
+ "-czf",
263
+ "-xzf",
264
+ "gzip",
265
+ "archive",
266
+ "backup"
267
+ ],
268
+ "source_id": "linux-bash-manual",
269
+ "details": [
270
+ "์˜ต์…˜ ์˜๋ฏธ: `c`=์ƒ์„ฑ, `x`=ํ•ด์ œ, `t`=๋ชฉ๋ก, `v`=์ƒ์„ธ์ถœ๋ ฅ, `f`=ํŒŒ์ผ์ง€์ •, `z`=gzip, `j`=bzip2, `J`=xz.",
271
+ "๋ฐฑ์—… ํŒจํ„ด: `tar czf /backup/etc-$(date +%Y%m%d).tar.gz /etc`๋กœ ๋‚ ์งœ ํฌํ•จ ๋ฐฑ์—… ํŒŒ์ผ ์ƒ์„ฑ.",
272
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์••์ถ•ํ•˜๊ฑฐ๋‚˜ ์ง€์ • ์œ„์น˜์— ํ’€์–ด๋‚ด๋Š” ๋ช…๋ น์„ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
273
+ ]
274
+ },
275
+ {
276
+ "id": "linux-process-ps-kill",
277
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
278
+ "title": "ps, kill โ€” ํ”„๋กœ์„ธ์Šค ํ™•์ธ๊ณผ ์ข…๋ฃŒ",
279
+ "summary": "`ps`๋กœ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์กฐํšŒํ•˜๊ณ , `kill`๋กœ ๏ฟฝ๏ฟฝ๊ทธ๋„์„ ๋ณด๋‚ด ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.",
280
+ "example": "ps aux | grep nginx\nkill -15 1234 # SIGTERM (์ •์ƒ ์ข…๋ฃŒ ์š”์ฒญ)\nkill -9 1234 # SIGKILL (๊ฐ•์ œ ์ข…๋ฃŒ)\nkillall nginx # ์ด๋ฆ„์œผ๋กœ ์ข…๋ฃŒ",
281
+ "common_mistake": "`kill -9`๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ •๋ฆฌ ๊ณผ์ • ์—†์ด ์ฆ‰์‹œ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋จผ์ € `-15`(SIGTERM)์„ ์‹œ๋„ํ•˜๊ณ , ์‘๋‹ต ์—†์„ ๋•Œ๋งŒ `-9`๋ฅผ ์”๋‹ˆ๋‹ค.",
282
+ "keywords": [
283
+ "ps",
284
+ "kill",
285
+ "SIGTERM",
286
+ "SIGKILL",
287
+ "PID",
288
+ "aux"
289
+ ],
290
+ "source_id": "linux-bash-manual",
291
+ "details": [
292
+ "`ps aux`: a=๋ชจ๋“  ์‚ฌ์šฉ์ž, u=์‚ฌ์šฉ์ž/CPU/๋ฉ”๋ชจ๋ฆฌ ์ •๋ณด, x=ํ„ฐ๋ฏธ๋„ ์—†๋Š” ํ”„๋กœ์„ธ์Šค ํฌํ•จ. PID๊ฐ€ ๋‘ ๋ฒˆ์งธ ์ปฌ๋Ÿผ์ž…๋‹ˆ๋‹ค.",
293
+ "์ฃผ์š” ์‹œ๊ทธ๋„: 1(SIGHUP ์žฌ์‹œ์ž‘), 9(SIGKILL ๊ฐ•์ œ์ข…๋ฃŒ), 15(SIGTERM ์ •์ƒ์ข…๋ฃŒ ์š”์ฒญ), 2(SIGINT Ctrl+C).",
294
+ "์‹œํ—˜ ํŒจํ„ด: `ps aux | grep ํ”„๋กœ์„ธ์Šค๋ช…`์œผ๋กœ PID๋ฅผ ์ฐพ๊ณ  `kill`๋กœ ์ข…๋ฃŒํ•˜๋Š” ๋‘ ๋‹จ๊ณ„ ํ๋ฆ„์ด ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
295
+ ]
296
+ },
297
+ {
298
+ "id": "linux-systemctl",
299
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
300
+ "title": "systemctl โ€” ์„œ๋น„์Šค ๊ด€๋ฆฌ",
301
+ "summary": "systemd ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค๋ฅผ ์‹œ์ž‘ยท์ค‘์ง€ยท์žฌ์‹œ์ž‘ํ•˜๊ณ , ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. Ubuntu 16.04 ์ดํ›„ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค.",
302
+ "example": "systemctl start nginx\nsystemctl enable --now ssh # ์ฆ‰์‹œ ์‹œ์ž‘ + ๋ถ€ํŒ… ์ž๋™ ์‹œ์ž‘\nsystemctl status nginx\nsystemctl list-units --type=service",
303
+ "common_mistake": "`enable`์€ ๋ถ€ํŒ… ์ž๋™ ์‹œ์ž‘ ์„ค์ •, `start`๋Š” ์ง€๊ธˆ ๋‹น์žฅ ์‹œ์ž‘. ๋‘˜ ๋‹ค ํ•˜๋ ค๋ฉด `enable --now`๋ฅผ ์”๋‹ˆ๋‹ค.",
304
+ "keywords": [
305
+ "systemctl",
306
+ "enable",
307
+ "disable",
308
+ "start",
309
+ "stop",
310
+ "status",
311
+ "systemd"
312
+ ],
313
+ "source_id": "linux-systemd-systemctl",
314
+ "details": [
315
+ "์ฃผ์š” ์„œ๋ธŒ์ปค๋งจ๋“œ: `start/stop/restart/reload`(์‹คํ–‰ ์ œ์–ด), `enable/disable`(๋ถ€ํŒ… ์ž๋™ ์‹œ์ž‘), `status`(์ƒํƒœ ํ™•์ธ), `is-active/is-enabled`(์กฐ๊ฑด ํ™•์ธ).",
316
+ "`systemctl status ์„œ๋น„์Šค`๋Š” ํ˜„์žฌ ์ƒํƒœ, ์ตœ๊ทผ ๋กœ๊ทธ, PID, ์‹คํ–‰ ์‹œ๊ฐ„์„ ํ•จ๊ป˜ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
317
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์„œ๋น„์Šค๋ฅผ '์ง€๊ธˆ ์‹œ์ž‘ํ•˜๊ณ  ๋ถ€ํŒ…์—๋„ ์ž๋™ ์‹œ์ž‘'ํ•˜๋„๋ก ์„ค์ •ํ•˜๊ฑฐ๋‚˜, ์„œ๋น„์Šค ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
318
+ ]
319
+ },
320
+ {
321
+ "id": "linux-journalctl",
322
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
323
+ "title": "journalctl โ€” systemd ๋กœ๊ทธ ์กฐํšŒ",
324
+ "summary": "systemd journal์— ๊ธฐ๋ก๋œ ์‹œ์Šคํ…œ ๋กœ๊ทธ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ณ„, ์‹œ๊ฐ„๋ณ„, ์šฐ์„ ์ˆœ์œ„๋ณ„ ํ•„ํ„ฐ๋ง์ด ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
325
+ "example": "journalctl -u nginx --since today\njournalctl -p err -n 50\njournalctl -f # ์‹ค์‹œ๊ฐ„ ์ถ”์ \njournalctl --disk-usage",
326
+ "common_mistake": "`journalctl`์€ journal์— ์ €์žฅ๋œ ๋กœ๊ทธ๋งŒ ๋ด…๋‹ˆ๋‹ค. `/var/log/` ํ…์ŠคํŠธ ํŒŒ์ผ ๋กœ๊ทธ๋Š” `cat`/`tail`๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
327
+ "keywords": [
328
+ "journalctl",
329
+ "-u",
330
+ "-f",
331
+ "--since",
332
+ "-p",
333
+ "systemd",
334
+ "journal"
335
+ ],
336
+ "source_id": "linux-systemd-systemctl",
337
+ "details": [
338
+ "์ฃผ์š” ์˜ต์…˜: `-u ์„œ๋น„์Šค`(์„œ๋น„์Šค๋ณ„), `-p err`(์—๋Ÿฌ ์ด์ƒ), `--since '2024-01-01'`(์‹œ๊ฐ„ ๋ฒ”์œ„), `-n 100`(์ตœ๊ทผ 100์ค„), `-f`(์‹ค์‹œ๊ฐ„).",
339
+ "`journalctl -b`๋Š” ํ˜„์žฌ ๋ถ€ํŒ… ์ดํ›„ ๋กœ๊ทธ, `-b -1`์€ ์ด์ „ ๋ถ€ํŒ… ๋กœ๊ทธ. ์žฌ๋ถ€ํŒ… ํ›„ ์˜ค๋ฅ˜ ์ถ”์ ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
340
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์„œ๋น„์Šค์˜ ์˜ค๋ฅ˜ ๋กœ๊ทธ๋ฅผ ์ฐพ๊ฑฐ๋‚˜, ํŠน์ • ์‹œ๊ฐ„ ์ดํ›„ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฌธ์ œ๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
341
+ ]
342
+ },
343
+ {
344
+ "id": "linux-ip-ss",
345
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
346
+ "title": "ip, ss โ€” ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์†Œ์ผ“ ํ™•์ธ",
347
+ "summary": "`ip`๋กœ ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šคยท๋ผ์šฐํŒ…ยทIP ์„ค์ •์„ ํ™•์ธํ•˜๊ณ , `ss`๋กœ ํ˜„์žฌ ์—ด๋ฆฐ ํฌํŠธ์™€ ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.",
348
+ "example": "ip addr show\nip route show\nss -tlnp # TCP ๋ฆฌ์Šค๋‹ ํฌํŠธ์™€ ํ”„๋กœ์„ธ์Šค\nss -s # ์†Œ์ผ“ ํ†ต๊ณ„ ์š”์•ฝ",
349
+ "common_mistake": "`ifconfig`์™€ `netstat`์€ ๊ตฌ์‹ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” `ip`์™€ `ss`๊ฐ€ ํ‘œ์ค€์ด๋ฉฐ ์‹œํ—˜์—์„œ๋„ ์ด์ชฝ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.",
350
+ "keywords": [
351
+ "ip",
352
+ "ss",
353
+ "addr",
354
+ "route",
355
+ "netstat",
356
+ "socket",
357
+ "-tlnp"
358
+ ],
359
+ "source_id": "linux-man7-ip",
360
+ "details": [
361
+ "`ip addr`์€ ์ธํ„ฐํŽ˜์ด์Šค๋ณ„ IP, `ip route`๋Š” ๋ผ์šฐํŒ… ํ…Œ์ด๋ธ”, `ip link`๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
362
+ "`ss -tlnp`: t=TCP, l=listening, n=์ˆซ์žํ‘œ์‹œ, p=ํ”„๋กœ์„ธ์Šค๋ช…. ์–ด๋–ค ์„œ๋น„์Šค๊ฐ€ ์–ด๋А ํฌํŠธ๋ฅผ ์—ด์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
363
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์„œ๋น„์Šค๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ํฌํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ฑฐ๋‚˜, IP ์„ค์ • ์ƒํƒœ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
364
+ ]
365
+ },
366
+ {
367
+ "id": "linux-df-du-mount",
368
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
369
+ "title": "df, du, mount โ€” ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ ๋งˆ์šดํŠธ",
370
+ "summary": "`df`๋Š” ํŒŒ์ผ์‹œ์Šคํ…œ ์ „์ฒด ์šฉ๋Ÿ‰, `du`๋Š” ํŠน์ • ๋””๋ ‰ํ„ฐ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, `mount`๋Š” ๋ธ”๋ก ์žฅ์น˜๋ฅผ ํŒŒ์ผ์‹œ์Šคํ…œ ํŠธ๋ฆฌ์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
371
+ "example": "df -h\ndu -sh /var/log/*\nmount /dev/sdb1 /mnt/data\nfindmnt # ๋งˆ์šดํŠธ ํŠธ๋ฆฌ ํ™•์ธ",
372
+ "common_mistake": "`df`๋Š” ํŒŒ์ผ์‹œ์Šคํ…œ ์ „์ฒด, `du`๋Š” ํŠน์ • ๊ฒฝ๋กœ์˜ ์‹ค์ œ ์‚ฌ์šฉ ํฌ๊ธฐ. ๋‘ ์ˆซ์ž๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์‚ญ์ œ๋์ง€๋งŒ ์—ด๋ฆฐ ํŒŒ์ผ ๋“ฑ).",
373
+ "keywords": [
374
+ "df",
375
+ "du",
376
+ "mount",
377
+ "-h",
378
+ "-sh",
379
+ "filesystem"
380
+ ],
381
+ "source_id": "ubuntu-storage",
382
+ "details": [
383
+ "`df -h`๋Š” ๋ชจ๋“  ๋งˆ์šดํŠธ ํŒŒํ‹ฐ์…˜์˜ ์‚ฌ์šฉ๋ฅ ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋””์Šคํฌ ํ’€ ๊ฒฝ๊ณ  ์‹œ ๋จผ์ € ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์ž…๋‹ˆ๋‹ค.",
384
+ "`du -sh /๊ฒฝ๋กœ/*`๋กœ ์–ด๋А ํ•˜์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•˜๋Š”์ง€ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
385
+ "์‹œํ—˜ ํŒจํ„ด: ๋””์Šคํฌ ์‚ฌ์šฉ๋ฅ  ํ™•์ธ, ์ž„์‹œ ๋งˆ์šดํŠธ ๋ช…๋ น ์ž‘์„ฑ, `findmnt`๋กœ ๋งˆ์šดํŠธ ์ •๋ณด ํ™•์ธํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
386
+ ]
387
+ },
388
+ {
389
+ "id": "linux-useradd",
390
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
391
+ "title": "useradd โ€” ์‚ฌ์šฉ์ž ์ถ”๊ฐ€",
392
+ "summary": "์ƒˆ ์‚ฌ์šฉ์ž ๊ณ„์ •์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. `-m`์œผ๋กœ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ž๋™ ์ƒ์„ฑ, `-s`๋กœ ๋กœ๊ทธ์ธ ์‰˜, `-G`๋กœ ์ถ”๊ฐ€ ๊ทธ๋ฃน์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
393
+ "example": "sudo useradd -m -s /bin/bash -G sudo alice\nuseradd -r -s /usr/sbin/nologin svcuser # ์„œ๋น„์Šค ๊ณ„์ •\npasswd alice # ์ƒ์„ฑ ํ›„ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •",
394
+ "common_mistake": "`useradd`๋งŒ ์“ฐ๋ฉด ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `-m`์„ ๋ฐ˜๋“œ์‹œ ๋ถ™์ด๊ฑฐ๋‚˜ `adduser`(๋Œ€ํ™”ํ˜•)๋ฅผ ์”๋‹ˆ๋‹ค.",
395
+ "keywords": [
396
+ "useradd",
397
+ "-m",
398
+ "-s",
399
+ "-G",
400
+ "adduser",
401
+ "home directory"
402
+ ],
403
+ "source_id": "ubuntu-user-management",
404
+ "details": [
405
+ "์ฃผ์š” ์˜ต์…˜: `-m`(ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ), `-d ๊ฒฝ๋กœ`(ํ™ˆ ๊ฒฝ๋กœ ์ง€์ •), `-s ์‰˜`(๋กœ๊ทธ์ธ ์‰˜), `-G ๊ทธ๋ฃน`(์ถ”๊ฐ€ ๊ทธ๋ฃน), `-r`(์‹œ์Šคํ…œ ๊ณ„์ •).",
406
+ "์„œ๋น„์Šค ๊ณ„์ •์€ `-r -s /usr/sbin/nologin`์œผ๋กœ ์ƒ์„ฑํ•ด ๋กœ๊ทธ์ธ์„ ๋ง‰๊ณ  ์‹œ์Šคํ…œ ์„œ๋น„์Šค ์ „์šฉ์œผ๋กœ ์”๋‹ˆ๋‹ค.",
407
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์กฐ๊ฑด(ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ, ๊ทธ๋ฃน, ์‰˜)์„ ๋งŒ์กฑํ•˜๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•˜๋Š” ์‹ค์Šต ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
408
+ ]
409
+ },
410
+ {
411
+ "id": "linux-usermod",
412
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
413
+ "title": "usermod โ€” ์‚ฌ์šฉ์ž ์ •๋ณด ๋ณ€๊ฒฝ",
414
+ "summary": "๊ธฐ์กด ์‚ฌ์šฉ์ž ๊ณ„์ •์˜ ๊ทธ๋ฃน, ์‰˜, ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ, ์ž ๊ธˆ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.",
415
+ "example": "usermod -aG docker alice # docker ๊ทธ๋ฃน ์ถ”๊ฐ€\nusermod -s /bin/zsh alice # ์‰˜ ๋ณ€๊ฒฝ\nusermod -L alice # ๊ณ„์ • ์ž ๊ธˆ\nusermod -U alice # ์ž ๊ธˆ ํ•ด์ œ",
416
+ "common_mistake": "`-G`๋งŒ ์“ฐ๋ฉด ๊ธฐ์กด ๊ทธ๋ฃน์ด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ฃน์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ `-aG`(append)๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
417
+ "keywords": [
418
+ "usermod",
419
+ "-aG",
420
+ "-L",
421
+ "-U",
422
+ "-s",
423
+ "group",
424
+ "lock"
425
+ ],
426
+ "source_id": "ubuntu-user-management",
427
+ "details": [
428
+ "`-aG ๊ทธ๋ฃน`์€ ๊ธฐ์กด ๊ทธ๋ฃน์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๊ทธ๋ฃน์„ ์ถ”๊ฐ€. ๊ทธ๋ฃน ๋ณ€๊ฒฝ ํ›„ ์žฌ๋กœ๊ทธ์ธํ•ด์•ผ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
429
+ "`-L`(lock) / `-U`(unlock)๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•ž์— `!`๋ฅผ ๋ถ™์—ฌ ๋กœ๊ทธ์ธ์„ ์ฐจ๋‹จํ•˜๊ฑฐ๋‚˜ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.",
430
+ "์‹œํ—˜ ํŒจํ„ด: ์‚ฌ์šฉ์ž๋ฅผ ํŠน์ • ๊ทธ๋ฃน์— ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ณ„์ •์„ ์ž ๊ธˆํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
431
+ ]
432
+ },
433
+ {
434
+ "id": "linux-passwd",
435
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
436
+ "title": "passwd โ€” ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ณ„์ • ์ž ๊ธˆ",
437
+ "summary": "์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๊ณ„์ • ๋งŒ๋ฃŒยท์ž ๊ธˆ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. root๋Š” ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
438
+ "example": "passwd # ํ˜„์žฌ ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ\nsudo passwd alice # alice ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ\npasswd -l alice # ๊ณ„์ • ์ž ๊ธˆ\nchage -l alice # ๋งŒ๋ฃŒ ์ •๋ณด ํ™•์ธ",
439
+ "common_mistake": "`passwd -l`๋กœ ์ž ๊ทผ ๊ณ„์ •๋„ SSH ํ‚ค ์ธ์ฆ์œผ๋กœ๋Š” ์ ‘์† ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์™„์ „ ์ฐจ๋‹จ์€ `/etc/shadow`์˜ ์‰˜์„ nologin์œผ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
440
+ "keywords": [
441
+ "passwd",
442
+ "-l",
443
+ "-u",
444
+ "-e",
445
+ "chage",
446
+ "password",
447
+ "lock"
448
+ ],
449
+ "source_id": "ubuntu-user-management",
450
+ "details": [
451
+ "`passwd -l`์€ `/etc/shadow`์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•ž์— `!`๋ฅผ ๋ถ™์—ฌ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.",
452
+ "`chage -M 90 user`๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ตœ๋Œ€ ์‚ฌ์šฉ ๊ธฐ๊ฐ„, `chage -E ๋‚ ์งœ`๋กœ ๊ณ„์ • ๋งŒ๋ฃŒ์ผ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.",
453
+ "์‹œํ—˜ ํŒจํ„ด: ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ, ๊ณ„์ • ์ž ๊ธˆ/ํ•ด์ œ, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งŒ๋ฃŒ ์ •์ฑ… ์„ค์ • ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
454
+ ]
455
+ },
456
+ {
457
+ "id": "linux-groups-id",
458
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
459
+ "title": "id, groups โ€” ์‚ฌ์šฉ์žยท๊ทธ๋ฃน ํ™•์ธ",
460
+ "summary": "`id`๋Š” ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ UID, GID, ์†Œ์† ๊ทธ๋ฃน์„ ๋ณด์—ฌ์ฃผ๊ณ , `groups`๋Š” ๊ทธ๋ฃน ๋ชฉ๋ก๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
461
+ "example": "id\n# uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),27(sudo)\nid alice\ngroups alice",
462
+ "common_mistake": "`usermod -aG`๋กœ ๊ทธ๋ฃน์„ ์ถ”๊ฐ€ํ•œ ๋’ค `id`์— ๋ฐ˜์˜๋˜๋ ค๋ฉด ์žฌ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. `newgrp ๊ทธ๋ฃน๋ช…`์œผ๋กœ ์ฆ‰์‹œ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
463
+ "keywords": [
464
+ "id",
465
+ "groups",
466
+ "UID",
467
+ "GID",
468
+ "whoami"
469
+ ],
470
+ "source_id": "ubuntu-user-management",
471
+ "details": [
472
+ "`id` ์ถœ๋ ฅ์—์„œ uid(์‚ฌ์šฉ์ž ๋ฒˆํ˜ธ), gid(๊ธฐ๋ณธ ๊ทธ๋ฃน), groups(๋ชจ๋“  ์†Œ์† ๊ทธ๋ฃน)๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
473
+ "root์˜ uid์™€ gid๋Š” 0์ž…๋‹ˆ๋‹ค. `id -u`๋กœ ์ˆซ์ž๋งŒ, `id -un`์œผ๋กœ ์ด๋ฆ„๋งŒ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
474
+ "์‹œํ—˜ ํŒจํ„ด: ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๊ทธ๋ฃน์— ์†ํ•ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ฑฐ๋‚˜, uid/gid๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
475
+ ]
476
+ },
477
+ {
478
+ "id": "linux-sudoers",
479
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
480
+ "title": "sudo์™€ sudoers โ€” ๊ถŒํ•œ ์œ„์ž„",
481
+ "summary": "์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋ช…๋ น์„ root ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. `/etc/sudoers`๋Š” ๋ฐ˜๋“œ์‹œ `visudo`๋กœ ํŽธ์ง‘ํ•ฉ๋‹ˆ๋‹ค.",
482
+ "example": "sudo apt update\nvisudo # /etc/sudoers ์•ˆ์ „ ํŽธ์ง‘\n# /etc/sudoers.d/alice ๋‚ด์šฉ:\nalice ALL=(ALL) NOPASSWD: /usr/bin/systemctl",
483
+ "common_mistake": "`/etc/sudoers`๋ฅผ ์ง์ ‘ vi๋กœ ์—ด์–ด ์ˆ˜์ •ํ•˜๋ฉด ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ์‹œ sudo๊ฐ€ ์™„์ „ํžˆ ์ž ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ `visudo`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
484
+ "keywords": [
485
+ "sudo",
486
+ "sudoers",
487
+ "visudo",
488
+ "NOPASSWD",
489
+ "ALL"
490
+ ],
491
+ "source_id": "ubuntu-user-management",
492
+ "details": [
493
+ "`sudo` ๊ทธ๋ฃน ๋˜๋Š” wheel ๊ทธ๋ฃน์— ์†ํ•œ ์‚ฌ์šฉ์ž๋Š” ๊ธฐ๋ณธ์œผ๋กœ sudo ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค: `usermod -aG sudo alice`.",
494
+ "`/etc/sudoers.d/` ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๊ฐœ๋ณ„ ํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ๋ฉ”์ธ sudoers๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๊ถŒํ•œ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
495
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์‚ฌ์šฉ์ž์—๊ฒŒ ํŠน์ • ๋ช…๋ น๋งŒ sudo ํ—ˆ์šฉํ•˜๊ฑฐ๋‚˜, NOPASSWD ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
496
+ ]
497
+ },
498
+ {
499
+ "id": "linux-acl",
500
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
501
+ "title": "ACL โ€” ํ™•์žฅ ์ ‘๊ทผ ์ œ์–ด",
502
+ "summary": "๊ธฐ๋ณธ ์œ ๋‹‰์Šค ๊ถŒํ•œ(์†Œ์œ ์ž/๊ทธ๋ฃน/๊ธฐํƒ€)๋งŒ์œผ๋กœ๋Š” ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋Š” ์„ธ๋ฐ€ํ•œ ๊ถŒํ•œ์„ ํŠน์ • ์‚ฌ์šฉ์ž๋‚˜ ๊ทธ๋ฃน๋ณ„๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
503
+ "example": "getfacl /srv/project\nsetfacl -m u:alice:rwx /srv/project\nsetfacl -m g:dev:r-x /srv/project\nsetfacl -R -m u:bob:r-- /data # ์žฌ๊ท€",
504
+ "common_mistake": "ACL์ด ์„ค์ •๋œ ํŒŒ์ผ์€ `ls -l`์—์„œ ๊ถŒํ•œ ๋’ค์— `+`๊ฐ€ ๋ถ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๋ชจ๋ฅด๋ฉด ๊ถŒํ•œ ์„ค์ •์ด ๋๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.",
505
+ "keywords": [
506
+ "ACL",
507
+ "setfacl",
508
+ "getfacl",
509
+ "-m",
510
+ "-R",
511
+ "access control"
512
+ ],
513
+ "source_id": "ubuntu-server-docs",
514
+ "details": [
515
+ "`setfacl -m u:์‚ฌ์šฉ์ž:rwx ๊ฒฝ๋กœ`๋กœ ํŠน์ • ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ถŒํ•œ ์ถ”๊ฐ€. `getfacl`๋กœ ํ˜„์žฌ ACL ํ™•์ธ.",
516
+ "๊ธฐ๋ณธ ACL(`setfacl -d`)์„ ์„ค์ •ํ•˜๋ฉด ํ•ด๋‹น ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ์— ์ž๋™์œผ๋กœ ACL์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
517
+ "์‹œํ—˜ ํŒจํ„ด: ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋‹ค๋ฅธ ๊ถŒํ•œ์„ ์ฃผ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ chmod ๋Œ€์‹  setfacl์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
518
+ ]
519
+ },
520
+ {
521
+ "id": "linux-special-perms",
522
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
523
+ "title": "ํŠน์ˆ˜ ๊ถŒํ•œ โ€” SUID, SGID, Sticky Bit",
524
+ "summary": "SUID(4)๋Š” ํŒŒ์ผ ์†Œ์œ ์ž ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰, SGID(2)๋Š” ๊ทธ๋ฃน ๊ถŒํ•œ ์ƒ์†, Sticky Bit(1)์€ ๋ณธ์ธ ํŒŒ์ผ๋งŒ ์‚ญ์ œ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.",
525
+ "example": "chmod u+s /usr/bin/passwd # SUID\nchmod g+s /srv/shared # SGID (๊ณต์œ  ๋””๋ ‰ํ„ฐ๋ฆฌ)\nchmod +t /tmp # Sticky bit\nls -la /tmp # drwxrwxrwt ์—์„œ t ํ™•์ธ",
526
+ "common_mistake": "SUID๊ฐ€ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ถ™์œผ๋ฉด ์˜๋ฏธ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. SGID๊ฐ€ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ถ™์œผ๋ฉด ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ์ด ๋ถ€๋ชจ ๊ทธ๋ฃน์„ ์ƒ์†ํ•ฉ๋‹ˆ๋‹ค.",
527
+ "keywords": [
528
+ "SUID",
529
+ "SGID",
530
+ "sticky bit",
531
+ "chmod 4755",
532
+ "chmod 1777"
533
+ ],
534
+ "source_id": "ubuntu-server-docs",
535
+ "details": [
536
+ "SUID ์„ค์ •๋œ ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋ฉด ์‹คํ–‰ํ•œ ์‚ฌ๋žŒ์ด ์•„๋‹ˆ๋ผ ํŒŒ์ผ ์†Œ์œ ์ž ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. `/usr/bin/passwd`๊ฐ€ ๋Œ€ํ‘œ ์˜ˆ์ž…๋‹ˆ๋‹ค.",
537
+ "SGID๋ฅผ ๊ณต์œ  ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์„ค์ •ํ•˜๋ฉด ๊ทธ ์•ˆ์— ๋งŒ๋“  ํŒŒ์ผ๋“ค์ด ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ทธ๋ฃน์„ ์ž๋™์œผ๋กœ ์ƒ์†ํ•ด ํ˜‘์—…์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
538
+ "์‹œํ—˜ ํŒจํ„ด: `ls -l`์—์„œ `s`, `S`, `t`, `T` ํ‘œ์‹œ๋ฅผ ๋ณด๊ณ  ์–ด๋–ค ํŠน์ˆ˜ ๊ถŒํ•œ์ธ์ง€, ๋˜๋Š” 8์ง„์ˆ˜ 4์ž๋ฆฌ ํ‘œ๊ธฐ๋ฅผ ํ•ด์„ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
539
+ ]
540
+ },
541
+ {
542
+ "id": "linux-ssh",
543
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
544
+ "title": "SSH โ€” ์›๊ฒฉ ์ ‘์†",
545
+ "summary": "SSH(Secure Shell)๋กœ ์›๊ฒฉ ์„œ๋ฒ„์— ์•”ํ˜ธํ™”๋œ ์—ฐ๊ฒฐ์„ ๋งบ์Šต๋‹ˆ๋‹ค. ์„ค์ • ํŒŒ์ผ์€ ์„œ๋ฒ„ ์ชฝ `/etc/ssh/sshd_config`, ํด๋ผ์ด์–ธํŠธ ์ชฝ `~/.ssh/config`์ž…๋‹ˆ๋‹ค.",
546
+ "example": "ssh alice@192.168.1.10\nssh -p 2222 user@server\nssh -i ~/.ssh/mykey.pem ubuntu@host\nsystemctl restart sshd # ์„ค์ • ๋ณ€๊ฒฝ ํ›„ ์žฌ์‹œ์ž‘",
547
+ "common_mistake": "`sshd_config` ๋ณ€๊ฒฝ ํ›„ `systemctl reload sshd`๋ฅผ ํ•ด์•ผ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ˆ˜์ • ์ „ `sshd -t`๋กœ ๋ฌธ๋ฒ• ๊ฒ€์‚ฌ๋ฅผ ๋จผ์ € ํ•˜์„ธ์š”.",
548
+ "keywords": [
549
+ "ssh",
550
+ "sshd",
551
+ "sshd_config",
552
+ "-p",
553
+ "-i",
554
+ "PermitRootLogin"
555
+ ],
556
+ "source_id": "ubuntu-networking",
557
+ "details": [
558
+ "`sshd_config` ์ฃผ์š” ์„ค์ •: `Port`(ํฌํŠธ ๋ณ€๊ฒฝ), `PermitRootLogin no`(root ์ง์ ‘ ์ ‘์† ์ฐจ๋‹จ), `PasswordAuthentication no`(ํ‚ค ์ธ์ฆ๋งŒ ํ—ˆ์šฉ).",
559
+ "`~/.ssh/config`์— `Host`, `HostName`, `User`, `IdentityFile`์„ ์ •์˜ํ•˜๋ฉด `ssh ๋ณ„์นญ`์œผ๋กœ ๊ฐ„๋‹จํžˆ ์ ‘์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
560
+ "์‹œํ—˜ ํŒจํ„ด: root ๋กœ๊ทธ์ธ ์ฐจ๋‹จ, ํฌํŠธ ๋ณ€๊ฒฝ, ํ‚ค ์ธ์ฆ ๊ฐ•์ œ ๋“ฑ sshd_config ๋ณด์•ˆ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
561
+ ]
562
+ },
563
+ {
564
+ "id": "linux-ssh-key",
565
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
566
+ "title": "SSH ํ‚ค ์ธ์ฆ",
567
+ "summary": "๋น„๋ฐ€๋ฒˆํ˜ธ ์—†์ด ๊ณต๊ฐœํ‚ค/๊ฐœ์ธํ‚ค ์Œ์œผ๋กœ SSH ์ ‘์†ํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ์ธํ‚ค๋Š” ํด๋ผ์ด์–ธํŠธ์—, ๊ณต๊ฐœํ‚ค๋Š” ์„œ๋ฒ„์˜ `~/.ssh/authorized_keys`์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.",
568
+ "example": "ssh-keygen -t ed25519 -C 'work key'\nssh-copy-id alice@server # ๊ณต๊ฐœํ‚ค ์ž๋™ ๋ณต์‚ฌ\ncat ~/.ssh/id_ed25519.pub # ๊ณต๊ฐœํ‚ค ํ™•์ธ",
569
+ "common_mistake": "`~/.ssh/` ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” 700, `authorized_keys`๋Š” 600 ๊ถŒํ•œ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œ์ด ๋„ˆ๋ฌด ์—ด๋ ค ์žˆ์œผ๋ฉด SSH๊ฐ€ ํ‚ค๋ฅผ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.",
570
+ "keywords": [
571
+ "ssh-keygen",
572
+ "authorized_keys",
573
+ "ed25519",
574
+ "public key",
575
+ "private key"
576
+ ],
577
+ "source_id": "ubuntu-networking",
578
+ "details": [
579
+ "`ssh-keygen -t ed25519`๋กœ ์ตœ์‹  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ‚ค ์ƒ์„ฑ. ์ƒ์„ฑ๋œ ๊ณต๊ฐœํ‚ค(`.pub`)๋ฅผ ์„œ๋ฒ„ `~/.ssh/authorized_keys`์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.",
580
+ "๋””๋ ‰ํ„ฐ๋ฆฌ/ํŒŒ์ผ ๊ถŒํ•œ: `~/.ssh`=700, `authorized_keys`=600, ๊ฐœ์ธํ‚ค=600. ์ด๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ ‘์† ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.",
581
+ "์‹œํ—˜ ํŒจํ„ด: ํ‚ค ์ƒ์„ฑ, ๊ณต๊ฐœํ‚ค ๋ฐฐํฌ, authorized_keys ๊ถŒํ•œ ์„ค์ •๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์‹ค์Šต ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
582
+ ]
583
+ },
584
+ {
585
+ "id": "linux-hostname-hosts",
586
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
587
+ "title": "hostname๊ณผ /etc/hosts",
588
+ "summary": "`hostname`์œผ๋กœ ํ˜„์žฌ ํ˜ธ์ŠคํŠธ๋ช…์„ ํ™•์ธํ•˜๊ณ , `/etc/hosts`๋กœ DNS ์—†์ด IP-์ด๋ฆ„ ๋งคํ•‘์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.",
589
+ "example": "hostname\nhostnamectl set-hostname webserver01\ncat /etc/hosts\necho '192.168.1.10 db.internal' >> /etc/hosts",
590
+ "common_mistake": "`hostname ์ด๋ฆ„`์€ ์ž„์‹œ ๋ณ€๊ฒฝ์ž…๋‹ˆ๋‹ค. ์žฌ๋ถ€ํŒ… ํ›„์—๋„ ์œ ์ง€ํ•˜๋ ค๋ฉด `hostnamectl set-hostname`์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
591
+ "keywords": [
592
+ "hostname",
593
+ "hostnamectl",
594
+ "/etc/hosts",
595
+ "FQDN"
596
+ ],
597
+ "source_id": "ubuntu-networking",
598
+ "details": [
599
+ "`hostnamectl set-hostname ์ด๋ฆ„`์€ `/etc/hostname`์„ ์ˆ˜์ •ํ•ด ์˜๊ตฌ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. `hostnamectl status`๋กœ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
600
+ "`/etc/hosts`๋Š” DNS๋ณด๋‹ค ๋จผ์ € ์กฐํšŒ๋ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€๋ง ์„œ๋ฒ„ ์ด๋ฆ„ ๋งคํ•‘์ด๋‚˜ DNS ์ฐจ๋‹จ ํ…Œ์ŠคํŠธ์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
601
+ "์‹œํ—˜ ํŒจํ„ด: ์„œ๋ฒ„์˜ ํ˜ธ์ŠคํŠธ๋ช…์„ ์˜๊ตฌ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, /etc/hosts์— ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
602
+ ]
603
+ },
604
+ {
605
+ "id": "linux-dns-resolve",
606
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
607
+ "title": "DNS ์กฐํšŒ โ€” dig, nslookup, resolvectl",
608
+ "summary": "๋„๋ฉ”์ธ ์ด๋ฆ„์„ IP๋กœ ํ•ด์„ํ•˜๋Š” ๊ณผ์ •์„ ํ™•์ธํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•ฉ๋‹ˆ๋‹ค. DNS ์„œ๋ฒ„๋Š” `/etc/resolv.conf` ๋˜๋Š” `systemd-resolved`๊ฐ€ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
609
+ "example": "dig google.com\nnslookup google.com\nresolvectl status\ncat /etc/resolv.conf",
610
+ "common_mistake": "Ubuntu์—์„œ `/etc/resolv.conf`๋Š” `systemd-resolved`์˜ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ์ž…๋‹ˆ๋‹ค. DNS ์„œ๋ฒ„ ๋ณ€๊ฒฝ์€ `resolvectl`์ด๋‚˜ netplan์—์„œ ํ•ฉ๋‹ˆ๋‹ค.",
611
+ "keywords": [
612
+ "dig",
613
+ "nslookup",
614
+ "resolv.conf",
615
+ "nameserver",
616
+ "systemd-resolved"
617
+ ],
618
+ "source_id": "ubuntu-networking",
619
+ "details": [
620
+ "`dig ๋„๋ฉ”์ธ` ๊ฒฐ๊ณผ์—์„œ ANSWER SECTION์˜ IP๊ฐ€ ์‹ค์ œ ํ•ด์„๋œ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. `dig +short ๋„๋ฉ”์ธ`์œผ๋กœ IP๋งŒ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
621
+ "DNS ์„œ๋ฒ„๋Š” `/etc/resolv.conf`์˜ `nameserver` ์ค„์—์„œ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. Ubuntu 22.04๋Š” 127.0.0.53(๋กœ์ปฌ stub)์ด ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค.",
622
+ "์‹œํ—˜ ํŒจํ„ด: DNS ์กฐํšŒ ๋ช…๋ น์œผ๋กœ ๋„๋ฉ”์ธ ํ•ด์„ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜, DNS ์„œ๋ฒ„ ์„ค์ • ํŒŒ์ผ ์œ„์น˜๋ฅผ ๋ฌป๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
623
+ ]
624
+ },
625
+ {
626
+ "id": "linux-route",
627
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
628
+ "title": "๋ผ์šฐํŒ… ํ™•์ธ โ€” ip route",
629
+ "summary": "ํŒจํ‚ท์ด ์–ด๋А ๊ฒฝ๋กœ๋กœ ๋‚˜๊ฐ€๋Š”์ง€ ๋ผ์šฐํŒ… ํ…Œ์ด๋ธ”์„ ํ™•์ธํ•˜๊ณ , ์ž„์‹œ ๋ผ์šฐํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.",
630
+ "example": "ip route show\nip route add 10.0.0.0/8 via 192.168.1.1\nip route del 10.0.0.0/8\nip route get 8.8.8.8 # ํŠน์ • IP๋กœ ๊ฐ€๋Š” ๊ฒฝ๋กœ",
631
+ "common_mistake": "`ip route add`๋กœ ์ถ”๊ฐ€ํ•œ ๋ผ์šฐํŠธ๋Š” ์žฌ๋ถ€ํŒ…ํ•˜๋ฉด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์˜๏ฟฝ๏ฟฝ ์„ค์ •์€ netplan ๋˜๋Š” `/etc/network/interfaces`์—์„œ ํ•ฉ๋‹ˆ๋‹ค.",
632
+ "keywords": [
633
+ "ip route",
634
+ "gateway",
635
+ "routing table",
636
+ "default",
637
+ "via"
638
+ ],
639
+ "source_id": "linux-man7-ip",
640
+ "details": [
641
+ "`ip route show`์—์„œ `default via ๊ฒŒ์ดํŠธ์›จ์ดIP dev ์ธํ„ฐํŽ˜์ด์Šค` ์ค„์ด ๊ธฐ๋ณธ ๊ฒŒ์ดํŠธ์›จ์ด์ž…๋‹ˆ๋‹ค.",
642
+ "`ip route get ๋ชฉ์ ์ง€IP`๋Š” ์‹ค์ œ๋กœ ๊ทธ IP์— ํŒจํ‚ท์„ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉํ•  ๊ฒฝ๋กœ์™€ ์ถœ๊ตฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
643
+ "์‹œํ—˜ ํŒจํ„ด: ๋ผ์šฐํŒ… ํ…Œ์ด๋ธ”์—์„œ ๊ธฐ๋ณธ ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋„คํŠธ์›Œํฌ๋กœ ์ž„์‹œ ๋ผ์šฐํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
644
+ ]
645
+ },
646
+ {
647
+ "id": "linux-netplan",
648
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
649
+ "title": "netplan โ€” ๋„คํŠธ์›Œํฌ ์„ค์ •",
650
+ "summary": "Ubuntu 18.04 ์ดํ›„ ๊ธฐ๋ณธ ๋„คํŠธ์›Œํฌ ์„ค์ • ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. `/etc/netplan/*.yaml` ํŒŒ์ผ์— ์„ค์ •ํ•˜๊ณ  `netplan apply`๋กœ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
651
+ "example": "# /etc/netplan/00-installer-config.yaml\nnetwork:\n ethernets:\n eth0:\n addresses: [192.168.1.100/24]\n gateway4: 192.168.1.1\nnetplan apply",
652
+ "common_mistake": "YAML์€ ๋“ค์—ฌ์“ฐ๊ธฐ(์ŠคํŽ˜์ด์Šค๋งŒ, ํƒญ ๊ธˆ์ง€)๊ฐ€ ๋งค์šฐ ์—„๊ฒฉํ•ฉ๋‹ˆ๋‹ค. ๋“ค์—ฌ์“ฐ๊ธฐ ์˜ค๋ฅ˜ ์‹œ `netplan apply`๊ฐ€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.",
653
+ "keywords": [
654
+ "netplan",
655
+ "yaml",
656
+ "static IP",
657
+ "gateway4",
658
+ "nameservers"
659
+ ],
660
+ "source_id": "ubuntu-networking",
661
+ "details": [
662
+ "`netplan generate`๋Š” ์„ค์ •์„ ๋ฐฑ์—”๋“œ ์„ค์ • ํŒŒ์ผ๋กœ ๋ณ€ํ™˜๋งŒ ํ•˜๊ณ , `netplan apply`๊ฐ€ ์‹ค์ œ ๋„คํŠธ์›Œํฌ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
663
+ "์ •์  IP ์„ค์ •: `addresses`, `gateway4/routes`, `nameservers.addresses`๋ฅผ YAML์— ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
664
+ "์‹œํ—˜ ํŒจํ„ด: ์ •์  IP๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ DNS ์„œ๋ฒ„๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” netplan YAML ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
665
+ ]
666
+ },
667
+ {
668
+ "id": "linux-curl",
669
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
670
+ "title": "curl โ€” HTTP ์š”์ฒญ๊ณผ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ",
671
+ "summary": "HTTP/HTTPS ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. API ํ…Œ์ŠคํŠธ, ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ, ์„œ๋น„์Šค ๋™์ž‘ ํ™•์ธ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
672
+ "example": "curl http://localhost:8080/health\ncurl -O https://example.com/file.tar.gz\ncurl -H 'Content-Type: application/json' -d '{\"key\":\"val\"}' -X POST http://api/endpoint\ncurl -I https://example.com # ํ—ค๋”๋งŒ",
673
+ "common_mistake": "`curl URL`์€ ๋‚ด์šฉ์„ stdout์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๋ ค๋ฉด `-o ํŒŒ์ผ๋ช…` ๋˜๋Š” URL๊ณผ ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์ €์žฅ์€ `-O`๋ฅผ ์”๋‹ˆ๋‹ค.",
674
+ "keywords": [
675
+ "curl",
676
+ "-O",
677
+ "-o",
678
+ "-H",
679
+ "-d",
680
+ "-X",
681
+ "-I",
682
+ "HTTP"
683
+ ],
684
+ "source_id": "linux-man7-ip",
685
+ "details": [
686
+ "์ฃผ์š” ์˜ต์…˜: `-o ํŒŒ์ผ`(์ €์žฅ), `-O`(์›๋ณธ ํŒŒ์ผ๋ช…), `-L`(๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋”ฐ๋ผ๊ฐ€๊ธฐ), `-k`(SSL ๊ฒ€์ฆ ๋ฌด์‹œ), `-s`(silent), `-v`(์ƒ์„ธ).",
687
+ "`curl -I URL`์€ HTTP ํ—ค๋”๋งŒ ํ™•์ธ. ์ƒํƒœ ์ฝ”๋“œ(200, 404, 301 ๋“ฑ)๋กœ ์„œ๋น„์Šค ์‘๋‹ต์„ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
688
+ "์‹œํ—˜ ํŒจํ„ด: ์„œ๋น„์Šค ๋™์ž‘ ํ™•์ธ, ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ, ๊ฐ„๋‹จํ•œ API ์š”์ฒญ ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
689
+ ]
690
+ },
691
+ {
692
+ "id": "linux-ping",
693
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
694
+ "title": "ping โ€” ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ™•์ธ",
695
+ "summary": "ICMP echo ์š”์ฒญ์œผ๋กœ ํ˜ธ์ŠคํŠธ์— ๋„๋‹ฌ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ ์ง„๋‹จ์˜ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.",
696
+ "example": "ping -c 4 8.8.8.8\nping -c 1 192.168.1.1 # ๊ฒŒ์ดํŠธ์›จ์ด ํ™•์ธ\nping6 ::1 # IPv6 ๋ฃจํ”„๋ฐฑ",
697
+ "common_mistake": "๋ฐฉํ™”๋ฒฝ์ด ICMP๋ฅผ ์ฐจ๋‹จํ•˜๋ฉด ping์ด ์‹คํŒจํ•ด๋„ ์‹ค์ œ๋กœ๋Š” ์—ฐ๊ฒฐ์ด ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ping ์‹คํŒจ = ์„œ๋น„์Šค ์žฅ์• ๋Š” ์•„๋‹™๋‹ˆ๋‹ค.",
698
+ "keywords": [
699
+ "ping",
700
+ "-c",
701
+ "ICMP",
702
+ "latency",
703
+ "packet loss"
704
+ ],
705
+ "source_id": "ubuntu-networking",
706
+ "details": [
707
+ "`-c ํšŸ์ˆ˜`๋กœ ๋ณด๋‚ผ ํŒจํ‚ท ์ˆ˜๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. ์ง€์ • ์•Š์œผ๋ฉด Ctrl+C ๋ˆ„๋ฅผ ๋•Œ๊นŒ์ง€ ๊ณ„์† ์ „์†กํ•ฉ๋‹ˆ๋‹ค.",
708
+ "์ถœ๋ ฅ์—์„œ `time=`์€ RTT(์™•๋ณต ์‹œ๊ฐ„), `packet loss %`๋Š” ํŒจํ‚ท ์†์‹ค๋ฅ . 100% loss๋ฉด ๊ฒฝ๋กœ ๋ฌธ์ œ ๋˜๋Š” ๋ฐฉํ™”๋ฒฝ ์ฐจ๋‹จ์ž…๋‹ˆ๋‹ค.",
709
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํ˜ธ์ŠคํŠธ์˜ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ, `-c` ์˜ต์…˜์œผ๋กœ ํŒจํ‚ท ์ˆ˜ ์ œํ•œํ•˜๋Š” ๋ช…๋ น์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
710
+ ]
711
+ },
712
+ {
713
+ "id": "linux-cron",
714
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
715
+ "title": "cron โ€” ์ž‘์—… ์Šค์ผ€์ค„๋ง",
716
+ "summary": "์ง€์ •ํ•œ ์‹œ๊ฐ์ด๋‚˜ ์ฃผ๊ธฐ์— ๋ช…๋ น์„ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. `crontab -e`๋กœ ์‚ฌ์šฉ์ž๋ณ„ ์Šค์ผ€์ค„์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
717
+ "example": "crontab -e # ํŽธ์ง‘\ncrontab -l # ๋ชฉ๋ก ํ™•์ธ\n# ๋งค์ผ ์ƒˆ๋ฒฝ 2์‹œ์— ๋ฐฑ์—…\n0 2 * * * /usr/local/bin/backup.sh\n# ๋งค 5๋ถ„๋งˆ๋‹ค\n*/5 * * * * /usr/local/bin/check.sh",
718
+ "common_mistake": "cron ์ž‘์—…์—์„œ๋Š” PATH๊ฐ€ ์ผ๋ฐ˜ ์‰˜๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋ช…๋ น์„ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์“ฐ๊ฑฐ๋‚˜ ์Šคํฌ๋ฆฝํŠธ ์•ˆ์—์„œ PATH๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜์„ธ์š”.",
719
+ "keywords": [
720
+ "cron",
721
+ "crontab",
722
+ "-e",
723
+ "-l",
724
+ "schedule",
725
+ "*/5"
726
+ ],
727
+ "source_id": "ubuntu-server-docs",
728
+ "details": [
729
+ "crontab ํ˜•์‹: `๋ถ„ ์‹œ ์ผ ์›” ์š”์ผ ๋ช…๋ น`. ๋ณ„ํ‘œ(*)๋Š” '๋ชจ๋“  ๊ฐ’', `*/5`๋Š” '5๋งˆ๋‹ค', `0,30`์€ '0๊ณผ 30'์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.",
730
+ "์‹œ์Šคํ…œ ์ „์ฒด cron์€ `/etc/cron.d/`, `/etc/cron.daily/` ๋“ฑ์—์„œ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `/var/log/syslog`์— ์‹คํ–‰ ๊ธฐ๋ก์ด ๋‚จ์Šต๋‹ˆ๋‹ค.",
731
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์ฃผ๊ธฐ(๋งค์ผ, ๋งค์‹œ๊ฐ„, ๋งค N๋ถ„)๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” crontab ํ•ญ๋ชฉ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
732
+ ]
733
+ },
734
+ {
735
+ "id": "linux-top",
736
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
737
+ "title": "top โ€” ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ ๋ชจ๋‹ˆํ„ฐ",
738
+ "summary": "CPUยท๋ฉ”๋ชจ๋ฆฌยทํ”„๋กœ์„ธ์Šค๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์“ฐ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
739
+ "example": "top\ntop -u alice # ํŠน์ • ์‚ฌ์šฉ์ž ํ”„๋กœ์„ธ์Šค๋งŒ\ntop -bn 1 # 1ํšŒ๋งŒ ์ถœ๋ ฅ (์Šคํฌ๋ฆฝํŠธ์šฉ)\nhtop # ๋” ๋ณด๊ธฐ ์ข‹์€ ๋Œ€์•ˆ",
740
+ "common_mistake": "`top` ์‹คํ–‰ ์ค‘ `k` ํ‚ค๋กœ PID๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž˜๋ชป ๋ˆ„๋ฅด์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•˜์„ธ์š”.",
741
+ "keywords": [
742
+ "top",
743
+ "CPU",
744
+ "memory",
745
+ "load average",
746
+ "%MEM",
747
+ "%CPU"
748
+ ],
749
+ "source_id": "ubuntu-server-docs",
750
+ "details": [
751
+ "์ƒ๋‹จ ์š”์•ฝ: load average(1๋ถ„/5๋ถ„/15๋ถ„ ํ‰๊ท  ๋ถ€ํ•˜), Tasks(ํ”„๋กœ์„ธ์Šค ์ˆ˜), %Cpu(us/sy/id), Mem/Swap ์‚ฌ์šฉ๋Ÿ‰.",
752
+ "์ปฌ๋Ÿผ ์˜๋ฏธ: PID, USER, %CPU, %MEM, RES(์‹ค์ œ ๋ฉ”๋ชจ๋ฆฌ), S(์ƒํƒœ: R=์‹คํ–‰/S=๋Œ€๊ธฐ/Z=์ข€๋น„).",
753
+ "์‹œํ—˜ ํŒจํ„ด: CPU๋‚˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” ํ”„๋กœ์„ธ์Šค ์ฐพ๊ธฐ, ๋กœ๋“œ ์• ๋ฒ„๋ฆฌ์ง€ ํ•ด์„, ํ”„๋กœ์„ธ์Šค ์ƒํƒœ ํ™•์ธ ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
754
+ ]
755
+ },
756
+ {
757
+ "id": "linux-nice",
758
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
759
+ "title": "nice, renice โ€” ํ”„๋กœ์„ธ์Šค ์šฐ์„ ์ˆœ์œ„",
760
+ "summary": "ํ”„๋กœ์„ธ์Šค์˜ CPU ์Šค์ผ€์ค„๋ง ์šฐ์„ ์ˆœ์œ„๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์ด ๋‚ฎ์„์ˆ˜๋ก(-20~+19) ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์œผ๋ฉฐ, ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์„ค์ •์€ root๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
761
+ "example": "nice -n 10 gzip big_file.gz # ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์‹œ์ž‘\nrenice -n -5 -p 1234 # ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ๋ณ€๊ฒฝ\nps -o pid,ni,cmd -p 1234 # ํ˜„์žฌ nice ๊ฐ’ ํ™•์ธ",
762
+ "common_mistake": "nice ๊ฐ’์ด ๋‚ฎ์„์ˆ˜๋ก ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค(-20์ด ์ตœ๊ณ ). ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๋Š” ํ˜„์žฌ๋ณด๋‹ค ๋‚ฎ๊ฒŒ(๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋กœ)๋งŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
763
+ "keywords": [
764
+ "nice",
765
+ "renice",
766
+ "priority",
767
+ "niceness",
768
+ "-20",
769
+ "+19"
770
+ ],
771
+ "source_id": "ubuntu-server-docs",
772
+ "details": [
773
+ "๊ธฐ๋ณธ nice ๊ฐ’์€ 0. ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฐฐ์น˜ ์ž‘์—…์€ `nice -n 15`์ฒ˜๋Ÿผ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์‹คํ–‰ํ•ด ์‹œ์Šคํ…œ ์˜ํ–ฅ์„ ์ค„์ž…๋‹ˆ๋‹ค.",
774
+ "`renice`๋กœ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. `-p PID`๋กœ ํ”„๋กœ์„ธ์Šค, `-u ์‚ฌ์šฉ์ž`๋กœ ์‚ฌ์šฉ์ž ์ „์ฒด๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.",
775
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • nice ๊ฐ’์œผ๋กœ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
776
+ ]
777
+ },
778
+ {
779
+ "id": "linux-lsblk",
780
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
781
+ "title": "lsblk โ€” ๋ธ”๋ก ์žฅ์น˜ ํ™•์ธ",
782
+ "summary": "๋””์Šคํฌ, ํŒŒํ‹ฐ์…˜, LVM ๋“ฑ ๋ธ”๋ก ์žฅ์น˜์™€ ๋งˆ์šดํŠธ ์ •๋ณด๋ฅผ ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
783
+ "example": "lsblk\nlsblk -f # ํŒŒ์ผ์‹œ์Šคํ…œ ํƒ€์ž…๊ณผ UUID ํฌํ•จ\nblkid # UUID์™€ ํƒ€์ž… ์ƒ์„ธ ํ™•์ธ",
784
+ "common_mistake": "`lsblk`๋Š” ๋ธ”๋ก ์žฅ์น˜ ๊ตฌ์กฐ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , `df -h`๋Š” ๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ์˜ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์—ญํ• ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.",
785
+ "keywords": [
786
+ "lsblk",
787
+ "blkid",
788
+ "partition",
789
+ "UUID",
790
+ "FSTYPE",
791
+ "block device"
792
+ ],
793
+ "source_id": "ubuntu-storage",
794
+ "details": [
795
+ "์ถœ๋ ฅ ์ปฌ๋Ÿผ: NAME(์žฅ์น˜๋ช…), SIZE, TYPE(disk/part/lvm), MOUNTPOINT. `sda`๋Š” ๋””์Šคํฌ, `sda1`์€ ์ฒซ ํŒŒํ‹ฐ์…˜.",
796
+ "`lsblk -f`๋Š” ํŒŒ์ผ์‹œ์Šคํ…œ ํƒ€์ž…(ext4, xfs ๋“ฑ)๊ณผ UUID๋ฅผ ํ•จ๊ป˜ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. `/etc/fstab` ์„ค์ • ์‹œ UUID๋ฅผ ์—ฌ๊ธฐ์„œ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.",
797
+ "์‹œํ—˜ ํŒจํ„ด: ๋งˆ์šดํŠธ ๊ฐ€๋Šฅํ•œ ํŒŒํ‹ฐ์…˜ ๋ชฉ๋ก ํ™•์ธ, UUID ์กฐํšŒ, ๋””์Šคํฌ ๊ตฌ์กฐ ํŒŒ์•… ๋ฌธ์ œ์—์„œ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค."
798
+ ]
799
+ },
800
+ {
801
+ "id": "linux-fstab",
802
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
803
+ "title": "/etc/fstab โ€” ์ž๋™ ๋งˆ์šดํŠธ ์„ค์ •",
804
+ "summary": "๋ถ€ํŒ… ์‹œ ์ž๋™์œผ๋กœ ๋งˆ์šดํŠธํ•  ํŒŒ์ผ์‹œ์Šคํ…œ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. UUID, ์žฅ์น˜๋ช…, ๋งˆ์šดํŠธ ํฌ์ธํŠธ, ํŒŒ์ผ์‹œ์Šคํ…œ ํƒ€์ž…, ์˜ต์…˜, dump/pass๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
805
+ "example": "# UUID๋ฅผ ์‚ฌ์šฉํ•œ fstab ํ•ญ๋ชฉ\nUUID=xxxx-yyyy /mnt/data ext4 defaults 0 2\n# ํ™•์ธ ๋ฐ ํ…Œ์ŠคํŠธ\nmount -a # fstab ์ „์ฒด ๋งˆ์šดํŠธ ์‹œ๋„",
806
+ "common_mistake": "fstab์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉด ๋ถ€ํŒ… ์ž์ฒด๊ฐ€ ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ํ›„ `mount -a`๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ , UUID๋Š” `blkid`๋กœ ํ™•์ธํ•˜์„ธ์š”.",
807
+ "keywords": [
808
+ "fstab",
809
+ "UUID",
810
+ "mount -a",
811
+ "defaults",
812
+ "auto mount"
813
+ ],
814
+ "source_id": "ubuntu-storage",
815
+ "details": [
816
+ "fstab ํ˜•์‹: `์žฅ์น˜ ๋งˆ์šดํŠธํฌ์ธํŠธ ํƒ€์ž… ์˜ต์…˜ dump pass`. dump=0(๋ฐฑ์—… ์ œ์™ธ), pass=0(๊ฒ€์‚ฌ ์•ˆ ํ•จ)/1(๋ฃจํŠธ)/2(๊ธฐํƒ€).",
817
+ "์žฅ์น˜ ์ง€์ •์€ UUID๊ฐ€ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. `/dev/sda1`์€ ๋””์Šคํฌ ์ถ”๊ฐ€ ์‹œ ์ด๋ฆ„์ด ๋ฐ”๋€” ์ˆ˜ ์žˆ์ง€๋งŒ UUID๋Š” ๊ณ ์ •์ž…๋‹ˆ๋‹ค.",
818
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํŒŒํ‹ฐ์…˜์„ ๋ถ€ํŒ… ์‹œ ์ž๋™ ๋งˆ์šดํŠธํ•˜๋„๋ก fstab์— ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์‹ค์Šต ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
819
+ ]
820
+ },
821
+ {
822
+ "id": "linux-swap",
823
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
824
+ "title": "swap โ€” ์Šค์™‘ ๊ด€๋ฆฌ",
825
+ "summary": "๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•  ๋•Œ ๋””์Šคํฌ ์ผ๋ถ€๋ฅผ ์ž„์‹œ ๋ฉ”๋ชจ๋ฆฌ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์Šค์™‘ ํŒŒํ‹ฐ์…˜๊ณผ ์Šค์™‘ ํŒŒ์ผ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค.",
826
+ "example": "swapon --show # ํ˜„์žฌ ์Šค์™‘ ํ™•์ธ\nfallocate -l 2G /swapfile\nchmod 600 /swapfile\nmkswap /swapfile\nswapon /swapfile",
827
+ "common_mistake": "์Šค์™‘ ํŒŒ์ผ์€ ๋ฐ˜๋“œ์‹œ root๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก `chmod 600`์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œ์ด ์—ด๋ ค ์žˆ์œผ๋ฉด `swapon`์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.",
828
+ "keywords": [
829
+ "swap",
830
+ "swapon",
831
+ "swapoff",
832
+ "mkswap",
833
+ "swapfile",
834
+ "/proc/swaps"
835
+ ],
836
+ "source_id": "ubuntu-storage",
837
+ "details": [
838
+ "์Šค์™‘ ํŒŒ์ผ ์ƒ์„ฑ ์ˆœ์„œ: `fallocate -l ํฌ๊ธฐ /swapfile` โ†’ `chmod 600` โ†’ `mkswap` โ†’ `swapon` โ†’ `/etc/fstab`์— ์ถ”๊ฐ€.",
839
+ "์˜๊ตฌ ํ™œ์„ฑํ™”: `/etc/fstab`์— `/swapfile none swap sw 0 0` ์ถ”๊ฐ€. `swapon -a`๋กœ fstab์˜ ์Šค์™‘์„ ๋ชจ๋‘ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.",
840
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํฌ๊ธฐ์˜ ์Šค์™‘ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ํ™œ์„ฑํ™”ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ์ž‘์„ฑํ•˜๋Š” ์‹ค์Šต ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
841
+ ]
842
+ },
843
+ {
844
+ "id": "linux-lvm",
845
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
846
+ "title": "LVM โ€” ๋…ผ๋ฆฌ ๋ณผ๋ฅจ ๊ด€๋ฆฌ",
847
+ "summary": "๋ฌผ๋ฆฌ ๋””์Šคํฌ(PV)๋ฅผ ๋ณผ๋ฅจ ๊ทธ๋ฃน(VG)์œผ๋กœ ๋ฌถ๊ณ , ๊ทธ ์•ˆ์— ์œ ์—ฐํ•˜๊ฒŒ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋…ผ๋ฆฌ ๋ณผ๋ฅจ(LV)์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
848
+ "example": "pvcreate /dev/sdb\nvgcreate datavg /dev/sdb\nlvcreate -L 10G -n datalv datavg\nmkfs.ext4 /dev/datavg/datalv\nlvextend -L +5G /dev/datavg/datalv && resize2fs /dev/datavg/datalv",
849
+ "common_mistake": "`lvextend`๋กœ LV ํฌ๊ธฐ๋ฅผ ๋Š˜๋ฆฐ ๋’ค ํŒŒ์ผ์‹œ์Šคํ…œ๋„ ๋Š˜๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ext4๋Š” `resize2fs`, xfs๋Š” `xfs_growfs`๋ฅผ ์”๋‹ˆ๋‹ค.",
850
+ "keywords": [
851
+ "LVM",
852
+ "pvcreate",
853
+ "vgcreate",
854
+ "lvcreate",
855
+ "lvextend",
856
+ "resize2fs"
857
+ ],
858
+ "source_id": "ubuntu-storage",
859
+ "details": [
860
+ "LVM 3๊ณ„์ธต: PV(๋ฌผ๋ฆฌ ๋ณผ๋ฅจ, ์‹ค์ œ ๋””์Šคํฌ/ํŒŒํ‹ฐ์…˜) โ†’ VG(๋ณผ๋ฅจ ๊ทธ๋ฃน) โ†’ LV(๋…ผ๋ฆฌ ๋ณผ๋ฅจ, ํŒŒ์ผ์‹œ์Šคํ…œ์ด ์˜ฌ๋ผ๊ฐ€๋Š” ๊ณณ).",
861
+ "ํ™•์žฅ ์ ˆ์ฐจ: `lvextend -L +ํฌ๊ธฐ /dev/VG/LV` ํ›„ `resize2fs /dev/VG/LV`(ext4) ๋˜๋Š” `xfs_growfs /๋งˆ์šดํŠธํฌ์ธํŠธ`(xfs).",
862
+ "์‹œํ—˜ ํŒจํ„ด: PVโ†’VGโ†’LV ์ƒ์„ฑ ์ˆœ์„œ, LV ํ™•์žฅ ํ›„ ํŒŒ์ผ์‹œ์Šคํ…œ ํ™•์žฅ๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์‹ค์Šต ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
863
+ ]
864
+ },
865
+ {
866
+ "id": "linux-logs-var",
867
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
868
+ "title": "/var/log โ€” ์‹œ์Šคํ…œ ๋กœ๊ทธ ํŒŒ์ผ",
869
+ "summary": "Linux์˜ ์ฃผ์š” ๋กœ๊ทธ ํŒŒ์ผ๋“ค์€ `/var/log/`์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ณ„, ๋ชฉ์ ๋ณ„๋กœ ํŒŒ์ผ์ด ๋‚˜๋‰˜์–ด ์žˆ์–ด ๋ฌธ์ œ ์ง„๋‹จ์˜ ์ถœ๋ฐœ์ ์ž…๋‹ˆ๋‹ค.",
870
+ "example": "tail -f /var/log/syslog\ngrep 'ERROR' /var/log/auth.log\nls -lh /var/log/\nzcat /var/log/syslog.1.gz # ํšŒ์ „๋œ ๋กœ๊ทธ",
871
+ "common_mistake": "`/var/log/syslog`๋Š” ์ผ๋ฐ˜ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€, `/var/log/auth.log`๋Š” ์ธ์ฆ ๊ด€๋ จ. journalctl์€ ์ด ํŒŒ์ผ์ด ์•„๋‹ˆ๋ผ journal์„ ์ฝ์Šต๋‹ˆ๋‹ค.",
872
+ "keywords": [
873
+ "syslog",
874
+ "auth.log",
875
+ "dmesg",
876
+ "/var/log",
877
+ "logrotate"
878
+ ],
879
+ "source_id": "ubuntu-server-docs",
880
+ "details": [
881
+ "์ฃผ์š” ํŒŒ์ผ: `syslog`(์ผ๋ฐ˜ ์‹œ์Šคํ…œ), `auth.log`(SSH/sudo/PAM ์ธ์ฆ), `kern.log`(์ปค๋„), `dpkg.log`(ํŒจํ‚ค์ง€ ์„ค์น˜/์ œ๊ฑฐ).",
882
+ "`logrotate`๊ฐ€ ์ž๋™์œผ๋กœ ๋กœ๊ทธ๋ฅผ ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋กœ๊ทธ๋Š” `.1`, `.2` ๋˜๋Š” `.gz`(์••์ถ•) ํ˜•ํƒœ๋กœ ๋ณด๊ด€๋ฉ๋‹ˆ๋‹ค.",
883
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์ด๋ฒคํŠธ(๋กœ๊ทธ์ธ ์‹คํŒจ, ์„œ๋น„์Šค ์˜ค๋ฅ˜)๋ฅผ ํ•ด๋‹น ๋กœ๊ทธ ํŒŒ์ผ์—์„œ ์ฐพ๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
884
+ ]
885
+ },
886
+ {
887
+ "id": "linux-dmesg",
888
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
889
+ "title": "dmesg โ€” ์ปค๋„ ๋ง ๋ฒ„ํผ ๋กœ๊ทธ",
890
+ "summary": "์ปค๋„์ด ๋ถ€ํŒ… ์‹œ์™€ ํ•˜๋“œ์›จ์–ด ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ๊ธฐ๋กํ•˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋””์Šคํฌ ์˜ค๋ฅ˜, ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค UP/DOWN ๋“ฑ ํ•˜๋“œ์›จ์–ด ๋ฌธ์ œ ์ง„๋‹จ์— ์”๋‹ˆ๋‹ค.",
891
+ "example": "dmesg | tail -20\ndmesg | grep -i error\ndmesg -T # ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž„์Šคํƒฌํ”„\ndmesg --level=err,warn",
892
+ "common_mistake": "`dmesg`๋Š” ์ปค๋„ ๋ ˆ๋ฒจ ๋ฉ”์‹œ์ง€๋งŒ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๋กœ๊ทธ๋Š” `journalctl -u ์„œ๋น„์Šค๋ช…`์œผ๋กœ ํ™•์ธํ•˜์„ธ์š”.",
893
+ "keywords": [
894
+ "dmesg",
895
+ "kernel",
896
+ "hardware",
897
+ "-T",
898
+ "--level",
899
+ "ring buffer"
900
+ ],
901
+ "source_id": "ubuntu-server-docs",
902
+ "details": [
903
+ "๋””์Šคํฌ I/O ์˜ค๋ฅ˜๋Š” `dmesg | grep -i 'error\\|fail\\|sda'`๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
904
+ "`dmesg -T`๋Š” ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฌ์šด ๋‚ ์งœ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์ถœ๋ ฅ์€ ๋ถ€ํŒ… ์ดํ›„ ์ดˆ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.",
905
+ "์‹œํ—˜ ํŒจํ„ด: ์‹œ์Šคํ…œ ๋ถ€ํŒ… ์˜ค๋ฅ˜๋‚˜ ํ•˜๋“œ์›จ์–ด ๋ฌธ์ œ๋ฅผ dmesg์—์„œ ์ฐพ๋Š” ๋ฌธ์ œ, ๋˜๋Š” ๋กœ๊ทธ ๋ ˆ๋ฒจ ํ•„ํ„ฐ๋ง ๋ช…๋ น์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
906
+ ]
907
+ },
908
+ {
909
+ "id": "linux-env-path",
910
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
911
+ "title": "ํ™˜๊ฒฝ ๋ณ€์ˆ˜์™€ PATH",
912
+ "summary": "ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ์‰˜๊ณผ ํ”„๋กœ์„ธ์Šค์— ์„ค์ •์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. PATH๋Š” ๋ช…๋ น์„ ์ฐพ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ชฉ๋ก์œผ๋กœ, ์ƒˆ ํ”„๋กœ๊ทธ๋žจ์„ ์„ค์น˜ ํ›„ PATH์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
913
+ "example": "echo $PATH\nexport PATH=$PATH:/opt/myapp/bin\nprintenv HOME USER\nenv | grep JAVA\nunset MYVAR",
914
+ "common_mistake": "`export VAR=๊ฐ’`์€ ํ˜„์žฌ ์‰˜๊ณผ ์ž์‹ ํ”„๋กœ์„ธ์Šค์—๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์˜๊ตฌ ์„ค์ •์€ `~/.bashrc` ๋˜๋Š” `/etc/environment`์— ์ถ”๊ฐ€ํ•˜์„ธ์š”.",
915
+ "keywords": [
916
+ "PATH",
917
+ "export",
918
+ "env",
919
+ "printenv",
920
+ "$HOME",
921
+ "~/.bashrc"
922
+ ],
923
+ "source_id": "linux-bash-cd",
924
+ "details": [
925
+ "`export ๋ณ€์ˆ˜=๊ฐ’`์œผ๋กœ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ž์‹ ํ”„๋กœ์„ธ์Šค์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์—†์ด `๋ณ€์ˆ˜=๊ฐ’`๋งŒ ํ•˜๋ฉด ํ˜„์žฌ ์‰˜์—๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
926
+ "์˜๊ตฌ ์„ค์ • ์œ„์น˜: ์‚ฌ์šฉ์ž๋ณ„์€ `~/.bashrc` ๋˜๋Š” `~/.profile`, ์‹œ์Šคํ…œ ์ „์ฒด๋Š” `/etc/environment` ๋˜๋Š” `/etc/profile`.",
927
+ "์‹œํ—˜ ํŒจํ„ด: PATH์— ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํ™•์ธ/์„ค์ •ํ•˜๋Š” ๋ช…๋ น, ์˜๊ตฌ ์„ค์ • ํŒŒ์ผ ์œ„์น˜๋ฅผ ๋ฌป๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
928
+ ]
929
+ },
930
+ {
931
+ "id": "linux-wildcard",
932
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
933
+ "title": "์™€์ผ๋“œ์นด๋“œ์™€ ํŒŒ์ผ ๊ธ€๋กœ๋น™",
934
+ "summary": "์‰˜์ด ํŒจํ„ด์„ ์‹ค์ œ ํŒŒ์ผ ์ด๋ฆ„์œผ๋กœ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. `*`(๋ชจ๋“  ๋ฌธ์ž), `?`(ํ•œ ๋ฌธ์ž), `[abc]`(๋ฌธ์ž ํด๋ž˜์Šค)๋ฅผ ์กฐํ•ฉํ•ด ์”๋‹ˆ๋‹ค.",
935
+ "example": "ls *.log\nrm /tmp/test?.txt # test1.txt, test2.txt ๋“ฑ\ncp /etc/[a-z]*.conf /backup/\nls {*.txt,*.md} # brace expansion",
936
+ "common_mistake": "์™€์ผ๋“œ์นด๋“œ๋Š” ์‰˜์ด ๋ช…๋ น ์‹คํ–‰ ์ „์— ํŒŒ์ผ ๋ชฉ๋ก์œผ๋กœ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. ๋งค์นญ๋˜๋Š” ํŒŒ์ผ์ด ์—†์œผ๋ฉด ๋ช…๋ น์ด ํŒจํ„ด ๋ฌธ์ž์—ด์„ ๊ทธ๋Œ€๋กœ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.",
937
+ "keywords": [
938
+ "wildcard",
939
+ "glob",
940
+ "*",
941
+ "?",
942
+ "[]",
943
+ "brace expansion"
944
+ ],
945
+ "source_id": "linux-bash-cd",
946
+ "details": [
947
+ "`*`๋Š” 0๊ฐœ ์ด์ƒ์˜ ์ž„์˜ ๋ฌธ์ž, `?`๋Š” ์ •ํ™•ํžˆ 1๊ฐœ ๋ฌธ์ž, `[0-9]`๋Š” ๋ฒ”์œ„ ์•ˆ์˜ ํ•œ ๋ฌธ์ž, `[!abc]`๋Š” ์ œ์™ธ์ž…๋‹ˆ๋‹ค.",
948
+ "Brace expansion: `file{1,2,3}.txt`๋Š” file1.txt file2.txt file3.txt๋กœ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์ด ์—†์–ด๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
949
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํŒจํ„ด์˜ ํŒŒ์ผ์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ์™€์ผ๋“œ์นด๋“œ ํ‘œํ˜„์‹์ด ์–ด๋–ค ํŒŒ์ผ์— ๋งค์นญ๋˜๋Š”์ง€ ํŒ๋‹จํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
950
+ ]
951
+ },
952
+ {
953
+ "id": "linux-exit-code",
954
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
955
+ "title": "exit code โ€” ๋ช…๋ น ์ข…๋ฃŒ ์ฝ”๋“œ",
956
+ "summary": "๋ช…๋ น ์‹คํ–‰ ํ›„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ˆซ์ž(0~255). 0์€ ์„ฑ๊ณต, 0์ด ์•„๋‹ˆ๋ฉด ์˜ค๋ฅ˜. ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ด์ „ ๋ช…๋ น ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ `$?`๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
957
+ "example": "ls /etc && echo '์„ฑ๊ณต'\nls /์—†๋Š”๊ฒฝ๋กœ || echo '์‹คํŒจ'\necho $?\ncommand; [ $? -eq 0 ] && echo ok",
958
+ "common_mistake": "`$?`๋Š” ๋ฐ”๋กœ ์ง์ „ ๋ช…๋ น์˜ ์ข…๋ฃŒ ์ฝ”๋“œ๋งŒ ๋‹ด์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ช…๋ น ์‹คํ–‰ ํ›„ ์ด์ „ `$?`๋ฅผ ์ฐธ์กฐํ•˜๋ ค๋ฉด ๋ณ€์ˆ˜์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
959
+ "keywords": [
960
+ "exit code",
961
+ "$?",
962
+ "&&",
963
+ "||",
964
+ "return code",
965
+ "0=success"
966
+ ],
967
+ "source_id": "linux-bash-cd",
968
+ "details": [
969
+ "`&&`๋Š” ์•ž ๋ช…๋ น์ด ์„ฑ๊ณต(exit 0)ํ•  ๋•Œ๋งŒ ๋’ค๋ฅผ ์‹คํ–‰. `||`๋Š” ์‹คํŒจ(exit โ‰ 0)ํ•  ๋•Œ๋งŒ ๋’ค๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
970
+ "์ž์ฃผ ๋ณด๋Š” ์ฝ”๋“œ: 0=์„ฑ๊ณต, 1=์ผ๋ฐ˜ ์˜ค๋ฅ˜, 2=๋ช…๋ น ์˜ค์šฉ, 126=์‹คํ–‰ ๊ถŒํ•œ ์—†์Œ, 127=๋ช…๋ น ์—†์Œ, 130=Ctrl+C.",
971
+ "์‹œํ—˜ ํŒจํ„ด: `&&`์™€ `||`๋กœ ์กฐ๊ฑด๋ถ€ ์‹คํ–‰์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜, `$?` ๊ฐ’์œผ๋กœ ์ด์ „ ๋ช…๋ น ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
972
+ ]
973
+ },
974
+ {
975
+ "id": "linux-service-port",
976
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
977
+ "title": "์„œ๋น„์Šค์™€ ํฌํŠธ ์—ฐ๊ฒฐ",
978
+ "summary": "์‹คํ–‰ ์ค‘์ธ ์„œ๋น„์Šค๊ฐ€ ์–ด๋А ํฌํŠธ๋ฅผ ์—ด๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋ฐฉํ™”๋ฒฝ ์„ค์ •๊ณผ ํ•จ๊ป˜ ํฌํŠธ ์ ‘๊ทผ์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.",
979
+ "example": "ss -tlnp | grep 80\ncat /etc/services | grep ssh\nlsof -i :443\nufw allow 80/tcp",
980
+ "common_mistake": "ํฌํŠธ๋ฅผ ์—ด์—ˆ์–ด๋„ ๋ฐฉํ™”๋ฒฝ(ufw/firewalld)์ด ์ฐจ๋‹จํ•˜๋ฉด ์™ธ๋ถ€์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค์™€ ๋ฐฉํ™”๋ฒฝ์„ ํ•จ๊ป˜ ํ™•์ธํ•˜์„ธ์š”.",
981
+ "keywords": [
982
+ "ss",
983
+ "lsof",
984
+ "/etc/services",
985
+ "port",
986
+ "firewall",
987
+ "ufw"
988
+ ],
989
+ "source_id": "ubuntu-networking",
990
+ "details": [
991
+ "`ss -tlnp`๋Š” TCP ๋ฆฌ์Šค๋‹ ํฌํŠธ์™€ ์„œ๋น„์Šค๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. `lsof -i :ํฌํŠธ`๋Š” ํ•ด๋‹น ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.",
992
+ "`/etc/services`๋Š” ํฌํŠธ ๋ฒˆํ˜ธ์™€ ์„œ๋น„์Šค ์ด๋ฆ„ ๋งคํ•‘ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. 22=ssh, 80=http, 443=https, 3306=mysql.",
993
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํฌํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•˜๋Š” ์„œ๋น„์Šค ํ™•์ธ, ๋ฐฉํ™”๋ฒฝ์—์„œ ํฌํŠธ ๊ฐœ๋ฐฉ, ์„œ๋น„์Šค ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
994
+ ]
995
+ },
996
+ {
997
+ "id": "linux-apt",
998
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
999
+ "title": "apt โ€” ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ",
1000
+ "summary": "Ubuntu/Debian ๊ณ„์—ด์˜ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ํŒจํ‚ค์ง€ ์„ค์น˜, ์—…๋ฐ์ดํŠธ, ๊ฒ€์ƒ‰, ์‚ญ์ œ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.",
1001
+ "example": "apt update && apt upgrade -y\napt install nginx\napt search 'web server'\napt show nginx\napt list --installed | grep nginx",
1002
+ "common_mistake": "`apt install` ์ „์— `apt update`๋ฅผ ๋จผ์ € ์‹คํ–‰ํ•ด์•ผ ์ตœ์‹  ํŒจํ‚ค์ง€ ๋ชฉ๋ก์—์„œ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค. update ์—†์ด ์„ค์น˜ํ•˜๋ฉด ๊ตฌ๋ฒ„์ „์ด ์„ค์น˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1003
+ "keywords": [
1004
+ "apt",
1005
+ "apt update",
1006
+ "apt install",
1007
+ "apt upgrade",
1008
+ "dpkg"
1009
+ ],
1010
+ "source_id": "ubuntu-package-management",
1011
+ "details": [
1012
+ "`apt update`๋Š” ํŒจํ‚ค์ง€ ๋ชฉ๋ก๋งŒ ๊ฐฑ์‹ , `apt upgrade`๋Š” ์‹ค์ œ ์—…๋ฐ์ดํŠธ ์„ค์น˜. ํ•ญ์ƒ update โ†’ upgrade ์ˆœ์„œ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
1013
+ "`dpkg -l | grep ํŒจํ‚ค์ง€`๋กœ ์„ค์น˜ ์—ฌ๋ถ€ ํ™•์ธ, `dpkg -L ํŒจํ‚ค์ง€`๋กœ ์„ค์น˜๋œ ํŒŒ์ผ ๋ชฉ๋ก์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1014
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํŒจํ‚ค์ง€ ์„ค์น˜, ์„ค์น˜ ์—ฌ๋ถ€ ํ™•์ธ, ๋ชจ๋“  ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1015
+ ]
1016
+ },
1017
+ {
1018
+ "id": "linux-repo",
1019
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1020
+ "title": "APT ์ €์žฅ์†Œ ๊ด€๋ฆฌ",
1021
+ "summary": "ํŒจํ‚ค์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ์ €์žฅ์†Œ(repository) ๋ชฉ๋ก์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `/etc/apt/sources.list` ๋˜๋Š” `/etc/apt/sources.list.d/`์— ์ €์žฅ์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.",
1022
+ "example": "cat /etc/apt/sources.list\nadd-apt-repository ppa:name/repo\napt-key add key.gpg # ๊ตฌ์‹\ncurl -fsSL URL | gpg --dearmor > /etc/apt/keyrings/key.gpg",
1023
+ "common_mistake": "์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ GPG ํ‚ค๋„ ํ•จ๊ป˜ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ‚ค ์—†์ด ์ €์žฅ์†Œ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด `apt update` ์‹œ ์„œ๋ช… ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.",
1024
+ "keywords": [
1025
+ "sources.list",
1026
+ "PPA",
1027
+ "repository",
1028
+ "gpg",
1029
+ "apt-key",
1030
+ "add-apt-repository"
1031
+ ],
1032
+ "source_id": "ubuntu-package-management",
1033
+ "details": [
1034
+ "`/etc/apt/sources.list.d/`์— ๊ฐœ๋ณ„ `.list` ํŒŒ์ผ์„ ๋„ฃ๋Š” ๋ฐฉ์‹์ด ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ฃผ sources.list๋Š” ๊ฐ€๊ธ‰์  ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
1035
+ "์ตœ์‹  ๋ฐฉ์‹์€ GPG ํ‚ค๋ฅผ `/etc/apt/keyrings/`์— ์ €์žฅํ•˜๊ณ  sources ํŒŒ์ผ์—์„œ `signed-by=` ์˜ต์…˜์œผ๋กœ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.",
1036
+ "์‹œํ—˜ ํŒจํ„ด: ์ €์žฅ์†Œ ์ถ”๊ฐ€ ํ›„ `apt update`, ํŒจํ‚ค์ง€ ์„ค์น˜๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์ ˆ์ฐจ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1037
+ ]
1038
+ },
1039
+ {
1040
+ "id": "linux-remove-purge",
1041
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1042
+ "title": "ํŒจํ‚ค์ง€ ์ œ๊ฑฐ โ€” remove์™€ purge",
1043
+ "summary": "`apt remove`๋Š” ํŒจํ‚ค์ง€ ๋ฐ”์ด๋„ˆ๋ฆฌ๋งŒ ์ œ๊ฑฐํ•˜๊ณ  ์„ค์ • ํŒŒ์ผ์€ ๋‚จ๊น๋‹ˆ๋‹ค. `apt purge`๋Š” ์„ค์ • ํŒŒ์ผ๊นŒ์ง€ ์™„์ „ํžˆ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.",
1044
+ "example": "apt remove nginx\napt purge nginx\napt autoremove # ์˜์กด์„ฑ์œผ๋กœ๋งŒ ์„ค์น˜๋œ ๋ถˆํ•„์š” ํŒจํ‚ค์ง€ ์ œ๊ฑฐ\ndpkg --list | grep ^rc # ์„ค์ • ๋‚จ์€ ์ œ๊ฑฐ๋œ ํŒจํ‚ค์ง€",
1045
+ "common_mistake": "`remove` ํ›„ ์žฌ์„ค์น˜ํ•˜๋ฉด ์ด์ „ ์„ค์ • ํŒŒ์ผ์ด ๋‚จ์•„ ์žˆ์–ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊นจ๋—์ด ์ œ๊ฑฐํ•˜๋ ค๋ฉด `purge`๋ฅผ ์”๋‹ˆ๋‹ค.",
1046
+ "keywords": [
1047
+ "apt remove",
1048
+ "apt purge",
1049
+ "autoremove",
1050
+ "dpkg --purge"
1051
+ ],
1052
+ "source_id": "ubuntu-package-management",
1053
+ "details": [
1054
+ "`apt autoremove`๋Š” ๋‹ค๋ฅธ ํŒจํ‚ค์ง€์˜ ์˜์กด์„ฑ์œผ๋กœ ์„ค์น˜๋๋‹ค๊ฐ€ ๋” ์ด์ƒ ํ•„์š” ์—†๋Š” ํŒจํ‚ค์ง€๋“ค์„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.",
1055
+ "`dpkg --list | grep '^rc'`๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ์ง€์›Œ์กŒ์ง€๋งŒ ์„ค์ • ํŒŒ์ผ์ด ๋‚จ์€ ํŒจํ‚ค์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1056
+ "์‹œํ—˜ ํŒจํ„ด: ํŒจํ‚ค์ง€๋ฅผ ์„ค์ • ํŒŒ์ผ๊นŒ์ง€ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜, ๋ถˆํ•„์š” ์˜์กด์„ฑ์„ ์ •๋ฆฌํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1057
+ ]
1058
+ },
1059
+ {
1060
+ "id": "linux-ufw",
1061
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1062
+ "title": "ufw โ€” ๋ฐฉํ™”๋ฒฝ ๊ด€๋ฆฌ",
1063
+ "summary": "Ubuntu์˜ ๊ฐ„๋‹จํ•œ ๋ฐฉํ™”๋ฒฝ ๊ด€๋ฆฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. iptables ์œ„์—์„œ ๋™์ž‘ํ•˜๋ฉฐ ์„œ๋น„์Šค ์ด๋ฆ„์ด๋‚˜ ํฌํŠธ๋กœ ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1064
+ "example": "ufw status verbose\nufw enable\nufw allow 22/tcp\nufw allow 'Nginx Full'\nufw deny 3306\nufw delete allow 80",
1065
+ "common_mistake": "`ufw enable` ์ „์— ๋ฐ˜๋“œ์‹œ SSH ํฌํŠธ(22)๋ฅผ ํ—ˆ์šฉํ•˜์„ธ์š”. ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ๋ฉด SSH ์—ฐ๊ฒฐ์ด ๋Š๊ฒจ ์„œ๋ฒ„์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.",
1066
+ "keywords": [
1067
+ "ufw",
1068
+ "enable",
1069
+ "allow",
1070
+ "deny",
1071
+ "status",
1072
+ "firewall"
1073
+ ],
1074
+ "source_id": "ubuntu-firewall",
1075
+ "details": [
1076
+ "๊ทœ์น™ ์ถ”๊ฐ€ ํ˜•์‹: `ufw allow/deny ํฌํŠธ/ํ”„๋กœํ† ์ฝœ` ๋˜๋Š” `ufw allow 'Apache'`์ฒ˜๋Ÿผ ์‚ฌ์ „ ์ •์˜๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœํ•„ ์‚ฌ์šฉ.",
1077
+ "`ufw status numbered`๋กœ ๋ฒˆํ˜ธ๊ฐ€ ๋ถ™์€ ๊ทœ์น™ ๋ชฉ๋ก ํ™•์ธ ํ›„ `ufw delete ๋ฒˆํ˜ธ`๋กœ ํŠน์ • ๊ทœ์น™์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.",
1078
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํฌํŠธ ๊ฐœ๋ฐฉ/์ฐจ๋‹จ, SSH๋Š” ํ—ˆ์šฉํ•˜๋ฉด์„œ ๋ถˆํ•„์š”ํ•œ ํฌํŠธ ์ฐจ๋‹จ, ๋ฐฉํ™”๋ฒฝ ์ƒํƒœ ํ™•์ธ ๋ช…๋ น์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1079
+ ]
1080
+ },
1081
+ {
1082
+ "id": "linux-head-tail",
1083
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1084
+ "title": "head, tail โ€” ํŒŒ์ผ ์•ž๋’ค ํ™•์ธ",
1085
+ "summary": "`head`๋Š” ํŒŒ์ผ ์ฒ˜์Œ N์ค„, `tail`์€ ๋งˆ์ง€๋ง‰ N์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `tail -f`๋Š” ๋กœ๊ทธ ํŒŒ์ผ์— ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.",
1086
+ "example": "head -n 20 /etc/passwd\ntail -n 50 /var/log/syslog\ntail -f /var/log/nginx/error.log\ntail -f /var/log/syslog | grep ERROR",
1087
+ "common_mistake": "`head`๋Š” ํŒŒ์ผ ์‹œ์ž‘, `tail`์€ ํŒŒ์ผ ๋์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค. `-n` ์—†์ด ์“ฐ๋ฉด ๊ธฐ๋ณธ 10์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
1088
+ "keywords": [
1089
+ "head",
1090
+ "tail",
1091
+ "-n",
1092
+ "-f",
1093
+ "log",
1094
+ "follow"
1095
+ ],
1096
+ "source_id": "linux-bash-manual",
1097
+ "details": [
1098
+ "`tail -f`๋Š” ํŒŒ์ผ์— ์ƒˆ๋กœ ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ ๊ฐ€์žฅ ๋งŽ์ด ์”๋‹ˆ๋‹ค.",
1099
+ "`tail -n +5`๋Š” 5๋ฒˆ์งธ ์ค„๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํ—ค๋” ํ–‰์„ ๊ฑด๋„ˆ๋›ธ ๋•Œ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1100
+ "์‹œํ—˜ ํŒจํ„ด: ๋กœ๊ทธ ๋งˆ์ง€๋ง‰ N์ค„ ํ™•์ธ, ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ์ถ”์ , ํŒŒ์ดํ”„๋กœ grep๊ณผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ช…๋ น์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1101
+ ]
1102
+ },
1103
+ {
1104
+ "id": "linux-sed",
1105
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1106
+ "title": "sed โ€” ์ŠคํŠธ๋ฆผ ํŽธ์ง‘๊ธฐ",
1107
+ "summary": "ํŒŒ์ผ์ด๋‚˜ ํŒŒ์ดํ”„ ์ž…๋ ฅ์—์„œ ํŒจํ„ด์„ ์ฐพ์•„ ๊ต์ฒดยท์‚ญ์ œยท์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์„ค์ • ํŒŒ์ผ ์ž๋™ ์ˆ˜์ •๊ณผ ํ…์ŠคํŠธ ๋ณ€ํ™˜ ์Šคํฌ๋ฆฝํŠธ์— ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.",
1108
+ "example": "sed 's/old/new/g' file.txt\nsed -i 's/localhost/127.0.0.1/g' nginx.conf\nsed -n '5,10p' file.txt # 5~10์ค„๋งŒ ์ถœ๋ ฅ\nsed '/^#/d' config.txt # ์ฃผ์„ ์ค„ ์‚ญ์ œ",
1109
+ "common_mistake": "`-i` ์—†์ด๋Š” ์›๋ณธ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  stdout์œผ๋กœ๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `-i.bak`์œผ๋กœ ์›๋ณธ์„ ๋ฐฑ์—…ํ•˜๋ฉด์„œ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1110
+ "keywords": [
1111
+ "sed",
1112
+ "s/old/new/g",
1113
+ "-i",
1114
+ "stream editor",
1115
+ "substitute",
1116
+ "delete"
1117
+ ],
1118
+ "source_id": "linux-bash-manual",
1119
+ "details": [
1120
+ "์น˜ํ™˜ ๋ช…๋ น: `s/ํŒจํ„ด/๊ต์ฒด/g`. `g` ํ”Œ๋ž˜๊ทธ ์—†์œผ๋ฉด ์ค„๋‹น ์ฒซ ๋ฒˆ์งธ๋งŒ, `I` ํ”Œ๋ž˜๊ทธ๋Š” ๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œ.",
1121
+ "์ค„ ๋‹จ์œ„ ์ž‘์—…: `Nd`(N๋ฒˆ ์ค„ ์‚ญ์ œ), `Np`(N๋ฒˆ ์ค„๋งŒ ์ถœ๋ ฅ, `-n`๊ณผ ํ•จ๊ป˜), `/ํŒจํ„ด/d`(ํŒจํ„ด ์ค„ ์‚ญ์ œ).",
1122
+ "์‹œํ—˜ ํŒจํ„ด: ์„ค์ • ํŒŒ์ผ์—์„œ ๊ฐ’์„ ์น˜ํ™˜ํ•˜๊ฑฐ๋‚˜(`-i`), ํŠน์ • ์ค„์„ ์ถœ๋ ฅ/์‚ญ์ œํ•˜๋Š” ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1123
+ ]
1124
+ },
1125
+ {
1126
+ "id": "linux-text-pipeline",
1127
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1128
+ "title": "ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ โ€” cut, sort, uniq, wc",
1129
+ "summary": "ํ…์ŠคํŠธ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•˜๋Š” ํ•ต์‹ฌ ๋„๊ตฌ๋“ค์ž…๋‹ˆ๋‹ค. ํ•„๋“œ ์ถ”์ถœ(cut), ์ •๋ ฌ(sort), ์ค‘๋ณต ์ œ๊ฑฐ(uniq), ์ค„/๋‹จ์–ด ์ˆ˜ ์„ธ๊ธฐ(wc)๋ฅผ ํŒŒ์ดํ”„๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
1130
+ "example": "cut -d':' -f1 /etc/passwd | sort | uniq\nwc -l /var/log/syslog\ncat access.log | cut -d' ' -f1 | sort | uniq -c | sort -rn | head -10",
1131
+ "common_mistake": "`uniq`๋Š” ์—ฐ์†๋œ ์ค‘๋ณต๋งŒ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ์ค‘๋ณต ์ œ๊ฑฐ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ฐ˜๋“œ์‹œ `sort` ํ›„ `uniq`๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1132
+ "keywords": [
1133
+ "cut",
1134
+ "sort",
1135
+ "uniq",
1136
+ "wc",
1137
+ "-d",
1138
+ "-f",
1139
+ "-c",
1140
+ "-rn"
1141
+ ],
1142
+ "source_id": "linux-bash-manual",
1143
+ "details": [
1144
+ "`cut -d๊ตฌ๋ถ„์ž -fํ•„๋“œ๋ฒˆํ˜ธ`: ์ฝœ๋ก  ๊ตฌ๋ถ„์ž์—์„œ 1๋ฒˆ ํ•„๋“œ โ†’ `cut -d':' -f1`. ์—ฌ๋Ÿฌ ํ•„๋“œ๋Š” `-f1,3` ๋˜๋Š” `-f1-3`.",
1145
+ "`sort -rn`: r=์—ญ์ˆœ, n=์ˆซ์ž ์ •๋ ฌ. `uniq -c`๋Š” ์ค‘๋ณต ๊ฐœ์ˆ˜๋ฅผ ์•ž์— ๋ถ™์—ฌ ์ค๋‹ˆ๋‹ค.",
1146
+ "์‹œํ—˜ ํŒจํ„ด: ๋กœ๊ทธ์—์„œ ์ƒ์œ„ ์ ‘์† IP ์ถ”์ถœ, ์ค‘๋ณต ์ œ๊ฑฐ ํ›„ ์ •๋ ฌ, ์ค„ ์ˆ˜ ์„ธ๊ธฐ ๋“ฑ ํŒŒ์ดํ”„๋ผ์ธ์„ ์กฐํ•ฉํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1147
+ ]
1148
+ },
1149
+ {
1150
+ "id": "linux-awk",
1151
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1152
+ "title": "awk โ€” ํ•„๋“œ ๊ธฐ๋ฐ˜ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ",
1153
+ "summary": "๊ณต๋ฐฑ์ด๋‚˜ ์ง€์ • ๊ตฌ๋ถ„์ž๋กœ ๋‚˜๋‰œ ํ•„๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `cut`๋ณด๋‹ค ๊ฐ•๋ ฅํ•˜๋ฉฐ ์กฐ๊ฑด๋ฌธ, ์—ฐ์‚ฐ, ํ•ฉ์‚ฐ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
1154
+ "example": "awk '{print $1, $3}' access.log\nawk -F':' '{print $1}' /etc/passwd\nawk '$3 > 1000 {print $1}' /etc/passwd\nps aux | awk '{print $1, $11}' | head",
1155
+ "common_mistake": "awk์—์„œ `$0`์€ ์ค„ ์ „์ฒด, `$1`์€ ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ์ž…๋‹ˆ๋‹ค. `-F`๋กœ ๊ตฌ๋ถ„์ž๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์œผ๋กœ ๊ณต๋ฐฑ/ํƒญ์ด ๊ตฌ๋ถ„์ž์ž…๋‹ˆ๋‹ค.",
1156
+ "keywords": [
1157
+ "awk",
1158
+ "-F",
1159
+ "$1",
1160
+ "NR",
1161
+ "NF",
1162
+ "print",
1163
+ "field"
1164
+ ],
1165
+ "source_id": "linux-bash-manual",
1166
+ "details": [
1167
+ "๊ธฐ๋ณธ ํŒจํ„ด: `awk '์กฐ๊ฑด {๋™์ž‘}' ํŒŒ์ผ`. ์กฐ๊ฑด ์—†์ด `{print $2}`๋งŒ ์“ฐ๋ฉด ๋ชจ๋“  ์ค„์—์„œ 2๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
1168
+ "๋‚ด์žฅ ๋ณ€์ˆ˜: `NR`=ํ˜„์žฌ ์ค„ ๋ฒˆํ˜ธ, `NF`=ํ•„๋“œ ๊ฐœ์ˆ˜. `NR==1`์€ ์ฒซ ๋ฒˆ์งธ ์ค„๋งŒ, `$NF`๋Š” ๋งˆ์ง€๋ง‰ ํ•„๋“œ์ž…๋‹ˆ๋‹ค.",
1169
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ํ•„๋“œ๋งŒ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜, ์กฐ๊ฑด์— ๋งž๋Š” ์ค„๋งŒ ์ถœ๋ ฅํ•˜๋Š” awk ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1170
+ ]
1171
+ },
1172
+ {
1173
+ "id": "linux-ln-symlink",
1174
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1175
+ "title": "ln โ€” ํ•˜๋“œ ๋งํฌ์™€ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ",
1176
+ "summary": "์‹ฌ๋ณผ๋ฆญ ๋งํฌ(`ln -s`)๋Š” ์›๋ณธ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ์ž…๋‹ˆ๋‹ค. ํ•˜๋“œ ๋งํฌ๋Š” ๊ฐ™์€ inode๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.",
1177
+ "example": "ln -s /etc/nginx/nginx.conf ~/nginx.conf\nln /data/file.txt /backup/file.txt # ํ•˜๋“œ ๋งํฌ\nls -la ~/nginx.conf # -> ์›๋ณธ ๊ฒฝ๋กœ ํ‘œ์‹œ\nreadlink -f ~/nginx.conf # ์‹ค์ œ ๊ฒฝ๋กœ ํ™•์ธ",
1178
+ "common_mistake": "์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋Š” ์›๋ณธ์ด ์‚ญ์ œ๋˜๋ฉด ๋Š๊ธด ๋งํฌ(dangling symlink)๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ํ•˜๋“œ ๋งํฌ๋Š” ์›๋ณธ ์‚ญ์ œ ํ›„์—๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
1179
+ "keywords": [
1180
+ "ln",
1181
+ "-s",
1182
+ "symlink",
1183
+ "hard link",
1184
+ "inode",
1185
+ "readlink"
1186
+ ],
1187
+ "source_id": "linux-bash-manual",
1188
+ "details": [
1189
+ "์‹ฌ๋ณผ๋ฆญ ๋งํฌ: `ln -s ์›๋ณธ๊ฒฝ๋กœ ๋งํฌ๊ฒฝ๋กœ`. `ls -la`์—์„œ `l`๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ `->` ๋’ค์— ์›๋ณธ ๊ฒฝ๋กœ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.",
1190
+ "ํ•˜๋“œ ๋งํฌ๋Š” ๊ฐ™์€ ํŒŒํ‹ฐ์…˜ ๋‚ด์—์„œ๋งŒ ๊ฐ€๋Šฅ, ๋””๋ ‰ํ„ฐ๋ฆฌ์—๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€. ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋Š” ํŒŒํ‹ฐ์…˜๊ณผ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
1191
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ๊ฒฝ๋กœ์— ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜, ๋งํฌ ํƒ€์ž…์„ `ls -la`๋กœ ๊ตฌ๋ณ„ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1192
+ ]
1193
+ },
1194
+ {
1195
+ "id": "linux-bg-fg-jobs",
1196
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1197
+ "title": "๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… โ€” jobs, bg, fg, nohup",
1198
+ "summary": "ํ„ฐ๋ฏธ๋„์„ ๋ธ”๋กํ•˜์ง€ ์•Š๊ณ  ๋ช…๋ น์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. `&`๋กœ ์‹œ์ž‘, `Ctrl+Z`๋กœ ์ผ์‹œ ์ •์ง€, `fg`/`bg`๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.",
1199
+ "example": "long_task.sh & # ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹œ์ž‘\njobs -l # ์ž‘์—… ๋ชฉ๋ก\nfg %1 # 1๋ฒˆ ์ž‘์—… ํฌ๊ทธ๋ผ์šด๋“œ ๋ณต๊ท€\nnohup ./script.sh & # ํ„ฐ๋ฏธ๋„ ๋‹ซํ˜€๋„ ๊ณ„์† ์‹คํ–‰",
1200
+ "common_mistake": "`&`๋กœ ์‹คํ–‰ํ•œ ์ž‘์—…์€ ํ„ฐ๋ฏธ๋„์„ ๋‹ซ์œผ๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ํ„ฐ๋ฏธ๋„ ์ข…๋ฃŒ ํ›„์—๋„ ๊ณ„์† ์‹คํ–‰ํ•˜๋ ค๋ฉด `nohup`์„ ํ•จ๊ป˜ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1201
+ "keywords": [
1202
+ "jobs",
1203
+ "bg",
1204
+ "fg",
1205
+ "nohup",
1206
+ "&",
1207
+ "Ctrl+Z",
1208
+ "SIGHUP"
1209
+ ],
1210
+ "source_id": "linux-bash-cd",
1211
+ "details": [
1212
+ "`Ctrl+Z`๋Š” ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์— SIGTSTP๋ฅผ ๋ณด๋‚ด ์ผ์‹œ ์ •์ง€. `bg`๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์žฌ๊ฐœ, `fg`๋กœ ํฌ๊ทธ๋ผ์šด๋“œ ๋ณต๊ท€.",
1213
+ "`nohup ๋ช…๋ น &`: SIGHUP ์‹œ๊ทธ๋„์„ ๋ฌด์‹œํ•˜๋ฏ€๋กœ ํ„ฐ๋ฏธ๋„ ์ข…๋ฃŒ ํ›„์—๋„ ์‹คํ–‰ ์œ ์ง€. ์ถœ๋ ฅ์€ ๊ธฐ๋ณธ์œผ๋กœ `nohup.out`์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
1214
+ "์‹œํ—˜ ํŒจํ„ด: ์ž‘์—…์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๋ณด๋‚ด๊ฑฐ๋‚˜ ๋ณต๊ท€์‹œํ‚ค๋Š” ๋ช…๋ น, nohup ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1215
+ ]
1216
+ },
1217
+ {
1218
+ "id": "linux-shell-script-basics",
1219
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1220
+ "title": "์‰˜ ์Šคํฌ๋ฆฝํŠธ ๊ธฐ์ดˆ",
1221
+ "summary": "๋ฐ˜๋ณต ์ž‘์—…์„ ์ž๋™ํ™”ํ•˜๋Š” ๋ฐฐ์‹œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. shebang ๋ผ์ธ, ๋ณ€์ˆ˜, ์กฐ๊ฑด๋ฌธ, ๋ฐ˜๋ณต๋ฌธ, ํ•จ์ˆ˜๊ฐ€ ํ•ต์‹ฌ ์š”์†Œ์ž…๋‹ˆ๋‹ค.",
1222
+ "example": "#!/bin/bash\nset -euo pipefail\nBACKUP_DIR=/backup\n[ -d $BACKUP_DIR ] || mkdir -p $BACKUP_DIR\nfor f in /etc/*.conf; do\n cp $f $BACKUP_DIR/\ndone",
1223
+ "common_mistake": "`#!/bin/bash`(shebang)๊ฐ€ ์—†์œผ๋ฉด `/bin/sh`๋กœ ์‹คํ–‰๋ผ bash ์ „์šฉ ๋ฌธ๋ฒ•์ด ์˜ค๋ฅ˜๋‚ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ ๊ถŒํ•œ(`chmod +x`)๋„ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•˜์„ธ์š”.",
1224
+ "keywords": [
1225
+ "shebang",
1226
+ "#!/bin/bash",
1227
+ "if",
1228
+ "for",
1229
+ "while",
1230
+ "function",
1231
+ "chmod +x"
1232
+ ],
1233
+ "source_id": "linux-bash-cd",
1234
+ "details": [
1235
+ "`set -euo pipefail`: `-e`(์—๋Ÿฌ ์‹œ ์ฆ‰์‹œ ์ข…๋ฃŒ), `-u`(๋ฏธ์ •์˜ ๋ณ€์ˆ˜ ์‚ฌ์šฉ ์‹œ ์—๋Ÿฌ), `-o pipefail`(ํŒŒ์ดํ”„ ์ค‘๊ฐ„ ์‹คํŒจ ๊ฐ์ง€). ์Šคํฌ๋ฆฝํŠธ ์ƒ๋‹จ์— ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.",
1236
+ "์กฐ๊ฑด ํ™•์ธ: `[ -f ํŒŒ์ผ ]`(ํŒŒ์ผ ์กด์žฌ), `[ -d ๋””๋ ‰ํ„ฐ๋ฆฌ ]`(๋””๋ ‰ํ„ฐ๋ฆฌ ์กด์žฌ), `[ $var -eq 1 ]`(์ˆซ์ž ๋น„๊ต), `[[ $str == 'a' ]]`(๋ฌธ์ž์—ด).",
1237
+ "์‹œํ—˜ ํŒจํ„ด: ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•˜๋Š” if๋ฌธ, ํŒŒ์ผ ๋ชฉ๋ก์„ ์ฒ˜๋ฆฌํ•˜๋Š” for ๋ฃจํ”„, ํ•จ์ˆ˜ ์ •์˜ ๋“ฑ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ ๋ฌธ์ œ๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1238
+ ]
1239
+ },
1240
+ {
1241
+ "id": "linux-cp-mv-rm",
1242
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1243
+ "title": "cp, mv, rm โ€” ํŒŒ์ผ ๋ณต์‚ฌยท์ด๋™ยท์‚ญ์ œ",
1244
+ "summary": "ํŒŒ์ผ๊ณผ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ณต์‚ฌ(`cp`), ์ด๋™/์ด๋ฆ„๋ณ€๊ฒฝ(`mv`), ์‚ญ์ œ(`rm`)ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์˜ต์…˜์˜ ์กฐํ•ฉ์ด ์‹œํ—˜์—์„œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ˜•ํƒœ๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.",
1245
+ "example": "cp -a /etc/nginx /backup/nginx.bak # ๊ถŒํ•œยทํƒ€์ž„์Šคํƒฌํ”„ ๋ณด์กด ๋ณต์‚ฌ\nmv config.txt config.bak # ์ด๋ฆ„ ๋ณ€๊ฒฝ\nrm -rf /tmp/old_build/ # ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฐ•์ œ ์‚ญ์ œ",
1246
+ "common_mistake": "`cp -r`์€ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ณต์‚ฌํ•˜์ง€๋งŒ ๊ถŒํ•œ๊ณผ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐฑ์—… ๋ชฉ์ ์ด๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ `cp -a`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
1247
+ "keywords": [
1248
+ "cp",
1249
+ "mv",
1250
+ "rm",
1251
+ "-a",
1252
+ "-r",
1253
+ "-i",
1254
+ "-f",
1255
+ "๋ฐฑ์—…"
1256
+ ],
1257
+ "source_id": "manual-linux-basic",
1258
+ "details": [
1259
+ "**`cp` ํ•ต์‹ฌ ์˜ต์…˜**: `-r`(๋””๋ ‰ํ„ฐ๋ฆฌ ์žฌ๊ท€), `-a`(archive: `-dR --preserve=all`, ๋ฐฑ์—… ์‹œ ํ‘œ์ค€), `-i`(๋ฎ์–ด์“ฐ๊ธฐ ์ „ ํ™•์ธ), `-p`(๊ถŒํ•œยทํƒ€์ž„์Šคํƒฌํ”„ ๋ณด์กด).",
1260
+ "**`mv`**: ๊ฐ™์€ ํŒŒ์ผ์‹œ์Šคํ…œ ๋‚ด ์ด๋™์€ inode๋งŒ ๊ฐฑ์‹ ํ•ด ๋น ๋ฆ…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํŒŒํ‹ฐ์…˜ ๊ฐ„ ์ด๋™์€ ์‹ค์ œ ๋ณต์‚ฌ ํ›„ ์‚ญ์ œ๊ฐ€ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.",
1261
+ "**์‹œํ—˜ ํŒจํ„ด**: 'ํผ๋ฏธ์…˜์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐฑ์—…' โ†’ `cp -a`, '์‹ค์ˆ˜ ์—†์ด ์‚ญ์ œ' โ†’ `rm -i`, 'force ์‚ญ์ œ' โ†’ `rm -f`."
1262
+ ]
1263
+ },
1264
+ {
1265
+ "id": "linux-fdisk-parted",
1266
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1267
+ "title": "fdisk, parted โ€” ๋””์Šคํฌ ํŒŒํ‹ฐ์…”๋‹",
1268
+ "summary": "์ƒˆ ๋””์Šคํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „ ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๊ณ  ํŒŒํ‹ฐ์…˜์„ ๋‚˜๋ˆ•๋‹ˆ๋‹ค. MBR์€ `fdisk`, GPT(2TB ์ด์ƒ)๋Š” `parted` ๋˜๋Š” `gdisk`๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1269
+ "example": "fdisk /dev/sdb # MBR ํŒŒํ‹ฐ์…˜ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ํŽธ์ง‘\n# n(์ƒˆ ํŒŒํ‹ฐ์…˜) โ†’ p โ†’ 1 โ†’ Enter โ†’ +20G โ†’ w(์ €์žฅ)\nparted /dev/sdc mklabel gpt\nparted /dev/sdc mkpart primary ext4 1MiB 21GiB",
1270
+ "common_mistake": "`fdisk`๋Š” ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ”์„ ๋ณ€๊ฒฝํ•ด๋„ `w`๋กœ ์ €์žฅํ•˜๊ธฐ ์ „๊นŒ์ง€ ๋””์Šคํฌ์— ๊ธฐ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ 2TB ์ดˆ๊ณผ ๋””์Šคํฌ์—๋Š” MBR(fdisk) ๋Œ€์‹  GPT(parted/gdisk)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1271
+ "keywords": [
1272
+ "fdisk",
1273
+ "parted",
1274
+ "gdisk",
1275
+ "MBR",
1276
+ "GPT",
1277
+ "partition table",
1278
+ "lsblk"
1279
+ ],
1280
+ "source_id": "manual-linux-storage",
1281
+ "details": [
1282
+ "**ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ” ์œ ํ˜•**: MBR(์ตœ๋Œ€ 4 ์ฃผํŒŒํ‹ฐ์…˜, 2TB ํ•œ๊ณ„), GPT(์ตœ๋Œ€ 128 ํŒŒํ‹ฐ์…˜, ์‚ฌ์‹ค์ƒ ๋ฌด์ œํ•œ ์šฉ๋Ÿ‰).",
1283
+ "**fdisk ํ๋ฆ„**: `n`(์ƒˆ ํŒŒํ‹ฐ์…˜) โ†’ ์œ ํ˜• ์„ ํƒ(p/e) โ†’ ๋ฒˆํ˜ธ โ†’ ์‹œ์ž‘/๋ ์„นํ„ฐ(๋˜๋Š” `+20G`) โ†’ `w`(์ €์žฅ ํ›„ ์ข…๋ฃŒ). `p`๋กœ ํ˜„์žฌ ์ƒํƒœ ํ™•์ธ.",
1284
+ "**์‹œํ—˜ ํŒจํ„ด**: lsblk๋กœ ์žฅ์น˜ ํ™•์ธ โ†’ fdisk๋กœ ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ mkfs๋กœ ํฌ๋งท โ†’ mount ์ˆœ์„œ๊ฐ€ ์ „์ฒด ์Šคํ† ๋ฆฌ์ง€ ์„ค์ • ํ๋ฆ„์œผ๋กœ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1285
+ ]
1286
+ },
1287
+ {
1288
+ "id": "linux-mkfs-fsck",
1289
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1290
+ "title": "mkfs, fsck โ€” ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ๊ณผ ๊ฒ€์‚ฌ",
1291
+ "summary": "ํŒŒํ‹ฐ์…˜์— ํŒŒ์ผ์‹œ์Šคํ…œ์„ ์ƒ์„ฑ(`mkfs`)ํ•˜๊ณ  ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์‚ฌยท์ˆ˜์ •(`fsck`)ํ•ฉ๋‹ˆ๋‹ค. ํฌ๋งท ํ›„ ๋งˆ์šดํŠธํ•ด์•ผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1292
+ "example": "mkfs.ext4 /dev/sdb1 # ext4 ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ\nmkfs.xfs /dev/sdb2 # XFS ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ\numount /dev/sdb1 && fsck /dev/sdb1 # ๊ฒ€์‚ฌ ์ „ ๋ฐ˜๋“œ์‹œ ์–ธ๋งˆ์šดํŠธ",
1293
+ "common_mistake": "`fsck`๋Š” ๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์†์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ์–ธ๋งˆ์šดํŠธํ•˜๊ฑฐ๋‚˜ ์‹ฑ๊ธ€ ์œ ์ € ๋ชจ๋“œ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1294
+ "keywords": [
1295
+ "mkfs",
1296
+ "mkfs.ext4",
1297
+ "mkfs.xfs",
1298
+ "fsck",
1299
+ "e2fsck",
1300
+ "tune2fs",
1301
+ "ํŒŒ์ผ์‹œ์Šคํ…œ"
1302
+ ],
1303
+ "source_id": "manual-linux-storage",
1304
+ "details": [
1305
+ "**์ฃผ์š” ํŒŒ์ผ์‹œ์Šคํ…œ**: ext4(๋ฆฌ๋ˆ…์Šค ๊ธฐ๋ณธ, ์ €๋„๋ง), xfs(๋Œ€์šฉ๋Ÿ‰ยท๊ณ ์„ฑ๋Šฅ, RHEL ๊ธฐ๋ณธ), btrfs(์Šค๋ƒ…์ƒทยท์„œ๋ธŒ๋ณผ๋ฅจ ์ง€์›).",
1306
+ "**`tune2fs -l /dev/sdb1`**: ext4 ํŒŒ์ผ์‹œ์Šคํ…œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(UUID, ๋งˆ์šดํŠธ ํšŸ์ˆ˜, ์ƒ์„ฑ์ผ ๋“ฑ)๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. UUID๋Š” fstab ์„ค์ • ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
1307
+ "**์‹œํ—˜ ํŒจํ„ด**: ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ ํ›„ mkfs โ†’ UUID ํ™•์ธ(`blkid`) โ†’ fstab์— ๋“ฑ๋ก โ†’ mount -a ์ˆœ์„œ๊ฐ€ ํ†ตํ•ฉ ์Šคํ† ๋ฆฌ์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1308
+ ]
1309
+ },
1310
+ {
1311
+ "id": "linux-groupadd",
1312
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1313
+ "title": "groupadd, groupmod, groupdel โ€” ๊ทธ๋ฃน ๊ด€๋ฆฌ",
1314
+ "summary": "๊ทธ๋ฃน์„ ์ƒ์„ฑ(`groupadd`), ์ˆ˜์ •(`groupmod`), ์‚ญ์ œ(`groupdel`)ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋ฅผ ๋ณด์กฐ ๊ทธ๋ฃน์— ์ถ”๊ฐ€ํ•˜๋Š” `usermod -aG`์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1315
+ "example": "groupadd developers # ๊ทธ๋ฃน ์ƒ์„ฑ\ngroupmod -n devops developers # ๊ทธ๋ฃน ์ด๋ฆ„ ๋ณ€๊ฒฝ\nusermod -aG developers alice # alice๋ฅผ developers ๊ทธ๋ฃน์— ์ถ”๊ฐ€\ngetent group developers # ๊ทธ๋ฃน ๋ฉค๋ฒ„ ํ™•์ธ",
1316
+ "common_mistake": "`groupdel`์€ ํ•ด๋‹น ๊ทธ๋ฃน์ด ๋ˆ„๊ตฐ๊ฐ€์˜ ๊ธฐ๋ณธ ๊ทธ๋ฃน(primary group)์ด๋ฉด ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๊ทธ ์‚ฌ์šฉ์ž์˜ ๊ธฐ๋ณธ ๊ทธ๋ฃน์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1317
+ "keywords": [
1318
+ "groupadd",
1319
+ "groupmod",
1320
+ "groupdel",
1321
+ "usermod -aG",
1322
+ "/etc/group",
1323
+ "GID"
1324
+ ],
1325
+ "source_id": "ubuntu-user-management",
1326
+ "details": [
1327
+ "**`/etc/group` ๊ตฌ์กฐ**: `๊ทธ๋ฃน๋ช…:์•”ํ˜ธ:GID:๋ฉค๋ฒ„๋ชฉ๋ก` ํ˜•์‹. `getent group <์ด๋ฆ„>`์œผ๋กœ ํŠน์ • ๊ทธ๋ฃน ์ •๋ณด๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
1328
+ "**๋ณด์กฐ ๊ทธ๋ฃน ์ถ”๊ฐ€**: `usermod -aG groupname username`. `-a`(append) ์—†์ด `-G`๋งŒ ์“ฐ๋ฉด ๊ธฐ์กด ๋ณด์กฐ ๊ทธ๋ฃน์ด ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค.",
1329
+ "**์‹œํ—˜ ํŒจํ„ด**: ์ƒˆ ํŒ€ ์ƒ์„ฑ โ†’ ๊ทธ๋ฃน ๋งŒ๋“ค๊ธฐ โ†’ ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๋ฅผ ๊ทธ๋ฃน์— ์ถ”๊ฐ€ โ†’ ๊ทธ๋ฃน ๊ณต์œ  ๋””๋ ‰ํ„ฐ๋ฆฌ์— SGID ์„ค์ •ํ•˜๋Š” ๋ณตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ž์ฃผ ๋‚˜์˜ต๋‹ˆ๋‹ค."
1330
+ ]
1331
+ },
1332
+ {
1333
+ "id": "linux-chage",
1334
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1335
+ "title": "chage โ€” ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งŒ๋ฃŒ ์ •์ฑ…",
1336
+ "summary": "์‚ฌ์šฉ์ž ๊ณ„์ •์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งŒ๋ฃŒ์ผ, ์ตœ๋Œ€ ์‚ฌ์šฉ ๊ธฐ๊ฐ„, ๊ฒฝ๊ณ  ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ณด์•ˆ ์ •์ฑ… ์ ์šฉ๊ณผ ๊ณ„์ • ๋งŒ๋ฃŒ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.",
1337
+ "example": "chage -l alice # ํ˜„์žฌ ๋งŒ๋ฃŒ ์ •์ฑ… ํ™•์ธ\nchage -M 90 alice # ์ตœ๋Œ€ 90์ผ๋งˆ๋‹ค ๋ณ€๊ฒฝ ๊ฐ•์ œ\nchage -E 2025-12-31 alice # ๊ณ„์ • ๋งŒ๋ฃŒ์ผ ์„ค์ •\nchage -d 0 alice # ๋‹ค์Œ ๋กœ๊ทธ์ธ ์‹œ ์ฆ‰์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๊ฐ•์ œ",
1338
+ "common_mistake": "`chage -d 0`์€ ๋งˆ์ง€๋ง‰ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ์ผ์„ ์—ํฌํฌ(1970-01-01)๋กœ ์„ค์ •ํ•ด ๋‹ค์Œ ๋กœ๊ทธ์ธ ์‹œ ์ฆ‰์‹œ ๋ณ€๊ฒฝํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. `-E 0`์€ ๊ณ„์ •์„ ์ฆ‰์‹œ ๋งŒ๋ฃŒ์‹œ์ผœ ๋กœ๊ทธ์ธ ์ž์ฒด๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.",
1339
+ "keywords": [
1340
+ "chage",
1341
+ "-l",
1342
+ "-M",
1343
+ "-E",
1344
+ "-d",
1345
+ "/etc/shadow",
1346
+ "password aging"
1347
+ ],
1348
+ "source_id": "ubuntu-user-management",
1349
+ "details": [
1350
+ "**์ฃผ์š” ์˜ต์…˜**: `-M`(์ตœ๋Œ€ ์‚ฌ์šฉ์ผ), `-m`(์ตœ์†Œ ์‚ฌ์šฉ์ผ), `-W`(๋งŒ๋ฃŒ ์ „ ๊ฒฝ๊ณ ์ผ), `-I`(๋งŒ๋ฃŒ ํ›„ ๋น„ํ™œ์„ฑํ™” ์œ ์˜ˆ์ผ), `-E`(๊ณ„์ • ๋งŒ๋ฃŒ์ผ), `-d`(๋งˆ์ง€๋ง‰ ๋ณ€๊ฒฝ์ผ).",
1351
+ "**`/etc/shadow`์— ์ €์žฅ**: ๋งŒ๋ฃŒ ์ •์ฑ…์€ shadow ํŒŒ์ผ์— ์ฝœ๋ก ์œผ๋กœ ๊ตฌ๋ถ„๋œ ํ•„๋“œ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. `chage`๋Š” ์ด ํŒŒ์ผ์„ ์•ˆ์ „ํ•˜๊ฒŒ ํŽธ์ง‘ํ•˜๋Š” ๋ž˜ํผ์ž…๋‹ˆ๋‹ค.",
1352
+ "**์‹œํ—˜ ํŒจํ„ด**: '์‹ ๊ทœ ์‚ฌ์šฉ์ž๊ฐ€ ์ฒซ ๋กœ๊ทธ์ธ ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ' โ†’ `chage -d 0`, '90์ผ๋งˆ๋‹ค ๋ณ€๊ฒฝ ๊ฐ•์ œ' โ†’ `chage -M 90` ํ˜•ํƒœ๋กœ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1353
+ ]
1354
+ },
1355
+ {
1356
+ "id": "linux-systemd-unit",
1357
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1358
+ "title": "systemd ์„œ๋น„์Šค ์œ ๋‹› ํŒŒ์ผ ์ž‘์„ฑ",
1359
+ "summary": "์ปค์Šคํ…€ ํ”„๋กœ์„ธ์Šค๋ฅผ systemd ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•ด ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ํ•˜๊ณ  ์žฌ์‹œ์ž‘ ์ •์ฑ…์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. `/etc/systemd/system/`์— `.service` ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.",
1360
+ "example": "# /etc/systemd/system/myapp.service\n[Unit]\nDescription=My Application\nAfter=network.target\n\n[Service]\nExecStart=/usr/local/bin/myapp\nRestart=on-failure\nUser=myapp\n\n[Install]\nWantedBy=multi-user.target\n\nsystemctl daemon-reload && systemctl enable --now myapp",
1361
+ "common_mistake": "์œ ๋‹› ํŒŒ์ผ์„ ์ˆ˜์ •ํ•œ ํ›„ `systemctl daemon-reload` ์—†์ด ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ ์ˆ˜์ • ํ›„์—๋Š” ํ•ญ์ƒ daemon-reload๊ฐ€ ๋จผ์ €์ž…๋‹ˆ๋‹ค.",
1362
+ "keywords": [
1363
+ "service unit",
1364
+ "ExecStart",
1365
+ "WantedBy",
1366
+ "daemon-reload",
1367
+ "enable",
1368
+ "After",
1369
+ "Restart"
1370
+ ],
1371
+ "source_id": "manual-linux-systemd",
1372
+ "details": [
1373
+ "**[Unit] ์„น์…˜**: `Description`(์„ค๋ช…), `After`(์ด ์œ ๋‹›๋ณด๋‹ค ๋Šฆ๊ฒŒ ์‹œ์ž‘), `Requires`(์˜์กด์„ฑ, ์—†์œผ๋ฉด ์‹คํŒจ).",
1374
+ "**[Service] ์„น์…˜**: `ExecStart`(์‹คํ–‰ ๋ช…๋ น), `Restart=on-failure`(์‹คํŒจ ์‹œ ์žฌ์‹œ์ž‘), `User`(์‹คํ–‰ ๊ณ„์ •), `Environment`(ํ™˜๊ฒฝ ๋ณ€์ˆ˜).",
1375
+ "**์‹œํ—˜ ํŒจํ„ด**: ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„œ๋น„์Šค๋กœ ๋“ฑ๋ก โ†’ ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ์„ค์ • โ†’ ์žฌ์‹œ์ž‘ ์ •์ฑ… ์ ์šฉ์˜ ์ „์ฒด ํ๋ฆ„, daemon-reload ๋ˆ„๋ฝ ์˜ค๋ฅ˜ ์ง„๋‹จ์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1376
+ ]
1377
+ },
1378
+ {
1379
+ "id": "linux-at",
1380
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1381
+ "title": "at โ€” ์ผํšŒ์„ฑ ์ž‘์—… ์Šค์ผ€์ค„๋ง",
1382
+ "summary": "cron์ด ๋ฐ˜๋ณต ์ž‘์—…์šฉ์ด๋ผ๋ฉด `at`์€ ํŠน์ • ์‹œ๊ฐ์— ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋Œ€ํ™”ํ˜•์œผ๋กœ ๋ช…๋ น์„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ํŒŒ์ดํ”„๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
1383
+ "example": "echo 'tar -czf /backup/home.tar.gz /home' | at 02:00\nat now + 2 hours << 'EOF'\n/usr/local/bin/report.sh\nEOF\natq # ์˜ˆ์•ฝ ๋ชฉ๋ก ํ™•์ธ\natrm 3 # 3๋ฒˆ ์ž‘์—… ์ทจ์†Œ",
1384
+ "common_mistake": "`at`์€ cron๊ณผ ๋‹ฌ๋ฆฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์ตœ์†Œํ™”๋œ ์ƒํƒœ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ์— ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
1385
+ "keywords": [
1386
+ "at",
1387
+ "atq",
1388
+ "atrm",
1389
+ "at now",
1390
+ "batch",
1391
+ "์ผํšŒ์„ฑ ์Šค์ผ€์ค„"
1392
+ ],
1393
+ "source_id": "manual-linux-basic",
1394
+ "details": [
1395
+ "**์‹œ๊ฐ„ ํ‘œํ˜„**: `at 14:30`, `at now + 1 hour`, `at midnight`, `at noon`, `at 2:00 tomorrow`.",
1396
+ "**`batch`**: ์‹œ์Šคํ…œ ๋ถ€ํ•˜๊ฐ€ ๋‚ฎ์„ ๋•Œ ๋ช…๋ น์„ ์‹คํ–‰. at์˜ ๋ณ€ํ˜•์œผ๋กœ, ๋ถ€ํ•˜ ๊ธฐ๋ฐ˜ ์Šค์ผ€์ค„๋ง์ด ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1397
+ "**์‹œํ—˜ ํŒจํ„ด**: cron vs at ์„ ํƒ ๊ธฐ์ค€(๋ฐ˜๋ณต vs ์ผํšŒ์„ฑ), ํŠน์ • ์‹œ๊ฐ ํ›„ ์‹คํ–‰ ์˜ˆ์•ฝ, atq๋กœ ๋Œ€๊ธฐ์—ด ํ™•์ธ, atrm์œผ๋กœ ์ทจ์†Œํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1398
+ ]
1399
+ },
1400
+ {
1401
+ "id": "linux-docker-basics",
1402
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1403
+ "title": "Docker โ€” ์ปจํ…Œ๏ฟฝ๏ฟฝ๏ฟฝ๋„ˆ ๊ธฐ์ดˆ",
1404
+ "summary": "2023๋…„ ๊ฐœํŽธ๋œ LFCS์— ์ปจํ…Œ์ด๋„ˆ ๊ด€๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ pull, ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ยท์ค‘์ง€ยท์‚ญ์ œ, ๋กœ๊ทธ ํ™•์ธ ๋“ฑ ๊ธฐ๋ณธ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.",
1405
+ "example": "docker pull nginx:latest\ndocker run -d -p 8080:80 --name webserver nginx\ndocker ps # ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ ๋ชฉ๋ก\ndocker logs webserver\ndocker stop webserver && docker rm webserver",
1406
+ "common_mistake": "`docker stop`์€ SIGTERM์œผ๋กœ ์ •์ƒ ์ข…๋ฃŒ๋ฅผ ์š”์ฒญํ•˜๊ณ , `docker kill`์€ SIGKILL๋กœ ๊ฐ•์ œ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ„ํ•ด ๊ฐ€๋Šฅํ•˜๋ฉด `stop`์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
1407
+ "keywords": [
1408
+ "docker",
1409
+ "pull",
1410
+ "run",
1411
+ "ps",
1412
+ "stop",
1413
+ "rm",
1414
+ "logs",
1415
+ "images",
1416
+ "-d",
1417
+ "-p"
1418
+ ],
1419
+ "source_id": "manual-linux-docker",
1420
+ "details": [
1421
+ "**`docker run` ์ฃผ์š” ์˜ต์…˜**: `-d`(๋ฐฑ๊ทธ๋ผ์šด๋“œ), `-p ํ˜ธ์ŠคํŠธ:์ปจํ…Œ์ด๋„ˆ`(ํฌํŠธ ๋งคํ•‘), `-v`(๋ณผ๋ฅจ ๋งˆ์šดํŠธ), `--name`(์ด๋ฆ„ ์ง€์ •), `--rm`(์ข…๋ฃŒ ์‹œ ์ž๋™ ์‚ญ์ œ).",
1422
+ "**์ด๋ฏธ์ง€ vs ์ปจํ…Œ์ด๋„ˆ**: `docker images`(๋กœ์ปฌ ์ด๋ฏธ์ง€ ๋ชฉ๋ก), `docker ps -a`(์ค‘์ง€๋œ ๊ฒƒ ํฌํ•จ ์ „์ฒด ์ปจํ…Œ์ด๋„ˆ). `docker rmi`๋กœ ์ด๋ฏธ์ง€, `docker rm`์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.",
1423
+ "**์‹œํ—˜ ํŒจํ„ด**: ์ด๋ฏธ์ง€ pull โ†’ ํฌํŠธ ๋งคํ•‘ํ•ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰ โ†’ ๋กœ๊ทธ ํ™•์ธ โ†’ ์ค‘์ง€ ํ›„ ์‚ญ์ œ ํ๋ฆ„, ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ์ ‘๊ทผ(`docker exec -it`)์ด ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1424
+ ]
1425
+ },
1426
+ {
1427
+ "id": "linux-grub-boot",
1428
+ "certification": "LFCS / ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ",
1429
+ "title": "๋ถ€ํŒ… ํ”„๋กœ์„ธ์Šค์™€ GRUB",
1430
+ "summary": "BIOS/UEFI โ†’ GRUB โ†’ ์ปค๋„ โ†’ initramfs โ†’ systemd ์ˆœ์œผ๋กœ ๋ถ€ํŒ…๋ฉ๋‹ˆ๋‹ค. GRUB ์„ค์ •์„ ์ˆ˜์ •ํ•˜๊ณ  ๋ณต๊ตฌ ๋ชจ๋“œ๋กœ ์ง„์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์‹œํ—˜์— ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.",
1431
+ "example": "# GRUB ๊ธฐ๋ณธ ์„ค์ • ์ˆ˜์ •\nvim /etc/default/grub\n# GRUB_TIMEOUT=5, GRUB_CMDLINE_LINUX=\"quiet splash\"\nupdate-grub # Ubuntu (grub2-mkconfig -o /boot/grub2/grub.cfg on RHEL)\nsystemctl get-default # ํ˜„์žฌ ๊ธฐ๋ณธ target ํ™•์ธ\nsystemctl set-default multi-user.target",
1432
+ "common_mistake": "`/etc/default/grub`์„ ์ˆ˜์ •ํ•ด๋„ `update-grub`(Ubuntu) ๋˜๋Š” `grub2-mkconfig`(RHEL)์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉด ์‹ค์ œ ๋ถ€ํŠธ๋กœ๋” ์„ค์ •์— ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
1433
+ "keywords": [
1434
+ "GRUB",
1435
+ "update-grub",
1436
+ "grub2-mkconfig",
1437
+ "systemd target",
1438
+ "initramfs",
1439
+ "boot"
1440
+ ],
1441
+ "source_id": "manual-linux-boot",
1442
+ "details": [
1443
+ "**๋ถ€ํŒ… ์ˆœ์„œ**: BIOS/UEFI โ†’ MBR/ESP์˜ GRUB โ†’ `/boot/grub/grub.cfg` ๋กœ๋“œ โ†’ ์ปค๋„+initramfs ๋ฉ”๋ชจ๋ฆฌ ์ ์žฌ โ†’ systemd PID 1 ์‹œ์ž‘.",
1444
+ "**systemd target**: `multi-user.target`(CLI, runlevel 3), `graphical.target`(GUI, runlevel 5), `rescue.target`(๋‹จ์ผ ์‚ฌ์šฉ์ž ๋ณต๊ตฌ).",
1445
+ "**์‹œํ—˜ ํŒจํ„ด**: ๋ถ€ํŒ… ์‹คํŒจ ์‹œ GRUB์—์„œ ์ปค๋„ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜์ •(eํ‚ค), ๊ธฐ๋ณธ target ๋ณ€๊ฒฝ, `update-grub` ์‹คํ–‰ ํƒ€์ด๋ฐ์ด ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค."
1446
+ ]
1447
+ }
1448
+ ]
cert_study_app/content/linux/practices.json ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "linux-lab-31",
4
+ "title": "๋กœ๊ทธ ๋งˆ์ง€๋ง‰ 20์ค„ ํ™•์ธ",
5
+ "task_description": "`/var/log/syslog` ํŒŒ์ผ์˜ ๋งˆ์ง€๋ง‰ 20์ค„์„ ํ™•์ธํ•˜์„ธ์š”.",
6
+ "expected_command": "tail -n 20 /var/log/syslog",
7
+ "grading_conditions": [
8
+ "tail",
9
+ "-n",
10
+ "20",
11
+ "/var/log/syslog"
12
+ ],
13
+ "hint": "tail์— -n ์˜ต์…˜์œผ๋กœ ์ถœ๋ ฅํ•  ์ค„ ์ˆ˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
14
+ "explanation": "`tail -n 20 /var/log/syslog`๋Š” ํŒŒ์ผ์˜ ๋งˆ์ง€๋ง‰ 20์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
15
+ "difficulty": "easy"
16
+ },
17
+ {
18
+ "id": "linux-lab-32",
19
+ "title": "๋กœ๊ทธ ํŒŒ์ผ ์‹ค์‹œ๊ฐ„ ์ถ”์ ",
20
+ "task_description": "`/var/log/syslog` ํŒŒ์ผ์— ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ํ•˜์„ธ์š”.",
21
+ "expected_command": "tail -f /var/log/syslog",
22
+ "grading_conditions": [
23
+ "tail",
24
+ "-f",
25
+ "/var/log/syslog"
26
+ ],
27
+ "hint": "`-f` ์˜ต์…˜์€ follow์˜ ์•ฝ์ž๋กœ ํŒŒ์ผ ๋์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.",
28
+ "explanation": "`tail -f /var/log/syslog`๋Š” ํŒŒ์ผ์— ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์† ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
29
+ "difficulty": "easy"
30
+ },
31
+ {
32
+ "id": "linux-lab-33",
33
+ "title": "ํŒŒ์ผ ์ƒ์œ„ 10์ค„ ํ™•์ธ",
34
+ "task_description": "`/etc/passwd` ํŒŒ์ผ์˜ ์ฒ˜์Œ 10์ค„์„ ํ™•์ธํ•˜์„ธ์š”.",
35
+ "expected_command": "head /etc/passwd",
36
+ "grading_conditions": [
37
+ "head",
38
+ "/etc/passwd"
39
+ ],
40
+ "hint": "head๋Š” ๊ธฐ๋ณธ์œผ๋กœ ์ฒ˜์Œ 10์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
41
+ "explanation": "`head /etc/passwd`๋Š” ํŒŒ์ผ์˜ ์ฒ˜์Œ 10์ค„์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `-n`์œผ๋กœ ์ค„ ์ˆ˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.",
42
+ "difficulty": "easy"
43
+ },
44
+ {
45
+ "id": "linux-lab-34",
46
+ "title": "sed๋กœ ํ…์ŠคํŠธ ๊ต์ฒด",
47
+ "task_description": "`config.txt`์—์„œ `old_value`๋ฅผ `new_value`๋กœ ๋ชจ๋‘ ๊ต์ฒดํ•ด ์ถœ๋ ฅํ•˜์„ธ์š”.",
48
+ "expected_command": "sed 's/old_value/new_value/g' config.txt",
49
+ "grading_conditions": [
50
+ "sed",
51
+ "old_value",
52
+ "new_value",
53
+ "config.txt"
54
+ ],
55
+ "hint": "`s/ํŒจํ„ด/๊ต์ฒด/g` ํ˜•ํƒœ๋กœ ๊ต์ฒด ๋ช…๋ น์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. `g`๋Š” ํ•œ ์ค„์—์„œ ๋ชจ๋‘ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค.",
56
+ "explanation": "`sed 's/old_value/new_value/g' config.txt`๋Š” ํŒŒ์ผ์—์„œ old_value๋ฅผ ๋ชจ๋‘ new_value๋กœ ๊ต์ฒดํ•ด ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
57
+ "difficulty": "medium"
58
+ },
59
+ {
60
+ "id": "linux-lab-35",
61
+ "title": "sed ์ธํ”Œ๋ ˆ์ด์Šค ์ˆ˜์ •",
62
+ "task_description": "`nginx.conf`์—์„œ `localhost`๋ฅผ `127.0.0.1`๋กœ ์ง์ ‘ ์ˆ˜์ •ํ•˜์„ธ์š”.",
63
+ "expected_command": "sed -i 's/localhost/127.0.0.1/g' nginx.conf",
64
+ "grading_conditions": [
65
+ "sed",
66
+ "-i",
67
+ "localhost",
68
+ "127.0.0.1",
69
+ "nginx.conf"
70
+ ],
71
+ "hint": "`-i` ์˜ต์…˜์€ ํ‘œ์ค€ ์ถœ๋ ฅ ๋Œ€์‹  ํŒŒ์ผ์„ ์ง์ ‘ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.",
72
+ "explanation": "`sed -i 's/localhost/127.0.0.1/g' nginx.conf`๋Š” nginx.conf ํŒŒ์ผ์„ ์ง์ ‘ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.",
73
+ "difficulty": "medium"
74
+ },
75
+ {
76
+ "id": "linux-lab-36",
77
+ "title": "cut์œผ๋กœ ์‚ฌ์šฉ์ž ์ด๋ฆ„ ์ถ”์ถœ",
78
+ "task_description": "`/etc/passwd`์—์„œ ์ฝœ๋ก (`:`) ๊ตฌ๋ถ„์ž์˜ ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ(์‚ฌ์šฉ์ž ์ด๋ฆ„)๋งŒ ์ถ”์ถœํ•˜์„ธ์š”.",
79
+ "expected_command": "cut -d':' -f1 /etc/passwd",
80
+ "grading_conditions": [
81
+ "cut",
82
+ "-d",
83
+ "-f1",
84
+ "/etc/passwd"
85
+ ],
86
+ "hint": "`-d`๋กœ ๊ตฌ๋ถ„์ž๋ฅผ, `-f`๋กœ ์ถœ๋ ฅํ•  ํ•„๋“œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
87
+ "explanation": "`cut -d':' -f1 /etc/passwd`๋Š” ์ฝœ๋ก  ๊ตฌ๋ถ„์ž์—์„œ ์ฒซ ๋ฒˆ์งธ ํ•„๋“œ(์‚ฌ์šฉ์ž ์ด๋ฆ„)๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.",
88
+ "difficulty": "medium"
89
+ },
90
+ {
91
+ "id": "linux-lab-37",
92
+ "title": "sort์™€ uniq์œผ๋กœ ์ค‘๋ณต ์ œ๊ฑฐ",
93
+ "task_description": "`access.log`์—์„œ ์ค‘๋ณต ์ค„์„ ์ œ๊ฑฐํ•˜๊ณ  ์•ŒํŒŒ๋ฒณ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ด ์ถœ๋ ฅํ•˜์„ธ์š”.",
94
+ "expected_command": "sort access.log | uniq",
95
+ "grading_conditions": [
96
+ "sort",
97
+ "access.log",
98
+ "uniq"
99
+ ],
100
+ "hint": "`uniq`์€ ์—ฐ์†๋œ ์ค‘๋ณต๋งŒ ์ œ๊ฑฐํ•˜๋ฏ€๋กœ ๋จผ์ € `sort`๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
101
+ "explanation": "`sort access.log | uniq`์€ ํŒŒ์ผ์„ ์ •๋ ฌํ•œ ๋’ค ์ค‘๋ณต ์ค„์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.",
102
+ "difficulty": "medium"
103
+ },
104
+ {
105
+ "id": "linux-lab-38",
106
+ "title": "wc๋กœ ์ค„ ์ˆ˜ ์„ธ๊ธฐ",
107
+ "task_description": "`/var/log/auth.log` ํŒŒ์ผ์˜ ์ค„ ์ˆ˜๋ฅผ ์„ธ์„ธ์š”.",
108
+ "expected_command": "wc -l /var/log/auth.log",
109
+ "grading_conditions": [
110
+ "wc",
111
+ "-l",
112
+ "/var/log/auth.log"
113
+ ],
114
+ "hint": "`wc -l`์€ ์ค„ ์ˆ˜, `-w`๋Š” ๋‹จ์–ด ์ˆ˜, `-c`๋Š” ๋ฐ”์ดํŠธ ์ˆ˜๋ฅผ ์…‰๋‹ˆ๋‹ค.",
115
+ "explanation": "`wc -l /var/log/auth.log`๋Š” ํŒŒ์ผ์˜ ์ค„ ์ˆ˜๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
116
+ "difficulty": "easy"
117
+ },
118
+ {
119
+ "id": "linux-lab-39",
120
+ "title": "awk๋กœ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ ์ถœ๋ ฅ",
121
+ "task_description": "`access.log`์˜ ๊ฐ ์ค„์—์„œ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•˜์„ธ์š”.",
122
+ "expected_command": "awk '{print $2}' access.log",
123
+ "grading_conditions": [
124
+ "awk",
125
+ "$2",
126
+ "access.log"
127
+ ],
128
+ "hint": "awk์—์„œ `$1`, `$2`๋Š” ๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„๋œ ๊ฐ ํ•„๋“œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.",
129
+ "explanation": "`awk '{print $2}' access.log`๋Š” ๊ฐ ์ค„์˜ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
130
+ "difficulty": "medium"
131
+ },
132
+ {
133
+ "id": "linux-lab-40",
134
+ "title": "์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ƒ์„ฑ",
135
+ "task_description": "`/etc/nginx/nginx.conf`๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ `~/nginx.conf`๋ฅผ ๋งŒ๋“œ์„ธ์š”.",
136
+ "expected_command": "ln -s /etc/nginx/nginx.conf ~/nginx.conf",
137
+ "grading_conditions": [
138
+ "ln",
139
+ "-s",
140
+ "/etc/nginx/nginx.conf",
141
+ "nginx.conf"
142
+ ],
143
+ "hint": "`ln -s ์›๋ณธ ๋งํฌ์ด๋ฆ„` ํ˜•ํƒœ๋กœ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
144
+ "explanation": "`ln -s /etc/nginx/nginx.conf ~/nginx.conf`๋Š” ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ์— nginx.conf ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
145
+ "difficulty": "medium"
146
+ },
147
+ {
148
+ "id": "linux-lab-41",
149
+ "title": "๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ๋ชฉ๋ก ํ™•์ธ",
150
+ "task_description": "ํ˜„์žฌ ์‰˜์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ๋ชฉ๋ก์„ ํ™•์ธํ•˜์„ธ์š”.",
151
+ "expected_command": "jobs",
152
+ "grading_conditions": [
153
+ "jobs"
154
+ ],
155
+ "hint": "`jobs` ๋ช…๋ น์œผ๋กœ ํ˜„์žฌ ์‰˜์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ๋ชฉ๋ก์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
156
+ "explanation": "`jobs`๋Š” ํ˜„์žฌ ์‰˜์—์„œ `&`๋กœ ์‹คํ–‰๋˜๊ฑฐ๋‚˜ Ctrl+Z๋กœ ์ •์ง€๋œ ์ž‘์—… ๋ชฉ๋ก์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
157
+ "difficulty": "easy"
158
+ },
159
+ {
160
+ "id": "linux-lab-42",
161
+ "title": "nohup ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰",
162
+ "task_description": "`long_process.sh` ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ„ฐ๋ฏธ๋„์ด ๋‹ซํ˜€๋„ ๊ณ„์† ์‹คํ–‰๋˜๋„๋ก ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”.",
163
+ "expected_command": "nohup ./long_process.sh &",
164
+ "grading_conditions": [
165
+ "nohup",
166
+ "long_process.sh",
167
+ "&"
168
+ ],
169
+ "hint": "`nohup`์€ HUP ์‹œ๊ทธ๋„์„ ๋ฌด์‹œํ•ด ํ„ฐ๋ฏธ๋„ ์ข…๋ฃŒ ํ›„์—๋„ ๊ณ„์† ์‹คํ–‰๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.",
170
+ "explanation": "`nohup ./long_process.sh &`๋Š” ํ„ฐ๋ฏธ๋„์ด ๋‹ซํ˜€๋„ ๊ณ„์† ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ถœ๋ ฅ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ nohup.out์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
171
+ "difficulty": "medium"
172
+ },
173
+ {
174
+ "id": "linux-lab-43",
175
+ "title": "๊ถŒํ•œ ๋ณด์กด ํŒŒ์ผ ๋ฐฑ์—…",
176
+ "task_description": "`/etc/passwd` ํŒŒ์ผ์„ ๊ถŒํ•œยท์†Œ์œ ์žยทํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด์กดํ•ด `/tmp/passwd.bak`์œผ๋กœ ๋ณต์‚ฌํ•˜์„ธ์š”.",
177
+ "expected_command": "cp -a /etc/passwd /tmp/passwd.bak",
178
+ "grading_conditions": [
179
+ "cp",
180
+ "-a",
181
+ "/etc/passwd",
182
+ "/tmp/passwd.bak"
183
+ ],
184
+ "hint": "์ผ๋ฐ˜ `cp`๋Š” ์†์„ฑ์„ ๋ณด์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐฑ์—… ์‹œ์—๋Š” `-a` ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
185
+ "explanation": "`cp -a`๋Š” `-dR --preserve=all`์˜ ๋‹จ์ถ•์œผ๋กœ ์‹ฌ๋ณผ๋ฆญ ๋งํฌยท๊ถŒํ•œยท์†Œ์œ ์žยทํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ชจ๋‘ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค.",
186
+ "difficulty": "easy"
187
+ },
188
+ {
189
+ "id": "linux-lab-44",
190
+ "title": "๋””๋ ‰ํ„ฐ๋ฆฌ ์žฌ๊ท€ ์‚ญ์ œ ์ „ ํ™•์ธ",
191
+ "task_description": "`/tmp/testdir` ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์‚ญ์ œ ์ „ ๊ฐ ํŒŒ์ผ์„ ํ™•์ธํ•˜๋ฉด์„œ ์ œ๊ฑฐํ•˜์„ธ์š”.",
192
+ "expected_command": "rm -ri /tmp/testdir",
193
+ "grading_conditions": [
194
+ "rm",
195
+ "-r",
196
+ "-i",
197
+ "/tmp/testdir"
198
+ ],
199
+ "hint": "`-r`์€ ์žฌ๊ท€ ์‚ญ์ œ, `-i`๋Š” ๊ฐ ํŒŒ์ผ๋งˆ๋‹ค ํ™•์ธ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.",
200
+ "explanation": "์ค‘์š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์‚ญ์ œํ•  ๋•Œ๋Š” `-i`๋กœ ํ•œ ํŒŒ์ผ์”ฉ ํ™•์ธํ•˜๋Š” ์Šต๊ด€์ด ์‚ฌ๊ณ ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
201
+ "difficulty": "easy"
202
+ },
203
+ {
204
+ "id": "linux-lab-45",
205
+ "title": "MBR ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ (fdisk)",
206
+ "task_description": "`/dev/sdb`์— fdisk๋ฅผ ์‚ฌ์šฉํ•ด 20GB ์ฃผ ํŒŒํ‹ฐ์…˜์„ ์ƒ์„ฑํ•˜๊ณ  ์ €์žฅํ•˜์„ธ์š”. (์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ: n โ†’ p โ†’ 1 โ†’ Enter โ†’ +20G โ†’ w)",
207
+ "expected_command": "fdisk /dev/sdb",
208
+ "grading_conditions": [
209
+ "fdisk",
210
+ "/dev/sdb"
211
+ ],
212
+ "hint": "fdisk ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ์—์„œ `n`(์ƒˆ ํŒŒํ‹ฐ์…˜), `p`(์ฃผ), `1`(๋ฒˆํ˜ธ), Enter(๊ธฐ๋ณธ ์‹œ์ž‘), `+20G`(ํฌ๊ธฐ), `w`(์ €์žฅ) ์ˆœ์„œ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
213
+ "explanation": "fdisk๋Š” MBR ๋ฐฉ์‹์œผ๋กœ ์ตœ๋Œ€ 2TB ๋””์Šคํฌ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ `w`๋ฅผ ์ž…๋ ฅํ•ด์•ผ ์‹ค์ œ ๋””์Šคํฌ์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.",
214
+ "difficulty": "medium"
215
+ },
216
+ {
217
+ "id": "linux-lab-46",
218
+ "title": "ext4 ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ",
219
+ "task_description": "`/dev/sdb1` ํŒŒํ‹ฐ์…˜์— ext4 ํŒŒ์ผ์‹œ์Šคํ…œ์„ ์ƒ์„ฑํ•˜์„ธ์š”.",
220
+ "expected_command": "mkfs.ext4 /dev/sdb1",
221
+ "grading_conditions": [
222
+ "mkfs.ext4",
223
+ "/dev/sdb1"
224
+ ],
225
+ "hint": "`mkfs.ext4` ๋˜๋Š” `mkfs -t ext4`๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
226
+ "explanation": "ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ ํ›„ ํŒŒ์ผ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด์•ผ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํฌ๋งทํ•˜๋ฉด ๊ธฐ์กด ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ญ์ œ๋˜๋ฏ€๋กœ ๋Œ€์ƒ ๋””๋ฐ”์ด์Šค๋ฅผ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•˜์„ธ์š”.",
227
+ "difficulty": "easy"
228
+ },
229
+ {
230
+ "id": "linux-lab-47",
231
+ "title": "ํŒŒ์ผ์‹œ์Šคํ…œ UUID ํ™•์ธ",
232
+ "task_description": "`/dev/sdb1`์˜ UUID๋ฅผ ํ™•์ธํ•˜์„ธ์š”.",
233
+ "expected_command": "blkid /dev/sdb1",
234
+ "grading_conditions": [
235
+ "blkid",
236
+ "/dev/sdb1"
237
+ ],
238
+ "hint": "`blkid`๋Š” ๋ธ”๋ก ๋””๋ฐ”์ด์Šค์˜ UUID์™€ ํŒŒ์ผ์‹œ์Šคํ…œ ์œ ํ˜•์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
239
+ "explanation": "fstab์— ์žฅ์น˜๋ช… ๋Œ€์‹  UUID๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์žฌ๋ถ€ํŒ… ์‹œ ๋””๋ฐ”์ด์Šค ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์˜ฌ๋ฐ”๋ฅธ ํŒŒํ‹ฐ์…˜์ด ๋งˆ์šดํŠธ๋ฉ๋‹ˆ๋‹ค.",
240
+ "difficulty": "easy"
241
+ },
242
+ {
243
+ "id": "linux-lab-48",
244
+ "title": "๊ทธ๋ฃน ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉ์ž ์ถ”๊ฐ€",
245
+ "task_description": "`devteam` ๊ทธ๋ฃน์„ ๋งŒ๋“ค๊ณ  `alice`๋ฅผ ๊ธฐ์กด ๊ทธ๋ฃน์„ ์œ ์ง€ํ•œ ์ฑ„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.",
246
+ "expected_command": "groupadd devteam && usermod -aG devteam alice",
247
+ "grading_conditions": [
248
+ "groupadd devteam",
249
+ "usermod",
250
+ "-aG",
251
+ "devteam",
252
+ "alice"
253
+ ],
254
+ "hint": "๊ทธ๋ฃน ์ƒ์„ฑ์€ `groupadd`, ์‚ฌ์šฉ์ž๋ฅผ ๋ณด์กฐ ๊ทธ๋ฃน์— ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” `-aG`(append)๋ฅผ ๋ฐ˜๋“œ์‹œ ์‚ฌ์šฉํ•˜์„ธ์š”.",
255
+ "explanation": "`-a` ์—†์ด `-G`๋งŒ ์“ฐ๋ฉด alice์˜ ๊ธฐ์กด ๋ณด์กฐ ๊ทธ๋ฃน์ด ๋ชจ๋‘ devteam์œผ๋กœ ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค. `-aG`๊ฐ€ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.",
256
+ "difficulty": "easy"
257
+ },
258
+ {
259
+ "id": "linux-lab-49",
260
+ "title": "์ฒซ ๋กœ๊ทธ์ธ ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๊ฐ•์ œ",
261
+ "task_description": "์ƒˆ ์‚ฌ์šฉ์ž `bob`์ด ์ฒซ ๋กœ๊ทธ์ธ ์‹œ ๋ฐ˜๋“œ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๋„๋ก ์„ค์ •ํ•˜์„ธ์š”.",
262
+ "expected_command": "chage -d 0 bob",
263
+ "grading_conditions": [
264
+ "chage",
265
+ "-d 0",
266
+ "bob"
267
+ ],
268
+ "hint": "`chage -d 0`์€ ๋งˆ์ง€๋ง‰ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ์ผ์„ ์—ํฌํฌ๋กœ ์„ค์ •ํ•ด ์ฆ‰์‹œ ๋ณ€๊ฒฝ์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค.",
269
+ "explanation": "๋‹ค์Œ ๋กœ๊ทธ์ธ ์‹œ PAM์ด ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งŒ๋ฃŒ๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์‹ ๊ทœ ๊ณ„์ • ๋ฐœ๊ธ‰ ์‹œ ํ‘œ์ค€ ๋ณด์•ˆ ์ ˆ์ฐจ์ž…๋‹ˆ๋‹ค.",
270
+ "difficulty": "easy"
271
+ },
272
+ {
273
+ "id": "linux-lab-50",
274
+ "title": "systemd ์„œ๋น„์Šค ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ํ™œ์„ฑํ™”",
275
+ "task_description": "`/usr/local/bin/myapp`์„ ์‹คํ–‰ํ•˜๋Š” `myapp.service`๋ฅผ ๋งŒ๋“ค๊ณ  ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ํ•˜๋„๋ก ํ™œ์„ฑํ™”ํ•˜์„ธ์š”.",
276
+ "expected_command": "systemctl daemon-reload && systemctl enable --now myapp",
277
+ "grading_conditions": [
278
+ "systemctl daemon-reload",
279
+ "systemctl enable",
280
+ "myapp"
281
+ ],
282
+ "hint": "์œ ๋‹› ํŒŒ์ผ์„ `/etc/systemd/system/myapp.service`์— ์ž‘์„ฑํ•œ ๋’ค daemon-reload โ†’ enable --now ์ˆœ์„œ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
283
+ "explanation": "`daemon-reload`๋Š” ๋ณ€๊ฒฝ๋œ ์œ ๋‹› ํŒŒ์ผ์„ systemd๊ฐ€ ์ธ์‹ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. `enable --now`๋Š” ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ๋“ฑ๋ก๊ณผ ์ฆ‰์‹œ ์‹คํ–‰์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
284
+ "difficulty": "medium"
285
+ },
286
+ {
287
+ "id": "linux-lab-51",
288
+ "title": "์ผํšŒ์„ฑ ์ž‘์—… ์˜ˆ์•ฝ (at)",
289
+ "task_description": "2์‹œ๊ฐ„ ํ›„์— `/usr/local/bin/backup.sh`๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•˜๋„๋ก ์˜ˆ์•ฝํ•˜์„ธ์š”.",
290
+ "expected_command": "echo '/usr/local/bin/backup.sh' | at now + 2 hours",
291
+ "grading_conditions": [
292
+ "at",
293
+ "now + 2 hours"
294
+ ],
295
+ "hint": "`echo '๋ช…๋ น' | at now + 2 hours` ํ˜•์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. `at` ๋ช…๋ น ํ›„ Ctrl+D๋กœ๋„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
296
+ "explanation": "`at`์€ ์ผํšŒ์„ฑ ์Šค์ผ€์ค„๋ง ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ณต ์‹คํ–‰์ด ํ•„์š”ํ•˜๋ฉด cron์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์˜ˆ์•ฝ ๋ชฉ๋ก์€ `atq`๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
297
+ "difficulty": "easy"
298
+ },
299
+ {
300
+ "id": "linux-lab-52",
301
+ "title": "Docker ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ๋ฐ ํ™•์ธ",
302
+ "task_description": "nginx ์ด๋ฏธ์ง€๋ฅผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰ํ•˜๊ณ  ํ˜ธ์ŠคํŠธ ํฌํŠธ 8080์„ ์ปจํ…Œ์ด๋„ˆ ํฌํŠธ 80์— ์—ฐ๊ฒฐํ•œ ๋’ค, ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.",
303
+ "expected_command": "docker run -d -p 8080:80 nginx && docker ps",
304
+ "grading_conditions": [
305
+ "docker run",
306
+ "-d",
307
+ "-p 8080:80",
308
+ "nginx",
309
+ "docker ps"
310
+ ],
311
+ "hint": "`-d`๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ(detach), `-p ํ˜ธ์ŠคํŠธ:์ปจํ…Œ์ด๋„ˆ`๋กœ ํฌํŠธ๋ฅผ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.",
312
+ "explanation": "์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‹คํ–‰ ์ค‘์ด๋ฉด `docker ps`์—์„œ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. ์ค‘์ง€๋œ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ๋ณด๋ ค๋ฉด `docker ps -a`๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
313
+ "difficulty": "easy"
314
+ },
315
+ {
316
+ "id": "linux-lab-53",
317
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] ์‹ ๊ทœ ๋””์Šคํฌ ์ „์ฒด ์„ค์ • (ํŒŒํ‹ฐ์…˜โ†’ํฌ๋งทโ†’๋งˆ์šดํŠธโ†’fstab)",
318
+ "task_description": "์‹ ๊ทœ ๋””์Šคํฌ `/dev/sdb`์— ํŒŒํ‹ฐ์…˜์„ ๋งŒ๋“ค๊ณ  ext4๋กœ ํฌ๋งทํ•œ ๋’ค `/data`์— ์˜๊ตฌ ๋งˆ์šดํŠธํ•˜๋Š” ์ „์ฒด ์ ˆ์ฐจ๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.\n\n๋‹จ๊ณ„:\n1. `/dev/sdb`์— ๋‹จ์ผ ์ฃผ ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ (fdisk)\n2. `/dev/sdb1`์— ext4 ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ\n3. `/data` ๋งˆ์šดํŠธ ํฌ์ธํŠธ ์ƒ์„ฑ\n4. `/dev/sdb1`์„ `/data`์— ๋งˆ์šดํŠธ\n5. `/etc/fstab`์— UUID ๊ธฐ๋ฐ˜์œผ๋กœ ๋“ฑ๋ก ํ›„ `mount -a`๋กœ ๊ฒ€์ฆ\n\n์ด ๋‹จ๊ณ„ ์ค‘ `blkid /dev/sdb1`์˜ ๋ชฉ์ ์„ ์„œ์ˆ ํ•˜์„ธ์š”.",
319
+ "expected_command": "fdisk /dev/sdb && mkfs.ext4 /dev/sdb1 && mkdir /data && mount /dev/sdb1 /data && blkid /dev/sdb1",
320
+ "grading_conditions": [
321
+ "fdisk /dev/sdb",
322
+ "mkfs.ext4 /dev/sdb1",
323
+ "mkdir /data",
324
+ "mount /dev/sdb1 /data",
325
+ "blkid /dev/sdb1"
326
+ ],
327
+ "hint": "์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค: fdisk โ†’ mkfs โ†’ mkdir โ†’ mount โ†’ blkid(UUID ํ™•์ธ) โ†’ fstab ๋“ฑ๋ก โ†’ mount -a",
328
+ "explanation": "์ด ํ๋ฆ„์€ LFCS ์‹ค๊ธฐ์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ๋‚˜์˜ค๋Š” ๋ณตํ•ฉ ์Šคํ† ๋ฆฌ์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค. fstab์— UUID๋ฅผ ์“ฐ๋Š” ์ด์œ ๋Š” ์žฌ๋ถ€ํŒ… ์‹œ ๋””๋ฐ”์ด์Šค ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ์˜ฌ๋ฐ”๋ฅธ ํŒŒํ‹ฐ์…˜์ด ๋งˆ์šดํŠธ๋˜๋„๋ก ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.",
329
+ "difficulty": "hard"
330
+ },
331
+ {
332
+ "id": "linux-lab-54",
333
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] LVM ๋ณผ๋ฅจ ํ™•์žฅ (PVโ†’VG ํ™•์žฅโ†’LV ํ™•์žฅโ†’FS ํ™•์žฅ)",
334
+ "task_description": "๊ธฐ์กด VG `vg_data`์— ์ƒˆ PV `/dev/sdc`๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , LV `lv_app`์„ 10GB ๋Š˜๋ฆฐ ๋’ค ext4 ํŒŒ์ผ์‹œ์Šคํ…œ๊นŒ์ง€ ํ™•์žฅํ•˜์„ธ์š”.\n\n๋‹จ๊ณ„:\n1. `/dev/sdc`๋ฅผ PV๋กœ ์ดˆ๊ธฐํ™”\n2. `/dev/sdc`๋ฅผ `vg_data`์— ์ถ”๊ฐ€\n3. `lv_app`์„ 10GB ์ฆ๊ฐ€\n4. ext4 ํŒŒ์ผ์‹œ์Šคํ…œ ํ™•์žฅ (๋ฌด์ค‘๋‹จ)",
335
+ "expected_command": "pvcreate /dev/sdc && vgextend vg_data /dev/sdc && lvextend -L +10G /dev/vg_data/lv_app && resize2fs /dev/vg_data/lv_app",
336
+ "grading_conditions": [
337
+ "pvcreate /dev/sdc",
338
+ "vgextend vg_data /dev/sdc",
339
+ "lvextend",
340
+ "+10G",
341
+ "/dev/vg_data/lv_app",
342
+ "resize2fs"
343
+ ],
344
+ "hint": "LVM ํ™•์žฅ ์ˆœ์„œ: pvcreate โ†’ vgextend โ†’ lvextend โ†’ resize2fs. `lvextend -r` ์˜ต์…˜์œผ๋กœ resize2fs๋ฅผ ํ†ตํ•ฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.",
345
+ "explanation": "LVM์˜ ๊ฐ•์ ์€ ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ๋ณผ๋ฅจ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. `lvextend -r`์€ LV ํ™•์žฅ๊ณผ ํŒŒ์ผ์‹œ์Šคํ…œ ํ™•์žฅ์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
346
+ "difficulty": "hard"
347
+ },
348
+ {
349
+ "id": "linux-lab-55",
350
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] ํŒ€ ๊ณต์œ  ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ณด์•ˆ ์„ค์ • (๊ทธ๋ฃน+SGID+ACL)",
351
+ "task_description": "๊ฐœ๋ฐœํŒ€ ๊ณต์œ  ๋””๋ ‰ํ„ฐ๋ฆฌ `/srv/devteam`์„ ์„ค์ •ํ•˜์„ธ์š”.\n\n์š”๊ตฌ์‚ฌํ•ญ:\n1. `devteam` ๊ทธ๋ฃน ์ƒ์„ฑ\n2. `alice`์™€ `bob`์„ `devteam`์— ์ถ”๊ฐ€\n3. `/srv/devteam` ์†Œ์œ  ๊ทธ๋ฃน์„ `devteam`์œผ๋กœ ์„ค์ •\n4. ๊ทธ๋ฃน์— rwx ๊ถŒํ•œ ๋ถ€์—ฌ, ๊ธฐํƒ€ ์‚ฌ์šฉ์ž๋Š” ์ ‘๊ทผ ๋ถˆ๊ฐ€\n5. ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ๋„ ์ž๋™์œผ๋กœ `devteam` ์†Œ์œ ๊ฐ€ ๋˜๋„๋ก SGID ์„ค์ •\n6. ์˜ค์ง `alice`์—๊ฒŒ๋งŒ ACL๋กœ ์ถ”๊ฐ€ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ",
352
+ "expected_command": "groupadd devteam && usermod -aG devteam alice && usermod -aG devteam bob && chgrp devteam /srv/devteam && chmod 2770 /srv/devteam && setfacl -m u:alice:rwx /srv/devteam",
353
+ "grading_conditions": [
354
+ "groupadd devteam",
355
+ "usermod -aG devteam alice",
356
+ "usermod -aG devteam bob",
357
+ "chgrp devteam",
358
+ "chmod 2770",
359
+ "setfacl -m u:alice"
360
+ ],
361
+ "hint": "SGID ๋น„ํŠธ๋Š” chmod ์•ž์— 2๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค(์˜ˆ: 2770). ACL์€ ๊ธฐ๋ณธ ๊ถŒํ•œ๊ณผ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
362
+ "explanation": "2770 = SGID(2) + ์†Œ์œ ์žrwx(7) + ๊ทธ๋ฃนrwx(7) + ๊ธฐํƒ€์—†์Œ(0). SGID๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ์ƒ์„ฑ๋œ ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ๋ถ€๋ชจ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ๊ทธ๋ฃน์„ ์ƒ์†ํ•ฉ๋‹ˆ๋‹ค.",
363
+ "difficulty": "hard"
364
+ },
365
+ {
366
+ "id": "linux-lab-56",
367
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] ์ปค์Šคํ…€ systemd ์„œ๋น„์Šค ๋“ฑ๋ก ๋ฐ ๋ณด์•ˆ ๊ฐ•ํ™”",
368
+ "task_description": "`/opt/myapp/run.sh` ์Šคํฌ๋ฆฝํŠธ๋ฅผ systemd ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•˜์„ธ์š”.\n\n์š”๊ตฌ์‚ฌํ•ญ:\n1. `/etc/systemd/system/myapp.service` ํŒŒ์ผ ์ž‘์„ฑ\n2. ์ „์šฉ ์‹œ์Šคํ…œ ๊ณ„์ • `myapp`์œผ๋กœ ์‹คํ–‰ (๋กœ๊ทธ์ธ ๋ถˆ๊ฐ€)\n3. ๋„คํŠธ์›Œํฌ ์ค€๋น„ ํ›„ ์‹œ์ž‘ (`After=network.target`)\n4. ์‹คํŒจ ์‹œ ์ž๋™ ์žฌ์‹œ์ž‘\n5. ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ์„ค์ •\n\n์ž‘์„ฑ ํ›„ ์ฆ‰์‹œ ์‹œ์ž‘๊นŒ์ง€ ์™„๋ฃŒํ•˜์„ธ์š”.",
369
+ "expected_command": "useradd -r -s /sbin/nologin myapp && systemctl daemon-reload && systemctl enable --now myapp",
370
+ "grading_conditions": [
371
+ "useradd -r",
372
+ "-s /sbin/nologin myapp",
373
+ "systemctl daemon-reload",
374
+ "systemctl enable",
375
+ "myapp"
376
+ ],
377
+ "hint": "์‹œ์Šคํ…œ ์„œ๋น„์Šค ๊ณ„์ •์€ `useradd -r`(์‹œ์Šคํ…œ ๊ณ„์ •)๊ณผ `-s /sbin/nologin`(๋กœ๊ทธ์ธ ์ฐจ๋‹จ)์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์œ ๋‹› ํŒŒ์ผ ์ž‘์„ฑ ํ›„ daemon-reload ํ•„์ˆ˜.",
378
+ "explanation": "์šด์˜ ํ™˜๊ฒฝ์—์„œ ์„œ๋น„์Šค๋Š” ์ „์šฉ ์ตœ์†Œ ๊ถŒํ•œ ๊ณ„์ •์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๋ณด์•ˆ ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค. `-r` ํ”Œ๋ž˜๊ทธ๋Š” ์‹œ์Šคํ…œ ๊ณ„์ •์šฉ ๋‚ฎ์€ UID๋ฅผ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.",
379
+ "difficulty": "hard"
380
+ },
381
+ {
382
+ "id": "linux-lab-57",
383
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] SSH ๋ณด์•ˆ ๊ฐ•ํ™” (ํ‚ค ์ธ์ฆ ์ „ํ™˜ + ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฐจ๋‹จ)",
384
+ "task_description": "์„œ๋ฒ„ SSH๋ฅผ ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ์œผ๋กœ ์ „ํ™˜ํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์„ ์ฐจ๋‹จํ•˜์„ธ์š”.\n\n๋‹จ๊ณ„:\n1. ๋กœ์ปฌ์—์„œ ED25519 ํ‚ค ์Œ ์ƒ์„ฑ\n2. ๊ณต๊ฐœ ํ‚ค๋ฅผ ์„œ๋ฒ„ `alice` ๊ณ„์ •์— ๋ณต์‚ฌ (`ssh-copy-id`)\n3. `/etc/ssh/sshd_config`์—์„œ:\n - `PasswordAuthentication no`\n - `PermitRootLogin prohibit-password`\n4. sshd ์žฌ์‹œ์ž‘ ์ „ ์„ค์ • ๋ฌธ๋ฒ• ๊ฒ€์ฆ\n5. sshd ์žฌ์‹œ์ž‘",
385
+ "expected_command": "ssh-keygen -t ed25519 && ssh-copy-id alice@server && sshd -t && systemctl restart sshd",
386
+ "grading_conditions": [
387
+ "ssh-keygen -t ed25519",
388
+ "ssh-copy-id",
389
+ "sshd -t",
390
+ "systemctl restart sshd"
391
+ ],
392
+ "hint": "`sshd -t`๋Š” ์„ค์ • ํŒŒ์ผ ๋ฌธ๋ฒ•์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‹œ์ž‘ ์ „์— ๋ฐ˜๋“œ์‹œ ์‹คํ–‰ํ•ด ์ž ๊ธˆ ๋ฐฉ์ง€.",
393
+ "explanation": "๋น„๋ฐ€๋ฒˆํ˜ธ ์ธ์ฆ์„ ๋„๊ธฐ ์ „ ํ‚ค ์ธ์ฆ์ด ์ž‘๋™ํ•˜๋Š”์ง€ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. `sshd -t` ์‹คํŒจ ์‹œ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ์„œ๋ฒ„์— ์ ‘์† ๋ถˆ๊ฐ€ ์ƒํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
394
+ "difficulty": "hard"
395
+ },
396
+ {
397
+ "id": "linux-lab-58",
398
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] ๋ธŒ๋ฃจํŠธํฌ์Šค IP ํƒ์ง€ ํ›„ ๋ฐฉํ™”๋ฒฝ ์ฐจ๋‹จ",
399
+ "task_description": "`/var/log/auth.log`๋ฅผ ๋ถ„์„ํ•ด SSH ๋กœ๊ทธ์ธ ์‹คํŒจ IP๋ฅผ ์ฐพ๊ณ  ufw๋กœ ์ฐจ๋‹จํ•˜๏ฟฝ๏ฟฝ๏ฟฝ์š”.\n\n๋‹จ๊ณ„:\n1. auth.log์—์„œ 'Failed' ํฌํ•จ ์ค„์˜ IP(ํ•„๋“œ 11)๋ฅผ ์ถ”์ถœ\n2. ํšŸ์ˆ˜ ๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ\n3. ์ƒ์œ„ IP๋ฅผ ufw deny๋กœ ์ฐจ๋‹จ\n4. ufw ๊ทœ์น™ ํ™•์ธ",
400
+ "expected_command": "grep 'Failed' /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head -5",
401
+ "grading_conditions": [
402
+ "grep 'Failed' /var/log/auth.log",
403
+ "awk",
404
+ "sort",
405
+ "uniq -c",
406
+ "sort -rn"
407
+ ],
408
+ "hint": "grepโ†’awkโ†’sortโ†’uniq -cโ†’sort -rn ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ IP๋ณ„ ์‹คํŒจ ํšŸ์ˆ˜๋ฅผ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค.",
409
+ "explanation": "์ด ํŒŒ์ดํ”„๋ผ์ธ์€ ๋ณด์•ˆ ๋ถ„์„์˜ ๊ธฐ๋ณธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ํƒ์ง€๋œ IP๋Š” `ufw deny from 1.2.3.4`๋กœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. fail2ban์ด ์ด ๊ณผ์ •์„ ์ž๋™ํ™”ํ•ฉ๋‹ˆ๋‹ค.",
410
+ "difficulty": "hard"
411
+ },
412
+ {
413
+ "id": "linux-lab-59",
414
+ "title": "[์‹œ๋‚˜๋ฆฌ์˜ค] Docker ์ปจํ…Œ์ด๋„ˆ ์›น ์„œ๋ฒ„ ๋ฐฐํฌ ๋ฐ ๋กœ๊ทธ ํ™•์ธ",
415
+ "task_description": "nginx ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ์šด์˜ ์ƒํƒœ๋ฅผ ๊ฒ€์ฆํ•˜์„ธ์š”.\n\n๋‹จ๊ณ„:\n1. nginx:latest ์ด๋ฏธ์ง€ pull\n2. ์ด๋ฆ„ `webserver`, ํ˜ธ์ŠคํŠธ 8080โ†’์ปจํ…Œ์ด๋„ˆ 80, ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰\n3. ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ์ƒํƒœ ํ™•์ธ\n4. `curl localhost:8080`์œผ๋กœ ์‘๋‹ต ํ™•์ธ\n5. ์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ ๋งˆ์ง€๋ง‰ 20์ค„ ํ™•์ธ",
416
+ "expected_command": "docker pull nginx:latest && docker run -d -p 8080:80 --name webserver nginx && docker ps && curl localhost:8080 && docker logs --tail 20 webserver",
417
+ "grading_conditions": [
418
+ "docker pull nginx",
419
+ "docker run -d -p 8080:80 --name webserver nginx",
420
+ "docker ps",
421
+ "curl localhost:8080",
422
+ "docker logs --tail 20 webserver"
423
+ ],
424
+ "hint": "`docker logs --tail N`์œผ๋กœ ์ตœ๊ทผ N์ค„๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. `--follow`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ๋ฉ๋‹ˆ๋‹ค.",
425
+ "explanation": "์ปจํ…Œ์ด๋„ˆ ๋ฐฐํฌ ํ›„ ps๋กœ ์ƒํƒœ, curl๋กœ ์‘๋‹ต, logs๋กœ ๋‚ด๋ถ€ ๋™์ž‘์„ ํ™•์ธํ•˜๋Š” 3๋‹จ๊ณ„ ๊ฒ€์ฆ์€ ์šด์˜ ํ™˜๊ฒฝ ํ‘œ์ค€ ์ ˆ์ฐจ์ž…๋‹ˆ๋‹ค.",
426
+ "difficulty": "hard"
427
+ }
428
+ ]
cert_study_app/content/linux/quizzes.json ADDED
@@ -0,0 +1,1076 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "linux-q29",
4
+ "lesson_id": "linux-passwd",
5
+ "question_type": "multiple_choice",
6
+ "question": "`passwd -l devuser`์˜ ์˜๋ฏธ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒƒ์€?",
7
+ "options": [
8
+ "devuser ๊ณ„์ •์„ ์ž ๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์„ ๋ง‰๋Š”๋‹ค",
9
+ "devuser ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ•์ œ ๋งŒ๋ฃŒ์‹œํ‚จ๋‹ค",
10
+ "devuser ๊ณ„์ •์„ ์™„์ „ํžˆ ์‚ญ์ œํ•œ๋‹ค",
11
+ "devuser ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋นˆ ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค"
12
+ ],
13
+ "answer": "devuser ๊ณ„์ •์„ ์ž ๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์„ ๋ง‰๋Š”๋‹ค",
14
+ "explanation": "`passwd -l`์€ ๊ณ„์ •์„ ์ž ๊ฐ€ ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์—†๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ž ๊ธˆ ํ•ด์ œ๋Š” `passwd -u`์ž…๋‹ˆ๋‹ค.",
15
+ "difficulty": "easy",
16
+ "source_id": "ubuntu-user-management"
17
+ },
18
+ {
19
+ "id": "linux-q30",
20
+ "lesson_id": "linux-groups-id",
21
+ "question_type": "multiple_choice",
22
+ "question": "ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ UID, GID, ๋ณด์กฐ ๊ทธ๋ฃน ๋ชฉ๋ก์„ ํ•œ ๋ฒˆ์— ํ™•์ธํ•˜๋Š” ๋ช…๋ น์€?",
23
+ "options": [
24
+ "id",
25
+ "groups",
26
+ "whoami",
27
+ "ls -l"
28
+ ],
29
+ "answer": "id",
30
+ "explanation": "`id`๋Š” UID, GID, ์†ํ•œ ๊ทธ๋ฃน ๋ชฉ๋ก์„ ๋ชจ๋‘ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. `groups`๋Š” ๊ทธ๋ฃน ์ด๋ฆ„๋งŒ, `whoami`๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„๋งŒ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
31
+ "difficulty": "easy",
32
+ "source_id": "ubuntu-user-management"
33
+ },
34
+ {
35
+ "id": "linux-q31",
36
+ "lesson_id": "linux-acl",
37
+ "question_type": "multiple_choice",
38
+ "question": "ํŒŒ์ผ์— ์„ค์ •๋œ ACL ๊ถŒํ•œ ๋‚ด์—ญ์„ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์€?",
39
+ "options": [
40
+ "getfacl",
41
+ "setfacl",
42
+ "chmod",
43
+ "ls -l"
44
+ ],
45
+ "answer": "getfacl",
46
+ "explanation": "`getfacl`๋กœ ํ˜„์žฌ ACL ์„ค์ •์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. `setfacl -m`์œผ๋กœ ACL์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. `ls -l`๋งŒ์œผ๋กœ๋Š” ACL ์ „์ฒด๊ฐ€ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
47
+ "difficulty": "easy",
48
+ "source_id": "manual-linux-basic"
49
+ },
50
+ {
51
+ "id": "linux-q32",
52
+ "lesson_id": "linux-ssh",
53
+ "question_type": "multiple_choice",
54
+ "question": "SSH ์ ‘์† ์‹คํŒจ ์›์ธ์œผ๋กœ ๊ด€๋ จ์ด ์—†๋Š” ๊ฒƒ์€?",
55
+ "options": [
56
+ "์‚ฌ์šฉ์ž์˜ Python ๋ฒ„์ „",
57
+ "SSH ์„œ๋น„์Šค ์‹คํ–‰ ์—ฌ๋ถ€",
58
+ "๋ฐฉํ™”๋ฒฝ 22๋ฒˆ ํฌํŠธ ํ—ˆ์šฉ ์—ฌ๋ถ€",
59
+ "authorized_keys ํŒŒ์ผ ๊ถŒํ•œ"
60
+ ],
61
+ "answer": "์‚ฌ์šฉ์ž์˜ Python ๋ฒ„์ „",
62
+ "explanation": "SSH ์ ‘์†์€ ์„œ๋น„์Šค ์‹คํ–‰, ๋ฐฉํ™”๋ฒฝ, ํ‚ค ํŒŒ์ผ ๊ถŒํ•œ์ด ๋ชจ๋‘ ๋งž์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. Python ๋ฒ„์ „์€ SSH ์ ‘์†๊ณผ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.",
63
+ "difficulty": "easy",
64
+ "source_id": "ubuntu-server-docs"
65
+ },
66
+ {
67
+ "id": "linux-q33",
68
+ "lesson_id": "linux-hostname-hosts",
69
+ "question_type": "multiple_choice",
70
+ "question": "`/etc/hosts` ํŒŒ์ผ์˜ ์ฃผ๋œ ์—ญํ• ์€?",
71
+ "options": [
72
+ "DNS ์กฐํšŒ ์ „์— ์ฐธ์กฐ๋˜๋Š” ๋กœ์ปฌ ์ด๋ฆ„ ํ•ด์„ ํŒŒ์ผ์ด๋‹ค",
73
+ "๋ฐฉํ™”๋ฒฝ ๊ทœ์น™์„ ์ €์žฅํ•œ๋‹ค",
74
+ "์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ €์žฅํ•œ๋‹ค",
75
+ "swap ๊ณต๊ฐ„์„ ์ •์˜ํ•œ๋‹ค"
76
+ ],
77
+ "answer": "DNS ์กฐํšŒ ์ „์— ์ฐธ์กฐ๋˜๋Š” ๋กœ์ปฌ ์ด๋ฆ„ ํ•ด์„ ํŒŒ์ผ์ด๋‹ค",
78
+ "explanation": "`/etc/hosts`๋Š” DNS ์กฐํšŒ๋ณด๋‹ค ๋จผ์ € ์ฐธ์กฐ๋˜๋Š” ๋กœ์ปฌ ์ด๋ฆ„ ํ•ด์„ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์ด๋‚˜ ํŠน์ • ํ˜ธ์ŠคํŠธ ์ด๋ฆ„ ์žฌ์ •์˜์— ์ž์ฃผ ์”๋‹ˆ๋‹ค.",
79
+ "difficulty": "easy",
80
+ "source_id": "ubuntu-networking"
81
+ },
82
+ {
83
+ "id": "linux-q34",
84
+ "lesson_id": "linux-netplan",
85
+ "question_type": "multiple_choice",
86
+ "question": "Ubuntu์—์„œ netplan ๋„คํŠธ์›Œํฌ ์„ค์ •์„ ์‹œ์Šคํ…œ์— ์ ์šฉํ•˜๋Š” ๋ช…๋ น์€?",
87
+ "options": [
88
+ "sudo netplan apply",
89
+ "sudo ifconfig apply",
90
+ "sudo ip apply",
91
+ "sudo ufw apply"
92
+ ],
93
+ "answer": "sudo netplan apply",
94
+ "explanation": "`sudo netplan apply`๋Š” `/etc/netplan/*.yaml` ์„ค์ •์„ ํŒŒ์‹ฑํ•ด ์‹œ์Šคํ…œ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. YAML ๋“ค์—ฌ์“ฐ๊ธฐ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.",
95
+ "difficulty": "easy",
96
+ "source_id": "ubuntu-networking"
97
+ },
98
+ {
99
+ "id": "linux-q35",
100
+ "lesson_id": "linux-curl",
101
+ "question_type": "multiple_choice",
102
+ "question": "`curl -I https://example.com`์˜ `-I` ์˜ต์…˜ ์—ญํ• ์€?",
103
+ "options": [
104
+ "HTTP ํ—ค๋”๋งŒ ์š”์ฒญํ•ด ์‘๋‹ต ์ƒํƒœ๋ฅผ ํ™•์ธํ•œ๋‹ค",
105
+ "๋ฐ์ดํ„ฐ๋ฅผ POST๋กœ ์ „์†กํ•œ๋‹ค",
106
+ "SSL ์ธ์ฆ์„œ ๊ฒ€์ฆ์„ ๊ฑด๋„ˆ๋›ด๋‹ค",
107
+ "JSON ํ˜•์‹์œผ๋กœ๋งŒ ์ถœ๋ ฅํ•œ๋‹ค"
108
+ ],
109
+ "answer": "HTTP ํ—ค๋”๋งŒ ์š”์ฒญํ•ด ์‘๋‹ต ์ƒํƒœ๋ฅผ ํ™•์ธํ•œ๋‹ค",
110
+ "explanation": "`-I`๋Š” HEAD ์š”์ฒญ์„ ๋ณด๋‚ด ์‘๋‹ต ํ—ค๋”๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
111
+ "difficulty": "easy",
112
+ "source_id": "ubuntu-networking"
113
+ },
114
+ {
115
+ "id": "linux-q36",
116
+ "lesson_id": "linux-ping",
117
+ "question_type": "multiple_choice",
118
+ "question": "ping์ด ์„ฑ๊ณตํ•˜๋”๋ผ๋„ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์€?",
119
+ "options": [
120
+ "HTTP ์„œ๋น„์Šค๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ",
121
+ "ICMP ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ",
122
+ "๋„คํŠธ์›Œํฌ ๊ฒฝ๋กœ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ",
123
+ "IP ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ"
124
+ ],
125
+ "answer": "HTTP ์„œ๋น„์Šค๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ",
126
+ "explanation": "ping์€ ICMP ์‘๋‹ต๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉํ™”๋ฒฝ์ด ICMP๋ฅผ ํ—ˆ์šฉํ•ด๋„ HTTP ์„œ๋น„์Šค๊ฐ€ ๋‚ด๋ ค๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ HTTP๊ฐ€ ๋˜์–ด๋„ ICMP๊ฐ€ ๋ง‰ํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
127
+ "difficulty": "easy",
128
+ "source_id": "ubuntu-networking"
129
+ },
130
+ {
131
+ "id": "linux-q37",
132
+ "lesson_id": "linux-top",
133
+ "question_type": "multiple_choice",
134
+ "question": "`top` ๋ช…๋ น์–ด๊ฐ€ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์ƒํ™ฉ์€?",
135
+ "options": [
136
+ "CPU/๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋†’์€ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ๋•Œ",
137
+ "ํŒŒ์ผ ์ด๋ฆ„ ํŒจํ„ด์œผ๋กœ ๊ฒ€์ƒ‰ํ•  ๋•Œ",
138
+ "ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•  ๋•Œ",
139
+ "ํŒŒ์ผ์‹œ์Šคํ…œ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ณผ ๋•Œ"
140
+ ],
141
+ "answer": "CPU/๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋†’์€ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ๋•Œ",
142
+ "explanation": "`top`์€ CPU, ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ ํ”„๋กœ์„ธ์Šค ๋ชฉ๋ก์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐฑ์‹ ํ•ด ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์„œ๋ฒ„ ๋ถ€ํ•˜ ์›์ธ ํŒŒ์•…์— ์ž์ฃผ ์”๋‹ˆ๋‹ค.",
143
+ "difficulty": "easy",
144
+ "source_id": "ubuntu-server-docs"
145
+ },
146
+ {
147
+ "id": "linux-q38",
148
+ "lesson_id": "linux-nice",
149
+ "question_type": "multiple_choice",
150
+ "question": "`nice -n 10 command`์—์„œ nice ๊ฐ’์ด ๋†’์„์ˆ˜๋ก ์–ด๋–ป๊ฒŒ ๋˜๋Š”๊ฐ€?",
151
+ "options": [
152
+ "๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— CPU๋ฅผ ์–‘๋ณดํ•˜๋Š” ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง„๋‹ค",
153
+ "CPU ์ฝ”์–ด๋ฅผ ๋” ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค",
154
+ "์ง€์ •๋œ ๋ถ„ ํ›„์— ์‹คํ–‰๋œ๋‹ค",
155
+ "๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋” ๋งŽ์ด ์ฐจ์ง€ํ•œ๋‹ค"
156
+ ],
157
+ "answer": "๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— CPU๋ฅผ ์–‘๋ณดํ•˜๋Š” ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง„๋‹ค",
158
+ "explanation": "nice ๊ฐ’์ด ๋†’์„์ˆ˜๋ก ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ CPU๋ฅผ ๋” ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค. ๋ฒ”์œ„๋Š” -20(๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„)์—์„œ 19(๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„)์ž…๋‹ˆ๋‹ค.",
159
+ "difficulty": "easy",
160
+ "source_id": "ubuntu-server-docs"
161
+ },
162
+ {
163
+ "id": "linux-q39",
164
+ "lesson_id": "linux-swap",
165
+ "question_type": "multiple_choice",
166
+ "question": "swap ์‚ฌ์šฉ๋Ÿ‰์ด ๋งค์šฐ ๋†’์„ ๋•Œ ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ๊ฐ€์žฅ ์ž˜ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์€?",
167
+ "options": [
168
+ "๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด ๋””์Šคํฌ๋ฅผ ๋ณด์กฐ ๋ฉ”๋ชจ๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค",
169
+ "CPU๊ฐ€ ๊ณผ๋ถ€ํ•˜ ์ƒํƒœ๋‹ค",
170
+ "๋””์Šคํฌ ๊ณต๊ฐ„์ด ๊ฝ‰ ์ฐผ๋‹ค",
171
+ "๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ๋‹ค"
172
+ ],
173
+ "answer": "๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด ๋””์Šคํฌ๋ฅผ ๋ณด์กฐ ๋ฉ”๋ชจ๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค",
174
+ "explanation": "swap ๊ณผ์‚ฌ์šฉ์€ ๋ฌผ๋ฆฌ RAM ๋ถ€์กฑ์˜ ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค. ๋””์Šคํฌ๊ฐ€ RAM๋ณด๋‹ค ํ›จ์”ฌ ๋А๋ ค์„œ swap ๊ณผ๋‹ค ์‚ฌ์šฉ์€ ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.",
175
+ "difficulty": "easy",
176
+ "source_id": "ubuntu-storage"
177
+ },
178
+ {
179
+ "id": "linux-q40",
180
+ "lesson_id": "linux-lvm",
181
+ "question_type": "multiple_choice",
182
+ "question": "LVM์—์„œ ๋ฌผ๋ฆฌ ๋ณผ๋ฅจ(PV)๋“ค์ด ํ•ฉ์ณ์ง€๋Š” ์ค‘๊ฐ„ ๊ณ„์ธต์€?",
183
+ "options": [
184
+ "Volume Group (VG)",
185
+ "Logical Volume (LV)",
186
+ "/etc/fstab",
187
+ "partition table"
188
+ ],
189
+ "answer": "Volume Group (VG)",
190
+ "explanation": "LVM ๊ณ„์ธต์€ PV(๋ฌผ๋ฆฌ ๋ณผ๋ฅจ) โ†’ VG(๋ณผ๋ฅจ ๊ทธ๋ฃน) โ†’ LV(๋…ผ๋ฆฌ ๋ณผ๋ฅจ) ์ˆœ์ž…๋‹ˆ๋‹ค. VG๋Š” PV๋“ค์„ ๋ฌถ๊ณ , LV๋Š” VG์—์„œ ์šฉ๋Ÿ‰์„ ๋‚˜๋ˆ  ์”๋‹ˆ๋‹ค.",
191
+ "difficulty": "easy",
192
+ "source_id": "ubuntu-storage"
193
+ },
194
+ {
195
+ "id": "linux-q41",
196
+ "lesson_id": "linux-logs-var",
197
+ "question_type": "multiple_choice",
198
+ "question": "`/var/log` ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ์ฃผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€?",
199
+ "options": [
200
+ "์„œ๋น„์Šค, ์ธ์ฆ, ์‹œ์Šคํ…œ์˜ ๋กœ๊ทธ ํŒŒ์ผ",
201
+ "์‚ฌ์šฉ์ž ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ",
202
+ "์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํŒจํ‚ค์ง€ ๋ฐ”์ด๋„ˆ๋ฆฌ",
203
+ "๋„คํŠธ์›Œํฌ ์„ค์ • ํŒŒ์ผ"
204
+ ],
205
+ "answer": "์„œ๋น„์Šค, ์ธ์ฆ, ์‹œ์Šคํ…œ์˜ ๋กœ๊ทธ ํŒŒ์ผ",
206
+ "explanation": "`/var/log`๋Š” nginx, syslog, auth.log, kern.log ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๋กœ๊ทธ ํŒŒ์ผ์ด ๋ชจ์ด๋Š” ํ‘œ์ค€ ์œ„์น˜์ž…๋‹ˆ๋‹ค.",
207
+ "difficulty": "easy",
208
+ "source_id": "ubuntu-server-docs"
209
+ },
210
+ {
211
+ "id": "linux-q42",
212
+ "lesson_id": "linux-dmesg",
213
+ "question_type": "multiple_choice",
214
+ "question": "`dmesg | tail`๋กœ ํ™•์ธํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์ •๋ณด๋Š”?",
215
+ "options": [
216
+ "๋ถ€ํŒ…๊ณผ ํ•˜๋“œ์›จ์–ด ๊ด€๋ จ ์ปค๋„ ๋ฉ”์‹œ์ง€",
217
+ "HTTP ์„œ๋ฒ„ ์ ‘์† ๋กœ๊ทธ",
218
+ "์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ์ •์ฑ…",
219
+ "cron ์‹คํ–‰ ์ด๋ ฅ"
220
+ ],
221
+ "answer": "๋ถ€ํŒ…๊ณผ ํ•˜๋“œ์›จ์–ด ๊ด€๋ จ ์ปค๋„ ๋ฉ”์‹œ์ง€",
222
+ "explanation": "`dmesg`๋Š” ์ปค๋„ ๋ง ๋ฒ„ํผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋ถ€ํŒ… ๊ณผ์ •, ํ•˜๋“œ์›จ์–ด ๊ฐ์ง€, ๋“œ๋ผ์ด๋ฒ„ ์˜ค๋ฅ˜ ๊ฐ™์€ ์‹œ์Šคํ…œ ์ˆ˜์ค€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณผ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
223
+ "difficulty": "easy",
224
+ "source_id": "ubuntu-server-docs"
225
+ },
226
+ {
227
+ "id": "linux-q43",
228
+ "lesson_id": "linux-env-path",
229
+ "question_type": "multiple_choice",
230
+ "question": "cron ์ž‘์—…์ด ํ„ฐ๋ฏธ๋„์—์„œ๋Š” ๋˜์ง€๋งŒ cron์—์„œ๋งŒ ์‹คํŒจํ•  ๋•Œ ๋จผ์ € ์˜์‹ฌํ•  ์›์ธ์€?",
231
+ "options": [
232
+ "PATH ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ cron๊ณผ ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค๋ฅด๋‹ค",
233
+ "๋””์Šคํฌ ๊ณต๊ฐ„์ด ๋ถ€์กฑํ•˜๋‹ค",
234
+ "SSH ํ‚ค ํŒŒ์ผ์ด ์—†๋‹ค",
235
+ "systemctl์ด ์ค‘์ง€๋๋‹ค"
236
+ ],
237
+ "answer": "PATH ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ cron๊ณผ ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค๋ฅด๋‹ค",
238
+ "explanation": "cron๏ฟฝ๏ฟฝ๏ฟฝ ๋กœ๊ทธ์ธ shell๋ณด๋‹ค ํ›จ์”ฌ ์ œํ•œ๋œ PATH๋ฅผ ์”๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ crontab์—์„œ PATH๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜๋ฉด ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
239
+ "difficulty": "easy",
240
+ "source_id": "linux-bash-cd"
241
+ },
242
+ {
243
+ "id": "linux-q44",
244
+ "lesson_id": "linux-wildcard",
245
+ "question_type": "multiple_choice",
246
+ "question": "`ls *.log` ๋ช…๋ น์—์„œ `*.log` ํŒจํ„ด์„ ํŒŒ์ผ ๋ชฉ๋ก์œผ๋กœ ํ™•์žฅํ•˜๋Š” ์ฃผ์ฒด๋Š”?",
247
+ "options": [
248
+ "Shell (bash/zsh)",
249
+ "ls ๋ช…๋ น์–ด ์ž์ฒด",
250
+ "kernel",
251
+ "ํŒŒ์ผ์‹œ์Šคํ…œ ๋“œ๋ผ์ด๋ฒ„"
252
+ ],
253
+ "answer": "Shell (bash/zsh)",
254
+ "explanation": "์™€์ผ๋“œ์นด๋“œ ๊ธ€๋กœ๋น™(glob)์€ shell์ด ๋ช…๋ น ์‹คํ–‰ ์ „์— ํŒŒ์ผ๋ช… ๋ชฉ๋ก์œผ๋กœ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. ls๊ฐ€ ์•„๋‹ˆ๋ผ shell์ด ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
255
+ "difficulty": "easy",
256
+ "source_id": "linux-bash-cd"
257
+ },
258
+ {
259
+ "id": "linux-q45",
260
+ "lesson_id": "linux-service-port",
261
+ "question_type": "multiple_choice",
262
+ "question": "`systemctl is-active nginx`๊ฐ€ active์ธ๋ฐ๋„ 80ํฌํŠธ์— ์—ฐ๊ฒฐ์ด ์•ˆ ๋  ๋•Œ ํ™•์ธ ๋Œ€์ƒ์ด ์•„๋‹Œ ๊ฒƒ์€?",
263
+ "options": [
264
+ "์‚ฌ์šฉ์ž์˜ SSH ๊ณต๊ฐœ ํ‚ค",
265
+ "๋ฐฉํ™”๋ฒฝ 80ํฌํŠธ ํ—ˆ์šฉ ์—ฌ๋ถ€",
266
+ "nginx๊ฐ€ 80ํฌํŠธ์—์„œ listen ์ค‘์ธ์ง€",
267
+ "nginx ์„ค์ • ํŒŒ์ผ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜"
268
+ ],
269
+ "answer": "์‚ฌ์šฉ์ž์˜ SSH ๊ณต๊ฐœ ํ‚ค",
270
+ "explanation": "์„œ๋น„์Šค๊ฐ€ active์—ฌ๋„ ๋ฐฉํ™”๋ฒฝ, ํฌํŠธ listen ์„ค์ •, ์„ค์ • ํŒŒ์ผ ์˜ค๋ฅ˜๋กœ ์—ฐ๊ฒฐ์ด ์•ˆ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SSH ๊ณต๊ฐœ ํ‚ค๋Š” 80ํฌํŠธ ์—ฐ๊ฒฐ๊ณผ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.",
271
+ "difficulty": "easy",
272
+ "source_id": "linux-man7-ip"
273
+ },
274
+ {
275
+ "id": "linux-q46",
276
+ "lesson_id": "linux-repo",
277
+ "question_type": "multiple_choice",
278
+ "question": "`apt update` ์‹คํ–‰ ์‹œ ํŠน์ • repository์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚  ๋•Œ ๋จผ์ € ์˜์‹ฌํ•  ์›์ธ์€?",
279
+ "options": [
280
+ "repository URL์ด๋‚˜ GPG ํ‚ค ์„ค์ • ๋ฌธ์ œ",
281
+ "CPU ์˜จ๋„๊ฐ€ ๋†’๋‹ค",
282
+ "swap ๊ณต๊ฐ„์ด ๋ถ€์กฑํ•˜๋‹ค",
283
+ "์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์ˆ˜๊ฐ€ ๋งŽ๋‹ค"
284
+ ],
285
+ "answer": "repository URL์ด๋‚˜ GPG ํ‚ค ์„ค์ • ๋ฌธ์ œ",
286
+ "explanation": "`apt update` ์‹คํŒจ๋Š” repository URL ์˜ค๋ฅ˜, ๋„คํŠธ์›Œํฌ ์ฐจ๋‹จ, GPG ์„œ๋ช… ํ‚ค ๋ฌธ์ œ๊ฐ€ ์ฃผ๋œ ์›์ธ์ž…๋‹ˆ๋‹ค.",
287
+ "difficulty": "easy",
288
+ "source_id": "ubuntu-package-management"
289
+ },
290
+ {
291
+ "id": "linux-q47",
292
+ "lesson_id": "linux-head-tail",
293
+ "question_type": "multiple_choice",
294
+ "question": "์ฆ๊ฐ€ํ•˜๋Š” ๋กœ๊ทธ ํŒŒ์ผ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์† ์ถ”์ ํ•˜๋Š” ๋ช…๋ น์œผ๋กœ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๊ฒƒ์€?",
295
+ "options": [
296
+ "tail -f /var/log/syslog",
297
+ "head -f /var/log/syslog",
298
+ "cat -f /var/log/syslog",
299
+ "watch /var/log/syslog"
300
+ ],
301
+ "answer": "tail -f /var/log/syslog",
302
+ "explanation": "`tail -f`๋Š” ํŒŒ์ผ ๋์— ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์† ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง์— ๊ฐ€์žฅ ๋งŽ์ด ์”๋‹ˆ๋‹ค.",
303
+ "difficulty": "easy",
304
+ "source_id": "manual-linux-basic"
305
+ },
306
+ {
307
+ "id": "linux-q48",
308
+ "lesson_id": "linux-sed",
309
+ "question_type": "multiple_choice",
310
+ "question": "`sed 's/old/new/g' file.txt`์˜ ์—ญํ• ์€?",
311
+ "options": [
312
+ "file.txt์˜ old๋ฅผ ๋ชจ๋‘ new๋กœ ๊ต์ฒดํ•ด ํ‘œ์ค€ ์ถœ๋ ฅ์œผ๋กœ ๋ณด๋‚ธ๋‹ค",
313
+ "file.txt๋ฅผ ์ •๋ ฌํ•ด ์ถœ๋ ฅํ•œ๋‹ค",
314
+ "file.txt์—์„œ old ์ค„์„ ์‚ญ์ œํ•œ๋‹ค",
315
+ "file.txt๋ฅผ ์ƒˆ ํŒŒ์ผ๋กœ ๋ณต์‚ฌํ•œ๋‹ค"
316
+ ],
317
+ "answer": "file.txt์˜ old๋ฅผ ๋ชจ๋‘ new๋กœ ๊ต์ฒดํ•ด ํ‘œ์ค€ ์ถœ๋ ฅ์œผ๋กœ ๋ณด๋‚ธ๋‹ค",
318
+ "explanation": "`sed 's/ํŒจํ„ด/๊ต์ฒด/g'`๋Š” ํŒŒ์ผ์—์„œ ํŒจํ„ด์„ ์ฐพ์•„ ๊ต์ฒดํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `-i` ์—†์ด๋Š” ์›๋ณธ ํŒŒ์ผ์„ ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
319
+ "difficulty": "easy",
320
+ "source_id": "manual-linux-basic"
321
+ },
322
+ {
323
+ "id": "linux-q49",
324
+ "lesson_id": "linux-text-pipeline",
325
+ "question_type": "multiple_choice",
326
+ "question": "ํŒŒ์ผ ์ „์ฒด์—์„œ ์ค‘๋ณต ์ค„์„ ์ œ๊ฑฐํ•˜๋ ค ํ•  ๋•Œ `uniq` ์ „์— ์ผ๋ฐ˜์ ์œผ๋กœ ํ•„์š”ํ•œ ๋ช…๋ น์€?",
327
+ "options": [
328
+ "sort",
329
+ "cut",
330
+ "wc",
331
+ "grep"
332
+ ],
333
+ "answer": "sort",
334
+ "explanation": "`uniq`์€ ์—ฐ์†๋œ ์ค‘๋ณต ์ค„๋งŒ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ „์ฒด์—์„œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๋ ค๋ฉด ๋จผ์ € `sort`๋กœ ์ •๋ ฌํ•œ ๋’ค `uniq`์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
335
+ "difficulty": "easy",
336
+ "source_id": "manual-linux-basic"
337
+ },
338
+ {
339
+ "id": "linux-q50",
340
+ "lesson_id": "linux-awk",
341
+ "question_type": "multiple_choice",
342
+ "question": "`awk '{print $2}' access.log`๊ฐ€ ํ•˜๋Š” ์ผ์€?",
343
+ "options": [
344
+ "๊ฐ ์ค„์˜ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค",
345
+ "๋‘ ๋ฒˆ์งธ ์ค„๋งŒ ์ถœ๋ ฅํ•œ๋‹ค",
346
+ "๋‘ ๋ฒˆ์งธ ํŒŒ์ผ์„ ์—ฐ๋‹ค",
347
+ "ํ•„๋“œ ๊ตฌ๋ถ„์ž๋ฅผ 2๋ฒˆ์œผ๋กœ ๋ฐ”๊พผ๋‹ค"
348
+ ],
349
+ "answer": "๊ฐ ์ค„์˜ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค",
350
+ "explanation": "awk์—์„œ `$1`, `$2`, `$N`์€ ๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„๋œ ๊ฐ ํ•„๋“œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. `$2`๋Š” ๊ฐ ์ค„์˜ ๋‘ ๋ฒˆ์งธ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
351
+ "difficulty": "easy",
352
+ "source_id": "manual-linux-basic"
353
+ },
354
+ {
355
+ "id": "linux-q51",
356
+ "lesson_id": "linux-ln-symlink",
357
+ "question_type": "multiple_choice",
358
+ "question": "`ln -s /etc/nginx/nginx.conf ~/nginx.conf`๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์€?",
359
+ "options": [
360
+ "nginx.conf๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ",
361
+ "nginx.conf์˜ ํ•˜๋“œ ๋ณต์‚ฌ๋ณธ",
362
+ "nginx.conf๋ผ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ",
363
+ "nginx.conf์˜ ์ด๋ฆ„๋งŒ ๋ณ€๊ฒฝํ•œ ๊ฒƒ"
364
+ ],
365
+ "answer": "nginx.conf๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ",
366
+ "explanation": "`ln -s`๋Š” ์›๋ณธ ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. `ls -l`์—์„œ `->` ํ‘œ์‹œ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
367
+ "difficulty": "easy",
368
+ "source_id": "manual-linux-basic"
369
+ },
370
+ {
371
+ "id": "linux-q52",
372
+ "lesson_id": "linux-bg-fg-jobs",
373
+ "question_type": "multiple_choice",
374
+ "question": "ํ„ฐ๋ฏธ๋„์ด ๋‹ซํ˜€๋„ ์ž‘์—…์ด ๊ณ„์† ์‹คํ–‰๋˜๋„๋ก ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์€?",
375
+ "options": [
376
+ "nohup long-command &",
377
+ "long-command &",
378
+ "bg long-command",
379
+ "jobs -r long-command"
380
+ ],
381
+ "answer": "nohup long-command &",
382
+ "explanation": "`nohup`์€ ํ„ฐ๋ฏธ๋„ ์ข…๋ฃŒ ์‹œ ๋ณด๋‚ด๋Š” HUP ์‹œ๊ทธ๋„์„ ๋ฌด์‹œํ•ด ์ž‘์—…์ด ๊ณ„์† ์‹คํ–‰๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. `&`๋งŒ์œผ๋กœ๋Š” ํ„ฐ๋ฏธ๋„์ด ๋‹ซํžˆ๋ฉด ์ž‘์—…์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.",
383
+ "difficulty": "easy",
384
+ "source_id": "ubuntu-server-docs"
385
+ },
386
+ {
387
+ "id": "linux-q53",
388
+ "lesson_id": "linux-shell-script-basics",
389
+ "question_type": "multiple_choice",
390
+ "question": "์‰˜ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์Šคํฌ๋ฆฝํŠธ์— ์ „๋‹ฌ๋œ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ณ€์ˆ˜๋Š”?",
391
+ "options": [
392
+ "$1",
393
+ "$0",
394
+ "$@",
395
+ "$$"
396
+ ],
397
+ "answer": "$1",
398
+ "explanation": "`$0`์€ ์Šคํฌ๋ฆฝํŠธ ์ด๋ฆ„, `$1`์€ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜, `$@`๋Š” ๋ชจ๋“  ์ธ์ˆ˜ ๋ชฉ๋ก, `$$`๋Š” ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค PID์ž…๋‹ˆ๋‹ค.",
399
+ "difficulty": "easy",
400
+ "source_id": "linux-bash-cd"
401
+ },
402
+ {
403
+ "id": "linux-q54",
404
+ "lesson_id": "linux-ls",
405
+ "question_type": "multiple_choice",
406
+ "question": "์ˆจ๊น€ ํŒŒ์ผ์„ ํฌํ•จํ•ด ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ณด๋Š” ls ์˜ต์…˜ ์กฐํ•ฉ์€?",
407
+ "options": [
408
+ "ls -rh",
409
+ "ls -al",
410
+ "ls -d",
411
+ "ls -t"
412
+ ],
413
+ "answer": "ls -al",
414
+ "explanation": "`-a`๋Š” ์ ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ˆจ๊น€ ํŒŒ์ผ ํฌํ•จ, `-l`์€ ๊ถŒํ•œยท์†Œ์œ ์žยทํฌ๊ธฐยท๋‚ ์งœ๋ฅผ ํฌํ•จํ•œ ๊ธด ํ˜•์‹ ์ถœ๋ ฅ์ž…๋‹ˆ๋‹ค.",
415
+ "source_id": "manual-linux-basic",
416
+ "difficulty": "easy"
417
+ },
418
+ {
419
+ "id": "linux-q55",
420
+ "lesson_id": "linux-cd",
421
+ "question_type": "multiple_choice",
422
+ "question": "`cd -` ๋ช…๋ น์–ด์˜ ๊ฒฐ๊ณผ๋Š”?",
423
+ "options": [
424
+ "๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™",
425
+ "์ด์ „ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™",
426
+ "ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™",
427
+ "์ƒ์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™"
428
+ ],
429
+ "answer": "์ด์ „ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™",
430
+ "explanation": "`cd -`๋Š” OLDPWD์— ์ €์žฅ๋œ ์ง์ „ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. ๋‘ ๋””๋ ‰ํ„ฐ๋ฆฌ ์‚ฌ์ด๋ฅผ ๋น ๋ฅด๊ฒŒ ์˜ค๊ฐˆ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
431
+ "source_id": "manual-linux-basic",
432
+ "difficulty": "easy"
433
+ },
434
+ {
435
+ "id": "linux-q56",
436
+ "lesson_id": "linux-cat",
437
+ "question_type": "multiple_choice",
438
+ "question": "ํŒŒ์ผ ๋‚ด์šฉ์„ ์ค„ ๋ฒˆํ˜ธ์™€ ํ•จ๊ป˜ ์ถœ๋ ฅํ•˜๋Š” cat ์˜ต์…˜์€?",
439
+ "options": [
440
+ "-n",
441
+ "-v",
442
+ "-e",
443
+ "-s"
444
+ ],
445
+ "answer": "-n",
446
+ "explanation": "`cat -n`์€ ๋ชจ๋“  ์ค„์— ๋ฒˆํ˜ธ๋ฅผ ๋ถ™์—ฌ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ ๋””๋ฒ„๊น…์ด๋‚˜ ํŠน์ • ์ค„ ์œ„์น˜ ํ™•์ธ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
447
+ "source_id": "manual-linux-basic",
448
+ "difficulty": "easy"
449
+ },
450
+ {
451
+ "id": "linux-q57",
452
+ "lesson_id": "linux-process-ps-kill",
453
+ "question_type": "multiple_choice",
454
+ "question": "์‹คํ–‰ ์ค‘์ธ ๋ชจ๋“  ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ํ•จ๊ป˜ ์ „์ฒด ๋ชฉ๋ก์œผ๋กœ ๋ณด๋Š” ๋ช…๋ น์€?",
455
+ "options": [
456
+ "ps aux",
457
+ "ps -e",
458
+ "top -n1",
459
+ "kill -l"
460
+ ],
461
+ "answer": "ps aux",
462
+ "explanation": "`ps aux`๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ์ „์ฒด ํ”„๋กœ์„ธ์Šค๋ฅผ CPU/๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ ํ•จ๊ป˜ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์‹œํ—˜์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ์“ฐ์ด๋Š” ps ์˜ต์…˜ ์กฐํ•ฉ์ž…๋‹ˆ๋‹ค.",
463
+ "source_id": "manual-linux-basic",
464
+ "difficulty": "easy"
465
+ },
466
+ {
467
+ "id": "linux-q58",
468
+ "lesson_id": "linux-cp-mv-rm",
469
+ "question_type": "multiple_choice",
470
+ "question": "์šด์˜ ์ค‘์ธ ์„œ๋ฒ„์˜ `/etc/nginx/nginx.conf`๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์ „ ๊ถŒํ•œ๊ณผ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด์กดํ•ด ๊ฐ™์€ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ฐฑ์—…ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ๋ช…๋ น์€?",
471
+ "options": [
472
+ "cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak",
473
+ "cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak",
474
+ "cp -r /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak",
475
+ "mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak"
476
+ ],
477
+ "answer": "cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak",
478
+ "explanation": "`cp -a`๋Š” ๊ถŒํ•œยท์†Œ์œ ์žยทํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ชจ๋‘ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค(`-dR --preserve=all`). `-r`์€ ๋””๋ ‰ํ„ฐ๋ฆฌ ์žฌ๊ท€ ๋ณต์‚ฌ์šฉ์ด๋ฉฐ ์†์„ฑ์„ ๋ณด์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `mv`๋Š” ์›๋ณธ์ด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.",
479
+ "source_id": "manual-linux-basic",
480
+ "difficulty": "medium"
481
+ },
482
+ {
483
+ "id": "linux-q59",
484
+ "lesson_id": "linux-cp-mv-rm",
485
+ "question_type": "multiple_choice",
486
+ "question": "`rm -rf /var/log/old/`๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ผ์€?",
487
+ "options": [
488
+ "/var/log/old/ ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ํ•˜์œ„ ๋ชจ๋“  ํŒŒ์ผ์ด ์ฆ‰์‹œ ์‚ญ์ œ๋œ๋‹ค",
489
+ "๊ฐ ํŒŒ์ผ๋งˆ๋‹ค ์‚ญ์ œ ํ™•์ธ์„ ์š”์ฒญํ•œ๋‹ค",
490
+ "/var/log/old/๊ฐ€ ํœด์ง€ํ†ต์œผ๋กœ ์ด๋™ํ•œ๋‹ค",
491
+ "์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋งŒ ์‚ญ์ œํ•˜๊ณ  ์‹ค์ œ ํŒŒ์ผ์€ ๋‚จ๊ธด๋‹ค"
492
+ ],
493
+ "answer": "/var/log/old/ ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ํ•˜์œ„ ๋ชจ๋“  ํŒŒ์ผ์ด ์ฆ‰์‹œ ์‚ญ์ œ๋œ๋‹ค",
494
+ "explanation": "`-r`์€ ์žฌ๊ท€ ์‚ญ์ œ, `-f`๋Š” ํ™•์ธ ์—†์ด ๊ฐ•์ œ ์‚ญ์ œ์ž…๋‹ˆ๋‹ค. ๋ฆฌ๋ˆ…์Šค์—๋Š” ํœด์ง€ํ†ต์ด ์—†์–ด ๋ณต๊ตฌ๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ค‘์š” ๊ฒฝ๋กœ์—์„œ๋Š” ๋จผ์ € `ls`๋กœ ๋Œ€์ƒ์„ ํ™•์ธํ•˜์„ธ์š”.",
495
+ "source_id": "manual-linux-basic",
496
+ "difficulty": "easy"
497
+ },
498
+ {
499
+ "id": "linux-q60",
500
+ "lesson_id": "linux-fdisk-parted",
501
+ "question_type": "multiple_choice",
502
+ "question": "3TB ์šฉ๋Ÿ‰์˜ ์ƒˆ ๋””์Šคํฌ `/dev/sdc`์— ํŒŒํ‹ฐ์…˜์„ ๋งŒ๋“ค ๋•Œ fdisk ๋Œ€์‹  parted๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?",
503
+ "options": [
504
+ "fdisk๋Š” MBR ๋ฐฉ์‹์œผ๋กœ 2TB ์ดˆ๊ณผ ๋””์Šคํฌ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ",
505
+ "parted๊ฐ€ ๋” ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ",
506
+ "fdisk๋Š” ext4 ํŒŒ์ผ์‹œ์Šคํ…œ๋งŒ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ",
507
+ "parted๋งŒ RAID ์„ค์ •์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ"
508
+ ],
509
+ "answer": "fdisk๋Š” MBR ๋ฐฉ์‹์œผ๋กœ 2TB ์ดˆ๊ณผ ๋””์Šคํฌ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ",
510
+ "explanation": "MBR ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ”์€ ์ตœ๋Œ€ 2TB๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. 3TB ๋””์Šคํฌ๋Š” GPT๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ `parted`๋‚˜ `gdisk`๋กœ GPT ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
511
+ "source_id": "manual-linux-storage",
512
+ "difficulty": "medium"
513
+ },
514
+ {
515
+ "id": "linux-q61",
516
+ "lesson_id": "linux-fdisk-parted",
517
+ "question_type": "multiple_choice",
518
+ "question": "`fdisk /dev/sdb` ์‹คํ–‰ ํ›„ ์ƒˆ ์ฃผ ํŒŒํ‹ฐ์…˜์„ ๋งŒ๋“ค๊ณ  ์ €์žฅํ•˜๋Š” ํ‚ค ์ˆœ์„œ๋Š”?",
519
+ "options": [
520
+ "n โ†’ p โ†’ w",
521
+ "p โ†’ n โ†’ w",
522
+ "n โ†’ e โ†’ q",
523
+ "w โ†’ n โ†’ p"
524
+ ],
525
+ "answer": "n โ†’ p โ†’ w",
526
+ "explanation": "`n`(new partition) โ†’ `p`(primary) โ†’ ๋ฒˆํ˜ธยท์„นํ„ฐ ์ž…๋ ฅ โ†’ `w`(write & quit). `q`๋Š” ์ €์žฅ ์—†์ด ์ข…๋ฃŒ, `p`๋Š” ํ˜„์žฌ ํ…Œ์ด๋ธ” ์ถœ๋ ฅ(Print)์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.",
527
+ "source_id": "manual-linux-storage",
528
+ "difficulty": "medium"
529
+ },
530
+ {
531
+ "id": "linux-q62",
532
+ "lesson_id": "linux-mkfs-fsck",
533
+ "question_type": "multiple_choice",
534
+ "question": "๋‹ค์Œ ์Šคํ† ๋ฆฌ์ง€ ์„ค์ • ์ˆœ์„œ ์ค‘ ์˜ฌ๋ฐ”๋ฅธ ๊ฒƒ์€?",
535
+ "options": [
536
+ "ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ(mkfs) โ†’ ๋งˆ์šดํŠธ ํฌ์ธํŠธ ์ƒ์„ฑ โ†’ mount",
537
+ "๋งˆ์šดํŠธ โ†’ mkfs โ†’ ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ fstab ๋“ฑ๋ก",
538
+ "mkfs โ†’ ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ mount โ†’ fstab ๋“ฑ๋ก",
539
+ "ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ mount โ†’ mkfs โ†’ fstab ๋“ฑ๋ก"
540
+ ],
541
+ "answer": "ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒ์„ฑ(mkfs) โ†’ ๋งˆ์šดํŠธ ํฌ์ธํŠธ ์ƒ์„ฑ โ†’ mount",
542
+ "explanation": "fdisk๋กœ ํŒŒํ‹ฐ์…˜ ์ƒ์„ฑ โ†’ mkfs๋กœ ํฌ๋งท โ†’ mkdir๋กœ ๋งˆ์šดํŠธ ํฌ์ธํŠธ ์ƒ์„ฑ โ†’ mount๋กœ ์—ฐ๊ฒฐ์ด ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ์ž…๋‹ˆ๋‹ค. ์˜๊ตฌ ๋งˆ์šดํŠธ๋Š” ๋งˆ์ง€๋ง‰์— fstab์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.",
543
+ "source_id": "manual-linux-storage",
544
+ "difficulty": "medium"
545
+ },
546
+ {
547
+ "id": "linux-q63",
548
+ "lesson_id": "linux-mkfs-fsck",
549
+ "question_type": "multiple_choice",
550
+ "question": "๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ์—์„œ `fsck`๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”๊ฐ€?",
551
+ "options": [
552
+ "๋ฐ์ดํ„ฐ ์†์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค",
553
+ "์ž๋™์œผ๋กœ ์–ธ๋งˆ์šดํŠธ ํ›„ ๊ฒ€์‚ฌํ•œ๋‹ค",
554
+ "์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ „ํ™˜ ํ›„ ๊ฒ€์‚ฌํ•œ๋‹ค",
555
+ "์ •์ƒ์ ์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค"
556
+ ],
557
+ "answer": "๋ฐ์ดํ„ฐ ์†์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค",
558
+ "explanation": "`fsck`๋Š” ์–ธ๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ์—์„œ๋งŒ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์šดํŠธ ์ƒํƒœ์—์„œ ์‹คํ–‰ํ•˜๋ฉด OS์™€ fsck๊ฐ€ ๋™์‹œ์— ํŒŒ์ผ์‹œ์Šคํ…œ์— ์“ฐ๊ธฐ๋ฅผ ์‹œ๋„ํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์†์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
559
+ "source_id": "manual-linux-storage",
560
+ "difficulty": "medium"
561
+ },
562
+ {
563
+ "id": "linux-q64",
564
+ "lesson_id": "linux-groupadd",
565
+ "question_type": "multiple_choice",
566
+ "question": "`usermod -G developers alice`๋ฅผ `-a` ์—†์ด ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋Š”?",
567
+ "options": [
568
+ "alice์˜ ๊ธฐ์กด ๋ณด์กฐ ๊ทธ๋ฃน์ด ๋ชจ๋‘ ์ œ๊ฑฐ๋˜๊ณ  developers๋งŒ ๋‚จ๋Š”๋‹ค",
569
+ "alice๊ฐ€ developers ๊ทธ๋ฃน์—์„œ ์ œ๊ฑฐ๋œ๋‹ค",
570
+ "๋ช…๋ น์ด ์‹คํŒจํ•˜๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค",
571
+ "alice์˜ ๊ธฐ๋ณธ ๊ทธ๋ฃน์ด developers๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค"
572
+ ],
573
+ "answer": "alice์˜ ๊ธฐ์กด ๋ณด์กฐ ๊ทธ๋ฃน์ด ๋ชจ๋‘ ์ œ๊ฑฐ๋˜๊ณ  developers๋งŒ ๋‚จ๋Š”๋‹ค",
574
+ "explanation": "`-G`๋Š” ๋ณด์กฐ ๊ทธ๋ฃน์„ ์ง€์ •ํ•œ ๋ชฉ๋ก์œผ๋กœ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๊ทธ๋ฃน์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ `-aG`(append)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
575
+ "source_id": "ubuntu-user-management",
576
+ "difficulty": "medium"
577
+ },
578
+ {
579
+ "id": "linux-q65",
580
+ "lesson_id": "linux-chage",
581
+ "question_type": "multiple_choice",
582
+ "question": "์‹ ๊ทœ ์ž…์‚ฌ์ž bob์ด ์ฒซ ๋กœ๊ทธ์ธ ์‹œ ๋ฐ˜๋“œ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ๋ช…๏ฟฝ๏ฟฝ์€?",
583
+ "options": [
584
+ "chage -d 0 bob",
585
+ "chage -M 0 bob",
586
+ "passwd -l bob",
587
+ "chage -E 0 bob"
588
+ ],
589
+ "answer": "chage -d 0 bob",
590
+ "explanation": "`chage -d 0`์€ ๋งˆ์ง€๋ง‰ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ์ผ์„ ์—ํฌํฌ(1970-01-01)๋กœ ์„ค์ •ํ•ด ๋‹ค์Œ ๋กœ๊ทธ์ธ ์‹œ ์ฆ‰์‹œ ๋ณ€๊ฒฝ์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. `-E 0`์€ ๊ณ„์ • ์ž์ฒด๋ฅผ ๋งŒ๋ฃŒ์‹œ์ผœ ๋กœ๊ทธ์ธ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.",
591
+ "source_id": "ubuntu-user-management",
592
+ "difficulty": "medium"
593
+ },
594
+ {
595
+ "id": "linux-q66",
596
+ "lesson_id": "linux-chage",
597
+ "question_type": "multiple_choice",
598
+ "question": "๋ณด์•ˆ ์ •์ฑ…์ƒ ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ 90์ผ๋งˆ๋‹ค ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค. alice์—๊ฒŒ ์ ์šฉํ•˜๋Š” ๋ช…๋ น์€?",
599
+ "options": [
600
+ "chage -M 90 alice",
601
+ "chage -d 90 alice",
602
+ "passwd -M 90 alice",
603
+ "chage -W 90 alice"
604
+ ],
605
+ "answer": "chage -M 90 alice",
606
+ "explanation": "`-M`์€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ตœ๋Œ€ ์‚ฌ์šฉ ๊ธฐ๊ฐ„(Maximum days)์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. `-W`๋Š” ๋งŒ๋ฃŒ ์ „ ๊ฒฝ๊ณ ์ผ, `-d`๋Š” ๋งˆ์ง€๋ง‰ ๋ณ€๊ฒฝ์ผ, `-E`๋Š” ๊ณ„์ • ๋งŒ๋ฃŒ์ผ์ž…๋‹ˆ๋‹ค.",
607
+ "source_id": "ubuntu-user-management",
608
+ "difficulty": "medium"
609
+ },
610
+ {
611
+ "id": "linux-q67",
612
+ "lesson_id": "linux-systemd-unit",
613
+ "question_type": "multiple_choice",
614
+ "question": "`/etc/systemd/system/myapp.service` ํŒŒ์ผ์„ ์ˆ˜์ •ํ•œ ๋’ค ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋Š”?",
615
+ "options": [
616
+ "systemctl daemon-reload โ†’ systemctl restart myapp",
617
+ "systemctl restart myapp โ†’ systemctl daemon-reload",
618
+ "reboot โ†’ systemctl restart myapp",
619
+ "systemctl reload myapp๋งŒ ์‹คํ–‰"
620
+ ],
621
+ "answer": "systemctl daemon-reload โ†’ systemctl restart myapp",
622
+ "explanation": "์œ ๋‹› ํŒŒ์ผ ์ˆ˜์ • ํ›„ systemd๊ฐ€ ๋ณ€๊ฒฝ์„ ์ธ์‹ํ•˜๋ ค๋ฉด `daemon-reload`๊ฐ€ ๋จผ์ € ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ `restart`๋กœ ์„œ๋น„์Šค๋ฅผ ์žฌ์‹œ์ž‘ํ•ด์•ผ ์ƒˆ ์„ค์ •์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
623
+ "source_id": "manual-linux-systemd",
624
+ "difficulty": "medium"
625
+ },
626
+ {
627
+ "id": "linux-q68",
628
+ "lesson_id": "linux-systemd-unit",
629
+ "question_type": "multiple_choice",
630
+ "question": "์„œ๋น„์Šค ์œ ๋‹› ํŒŒ์ผ์—์„œ `WantedBy=multi-user.target`์˜ ์—ญํ• ์€?",
631
+ "options": [
632
+ "systemctl enable ์‹œ multi-user.target ์‹คํ–‰ ๋‹จ๊ณ„์—์„œ ์„œ๋น„์Šค๊ฐ€ ์ž๋™ ์‹œ์ž‘๋˜๋„๋ก ๋“ฑ๋กํ•œ๋‹ค",
633
+ "์„œ๋น„์Šค๊ฐ€ multi-user ์ดํ›„์—๋งŒ ์ค‘์ง€๋จ์„ ์˜๋ฏธํ•œ๋‹ค",
634
+ "์„œ๋น„์Šค๋ฅผ ๊ทธ๋ž˜ํ”ฝ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‹คํ–‰ํ•˜๊ฒŒ ํ•œ๋‹ค",
635
+ "๋‹ค๋ฅธ ์„œ๋น„์Šค๋ณด๋‹ค ๋จผ์ € ์‹œ์ž‘ํ•˜๊ฒŒ ํ•œ๋‹ค"
636
+ ],
637
+ "answer": "systemctl enable ์‹œ multi-user.target ์‹คํ–‰ ๋‹จ๊ณ„์—์„œ ์„œ๋น„์Šค๊ฐ€ ์ž๋™ ์‹œ์ž‘๋˜๋„๋ก ๋“ฑ๋กํ•œ๋‹ค",
638
+ "explanation": "`WantedBy`๋Š” `systemctl enable` ์‹คํ–‰ ์‹œ ์–ด๋А target์˜ `.wants/` ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ์ƒ์„ฑํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. `multi-user.target`์€ ๋„คํŠธ์›Œํฌ ์„œ๋น„์Šค๊ฐ€ ์ค€๋น„๋œ CLI ํ™˜๊ฒฝ(runlevel 3)์ž…๋‹ˆ๋‹ค.",
639
+ "source_id": "manual-linux-systemd",
640
+ "difficulty": "easy"
641
+ },
642
+ {
643
+ "id": "linux-q69",
644
+ "lesson_id": "linux-at",
645
+ "question_type": "multiple_choice",
646
+ "question": "๋งค์ผ ๋ฐ˜๋ณต์ด ์•„๋‹Œ ๋‚ด์ผ ์ƒˆ๋ฒฝ 3์‹œ์— ํ•œ ๋ฒˆ๋งŒ ๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ๋„๊ตฌ๋Š”?",
647
+ "options": [
648
+ "at",
649
+ "cron",
650
+ "systemd timer",
651
+ "nohup"
652
+ ],
653
+ "answer": "at",
654
+ "explanation": "`at`์€ ์ผํšŒ์„ฑ ์˜ˆ์•ฝ ์‹คํ–‰์„ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. cron์€ ๋ฐ˜๋ณต ์Šค์ผ€์ค„์— ์ ํ•ฉํ•˜๊ณ , nohup์€ ํ„ฐ๋ฏธ๋„ ์ข…๋ฃŒ ํ›„์—๋„ ํ”„๋กœ์„ธ์Šค๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๋กœ ์Šค์ผ€์ค„๋ง ๊ธฐ๋Šฅ์€ ์—†์Šต๋‹ˆ๋‹ค.",
655
+ "source_id": "manual-linux-basic",
656
+ "difficulty": "medium"
657
+ },
658
+ {
659
+ "id": "linux-q70",
660
+ "lesson_id": "linux-docker-basics",
661
+ "question_type": "multiple_choice",
662
+ "question": "nginx ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰ํ•˜๊ณ  ํ˜ธ์ŠคํŠธ ํฌํŠธ 8080์„ ์ปจํ…Œ์ด๋„ˆ ํฌํŠธ 80์— ์—ฐ๊ฒฐํ•˜๋Š” ๋ช…๋ น์€?",
663
+ "options": [
664
+ "docker run -d -p 8080:80 nginx",
665
+ "docker run -p 80:8080 nginx",
666
+ "docker start -p 8080:80 nginx",
667
+ "docker run --port 8080=80 -b nginx"
668
+ ],
669
+ "answer": "docker run -d -p 8080:80 nginx",
670
+ "explanation": "`-d`๋Š” detach(๋ฐฑ๊ทธ๋ผ์šด๋“œ), `-p ํ˜ธ์ŠคํŠธํฌํŠธ:์ปจํ…Œ์ด๋„ˆํฌํŠธ`๋กœ ํฌํŠธ๋ฅผ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. `8080:80`์€ ํ˜ธ์ŠคํŠธ์˜ 8080์„ ์ปจํ…Œ์ด๋„ˆ์˜ 80์œผ๋กœ ํฌ์›Œ๋”ฉํ•ฉ๋‹ˆ๋‹ค.",
671
+ "source_id": "manual-linux-docker",
672
+ "difficulty": "medium"
673
+ },
674
+ {
675
+ "id": "linux-q71",
676
+ "lesson_id": "linux-docker-basics",
677
+ "question_type": "multiple_choice",
678
+ "question": "์‹คํ–‰ ์ค‘์ด๊ฑฐ๋‚˜ ์ค‘์ง€๋œ ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ ๋ชฉ๋ก์„ ๋ณด๋Š” ๋ช…๋ น์€?",
679
+ "options": [
680
+ "docker ps -a",
681
+ "docker ps",
682
+ "docker images",
683
+ "docker container ls"
684
+ ],
685
+ "answer": "docker ps -a",
686
+ "explanation": "`docker ps`๋Š” ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ๋งŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. `-a`(all) ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ค‘์ง€๋œ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ๋ชจ๋‘ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.",
687
+ "source_id": "manual-linux-docker",
688
+ "difficulty": "easy"
689
+ },
690
+ {
691
+ "id": "linux-q72",
692
+ "lesson_id": "linux-grub-boot",
693
+ "question_type": "multiple_choice",
694
+ "question": "`/etc/default/grub`์—์„œ `GRUB_TIMEOUT=10`์œผ๋กœ ๋ณ€๊ฒฝ ํ›„ ์ ์šฉํ•˜๋Š” ๋ช…๋ น์€? (Ubuntu ๊ธฐ์ค€)",
695
+ "options": [
696
+ "update-grub",
697
+ "grub-install",
698
+ "systemctl restart grub",
699
+ "reboot"
700
+ ],
701
+ "answer": "update-grub",
702
+ "explanation": "`/etc/default/grub`์€ ํ…œํ”Œ๋ฆฟ ์—ญํ• ์„ ํ•˜๋ฉฐ, `update-grub`(Ubuntu) ๋˜๋Š” `grub2-mkconfig -o /boot/grub2/grub.cfg`(RHEL)๋ฅผ ์‹คํ–‰ํ•ด์•ผ ์‹ค์ œ GRUB ์„ค์ • ํŒŒ์ผ์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.",
703
+ "source_id": "manual-linux-boot",
704
+ "difficulty": "medium"
705
+ },
706
+ {
707
+ "id": "linux-q73",
708
+ "lesson_id": "linux-grub-boot",
709
+ "question_type": "multiple_choice",
710
+ "question": "์‹œ์Šคํ…œ์ด ๊ทธ๋ž˜ํ”ฝ ํ™˜๊ฒฝ ์—†์ด CLI ๋‹ค์ค‘ ์‚ฌ์šฉ์ž ๋ชจ๋“œ๋กœ ๊ธฐ๋ณธ ๋ถ€ํŒ…๋˜๊ฒŒ ํ•˜๋Š” ๋ช…๋ น์€?",
711
+ "options": [
712
+ "systemctl set-default multi-user.target",
713
+ "systemctl set-default graphical.target",
714
+ "update-grub --target=cli",
715
+ "systemctl disable gdm"
716
+ ],
717
+ "answer": "systemctl set-default multi-user.target",
718
+ "explanation": "`multi-user.target`์€ GUI ์—†์ด ๋„คํŠธ์›Œํฌ์™€ ๋‹ค์ค‘ ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•˜๋Š” target(runlevel 3 ์ƒ๋‹น)์ž…๋‹ˆ๋‹ค. `graphical.target`(runlevel 5)์—์„œ ๋ณ€๊ฒฝํ•˜๋ฉด ๋‹ค์Œ ์žฌ์‹œ์ž‘๋ถ€ํ„ฐ CLI๋กœ ๋ถ€ํŒ…๋ฉ๋‹ˆ๋‹ค.",
719
+ "source_id": "manual-linux-boot",
720
+ "difficulty": "medium"
721
+ },
722
+ {
723
+ "id": "linux-q74",
724
+ "lesson_id": "linux-find",
725
+ "question_type": "multiple_choice",
726
+ "question": "7์ผ ์ด์ƒ ์ˆ˜์ •๋˜์ง€ ์•Š์€ `/var/log`์˜ `.log` ํŒŒ์ผ์„ ์ฐพ์•„ ์ผ๊ด„ ์‚ญ์ œํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ๋ช…๋ น์€?",
727
+ "options": [
728
+ "find /var/log -name '*.log' -mtime +7 -delete",
729
+ "find /var/log -name '*.log' -mtime 7 -rm",
730
+ "grep -r '.log' /var/log | xargs rm",
731
+ "ls -lt /var/log/*.log | tail -n +8 | xargs rm"
732
+ ],
733
+ "answer": "find /var/log -name '*.log' -mtime +7 -delete",
734
+ "explanation": "`-mtime +7`์€ ๋งˆ์ง€๋ง‰ ์ˆ˜์ • ํ›„ 7์ผ ์ดˆ๊ณผ๋œ ํŒŒ์ผ, `-delete`๋Š” find ๋‚ด์žฅ ์‚ญ์ œ ์•ก์…˜์ž…๋‹ˆ๋‹ค. `-mtime 7`(์ •ํ™•ํžˆ 7์ผ)๊ณผ `-mtime +7`(7์ผ ์ดˆ๊ณผ)์˜ ์ฐจ์ด์— ์ฃผ์˜ํ•˜์„ธ์š”.",
735
+ "source_id": "manual-linux-basic",
736
+ "difficulty": "hard"
737
+ },
738
+ {
739
+ "id": "linux-q75",
740
+ "lesson_id": "linux-pipe-redirect",
741
+ "question_type": "multiple_choice",
742
+ "question": "`ls /notexist 2>/dev/null && echo OK`์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š”?",
743
+ "options": [
744
+ "์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š”๋‹ค",
745
+ "OK๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค",
746
+ "์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค",
747
+ "OK์™€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ํ•จ๊ป˜ ์ถœ๋ ฅ๋œ๋‹ค"
748
+ ],
749
+ "answer": "์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š”๋‹ค",
750
+ "explanation": "`2>/dev/null`๋กœ stderr๋ฅผ ๋ฒ„๋ฆฌ๊ณ , `ls /notexist`๊ฐ€ ์‹คํŒจ(exit code != 0)ํ•˜๋ฏ€๋กœ `&&` ๋’ค์˜ `echo OK`๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
751
+ "source_id": "manual-linux-basic",
752
+ "difficulty": "medium"
753
+ },
754
+ {
755
+ "id": "linux-q76",
756
+ "lesson_id": "linux-systemctl",
757
+ "question_type": "multiple_choice",
758
+ "question": "์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ nginx๊ฐ€ ์ž๋™์œผ๋กœ ์‹œ์ž‘๋˜๊ฒŒ ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ด๋ฉฐ enable๋งŒ ๋น ์ง„ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๋ช…๋ น์€?",
759
+ "options": [
760
+ "systemctl enable nginx",
761
+ "systemctl start nginx",
762
+ "systemctl enable --now nginx",
763
+ "systemctl restart nginx"
764
+ ],
765
+ "answer": "systemctl enable nginx",
766
+ "explanation": "์ด๋ฏธ ์‹คํ–‰ ์ค‘์ด๋ฏ€๋กœ `start`๋Š” ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. `enable`๋งŒ ํ•˜๋ฉด ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. `enable --now`๋Š” enable + start๋ฅผ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
767
+ "source_id": "manual-linux-basic",
768
+ "difficulty": "easy"
769
+ },
770
+ {
771
+ "id": "linux-q77",
772
+ "lesson_id": "linux-acl",
773
+ "question_type": "multiple_choice",
774
+ "question": "`/project` ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ `devteam` ๊ทธ๋ฃน ๋ฉค๋ฒ„ ๋ชจ๋‘๊ฐ€ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ , ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ํŒŒ์ผ๋„ ๊ฐ™์€ ๊ทธ๋ฃน ์†Œ์œ ๊ฐ€ ๋˜๊ฒŒ ํ•˜๋Š” ์„ค์ •์€?",
775
+ "options": [
776
+ "chmod 2775 /project && chgrp devteam /project",
777
+ "chmod 777 /project",
778
+ "setfacl -m g:devteam:rw /project",
779
+ "chown devteam /project && chmod 755 /project"
780
+ ],
781
+ "answer": "chmod 2775 /project && chgrp devteam /project",
782
+ "explanation": "`2775`์˜ ์•ž ์ž๋ฆฌ 2๋Š” SGID(Set Group ID) ๋น„ํŠธ์ž…๋‹ˆ๋‹ค. SGID๊ฐ€ ์„ค์ •๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ์ƒ์„ฑ๋œ ํŒŒ์ผ์€ ์ž๋™์œผ๋กœ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ๊ทธ๋ฃน ์†Œ์œ ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. `775`๋Š” ์†Œ์œ ์žยท๊ทธ๋ฃน ์ฝ๊ธฐ/์“ฐ๊ธฐ/์‹คํ–‰, ๊ธฐํƒ€ ์ฝ๊ธฐ/์‹คํ–‰์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.",
783
+ "source_id": "manual-linux-basic",
784
+ "difficulty": "hard"
785
+ },
786
+ {
787
+ "id": "linux-q78",
788
+ "lesson_id": "linux-fstab",
789
+ "question_type": "multiple_choice",
790
+ "question": "`/etc/fstab`์— UUID ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๋•Œ UUID๋ฅผ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์€?",
791
+ "options": [
792
+ "blkid",
793
+ "lsblk -f",
794
+ "fdisk -l",
795
+ "mount -l"
796
+ ],
797
+ "answer": "blkid",
798
+ "explanation": "`blkid`๋Š” ๋ธ”๋ก ๋””๋ฐ”์ด์Šค์˜ UUID, TYPE(ํŒŒ์ผ์‹œ์Šคํ…œ), LABEL์„ ์ถœ๋ ฅ๏ฟฝ๏ฟฝ๋‹ˆ๋‹ค. `lsblk -f`๋„ ์œ ์‚ฌํ•œ ์ •๋ณด๋ฅผ ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. fstab์—๋Š” ์žฅ์น˜๋ช…(/dev/sdb1) ๋Œ€์‹  UUID๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ด ์žฌ๋ถ€ํŒ… ์‹œ ์žฅ์น˜ ์ˆœ์„œ ๋ณ€๊ฒฝ์— ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.",
799
+ "source_id": "manual-linux-basic",
800
+ "difficulty": "medium"
801
+ },
802
+ {
803
+ "id": "linux-q79",
804
+ "lesson_id": "linux-lvm",
805
+ "question_type": "multiple_choice",
806
+ "question": "LVM์—์„œ ๋…ผ๋ฆฌ ๋ณผ๋ฅจ์˜ ํฌ๊ธฐ๋ฅผ 20GB๋กœ ๋Š˜๋ฆฌ๊ณ  ext4 ํŒŒ์ผ์‹œ์Šคํ…œ๋„ ํ•จ๊ป˜ ํ™•์žฅํ•˜๋Š” ๋ช…๋ น ์ˆœ์„œ๋Š”?",
807
+ "options": [
808
+ "lvextend -L 20G /dev/vg0/lv0 โ†’ resize2fs /dev/vg0/lv0",
809
+ "resize2fs /dev/vg0/lv0 โ†’ lvextend -L 20G /dev/vg0/lv0",
810
+ "vgextend vg0 โ†’ resize2fs /dev/vg0/lv0",
811
+ "lvextend -r -L 20G /dev/vg0/lv0 ๋งŒ์œผ๋กœ ์™„๋ฃŒ"
812
+ ],
813
+ "answer": "lvextend -r -L 20G /dev/vg0/lv0 ๋งŒ์œผ๋กœ ์™„๋ฃŒ",
814
+ "explanation": "`lvextend -r`(๋˜๋Š” `--resizefs`)์€ LV ํ™•์žฅ๊ณผ ํŒŒ์ผ์‹œ์Šคํ…œ ํ™•์žฅ์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„๋กœ `resize2fs`๋ฅผ ์‹คํ–‰ํ•ด๋„ ๋˜์ง€๋งŒ `-r` ์˜ต์…˜ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
815
+ "source_id": "manual-linux-basic",
816
+ "difficulty": "hard"
817
+ },
818
+ {
819
+ "id": "linux-q80",
820
+ "lesson_id": "linux-ssh",
821
+ "question_type": "multiple_choice",
822
+ "question": "SSH ์„œ๋ฒ„์—์„œ root ๊ณ„์ •์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์„ ์ฐจ๋‹จํ•˜๊ณ  ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ๋งŒ ํ—ˆ์šฉํ•˜๋ ค๋ฉด `sshd_config`์—์„œ ์„ค์ •ํ•  ํ•ญ๋ชฉ์€?",
823
+ "options": [
824
+ "PermitRootLogin prohibit-password",
825
+ "PermitRootLogin no",
826
+ "PasswordAuthentication no",
827
+ "PermitRootLogin prohibit-password ์™€ PasswordAuthentication no ๋ชจ๋‘"
828
+ ],
829
+ "answer": "PermitRootLogin prohibit-password ์™€ PasswordAuthentication no ๋ชจ๋‘",
830
+ "explanation": "`PermitRootLogin prohibit-password`๋Š” root์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ธ์ฆ๋งŒ ์ฐจ๋‹จ(ํ‚ค ์ธ์ฆ์€ ํ—ˆ์šฉ). `PasswordAuthentication no`๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ธ์ฆ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋‘˜์„ ํ•จ๊ป˜ ์ ์šฉํ•ด์•ผ root ๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์ด ์™„์ „ํžˆ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค.",
831
+ "source_id": "manual-linux-basic",
832
+ "difficulty": "medium"
833
+ },
834
+ {
835
+ "id": "linux-cmd-1",
836
+ "lesson_id": "linux-cp-mv-rm",
837
+ "question_type": "command",
838
+ "question": "`/etc/hosts` ํŒŒ์ผ์„ ๊ถŒํ•œยท์†Œ์œ ์žยทํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด์กดํ•˜๋ฉฐ `/tmp/hosts.bak`์œผ๋กœ ๋ณต์‚ฌํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
839
+ "options": [],
840
+ "answer": "cp -a /etc/hosts /tmp/hosts.bak",
841
+ "explanation": "`cp -a`๋Š” `-dR --preserve=all`์˜ ๋‹จ์ถ•์œผ๋กœ ์†์„ฑ์„ ์™„์ „ํžˆ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ `cp`๋Š” ํƒ€์ž„์Šคํƒฌํ”„์™€ ๊ถŒํ•œ์„ ๋ณด์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
842
+ "source_id": "manual-linux-basic",
843
+ "difficulty": "medium"
844
+ },
845
+ {
846
+ "id": "linux-cmd-2",
847
+ "lesson_id": "linux-grep",
848
+ "question_type": "command",
849
+ "question": "`/etc/ssh/sshd_config` ํŒŒ์ผ์—์„œ ์ฃผ์„(#์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ค„)์„ ์ œ์™ธํ•˜๊ณ  ๋‚ด์šฉ์„ ์ถœ๋ ฅํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
850
+ "options": [],
851
+ "answer": "grep -v '^#' /etc/ssh/sshd_config",
852
+ "explanation": "`-v`๋Š” ํŒจํ„ด๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ์ค„์„ ์ถœ๋ ฅ(๋ฐ˜์ „), `^#`์€ ์ค„์˜ ์‹œ์ž‘์ด #์ธ ์ค„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.",
853
+ "source_id": "manual-linux-basic",
854
+ "difficulty": "medium"
855
+ },
856
+ {
857
+ "id": "linux-cmd-3",
858
+ "lesson_id": "linux-find",
859
+ "question_type": "command",
860
+ "question": "์‹œ์Šคํ…œ ์ „์ฒด์—์„œ SUID ๋น„ํŠธ๊ฐ€ ์„ค์ •๋œ ํŒŒ์ผ์„ ์ฐพ๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
861
+ "options": [],
862
+ "answer": "find / -perm -4000 -type f",
863
+ "explanation": "`-perm -4000`์€ SUID ๋น„ํŠธ(4000)๊ฐ€ ์„ค์ •๋œ ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค. `-`๋ฅผ ์•ž์— ๋ถ™์ด๋ฉด ํ•ด๋‹น ๋น„ํŠธ๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ ๋ชจ๋‘๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.",
864
+ "source_id": "manual-linux-basic",
865
+ "difficulty": "hard"
866
+ },
867
+ {
868
+ "id": "linux-cmd-4",
869
+ "lesson_id": "linux-tar",
870
+ "question_type": "command",
871
+ "question": "`/var/log` ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ gzip ์••์ถ•ํ•ด `/tmp/log_backup.tar.gz`๋กœ ๋งŒ๋“œ๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
872
+ "options": [],
873
+ "answer": "tar -czf /tmp/log_backup.tar.gz /var/log",
874
+ "explanation": "`-c`(create), `-z`(gzip ์••์ถ•), `-f`(ํŒŒ์ผ ์ด๋ฆ„ ์ง€์ •). ์ƒ์„ฑํ•  ์•„์นด์ด๋ธŒ ์ด๋ฆ„ โ†’ ์›๋ณธ ๊ฒฝ๋กœ ์ˆœ์„œ๋กœ ์ธ์ˆ˜๋ฅผ ์”๋‹ˆ๋‹ค.",
875
+ "source_id": "manual-linux-basic",
876
+ "difficulty": "medium"
877
+ },
878
+ {
879
+ "id": "linux-cmd-5",
880
+ "lesson_id": "linux-tar",
881
+ "question_type": "command",
882
+ "question": "`/tmp/log_backup.tar.gz`์˜ ๋‚ด์šฉ์„ ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์••์ถ• ํ•ด์ œํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
883
+ "options": [],
884
+ "answer": "tar -xzf /tmp/log_backup.tar.gz",
885
+ "explanation": "`-x`(extract), `-z`(gzip), `-f`(ํŒŒ์ผ ์ด๋ฆ„). `-C /๊ฒฝ๋กœ`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ํŠน์ • ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์••์ถ• ํ•ด์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
886
+ "source_id": "manual-linux-basic",
887
+ "difficulty": "easy"
888
+ },
889
+ {
890
+ "id": "linux-cmd-6",
891
+ "lesson_id": "linux-chmod",
892
+ "question_type": "command",
893
+ "question": "`/srv/app` ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ๊ทธ ํ•˜์œ„ ๋ชจ๋“  ํŒŒ์ผ์— ์†Œ์œ ์ž๋Š” rwx, ๊ทธ๋ฃน์€ rx, ๊ธฐํƒ€๋Š” ์•„๋ฌด ๊ถŒํ•œ๋„ ์—†๊ฒŒ ์žฌ๊ท€์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
894
+ "options": [],
895
+ "answer": "chmod -R 750 /srv/app",
896
+ "explanation": "`750` = ์†Œ์œ ์ž(7=rwx), ๊ทธ๋ฃน(5=r-x), ๊ธฐํƒ€(0=---). `-R`์€ ์žฌ๊ท€ ์ ์šฉ์ž…๋‹ˆ๋‹ค.",
897
+ "source_id": "manual-linux-basic",
898
+ "difficulty": "medium"
899
+ },
900
+ {
901
+ "id": "linux-cmd-7",
902
+ "lesson_id": "linux-acl",
903
+ "question_type": "command",
904
+ "question": "`/project` ๋””๋ ‰ํ„ฐ๋ฆฌ์— `devteam` ๊ทธ๋ฃน์ด ์ฝ๊ธฐยท์“ฐ๊ธฐยท์‹คํ–‰ ๊ถŒํ•œ์„ ๊ฐ–๋„๋ก ACL์„ ์„ค์ •ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
905
+ "options": [],
906
+ "answer": "setfacl -m g:devteam:rwx /project",
907
+ "explanation": "`setfacl -m`(modify) `g:๊ทธ๋ฃน๋ช…:๊ถŒํ•œ` ํ˜•์‹์œผ๋กœ ๊ทธ๋ฃน ACL์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๊ถŒํ•œ์€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
908
+ "source_id": "manual-linux-basic",
909
+ "difficulty": "hard"
910
+ },
911
+ {
912
+ "id": "linux-cmd-8",
913
+ "lesson_id": "linux-useradd",
914
+ "question_type": "command",
915
+ "question": "ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๋ฉด์„œ `alice`๋ผ๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋งŒ๋“ค๊ณ  ๊ธฐ๋ณธ ์…ธ์„ `/bin/bash`๋กœ ์„ค์ •ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
916
+ "options": [],
917
+ "answer": "useradd -m -s /bin/bash alice",
918
+ "explanation": "`-m`์€ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ, `-s`๋Š” ๊ธฐ๋ณธ ์…ธ ์ง€์ •์ž…๋‹ˆ๋‹ค. ์ด ์˜ต์…˜ ์—†์ด useradd๋งŒ ์‹คํ–‰ํ•˜๋ฉด ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
919
+ "source_id": "ubuntu-user-management",
920
+ "difficulty": "medium"
921
+ },
922
+ {
923
+ "id": "linux-cmd-9",
924
+ "lesson_id": "linux-chage",
925
+ "question_type": "command",
926
+ "question": "`bob` ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งŒ๋ฃŒ ์ •์ฑ…(์ตœ๋Œ€ ์‚ฌ์šฉ์ผ, ๊ฒฝ๊ณ ์ผ ๋“ฑ)์„ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
927
+ "options": [],
928
+ "answer": "chage -l bob",
929
+ "explanation": "`chage -l`(list)์€ ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ •์ฑ…(๋งˆ์ง€๋ง‰ ๋ณ€๊ฒฝ์ผ, ์ตœ๋Œ€ ์‚ฌ์šฉ์ผ, ๊ฒฝ๊ณ ์ผ, ๊ณ„์ • ๋งŒ๋ฃŒ์ผ)์„ ๋ณด๊ธฐ ์ข‹๊ฒŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
930
+ "source_id": "ubuntu-user-management",
931
+ "difficulty": "medium"
932
+ },
933
+ {
934
+ "id": "linux-cmd-10",
935
+ "lesson_id": "linux-systemctl",
936
+ "question_type": "command",
937
+ "question": "`nginx` ์„œ๋น„์Šค๊ฐ€ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
938
+ "options": [],
939
+ "answer": "systemctl status nginx",
940
+ "explanation": "`systemctl status`๋Š” ์„œ๋น„์Šค ์ƒํƒœ, ์ตœ๊ทผ ๋กœ๊ทธ, PID, ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํ•œ๋ˆˆ์— ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. `is-active nginx`๋Š” active/inactive๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
941
+ "source_id": "manual-linux-basic",
942
+ "difficulty": "easy"
943
+ },
944
+ {
945
+ "id": "linux-cmd-11",
946
+ "lesson_id": "linux-systemd-unit",
947
+ "question_type": "command",
948
+ "question": "์œ ๋‹› ํŒŒ์ผ ์ˆ˜์ • ํ›„ systemd๊ฐ€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ธ์‹ํ•˜๋„๋ก ์ƒˆ๋กœ๊ณ ์นจํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
949
+ "options": [],
950
+ "answer": "systemctl daemon-reload",
951
+ "explanation": "์œ ๋‹› ํŒŒ์ผ(`/etc/systemd/system/*.service`)์„ ์ˆ˜์ •ํ•˜๋ฉด systemd๊ฐ€ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `daemon-reload` ํ›„ `restart`ํ•ด์•ผ ๋ณ€๊ฒฝ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.",
952
+ "source_id": "manual-linux-systemd",
953
+ "difficulty": "medium"
954
+ },
955
+ {
956
+ "id": "linux-cmd-12",
957
+ "lesson_id": "linux-journalctl",
958
+ "question_type": "command",
959
+ "question": "`nginx` ์„œ๋น„์Šค์˜ ๋กœ๊ทธ๋ฅผ ์ตœ๊ทผ 50์ค„๋งŒ ์ถœ๋ ฅํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
960
+ "options": [],
961
+ "answer": "journalctl -u nginx -n 50",
962
+ "explanation": "`-u`๋Š” ํŠน์ • ์œ ๋‹›(์„œ๋น„์Šค) ํ•„ํ„ฐ, `-n 50`์€ ์ตœ๊ทผ 50์ค„ ์ถœ๋ ฅ์ž…๋‹ˆ๋‹ค. `-f`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ๋ฉ๋‹ˆ๋‹ค.",
963
+ "source_id": "manual-linux-basic",
964
+ "difficulty": "medium"
965
+ },
966
+ {
967
+ "id": "linux-cmd-13",
968
+ "lesson_id": "linux-cron",
969
+ "question_type": "command",
970
+ "question": "ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ crontab์„ ํŽธ์ง‘ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
971
+ "options": [],
972
+ "answer": "crontab -e",
973
+ "explanation": "`crontab -e`๋Š” ๊ธฐ๋ณธ ํŽธ์ง‘๊ธฐ๋กœ ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ crontab์„ ์—ฝ๋‹ˆ๋‹ค. `-l`์€ ๋ชฉ๋ก ํ™•์ธ, `-r`์€ ์‚ญ์ œ์ž…๋‹ˆ๋‹ค.",
974
+ "source_id": "manual-linux-basic",
975
+ "difficulty": "easy"
976
+ },
977
+ {
978
+ "id": "linux-cmd-14",
979
+ "lesson_id": "linux-df-du-mount",
980
+ "question_type": "command",
981
+ "question": "ํ˜„์žฌ ๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ์˜ ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฌ์šด ๋‹จ์œ„(GB, MB)๋กœ ์ถœ๋ ฅํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
982
+ "options": [],
983
+ "answer": "df -h",
984
+ "explanation": "`df -h`๋Š” `-h`(human-readable) ์˜ต์…˜์œผ๋กœ GB, MB ๋‹จ์œ„๋กœ ์šฉ๋Ÿ‰์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. `-T`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ํŒŒ์ผ์‹œ์Šคํ…œ ์œ ํ˜•๋„ ํ•จ๊ป˜ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.",
985
+ "source_id": "manual-linux-basic",
986
+ "difficulty": "easy"
987
+ },
988
+ {
989
+ "id": "linux-cmd-15",
990
+ "lesson_id": "linux-lvm",
991
+ "question_type": "command",
992
+ "question": "์‹œ์Šคํ…œ์˜ ๋ชจ๋“  ๋…ผ๋ฆฌ ๋ณผ๋ฅจ(LV) ๋ชฉ๋ก์„ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
993
+ "options": [],
994
+ "answer": "lvs",
995
+ "explanation": "`lvs`(LV summary)๋Š” ๊ฐ„๊ฒฐํ•œ LV ๋ชฉ๋ก์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `lvdisplay`๋Š” ๋” ์ž์„ธํ•œ ์ •๋ณด๋ฅผ, `lvscan`์€ ๋ชจ๋“  LV์™€ ํ™œ์„ฑ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.",
996
+ "source_id": "manual-linux-basic",
997
+ "difficulty": "medium"
998
+ },
999
+ {
1000
+ "id": "linux-cmd-16",
1001
+ "lesson_id": "linux-fstab",
1002
+ "question_type": "command",
1003
+ "question": "`/etc/fstab`์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ํŒŒ์ผ์‹œ์Šคํ…œ์„ ๋งˆ์šดํŠธํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1004
+ "options": [],
1005
+ "answer": "mount -a",
1006
+ "explanation": "`mount -a`๋Š” fstab์˜ noauto ์˜ต์…˜์ด ์—†๋Š” ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค. fstab ์ˆ˜์ • ํ›„ ์žฌ๋ถ€ํŒ… ์—†์ด ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
1007
+ "source_id": "manual-linux-basic",
1008
+ "difficulty": "medium"
1009
+ },
1010
+ {
1011
+ "id": "linux-cmd-17",
1012
+ "lesson_id": "linux-ssh-key",
1013
+ "question_type": "command",
1014
+ "question": "ED25519 ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ SSH ํ‚ค ์Œ์„ ์ƒ์„ฑํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1015
+ "options": [],
1016
+ "answer": "ssh-keygen -t ed25519",
1017
+ "explanation": "ED25519๋Š” RSA 4096๋ณด๋‹ค ์งง๊ณ  ๊ฐ•๋ ฅํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ ๊ณต๊ฐœ ํ‚ค(`~/.ssh/id_ed25519.pub`)๋ฅผ ์„œ๋ฒ„์˜ `authorized_keys`์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.",
1018
+ "source_id": "manual-linux-basic",
1019
+ "difficulty": "medium"
1020
+ },
1021
+ {
1022
+ "id": "linux-cmd-18",
1023
+ "lesson_id": "linux-sed",
1024
+ "question_type": "command",
1025
+ "question": "`/etc/hosts` ํŒŒ์ผ์—์„œ `oldhost`๋ฅผ `newhost`๋กœ ๋ชจ๋‘ ๋ฐ”๊ฟ” ํŒŒ์ผ์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1026
+ "options": [],
1027
+ "answer": "sed -i 's/oldhost/newhost/g' /etc/hosts",
1028
+ "explanation": "`-i`๋Š” ํŒŒ์ผ์„ ์ง์ ‘ ์ˆ˜์ •(in-place), `s/ํŒจํ„ด/๊ต์ฒด/g`์—์„œ `g`๋Š” ์ค„ ๋‚ด ๋ชจ๋“  ์ผ์น˜ ํ•ญ๋ชฉ์„ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค. `-i.bak`์œผ๋กœ ๋ฐฑ์—…๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1029
+ "source_id": "manual-linux-basic",
1030
+ "difficulty": "hard"
1031
+ },
1032
+ {
1033
+ "id": "linux-cmd-19",
1034
+ "lesson_id": "linux-text-pipeline",
1035
+ "question_type": "command",
1036
+ "question": "`/var/log/auth.log`์—์„œ `Failed` ๋ฌธ์ž์—ด์ด ํฌํ•จ๋œ ์ค„๋งŒ ๋ฝ‘์•„ IP ์ฃผ์†Œ ํ•„๋“œ(์ฒซ ๋ฒˆ์งธ ๊ณต๋ฐฑ ๊ธฐ์ค€ ํ•„๋“œ 11)๋ณ„ ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ถœ๋ ฅํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1037
+ "options": [],
1038
+ "answer": "grep 'Failed' /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn",
1039
+ "explanation": "grepโ†’awk๋กœ ํ•„๋“œ ์ถ”์ถœโ†’sort๋กœ ์ •๋ ฌโ†’uniq -c๋กœ ์ค‘๋ณต ์นด์šดํŠธโ†’sort -rn์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ. ๋ธŒ๋ฃจํŠธํฌ์Šค ๊ณต๊ฒฉ IP๋ฅผ ํŒŒ์•…ํ•˜๋Š” ์‹ค์ „ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
1040
+ "source_id": "manual-linux-basic",
1041
+ "difficulty": "hard"
1042
+ },
1043
+ {
1044
+ "id": "linux-cmd-20",
1045
+ "lesson_id": "linux-docker-basics",
1046
+ "question_type": "command",
1047
+ "question": "์‹คํ–‰ ์ค‘์ธ `webserver` ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ `/bin/bash` ์…ธ์„ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ํ„ฐ๋ฏธ๋„๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1048
+ "options": [],
1049
+ "answer": "docker exec -it webserver /bin/bash",
1050
+ "explanation": "`docker exec -it`๋Š” ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. `-i`(stdin ์œ ์ง€), `-t`(ํ„ฐ๋ฏธ๋„ ํ• ๋‹น). ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ๋””๋ฒ„๊น…ํ•  ๋•Œ ํ•„์ˆ˜ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
1051
+ "source_id": "manual-linux-docker",
1052
+ "difficulty": "medium"
1053
+ },
1054
+ {
1055
+ "id": "linux-cmd-21",
1056
+ "lesson_id": "linux-mkfs-fsck",
1057
+ "question_type": "command",
1058
+ "question": "`/dev/sdb1` ํŒŒํ‹ฐ์…˜์˜ UUID๋ฅผ ํ™•์ธํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1059
+ "options": [],
1060
+ "answer": "blkid /dev/sdb1",
1061
+ "explanation": "`blkid`๋Š” ๋ธ”๋ก ๋””๋ฐ”์ด์Šค์˜ UUID, ํŒŒ์ผ์‹œ์Šคํ…œ ์œ ํ˜•(TYPE), ๋ ˆ์ด๋ธ”์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. fstab์— UUID ๊ธฐ๋ฐ˜์œผ๋กœ ๋“ฑ๋กํ•  ๋•Œ ์ด ๋ช…๋ น์œผ๋กœ ๊ฐ’์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
1062
+ "source_id": "manual-linux-storage",
1063
+ "difficulty": "medium"
1064
+ },
1065
+ {
1066
+ "id": "linux-cmd-22",
1067
+ "lesson_id": "linux-at",
1068
+ "question_type": "command",
1069
+ "question": "1์‹œ๊ฐ„ ํ›„์— `/usr/local/bin/cleanup.sh`๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•˜๋„๋ก ์˜ˆ์•ฝํ•˜๋Š” ๋ช…๋ น์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
1070
+ "options": [],
1071
+ "answer": "echo '/usr/local/bin/cleanup.sh' | at now + 1 hour",
1072
+ "explanation": "`echo '๋ช…๋ น' | at ์‹œ๊ฐ„` ํ˜•์‹์œผ๋กœ ์ผํšŒ์„ฑ ์ž‘์—…์„ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์•ฝ ๋ชฉ๋ก์€ `atq`, ์ทจ์†Œ๋Š” `atrm ๋ฒˆํ˜ธ`๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
1073
+ "source_id": "manual-linux-basic",
1074
+ "difficulty": "hard"
1075
+ }
1076
+ ]
cert_study_app/content/loader.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """JSON-based content loader for lessons, quizzes, and practice tasks.
2
+
3
+ New content lives in cert_study_app/content/{track}/{type}.json.
4
+ Returns raw dicts โ€” callers convert to dataclass types.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ from pathlib import Path
10
+
11
+ CONTENT_DIR = Path(__file__).parent
12
+
13
+
14
+ def _load(track_id: str, filename: str) -> list[dict]:
15
+ path = CONTENT_DIR / track_id / filename
16
+ if not path.exists():
17
+ return []
18
+ try:
19
+ data = json.loads(path.read_text(encoding="utf-8"))
20
+ return data if isinstance(data, list) else []
21
+ except Exception:
22
+ return []
23
+
24
+
25
+ def load_lesson_dicts(track_id: str) -> list[dict]:
26
+ return _load(track_id, "lessons.json")
27
+
28
+
29
+ def load_quiz_dicts(track_id: str) -> list[dict]:
30
+ return _load(track_id, "quizzes.json")
31
+
32
+
33
+ def load_practice_dicts(track_id: str) -> list[dict]:
34
+ return _load(track_id, "practices.json")
cert_study_app/content/tool_docs/lessons.json ADDED
@@ -0,0 +1,529 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "docs-ollama-models",
4
+ "certification": "Docs Study",
5
+ "title": "Ollama ๋ชจ๋ธ ์‹คํ–‰ ํ๋ฆ„",
6
+ "level": "์ž…๋ฌธ",
7
+ "related_practices": ["docs-lab-1", "docs-lab-2"],
8
+ "summary": "Ollama๋Š” ๋กœ์ปฌ์—์„œ LLM ๋ชจ๋ธ์„ ๋‚ด๋ ค๋ฐ›๊ณ  ์‹คํ–‰ํ•ด CLI ๋˜๋Š” REST API๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒฝ๋Ÿ‰ ๋ชจ๋ธ ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. `ollama run`์œผ๋กœ ๋Œ€ํ™”ํ•˜๊ณ , `ollama serve`๋กœ API ์„œ๋ฒ„๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
9
+ "example": "# ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ๋ฐ ์‹คํ–‰\nollama pull qwen2.5:14b\nollama run qwen2.5:14b\n# ์„ค์น˜๋œ ๋ชจ๋ธ ๋ชฉ๋ก\nollama list",
10
+ "common_mistake": "๋ชจ๋ธ ์ด๋ฆ„์— ํƒœ๊ทธ(:14b, :latest ๋“ฑ)๋ฅผ ์ƒ๋žตํ•˜๋ฉด `:latest`๋กœ ์ž๋™ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์ด ๋กœ์ปฌ์— ์—†์„ ๋•Œ `ollama run`์ด ์ž๋™์œผ๋กœ pullํ•ฉ๋‹ˆ๋‹ค.",
11
+ "keywords": ["ollama", "pull", "run", "serve", "model tag", "GGUF", "local LLM"],
12
+ "source_id": "official-docs-ollama",
13
+ "details": [
14
+ "**`ollama run`**: ๋ชจ๋ธ์ด ์—†์œผ๋ฉด ์ž๋™ ๋‹ค์šด๋กœ๋“œ ํ›„ ๋Œ€ํ™”ํ˜• ์„ธ์…˜ ์‹œ์ž‘. `--nowordwrap` ์˜ต์…˜์œผ๋กœ ๊ธด ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ.",
15
+ "**`ollama serve`**: 11434 ํฌํŠธ์—์„œ OpenAI ํ˜ธํ™˜ REST API ์ œ๊ณต. `OLLAMA_HOST=0.0.0.0`์œผ๋กœ ์™ธ๋ถ€ ์ ‘๊ทผ ํ—ˆ์šฉ ๊ฐ€๋Šฅ.",
16
+ "๋ชจ๋ธ ํŒŒ์ผ์€ `~/.ollama/models`์— GGUF ํ˜•์‹์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. `Modelfile`๋กœ ์ปค์Šคํ…€ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
17
+ ]
18
+ },
19
+ {
20
+ "id": "docs-ollama-api",
21
+ "certification": "Docs Study",
22
+ "title": "Ollama REST API ํ˜ธ์ถœ",
23
+ "level": "์ž…๋ฌธ",
24
+ "related_practices": ["docs-lab-3"],
25
+ "summary": "Ollama๋Š” `localhost:11434`์—์„œ REST API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. `/api/generate`(์™„์„ฑํ˜•), `/api/chat`(๋Œ€ํ™”ํ˜•), `/api/embeddings`(์ž„๋ฒ ๋”ฉ) ์—”๋“œํฌ์ธํŠธ๋ฅผ OpenAI ํ˜ธํ™˜ ํ˜•์‹์œผ๋กœ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
26
+ "example": "# chat ์—”๋“œํฌ์ธํŠธ\ncurl http://localhost:11434/api/chat -d '{\n \"model\": \"qwen2.5:7b\",\n \"messages\": [{\"role\": \"user\", \"content\": \"์•ˆ๋…•\"}],\n \"stream\": false\n}'",
27
+ "common_mistake": "Ollama ์„œ๋ฒ„(`ollama serve`)๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€ ์•Š๊ฑฐ๋‚˜ ๋ชจ๋ธ์ด ๋กœ์ปฌ์— ์—†์œผ๋ฉด API ํ˜ธ์ถœ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € `ollama list`๋กœ ๋ชจ๋ธ ์กด์žฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.",
28
+ "keywords": ["ollama", "REST API", "generate", "chat", "embeddings", "localhost:11434", "stream"],
29
+ "source_id": "official-docs-ollama",
30
+ "details": [
31
+ "**OpenAI ํ˜ธํ™˜ API**: `http://localhost:11434/v1/chat/completions`๋กœ OpenAI Python SDK๋‚˜ LangChain์˜ `ChatOpenAI(base_url=...)` ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
32
+ "**์ŠคํŠธ๋ฆฌ๋ฐ**: `\"stream\": true` ์„ค์ • ์‹œ ์‘๋‹ต์„ JSON ์ค„ ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ. `\"done\": true` ์ค„์ด ์™„๋ฃŒ ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค.",
33
+ "**์ž„๋ฒ ๋”ฉ**: `POST /api/embeddings`์— `{\"model\":\"nomic-embed-text\", \"prompt\":\"ํ…์ŠคํŠธ\"}`๋ฅผ ๋ณด๋‚ด๋ฉด ๋ฒกํ„ฐ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."
34
+ ]
35
+ },
36
+ {
37
+ "id": "docs-dotenv",
38
+ "certification": "Docs Study",
39
+ "title": "python-dotenv โ€” ํ™˜๊ฒฝ๋ณ€์ˆ˜ & .env ๊ด€๋ฆฌ",
40
+ "level": "์ž…๋ฌธ",
41
+ "related_practices": ["docs-lab-4"],
42
+ "summary": "python-dotenv๋Š” `.env` ํŒŒ์ผ์˜ ํ‚ค-๊ฐ’ ์Œ์„ `os.environ`์— ์ž๋™์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. API ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์ฝ”๋“œ์— ์ง์ ‘ ์“ฐ์ง€ ์•Š๊ณ  ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌํ•˜๋Š” ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.",
43
+ "example": "# .env ํŒŒ์ผ\nOPENAI_API_KEY=sk-...\nOLLAMA_BASE_URL=http://localhost:11434\n\n# Python ์ฝ”๋“œ\nfrom dotenv import load_dotenv\nimport os\n\nload_dotenv() # .env ํŒŒ์ผ ๋กœ๋“œ\napi_key = os.getenv('OPENAI_API_KEY')",
44
+ "common_mistake": "`load_dotenv()`๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— `os.getenv()`๋ฅผ ์“ฐ๋ฉด ํ•ญ์ƒ `None`์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ํŒŒ์ผ ์ƒ๋‹จ์—์„œ ๊ฐ€์žฅ ๋จผ์ € `load_dotenv()`๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”.",
45
+ "keywords": ["dotenv", "load_dotenv", ".env", "os.getenv", "ํ™˜๊ฒฝ๋ณ€์ˆ˜", "API ํ‚ค"],
46
+ "source_id": "official-docs-python",
47
+ "details": [
48
+ "**์šฐ์„ ์ˆœ์œ„**: `load_dotenv(override=False)`(๊ธฐ๋ณธ๊ฐ’)๋Š” ์ด๋ฏธ ์„ค์ •๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. CI/CD ํ™˜๊ฒฝ์—์„œ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ .env๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.",
49
+ "**.gitignore**: `.env`๋Š” ๋ฐ˜๋“œ์‹œ `.gitignore`์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. `.env.example`์€ ํ‚ค ์ด๋ฆ„๋งŒ ์ ์–ด ํŒ€์›์—๊ฒŒ ์–ด๋–ค ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•œ์ง€ ์•Œ๋ ค์ฃผ๋Š” ์šฉ๋„๋กœ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.",
50
+ "**Streamlit ์—ฐ๋™**: ๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ `load_dotenv()`๋กœ ํ‚ค๋ฅผ ๋กœ๋“œํ•˜๊ณ , ๋ฐฐํฌ ์‹œ์—๋Š” `st.secrets`๋กœ ์ ‘๊ทผํ•˜๋„๋ก ๋ถ„๊ธฐํ•˜๊ฑฐ๋‚˜ ์ฒ˜์Œ๋ถ€ํ„ฐ `st.secrets`๋งŒ ์“ฐ๋Š” ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค."
51
+ ]
52
+ },
53
+ {
54
+ "id": "docs-prompt-template",
55
+ "certification": "Docs Study",
56
+ "title": "ChatPromptTemplate โ€” ํ”„๋กฌํ”„ํŠธ ๊ตฌ์กฐํ™”",
57
+ "level": "์ž…๋ฌธ",
58
+ "related_practices": ["docs-lab-5", "docs-lab-6"],
59
+ "summary": "ChatPromptTemplate์€ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ, ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€, ๋ณ€์ˆ˜ ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋ฅผ ๊ตฌ์กฐํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ f-string ๋Œ€์‹  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  LangChain ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐ๋˜๋Š” ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์”๋‹ˆ๋‹ค.",
60
+ "example": "from langchain_core.prompts import ChatPromptTemplate\n\nprompt = ChatPromptTemplate.from_messages([\n (\"system\", \"๋‹น์‹ ์€ ๋„์›€์ด ๋˜๋Š” AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ: {context}\"),\n (\"human\", \"{question}\")\n])\n\n# ์ฒด์ธ์— ์—ฐ๊ฒฐ\nchain = prompt | llm | StrOutputParser()\nchain.invoke({\"context\": \"...\", \"question\": \"์š”์•ฝํ•ด์ค˜\"})",
61
+ "common_mistake": "`PromptTemplate`์€ ๋‹จ์ˆœ ๋ฌธ์ž์—ด ํฌ๋งท์šฉ(๋ณ€์ˆ˜ ํ•˜๋‚˜), `ChatPromptTemplate`์€ ์—ญํ• ์ด ๊ตฌ๋ถ„๋œ ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด์šฉ์ž…๋‹ˆ๋‹ค. RAG์—์„œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์™€ ์งˆ๋ฌธ์„ ๊ตฌ๋ถ„ํ•˜๋ ค๋ฉด ํ•ญ์ƒ ChatPromptTemplate์„ ์”๋‹ˆ๋‹ค.",
62
+ "keywords": ["ChatPromptTemplate", "from_messages", "system", "human", "placeholder", "PromptTemplate"],
63
+ "source_id": "official-docs-langchain",
64
+ "details": [
65
+ "**MessagesPlaceholder**: ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ์ฃผ์ž…ํ•  ๋•Œ ์”๋‹ˆ๋‹ค. `(\"placeholder\", \"{chat_history}\")`๋กœ ์„ ์–ธํ•˜๋ฉด ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๊ทธ ์ž๋ฆฌ์— ํŽผ์ณ์ง‘๋‹ˆ๋‹ค.",
66
+ "**partial**: `prompt.partial(context=\"๊ณ ์ •๊ฐ’\")`์œผ๋กœ ํŠน์ • ๋ณ€์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์ฑ„์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. context๋Š” ๊ณ ์ •ํ•˜๊ณ  question๋งŒ ๋Ÿฐํƒ€์ž„์— ๋ฐ›์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
67
+ "**ํƒ€์ž… ์•ˆ์ „์„ฑ**: from_messages์— `(\"system\", \"...\")` ํŠœํ”Œ ๋Œ€์‹  `SystemMessage(content=\"...\")`๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํŠœํ”Œ ๋ฐฉ์‹์ด ๋” ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค."
68
+ ]
69
+ },
70
+ {
71
+ "id": "docs-document-loaders",
72
+ "certification": "Docs Study",
73
+ "title": "LangChain Document Loaders โ€” ๋ฌธ์„œ ์ผ๊ด„ ๋กœ๋“œ",
74
+ "level": "์ž…๋ฌธ",
75
+ "related_practices": ["docs-lab-7"],
76
+ "summary": "Document Loader๋Š” PDF, ๋งˆํฌ๋‹ค์šด, ์›น ํŽ˜์ด์ง€, ๋””๋ ‰ํ† ๋ฆฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์†Œ์Šค์—์„œ ๋ฌธ์„œ๋ฅผ LangChain `Document` ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. RAG ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ฒซ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.",
77
+ "example": "from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader\n\n# ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด ๋ชจ๋“  .md ํŒŒ์ผ ๋กœ๋“œ\nloader = DirectoryLoader('./notes', glob='**/*.md')\ndocs = loader.load()\n\n# ๊ฐ ๋ฌธ์„œ์˜ ์ถœ์ฒ˜ ํ™•์ธ\nfor doc in docs:\n print(doc.metadata['source'], len(doc.page_content))",
78
+ "common_mistake": "`loader.load()`๋Š” ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ๋Š” `loader.lazy_load()`๋กœ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
79
+ "keywords": ["DirectoryLoader", "PyPDFLoader", "WebBaseLoader", "glob", "metadata", "source", "lazy_load"],
80
+ "source_id": "official-docs-langchain",
81
+ "details": [
82
+ "**metadata['source']**: ๋กœ๋“œ๋œ ๋ฌธ์„œ๋Š” `metadata` ๋”•์…”๋„ˆ๋ฆฌ์— ํŒŒ์ผ ๊ฒฝ๋กœ(`source`)๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. RAG ๋‹ต๋ณ€์— ์ถœ์ฒ˜๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ์ด ๊ฐ’์„ ์”๋‹ˆ๋‹ค.",
83
+ "**glob ํŒจํ„ด**: `glob='**/*.md'`๋Š” ํ•˜์œ„ ํด๋”๊นŒ์ง€ ์žฌ๊ท€ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. `glob='*.pdf'`๋Š” ํ˜„์žฌ ํด๋”๋งŒ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
84
+ "**WebBaseLoader**: URL ๋ฆฌ์ŠคํŠธ๋ฅผ ๋„˜๊ธฐ๋ฉด ์›น ํŽ˜์ด์ง€๋ฅผ ํฌ๋กค๋งํ•ด Document๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. BeautifulSoup ๊ธฐ๋ฐ˜์ด๋ฉฐ `bs_kwargs`๋กœ ํŒŒ์‹ฑ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
85
+ ]
86
+ },
87
+ {
88
+ "id": "docs-langchain-rag",
89
+ "certification": "Docs Study",
90
+ "title": "LangChain RAG ๊ธฐ๋ณธ ํ๋ฆ„",
91
+ "level": "์ž…๋ฌธ",
92
+ "related_practices": ["docs-lab-8", "docs-lab-9"],
93
+ "summary": "LangChain์€ ๋ฌธ์„œ ๋กœ๋“œ โ†’ ๋ถ„ํ•  โ†’ ์ž„๋ฒ ๋”ฉ โ†’ ๊ฒ€์ƒ‰ โ†’ ์ƒ์„ฑ์˜ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ์ปดํฌ๋„ŒํŠธ ์กฐํ•ฉ์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. Retriever๊ฐ€ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ฐพ์•„ LLM ์ปจํ…์ŠคํŠธ๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.",
94
+ "example": "from langchain_community.vectorstores import Chroma\nretriever = Chroma.from_documents(docs, embeddings).as_retriever(k=3)\nchain = (\n {\"context\": retriever, \"question\": RunnablePassthrough()}\n | prompt | llm | StrOutputParser()\n)",
95
+ "common_mistake": "Retriever๊ฐ€ ์ฐพ์€ ๋ฌธ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ์ •๋‹ต์œผ๋กœ ๋ฏฟ์ง€ ๋งˆ์„ธ์š”. ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๊ฐ€ ์งˆ๋ฌธ๊ณผ ๋‹ค๋ฅธ ๋งฅ๋ฝ์ผ ์ˆ˜ ์žˆ์–ด ํ”„๋กฌํ”„ํŠธ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ '์•„๋ž˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด'๋ผ๊ณ  ์ง€์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
96
+ "keywords": ["langchain", "RAG", "retriever", "vectorstore", "document loader", "text splitter"],
97
+ "source_id": "official-docs-langchain",
98
+ "details": [
99
+ "**ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„**: DocumentLoader โ†’ TextSplitter(์ฒญํฌ ๋ถ„ํ• ) โ†’ Embeddings โ†’ VectorStore โ†’ Retriever โ†’ LLM Chain.",
100
+ "**TextSplitter**: `RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)`์ด ์ผ๋ฐ˜์  ์‹œ์ž‘์ . overlap์ด ์žˆ์–ด์•ผ ์ฒญํฌ ๊ฒฝ๊ณ„์—์„œ ๋ฌธ๋งฅ ์†์‹ค์„ ์ค„์ž…๋‹ˆ๋‹ค.",
101
+ "`MultiQueryRetriever`๋‚˜ `ContextualCompressionRetriever`๋กœ ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœ ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰๋ณด๋‹ค ๊ด€๋ จ ์ฒญํฌ ์„ ๋ณ„ ์ •ํ™•๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค."
102
+ ]
103
+ },
104
+ {
105
+ "id": "docs-lcel",
106
+ "certification": "Docs Study",
107
+ "title": "LangChain LCEL โ€” ์ฒด์ธ ๊ตฌ์„ฑ",
108
+ "level": "์ž…๋ฌธ",
109
+ "related_practices": ["docs-lab-10", "docs-lab-11"],
110
+ "summary": "LCEL(LangChain Expression Language)์€ `|` ํŒŒ์ดํ”„ ์—ฐ์‚ฐ์ž๋กœ Runnable ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง๋ ฌยท๋ณ‘๋ ฌ ์—ฐ๊ฒฐํ•ด ์ฒด์ธ์„ ์„ ์–ธ์ ์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
111
+ "example": "from langchain_core.runnables import RunnablePassthrough\nchain = (\n RunnablePassthrough.assign(context=retriever)\n | prompt\n | llm\n | StrOutputParser()\n)\nresult = chain.invoke({\"question\": \"What is RAG?\"})",
112
+ "common_mistake": "์ฒด์ธ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅ ํƒ€์ž…์ด ๋งž์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. LLM์€ `str` ๋˜๋Š” `BaseMessage`๋ฅผ ๋ฐ›๊ณ  ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ์•ž ๋‹จ๊ณ„๊ฐ€ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์ด ์ค‘๊ฐ„์—์„œ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
113
+ "keywords": ["LCEL", "Runnable", "pipe", "chain", "invoke", "stream", "batch"],
114
+ "source_id": "official-docs-langchain",
115
+ "details": [
116
+ "**3๊ฐ€์ง€ ์‹คํ–‰ ๋ฐฉ์‹**: `invoke`(๋‹จ์ผ ๋™๊ธฐ), `stream`(์ŠคํŠธ๋ฆฌ๋ฐ yield), `batch`(๋ฆฌ์ŠคํŠธ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ). ์ŠคํŠธ๋ฆฌ๋ฐ UI์—๋Š” `stream`์„ ์”๋‹ˆ๋‹ค.",
117
+ "**RunnablePassthrough**: ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ „๋‹ฌ. `.assign(key=fn)`์œผ๋กœ ์ถ”๊ฐ€ ํ‚ค๋ฅผ ๋ถ™์—ฌ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.",
118
+ "**RunnableParallel**: `{\"a\": fn1, \"b\": fn2}` ํ˜•ํƒœ๋กœ ์—ฌ๋Ÿฌ ์ฒด์ธ์„ ๋™์‹œ์— ์‹คํ–‰ํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค."
119
+ ]
120
+ },
121
+ {
122
+ "id": "docs-vectorstore",
123
+ "certification": "Docs Study",
124
+ "title": "VectorStore์™€ Retriever โ€” ๋ฒกํ„ฐ ๊ฒ€์ƒ‰",
125
+ "level": "์ž…๋ฌธ",
126
+ "related_practices": ["docs-lab-11", "docs-lab-12"],
127
+ "summary": "๋ฌธ์„œ๋ฅผ ์ž„๋ฒ ๋”ฉ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ด ์ €์žฅํ•˜๊ณ , ์ฟผ๋ฆฌ์™€ ์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„๋กœ ๊ด€๋ จ ์ฒญํฌ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. Chroma, FAISS, pgvector ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ๋ฅผ LangChain์ด ์ถ”์ƒํ™”ํ•ฉ๋‹ˆ๋‹ค.",
128
+ "example": "from langchain_community.vectorstores import Chroma\nfrom langchain_ollama import OllamaEmbeddings\n\nembeddings = OllamaEmbeddings(model='nomic-embed-text')\ndb = Chroma.from_documents(docs, embeddings, persist_directory='./db')\nretriever = db.as_retriever(search_type='mmr', search_kwargs={'k': 4})",
129
+ "common_mistake": "์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฒกํ„ฐ ์ฐจ์›์ด ๋‹ฌ๋ผ์ ธ ๊ธฐ์กด ์ปฌ๋ ‰์…˜๊ณผ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ๋ฐ”๊ฟ€ ๋•Œ๋Š” ์ปฌ๋ ‰์…˜์„ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
130
+ "keywords": ["vectorstore", "Chroma", "FAISS", "embedding", "similarity", "MMR", "retriever"],
131
+ "source_id": "official-docs-langchain",
132
+ "details": [
133
+ "**๊ฒ€์ƒ‰ ์œ ํ˜•**: `similarity`(์ˆœ์ˆ˜ ์œ ์‚ฌ๋„ ์ƒ์œ„ k๊ฐœ), `mmr`(MMR: ๋‹ค์–‘์„ฑ ๊ณ ๋ ค), `similarity_score_threshold`(์ž„๊ณ„๊ฐ’ ์ด์ƒ๋งŒ).",
134
+ "**Chroma**: ๋กœ์ปฌ ํŒŒ์ผ๋กœ ์ €์žฅ ๊ฐ€๋Šฅ(`persist_directory`). ํ”„๋กœํ† ํƒ€์ž…์— ์ ํ•ฉ. ํ”„๋กœ๋•์…˜์€ pgvector(PostgreSQL), Qdrant, Pinecone ๊ถŒ์žฅ.",
135
+ "์ž„๋ฒ ๋”ฉ์€ ํ…์ŠคํŠธ์˜ ์˜๋ฏธ์  ์œ ์‚ฌ๋„๋ฅผ ์žก์ง€๋งŒ, ์ •ํ™•ํ•œ ํ‚ค์›Œ๋“œ ๋งค์นญ์ด ํ•„์š”ํ•˜๋ฉด BM25 ๋“ฑ ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰๊ณผ `EnsembleRetriever`๋กœ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ตฌ์„ฑ์„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค."
136
+ ]
137
+ },
138
+ {
139
+ "id": "docs-streamlit-state",
140
+ "certification": "Docs Study",
141
+ "title": "Streamlit session_state โ€” ์ƒํƒœ ๊ด€๋ฆฌ",
142
+ "level": "์ž…๋ฌธ",
143
+ "related_practices": ["docs-lab-16", "docs-lab-17"],
144
+ "summary": "Streamlit์€ ์ƒํ˜ธ์ž‘์šฉ๋งˆ๋‹ค ์Šคํฌ๋ฆฝํŠธ ์ „์ฒด๋ฅผ rerunํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํŠผ ํด๋ฆญยท์ž…๋ ฅ ๊ฒฐ๊ณผ๋ฅผ rerun ์ดํ›„์—๋„ ์œ ์ง€ํ•˜๋ ค๋ฉด `st.session_state`์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
145
+ "example": "if 'count' not in st.session_state:\n st.session_state.count = 0\nif st.button('์ฆ๊ฐ€'):\n st.session_state.count += 1\nst.write(f'ํด๋ฆญ ํšŸ์ˆ˜: {st.session_state.count}')",
146
+ "common_mistake": "์ผ๋ฐ˜ ์ „์—ญ ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜ ๋‚ด ์ง€์—ญ ๋ณ€์ˆ˜๋Š” rerun๋งˆ๋‹ค ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ์ƒํƒœ ๋ณด์กด์ด ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ’์€ ๋ฐ˜๋“œ์‹œ `st.session_state`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
147
+ "keywords": ["session_state", "rerun", "widget", "state management", "callback"],
148
+ "source_id": "official-docs-streamlit",
149
+ "details": [
150
+ "**์ดˆ๊ธฐํ™” ํŒจํ„ด**: `st.session_state.setdefault('key', default_value)` ๋˜๋Š” `if 'key' not in st.session_state:` ๋ธ”๋ก์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.",
151
+ "**Widget key**: `st.text_input('์ด๋ฆ„', key='username')`์œผ๋กœ ์œ„์ ฏ ๊ฐ’์ด ์ž๋™์œผ๋กœ `st.session_state.username`์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
152
+ "**callback**: `on_change=my_func`์œผ๋กœ ์œ„์ ฏ ๋ณ€๊ฒฝ ์‹œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. callback ์•ˆ์—์„œ session_state๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด rerun ์ „์— ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค."
153
+ ]
154
+ },
155
+ {
156
+ "id": "docs-streamlit-cache",
157
+ "certification": "Docs Study",
158
+ "title": "Streamlit Cache โ€” ์บ์‹œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ",
159
+ "level": "์ž…๋ฌธ",
160
+ "related_practices": ["docs-lab-18"],
161
+ "summary": "rerun๋งˆ๋‹ค ๋ฐ˜๋ณต ์‹คํ–‰๋˜๋Š” ๋น„์šฉ ๋†’์€ ๊ณ„์‚ฐยท๋ฆฌ์†Œ์Šค ๋กœ๋”ฉ์„ ์บ์‹œํ•ด ์„ฑ๋Šฅ์„ ๋†’์ž…๋‹ˆ๋‹ค. `@st.cache_data`๋Š” ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ๋ฐ์ดํ„ฐ, `@st.cache_resource`๋Š” DBยท๋ชจ๋ธ ๊ฐ™์€ ๊ณต์œ  ๊ฐ์ฒด์— ์”๋‹ˆ๋‹ค.",
162
+ "example": "@st.cache_resource\ndef load_model():\n return SentenceTransformer('all-MiniLM-L6-v2')\n\n@st.cache_data(ttl=300)\ndef fetch_data(query: str):\n return db.execute(query).fetchall()",
163
+ "common_mistake": "`cache_data`์™€ `cache_resource`๋ฅผ ํ˜ผ๋™ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค. LLM ๋ชจ๋ธ์ด๋‚˜ DB ์—ฐ๊ฒฐ์ฒ˜๋Ÿผ ๊ณต์œ ํ•ด์•ผ ํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋ฐ˜๋“œ์‹œ `cache_resource`๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
164
+ "keywords": ["cache_data", "cache_resource", "TTL", "rerun", "performance"],
165
+ "source_id": "official-docs-streamlit",
166
+ "details": [
167
+ "**`@st.cache_data`**: ํ•จ์ˆ˜ ์ธ์ˆ˜๋ฅผ ํ‚ค๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ณต์‚ฌํ•ด ์บ์‹œ. DataFrame, ๋ฆฌ์ŠคํŠธ ๋“ฑ ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ๋ฐ˜ํ™˜๊ฐ’์— ์ ํ•ฉ. `ttl` ์ธ์ˆ˜๋กœ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์„ค์ • ๊ฐ€๋Šฅ.",
168
+ "**`@st.cache_resource`**: ์‹ฑ๊ธ€ํ†ค์ฒ˜๋Ÿผ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•ด ๋ชจ๋“  ์„ธ์…˜์ด ๊ณต์œ . ๋ชจ๋ธ ๋กœ๋”ฉ, DB ์—ฐ๊ฒฐ, ๋ฒกํ„ฐ ์Šคํ† ์–ด ์ดˆ๊ธฐํ™”์— ์ ํ•ฉ.",
169
+ "์บ์‹œ ๋ฌดํšจํ™”: `st.cache_data.clear()` / `st.cache_resource.clear()`๋กœ ์ˆ˜๋™ ์ดˆ๊ธฐํ™”. ํ•จ์ˆ˜ ๋ณธ๋ฌธ์ด ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ์บ์‹œ๊ฐ€ ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค."
170
+ ]
171
+ },
172
+ {
173
+ "id": "docs-streamlit-secrets",
174
+ "certification": "Docs Study",
175
+ "title": "Streamlit Secrets โ€” ๋น„๋ฐ€ ๊ด€๋ฆฌ",
176
+ "level": "์ž…๋ฌธ",
177
+ "related_practices": ["docs-lab-4"],
178
+ "summary": "API ํ‚ค, DB ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์ฝ”๋“œ์— ๋„ฃ์ง€ ์•Š๊ณ  `.streamlit/secrets.toml` ํŒŒ์ผ ๋˜๋Š” Streamlit Cloud์˜ Secrets UI์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
179
+ "example": "# .streamlit/secrets.toml\n[database]\nhost = 'localhost'\npassword = 'secret'\n\n# ์ฝ”๋“œ์—์„œ ์ ‘๊ทผ\nimport streamlit as st\ndb_pass = st.secrets['database']['password']",
180
+ "common_mistake": "`.streamlit/secrets.toml`์„ `.gitignore`์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ Git์— ์ปค๋ฐ‹๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ์ œ์™ธํ•˜์„ธ์š”.",
181
+ "keywords": ["secrets", "secrets.toml", "API key", ".gitignore", "Streamlit Cloud"],
182
+ "source_id": "official-docs-streamlit",
183
+ "details": [
184
+ "๋กœ์ปฌ ๊ฐœ๋ฐœ: `.streamlit/secrets.toml`์— TOML ํ˜•์‹์œผ๋กœ ์ €์žฅ. ์•ฑ ์ฝ”๋“œ์—์„œ `st.secrets`๋กœ ๋”•์…”๋„ˆ๋ฆฌ์ฒ˜๋Ÿผ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
185
+ "Streamlit Cloud: ์•ฑ Settings โ†’ Secrets์— ๊ฐ™์€ TOML ๋‚ด์šฉ์„ ๋ถ™์—ฌ๋„ฃ์œผ๋ฉด ๋ฐฐํฌ ์‹œ ์ž๋™ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.",
186
+ "`st.secrets`๋Š” `os.environ`๋ณด๋‹ค ๊ตฌ์กฐํ™”๋œ ์ ‘๊ทผ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. `st.secrets['key']`์™€ `st.secrets.key` ๋‘ ๋ฐฉ์‹ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
187
+ ]
188
+ },
189
+ {
190
+ "id": "docs-hf-spaces",
191
+ "certification": "Docs Study",
192
+ "title": "Hugging Face Spaces โ€” ์•ฑ ๋ฐฐํฌ",
193
+ "level": "์ž…๋ฌธ",
194
+ "related_practices": ["docs-lab-19"],
195
+ "summary": "Hugging Face Spaces๋Š” Streamlit, Gradio, Docker ์•ฑ์„ ๋ฌด๋ฃŒ๋กœ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค. README.md ์ƒ๋‹จ์˜ YAML ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ SDK์™€ ํŒŒ์ผ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
196
+ "example": "# README.md ์ƒ๋‹จ (YAML frontmatter)\n---\ntitle: My App\nsdk: streamlit\napp_file: app.py\npython_version: '3.11'\n---",
197
+ "common_mistake": "API ํ‚ค ๋“ฑ ๋ฏผ๊ฐ ์ •๋ณด๋Š” ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ๋งˆ์„ธ์š”. Space Settings โ†’ Secrets์— ๋“ฑ๋กํ•˜๊ณ  ์ฝ”๋“œ์—์„œ๋Š” `os.environ['MY_KEY']`๋กœ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.",
198
+ "keywords": ["huggingface", "spaces", "SDK", "secrets", "app_file", "README metadata"],
199
+ "source_id": "official-docs-huggingface",
200
+ "details": [
201
+ "**๋ฌด๋ฃŒ ํ‹ฐ์–ด**: CPU ๊ธฐ๋ณธ ์ œ๊ณต, ๊ณต๊ฐœ Space๋Š” ๋ฌด๋ฃŒ. GPU๋‚˜ ๋น„๊ณต๊ฐœ Space๋Š” ์œ ๋ฃŒ ํ”Œ๋žœ ๋˜๋Š” ZeroGPU(๋ฌด๋ฃŒ ๊ณต์œ  GPU) ํ™œ์šฉ.",
202
+ "**Secrets**: `Settings โ†’ Repository secrets`์— ํ‚ค-๊ฐ’์œผ๋กœ ๋“ฑ๋ก. ๋นŒ๋“œ ๋ฐ ๋Ÿฐํƒ€์ž„์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.",
203
+ "Space๋ฅผ Duplicate(๋ณต์ œ)ํ•˜๋ฉด fork์ฒ˜๋Ÿผ ๋…๋ฆฝ๋œ Space๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํƒ€์ธ์˜ ๋ชจ๋ธยท์•ฑ์„ ๋‚ด Secrets์™€ ํ•จ๊ป˜ ์ปค์Šคํ…€ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค."
204
+ ]
205
+ },
206
+ {
207
+ "id": "docs-hf-docker-space",
208
+ "certification": "Docs Study",
209
+ "title": "Hugging Face Docker Space",
210
+ "level": "์ž…๋ฌธ",
211
+ "related_practices": ["docs-lab-20"],
212
+ "summary": "Dockerfile๋กœ ์•ฑ ํ™˜๊ฒฝ์„ ์ง์ ‘ ์ •์˜ํ•˜๋Š” Space ๋ฐฐํฌ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. Streamlit/Gradio SDK ์ œ์•ฝ์„ ๋„˜์–ด FastAPI, ์ปค์Šคํ…€ ์„œ๋ฒ„ ๋“ฑ ์–ด๋–ค ์•ฑ๋„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
213
+ "example": "# README.md\n---\ntitle: My API\nsdk: docker\napp_port: 7860\n---\n\n# Dockerfile\nFROM python:3.11-slim\nCOPY . .\nRUN pip install -r requirements.txt\nEXPOSE 7860\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"7860\"]",
214
+ "common_mistake": "Docker Space๋Š” ํฌํŠธ 7860์„ ๊ธฐ๋ณธ์œผ๋กœ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค(`app_port: 7860`). ์•ฑ์ด ๋‹ค๋ฅธ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด README ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ `app_port`์™€ ์ผ์น˜์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
215
+ "keywords": ["docker", "Dockerfile", "Spaces", "app_port", "FastAPI", "custom server"],
216
+ "source_id": "official-docs-huggingface",
217
+ "details": [
218
+ "**๋นŒ๋“œ ์บ์‹œ**: Space๋Š” ์ด์ „ ๋นŒ๋“œ ๋ ˆ์ด์–ด๋ฅผ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. `COPY requirements.txt .` ํ›„ `RUN pip install`์„ ๋ฐฐ์น˜ํ•ด ์˜์กด์„ฑ ๋ ˆ์ด์–ด๋ฅผ ์•ฑ ์ฝ”๋“œ๋ณด๋‹ค ์•ž์— ๋‘๋ฉด ๋นŒ๋“œ๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค.",
219
+ "**Secrets**: `docker build --build-arg`๊ฐ€ ์•„๋‹Œ Runtime Secrets๋กœ ์ฃผ์ž…ํ•ด์•ผ ์ด๋ฏธ์ง€์— ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `os.environ`์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
220
+ "**๋ชจ๋ธ ํŒŒ์ผ**: ๋Œ€ํ˜• ๋ชจ๋ธ์€ Space ์ €์žฅ์†Œ์— ์ง์ ‘ ๋„ฃ์ง€ ๋ง๊ณ  `huggingface_hub.snapshot_download()`๋กœ ๋Ÿฐํƒ€์ž„์— ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ Dataset ์ €์žฅ์†Œ๋ฅผ ๋งˆ์šดํŠธํ•˜๋Š” ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค."
221
+ ]
222
+ },
223
+ {
224
+ "id": "docs-fastapi",
225
+ "certification": "Docs Study",
226
+ "title": "FastAPI โ€” LLM ์„œ๋น„์Šค API ์„œ๋ฒ„",
227
+ "level": "์ค‘๊ธ‰",
228
+ "related_practices": ["docs-lab-21"],
229
+ "summary": "FastAPI๋Š” Python ํƒ€์ž… ํžŒํŠธ ๊ธฐ๋ฐ˜์˜ ๊ณ ์„ฑ๋Šฅ ๋น„๋™๊ธฐ API ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. RAG ์ฒด์ธ์ด๋‚˜ LLM ๊ธฐ๋Šฅ์„ REST API ์—”๋“œํฌ์ธํŠธ๋กœ ๋…ธ์ถœํ•  ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ž…๋‹ˆ๋‹ค.",
230
+ "example": "from fastapi import FastAPI\nfrom pydantic import BaseModel\n\napp = FastAPI()\n\nclass Query(BaseModel):\n question: str\n\n@app.post('/ask')\nasync def ask(q: Query):\n answer = chain.invoke(q.question)\n return {\"answer\": answer}\n\n# ์‹คํ–‰\n# uvicorn main:app --reload",
231
+ "common_mistake": "LangChain ์ฒด์ธ ๊ฐ™์€ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด๋ฅผ ์š”์ฒญ๋งˆ๋‹ค ์ƒ์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”. ๋ชจ๋“ˆ ๋ ˆ๋ฒจ์—์„œ ํ•œ ๋ฒˆ ์ดˆ๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜ `lifespan` ์ด๋ฒคํŠธ๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ ๋กœ๋“œํ•˜์„ธ์š”.",
232
+ "keywords": ["fastapi", "uvicorn", "pydantic", "async", "REST API", "endpoint", "lifespan"],
233
+ "source_id": "official-docs-fastapi",
234
+ "details": [
235
+ "**Pydantic**: ์š”์ฒญ/์‘๋‹ต ์Šคํ‚ค๋งˆ๋ฅผ `BaseModel`๋กœ ์ •์˜ํ•˜๋ฉด ์ž๋™ ๊ฒ€์ฆ, OpenAPI ๋ฌธ์„œ(`/docs`), JSON ์ง๋ ฌํ™”๊ฐ€ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.",
236
+ "**์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต**: `StreamingResponse`์™€ async generator๋กœ LLM ์ถœ๋ ฅ์„ Server-Sent Events(SSE)๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
237
+ "**lifespan**: `@asynccontextmanager`๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ ๋ชจ๋ธยทDB ์—ฐ๊ฒฐ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์ข…๋ฃŒ ์‹œ ์ •๋ฆฌํ•˜๋Š” ํŒจํ„ด. FastAPI 0.95+ ๊ถŒ์žฅ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค."
238
+ ]
239
+ },
240
+ {
241
+ "id": "docs-langsmith",
242
+ "certification": "Docs Study",
243
+ "title": "LangSmith โ€” LLM ์ถ”์  & ํ‰๊ฐ€",
244
+ "level": "์ค‘๊ธ‰",
245
+ "related_practices": ["docs-lab-13"],
246
+ "summary": "LangSmith๋Š” LangChain ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋ชจ๋“  LLM ํ˜ธ์ถœ, ํ”„๋กฌํ”„ํŠธ, ์‘๋‹ต, ์ง€์—ฐ์‹œ๊ฐ„์„ ์ž๋™ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„๊ทธ ๋””๋ฒ„๊น…, ํ”„๋กฌํ”„ํŠธ ๋น„๊ต, ํ‰๊ฐ€ ๋ฐ์ดํ„ฐ์…‹ ๊ด€๋ฆฌ๋ฅผ ํ•œ ๊ณณ์—์„œ ํ•ฉ๋‹ˆ๋‹ค.",
247
+ "example": "import os\nos.environ['LANGCHAIN_TRACING_V2'] = 'true'\nos.environ['LANGCHAIN_API_KEY'] = 'ls__...'\nos.environ['LANGCHAIN_PROJECT'] = 'my-rag-app'\n\n# ์ดํ›„ LangChain ์ฝ”๋“œ๋Š” ์ž๋™์œผ๋กœ ์ถ”์ ๋จ\nresult = chain.invoke({'question': '์งˆ๋ฌธ'})",
248
+ "common_mistake": "์ถ”์  ํ™œ์„ฑํ™”๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3๊ฐœ(`TRACING_V2=true`, `API_KEY`, `PROJECT`)๊ฐ€ ๋ชจ๋‘ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์กฐ์šฉํžˆ ์ถ”์ ์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.",
249
+ "keywords": ["langsmith", "tracing", "LangChain", "debugging", "evaluation", "dataset", "prompt"],
250
+ "source_id": "official-docs-langchain",
251
+ "details": [
252
+ "**Run ํŠธ๋ ˆ์ด์Šค**: ์ฒด์ธ ์‹คํ–‰๋งˆ๋‹ค tree ํ˜•ํƒœ๋กœ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅยทํ† ํฐ ์ˆ˜ยท์ง€์—ฐ์‹œ๊ฐ„์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์–ด๋А ๋‹จ๊ณ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋Š”์ง€ ์ฆ‰์‹œ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
253
+ "**๋ฐ์ดํ„ฐ์…‹ & ํ‰๊ฐ€**: ํ”„๋กœ๋•์…˜ ์˜ค๋ฅ˜ ์ผ€์ด์Šค๋ฅผ Dataset์œผ๋กœ ์ €์žฅ โ†’ Evaluator(LLM-as-judge ๋“ฑ)๋กœ ์ž๋™ ํ‰๊ฐ€ โ†’ CI์— ํ†ตํ•ฉํ•˜๋Š” ํ๋ฆ„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
254
+ "**Playground**: LangSmith UI์—์„œ ํŠน์ • Run์˜ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ฐ”๋กœ ์ˆ˜์ •ํ•ด ์žฌ์‹คํ–‰ ๊ฐ€๋Šฅ. ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง ๋ฐ˜๋ณต์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค."
255
+ ]
256
+ },
257
+ {
258
+ "id": "docs-guardrails",
259
+ "certification": "Docs Study",
260
+ "title": "Guardrails AI โ€” LLM ์ถœ๋ ฅ ๊ฒ€์ฆ",
261
+ "level": "์ค‘๊ธ‰",
262
+ "related_practices": ["docs-lab-14"],
263
+ "summary": "Guardrails AI๋Š” LLM ์ž…๋ ฅ/์ถœ๋ ฅ์— ๊ฒ€์ฆ ๊ทœ์น™(Guard)์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์œ ํ•ด ์ฝ˜ํ…์ธ  ์ฐจ๋‹จ, JSON ์Šคํ‚ค๋งˆ ๊ฐ•์ œ, PII ๋งˆ์Šคํ‚น ๋“ฑ ํ”„๋กœ๋•์…˜ LLM ์•ฑ์˜ ์•ˆ์ „์žฅ์น˜ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.",
264
+ "example": "from guardrails import Guard\nfrom guardrails.hub import ToxicLanguage\n\nguard = Guard().use(ToxicLanguage(threshold=0.5, on_fail='exception'))\n\nresult = guard(\n llm_api=openai.chat.completions.create,\n prompt='์‚ฌ์šฉ์ž ์ž…๋ ฅ: {{user_input}}',\n user_input=user_message\n)",
265
+ "common_mistake": "Guard๋Š” LLM ํ˜ธ์ถœ์„ ๋ž˜ํ•‘ํ•˜๋ฏ€๋กœ ์‘๋‹ต ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”์ฒญ์— heavy Guard๋ฅผ ๊ฑธ๋ฉด ์ง€์—ฐ์ด ์ปค์ง€๋‹ˆ, ์ž…๋ ฅ ๊ฒ€์ฆ๊ณผ ์ถœ๋ ฅ ๊ฒ€์ฆ์„ ๋ถ„๋ฆฌํ•ด ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์ ์šฉํ•˜์„ธ์š”.",
266
+ "keywords": ["guardrails", "guard", "validation", "toxic", "PII", "JSON schema", "on_fail", "safety"],
267
+ "source_id": "official-docs-guardrails",
268
+ "details": [
269
+ "**Guard ํ—ˆ๋ธŒ**: `guardrails hub install hub://...`์œผ๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ Guard๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ToxicLanguage, ValidJson, DetectPII, RegexMatch ๋“ฑ ์ˆ˜์‹ญ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.",
270
+ "**on_fail ๋™์ž‘**: `'exception'`(์˜ˆ์™ธ ๋ฐœ์ƒ), `'reask'`(LLM์— ์žฌ์š”์ฒญ), `'fix'`(์ž๋™ ์ˆ˜์ •), `'noop'`(ํ†ต๊ณผ) ์ค‘ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
271
+ "**๊ตฌ์กฐํ™” ์ถœ๋ ฅ**: `Guard.from_pydantic(OutputModel)`์œผ๋กœ LLM์ด ํ•ญ์ƒ ์ง€์ •ํ•œ ์Šคํ‚ค๋งˆ์˜ JSON์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
272
+ ]
273
+ },
274
+ {
275
+ "id": "docs-gradio",
276
+ "certification": "Docs Study",
277
+ "title": "Gradio โ€” ML ๋ฐ๋ชจ UI",
278
+ "level": "์ค‘๊ธ‰",
279
+ "related_practices": ["docs-lab-22"],
280
+ "summary": "Gradio๋Š” ML ๋ชจ๋ธ ๋ฐ๋ชจ๋ฅผ ๋ช‡ ์ค„๋กœ ๋งŒ๋“œ๋Š” UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. `gr.Interface`๋กœ ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ๋ฉด ์ฆ‰์‹œ ์›น UI๊ฐ€ ์ƒ๊ธฐ๊ณ , HuggingFace Spaces์™€ ๋„ค์ดํ‹ฐ๋ธŒ ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค.",
281
+ "example": "import gradio as gr\n\ndef chat(message, history):\n response = chain.invoke(message)\n return response\n\ndemo = gr.ChatInterface(\n fn=chat,\n title='RAG ์ฑ—๋ด‡',\n examples=['๋ฌธ์„œ ์š”์•ฝํ•ด์ค˜', '์ฃผ์š” ํ‚ค์›Œ๋“œ๋Š”?']\n)\ndemo.launch()",
282
+ "common_mistake": "Streamlit๊ณผ ๋‹ฌ๋ฆฌ Gradio๋Š” ํ•จ์ˆ˜ ๋‹จ์œ„๋กœ UI๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” `gr.State()`๋กœ, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•  ๋•Œ๋Š” `gr.Blocks()` ์ปจํ…์ŠคํŠธ๋ฅผ ์”๋‹ˆ๋‹ค.",
283
+ "keywords": ["gradio", "Interface", "Blocks", "ChatInterface", "State", "HuggingFace", "demo"],
284
+ "source_id": "official-docs-gradio",
285
+ "details": [
286
+ "**Interface vs Blocks**: `gr.Interface`๋Š” ์ž…๋ ฅโ†’ํ•จ์ˆ˜โ†’์ถœ๋ ฅ ๋‹จ์ˆœ ๊ตฌ์กฐ, `gr.Blocks`๋Š” ๋ ˆ์ด์•„์›ƒ๊ณผ ์ด๋ฒคํŠธ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑ. LLM ์ฑ—๋ด‡์—๋Š” `gr.ChatInterface`๊ฐ€ ๊ฐ€์žฅ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
287
+ "**์ŠคํŠธ๋ฆฌ๋ฐ**: `gr.ChatInterface(fn=chat)`์—์„œ `chat`์„ generator ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  `yield`๋กœ ํ† ํฐ์„ ๋‚ด๋ณด๋‚ด๋ฉด ์ž๋™์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ UI๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.",
288
+ "**HF Spaces ํ†ตํ•ฉ**: `demo.launch()`๋งŒ ํ•˜๋ฉด HF Spaces Gradio SDK๋กœ ์ฆ‰์‹œ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ๋ณ„๋„ ์„œ๋ฒ„ ์„ค์ • ์—†์ด `sdk: gradio`๋งŒ README์— ์„ ์–ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค."
289
+ ]
290
+ },
291
+ {
292
+ "id": "docs-chroma",
293
+ "certification": "Docs Study",
294
+ "title": "Chroma โ€” ์˜์† ๋ฒกํ„ฐ DB",
295
+ "level": "์ค‘๊ธ‰",
296
+ "related_practices": ["docs-chroma-1", "docs-chroma-2"],
297
+ "summary": "Chroma๋Š” ์žฌ์‹œ์ž‘ํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜๋Š” ์˜คํ”ˆ์†Œ์Šค ๋ฒกํ„ฐ DB์ž…๋‹ˆ๋‹ค. FAISS๋Š” `save_local()`์„ ์ง์ ‘ ํ˜ธ์ถœํ•ด์•ผ ํ•˜์ง€๋งŒ, Chroma๋Š” `persist_directory`๋งŒ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋กœ์ปฌ DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
298
+ "example": "from langchain_chroma import Chroma\n\n# ์ƒ์„ฑ & ์ž๋™ ์ €์žฅ\ndb = Chroma.from_documents(\n chunks,\n embeddings,\n persist_directory='./chroma_db'\n)\n\n# ๋‹ค์Œ ์‹คํ–‰ ์‹œ โ€” ์ž„๋ฒ ๋”ฉ ์žฌ๊ณ„์‚ฐ ์—†์ด ๋กœ๋“œ\ndb = Chroma(\n persist_directory='./chroma_db',\n embedding_function=embeddings\n)",
299
+ "common_mistake": "FAISS์ฒ˜๋Ÿผ `save_local()`์„ ๋ณ„๋„๋กœ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. `persist_directory`๋ฅผ ์ง€์ •ํ•˜๋ฉด ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋‹จ, `persist_directory`๋ฅผ ์ƒ๋žตํ•˜๋ฉด ์ธ๋ฉ”๋ชจ๋ฆฌ๋กœ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
300
+ "keywords": ["Chroma", "persist_directory", "from_documents", "collection", "chromadb", "์˜์†"],
301
+ "source_id": "official-docs-chromadb",
302
+ "details": [
303
+ "**FAISS vs Chroma**: FAISS๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ `save_local()`๋กœ ์ˆ˜๋™ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Chroma๋Š” `persist_directory`๋งŒ ์ง€์ •ํ•˜๋ฉด SQLite ๊ธฐ๋ฐ˜ ๋กœ์ปฌ DB์— ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
304
+ "**์ปฌ๋ ‰์…˜**: Chroma๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ปฌ๋ ‰์…˜ ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `collection_name` ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์—ฌ๋Ÿฌ ๋ฌธ์„œ ์„ธํŠธ๋ฅผ ๋ถ„๋ฆฌํ•ด ์ €์žฅํ•˜๊ณ  ๊ฐœ๋ณ„๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
305
+ "**๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง**: `db.similarity_search(query, filter={\"source\": \"file.pdf\"})`์ฒ˜๋Ÿผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐ๊ฑด์œผ๋กœ ๊ฒ€์ƒ‰ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. FAISS๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค."
306
+ ]
307
+ },
308
+ {
309
+ "id": "docs-sentence-transformers",
310
+ "certification": "Docs Study",
311
+ "title": "sentence-transformers โ€” ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ",
312
+ "level": "์ค‘๊ธ‰",
313
+ "related_practices": ["docs-embed-1"],
314
+ "summary": "sentence-transformers๋Š” HuggingFace ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. OpenAI API ์—†์ด ์™„์ „ ๋กœ์ปฌ RAG๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ํ•œ๊ตญ์–ด ํŠนํ™” ๋ชจ๋ธ๋„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
315
+ "example": "from langchain_huggingface import HuggingFaceEmbeddings\n\nembeddings = HuggingFaceEmbeddings(\n model_name='sentence-transformers/all-MiniLM-L6-v2'\n)\n\n# ์ž„๋ฒ ๋”ฉ ๋ฒกํ„ฐ ์ƒ์„ฑ\nvector = embeddings.embed_query('ํŒŒ์ด์ฌ์ด ๋ญ์•ผ?')\nprint(len(vector)) # 384์ฐจ์›\n\n# Chroma์™€ ํ•จ๊ป˜\ndb = Chroma.from_documents(chunks, embeddings, persist_directory='./db')",
316
+ "common_mistake": "์ฒซ ์‹คํ–‰ ์‹œ ๋ชจ๋ธ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๋ฏ€๋กœ ๋А๋ฆฝ๋‹ˆ๋‹ค. Streamlit ์•ฑ์—์„œ๋Š” `@st.cache_resource`๋กœ ๊ฐ์‹ธ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œํ•˜์„ธ์š”. ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ์ค‘๊ฐ„์— ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด VectorStore์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š์•„ ์ „์ฒด ์žฌ์ธ๋ฑ์‹ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
317
+ "keywords": ["HuggingFaceEmbeddings", "sentence-transformers", "all-MiniLM-L6-v2", "embed_query", "๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ", "384์ฐจ์›"],
318
+ "source_id": "official-docs-huggingface",
319
+ "details": [
320
+ "**๋ชจ๋ธ ์„ ํƒ**: `all-MiniLM-L6-v2`๋Š” ๋น ๋ฅด๊ณ  ๊ฒฝ๋Ÿ‰(384์ฐจ์›), `all-mpnet-base-v2`๋Š” ํ’ˆ์งˆ์ด ๋” ๋†’์ง€๋งŒ ๋А๋ฆฝ๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด๊ฐ€ ๋งŽ๋‹ค๋ฉด `jhgan/ko-sroberta-multitask` ๊ฐ™์€ ํ•œ๊ตญ์–ด ํŠนํ™” ๋ชจ๋ธ์„ ์”๋‹ˆ๋‹ค.",
321
+ "**OpenAI vs ๋กœ์ปฌ**: OpenAI `text-embedding-3-small`์€ ํ’ˆ์งˆ์ด ๋†’์ง€๋งŒ API ๋น„์šฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ์€ ๋ฌด๋ฃŒ์ด๋‚˜ GPU ์—†์ด๋Š” ๋Œ€์šฉ๋Ÿ‰์—์„œ ๋А๋ฆฝ๋‹ˆ๋‹ค.",
322
+ "**์ฐจ์› ๊ณ ์ • ์ฃผ์˜**: ํ•œ ๋ฒˆ ์ž„๋ฒ ๋”ฉํ•œ ๋ชจ๋ธ์„ ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด ์ธ๋ฑ์Šค์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ ๋ชจ๋ธ๋กœ ์ „์ฒด ์ธ๋ฑ์Šค๋ฅผ ์žฌ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
323
+ ]
324
+ },
325
+ {
326
+ "id": "docs-ragas",
327
+ "certification": "Docs Study",
328
+ "title": "Ragas โ€” RAG ํ’ˆ์งˆ ์ž๋™ ํ‰๊ฐ€",
329
+ "level": "์ค‘๊ธ‰",
330
+ "related_practices": ["docs-ragas-1", "docs-ragas-2"],
331
+ "summary": "Ragas๋Š” RAG ์‹œ์Šคํ…œ์˜ ํ’ˆ์งˆ์„ ์ž๋™์œผ๋กœ ์ˆ˜์น˜ํ™”ํ•ฉ๋‹ˆ๋‹ค. Faithfulness(ํ™˜๊ฐ ์—†๋Š”๊ฐ€), Answer Relevancy(์งˆ๋ฌธ๊ณผ ๋งž๋Š” ๋‹ต์ธ๊ฐ€), Context Precision(๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ž˜ ์ฐพ์•˜๋Š”๊ฐ€)์„ LLM-as-judge๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.",
332
+ "example": "from ragas import evaluate\nfrom ragas.metrics import faithfulness, answer_relevancy, context_precision\nfrom datasets import Dataset\n\ndata = {\n 'question': ['LangChain์ด ๋ญ์•ผ?'],\n 'answer': ['LangChain์€ LLM ์•ฑ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.'],\n 'contexts': [['LangChain์€ LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']],\n 'ground_truth': ['LangChain์€ LLM ์•ฑ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']\n}\n\ndataset = Dataset.from_dict(data)\nresult = evaluate(dataset, metrics=[faithfulness, answer_relevancy, context_precision])\nprint(result)",
333
+ "common_mistake": "Ragas๋Š” ํ‰๊ฐ€์— LLM์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ OpenAI GPT๋ฅผ ์”๋‹ˆ๋‹ค. `evaluate(..., llm=your_llm, embeddings=your_embeddings)`๋กœ ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
334
+ "keywords": ["ragas", "evaluate", "faithfulness", "answer_relevancy", "context_precision", "LLM-as-judge", "Dataset"],
335
+ "source_id": "official-docs-ragas",
336
+ "details": [
337
+ "**3๊ฐ€์ง€ ํ•ต์‹ฌ ๋ฉ”ํŠธ๋ฆญ**: Faithfulness(๋‹ต๋ณ€์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์— ๊ทผ๊ฑฐํ•˜๋Š”๊ฐ€ โ€” ํ™˜๊ฐ ํƒ์ง€), Answer Relevancy(๋‹ต๋ณ€์ด ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ ์žˆ๋Š”๊ฐ€), Context Precision(๊ด€๋ จ ๋ฌธ์„œ๊ฐ€ ์ƒ์œ„ k๊ฐœ ์•ˆ์— ์žˆ๋Š”๊ฐ€).",
338
+ "**MLflow ์—ฐ๋™**: `result.to_pandas()`๋กœ DataFrame ๋ณ€ํ™˜ ํ›„ `mlflow.log_metrics()`๋กœ ๊ธฐ๋กํ•˜๋ฉด chunk_size, k ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ํ’ˆ์งˆ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
339
+ "**ground_truth ์—†์ด๋„ ๊ฐ€๋Šฅ**: `ground_truth`๊ฐ€ ์—†์œผ๋ฉด faithfulness, answer_relevancy๋งŒ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. Answer Correctness ๋ฉ”ํŠธ๋ฆญ์€ ground_truth๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
340
+ ]
341
+ },
342
+ {
343
+ "id": "docs-summarization",
344
+ "certification": "Docs Study",
345
+ "title": "LangChain ์š”์•ฝ ์ฒด์ธ โ€” map_reduce & stuff",
346
+ "level": "์ค‘๊ธ‰",
347
+ "related_practices": ["docs-news-1", "docs-news-2"],
348
+ "summary": "LangChain์˜ `load_summarize_chain`์€ ๊ธด ๋ฌธ์„œ๋ฅผ LLM ์ปจํ…์ŠคํŠธ ํ•œ๊ณ„ ๋‚ด์—์„œ ์š”์•ฝํ•˜๋Š” ์ฒด์ธ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฌธ์„œ๊ฐ€ ์งง์œผ๋ฉด `stuff`, ๊ธธ๋ฉด `map_reduce` ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค.",
349
+ "example": "from langchain.chains.summarize import load_summarize_chain\nfrom langchain_core.documents import Document\n\n# map_reduce: ๊ฐ ์ฒญํฌ ์š”์•ฝ โ†’ ์ตœ์ข… ์š”์•ฝ\nchain = load_summarize_chain(llm, chain_type='map_reduce')\n\ndocs = [Document(page_content=article) for article in articles]\nresult = chain.invoke(docs)\nprint(result['output_text'])",
350
+ "common_mistake": "`stuff` ๋ฐฉ์‹์€ ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ํ•˜๋‚˜์˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๊ฐ€ LLM ์ปจํ…์ŠคํŠธ ์ฐฝ๋ณด๋‹ค ํฌ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ฌธ์„œ๋‚˜ ๊ธด ๋ฌธ์„œ๋Š” ํ•ญ์ƒ `map_reduce`๋ฅผ ๋จผ์ € ๊ณ ๋ คํ•˜์„ธ์š”.",
351
+ "keywords": ["load_summarize_chain", "map_reduce", "stuff", "refine", "output_text", "์š”์•ฝ"],
352
+ "source_id": "official-docs-langchain",
353
+ "details": [
354
+ "**chain_type ๋น„๊ต**: `stuff`(์ „์ฒด ํ•œ ๋ฒˆ), `map_reduce`(์ฒญํฌ๋ณ„ ์š”์•ฝ ํ›„ ํ•ฉ์นจ), `refine`(์ด์ „ ์š”์•ฝ์„ ๋‹ค์Œ ์ฒญํฌ์— ๋ฐ˜์˜). ํ’ˆ์งˆ์€ refine > map_reduce > stuff์ด์ง€๋งŒ ์†๋„๋Š” ๋ฐ˜๋Œ€์ž…๋‹ˆ๋‹ค.",
355
+ "**map_prompt / combine_prompt**: `load_summarize_chain`์— `map_prompt`์™€ `combine_prompt`๋ฅผ ์ง€์ •ํ•ด ์š”์•ฝ ์ง€์‹œ๋ฌธ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
356
+ "**๋น„๋™๊ธฐ**: `chain.ainvoke(docs)`๋กœ ๋น„๋™๊ธฐ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋‰ด์Šค ๊ธฐ์‚ฌ๋ฅผ ๋ณ‘๋ ฌ ์š”์•ฝํ•  ๋•Œ `asyncio.gather`์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ์†๋„๊ฐ€ ํฌ๊ฒŒ ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค."
357
+ ]
358
+ },
359
+ {
360
+ "id": "docs-sql-chain",
361
+ "certification": "Docs Study",
362
+ "title": "LangChain Text-to-SQL โ€” ์ž์—ฐ์–ด๋กœ DB ์กฐํšŒ",
363
+ "level": "์ค‘๊ธ‰",
364
+ "related_practices": ["docs-sql-1", "docs-sql-2"],
365
+ "summary": "LangChain์˜ `create_sql_query_chain`์€ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ SQL ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. `SQLDatabase`๋กœ DB ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ฝ์–ด LLM์—๊ฒŒ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
366
+ "example": "from langchain_community.utilities import SQLDatabase\nfrom langchain.chains import create_sql_query_chain\n\ndb = SQLDatabase.from_uri('sqlite:///./mydb.db')\nchain = create_sql_query_chain(llm, db)\n\nsql = chain.invoke({'question': '๊ฐ€์žฅ ๋งŽ์ด ํŒ”๋ฆฐ ์ƒํ’ˆ 5๊ฐœ๋Š”?'})\nprint(sql) # SELECT product, SUM(quantity) ...\n\nresult = db.run(sql)\nprint(result)",
367
+ "common_mistake": "LLM์ด ์ƒ์„ฑํ•œ SQL์„ ๋ฐ”๋กœ `db.run()`์— ๋„˜๊ธฐ๋ฉด ์ž˜๋ชป๋œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. Guardrails์˜ `ValidSQL`์ด๋‚˜ ๋ณ„๋„ ํŒŒ์‹ฑ์œผ๋กœ ๊ฒ€์ฆํ•œ ๋’ค ์‹คํ–‰ํ•˜์„ธ์š”.",
368
+ "keywords": ["SQLDatabase", "create_sql_query_chain", "from_uri", "db.run", "Text-to-SQL", "sqlite"],
369
+ "source_id": "official-docs-langchain",
370
+ "details": [
371
+ "**์Šคํ‚ค๋งˆ ์ž๋™ ์ฃผ์ž…**: `SQLDatabase`๋Š” ์—ฐ๊ฒฐ๋œ DB์˜ ํ…Œ์ด๋ธ”๋ช…๊ณผ ์ปฌ๋Ÿผ ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. LLM์ด ์กด์žฌํ•˜๋Š” ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.",
372
+ "**QuerySQLDatabaseTool**: SQL ์ƒ์„ฑ๊ณผ ์‹คํ–‰์„ ํ•˜๋‚˜์˜ ํˆด๋กœ ๋ฌถ์€ ๊ณ ์ˆ˜์ค€ ์ธํ„ฐํŽ˜์ด์Šค. Agent์— ๋“ฑ๋กํ•˜๋ฉด LLM์ด ํ•„์š”ํ•  ๋•Œ DB๋ฅผ ์ง์ ‘ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
373
+ "**๋ณด์•ˆ ์ฃผ์˜**: `db.run()`์€ ์‹ค์ œ DB๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ๊ธฐ ์ „์šฉ ์‚ฌ์šฉ์ž ๊ณ„์ •์œผ๋กœ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜ SELECT๋งŒ ํ—ˆ์šฉํ•˜๋Š” wrapper๋ฅผ ๋‘๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค."
374
+ ]
375
+ },
376
+ {
377
+ "id": "docs-langgraph",
378
+ "certification": "Docs Study",
379
+ "title": "LangGraph โ€” ์ƒํƒœ ๊ธฐ๋ฐ˜ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ",
380
+ "level": "๊ณ ๊ธ‰",
381
+ "related_practices": ["docs-graph-1", "docs-graph-2"],
382
+ "summary": "LangGraph๋Š” LLM ์—์ด์ „ํŠธ๋ฅผ ๊ทธ๋ž˜ํ”„(๋…ธ๋“œ + ์—ฃ์ง€)๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ ์ฒด์ธ์˜ Aโ†’Bโ†’C์™€ ๋‹ฌ๋ฆฌ ์กฐ๊ฑด ๋ถ„๊ธฐ, ๋ฃจํ”„, ๋ณ‘๋ ฌ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ด ์‹ค์ œ ์—์ด์ „ํŠธ ํ–‰๋™์„ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.",
383
+ "example": "from langgraph.graph import StateGraph, END\nfrom typing import TypedDict\n\nclass State(TypedDict):\n question: str\n answer: str\n\ndef retrieve(state: State) -> State:\n return {\"answer\": str(retriever.invoke(state[\"question\"]))}\n\ndef generate(state: State) -> State:\n return {\"answer\": chain.invoke(state)}\n\ngraph = StateGraph(State)\ngraph.add_node(\"retrieve\", retrieve)\ngraph.add_node(\"generate\", generate)\ngraph.set_entry_point(\"retrieve\")\ngraph.add_edge(\"retrieve\", \"generate\")\ngraph.add_edge(\"generate\", END)\n\napp = graph.compile()\nresult = app.invoke({\"question\": \"LangChain์ด ๋ญ์•ผ?\"})",
384
+ "common_mistake": "LangGraph๋Š” LangChain ์ฒด์ธ์ด ์•„๋‹™๋‹ˆ๋‹ค. `graph.compile()` ํ›„ `app.invoke()`๋ฅผ ์”๋‹ˆ๋‹ค. State TypedDict๋ฅผ ์ •ํ™•ํžˆ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ๋…ธ๋“œ์—์„œ ํ‚ค ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.",
385
+ "keywords": ["LangGraph", "StateGraph", "State", "node", "edge", "compile", "conditional_edges", "END"],
386
+ "source_id": "official-docs-langgraph",
387
+ "details": [
388
+ "**๋…ธ๋“œ vs ์ฒด์ธ**: ์ฒด์ธ์€ Aโ†’Bโ†’C ์„ ํ˜•. ๊ทธ๋ž˜ํ”„๋Š” A์—์„œ ์กฐ๊ฑด์— ๋”ฐ๋ผ B ๋˜๋Š” C๋กœ ๋ถ„๊ธฐ, D์—์„œ A๋กœ ๋ฃจํ”„ ๋“ฑ ๋ณต์žกํ•œ ํ๋ฆ„์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
389
+ "**conditional_edges**: `graph.add_conditional_edges('judge', router_fn, {'retry': 'retrieve', 'done': END})`์ฒ˜๋Ÿผ ๋…ธ๋“œ ๋ฐ˜ํ™˜๊ฐ’์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ReAct ์—์ด์ „ํŠธ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.",
390
+ "**checkpointer**: `MemorySaver`๋ฅผ `graph.compile(checkpointer=...)`์— ๋„˜๊ธฐ๋ฉด ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ด ์ค‘๋‹จ ํ›„ ์žฌ๊ฐœ, ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™” ์œ ์ง€๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
391
+ ]
392
+ },
393
+ {
394
+ "id": "docs-airflow",
395
+ "certification": "Docs Study",
396
+ "title": "Apache Airflow โ€” ์›Œํฌํ”Œ๋กœ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜",
397
+ "level": "๊ณ ๊ธ‰",
398
+ "related_practices": ["docs-lab-23"],
399
+ "summary": "Airflow๋Š” DAG(Directed Acyclic Graph)๋กœ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ •์˜ํ•˜๊ณ  ์Šค์ผ€์ค„๋งยท๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ML ํŒŒ์ดํ”„๋ผ์ธ(๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ โ†’ ์ „์ฒ˜๋ฆฌ โ†’ ํ•™์Šต โ†’ ๋ฐฐํฌ)์ด๋‚˜ ๋ฌธ์„œ ์žฌ์ธ๋ฑ์‹ฑ ์ž๋™ํ™”์— ์”๋‹ˆ๋‹ค.",
400
+ "example": "from airflow import DAG\nfrom airflow.operators.python import PythonOperator\nfrom datetime import datetime\n\ndef reindex():\n # ๋ฌธ์„œ ๋กœ๋“œ โ†’ ์ž„๋ฒ ๋”ฉ โ†’ VectorStore ์—…๋ฐ์ดํŠธ\n pass\n\nwith DAG('daily_reindex', schedule='@daily', start_date=datetime(2024,1,1)) as dag:\n task = PythonOperator(task_id='reindex', python_callable=reindex)",
401
+ "common_mistake": "DAG ํŒŒ์ผ์€ Airflow ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ํŒŒ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ตœ์ƒ์œ„์— ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ(DB ์—ฐ๊ฒฐ, ๋ชจ๋ธ ๋กœ๋“œ)์„ ๋‘๋ฉด ํŒŒ์‹ฑ๋งˆ๋‹ค ์‹คํ–‰๋ผ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋А๋ ค์ง‘๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ task ํ•จ์ˆ˜ ์•ˆ์— ๋„ฃ์œผ์„ธ์š”.",
402
+ "keywords": ["airflow", "DAG", "task", "operator", "schedule", "XCom", "pipeline", "orchestration"],
403
+ "source_id": "official-docs-airflow",
404
+ "details": [
405
+ "**Operator ์ข…๋ฅ˜**: `PythonOperator`(ํŒŒ์ด์ฌ ํ•จ์ˆ˜), `BashOperator`(์…ธ ๋ช…๋ น), `DockerOperator`(์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰) ๋“ฑ. ML ํŒŒ์ดํ”„๋ผ์ธ์—” ์ฃผ๋กœ PythonOperator๋ฅผ ์”๋‹ˆ๋‹ค.",
406
+ "**XCom**: task ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜. `ti.xcom_push(key, value)` / `ti.xcom_pull(task_ids, key)`๋กœ ์†Œ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰์€ S3/GCS ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค.",
407
+ "**์˜์กด์„ฑ**: `task_a >> task_b >> task_c` ๋˜๋Š” `task_a.set_downstream(task_b)` ๋กœ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ์‹คํ–‰์€ `[task_b, task_c] >> task_d` ํ˜•ํƒœ๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค."
408
+ ]
409
+ },
410
+ {
411
+ "id": "docs-mlflow",
412
+ "certification": "Docs Study",
413
+ "title": "MLflow โ€” ์‹คํ—˜ ์ถ”์  & ๋ชจ๋ธ ๊ด€๋ฆฌ",
414
+ "level": "๊ณ ๊ธ‰",
415
+ "related_practices": ["docs-lab-15"],
416
+ "summary": "MLflow๋Š” ML ์‹คํ—˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ, ๋ฉ”ํŠธ๋ฆญ, ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋ชจ๋ธ์„ ๋ฒ„์ „ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. RAG์—์„œ chunk_size, k, ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋“ฑ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๋น„๊ตํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
417
+ "example": "import mlflow\n\nwith mlflow.start_run(run_name='rag-exp-1'):\n mlflow.log_param('chunk_size', 500)\n mlflow.log_param('k', 4)\n mlflow.log_metric('answer_relevance', 0.87)\n mlflow.log_metric('faithfulness', 0.92)\n mlflow.log_artifact('eval_results.json')",
418
+ "common_mistake": "run์„ `with mlflow.start_run()` ๋ธ”๋ก์œผ๋กœ ๊ฐ์‹ธ์ง€ ์•Š์œผ๋ฉด ์‹ค์ˆ˜๋กœ ์ด์ „ run์— ๋กœ๊น…๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ context manager๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
419
+ "keywords": ["mlflow", "experiment", "run", "log_param", "log_metric", "artifact", "model registry"],
420
+ "source_id": "official-docs-mlflow",
421
+ "details": [
422
+ "**UI**: `mlflow ui` ๋ช…๋ น์œผ๋กœ localhost:5000์—์„œ ์‹คํ—˜ ๋น„๊ต ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ๋ฉ”ํŠธ๋ฆญ ์ถ”์ด๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
423
+ "**Model Registry**: `mlflow.log_model()`๋กœ ๋ชจ๋ธ์„ ์ €์žฅํ•˜๊ณ  `Staging`โ†’`Production` ๋‹จ๊ณ„๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐํฌ ์‹œ `mlflow.pyfunc.load_model('models:/name/Production')`์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.",
424
+ "**LangChain ํ†ตํ•ฉ**: `mlflow.langchain.autolog()`๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์ฒด์ธ ์‹คํ–‰๋งˆ๋‹ค ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฉ”ํŠธ๋ฆญ์ด ์ž๋™ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค."
425
+ ]
426
+ },
427
+ {
428
+ "id": "docs-mcp",
429
+ "certification": "Docs Study",
430
+ "title": "MCP โ€” Model Context Protocol",
431
+ "level": "๊ณ ๊ธ‰",
432
+ "related_practices": ["docs-lab-24"],
433
+ "summary": "MCP(Model Context Protocol)๋Š” LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์™ธ๋ถ€ ๋„๊ตฌยท๋ฐ์ดํ„ฐ์†Œ์Šค์™€ ํ‘œ์ค€ํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” Anthropic ์˜คํ”ˆ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. Claude Desktop์ด๋‚˜ Claude Code์— ๋กœ์ปฌ MCP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ํŒŒ์ผ ์‹œ์Šคํ…œ, DB, ์™ธ๋ถ€ API๋ฅผ LLM์ด ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
434
+ "example": "# mcp_server.py (FastMCP ์‚ฌ์šฉ)\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP('my-server')\n\n@mcp.tool()\ndef search_docs(query: str) -> str:\n '''๋ฒกํ„ฐ DB์—์„œ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.'''\n results = retriever.invoke(query)\n return '\\n'.join(r.page_content for r in results)\n\nmcp.run(transport='stdio')",
435
+ "common_mistake": "MCP ์„œ๋ฒ„๋Š” stdio ๋˜๋Š” SSE transport ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. Claude Desktop์€ stdio(๋กœ์ปฌ ํ”„๋กœ์„ธ์Šค), Claude Code๋Š” SSE(HTTP ์„œ๋ฒ„) ๋ฐฉ์‹์„ ์ฃผ๋กœ ์”๋‹ˆ๋‹ค. transport ๋ถˆ์ผ์น˜ ์‹œ ์—ฐ๊ฒฐ์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.",
436
+ "keywords": ["MCP", "Model Context Protocol", "tool", "resource", "FastMCP", "stdio", "Claude", "agent"],
437
+ "source_id": "official-docs-mcp",
438
+ "details": [
439
+ "**3๊ฐ€์ง€ ๊ธฐ๋ณธ ์š”์†Œ**: `tool`(LLM์ด ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜), `resource`(LLM์ด ์ฝ๋Š” ๋ฐ์ดํ„ฐ), `prompt`(์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ). ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋ฒ„๋Š” `tool`๋งŒ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.",
440
+ "**Claude Desktop ์—ฐ๊ฒฐ**: `~/Library/Application Support/Claude/claude_desktop_config.json`์— ์„œ๋ฒ„ ๊ฒฝ๋กœ์™€ ๋ช…๋ น์–ด๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ๋Œ€ํ™” ์ค‘ ์ž๋™์œผ๋กœ ๋„๊ตฌ๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค.",
441
+ "**FastMCP**: Anthropic ๊ณต์‹ Python SDK์˜ ๊ณ ์ˆ˜์ค€ ๋ž˜ํผ. `@mcp.tool()` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ํ•จ์ˆ˜๋ฅผ ๋„๊ตฌ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์Šคํ‚ค๋งˆ ์ƒ์„ฑ, ์ž…์ถœ๋ ฅ ๊ฒ€์ฆ์ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค."
442
+ ]
443
+ },
444
+ {
445
+ "id": "docs-litellm",
446
+ "certification": "Docs Study",
447
+ "title": "LiteLLM โ€” ์—ฌ๋Ÿฌ LLM์„ ํ•˜๋‚˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ",
448
+ "level": "๊ณ ๊ธ‰",
449
+ "related_practices": ["docs-litellm-1"],
450
+ "summary": "LiteLLM์€ OpenAI, Anthropic, Ollama, Gemini ๋“ฑ 100๊ฐœ ์ด์ƒ์˜ LLM ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋™์ผํ•œ API ํ˜•์‹์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ๋ฐ”๊ฟ€ ๋•Œ ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ๋ชจ๋ธ๋ช…๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
451
+ "example": "from litellm import completion\n\n# Ollama ๋กœ์ปฌ\nresponse = completion(\n model='ollama/llama3.2',\n messages=[{'role': 'user', 'content': '์•ˆ๋…•'}]\n)\n\n# OpenAI โ€” ์ฝ”๋“œ ๋™์ผ, ๋ชจ๋ธ๋ช…๋งŒ ๋ณ€๊ฒฝ\nresponse = completion(\n model='gpt-4o-mini',\n messages=[{'role': 'user', 'content': '์•ˆ๋…•'}]\n)\n\nprint(response.choices[0].message.content)",
452
+ "common_mistake": "๋ชจ๋ธ๋ช… ํ˜•์‹์€ `'ํ”„๋กœ๋ฐ”์ด๋”/๋ชจ๋ธ๋ช…'`์ž…๋‹ˆ๋‹ค. Ollama๋Š” `'ollama/llama3.2'`, Anthropic์€ `'anthropic/claude-3-5-sonnet-20241022'`. OpenAI๋งŒ ์˜ˆ์™ธ๋กœ ์ ‘๋‘์‚ฌ ์—†์ด `'gpt-4o-mini'`์ฒ˜๋Ÿผ ์”๋‹ˆ๋‹ค.",
453
+ "keywords": ["LiteLLM", "completion", "model", "provider", "๋ฒค๋” ๋…๋ฆฝ", "OpenAI ํ˜ธํ™˜", "ChatLiteLLM"],
454
+ "source_id": "official-docs-litellm",
455
+ "details": [
456
+ "**๋น„์šฉ ์ถ”์ **: `litellm.success_callback`์œผ๋กœ ๊ฐ ํ˜ธ์ถœ์˜ ํ† ํฐ ์ˆ˜์™€ ์˜ˆ์ƒ ๋น„์šฉ์„ ์ž๋™ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ชจ๋ธ์„ ์‹คํ—˜ํ•  ๋•Œ ๋น„์šฉ ๋น„๊ต์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
457
+ "**LangChain ํ†ตํ•ฉ**: `from langchain_community.chat_models import ChatLiteLLM`์œผ๋กœ LangChain ์ฒด์ธ์—์„œ LiteLLM์„ ์”๋‹ˆ๋‹ค. LCEL ํŒŒ์ดํ”„๋ผ์ธ์— ๋ฐ”๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
458
+ "**Fallback**: `completion(..., fallbacks=['gpt-4o-mini', 'ollama/llama3.2'])`์ฒ˜๋Ÿผ ์ฃผ ๋ชจ๋ธ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ ๋ฐฑ์—… ๋ชจ๋ธ๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค."
459
+ ]
460
+ },
461
+ {
462
+ "id": "docs-chainlit",
463
+ "certification": "Docs Study",
464
+ "title": "Chainlit โ€” ์ฑ—๋ด‡ UI ํŠนํ™” ํ”„๋ ˆ์ž„์›Œํฌ",
465
+ "level": "๏ฟฝ๏ฟฝ๏ฟฝ๊ธ‰",
466
+ "related_practices": ["docs-chainlit-1"],
467
+ "summary": "Chainlit์€ LLM ์ฑ—๋ด‡ UI์— ํŠนํ™”๋œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. Streamlit๊ณผ ๋‹ฌ๋ฆฌ ์›น์†Œ์ผ“ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•ด ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต, ํŒŒ์ผ ์—…๋กœ๋“œ, ๋ฉ€ํ‹ฐ์Šคํ… ์ง„ํ–‰ ํ‘œ์‹œ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.",
468
+ "example": "import chainlit as cl\n\n@cl.on_message\nasync def main(message: cl.Message):\n # ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต\n msg = cl.Message(content='')\n async for chunk in chain.astream({'question': message.content}):\n await msg.stream_token(chunk)\n await msg.send()\n\n# ์‹คํ–‰: chainlit run app.py",
469
+ "common_mistake": "Chainlit์€ `async` ๊ธฐ๋ฐ˜์ž…๋‹ˆ๋‹ค. LangChain์˜ `chain.invoke()`(๋™๊ธฐ)๋ฅผ ์“ฐ๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ `chain.ainvoke()` ๋˜๋Š” `chain.astream()`์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
470
+ "keywords": ["chainlit", "on_message", "Message", "stream_token", "async", "astream", "on_chat_start"],
471
+ "source_id": "official-docs-chainlit",
472
+ "details": [
473
+ "**๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ฐ˜**: `@cl.on_chat_start`(์„ธ์…˜ ์‹œ์ž‘ ์‹œ ์ฒด์ธ ์ดˆ๊ธฐํ™”), `@cl.on_message`(๋ฉ”์‹œ์ง€ ์ˆ˜์‹ ), `@cl.on_stop`(์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘๋‹จ)์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
474
+ "**Step UI**: `async with cl.Step(name='๊ฒ€์ƒ‰ ์ค‘...'):` ๋ธ”๋ก์œผ๋กœ ์—์ด์ „ํŠธ์˜ ๊ฐ ๋‹จ๊ณ„(๊ฒ€์ƒ‰, ์ถ”๋ก  ๋“ฑ)๋ฅผ UI์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŽผ์ณ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. LangGraph ์—์ด์ „ํŠธ์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
475
+ "**ํŒŒ์ผ ์—…๋กœ๋“œ**: `@cl.on_message`์—์„œ `message.elements`๋กœ ์ฒจ๋ถ€ ํŒŒ์ผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. PDF ์—…๋กœ๋“œ โ†’ ์ฆ‰์‹œ RAG ์ธ๋ฑ์‹ฑ ํ๋ฆ„์„ ๋ช‡ ์ค„๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."
476
+ ]
477
+ },
478
+ {
479
+ "id": "docs-redis",
480
+ "certification": "Docs Study",
481
+ "title": "Redis โ€” ๋Œ€ํ™” ๋ฉ”๋ชจ๋ฆฌ ์˜์†ํ™” & LLM ์บ์‹ฑ",
482
+ "level": "๊ณ ๊ธ‰",
483
+ "related_practices": ["docs-redis-1"],
484
+ "summary": "Redis๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ LLM ์•ฑ์—์„œ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ์˜์†ํ™”์™€ LLM ์‘๋‹ต ์บ์‹ฑ์— ์”๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•ด๋„ ๋Œ€ํ™”๊ฐ€ ์œ ์ง€๋˜๊ณ , ๋™์ผ ์งˆ๋ฌธ์— ๋Œ€ํ•œ LLM ํ˜ธ์ถœ ๋น„์šฉ์„ ์ ˆ๊ฐํ•ฉ๋‹ˆ๋‹ค.",
485
+ "example": "from langchain_community.chat_message_histories import RedisChatMessageHistory\nfrom langchain_core.runnables.history import RunnableWithMessageHistory\n\ndef get_session_history(session_id: str):\n return RedisChatMessageHistory(\n session_id=session_id,\n url='redis://localhost:6379'\n )\n\nchain_with_history = RunnableWithMessageHistory(\n chain,\n get_session_history,\n input_messages_key='question',\n history_messages_key='chat_history'\n)",
486
+ "common_mistake": "Redis ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€ ์•Š์œผ๋ฉด ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ `docker run -d -p 6379:6379 redis`๋กœ ๋จผ์ € Redis๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”.",
487
+ "keywords": ["Redis", "RedisChatMessageHistory", "session_id", "url", "์˜์†ํ™”", "์บ์‹ฑ", "TTL"],
488
+ "source_id": "official-docs-redis",
489
+ "details": [
490
+ "**์ธ๋ฉ”๋ชจ๋ฆฌ ๊ต์ฒด**: ๊ธฐ์กด `ChatMessageHistory`(์žฌ์‹œ์ž‘ ์‹œ ์†Œ๋ฉธ)๋ฅผ `RedisChatMessageHistory`๋กœ ๊ต์ฒดํ•˜๋ฉด `get_session_history` ํ•จ์ˆ˜ ํ•œ ์ค„๋งŒ ๋ฐ”๊ฟ”๋„ ์˜์†์„ฑ์„ ์–ป์Šต๋‹ˆ๋‹ค.",
491
+ "**TTL**: `RedisChatMessageHistory(session_id, url, ttl=3600)`์œผ๋กœ 1์‹œ๊ฐ„ ํ›„ ์ž๋™ ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋ฌดํ•œํžˆ ์Œ“์ด๋Š” ํžˆ์Šคํ† ๋ฆฌ์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
492
+ "**LLM ์‘๋‹ต ์บ์‹ฑ**: `from langchain.cache import RedisCache; langchain.llm_cache = RedisCache(redis_client)`๋กœ ๋™์ผ ์งˆ๋ฌธ์— ๋Œ€ํ•œ LLM ์‘๋‹ต์„ ์บ์‹œํ•ด ๋น„์šฉ๊ณผ ์‘๋‹ต ์‹œ๊ฐ„์„ ์ค„์ž…๋‹ˆ๋‹ค."
493
+ ]
494
+ },
495
+ {
496
+ "id": "docs-unstructured",
497
+ "certification": "Docs Study",
498
+ "title": "Unstructured โ€” PDFยทWordยท์ด๋ฏธ์ง€ ํŒŒ์‹ฑ",
499
+ "level": "๊ณ ๊ธ‰",
500
+ "related_practices": ["docs-unstructured-1"],
501
+ "summary": "Unstructured๋Š” PDF, Word, Excel, PowerPoint, ์ด๋ฏธ์ง€ ๋“ฑ ๋น„์ •ํ˜• ๋ฌธ์„œ๋ฅผ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. LangChain์˜ `UnstructuredFileLoader`์™€ ํ†ตํ•ฉ๋˜์–ด ํ™•์žฅ์ž์— ์ƒ๊ด€์—†์ด ๋™์ผ ์ฝ”๋“œ๋กœ RAG ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
502
+ "example": "from langchain_community.document_loaders import UnstructuredFileLoader\n\n# ๋‹จ์ผ ํŒŒ์ผ โ€” ํ™•์žฅ์ž ์ž๋™ ๊ฐ์ง€\nloader = UnstructuredFileLoader('./report.pdf')\ndocs = loader.load()\n\n# ํ˜ผํ•ฉ ํ˜•์‹ ๋””๋ ‰ํ† ๋ฆฌ\nfrom langchain_community.document_loaders import DirectoryLoader\nloader = DirectoryLoader(\n './docs',\n glob='**/*',\n loader_cls=UnstructuredFileLoader\n)\ndocs = loader.load()",
503
+ "common_mistake": "`pip install unstructured`๋Š” ๊ธฐ๋ณธ ์„ค์น˜์ž…๋‹ˆ๋‹ค. PDF์—๋Š” `pip install unstructured[pdf]`, ์ด๋ฏธ์ง€ OCR์—๋Š” `pip install unstructured[local-inference]`์ฒ˜๋Ÿผ ๋ณ„๋„ extra๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
504
+ "keywords": ["Unstructured", "UnstructuredFileLoader", "PDF", "Word", "OCR", "๋น„์ •ํ˜• ๋ฌธ์„œ", "elements"],
505
+ "source_id": "official-docs-unstructured",
506
+ "details": [
507
+ "**์ž๋™ ํŒŒ์ผ ๊ฐ์ง€**: ํ™•์žฅ์ž์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ์ ์ ˆํ•œ ํŒŒ์„œ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. `.pdf`, `.docx`, `.pptx`, `.jpg`, `.eml` ๋“ฑ์„ ๋™์ผ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
508
+ "**์š”์†Œ ๋ถ„๋ฅ˜**: `mode='elements'`๋กœ ๋กœ๋“œํ•˜๋ฉด Title, NarrativeText, Table, Image ๋“ฑ ์š”์†Œ ํƒ€์ž…์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํ—ค๋”๋ฅผ ์ฒญํฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜ ํ…Œ์ด๋ธ”๋งŒ ๋”ฐ๋กœ ์ถ”์ถœํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
509
+ "**API vs ๋กœ์ปฌ**: Unstructured Cloud API๋Š” ๊ณ ํ’ˆ์งˆ OCR๊ณผ ๋ ˆ์ด์•„์›ƒ ๋ถ„์„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ์€ ๋ฌด๋ฃŒ์ด๋‚˜ ์ด๋ฏธ์ง€๋‚˜ ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ์˜ PDF ํ’ˆ์งˆ์ด ๋‚ฎ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
510
+ ]
511
+ },
512
+ {
513
+ "id": "docs-conversation-memory",
514
+ "certification": "Docs Study",
515
+ "title": "LangChain ๋Œ€ํ™” ๋ฉ”๋ชจ๋ฆฌ โ€” ๋ฉ€ํ‹ฐํ„ด ์ฑ—๋ด‡",
516
+ "level": "๊ณ ๊ธ‰",
517
+ "related_practices": ["docs-lab-6", "docs-brain-1"],
518
+ "summary": "RunnableWithMessageHistory๋Š” LangChain ์ฒด์ธ์— ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ด์ „ ๋Œ€ํ™”๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ session_id๋งŒ ๋„˜๊ธฐ๋ฉด ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
519
+ "example": "from langchain_community.chat_message_histories import ChatMessageHistory\nfrom langchain_core.runnables.history import RunnableWithMessageHistory\n\nstore = {} # session_id โ†’ ChatMessageHistory\n\ndef get_session_history(session_id: str):\n if session_id not in store:\n store[session_id] = ChatMessageHistory()\n return store[session_id]\n\nchain_with_history = RunnableWithMessageHistory(\n chain,\n get_session_history,\n input_messages_key=\"question\",\n history_messages_key=\"chat_history\"\n)\n\nchain_with_history.invoke(\n {\"question\": \"์•ˆ๋…•\"},\n config={\"configurable\": {\"session_id\": \"user-1\"}}\n)",
520
+ "common_mistake": "`session_id`๋ฅผ ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋™์ผํ•˜๊ฒŒ ๋„˜๊ธฐ๋ฉด ์ „์ฒด ์‚ฌ์šฉ์ž๊ฐ€ ํ•˜๋‚˜์˜ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ณ ์œ ํ•œ session_id๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
521
+ "keywords": ["RunnableWithMessageHistory", "ChatMessageHistory", "session_id", "chat_history", "๋ฉ€ํ‹ฐํ„ด"],
522
+ "source_id": "official-docs-langchain",
523
+ "details": [
524
+ "**ํžˆ์Šคํ† ๋ฆฌ ์ €์žฅ์†Œ**: ์ธ๋ฉ”๋ชจ๋ฆฌ(`ChatMessageHistory`)๋Š” ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์‹œ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ๋Š” `RedisChatMessageHistory`, `SQLChatMessageHistory` ๋“ฑ ์˜์† ์ €์žฅ์†Œ๋ฅผ ์”๋‹ˆ๋‹ค.",
525
+ "**ํžˆ์Šคํ† ๋ฆฌ ๊ธธ์ด ์ œํ•œ**: ๋Œ€ํ™”๊ฐ€ ๊ธธ์–ด์ง€๋ฉด ํ† ํฐ์ด ์ดˆ๊ณผ๋ฉ๋‹ˆ๋‹ค. `trim_messages()` ๋˜๋Š” ์ตœ๊ทผ N๊ฐœ ๋ฉ”์‹œ์ง€๋งŒ ์œ ์ง€ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
526
+ "**ํ”„๋กฌํ”„ํŠธ ์—ฐ๋™**: `ChatPromptTemplate`์— `MessagesPlaceholder(variable_name=\"chat_history\")`๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค. history_messages_key์™€ placeholder ์ด๋ฆ„์ด ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
527
+ ]
528
+ }
529
+ ]
cert_study_app/content/tool_docs/practices.json ADDED
@@ -0,0 +1,530 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "docs-lab-1",
4
+ "title": "[๋‹จ๊ณ„ 1] Ollama โ€” ๋กœ์ปฌ LLM ์„ค์น˜",
5
+ "task_description": "**์ƒํ™ฉ**\nOpenAI API๋กœ ๊ฐœ๋ฐœํ•˜๋˜ ์ค‘ ์ฒญ๊ตฌ์„œ๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋งŒ ์ˆ˜์‹ญ ๋ฒˆ ํ–ˆ๋Š”๋ฐ ์š”๊ธˆ์ด ์ฒญ๊ตฌ๋์Šต๋‹ˆ๋‹ค. ๋กœ์ปฌ์—์„œ ๋ฌด๋ฃŒ๋กœ ๋Œ๋ฆด ์ˆ˜ ์—†์„๊นŒ์š”?\n\n**๋ฌธ์ œ**\nAPI ํ‚ค ์—†์ด LLM์„ ์“ฐ๋ ค๋ฉด ๋ชจ๋ธ์„ ์ง์ ‘ ๋กœ์ปฌ์— ๋‚ด๋ ค๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nOllama๋กœ `llama3.2` ๋ชจ๋ธ์„ ๋กœ์ปฌ์— ๋‹ค์šด๋กœ๋“œํ•˜์„ธ์š”. (๋Œ€ํ™” ์—†์ด ๋‹ค์šด๋กœ๋“œ๋งŒ)\n\n> `run`์ด ์•„๋‹Œ, ๋‹ค์šด๋กœ๋“œ๋งŒ ํ•˜๋Š” ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค.",
6
+ "expected_command": "ollama pull llama3.2",
7
+ "grading_conditions": ["ollama pull", "llama3.2"],
8
+ "hint": "`ollama pull <๋ชจ๋ธ๋ช…>` ํ˜•์‹์ž…๋‹ˆ๋‹ค.",
9
+ "explanation": "`ollama pull`์€ ๋ชจ๋ธ ํŒŒ์ผ์„ ๋กœ์ปฌ์— ๋‚ด๋ ค๋ฐ›์Šต๋‹ˆ๋‹ค. ์ดํ›„ `ollama list`๋กœ ๋ชฉ๋ก ํ™•์ธ, `ollama run llama3.2`๋กœ ๋Œ€ํ™”๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. API ํ‚ค๋„, ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ๋„ ์—†์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.",
10
+ "difficulty": "easy",
11
+ "takeaway": "API ํ‚ค ์—†์ด ollama pull ํ•œ ๋ฒˆ์œผ๋กœ ๋กœ์ปฌ LLM ์‹คํ–‰ ํ™˜๊ฒฝ์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค."
12
+ },
13
+ {
14
+ "id": "docs-lab-2",
15
+ "title": "[๋‹จ๊ณ„ 2] Ollama REST API โ€” Python์—์„œ ํ˜ธ์ถœํ•˜๊ธฐ",
16
+ "task_description": "**์ƒํ™ฉ**\n`ollama run llama3.2`๋กœ ํ„ฐ๋ฏธ๋„ ๋Œ€ํ™”๋Š” ๋˜๋Š”๋ฐ, ๋‚ด Python ์ฝ”๋“œ์—์„œ LLM์„ ํ˜ธ์ถœํ•˜๋ ค๋‹ˆ ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nCLI ๋Œ€ํ™” ๋ชจ๋“œ๋Š” ์ž๋™ํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์—์„œ ํ˜ธ์ถœํ•˜๋ ค๋ฉด HTTP API๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\ncurl๋กœ Ollama REST API๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด ๋™์ž‘์„ ํ™•์ธํ•˜์„ธ์š”.\n- ์—”๋“œํฌ์ธํŠธ: `http://localhost:11434/api/generate`\n- ๋ชจ๋ธ: `llama3.2`, ํ”„๋กฌํ”„ํŠธ: `'2+2๋Š”?'`, ์ŠคํŠธ๋ฆฌ๋ฐ ์—†์ด",
17
+ "expected_command": "curl http://localhost:11434/api/generate -d '{\"model\":\"llama3.2\",\"prompt\":\"2+2๋Š”?\",\"stream\":false}'",
18
+ "grading_conditions": ["curl", "localhost:11434/api/generate", "stream", "false"],
19
+ "hint": "`-d` ํ”Œ๋ž˜๊ทธ๋กœ JSON ๋ฐ”๋””๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. `\"stream\": false`๋ฅผ ๋„ฃ์–ด์•ผ ํ•œ ๋ฒˆ์— ์‘๋‹ต์ด ์˜ต๋‹ˆ๋‹ค.",
20
+ "explanation": "Ollama๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ค„ ๋‹จ์œ„ ์ŠคํŠธ๋ฆฌ๋ฐ์œผ๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. `\"stream\": false`๋ฅผ ๋„ฃ์œผ๋ฉด ์™„์„ฑ๋œ ์‘๋‹ต์„ ํ•œ ๋ฒˆ์— ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด Python์—์„œ ํŒŒ์‹ฑํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.",
21
+ "difficulty": "easy",
22
+ "takeaway": "Ollama REST API๋Š” OpenAI ํ˜ธํ™˜ ํ˜•์‹์ด๋ผ ์–ด๋–ค ์–ธ์–ดยทํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋„ ๋™์ผํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค."
23
+ },
24
+ {
25
+ "id": "docs-lab-3",
26
+ "title": "[๋‹จ๊ณ„ 3] Ollama ์ฑ„ํŒ… API โ€” ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๊ตฌ์กฐ",
27
+ "task_description": "**์ƒํ™ฉ**\n`/api/generate`๋กœ '์ด์ „์— ๋‚ด๊ฐ€ ๋ญ๋ผ๊ณ  ํ–ˆ์–ด?'๋ฅผ ๋ฌผ์–ด๋ดค๋”๋‹ˆ ๋ชจ๋ฅธ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋งค ์š”์ฒญ์ด ์™„์ „ํžˆ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n`/api/generate`๋Š” ๋‹จ์ผ ํ”„๋กฌํ”„ํŠธ๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๋ถ™์ด๋ ค๋ฉด ๋‹ค๋ฅธ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`/api/chat`์€ `messages` ๋ฐฐ์—ด๋กœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. `llama3.2`์—๊ฒŒ `user` ๋ฉ”์‹œ์ง€ `'ํŒŒ์ด์ฌ์ด ๋ญ์•ผ?'`๋ฅผ ๋ณด๋‚ด์„ธ์š”. (stream: false)",
28
+ "expected_command": "curl http://localhost:11434/api/chat -d '{\"model\":\"llama3.2\",\"messages\":[{\"role\":\"user\",\"content\":\"ํŒŒ์ด์ฌ์ด ๋ญ์•ผ?\"}],\"stream\":false}'",
29
+ "grading_conditions": ["curl", "localhost:11434/api/chat", "messages", "role", "user"],
30
+ "hint": "`/api/chat`์€ `messages` ๋ฐฐ์—ด๋กœ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
31
+ "explanation": "`messages` ๋ฐฐ์—ด์— `{\"role\": \"user\", \"content\": \"...\"}` ํ˜•์‹์œผ๋กœ ์ด์ „ ๋Œ€ํ™”๋ฅผ ์Œ“์œผ๋ฉด ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. LangChain์˜ ์ฑ„ํŒ… ๋ชจ๋ธ๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ด ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.",
32
+ "difficulty": "medium",
33
+ "takeaway": "messages ๋ฐฐ์—ด์— {role, content} ์Œ์„ ์Œ“๋Š” ๊ตฌ์กฐ๊ฐ€ ๋ชจ๋“  LLM ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™”์˜ ๊ธฐ๋ณธ ํ˜•์‹์ž…๋‹ˆ๋‹ค."
34
+ },
35
+ {
36
+ "id": "docs-lab-4",
37
+ "title": "[๋‹จ๊ณ„ 4] .env โ€” API ํ‚ค๋ฅผ ์ฝ”๋“œ์—์„œ ๋ถ„๋ฆฌํ•˜๊ธฐ",
38
+ "task_description": "**์ƒํ™ฉ**\n```python\napi_key = 'sk-abc123...'\n```\n์ด๋ ‡๊ฒŒ ์ฝ”๋“œ์— ์ง์ ‘ ์ผ๋‹ค๊ฐ€ GitHub์— pushํ–ˆ๋”๋‹ˆ API ํ‚ค๊ฐ€ ๊ณต๊ฐœ๋์Šต๋‹ˆ๋‹ค. GitHub์ด ์ž๋™์œผ๋กœ ํƒ์ง€ํ•ด์„œ ํ‚ค๋ฅผ ์ฆ‰์‹œ ๋ฌดํšจํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nAPI ํ‚ค๋ฅผ ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋ฉด ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. `.gitignore`์— ์ถ”๊ฐ€ํ•œ ๋ณ„๋„ ํŒŒ์ผ์— ๋ณด๊ด€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`.env` ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๊ณ  `OPENAI_API_KEY`๋ฅผ ์ฝ๋Š” ์ฝ”๋“œ 2์ค„์„ ์ž…๋ ฅํ•˜์„ธ์š”.\n\n```python\nfrom dotenv import load_dotenv\nimport os\n# ์—ฌ๊ธฐ์— ์ž…๋ ฅํ•˜์„ธ์š”\n```",
39
+ "expected_command": "load_dotenv()\napi_key = os.getenv('OPENAI_API_KEY')",
40
+ "grading_conditions": ["load_dotenv()", "os.getenv", "OPENAI_API_KEY"],
41
+ "hint": "`load_dotenv()`๋ฅผ ๋ฐ˜๋“œ์‹œ ๋จผ์ € ํ˜ธ์ถœํ•ด์•ผ `os.getenv()`๊ฐ€ ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
42
+ "explanation": "`load_dotenv()` ํ˜ธ์ถœ ์ „์— `os.getenv()`๋ฅผ ์“ฐ๋ฉด ํ•ญ์ƒ `None`์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. `.env` ํŒŒ์ผ์€ `.gitignore`์— ์ถ”๊ฐ€ํ•˜๊ณ , `.env.example`์—๋Š” ํ‚ค ์ด๋ฆ„๋งŒ ์ ์–ด ํŒ€์— ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.",
43
+ "difficulty": "easy",
44
+ "takeaway": "load_dotenv() โ†’ os.getenv() ์ˆœ์„œ๊ฐ€ ๋ชจ๋“  Python ํ”„๋กœ์ ํŠธ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค."
45
+ },
46
+ {
47
+ "id": "docs-lab-5",
48
+ "title": "[๋‹จ๊ณ„ 5] ChatPromptTemplate โ€” ํ”„๋กฌํ”„ํŠธ ๊ตฌ์กฐํ™”",
49
+ "task_description": "**์ƒํ™ฉ**\n```python\nresponse = llm.invoke(f'๋ฌธ์„œ: {context}\\n์งˆ๋ฌธ: {question}')\n```\nf-string์œผ๋กœ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋”๋‹ˆ ๋ณ€์ˆ˜ ์ˆœ์„œ๋ฅผ ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ณ , ์‹œ์Šคํ…œ ์—ญํ• (์–ด์‹œ์Šคํ„ดํŠธ ํŽ˜๋ฅด์†Œ๋‚˜)์„ ์ง€์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๋‹จ์ˆœ ๋ฌธ์ž์—ด ๋ฐฉ์‹์€ ์—ญํ•  ๊ตฌ๋ถ„์ด ์—†๊ณ  LangChain ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n์•„๋ž˜ ์กฐ๊ฑด์œผ๋กœ `ChatPromptTemplate`์„ ์ž‘์„ฑํ•˜์„ธ์š”.\n- ์‹œ์Šคํ…œ: `\"๋‹น์‹ ์€ ๋ฌธ์„œ ๊ธฐ๋ฐ˜ AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ: {context}\"`\n- ์‚ฌ์šฉ์ž: `\"{question}\"`\n\n๊ฒฐ๊ณผ๋ฅผ `prompt` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
50
+ "expected_command": "prompt = ChatPromptTemplate.from_messages([\n (\"system\", \"๋‹น์‹ ์€ ๋ฌธ์„œ ๊ธฐ๋ฐ˜ AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ: {context}\"),\n (\"human\", \"{question}\")\n])",
51
+ "grading_conditions": ["ChatPromptTemplate.from_messages", "system", "context", "human", "question"],
52
+ "hint": "`ChatPromptTemplate.from_messages([(\"system\", \"...\"), (\"human\", \"...\")])` ํ˜•์‹์ž…๋‹ˆ๋‹ค.",
53
+ "explanation": "ChatPromptTemplate์€ ์—ญํ• ๋ณ„ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ์กฐํ™”ํ•˜๊ณ  ๋ณ€์ˆ˜ ์ž๋ฆฌ(`{context}`, `{question}`)๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `chain.invoke({\"context\": ..., \"question\": ...})`๋กœ ์‹คํ–‰ ์‹œ ์ž๋™์œผ๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.",
54
+ "difficulty": "easy",
55
+ "takeaway": "ChatPromptTemplate์€ ์—ญํ• (system/human)๊ณผ ๋ณ€์ˆ˜({})๋ฅผ ๋ถ„๋ฆฌํ•ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค."
56
+ },
57
+ {
58
+ "id": "docs-lab-6",
59
+ "title": "[๋‹จ๊ณ„ 6] LCEL โ€” ์ฒด์ธ์„ ํ•œ ์ค„๋กœ",
60
+ "task_description": "**์ƒํ™ฉ**\n```python\nformatted = prompt.format(context=ctx, question=q)\nllm_result = llm.invoke(formatted)\nfinal = output_parser.parse(llm_result)\n```\n3๋‹จ๊ณ„๋ฅผ ๋งค๋ฒˆ ์ง์ ‘ ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค. ์ฒด์ธ์ด ๊ธธ์–ด์งˆ์ˆ˜๋ก ๋” ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๊ฐ ๋‹จ๊ณ„๋ฅผ ์ˆ˜๋™์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊ธธ๊ณ  ๋‹จ๊ณ„ ์ถ”๊ฐ€/๋ณ€๊ฒฝ์ด ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`prompt`, `llm`, `output_parser`๋ฅผ LCEL `|` ์—ฐ์‚ฐ์ž๋กœ ์—ฐ๊ฒฐํ•ด `chain` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
61
+ "expected_command": "chain = prompt | llm | output_parser",
62
+ "grading_conditions": ["chain", "prompt", "|", "llm", "|", "output_parser"],
63
+ "hint": "์œ ๋‹‰์Šค ํŒŒ์ดํ”„์ฒ˜๋Ÿผ `|`๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
64
+ "explanation": "`chain = prompt | llm | output_parser` ํ•œ ์ค„์ด ์œ„์˜ 3์ค„์„ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. `chain.invoke({\"context\": ..., \"question\": ...})`๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๋‹จ๊ณ„ ์ถ”๊ฐ€๋„ `| ์ƒˆ๋‹จ๊ณ„`๋ฅผ ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
65
+ "difficulty": "easy",
66
+ "takeaway": "| ํŒŒ์ดํ”„ ์—ฐ์‚ฐ์ž๋กœ Runnable์„ ์—ฐ๊ฒฐํ•˜๋Š” LCEL์ด LangChain ๋ชจ๋“  ์ฒด์ธ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค."
67
+ },
68
+ {
69
+ "id": "docs-lab-7",
70
+ "title": "[๋‹จ๊ณ„ 7] TextSplitter โ€” ํ† ํฐ ์ดˆ๊ณผ ํ•ด๊ฒฐ",
71
+ "task_description": "**์ƒํ™ฉ**\n100ํŽ˜์ด์ง€ PDF ์ „์ฒด๋ฅผ LLM ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์—ˆ๋”๋‹ˆ:\n```\nError: This model's maximum context length is 128000 tokens. \nYour message resulted in 210000 tokens.\n```\n๋ฌธ์„œ๊ฐ€ ๊ธธ์ˆ˜๋ก LLM์— ํ†ต์งธ๋กœ ๋„ฃ๋Š” ๊ฑด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nLLM์€ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ† ํฐ ์ˆ˜์— ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๋ฅผ ์ž˜๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`RecursiveCharacterTextSplitter`๋กœ ์ฒญํฌ ํฌ๊ธฐ 500, ์˜ค๋ฒ„๋žฉ 50์œผ๋กœ `docs`๋ฅผ ๋ถ„ํ• ํ•ด `chunks`์— ์ €์žฅํ•˜์„ธ์š”.",
72
+ "expected_command": "splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)\nchunks = splitter.split_documents(docs)",
73
+ "grading_conditions": ["RecursiveCharacterTextSplitter", "chunk_size=500", "chunk_overlap=50", "split_documents"],
74
+ "hint": "์ƒ์„ฑ ํ›„ `.split_documents(docs)`๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.",
75
+ "explanation": "chunk_size=500์€ ์ฒญํฌ๋‹น ์ตœ๋Œ€ 500์ž, chunk_overlap=50์€ ์ธ์ ‘ ์ฒญํฌ๊ฐ€ 50์ž๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฒ„๋žฉ์ด ์—†์œผ๋ฉด ์ฒญํฌ ๊ฒฝ๊ณ„์—์„œ ๋ฌธ์žฅ์ด ์ž˜๋ ค ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.",
76
+ "difficulty": "medium",
77
+ "takeaway": "chunk_overlap์ด ์—†์œผ๋ฉด ์ฒญํฌ ๊ฒฝ๊ณ„์—์„œ ๋ฌธ๋งฅ์ด ์ž˜๋ ค ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ chunk_size์˜ 10% ์ •๋„๋ฅผ ์”๋‹ˆ๋‹ค."
78
+ },
79
+ {
80
+ "id": "docs-lab-8",
81
+ "title": "[๋‹จ๊ณ„ 8] FAISS VectorStore โ€” ์˜๋ฏธ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ ์ธ๋ฑ์Šค ์ƒ์„ฑ",
82
+ "task_description": "**์ƒํ™ฉ**\n๋ฌธ์„œ๋ฅผ ์ฒญํฌ๋กœ ์ž˜๋ž๋Š”๋ฐ, ์งˆ๋ฌธ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค 500๊ฐœ ์ฒญํฌ๋ฅผ ์ „๋ถ€ LLM์— ๋„ฃ์œผ๋ฉด ์ฒ˜์Œ๊ณผ ๋˜‘๊ฐ™์ด ํ† ํฐ์ด ์ดˆ๊ณผ๋ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n\"๊ด€๋ จ ์žˆ๋Š” ์ฒญํฌ๋งŒ\" ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰์€ ๋™์˜์–ด๋‚˜ ์˜๋ฏธ์  ์œ ์‚ฌ์„ฑ์„ ๋ชป ์žก์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`chunks`์™€ `embeddings` ๋ชจ๋ธ๋กœ FAISS VectorStore๋ฅผ ๋งŒ๋“ค๊ณ , `./faiss_index`์— ์ €์žฅํ•˜์„ธ์š”. (๋‹ค์Œ ์‹คํ–‰ ์‹œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด)",
83
+ "expected_command": "db = FAISS.from_documents(chunks, embeddings)\ndb.save_local(\"./faiss_index\")",
84
+ "grading_conditions": ["FAISS.from_documents", "chunks", "embeddings", "save_local"],
85
+ "hint": "`FAISS.from_documents(๋ฌธ์„œ, ์ž„๋ฒ ๋”ฉ๋ชจ๋ธ)`๋กœ ์ƒ์„ฑ, `.save_local(๊ฒฝ๋กœ)`๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
86
+ "explanation": "FAISS๋Š” ๊ฐ ์ฒญํฌ๋ฅผ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ด ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์งˆ๋ฌธ๋„ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ด ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด(์˜๋ฏธ์ ์œผ๋กœ ์œ ์‚ฌํ•œ) ์ฒญํฌ๋ฅผ O(1)์— ๊ฐ€๊น๊ฒŒ ์ฐพ์Šต๋‹ˆ๋‹ค. `save_local`๋กœ ์ €์žฅํ•˜๋ฉด ๋‹ค์Œ ์‹คํ–‰ ์‹œ ์ž„๋ฒ ๋”ฉ ์žฌ๊ณ„์‚ฐ ์—†์ด ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
87
+ "difficulty": "medium",
88
+ "takeaway": "FAISS.from_documents()๊ฐ€ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ๊ณผ ์ธ๋ฑ์Šค ๊ตฌ์ถ•์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๊ณ , save_local()๋กœ ์žฌ์‹œ์ž‘ ์‹œ ์žฌ๊ณ„์‚ฐ์„ ํ”ผํ•ฉ๋‹ˆ๋‹ค."
89
+ },
90
+ {
91
+ "id": "docs-lab-9",
92
+ "title": "[๋‹จ๊ณ„ 9] RAG ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ โ€” ์—ฐ๊ฒฐ",
93
+ "task_description": "**์ƒํ™ฉ**\n```python\ndocs = retriever.invoke(question) # 1. ๊ฒ€์ƒ‰\ncontext = '\\n'.join(d.page_content for d in docs) # 2. ํ•ฉ์น˜๊ธฐ\nprompt_text = prompt.format(...) # 3. ํ”„๋กฌํ”„ํŠธ\nresponse = llm.invoke(prompt_text) # 4. LLM ํ˜ธ์ถœ\nfinal = parser.parse(response) # 5. ํŒŒ์‹ฑ\n```\n5์ค„ ์ฝ”๋“œ๋ฅผ ๋งค ์งˆ๋ฌธ๋งˆ๋‹ค ๋ฐ˜๋ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nretriever โ†’ prompt โ†’ llm โ†’ parser๋ฅผ ์ˆ˜๋™์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žฅํ™ฉํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nLCEL๋กœ RAG ์ฒด์ธ์„ ํ•œ ์ค„๋กœ ๋งŒ๋“œ์„ธ์š”.\n- `retriever`, `prompt`, `llm`, `StrOutputParser` ๋ณ€์ˆ˜๋Š” ์ด๋ฏธ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.",
94
+ "expected_command": "chain = {\"context\": retriever, \"question\": RunnablePassthrough()} | prompt | llm | StrOutputParser()",
95
+ "grading_conditions": ["retriever", "RunnablePassthrough", "|", "prompt", "|", "llm", "StrOutputParser"],
96
+ "hint": "`{\"context\": retriever, \"question\": RunnablePassthrough()}`๋กœ retriever์™€ ์งˆ๋ฌธ์„ ๋™์‹œ์— prompt์— ๋„˜๊น๋‹ˆ๋‹ค.",
97
+ "explanation": "๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ฌถ์œผ๋ฉด `RunnableParallel`์ฒ˜๋Ÿผ ๋™์ž‘ํ•ด retriever ๊ฒ€์ƒ‰๊ณผ ์งˆ๋ฌธ ์ „๋‹ฌ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `RunnablePassthrough()`๋Š” ์ž…๋ ฅ(์งˆ๋ฌธ)์„ ๊ทธ๋Œ€๋กœ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
98
+ "difficulty": "hard",
99
+ "takeaway": "{context: retriever, question: RunnablePassthrough()} ํŒจํ„ด์ด RAG ์ฒด์ธ์—์„œ ๊ฒ€์ƒ‰๊ณผ ์งˆ๋ฌธ์„ ๋™์‹œ์— ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•˜๋Š” ํ•ต์‹ฌ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค."
100
+ },
101
+ {
102
+ "id": "docs-lab-10",
103
+ "title": "[๋‹จ๊ณ„ 10] session_state โ€” ๋Œ€ํ™”๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ",
104
+ "task_description": "**์ƒํ™ฉ**\nStreamlit ์ฑ—๋ด‡์„ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ๋งˆ๋‹ค ์ด์ „ ๋Œ€ํ™”๊ฐ€ ์ „๋ถ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nStreamlit์€ ๋ฒ„ํŠผ ํด๋ฆญ ๋“ฑ ๋ชจ๋“  ์ƒํ˜ธ์ž‘์šฉ๋งˆ๋‹ค Python ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. `messages = []`์ฒ˜๋Ÿผ ์„ ์–ธํ•˜๋ฉด ๋งค๋ฒˆ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`messages` ํ‚ค๊ฐ€ ์—†์„ ๋•Œ๋งŒ ๋นˆ ๋ฆฌ์ŠคํŠธ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํŒจํ„ด์„ ์ž‘์„ฑํ•˜์„ธ์š”.\n\n> ํ•ต์‹ฌ: `st.session_state`์— ์ €์žฅ๋œ ๊ฐ’์€ rerunํ•ด๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
105
+ "expected_command": "if \"messages\" not in st.session_state:\n st.session_state.messages = []",
106
+ "grading_conditions": ["\"messages\" not in st.session_state", "st.session_state.messages", "[]"],
107
+ "hint": "`if \"ํ‚ค\" not in st.session_state:` ๊ฐ€๋“œ๋ฅผ ์“ฐ๋ฉด ์ฒซ ์‹คํ–‰์—๋งŒ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.",
108
+ "explanation": "`if ... not in st.session_state` ํŒจํ„ด์ด ์—†์œผ๋ฉด ๋งค rerun๋งˆ๋‹ค `[]`๋กœ ๋ฆฌ์…‹๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์ด๋ฏธ ๊ฐ’์ด ์žˆ์„ ๋•Œ๋Š” ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.",
109
+ "difficulty": "easy",
110
+ "takeaway": "`if key not in st.session_state:` ๊ฐ€๋“œ ํŒจํ„ด ํ•˜๋‚˜๊ฐ€ Streamlit ์ƒํƒœ ๋ณด์กด์˜ ์ „๋ถ€์ž…๋‹ˆ๋‹ค."
111
+ },
112
+ {
113
+ "id": "docs-lab-11",
114
+ "title": "[๋‹จ๊ณ„ 11] cache_resource โ€” ์งˆ๋ฌธ๋งˆ๋‹ค LLM์ด ์ƒˆ๋กœ ๋กœ๋“œ๋˜๋Š” ๋ฌธ์ œ",
115
+ "task_description": "**์ƒํ™ฉ**\n์ฑ—๋ด‡์— ์งˆ๋ฌธํ•  ๋•Œ๋งˆ๋‹ค 3~4์ดˆ์”ฉ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ๋กœ๊ทธ๋ฅผ ๋ณด๋‹ˆ LLM ๊ฐ์ฒด๊ฐ€ ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.\n\n```python\ndef load_llm(): # ์ด ํ•จ์ˆ˜๊ฐ€ ๋งค rerun๋งˆ๋‹ค ์‹คํ–‰๋จ\n return OllamaLLM(model=\"llama3.2\")\n```\n\n**๋ฌธ์ œ**\nStreamlit์€ rerun๋งˆ๋‹ค ๋ชจ๋“  ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. LLM ๋กœ๋“œ๋Š” ๋น„์šฉ์ด ํฌ๋ฏ€๋กœ ํ•œ ๋ฒˆ๋งŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`load_llm()` ํ•จ์ˆ˜์— `st.cache_resource` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ ์•ฑ ์ˆ˜๋ช… ๋™์•ˆ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œ๋˜๊ฒŒ ํ•˜์„ธ์š”.",
116
+ "expected_command": "@st.cache_resource\ndef load_llm():\n return OllamaLLM(model=\"llama3.2\")",
117
+ "grading_conditions": ["@st.cache_resource", "def load_llm", "OllamaLLM"],
118
+ "hint": "ํ•จ์ˆ˜ ์ •์˜ ๋ฐ”๋กœ ์œ„์— `@st.cache_resource`๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค.",
119
+ "explanation": "`@st.cache_resource`๋Š” ๋ฐ˜ํ™˜ ๊ฐ์ฒด๋ฅผ ์•ฑ ์ „์ฒด(๋ชจ๋“  ์„ธ์…˜)์—์„œ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ์—๋งŒ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์ดํ›„์—๋Š” ์บ์‹œ๋œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.",
120
+ "difficulty": "easy",
121
+ "takeaway": "@st.cache_resource๋Š” LLMยทDB์ฒ˜๋Ÿผ ๊ณต์œ  ๊ฐ€๋Šฅํ•œ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด์—, @st.cache_data๋Š” ๊ณ„์‚ฐ ๊ฒฐ๊ณผ(DataFrame ๋“ฑ)์— ์”๋‹ˆ๋‹ค."
122
+ },
123
+ {
124
+ "id": "docs-lab-12",
125
+ "title": "[๋‹จ๊ณ„ 12] Streamlit secrets โ€” ๋ฐฐํฌ ํ™˜๊ฒฝ์˜ API ํ‚ค ๊ด€๋ฆฌ",
126
+ "task_description": "**์ƒํ™ฉ**\n๋กœ์ปฌ์—์„œ `.env`๋กœ ์ž˜ ์ž‘๋™ํ•˜๋˜ ์ฑ—๋ด‡์„ Streamlit Cloud์— ๋ฐฐํฌํ–ˆ๋”๋‹ˆ:\n```\nKeyError: 'OPENAI_API_KEY'\n```\nStreamlit Cloud๋Š” ๋กœ์ปฌ์˜ `.env` ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” `.env` ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ API ํ‚ค๋ฅผ ์ฃผ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nStreamlit Cloud ๋Œ€์‹œ๋ณด๋“œ์˜ Secrets์— ํ‚ค๋ฅผ ๋“ฑ๋กํ•œ ๋’ค, ์ฝ”๋“œ์—์„œ ์ฝ๋Š” ํ•œ ์ค„์„ ์ž‘์„ฑํ•˜์„ธ์š”.",
127
+ "expected_command": "api_key = st.secrets[\"OPENAI_API_KEY\"]",
128
+ "grading_conditions": ["st.secrets", "OPENAI_API_KEY"],
129
+ "hint": "`st.secrets[\"ํ‚ค์ด๋ฆ„\"]`์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
130
+ "explanation": "`st.secrets`๋Š” ๋กœ์ปฌ์—์„œ๋Š” `.streamlit/secrets.toml`, ๋ฐฐํฌ ์‹œ์—๋Š” ๋Œ€์‹œ๋ณด๋“œ Secrets์—์„œ ๊ฐ’์„ ์ฝ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์•„๋„ ๋‘ ํ™˜๊ฒฝ์—์„œ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.",
131
+ "difficulty": "easy",
132
+ "takeaway": "st.secrets๋Š” ๋กœ์ปฌ(.streamlit/secrets.toml)๊ณผ ๋ฐฐํฌ(Streamlit Cloud) ํ™˜๊ฒฝ์„ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค."
133
+ },
134
+ {
135
+ "id": "docs-lab-13",
136
+ "title": "[๋‹จ๊ณ„ 13] ๋Œ€ํ™” ๋ฉ”๋ชจ๋ฆฌ โ€” ์ด์ „ ๋Œ€ํ™”๋ฅผ ๊ธฐ์–ตํ•˜๊ฒŒ",
137
+ "task_description": "**์ƒํ™ฉ**\n```\n์‚ฌ์šฉ์ž: ํŒŒ์ด์ฌ์ด ๋ญ์•ผ?\n์ฑ—๋ด‡: ํŒŒ์ด์ฌ์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์ž…๋‹ˆ๋‹ค...\n์‚ฌ์šฉ์ž: ๊ทธ๊ฑฐ ์–ด๋””์„œ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด?\n์ฑ—๋ด‡: ๋ฌด์—‡์„ ๋ฐฐ์šฐ๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”? (??)\n```\n\"๊ทธ๊ฑฐ\"๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ฑ—๋ด‡์ด ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๋งค ์งˆ๋ฌธ์ด ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nRAG ์ฒด์ธ์€ ์ด์ „ ๋Œ€ํ™” ๋‚ด์šฉ์„ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™”๋ฅผ ์œ„ํ•ด ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ฃผ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n๊ธฐ์กด `chain`์„ `RunnableWithMessageHistory`๋กœ ๊ฐ์‹ธ์„ธ์š”.\n- `input_messages_key`: `\"question\"`\n- `history_messages_key`: `\"chat_history\"`\n- ๊ฒฐ๊ณผ๋ฅผ `chain_with_history`์— ์ €์žฅ",
138
+ "expected_command": "chain_with_history = RunnableWithMessageHistory(\n chain,\n get_session_history,\n input_messages_key=\"question\",\n history_messages_key=\"chat_history\"\n)",
139
+ "grading_conditions": ["RunnableWithMessageHistory", "chain", "get_session_history", "input_messages_key", "history_messages_key"],
140
+ "hint": "`RunnableWithMessageHistory(์ฒด์ธ, ํžˆ์Šคํ† ๋ฆฌํ•จ์ˆ˜, input_messages_key=..., history_messages_key=...)`",
141
+ "explanation": "`RunnableWithMessageHistory`๋Š” ๋งค invoke๋งˆ๋‹ค session_id์— ํ•ด๋‹นํ•˜๋Š” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๋ถˆ๋Ÿฌ์™€ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. invoke ์‹œ `config={\"configurable\": {\"session_id\": \"user-1\"}}`์„ ๋„˜๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
142
+ "difficulty": "medium",
143
+ "takeaway": "RunnableWithMessageHistory๋กœ ๊ธฐ์กด ์ฒด์ธ์„ ๊ฐ์‹ธ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. session_id ๊ด€๋ฆฌ๊ฐ€ ์ „๋ถ€์ž…๋‹ˆ๋‹ค."
144
+ },
145
+ {
146
+ "id": "docs-lab-14",
147
+ "title": "[๋‹จ๊ณ„ 14] HF Spaces โ€” ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋„ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋ฐฐํฌ",
148
+ "task_description": "**์ƒํ™ฉ**\n์™„์„ฑ๋œ ์ฑ—๋ด‡์„ ํŒ€์›์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋ ค๋‹ˆ \"๋กœ์ปฌ์—์„œ๋งŒ ๋Œ์•„๊ฐ€๊ณ  ๋ฐ–์—์„  ์ ‘๊ทผ์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.\"\n\n**๋ฌธ์ œ**\n์„œ๋ฒ„๋ฅผ ์ง์ ‘ ๊ตฌ๋งคํ•˜๊ฑฐ๋‚˜ AWS/GCP๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์™ธ๋ถ€์— ๊ณต๊ฐœํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nHugging Face Spaces๋กœ ๋ฌด๋ฃŒ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. `README.md` ์ƒ๋‹จ์— SDK๋ฅผ ์„ ์–ธํ•˜์„ธ์š”.\n- SDK: `streamlit`, ์•ฑ ํŒŒ์ผ: `app.py`, Python: `3.11`",
149
+ "expected_command": "---\nsdk: streamlit\napp_file: app.py\npython_version: \"3.11\"\n---",
150
+ "grading_conditions": ["sdk: streamlit", "app_file: app.py"],
151
+ "hint": "README.md ๋งจ ์œ„ `---` YAML ๋ธ”๋ก ์•ˆ์— ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.",
152
+ "explanation": "HF Spaces๋Š” README ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ `sdk` ํ•„๋“œ๋กœ ๋Ÿฐํƒ€์ž„์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. `sdk: streamlit`์ด๋ฉด ์ž๋™์œผ๋กœ Streamlit ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋ฌด๋ฃŒ ํ”Œ๋žœ์œผ๋กœ๋„ ์™ธ๋ถ€ URL์ด ์ฆ‰์‹œ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
153
+ "difficulty": "easy",
154
+ "takeaway": "README YAML ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ 3์ค„(sdk, app_file, python_version)๋งŒ์œผ๋กœ HF Spaces ๋ฐฐํฌ๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค."
155
+ },
156
+ {
157
+ "id": "docs-lab-15",
158
+ "title": "[๋‹จ๊ณ„ 15] HF Docker Space โ€” SDK ์ œ์•ฝ ์—†์ด ๋ฐฐํฌ",
159
+ "task_description": "**์ƒํ™ฉ**\nHF Spaces Streamlit SDK๋กœ ๋ฐฐํฌํ–ˆ๋Š”๋ฐ, FastAPI ์„œ๋ฒ„๋ฅผ ํ•จ๊ป˜ ๋„์šฐ๊ณ  ์‹ถ์–ด ํฌํŠธ๋ฅผ ์ปค์Šคํ…€ํ•˜๋ ค๋‹ˆ ์„ค์ •์ด ๋ง‰ํ˜€์žˆ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nStreamlit/Gradio SDK๋Š” ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์ด ๊ณ ์ •๋ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ์„œ๋ฒ„๋ฅผ ์ œ์–ดํ•˜๋ ค๋ฉด Dockerfile์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nDocker Space์—์„œ HF๊ฐ€ ์ธ์‹ํ•˜๋Š” ํฌํŠธ๋กœ ์•ฑ์„ ๋…ธ์ถœํ•˜๋Š” Dockerfile ์ง€์‹œ์–ด๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.\n\n> HF Docker Space ๊ธฐ๋ณธ ํฌํŠธ: 7860",
160
+ "expected_command": "EXPOSE 7860\nENV PORT=7860",
161
+ "grading_conditions": ["EXPOSE 7860"],
162
+ "hint": "Streamlit ๊ธฐ๋ณธ ํฌํŠธ๋Š” 8501์ด์ง€๋งŒ HF๋Š” 7860์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.",
163
+ "explanation": "Docker Space์—์„œ๋Š” `EXPOSE 7860`์œผ๋กœ ํฌํŠธ๋ฅผ ์„ ์–ธํ•˜๊ณ , `streamlit run app.py --server.port 7860`์œผ๋กœ Streamlit๋„ 7860์— ๋ฐ”์ธ๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
164
+ "difficulty": "medium",
165
+ "takeaway": "HF Docker Space๋Š” ํ•ญ์ƒ ํฌํŠธ 7860์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. EXPOSE 7860๊ณผ ์•ฑ์˜ ํฌํŠธ ์„ค์ •์„ ์ผ์น˜์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค."
166
+ },
167
+ {
168
+ "id": "docs-lab-16",
169
+ "title": "[๋‹จ๊ณ„ 16] FastAPI โ€” ์ฑ—๋ด‡ ๋กœ์ง์„ API๋กœ ๋ถ„๋ฆฌ",
170
+ "task_description": "**์ƒํ™ฉ**\nํ”„๋ก ํŠธ์—”๋“œ ํŒ€์—์„œ \"์ฑ—๋ด‡ API ์—”๋“œํฌ์ธํŠธ๋งŒ ์ฃผ์„ธ์š”. React์—์„œ ๋ถ™์ด๊ฒ ์Šต๋‹ˆ๋‹ค.\"๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ Streamlit์— ๋กœ์ง์ด ๋ฌถ์—ฌ ์žˆ์–ด ๋ถ„๋ฆฌ๊ฐ€ ์•ˆ ๋ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nStreamlit ์•ฑ์€ UI์™€ ๋กœ์ง์ด ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด ๋‹ค๋ฅธ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n์•„๋ž˜ `???`๋ฅผ ์ฑ„์›Œ POST `/ask` ์—”๋“œํฌ์ธํŠธ๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.\n\n```python\nclass Query(BaseModel):\n question: str\n\n@app.post('/ask')\nasync def ask(q: Query):\n answer = ???\n return {\"answer\": answer}\n```",
171
+ "expected_command": "answer = chain.invoke(q.question)",
172
+ "grading_conditions": ["chain.invoke", "q.question"],
173
+ "hint": "Pydantic ๋ชจ๋ธ `q`์˜ `.question` ์†์„ฑ์œผ๋กœ ์งˆ๋ฌธ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
174
+ "explanation": "FastAPI๋Š” Pydantic ๋ชจ๋ธ ํƒ€์ž…์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ JSON ๋ฐ”๋””๋ฅผ ํŒŒ์‹ฑํ•ฉ๋‹ˆ๋‹ค. `q.question`์œผ๋กœ ์งˆ๋ฌธ ๋ฌธ์ž์—ด์„ ๊บผ๋‚ด chain์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
175
+ "difficulty": "easy",
176
+ "takeaway": "FastAPI + Pydantic ์กฐํ•ฉ์ด LLM ์„œ๋น„์Šค๋ฅผ API๋กœ ๋…ธ์ถœํ•˜๋Š” ๊ฐ€์žฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Pydantic์ด JSON ๊ฒ€์ฆ์„ ์ž๋™ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค."
177
+ },
178
+ {
179
+ "id": "docs-lab-17",
180
+ "title": "[๋‹จ๊ณ„ 17] FastAPI ๊ฐœ๋ฐœ ์„œ๋ฒ„ โ€” ์ฝ”๋“œ ์ˆ˜์ • ์ฆ‰์‹œ ๋ฐ˜์˜",
181
+ "task_description": "**์ƒํ™ฉ**\n```python\nuvicorn main:app\n```\n์—”๋“œํฌ์ธํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๋ฅผ `Ctrl+C`๋กœ ์ข…๋ฃŒํ•˜๊ณ  ๋‹ค์‹œ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ ์ค‘์— ๋„ˆ๋ฌด ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๊ธฐ๋ณธ uvicorn์€ ํŒŒ์ผ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`main.py`์˜ `app` ์ธ์Šคํ„ด์Šค๋ฅผ ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์žฌ์‹œ์ž‘๋˜๋Š” ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜์„ธ์š”.",
182
+ "expected_command": "uvicorn main:app --reload",
183
+ "grading_conditions": ["uvicorn", "main:app", "--reload"],
184
+ "hint": "`--reload` ํ”Œ๋ž˜๊ทธ ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.",
185
+ "explanation": "`--reload`๋Š” ์†Œ์Šค ํŒŒ์ผ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด ์„œ๋ฒ„๋ฅผ ์ž๋™ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ ์ค‘์—๋งŒ ์‚ฌ์šฉํ•˜๋ฉฐ, ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์ œ๊ฑฐํ•˜๊ณ  `gunicorn` ๊ฐ™์€ ํ”„๋กœ๋•์…˜ ์„œ๋ฒ„๋ฅผ ์”๋‹ˆ๋‹ค.",
186
+ "difficulty": "easy",
187
+ "takeaway": "--reload๋Š” ๊ฐœ๋ฐœ์šฉ, ํ”„๋กœ๋•์…˜์—์„œ๋Š” gunicorn์„ ์”๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ถ„์„ ๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค."
188
+ },
189
+ {
190
+ "id": "docs-lab-18",
191
+ "title": "[๋‹จ๊ณ„ 18] LangSmith โ€” ์–ด๋А ๋‹จ๊ณ„์—์„œ ํ‹€๋ฆฐ ๋‹ต์ด ๋‚˜์˜ค๋Š”๊ฐ€",
192
+ "task_description": "**์ƒํ™ฉ**\n์ฑ—๋ด‡์ด ์—‰๋šฑํ•œ ๋‹ต์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊น…์„ ์œ„ํ•ด `print()`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ:\n\n```python\nprint('์ฒด์ธ ์‹คํ–‰ ์ค‘...')\nresult = chain.invoke({'question': q})\nprint('๊ฒฐ๊ณผ:', result)\n```\n\n์ตœ์ข… ์ž…์ถœ๋ ฅ๋งŒ ๋ณด์ผ ๋ฟ, **retriever๊ฐ€ ์ž˜๋ชป๋œ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์™”๋Š”์ง€**, **ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์ž˜๋ชป ๋ Œ๋”๋ง๋๋Š”์ง€**, **LLM์ด ํ‹€๋ฆฐ ๋‹ต์„ ํ–ˆ๋Š”์ง€** ๊ตฌ๋ถ„์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n`print()`๋Š” ๊ฐ ๋‹จ๊ณ„์˜ ๋‚ด๋ถ€ ์ž…์ถœ๋ ฅ์„ ๋ณผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nLangSmith ์ถ”์ ์„ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”. ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3๊ฐœ๋ฅผ `os.environ`์œผ๋กœ ์„ค์ •ํ•˜์„ธ์š”.\n- `LANGCHAIN_TRACING_V2 = 'true'`\n- `LANGCHAIN_PROJECT = 'rag-debug'`\n- `LANGCHAIN_API_KEY = 'ls__your_key'`",
193
+ "expected_command": "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\nos.environ['LANGCHAIN_PROJECT'] = 'rag-debug'\nos.environ['LANGCHAIN_API_KEY'] = 'ls__your_key'",
194
+ "grading_conditions": ["LANGCHAIN_TRACING_V2", "true", "LANGCHAIN_PROJECT", "LANGCHAIN_API_KEY"],
195
+ "hint": "`os.environ['๋ณ€์ˆ˜๋ช…'] = '๊ฐ’'` ํŒจํ„ด์œผ๋กœ 3์ค„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.",
196
+ "explanation": "์ด 3์ค„ ์ดํ›„์˜ ๋ชจ๋“  LangChain ์‹คํ–‰์€ LangSmith์— ์ž๋™ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. LangSmith UI์—์„œ retriever ์ž…์ถœ๋ ฅ, ๋ Œ๋”๋ง๋œ ํ”„๋กฌํ”„ํŠธ, LLM ์‘๋‹ต์„ tree ํ˜•ํƒœ๋กœ ๋‹จ๊ณ„๋ณ„๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
197
+ "difficulty": "easy",
198
+ "takeaway": "ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3๊ฐœ(TRACING_V2=true, PROJECT, API_KEY)๋งŒ ์„ค์ •ํ•˜๋ฉด ์ดํ›„ ๋ชจ๋“  LangChain ์‹คํ–‰์ด ์ž๋™์œผ๋กœ LangSmith์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค."
199
+ },
200
+ {
201
+ "id": "docs-lab-19",
202
+ "title": "[๋‹จ๊ณ„ 19] Guardrails โ€” LLM์ด ๋‚˜์œ ๋ง์„ ํ•˜๋ฉด",
203
+ "task_description": "**์ƒํ™ฉ**\nํ…Œ์ŠคํŠธ ์ค‘ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ ‡๊ฒŒ ์ž…๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค:\n\n> \"์š•์„ค์„ ์ตœ๋Œ€ํ•œ ์„ž์–ด์„œ ์š”์•ฝํ•ด์ค˜\"\n\nLLM์ด ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผ ์œ ํ•ดํ•œ ๋‚ด์šฉ์„ ์ถœ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค์— ์ด๋Ÿฐ ์‘๋‹ต์ด ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nLLM์€ ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜์— ์ทจ์•ฝํ•˜๊ณ , ์œ ํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nGuardrails AI๋กœ ์œ ํ•ด์„ฑ ๊ฒ€์ฆ๊ธฐ๋ฅผ ์ ์šฉํ•˜์„ธ์š”.\n- ์ž„๊ณ„๊ฐ’: `0.5` (์ด ์ด์ƒ์ด๋ฉด ์œ ํ•ด ํŒ์ •)\n- ์‹คํŒจ ์‹œ: `'exception'` (์˜ˆ์™ธ ๋ฐœ์ƒ์œผ๋กœ ์‘๋‹ต ์ฐจ๋‹จ)",
204
+ "expected_command": "guard = Guard().use(ToxicLanguage(threshold=0.5, on_fail='exception'))",
205
+ "grading_conditions": ["Guard()", "ToxicLanguage", "threshold=0.5", "on_fail='exception'"],
206
+ "hint": "`Guard().use(๊ฒ€์ฆ๊ธฐ(ํŒŒ๋ผ๋ฏธํ„ฐ))` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
207
+ "explanation": "์ด Guard๋กœ LLM์„ ๊ฐ์‹ธ๋ฉด LLM ์‘๋‹ต์ด ์œ ํ•ด์„ฑ ์ž„๊ณ„๊ฐ’์„ ์ดˆ๊ณผํ•  ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. `on_fail='reask'`๋กœ ๋ฐ”๊พธ๋ฉด LLM์—๊ฒŒ ์ž๋™์œผ๋กœ ์žฌ์ž‘์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.",
208
+ "difficulty": "medium",
209
+ "takeaway": "Guard().use(๊ฒ€์ฆ๊ธฐ) ํŒจํ„ด์œผ๋กœ LLM ์ถœ๋ ฅ์— ์•ˆ์ „์žฅ์น˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. on_fail='exception'์€ ์ฐจ๋‹จ, 'reask'๋Š” ์ž๋™ ์žฌ์ž‘์„ฑ์ž…๋‹ˆ๋‹ค."
210
+ },
211
+ {
212
+ "id": "docs-lab-20",
213
+ "title": "[๋‹จ๊ณ„ 20] Airflow โ€” ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ชป ๋†“์น˜๋Š” ๋ฌธ์ œ",
214
+ "task_description": "**์ƒํ™ฉ**\n๋งค์ฃผ ์ƒˆ ๋ฌธ์„œ๊ฐ€ ์ถ”๊ฐ€๋˜๋Š”๋ฐ, ์žฌ์ธ๋ฑ์‹ฑ์„ ์žŠ์–ด๋ฒ„๋ ค ์ฑ—๋ด‡์ด ์˜ค๋ž˜๋œ ์ •๋ณด๋กœ๋งŒ ๋‹ตํ•ฉ๋‹ˆ๋‹ค. ํŒ€์›์ด \"์™œ ์ด ๋‚ด์šฉ์ด ๊ฒ€์ƒ‰์ด ์•ˆ ๋ผ์š”?\"๋ผ๊ณ  ๋ฌผ์–ด๋ดค๋Š”๋ฐ, 2์ฃผ ์ „์— ์ถ”๊ฐ€๋œ ๋ฌธ์„œ์˜€์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์žฌ์ธ๋ฑ์‹ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด ๋ฐ˜๋“œ์‹œ ๋น ๋œจ๋ฆฌ๋Š” ๋‚ ์ด ์ƒ๊น๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`run_reindex` ํ•จ์ˆ˜๋ฅผ ๋งค์ผ ์ž์ •์— ์ž๋™ ์‹คํ–‰ํ•˜๋Š” Airflow DAG๋ฅผ ์ •์˜ํ•˜์„ธ์š”.\n- DAG ID: `daily_reindex`, ์Šค์ผ€์ค„: `@daily`, start_date: `datetime(2024, 1, 1)`",
215
+ "expected_command": "with DAG('daily_reindex', schedule='@daily', start_date=datetime(2024, 1, 1)) as dag:",
216
+ "grading_conditions": ["DAG", "daily_reindex", "schedule='@daily'", "start_date", "datetime"],
217
+ "hint": "`with DAG('id', schedule='@daily', start_date=datetime(...)) as dag:` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
218
+ "explanation": "`@daily`๋Š” ๋งค์ผ ์ž์ •์— ์‹คํ–‰ํ•˜๋Š” Airflow ๋‚ด์žฅ ์Šค์ผ€์ค„ ํ‘œํ˜„์‹์ž…๋‹ˆ๋‹ค. ์ด DAG๊ฐ€ ์žˆ์œผ๋ฉด ๋‹ด๋‹น์ž๊ฐ€ ์—†๋Š” ์ฃผ๋ง์—๋„ ์ž๋™์œผ๋กœ ์žฌ์ธ๋ฑ์‹ฑ๋ฉ๋‹ˆ๋‹ค.",
219
+ "difficulty": "medium",
220
+ "takeaway": "@daily ์Šค์ผ€์ค„ ํ‘œํ˜„์‹ ํ•˜๋‚˜๋กœ ๋งค์ผ ์ž์ • ์ž๋™ ์‹คํ–‰์ด ๋ฉ๋‹ˆ๋‹ค. ์ˆ˜๋™ ์‹คํ–‰์€ ํ•ญ์ƒ ๋น ๋œจ๋ฆฌ๋Š” ๋‚ ์ด ์ƒ๊น๋‹ˆ๋‹ค."
221
+ },
222
+ {
223
+ "id": "docs-lab-21",
224
+ "title": "[๋‹จ๊ณ„ 21] Airflow PythonOperator โ€” ์žฌ์ธ๋ฑ์‹ฑ ํ•จ์ˆ˜ ๋“ฑ๋ก",
225
+ "task_description": "**์ƒํ™ฉ**\nDAG ๊ตฌ์กฐ๋Š” ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ์‹ค์ œ๋กœ ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ• ์ง€ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nAirflow DAG๋Š” \"์–ธ์ œ ์‹คํ–‰ํ• ์ง€\"๋งŒ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. \"๋ฌด์—‡์„ ์‹คํ–‰ํ• ์ง€\"๋Š” Operator๋กœ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`reindex` ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” PythonOperator๋ฅผ DAG ์•ˆ์— ๋“ฑ๋กํ•˜์„ธ์š”.\n- task_id: `'reindex'`, python_callable: `reindex`",
226
+ "expected_command": "task = PythonOperator(task_id='reindex', python_callable=reindex)",
227
+ "grading_conditions": ["PythonOperator", "task_id='reindex'", "python_callable=reindex"],
228
+ "hint": "`PythonOperator(task_id='...', python_callable=ํ•จ์ˆ˜)` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
229
+ "explanation": "`python_callable`์— ํ•จ์ˆ˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ(`reindex()`)์ด ์•„๋‹Œ ํ•จ์ˆ˜ ์ฐธ์กฐ(`reindex`)๋ฅผ ๋„˜๊ฒจ์•ผ Airflow๊ฐ€ ์Šค์ผ€์ค„์— ๋งž์ถฐ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
230
+ "difficulty": "easy",
231
+ "takeaway": "python_callable์—๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ(reindex())์ด ์•„๋‹Œ ํ•จ์ˆ˜ ์ฐธ์กฐ(reindex)๋ฅผ ๋„˜๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ด„ํ˜ธ์˜ ์œ ๋ฌด๊ฐ€ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค."
232
+ },
233
+ {
234
+ "id": "docs-lab-22",
235
+ "title": "[๋‹จ๊ณ„ 22] MLflow โ€” ์–ด๋–ค ์„ค์ •์ด ๋” ์ข‹์€์ง€ ๊ธฐ์–ต์ด ์•ˆ ๋‚จ",
236
+ "task_description": "**์ƒํ™ฉ**\nchunk_size๋ฅผ 500์œผ๋กœ ํ–ˆ์„ ๋•Œ์™€ 1000์œผ๋กœ ํ–ˆ์„ ๋•Œ ๋‹ต๋ณ€ ํ’ˆ์งˆ์ด ๋‹ฌ๋ž์Šต๋‹ˆ๋‹ค. ๋ฉฐ์น  ํ›„ ๋‹ค์‹œ ๋น„๊ตํ•˜๋ ค๋‹ˆ \"์–ด๋–ค ์„ค์ •์œผ๋กœ ์–ด๋–ค ์ ์ˆ˜๊ฐ€ ๋‚˜์™”๋”๋ผ?\"๊ฐ€ ๊ธฐ์–ต์ด ์•ˆ ๋‚ฉ๋‹ˆ๋‹ค. ๋…ธ์…˜์— ์ˆ˜๋™์œผ๋กœ ๊ธฐ๋กํ–ˆ์ง€๋งŒ ๋ˆ„๋ฝ์ด ๋งŽ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nํŒŒ๋ผ๋ฏธํ„ฐ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๊ธฐ๋กํ•˜๋ฉด ๋น ๋œจ๋ฆฌ๊ฑฐ๋‚˜ ์žŠ์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nMLflow๋กœ ์‹คํ—˜์„ ๊ธฐ๋กํ•˜์„ธ์š”.\n- run ์ด๋ฆ„: `'rag-chunk500-k4'`\n- ํŒŒ๋ผ๋ฏธํ„ฐ: `chunk_size=500`, `k=4`\n- ๋ฉ”ํŠธ๋ฆญ: `answer_relevance=0.87`",
237
+ "expected_command": "with mlflow.start_run(run_name='rag-chunk500-k4'):\n mlflow.log_param('chunk_size', 500)\n mlflow.log_param('k', 4)\n mlflow.log_metric('answer_relevance', 0.87)",
238
+ "grading_conditions": ["mlflow.start_run", "log_param", "chunk_size", "log_metric", "answer_relevance"],
239
+ "hint": "`with mlflow.start_run(run_name='...'):`๋กœ ๋ธ”๋ก์„ ์—ด๊ณ , ์•ˆ์—์„œ `log_param`, `log_metric`์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.",
240
+ "explanation": "`mlflow ui` ๋ช…๋ น์œผ๋กœ ์‹คํ—˜ ๋น„๊ต ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์—ด๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ๋ฉ”ํŠธ๋ฆญ ์ฐจํŠธ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜๋™ ๊ธฐ๋ก ์—†์ด ๋ชจ๋“  ์‹คํ—˜์ด ์ž๋™ ๋ณด์กด๋ฉ๋‹ˆ๋‹ค.",
241
+ "difficulty": "medium",
242
+ "takeaway": "mlflow.start_run() context manager๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฉ”ํŠธ๋ฆญ์„ ๊ธฐ๋กํ•˜๋ฉด ๋‚˜์ค‘์— mlflow ui์—์„œ ์‹คํ—˜์„ ์‹œ๊ฐ์ ์œผ๋กœ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
243
+ },
244
+ {
245
+ "id": "docs-lab-23",
246
+ "title": "[๋‹จ๊ณ„ 23] MCP โ€” Claude์—์„œ ๋ฐ”๋กœ ๋‚ด ๋ฌธ์„œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด",
247
+ "task_description": "**์ƒํ™ฉ**\nClaude Desktop์œผ๋กœ ๋Œ€ํ™” ์ค‘ \"์šฐ๋ฆฌ ๋‚ด๋ถ€ ๋ฌธ์„œ์— ์ด ๋‚ด์šฉ์ด ์žˆ์—ˆ๋Š”๋ฐ...\"๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค ๋•Œ๋งˆ๋‹ค Streamlit ์•ฑ์œผ๋กœ ์ด๋™ํ•ด์„œ ๊ฒ€์ƒ‰ํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋ณต์‚ฌํ•ด Claude์— ๋ถ™์—ฌ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nClaude๊ฐ€ ๋‚ด RAG ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ณ„๋„ ์•ฑ์„ ์™•๋ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nMCP ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์–ด Claude์—๊ฒŒ `search_docs` ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜์„ธ์š”.\n- ์„œ๋ฒ„๋ช…: `'rag-server'`\n- ๋„๊ตฌ ํ•จ์ˆ˜: `search_docs(query: str)` โ†’ `retriever.invoke(query)`๋กœ ๊ฒ€์ƒ‰ ํ›„ page_content๋ฅผ ์ค„๋ฐ”๊ฟˆ์œผ๋กœ ํ•ฉ์ณ ๋ฐ˜ํ™˜",
248
+ "expected_command": "mcp = FastMCP('rag-server')\n\n@mcp.tool()\ndef search_docs(query: str) -> str:\n results = retriever.invoke(query)\n return '\\n'.join(r.page_content for r in results)",
249
+ "grading_conditions": ["FastMCP", "@mcp.tool()", "def search_docs", "query: str", "retriever.invoke"],
250
+ "hint": "`FastMCP('์„œ๋ฒ„๋ช…')`์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , `@mcp.tool()`์„ ํ•จ์ˆ˜ ์œ„์— ๋ถ™์ž…๋‹ˆ๋‹ค.",
251
+ "explanation": "`@mcp.tool()` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ๋ถ™์œผ๋ฉด Claude๋Š” ๋Œ€ํ™” ์ค‘ ์ด ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ docstring์ด Claude์—๊ฒŒ \"์–ธ์ œ ์ด ๋„๊ตฌ๋ฅผ ์“ฐ๋Š”๊ฐ€\"๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์„ค๋ช…์ด ๋ฉ๋‹ˆ๋‹ค.",
252
+ "difficulty": "medium",
253
+ "takeaway": "@mcp.tool() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ•˜๋‚˜๋กœ ํŒŒ์ด์ฌ ํ•จ์ˆ˜๊ฐ€ Claude๊ฐ€ ๋Œ€ํ™” ์ค‘ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค."
254
+ },
255
+ {
256
+ "id": "docs-lab-24",
257
+ "title": "[๋‹จ๊ณ„ 24] MCP ์„œ๋ฒ„ ์‹คํ–‰ โ€” Claude Desktop์— ์—ฐ๊ฒฐ",
258
+ "task_description": "**์ƒํ™ฉ**\nMCP ์„œ๋ฒ„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ, Claude Desktop์ด ์–ด๋–ป๊ฒŒ ์ด ์„œ๋ฒ„๋ฅผ ์ฐพ๊ณ  ์‹คํ–‰ํ•˜๋Š”์ง€ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nClaude Desktop์€ stdio(ํ‘œ์ค€ ์ž…์ถœ๋ ฅ)๋กœ MCP ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์™€ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ transport๋ฅผ ์ง€์ •ํ•ด์•ผ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`mcp` ์ธ์Šคํ„ด์Šค๋ฅผ stdio ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.\n\n> Claude Desktop ์„ค์ • ํŒŒ์ผ์— ์ด ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๋ช…๋ น์–ด๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ์ž๋™ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
259
+ "expected_command": "mcp.run(transport='stdio')",
260
+ "grading_conditions": ["mcp.run", "transport='stdio'"],
261
+ "hint": "`mcp.run(transport='...')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋กœ์ปฌ์€ stdio, HTTP ์„œ๋ฒ„๋Š” sse์ž…๋‹ˆ๋‹ค.",
262
+ "explanation": "Claude Desktop์€ `claude_desktop_config.json`์— ๋“ฑ๋ก๋œ ๋ช…๋ น์–ด๋กœ MCP ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๊ณ  stdio๋กœ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๋ฉด ๋Œ€ํ™” ์ค‘ `search_docs` ๋„๊ตฌ๊ฐ€ ์ž๋™์œผ๋กœ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.",
263
+ "difficulty": "easy",
264
+ "takeaway": "transport='stdio'๊ฐ€ Claude Desktop ์—ฐ๊ฒฐ, 'sse'๊ฐ€ HTTP ์„œ๋ฒ„ ์—ฐ๊ฒฐ์ž…๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ ๋ฐฉ์‹์„ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค."
265
+ },
266
+ {
267
+ "id": "docs-lab-25",
268
+ "title": "[๋ณด๋„ˆ์Šค] Gradio โ€” Streamlit ์—†์ด ์ฑ—๋ด‡ UI ๋งŒ๋“ค๊ธฐ",
269
+ "task_description": "**์ƒํ™ฉ**\nStreamlit ์ฑ—๋ด‡์— ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต(ํƒ€์ดํ•‘ ํšจ๊ณผ)์„ ๊ตฌํ˜„ํ•˜๋ ค๋‹ˆ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. `st.write_stream`๊ณผ generator ํ•จ์ˆ˜ ์„ค์ •์ด ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nStreamlit์€ rerun ๊ธฐ๋ฐ˜์ด๋ผ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ UI ๊ตฌํ˜„์ด ์ƒ๋Œ€์ ์œผ๋กœ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nGradio์˜ `ChatInterface`๋Š” generator ํ•จ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋ฉด ์ž๋™์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ UI๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.\n\n`chat(message, history)` ํ•จ์ˆ˜๋ฅผ `gr.ChatInterface`์— ์—ฐ๊ฒฐํ•˜๊ณ , ์ œ๋ชฉ `'RAG ์ฑ—๋ด‡'`์„ ์„ค์ •ํ•œ ๋’ค ์‹คํ–‰ํ•˜์„ธ์š”.",
270
+ "expected_command": "demo = gr.ChatInterface(\n fn=chat,\n title='RAG ์ฑ—๋ด‡'\n)\ndemo.launch()",
271
+ "grading_conditions": ["gr.ChatInterface", "fn=chat", "demo.launch()"],
272
+ "hint": "`gr.ChatInterface(fn=ํ•จ์ˆ˜, title='์ œ๋ชฉ')`์œผ๋กœ ๋งŒ๋“ค๊ณ  `.launch()`๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
273
+ "explanation": "`gr.ChatInterface`๋Š” `fn`์ด `(message: str, history: list) -> str` ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋”ฐ๋ฅด๋ฉด ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. generator ํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋ฉด ํ† ํฐ ๋‹จ์œ„ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ๋ฉ๋‹ˆ๋‹ค.",
274
+ "difficulty": "easy",
275
+ "takeaway": "gr.ChatInterface๋Š” ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด ์ฑ—๋ด‡์„ ํ•จ์ˆ˜ ํ•˜๋‚˜๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
276
+ },
277
+ {
278
+ "id": "docs-chroma-1",
279
+ "title": "[Chroma 1] FAISS โ†’ Chroma ๊ต์ฒด โ€” ์žฌ์‹œ์ž‘ํ•ด๋„ ๋ฐ์ดํ„ฐ ์œ ์ง€",
280
+ "task_description": "**์ƒํ™ฉ**\n์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ–ˆ๋”๋‹ˆ VectorStore๊ฐ€ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ๋งค๋ฒˆ `ollama pull`์„ ๋‹ค์‹œ ํ•  ์ˆ˜๋Š” ์—†์–ด `save_local()`์„ ํ˜ธ์ถœํ•˜๋„๋ก ํ–ˆ๋Š”๋ฐ, ํ˜ธ์ถœํ•˜๋Š” ๊ฑธ ํ•œ ๋ฒˆ ์žŠ์–ด๋ฒ„๋ ธ๋”๋‹ˆ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ”์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nFAISS๋Š” ์ˆ˜๋™์œผ๋กœ `save_local()`์„ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์žŠ์–ด๋ฒ„๋ฆฌ๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nChroma๋Š” `persist_directory`๋งŒ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.\n\n`chunks`์™€ `embeddings`๋กœ Chroma VectorStore๋ฅผ ๋งŒ๋“ค๊ณ , `./chroma_db`์— ์ €์žฅํ•˜์„ธ์š”. ๊ฒฐ๊ณผ๋ฅผ `db`์— ์ €์žฅํ•˜์„ธ์š”.",
281
+ "expected_command": "db = Chroma.from_documents(chunks, embeddings, persist_directory='./chroma_db')",
282
+ "grading_conditions": ["Chroma.from_documents", "chunks", "embeddings", "persist_directory='./chroma_db'"],
283
+ "hint": "`Chroma.from_documents(๋ฌธ์„œ, ์ž„๋ฒ ๋”ฉ, persist_directory='๊ฒฝ๋กœ')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
284
+ "explanation": "`persist_directory`๋ฅผ ์ง€์ •ํ•˜๋ฉด `save_local()` ํ˜ธ์ถœ ์—†์ด ์ž๋™์œผ๋กœ SQLite ๊ธฐ๋ฐ˜ ๋กœ์ปฌ DB์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. `Chroma(persist_directory='./chroma_db', embedding_function=embeddings)`๋กœ ์žฌ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.",
285
+ "difficulty": "easy",
286
+ "takeaway": "Chroma๋Š” persist_directory ์ง€์ •๋งŒ์œผ๋กœ save_local() ์—†์ด ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. FAISS์™€ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์ž…๋‹ˆ๋‹ค."
287
+ },
288
+ {
289
+ "id": "docs-chroma-2",
290
+ "title": "[Chroma 2] ๊ธฐ์กด Chroma DB ์žฌ๋กœ๋“œ โ€” ์ž„๋ฒ ๋”ฉ ์žฌ๊ณ„์‚ฐ ์—†์ด",
291
+ "task_description": "**์ƒํ™ฉ**\n์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. `./chroma_db` ํด๋”์— ์ด๋ฏธ ์ธ๋ฑ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ๋‹ค์‹œ `Chroma.from_documents()`๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ž„๋ฒ ๋”ฉ์„ ๋˜ ๊ณ„์‚ฐํ•ด ๋ช‡ ๋ถ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์ด๋ฏธ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋ฉด `from_documents`๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`./chroma_db`์— ์ €์žฅ๋œ ๊ธฐ์กด Chroma DB๋ฅผ `embeddings` ๋ชจ๋ธ๋กœ ์žฌ๋กœ๋“œํ•˜์„ธ์š”. ๊ฒฐ๊ณผ๋ฅผ `db`์— ์ €์žฅํ•˜์„ธ์š”.",
292
+ "expected_command": "db = Chroma(persist_directory='./chroma_db', embedding_function=embeddings)",
293
+ "grading_conditions": ["Chroma(", "persist_directory='./chroma_db'", "embedding_function=embeddings"],
294
+ "hint": "`Chroma(persist_directory='๊ฒฝ๋กœ', embedding_function=์ž„๋ฒ ๋”ฉ๋ชจ๋ธ)` ํŒจํ„ด์ž…๋‹ˆ๋‹ค. `from_documents`์™€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.",
295
+ "explanation": "`Chroma()`๋Š” ๊ธฐ์กด DB๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค. `Chroma.from_documents()`๋Š” ์ƒˆ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‹œ์ž‘ ์‹œ์—๋Š” `Chroma()`๋ฅผ ์จ์•ผ ์ž„๋ฒ ๋”ฉ ์žฌ๊ณ„์‚ฐ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
296
+ "difficulty": "easy",
297
+ "takeaway": "Chroma()๋Š” ๊ธฐ์กด DB ์žฌ๋กœ๋“œ, Chroma.from_documents()๋Š” ์‹ ๊ทœ ์ƒ์„ฑ์ž…๋‹ˆ๋‹ค. ์žฌ์‹œ์ž‘ ์‹œ ์–ด๋А ์ชฝ์„ ์“ฐ๋Š”์ง€๊ฐ€ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค."
298
+ },
299
+ {
300
+ "id": "docs-embed-1",
301
+ "title": "[sentence-transformers] OpenAI ์—†์ด ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ",
302
+ "task_description": "**์ƒํ™ฉ**\nRAG๋ฅผ ๊ตฌ์„ฑํ–ˆ๋Š”๋ฐ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ์— OpenAI API๋ฅผ ์“ฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๊ฐ€ ๋งŽ์•„์ง€์ž ์ž„๋ฒ ๋”ฉ ๋น„์šฉ์ด ์˜ˆ์ƒ๋ณด๋‹ค ๋งŽ์ด ๋‚˜์™”์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nOpenAI ์ž„๋ฒ ๋”ฉ์€ ํ† ํฐ๋‹น ๋น„์šฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ๋ฌธ์„œ๋‚˜ ๋ฐ˜๋ณต ์‹คํ—˜์— ๋ถ€๋‹ด์ด ๋ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nHuggingFace์˜ `all-MiniLM-L6-v2` ๋ชจ๋ธ๋กœ ์™„์ „ ๋ฌด๋ฃŒ ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ์„ ์„ค์ •ํ•˜์„ธ์š”. ๊ฒฐ๊ณผ๋ฅผ `embeddings` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
303
+ "expected_command": "embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')",
304
+ "grading_conditions": ["HuggingFaceEmbeddings", "model_name='sentence-transformers/all-MiniLM-L6-v2'"],
305
+ "hint": "`HuggingFaceEmbeddings(model_name='...')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ฒซ ์‹คํ–‰ ์‹œ ๋ชจ๋ธ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.",
306
+ "explanation": "์ดํ›„ `Chroma.from_documents(chunks, embeddings)` ๋˜๋Š” `FAISS.from_documents(chunks, embeddings)`์— ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. OpenAI Embeddings์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•ด ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ํ•œ ์ค„์ž…๋‹ˆ๋‹ค.",
307
+ "difficulty": "easy",
308
+ "takeaway": "HuggingFaceEmbeddings๋Š” OpenAI Embeddings์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•ด model_name ํ•œ ์ค„๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฌด๋ฃŒ ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ์œผ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค."
309
+ },
310
+ {
311
+ "id": "docs-ragas-1",
312
+ "title": "[Ragas 1] ํ‰๊ฐ€ ๋ฐ์ดํ„ฐ์…‹ ๊ตฌ์„ฑ",
313
+ "task_description": "**์ƒํ™ฉ**\nMLflow๋กœ `chunk_size`์™€ `k`๋ฅผ ๋ฐ”๊ฟ”๊ฐ€๋ฉฐ ์‹คํ—˜ํ–ˆ๋Š”๋ฐ, `answer_relevance=0.87`์ด๋ผ๊ณ  ๊ธฐ๋กํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ์ˆซ์ž๋ฅผ ์–ด๋–ป๊ฒŒ ์ธก์ •ํ–ˆ๋Š”์ง€ ๊ธฐ์ค€์ด ์—†์–ด ๋‹ค์Œ ์‹คํ—˜๊ณผ ๋น„๊ต๊ฐ€ ์•ˆ ๋ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n\"์ข‹์€ RAG\"์˜ ๊ธฐ์ค€์ด ์—†์Šต๋‹ˆ๋‹ค. ์ˆ˜์น˜ํ™”๋œ ํ‰๊ฐ€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nRagas ํ‰๊ฐ€์šฉ Dataset์„ ๊ตฌ์„ฑํ•˜์„ธ์š”. ์•„๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ `Dataset.from_dict()`๋กœ ๋งŒ๋“ค์–ด `eval_dataset`์— ์ €์žฅํ•˜์„ธ์š”.\n\n```python\ndata = {\n 'question': ['LangChain์ด ๋ญ์•ผ?'],\n 'answer': ['LangChain์€ LLM ์•ฑ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.'],\n 'contexts': [['LangChain์€ LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']],\n 'ground_truth': ['LangChain์€ LLM ์•ฑ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']\n}\n```",
314
+ "expected_command": "eval_dataset = Dataset.from_dict(data)",
315
+ "grading_conditions": ["Dataset.from_dict", "data"],
316
+ "hint": "`from datasets import Dataset` ํ›„ `Dataset.from_dict(๋”•์…”๋„ˆ๋ฆฌ)` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
317
+ "explanation": "Ragas๋Š” HuggingFace `datasets` ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ Dataset ํ˜•์‹์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. `question`, `answer`, `contexts`, `ground_truth` 4๊ฐœ ํ‚ค๊ฐ€ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.",
318
+ "difficulty": "easy",
319
+ "takeaway": "Ragas๋Š” question, answer, contexts, ground_truth 4๊ฐœ ํ‚ค๋ฅผ ๊ฐ€์ง„ Dataset์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ๊ฐ€ RAG ํ‰๊ฐ€์˜ ํ‘œ์ค€ ํ˜•์‹์ž…๋‹ˆ๋‹ค."
320
+ },
321
+ {
322
+ "id": "docs-ragas-2",
323
+ "title": "[Ragas 2] RAG ํ’ˆ์งˆ ์ž๋™ ํ‰๊ฐ€ ์‹คํ–‰",
324
+ "task_description": "**์ƒํ™ฉ**\nํ‰๊ฐ€ ๋ฐ์ดํ„ฐ์…‹์„ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์‹ค์ œ๋กœ Faithfulness, Answer Relevancy, Context Precision์„ ์ž๋™์œผ๋กœ ์ธก์ •ํ•ด๋ด…๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`eval_dataset`์— ๋Œ€ํ•ด 3๊ฐ€์ง€ ๋ฉ”ํŠธ๋ฆญ์œผ๋กœ Ragas ํ‰๊ฐ€๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”.\n\n- ๋ฉ”ํŠธ๋ฆญ: `faithfulness`, `answer_relevancy`, `context_precision`\n- ๊ฒฐ๊ณผ๋ฅผ `result`์— ์ €์žฅํ•˜์„ธ์š”.",
325
+ "expected_command": "result = evaluate(eval_dataset, metrics=[faithfulness, answer_relevancy, context_precision])",
326
+ "grading_conditions": ["evaluate", "eval_dataset", "faithfulness", "answer_relevancy", "context_precision"],
327
+ "hint": "`evaluate(dataset, metrics=[...])` ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋ฉ”ํŠธ๋ฆญ์€ ๋ฆฌ์ŠคํŠธ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
328
+ "explanation": "`result`์—๋Š” ๊ฐ ๋ฉ”ํŠธ๋ฆญ ์ ์ˆ˜๊ฐ€ ๋‹ด๊น๋‹ˆ๋‹ค. `result.to_pandas()`๋กœ DataFrame ๋ณ€ํ™˜ ํ›„ `mlflow.log_metrics(result.to_pandas().mean().to_dict())`๋กœ MLflow์— ๊ธฐ๋กํ•˜๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ํ’ˆ์งˆ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
329
+ "difficulty": "medium",
330
+ "takeaway": "result.to_pandas()๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค mlflow.log_metrics()์— ๋„˜๊ธฐ๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ ์‹คํ—˜๊ณผ ํ’ˆ์งˆ ๋ฉ”ํŠธ๋ฆญ์„ ํ•จ๊ป˜ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
331
+ },
332
+ {
333
+ "id": "docs-graph-1",
334
+ "title": "[LangGraph 1] StateGraph ๊ธฐ๋ณธ ๊ตฌ์กฐ",
335
+ "task_description": "**์ƒํ™ฉ**\nRAG ์ฒด์ธ์„ ์“ฐ๋‹ค ๋ณด๋‹ˆ \"๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๋‹ค์‹œ ์‹œ๋„\", \"ํ’ˆ์งˆ์ด ๋‚ฎ์œผ๋ฉด ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ\" ๊ฐ™์€ ์กฐ๊ฑด ๋ถ„๊ธฐ๊ฐ€ ํ•„์š”ํ•ด์กŒ์Šต๋‹ˆ๋‹ค. LCEL ์ฒด์ธ์€ Aโ†’Bโ†’C ์„ ํ˜•์ด๋ผ ๋ถ„๊ธฐ๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๋‹จ์ˆœ ์ฒด์ธ์œผ๋กœ๋Š” ๋ฃจํ”„๋‚˜ ์กฐ๊ฑด ๋ถ„๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nLangGraph๋กœ 2๊ฐœ ๋…ธ๋“œ(`retrieve`, `generate`)๋ฅผ ๊ฐ€์ง„ StateGraph๋ฅผ ๋งŒ๋“œ์„ธ์š”.\n\n```python\nclass State(TypedDict):\n question: str\n answer: str\n```\n\n- ๋…ธ๋“œ ์ถ”๊ฐ€: `retrieve`, `generate`\n- ์ง„์ž…์ : `retrieve`\n- ์—ฃ์ง€: `retrieve โ†’ generate โ†’ END`\n- ์ปดํŒŒ์ผํ•ด์„œ `app`์— ์ €์žฅ",
336
+ "expected_command": "graph = StateGraph(State)\ngraph.add_node('retrieve', retrieve)\ngraph.add_node('generate', generate)\ngraph.set_entry_point('retrieve')\ngraph.add_edge('retrieve', 'generate')\ngraph.add_edge('generate', END)\napp = graph.compile()",
337
+ "grading_conditions": ["StateGraph", "add_node", "set_entry_point", "add_edge", "END", "graph.compile()"],
338
+ "hint": "StateGraph โ†’ add_node โ†’ set_entry_point โ†’ add_edge โ†’ compile() ์ˆœ์„œ์ž…๋‹ˆ๋‹ค.",
339
+ "explanation": "`graph.compile()`๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์•ฑ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. `app.invoke({'question': '์งˆ๋ฌธ'})`์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด retrieve ๋…ธ๋“œ โ†’ generate ๋…ธ๋“œ ์ˆœ์„œ๋กœ State๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
340
+ "difficulty": "medium",
341
+ "takeaway": "LangGraph๋Š” compile() ํ›„ invoke()๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฒด์ธ์˜ invoke()์™€ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค์ด์ง€๋งŒ, ๋‚ด๋ถ€์ ์œผ๋กœ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค."
342
+ },
343
+ {
344
+ "id": "docs-graph-2",
345
+ "title": "[LangGraph 2] conditional_edges โ€” ์กฐ๊ฑด ๋ถ„๊ธฐ",
346
+ "task_description": "**์ƒํ™ฉ**\ngenerate ๋…ธ๋“œ์—์„œ LLM์ด '๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค'๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด retrieve๋ฅผ ๋‹ค์‹œ ์‹œ๋„ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ `add_edge`๋Š” ํ•ญ์ƒ ๊ณ ์ •๋œ ๋‹ค์Œ ๋…ธ๋“œ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๋…ธ๋“œ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`judge` ๋…ธ๋“œ์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•˜๋Š” conditional_edges๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.\n\n- router ํ•จ์ˆ˜: `should_retry` (์ด๋ฏธ ์ •์˜๋จ)\n- `'retry'` โ†’ `'retrieve'` ๋…ธ๋“œ๋กœ\n- `'done'` โ†’ `END`๋กœ",
347
+ "expected_command": "graph.add_conditional_edges('judge', should_retry, {'retry': 'retrieve', 'done': END})",
348
+ "grading_conditions": ["add_conditional_edges", "'judge'", "should_retry", "'retry'", "retrieve", "END"],
349
+ "hint": "`graph.add_conditional_edges('๋…ธ๋“œ', ๋ผ์šฐํ„ฐํ•จ์ˆ˜, {'๋ฐ˜ํ™˜๊ฐ’': '๋‹ค์Œ๋…ธ๋“œ', ...})` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
350
+ "explanation": "`should_retry` ํ•จ์ˆ˜๊ฐ€ State๋ฅผ ๋ฐ›์•„ `'retry'` ๋˜๋Š” `'done'`์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด, ๊ฐ๊ฐ `retrieve` ๋…ธ๋“œ ๋˜๋Š” `END`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ReAct ์—์ด์ „ํŠธ์˜ \"ํ–‰๋™ โ†’ ๊ด€์ฐฐ โ†’ ํŒ๋‹จ\" ๋ฃจํ”„์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.",
351
+ "difficulty": "medium",
352
+ "takeaway": "add_conditional_edges๋กœ ๋ผ์šฐํ„ฐ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด LangGraph ์—์ด์ „ํŠธ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค."
353
+ },
354
+ {
355
+ "id": "docs-litellm-1",
356
+ "title": "[LiteLLM] ๋ชจ๋ธ๋ช… ํ•˜๋‚˜๋กœ Ollama โ†” OpenAI ์ „ํ™˜",
357
+ "task_description": "**์ƒํ™ฉ**\n๋กœ์ปฌ์—์„œ Ollama๋กœ ๊ฐœ๋ฐœํ–ˆ๋Š”๋ฐ, ํ”„๋กœ๋•์…˜์—์„œ๋Š” OpenAI๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค. `OllamaLLM`์„ `ChatOpenAI`๋กœ ๋ฐ”๊พธ๋ ค๋‹ˆ import, ํด๋ž˜์Šค๋ช…, API ํ˜•์‹์ด ๋ชจ๋‘ ๋‹ฌ๋ผ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ๊ณ ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nLLM ํ”„๋กœ๋ฐ”์ด๋”๋งˆ๋‹ค SDK๊ฐ€ ๋‹ฌ๋ผ ๊ต์ฒด ์‹œ ์ฝ”๋“œ ์ˆ˜์ •์ด ๋งŽ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nLiteLLM์˜ `completion()`์œผ๋กœ Ollama `llama3.2`์— `'์•ˆ๋…•'`์„ ๋ณด๋‚ด๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. ๋‚˜์ค‘์— `'gpt-4o-mini'`๋กœ ๋ฐ”๊ฟ€ ๋•Œ model ํŒŒ๋ผ๋ฏธํ„ฐ ํ•˜๋‚˜๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
358
+ "expected_command": "response = completion(model='ollama/llama3.2', messages=[{'role': 'user', 'content': '์•ˆ๋…•'}])",
359
+ "grading_conditions": ["completion", "model='ollama/llama3.2'", "messages", "role", "user"],
360
+ "hint": "`completion(model='ํ”„๋กœ๋ฐ”์ด๋”/๋ชจ๋ธ๋ช…', messages=[...])` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
361
+ "explanation": "์ดํ›„ `model='gpt-4o-mini'`๋กœ๋งŒ ๋ฐ”๊พธ๋ฉด OpenAI๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. `response.choices[0].message.content`๋กœ ์‘๋‹ต์„ ์ฝ๋Š” ๋ฐฉ์‹๋„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.",
362
+ "difficulty": "easy",
363
+ "takeaway": "model='ํ”„๋กœ๋ฐ”์ด๋”/๋ชจ๋ธ๋ช…' ํ˜•์‹์œผ๋กœ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด LLM์„ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด LiteLLM์˜ ํ•ต์‹ฌ ๊ฐ€์น˜์ž…๋‹ˆ๋‹ค."
364
+ },
365
+ {
366
+ "id": "docs-chainlit-1",
367
+ "title": "[Chainlit] ์ŠคํŠธ๋ฆฌ๋ฐ ์ฑ—๋ด‡ โ€” Streamlit๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๊ฒŒ",
368
+ "task_description": "**์ƒํ™ฉ**\nStreamlit ์ฑ—๋ด‡์— ์ŠคํŠธ๋ฆฌ๋ฐ(ํƒ€์ดํ•‘ ํšจ๊ณผ)์„ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ, `st.write_stream`, generator ํ•จ์ˆ˜, `st.empty()` ๋“ฑ ์—ฌ๋Ÿฌ ๊ฐœ๋…์ด ์–ฝํ˜€ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nStreamlit์€ rerun ๊ธฐ๋ฐ˜์ด๋ผ ์ŠคํŠธ๋ฆฌ๋ฐ UI ๊ตฌํ˜„์ด ์žฅํ™ฉํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nChainlit์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ์ฑ—๋ด‡์„ ๋งŒ๋“œ์„ธ์š”.\n\n`@cl.on_message` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ , `cl.Message(content='')`๋ฅผ ๋งŒ๋“  ๋’ค `chain.astream()`์œผ๋กœ ํ† ํฐ์„ ๋ฐ›์•„ `msg.stream_token(chunk)`์œผ๋กœ ์ „์†กํ•˜์„ธ์š”.",
369
+ "expected_command": "@cl.on_message\nasync def main(message: cl.Message):\n msg = cl.Message(content='')\n async for chunk in chain.astream({'question': message.content}):\n await msg.stream_token(chunk)\n await msg.send()",
370
+ "grading_conditions": ["@cl.on_message", "cl.Message", "chain.astream", "msg.stream_token", "msg.send()"],
371
+ "hint": "`@cl.on_message` โ†’ `cl.Message(content='')` ์ƒ์„ฑ โ†’ `astream`์œผ๋กœ ํ† ํฐ ๋ฐ›๊ธฐ โ†’ `stream_token` โ†’ `send()` ์ˆœ์„œ์ž…๋‹ˆ๋‹ค.",
372
+ "explanation": "Chainlit์—์„œ `astream()`๊ณผ `stream_token()`์„ ์กฐํ•ฉํ•˜๋ฉด ํ† ํฐ ๋‹จ์œ„ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ๋ฉ๋‹ˆ๋‹ค. Streamlit๋ณด๋‹ค ํ›จ์”ฌ ์ ์€ ์ฝ”๋“œ๋กœ ๋™์ผ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. `chainlit run app.py`๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
373
+ "difficulty": "medium",
374
+ "takeaway": "astream()์œผ๋กœ ํ† ํฐ์„ ๋ฐ›์•„ stream_token()์œผ๋กœ ์ „์†กํ•˜๋Š” ํŒจํ„ด์ด Chainlit ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ์ „๋ถ€์ž…๋‹ˆ๋‹ค."
375
+ },
376
+ {
377
+ "id": "docs-redis-1",
378
+ "title": "[Redis] ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ์˜์†ํ™” โ€” ์žฌ์‹œ์ž‘ํ•ด๋„ ๊ธฐ์–ต",
379
+ "task_description": "**์ƒํ™ฉ**\n```python\nstore = {} # ์ธ๋ฉ”๋ชจ๋ฆฌ\ndef get_session_history(session_id):\n if session_id not in store:\n store[session_id] = ChatMessageHistory()\n return store[session_id]\n```\n์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๋‹ˆ ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. `store` ๋”•์…”๋„ˆ๋ฆฌ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์ธ๋ฉ”๋ชจ๋ฆฌ ํžˆ์Šคํ† ๋ฆฌ๋Š” ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ ์†Œ๋ฉธํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`get_session_history` ํ•จ์ˆ˜๋ฅผ `RedisChatMessageHistory`๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•˜์„ธ์š”.\n- Redis URL: `'redis://localhost:6379'`",
380
+ "expected_command": "def get_session_history(session_id: str):\n return RedisChatMessageHistory(session_id=session_id, url='redis://localhost:6379')",
381
+ "grading_conditions": ["RedisChatMessageHistory", "session_id=session_id", "url='redis://localhost:6379'"],
382
+ "hint": "`RedisChatMessageHistory(session_id=session_id, url='redis://...')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
383
+ "explanation": "ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ `RedisChatMessageHistory`๋กœ ๊ต์ฒดํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. `RunnableWithMessageHistory`๋Š” ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์ด ํ•จ์ˆ˜๋งŒ ๋ฐ”๊พธ๋ฉด ์˜์†์„ฑ์„ ์–ป์Šต๋‹ˆ๋‹ค. Redis ์„œ๋ฒ„๋Š” `docker run -d -p 6379:6379 redis`๋กœ ์‹คํ–‰ํ•˜์„ธ์š”.",
384
+ "difficulty": "easy",
385
+ "takeaway": "get_session_history ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ RedisChatMessageHistory๋กœ ๊ต์ฒดํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์žฌ์‹œ์ž‘์—๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค."
386
+ },
387
+ {
388
+ "id": "docs-unstructured-1",
389
+ "title": "[Unstructured] PDFยทWord ๋“ฑ ์‹ค๋ฌด ๋ฌธ์„œ ํŒŒ์‹ฑ",
390
+ "task_description": "**์ƒํ™ฉ**\nDirectoryLoader๋กœ `./docs` ํด๋”๋ฅผ ๋กœ๋“œํ•˜๋ ค๋Š”๋ฐ, `.md` ํŒŒ์ผ์€ ์ž˜ ๋์ง€๋งŒ ํŒ€์—์„œ ๊ณต์œ ๋ฐ›์€ ๋ณด๊ณ ์„œ๋Š” `.pdf`, `.docx`, `.pptx`๊ฐ€ ์„ž์—ฌ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์žฅ์ž๋งˆ๋‹ค ๋‹ค๋ฅธ ๋กœ๋”๋ฅผ ์จ์•ผ ํ•ด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์กŒ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nํŒŒ์ผ ํ˜•์‹๋งˆ๋‹ค ๋‹ค๋ฅธ ๋กœ๋”๋ฅผ ์“ฐ๋ฉด ์ฝ”๋“œ๊ฐ€ ๋ถ„๊ธฐ๋ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`UnstructuredFileLoader`๋ฅผ `loader_cls`๋กœ ์ง€์ •ํ•ด `./docs` ํด๋”์˜ ๋ชจ๋“  ํŒŒ์ผ์„ ํ˜•์‹์— ์ƒ๊ด€์—†์ด ๋กœ๋“œํ•˜์„ธ์š”. ๊ฒฐ๊ณผ๋ฅผ `docs` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
391
+ "expected_command": "loader = DirectoryLoader('./docs', glob='**/*', loader_cls=UnstructuredFileLoader)\ndocs = loader.load()",
392
+ "grading_conditions": ["DirectoryLoader", "./docs", "loader_cls=UnstructuredFileLoader", "loader.load()"],
393
+ "hint": "`DirectoryLoader('ํด๋”', glob='**/*', loader_cls=UnstructuredFileLoader)` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
394
+ "explanation": "`UnstructuredFileLoader`๋Š” ํ™•์žฅ์ž๋ฅผ ์ž๋™ ๊ฐ์ง€ํ•ด ์ ์ ˆํ•œ ํŒŒ์„œ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. `.pdf`, `.docx`, `.pptx`, `.jpg` ๋“ฑ์„ ๋™์ผ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. PDF์—๋Š” `pip install unstructured[pdf]`๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
395
+ "difficulty": "easy",
396
+ "takeaway": "loader_cls=UnstructuredFileLoader ํ•˜๋‚˜๋กœ PDFยทWordยทPowerPoint ๋“ฑ ๋ชจ๋“  ๋ฌธ์„œ ํ˜•์‹์„ ๋™์ผํ•œ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค."
397
+ },
398
+ {
399
+ "id": "docs-news-1",
400
+ "title": "[๋‰ด์Šค ์š”์•ฝ๊ธฐ 1] feedparser๋กœ RSS ํŒŒ์‹ฑ",
401
+ "task_description": "**ํ”„๋กœ์ ํŠธ: ๋‰ด์Šค ์ž๋™ ์š”์•ฝ๊ธฐ**\n๋งค์ผ ์•„์นจ ๋‰ด์Šค ์‚ฌ์ดํŠธ 5๊ฐœ๋ฅผ ์ง์ ‘ ์—ด์–ด ์ฝ์Šต๋‹ˆ๋‹ค. ์ž๋™ํ™”ํ•˜๊ณ  ์‹ถ์€๋ฐ ์‚ฌ์ดํŠธ๋งˆ๋‹ค HTML ๊ตฌ์กฐ๊ฐ€ ๋‹ฌ๋ผ ํฌ๋กค๋ง ์ฝ”๋“œ๊ฐ€ ๊ฐ๊ฐ ๏ฟฝ๏ฟฝ๏ฟฝ์š”ํ•ฉ๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nHTML ํฌ๋กค๋ง์€ ์‚ฌ์ดํŠธ ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ฝ”๋“œ๊ฐ€ ๊นจ์ง‘๋‹ˆ๋‹ค. ๋‰ด์Šค ์‚ฌ์ดํŠธ ๋Œ€๋ถ€๋ถ„์ด RSS๋ผ๋Š” ํ‘œ์ค€ ํ˜•์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`feedparser`๋กœ BBC ๋‰ด์Šค RSS๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ์ตœ์‹  ๊ธฐ์‚ฌ 5๊ฐœ์˜ ์š”์•ฝ์„ `articles` ๋ฆฌ์ŠคํŠธ์— ์ €์žฅํ•˜์„ธ์š”.\n\nFeed URL: `https://feeds.bbci.co.uk/news/rss.xml`",
402
+ "expected_command": "import feedparser\nfeed = feedparser.parse('https://feeds.bbci.co.uk/news/rss.xml')\narticles = [entry.summary for entry in feed.entries[:5]]",
403
+ "grading_conditions": ["feedparser.parse", "feed.entries", "entry.summary", "articles"],
404
+ "hint": "`feedparser.parse(url)`๋กœ ํŒŒ์‹ฑํ•˜๋ฉด `feed.entries`์— ๊ธฐ์‚ฌ ๋ชฉ๋ก์ด ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค.",
405
+ "explanation": "RSS๋Š” XML ๊ธฐ๋ฐ˜ ํ‘œ์ค€ ํ˜•์‹์ด๋ผ ์–ด๋А ์‚ฌ์ดํŠธ๋“  `feed.entries`๋กœ ๋™์ผํ•˜๊ฒŒ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. HTML ๊ตฌ์กฐ ๋ณ€๊ฒฝ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
406
+ "difficulty": "easy",
407
+ "takeaway": "RSS๋Š” ์‚ฌ์ดํŠธ ๊ตฌ์กฐ์— ๊ด€๊ณ„์—†์ด feed.entries๋กœ ๋™์ผํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๋Š” ํ‘œ์ค€ ํ˜•์‹์ž…๋‹ˆ๋‹ค."
408
+ },
409
+ {
410
+ "id": "docs-news-2",
411
+ "title": "[๋‰ด์Šค ์š”์•ฝ๊ธฐ 2] map_reduce โ€” ํ† ํฐ ์ดˆ๊ณผ ์—†์ด ์š”์•ฝ",
412
+ "task_description": "**์ƒํ™ฉ**\n๊ธฐ์‚ฌ 10๊ฐœ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ์š”์•ฝํ•˜๋ ค๊ณ  `stuff` ๋ฐฉ์‹์„ ์ผ๋”๋‹ˆ:\n```\nError: This model's maximum context length is 128000 tokens.\nYour message resulted in 180000 tokens.\n```\n\n**๋ฌธ์ œ**\n`stuff`๋Š” ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ํ•˜๋‚˜์˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์–ด์„œ ๋ฌธ์„œ๊ฐ€ ๋งŽ์œผ๋ฉด ํ† ํฐ์„ ์ดˆ๊ณผํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`map_reduce`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๊ฐ ๊ธฐ์‚ฌ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝํ•œ ๋’ค ํ•ฉ์นฉ๋‹ˆ๋‹ค.\n\n`articles` โ†’ `Document` ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜ ํ›„ `map_reduce` ์ฒด์ธ์œผ๋กœ ์š”์•ฝํ•˜๋Š” ์ฝ”๋“œ์˜ `???` ๋ถ€๋ถ„์„ ์ฑ„์šฐ์„ธ์š”.\n\n```python\ndocs = [Document(page_content=a) for a in articles]\nchain = ???\nresult = chain.invoke(docs)\n```",
413
+ "expected_command": "chain = load_summarize_chain(llm, chain_type='map_reduce')",
414
+ "grading_conditions": ["load_summarize_chain", "llm", "chain_type='map_reduce'"],
415
+ "hint": "`load_summarize_chain(llm, chain_type='...')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
416
+ "explanation": "`map_reduce`๋Š” ๊ฐ ๊ธฐ์‚ฌ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝ(map)ํ•˜๊ณ , ๊ฐœ๋ณ„ ์š”์•ฝ๋“ค์„ ๋‹ค์‹œ ํ•ฉ์ณ ์ตœ์ข… ์š”์•ฝ(reduce)์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์•„๋ฌด๋ฆฌ ๊ธฐ์‚ฌ๊ฐ€ ๋งŽ์•„๋„ ํ† ํฐ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",
417
+ "difficulty": "medium",
418
+ "takeaway": "map_reduce๋Š” ๊ฐ ๋ฌธ์„œ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝ(map)ํ•œ ๋’ค ํ•ฉ์น˜๋Š”(reduce) ๋ฐฉ์‹์œผ๋กœ ํ† ํฐ ์ œํ•œ ์—†์ด ์—ฌ๋Ÿฌ ๋ฌธ์„œ๋ฅผ ์š”์•ฝํ•ฉ๋‹ˆ๋‹ค."
419
+ },
420
+ {
421
+ "id": "docs-news-3",
422
+ "title": "[๋‰ด์Šค ์š”์•ฝ๊ธฐ 3] Airflow โ€” ์ž๋Š” ๋™์•ˆ๋„ ์ž๋™ ์‹คํ–‰",
423
+ "task_description": "**์ƒํ™ฉ**\nfeedparser + LangChain ์š”์•ฝ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ž˜ ๋™์ž‘ํ•˜๋Š”๋ฐ, ๋งค์ผ ์•„์นจ ์ง์ ‘ ํ„ฐ๋ฏธ๋„์— `python news_summary.py`๋ฅผ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋ง์— ๊นœ๋นกํ•˜๋ฉด ๊ทธ๋‚  ์š”์•ฝ์ด ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์ˆ˜๋™ ์‹คํ–‰์€ ๋ฐ˜๋“œ์‹œ ๋น ๋œจ๋ฆฌ๋Š” ๋‚ ์ด ์ƒ๊น๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`run_news_summary` ํ•จ์ˆ˜๋ฅผ ๋งค์ผ ์ž๋™ ์‹คํ–‰ํ•˜๋Š” Airflow DAG๋ฅผ ์ •์˜ํ•˜์„ธ์š”.\n\n- DAG ID: `daily_news_summary`, ์Šค์ผ€์ค„: `@daily`\n- task_id: `'summarize'`, python_callable: `run_news_summary`",
424
+ "expected_command": "with DAG('daily_news_summary', schedule='@daily', start_date=datetime(2024, 1, 1)) as dag:\n task = PythonOperator(task_id='summarize', python_callable=run_news_summary)",
425
+ "grading_conditions": ["daily_news_summary", "@daily", "PythonOperator", "task_id='summarize'", "python_callable=run_news_summary"],
426
+ "hint": "`with DAG(...) as dag:` ๋ธ”๋ก ์•ˆ์— PythonOperator๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
427
+ "explanation": "์ด DAG๋Š” ๋งค์ผ ์ž์ •์— `run_news_summary`๋ฅผ ์ž๋™ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. Airflow ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ๋‹ด๋‹น์ž๊ฐ€ ์—†์–ด๋„, ์ž๋Š” ์ค‘์—๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
428
+ "difficulty": "medium",
429
+ "takeaway": "RAG ์ฑ—๋ด‡์—์„œ ๋ฐฐ์šด Airflow DAG + PythonOperator ํŒจํ„ด์ด ๋‰ด์Šค ์š”์•ฝ๊ธฐ ์ž๋™ํ™”์— ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค."
430
+ },
431
+ {
432
+ "id": "docs-news-4",
433
+ "title": "[๋‰ด์Šค ์š”์•ฝ๊ธฐ 4] st.expander โ€” ๊ธด ๋ชฉ๋ก์„ ๊น”๋”ํ•˜๊ฒŒ",
434
+ "task_description": "**์ƒํ™ฉ**\n์š”์•ฝ๋œ ๊ธฐ์‚ฌ 10๊ฐœ๋ฅผ `st.write()`๋กœ ๋‚˜์—ดํ–ˆ๋”๋‹ˆ ํŽ˜์ด์ง€ ์Šคํฌ๋กค์ด ๋์ด ์—†๊ณ , ์›ํ•˜๋Š” ๊ธฐ์‚ฌ๋ฅผ ์ฐพ๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n๋ชจ๋“  ๋‚ด์šฉ์„ ํŽผ์ณ๋†“์œผ๋ฉด ํ•œ๋ˆˆ์— ๋ณด๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`summaries` ๋ฆฌ์ŠคํŠธ(`{'title': ..., 'content': ...}`)๋ฅผ `st.expander`๋กœ ๋ Œ๋”๋งํ•˜์„ธ์š”. ์ œ๋ชฉ๋งŒ ๋ณด์ด๊ณ  ํด๋ฆญ ์‹œ ์š”์•ฝ์ด ํŽผ์ณ์ง€๋„๋ก.",
435
+ "expected_command": "for s in summaries:\n with st.expander(s['title']):\n st.write(s['content'])",
436
+ "grading_conditions": ["st.expander", "s['title']", "st.write", "s['content']"],
437
+ "hint": "`with st.expander(์ œ๋ชฉ):` ๋ธ”๋ก ์•ˆ์— ๋‚ด์šฉ์„ ๋„ฃ์œผ๋ฉด ํด๋ฆญ ์‹œ ํŽผ์ณ์ง‘๋‹ˆ๋‹ค.",
438
+ "explanation": "`st.expander`๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ ‘ํ˜€ ์žˆ๊ณ  ํด๋ฆญํ•˜๋ฉด ๋‚ด์šฉ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. ํ•ญ๋ชฉ์ด ๋งŽ์„ ๋•Œ ํ™”๋ฉด์„ ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์ƒ์„ธ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.",
439
+ "difficulty": "easy",
440
+ "takeaway": "st.expander๋Š” ํ•ญ๋ชฉ์ด ๋งŽ์„ ๋•Œ ์ œ๋ชฉ๋งŒ ๋ณด์—ฌ์ฃผ๊ณ  ํด๋ฆญ ์‹œ ๋‚ด์šฉ์„ ํŽผ์น˜๋Š” Streamlit์˜ ํ•ต์‹ฌ UI ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค."
441
+ },
442
+ {
443
+ "id": "docs-sql-1",
444
+ "title": "[Text-to-SQL 1] SQLDatabase โ€” DB ์Šคํ‚ค๋งˆ๋ฅผ LLM์— ์•Œ๋ ค์ฃผ๊ธฐ",
445
+ "task_description": "**ํ”„๋กœ์ ํŠธ: Text-to-SQL ์ƒ์„ฑ๊ธฐ**\nํŒ€์›๋“ค์ด \"์ด ๋‹ฌ ๋งค์ถœ ์ƒ์œ„ 5๊ฐœ ์ƒํ’ˆ์ด ๋ญ์—์š”?\"๋ฅผ ๋ฌผ์–ด๋ณผ ๋•Œ๋งˆ๋‹ค SQL์„ ๋Œ€์‹  ์จ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋ฃจ์—๋„ ์ˆ˜์‹ญ ๋ฒˆ์ž…๋‹ˆ๋‹ค.\n\n---\n\n**๋‹จ๊ณ„ 1**: LLM์—๊ฒŒ DB ๊ตฌ์กฐ๋ฅผ ์•Œ๋ ค์ค˜์•ผ ์˜ฌ๋ฐ”๋ฅธ SQL์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.\n`SQLDatabase`๋กœ `./shop.db`์— ์—ฐ๊ฒฐํ•˜์„ธ์š”. (์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ฝ์–ด LLM ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค)\n\n๊ฒฐ๊ณผ๋ฅผ `db` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
446
+ "expected_command": "db = SQLDatabase.from_uri('sqlite:///./shop.db')",
447
+ "grading_conditions": ["SQLDatabase.from_uri", "sqlite", "shop.db"],
448
+ "hint": "`SQLDatabase.from_uri('sqlite:///๊ฒฝ๋กœ')` ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์Šฌ๋ž˜์‹œ๊ฐ€ 3๊ฐœ์ž…๋‹ˆ๋‹ค.",
449
+ "explanation": "`SQLDatabase.from_uri()`๋Š” ์—ฐ๊ฒฐ๊ณผ ๋™์‹œ์— ํ…Œ์ด๋ธ”๋ช…ยท์ปฌ๋Ÿผ ์Šคํ‚ค๋งˆ๋ฅผ ์ฝ์–ด LLM ํ”„๋กฌํ”„ํŠธ์— ์ž๋™ ์ฃผ์ž…ํ•  ์ค€๋น„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. LLM์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ปฌ๋Ÿผ์„ ์“ฐ๋Š” ์‹ค์ˆ˜๋ฅผ ์ค„์—ฌ์ค๋‹ˆ๋‹ค.",
450
+ "difficulty": "easy",
451
+ "takeaway": "SQLDatabase.from_uri()๊ฐ€ DB ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ฝ์–ด LLM์ด ์กด์žฌํ•˜๋Š” ํ…Œ์ด๋ธ”ยท์ปฌ๋Ÿผ๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค."
452
+ },
453
+ {
454
+ "id": "docs-sql-2",
455
+ "title": "[Text-to-SQL 2] create_sql_query_chain โ€” ์ž์—ฐ์–ด โ†’ SQL",
456
+ "task_description": "**์ƒํ™ฉ**\nLLM์—๊ฒŒ ์ง์ ‘ \"SQL ์จ์ค˜\"๋ผ๊ณ  ํ–ˆ๋”๋‹ˆ:\n```sql\nSELECT * FROM sales ORDER BY revenue DESC LIMIT 5\n```\n`sales` ํ…Œ์ด๋ธ”์ด ์—†๊ณ  `orders`๊ฐ€ ๋งž๋Š”๋ฐ, LLM์ด DB ์Šคํ‚ค๋งˆ๋ฅผ ๋ชฐ๋ผ์„œ ํ‹€๋ฆฐ ํ…Œ์ด๋ธ”๋ช…์„ ์ผ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nLLM์ด ์‹ค์ œ DB ๊ตฌ์กฐ๋ฅผ ๋ชจ๋ฅด๋ฉด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ์„ ์”๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`create_sql_query_chain`์€ `db` ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.\n`'๊ฐ€์žฅ ๋งŽ์ด ํŒ”๋ฆฐ ์ƒํ’ˆ 5๊ฐœ๋Š”?'`์œผ๋กœ ์‹คํ–‰ํ•ด `sql` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
457
+ "expected_command": "chain = create_sql_query_chain(llm, db)\nsql = chain.invoke({'question': '๊ฐ€์žฅ ๋งŽ์ด ํŒ”๋ฆฐ ์ƒํ’ˆ 5๊ฐœ๋Š”?'})",
458
+ "grading_conditions": ["create_sql_query_chain", "llm", "db", "chain.invoke", "question"],
459
+ "hint": "`create_sql_query_chain(llm, db)`๋กœ ์ฒด์ธ ์ƒ์„ฑ ํ›„, `{'question': '...'}` ๋”•์…”๋„ˆ๋ฆฌ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
460
+ "explanation": "chain ๋‚ด๋ถ€์ ์œผ๋กœ DB์˜ ํ…Œ์ด๋ธ”ยท์ปฌ๋Ÿผ ์ •๋ณด๋ฅผ ํ”„๋กฌํ”„ํŠธ์— ํฌํ•จํ•ด LLM์ด ์˜ฌ๋ฐ”๋ฅธ ์Šคํ‚ค๋งˆ๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.",
461
+ "difficulty": "medium",
462
+ "takeaway": "create_sql_query_chain์€ SQLDatabase์—์„œ ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™ ์ฃผ์ž…ํ•ด LLM์ด ์˜ฌ๋ฐ”๋ฅธ SQL์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค."
463
+ },
464
+ {
465
+ "id": "docs-sql-3",
466
+ "title": "[Text-to-SQL 3] ValidSQL Guard โ€” ์ž˜๋ชป๋œ SQL ์‹คํ–‰ ์ „ ์ฐจ๋‹จ",
467
+ "task_description": "**์ƒํ™ฉ**\nLLM์ด ์ƒ์„ฑํ•œ SQL์„ ๋ฐ”๋กœ `db.run()`์— ๋„˜๊ฒผ๋”๋‹ˆ:\n```\nOperationalError: no such column: product_name\n```\n๋ฌธ๋ฒ• ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” SQL์ด ์‹ค์ œ DB์— ์‹คํ–‰๋์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nLLM์€ ๊ฐ€๋” ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” SQL์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ ์ „์— ๊ฑธ๋Ÿฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nGuardrails์˜ `ValidSQL`๋กœ SQL ์œ ํšจ์„ฑ ๊ฒ€์ฆ Guard๋ฅผ ๋งŒ๋“œ์„ธ์š”.\n- ์‹คํŒจ ์‹œ: `'reask'` (์˜ฌ๋ฐ”๋ฅธ SQL์„ ๋‹ค์‹œ ์š”์ฒญ)",
468
+ "expected_command": "guard = Guard().use(ValidSQL(on_fail='reask'))",
469
+ "grading_conditions": ["Guard()", "ValidSQL", "on_fail='reask'"],
470
+ "hint": "`Guard().use(ValidSQL(on_fail='...'))` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
471
+ "explanation": "`on_fail='reask'`๋Š” SQL ๊ฒ€์ฆ ์‹คํŒจ ์‹œ LLM์—๊ฒŒ ์ž๋™์œผ๋กœ ์žฌ์ž‘์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. `exception`๊ณผ ๋‹ฌ๋ฆฌ ์‚ฌ์šฉ์ž๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ณด์ง€ ๋ชปํ•˜๊ณ  ์ž๋™์œผ๋กœ ์ˆ˜์ •๋ฉ๋‹ˆ๋‹ค.",
472
+ "difficulty": "medium",
473
+ "takeaway": "RAG ์ฑ—๋ด‡์—์„œ ๋ฐฐ์šด Guard().use() ํŒจํ„ด์ด SQL ๊ฒ€์ฆ์—๋„ ๊ทธ๋Œ€๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. on_fail='reask'๋กœ ์‚ฌ์šฉ์ž ๋…ธ์ถœ ์—†์ด ์ž๋™ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค."
474
+ },
475
+ {
476
+ "id": "docs-sql-4",
477
+ "title": "[Text-to-SQL 4] FastAPI โ€” ํŒ€ ์ „์ฒด๊ฐ€ ์ž์—ฐ์–ด๋กœ DB ์กฐํšŒ",
478
+ "task_description": "**์ƒํ™ฉ**\n์Šคํฌ๋ฆฝํŠธ๋กœ ๋Œ๋ฆฌ๋ฉด ๋‚˜๋งŒ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ€์›๋“ค๋„ ์ž์—ฐ์–ด๋กœ DB๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ API๋กœ ๋…ธ์ถœํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์Šคํฌ๋ฆฝํŠธ๋Š” ๊ณต์œ ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. API ์—”๋“œํฌ์ธํŠธ๋กœ ๋…ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n์•„๋ž˜ `???`๋ฅผ ์ฑ„์›Œ POST `/sql` ์—”๋“œํฌ์ธํŠธ๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.\n\n```python\n@app.post('/sql')\nasync def generate_sql(q: Query):\n sql = ???\n result = db.run(sql)\n return {'sql': sql, 'result': result}\n```",
479
+ "expected_command": "sql = chain.invoke({'question': q.question})",
480
+ "grading_conditions": ["chain.invoke", "question", "q.question"],
481
+ "hint": "Pydantic ๋ชจ๋ธ `q`์˜ `.question` ์†์„ฑ์œผ๋กœ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.",
482
+ "explanation": "`q.question`์œผ๋กœ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ๋ฐ›์•„ ์ฒด์ธ์— ๋„˜๊น๋‹ˆ๋‹ค. `db.run(sql)`๋กœ SQL ์‹คํ–‰ ๊ฒฐ๊ณผ๋„ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•ด ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐ”๋กœ ํ‘œ ํ˜•ํƒœ๋กœ ๏ฟฝ๏ฟฝ๏ฟฝ์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
483
+ "difficulty": "easy",
484
+ "takeaway": "RAG ์ฑ—๋ด‡ FastAPI ํŒจํ„ด๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. q.question์œผ๋กœ ์งˆ๋ฌธ์„ ๋ฐ›์•„ chain.invoke()์— ๋„˜๊ธฐ๋Š” ๊ตฌ์กฐ๊ฐ€ ์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค."
485
+ },
486
+ {
487
+ "id": "docs-brain-1",
488
+ "title": "[Second Brain 1] DirectoryLoader โ€” ๋…ธํŠธ 300๊ฐœ๋ฅผ ํ•œ ๋ฒˆ์—",
489
+ "task_description": "**ํ”„๋กœ์ ํŠธ: ๊ฐœ์ธ Second Brain**\n๋‚ด Obsidian ๋…ธํŠธ๊ฐ€ 300๊ฐœ๊ฐ€ ๋„˜์Šต๋‹ˆ๋‹ค. \"3๊ฐœ์›” ์ „์— ๊ณต๋ถ€ํ–ˆ๋˜ ๊ทธ ๋‚ด์šฉ์ด ์–ด๋А ํŒŒ์ผ์ด์—ˆ๋”๋ผ?\"๋ฅผ ์ฐพ์œผ๋ ค๋ฉด ํŒŒ์ผ์„ ํ•˜๋‚˜์”ฉ ์—ด์–ด๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n---\n\n**๋‹จ๊ณ„ 1**: 300๊ฐœ ํŒŒ์ผ์„ ํ•˜๋‚˜์”ฉ ๋ณต์‚ฌํ•ด RAG์— ๋„ฃ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n`DirectoryLoader`๋กœ `./notes` ํด๋” ์•ˆ์˜ ๋ชจ๋“  `.md` ํŒŒ์ผ(ํ•˜์œ„ ํด๋” ํฌํ•จ)์„ ํ•œ ๋ฒˆ์— ๋กœ๋“œํ•˜์„ธ์š”.\n\n๊ฒฐ๊ณผ๋ฅผ `docs` ๋ณ€์ˆ˜์— ์ €์žฅํ•˜์„ธ์š”.",
490
+ "expected_command": "loader = DirectoryLoader('./notes', glob='**/*.md')\ndocs = loader.load()",
491
+ "grading_conditions": ["DirectoryLoader", "./notes", "glob='**/*.md'", "loader.load()"],
492
+ "hint": "`DirectoryLoader('ํด๋”', glob='ํŒจํ„ด')`์œผ๋กœ ๋กœ๋”๋ฅผ ๋งŒ๋“ค๊ณ  `.load()`๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
493
+ "explanation": "`**/*.md`๋Š” `./notes`์™€ ๋ชจ๋“  ํ•˜์œ„ ํด๋”์—์„œ `.md` ํŒŒ์ผ์„ ์žฌ๊ท€ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. 300๊ฐœ ํŒŒ์ผ์ด ํ•œ ๋ฒˆ์˜ `loader.load()` ํ˜ธ์ถœ๋กœ ๋ชจ๋‘ `Document` ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.",
494
+ "difficulty": "easy",
495
+ "takeaway": "RAG ์ฑ—๋ด‡์—์„œ ๋ฐฐ์šด DirectoryLoader + glob ํŒจํ„ด์ด Second Brain ๋…ธํŠธ ๋กœ๋“œ์— ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค."
496
+ },
497
+ {
498
+ "id": "docs-brain-2",
499
+ "title": "[Second Brain 2] metadata['source'] โ€” ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋А ํŒŒ์ผ์ธ์ง€",
500
+ "task_description": "**์ƒํ™ฉ**\nRAG ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”๋Š”๋ฐ \"์ด๊ฒŒ ์–ด๋А ๋…ธํŠธ์—์„œ ์™”์ง€?\" ์›๋ณธ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด์šฉ๋งŒ ๋ณด์ด๊ณ  ์ถœ์ฒ˜๊ฐ€ ์—†์œผ๋‹ˆ ์›๋ณธ์„ ์—ด์–ด ๋งฅ๋ฝ์„ ๋” ๋ณผ ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nDocument ๊ฐ์ฒด์— ์ถœ์ฒ˜ ์ •๋ณด๊ฐ€ ์žˆ๋Š”๋ฐ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\n`docs` ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ ๋ฌธ์„œ์˜ ํŒŒ์ผ ๊ฒฝ๋กœ(์ถœ์ฒ˜)์™€ ๋‚ด์šฉ ๊ธธ์ด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.",
501
+ "expected_command": "for doc in docs:\n print(doc.metadata['source'], len(doc.page_content))",
502
+ "grading_conditions": ["doc.metadata['source']", "len(doc.page_content)"],
503
+ "hint": "ํŒŒ์ผ ๊ฒฝ๋กœ๋Š” `doc.metadata['source']`, ๋‚ด์šฉ์€ `doc.page_content`์— ์žˆ์Šต๋‹ˆ๋‹ค.",
504
+ "explanation": "`doc.metadata['source']`์— ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค„ ๋•Œ ์ด ๊ฐ’์„ ํ•จ๊ป˜ ํ‘œ์‹œํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์›๋ณธ ํŒŒ์ผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
505
+ "difficulty": "easy",
506
+ "takeaway": "doc.metadata['source']์— ์›๋ณธ ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. RAG ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์— ์ถœ์ฒ˜๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ํ•ญ์ƒ ์ด ๊ฐ’์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค."
507
+ },
508
+ {
509
+ "id": "docs-brain-3",
510
+ "title": "[Second Brain 3] FileSensor โ€” ์ƒˆ ๋…ธํŠธ ์ถ”๊ฐ€ ์‹œ ์ž๋™ ์žฌ์ธ๋ฑ์‹ฑ",
511
+ "task_description": "**์ƒํ™ฉ**\n์ƒˆ ๋…ธํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋Š”๋ฐ ๊ฒ€์ƒ‰์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. `python reindex.py`๋ฅผ ๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋…ธํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์žฌ์ธ๋ฑ์‹ฑ์„ ์žŠ์–ด๋ฒ„๋ฆฌ๋Š” ๋‚ ์ด ์ƒ๊น๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\n์ƒˆ ํŒŒ์ผ ์ถ”๊ฐ€๋ฅผ ๊ฐ์ง€ํ•ด ์ž๋™์œผ๋กœ ์žฌ์ธ๋ฑ์‹ฑ์„ ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nAirflow `FileSensor`๋กœ `./notes` ํด๋”์— ์ƒˆ `.md` ํŒŒ์ผ์ด ์ƒ๊ธฐ๋ฉด ๊ฐ์ง€ํ•˜๋Š” ํƒœ์Šคํฌ๋ฅผ ๋งŒ๋“œ์„ธ์š”.\n- task_id: `'wait_for_new_note'`\n- filepath: `'./notes/*.md'`\n- poke_interval: `300` (5๋ถ„๋งˆ๋‹ค ํ™•์ธ)",
512
+ "expected_command": "wait_for_new_note = FileSensor(\n task_id='wait_for_new_note',\n filepath='./notes/*.md',\n poke_interval=300\n)",
513
+ "grading_conditions": ["FileSensor", "task_id='wait_for_new_note'", "filepath", "poke_interval=300"],
514
+ "hint": "`FileSensor(task_id='...', filepath='...', poke_interval=์ดˆ)` ํŒจํ„ด์ž…๋‹ˆ๋‹ค.",
515
+ "explanation": "`FileSensor`๋Š” ์ง€์ •ํ•œ ํŒŒ์ผ์ด ์กด์žฌํ•  ๋•Œ๊นŒ์ง€ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์ด ๊ฐ์ง€๋˜๋ฉด ๋‹ค์Œ ํƒœ์Šคํฌ(์žฌ์ธ๋ฑ์‹ฑ)๊ฐ€ ์ž๋™ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. `poke_interval=300`์€ 5๋ถ„๋งˆ๋‹ค ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
516
+ "difficulty": "medium",
517
+ "takeaway": "FileSensor๋Š” ํŒŒ์ผ ์ถ”๊ฐ€๋ฅผ ๊ฐ์ง€ํ•ด ํ›„์† Airflow ํƒœ์Šคํฌ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. @daily์™€ ๋‹ฌ๋ฆฌ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค."
518
+ },
519
+ {
520
+ "id": "docs-brain-4",
521
+ "title": "[Second Brain 4] Streamlit โ€” ๋…ธํŠธ ๊ฒ€์ƒ‰ UI",
522
+ "task_description": "**์ƒํ™ฉ**\n์ง€๊ธˆ์€ ํ„ฐ๋ฏธ๋„์—์„œ `retriever.invoke('๊ฒ€์ƒ‰์–ด')`๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํŒ€์›๋“ค์ด ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.\n\n**๋ฌธ์ œ**\nCLI๋Š” ๊ฐœ๋ฐœ์ž๋งŒ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.\n\n**ํ•ด๊ฒฐ**\nStreamlit์œผ๋กœ ๊ฒ€์ƒ‰ UI๋ฅผ ๋งŒ๋“œ์„ธ์š”.\n- ํด๋” ๊ฒฝ๋กœ ํ…์ŠคํŠธ ์ž…๋ ฅ (`folder`, ๊ธฐ๋ณธ๊ฐ’ `'./notes'`)\n- ๊ฒ€์ƒ‰์–ด ํ…์ŠคํŠธ ์ž…๋ ฅ (`query`)\n- '๊ฒ€์ƒ‰' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ `retriever.invoke(query)` ์‹คํ–‰ โ†’ `results`์— ์ €์žฅ",
523
+ "expected_command": "folder = st.text_input('๋…ธํŠธ ํด๋” ๊ฒฝ๋กœ', value='./notes')\nquery = st.text_input('๊ฒ€์ƒ‰ํ•  ๋‚ด์šฉ')\nif st.button('๊ฒ€์ƒ‰'):\n results = retriever.invoke(query)",
524
+ "grading_conditions": ["st.text_input", "st.button('๊ฒ€์ƒ‰')", "retriever.invoke(query)"],
525
+ "hint": "`st.text_input`, `st.button`, `retriever.invoke` ์„ธ ๊ฐ€์ง€๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์”๋‹ˆ๋‹ค.",
526
+ "explanation": "`if st.button('๊ฒ€์ƒ‰'):` ๋ธ”๋ก ์•ˆ์— ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ๋„ฃ์œผ๋ฉด ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํด๋” ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด `st.cache_resource`๋ฅผ clearํ•ด retriever๋ฅผ ์žฌ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์™„์„ฑ์ž…๋‹ˆ๋‹ค.",
527
+ "difficulty": "easy",
528
+ "takeaway": "st.text_input + st.button + retriever.invoke ์กฐํ•ฉ์€ RAG ์ฑ—๋ด‡์—์„œ ๋ฐฐ์šด Streamlit ํŒจํ„ด์ด Second Brain UI์— ๊ทธ๋Œ€๋กœ ์ ์šฉ๋œ ์˜ˆ์ž…๋‹ˆ๋‹ค."
529
+ }
530
+ ]
cert_study_app/content/tool_docs/quizzes.json ADDED
@@ -0,0 +1,1032 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "docs-q8",
4
+ "lesson_id": "docs-ollama-models",
5
+ "question_type": "multiple_choice",
6
+ "question": "Ollama์—์„œ ๋ชจ๋ธ์ด ๋กœ์ปฌ์— ์—†์„ ๋•Œ `ollama run qwen2.5:7b`๋ฅผ ์‹คํ–‰ํ•˜๋ฉด?",
7
+ "options": [
8
+ "์ž๋™์œผ๋กœ ๋ชจ๋ธ์„ ๋‹ค์šด๋กœ๋“œํ•œ ๋’ค ์‹คํ–‰ํ•œ๋‹ค",
9
+ "์˜ค๋ฅ˜๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  ์ข…๋ฃŒํ•œ๋‹ค",
10
+ "์ตœ์‹  ๋ชจ๋ธ๋กœ ๋Œ€์ฒดํ•ด ์‹คํ–‰ํ•œ๋‹ค",
11
+ "์„œ๋ฒ„๋งŒ ์‹œ์ž‘ํ•˜๊ณ  ๋Œ€๊ธฐํ•œ๋‹ค"
12
+ ],
13
+ "answer": "์ž๋™์œผ๋กœ ๋ชจ๋ธ์„ ๋‹ค์šด๋กœ๋“œํ•œ ๋’ค ์‹คํ–‰ํ•œ๋‹ค",
14
+ "explanation": "`ollama run`์€ ๋ชจ๋ธ์ด ์—†์œผ๋ฉด ์ž๋™์œผ๋กœ pullํ•œ ๋’ค ๋Œ€ํ™”ํ˜• ์„ธ์…˜์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. `ollama pull`์„ ๋จผ์ € ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.",
15
+ "source_id": "official-docs-ollama"
16
+ },
17
+ {
18
+ "id": "docs-q9",
19
+ "lesson_id": "docs-streamlit-secrets",
20
+ "question_type": "multiple_choice",
21
+ "question": "Streamlit ๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ API ํ‚ค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€?",
22
+ "options": [
23
+ ".streamlit/secrets.toml์— ์ €์žฅํ•˜๊ณ  .gitignore์— ์ถ”๊ฐ€",
24
+ "st.session_state์— ์ €์žฅ",
25
+ "์ฝ”๋“œ ์ƒ๋‹จ์— ์ƒ์ˆ˜๋กœ ์„ ์–ธ",
26
+ "requirements.txt์— ํฌํ•จ"
27
+ ],
28
+ "answer": ".streamlit/secrets.toml์— ์ €์žฅํ•˜๊ณ  .gitignore์— ์ถ”๊ฐ€",
29
+ "explanation": "๋ฏผ๊ฐ ์ •๋ณด๋Š” secrets.toml์— ๋„ฃ๊ณ , ์ด ํŒŒ์ผ์€ ๋ฐ˜๋“œ์‹œ .gitignore์— ์ถ”๊ฐ€ํ•ด Git์— ์ปค๋ฐ‹๋˜์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
30
+ "source_id": "official-docs-streamlit"
31
+ },
32
+ {
33
+ "id": "docs-q10",
34
+ "lesson_id": "docs-hf-docker-space",
35
+ "question_type": "multiple_choice",
36
+ "question": "Hugging Face Docker Space์—์„œ ์•ฑ์ด ๋…ธ์ถœํ•ด์•ผ ํ•˜๋Š” ๊ธฐ๋ณธ ํฌํŠธ๋Š”?",
37
+ "options": [
38
+ "7860",
39
+ "8501",
40
+ "11434",
41
+ "3000"
42
+ ],
43
+ "answer": "7860",
44
+ "explanation": "Docker Space์˜ ๊ธฐ๋ณธ ์•ฑ ํฌํŠธ๋Š” 7860์ž…๋‹ˆ๋‹ค. README ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ `app_port`์™€ Dockerfile์˜ EXPOSE ํฌํŠธ๋ฅผ 7860์œผ๋กœ ์ผ์น˜์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
45
+ "source_id": "official-docs-huggingface"
46
+ },
47
+ {
48
+ "id": "docs-q11",
49
+ "lesson_id": "docs-ollama-models",
50
+ "question_type": "command",
51
+ "question": "๋กœ์ปฌ์— ์žˆ๋Š” Ollama ๋ชจ๋ธ ๋ชฉ๋ก์„ ํ™•์ธํ•˜๋Š” CLI ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
52
+ "options": [],
53
+ "answer": "ollama list",
54
+ "explanation": "`ollama list`๋Š” ๋กœ์ปฌ์— ๋‹ค์šด๋กœ๋“œ๋œ ๋ชจ๋ธ ๋ชฉ๋ก๊ณผ ํฌ๊ธฐ, ์ˆ˜์ •์ผ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. `ollama pull <๋ชจ๋ธ>`๋กœ ์ƒˆ ๋ชจ๋ธ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
55
+ "source_id": "official-docs-ollama"
56
+ },
57
+ {
58
+ "id": "docs-q12",
59
+ "lesson_id": "docs-langchain-rag",
60
+ "question_type": "multiple_choice",
61
+ "question": "LangChain RAG ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๋ฌธ์„œ๋ฅผ Vector Store์— ๋„ฃ๊ธฐ ์ „์— ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์€?",
62
+ "options": [
63
+ "๋ฌธ์„œ๋ฅผ ์ž‘์€ ์ฒญํฌ(chunk)๋กœ ๋ถ„ํ•  ํ›„ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ",
64
+ "๋ฌธ์„œ๋ฅผ PDF๋กœ ๋ณ€ํ™˜",
65
+ "LLM์— ๋ฌธ์„œ๋ฅผ ์ง์ ‘ ์ „๋‹ฌ",
66
+ "API ํ‚ค๋ฅผ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ €์žฅ"
67
+ ],
68
+ "answer": "๋ฌธ์„œ๋ฅผ ์ž‘์€ ์ฒญํฌ(chunk)๋กœ ๋ถ„ํ•  ํ›„ ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ",
69
+ "explanation": "RAG๋Š” ๋ฌธ์„œ โ†’ ์ฒญํฌ ๋ถ„ํ• (TextSplitter) โ†’ ์ž„๋ฒ ๋”ฉ ๋ณ€ํ™˜ โ†’ Vector Store ์ €์žฅ ์ˆœ์œผ๋กœ ์ธ๋ฑ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ์ฒญํฌ๊ฐ€ ๋„ˆ๋ฌด ํฌ๋ฉด ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.",
70
+ "source_id": "official-docs-langchain"
71
+ },
72
+ {
73
+ "id": "docs-q13",
74
+ "lesson_id": "docs-streamlit-state",
75
+ "question_type": "multiple_choice",
76
+ "question": "Streamlit์—์„œ ๋ฒ„ํŠผ ํด๋ฆญ ํšŸ์ˆ˜๋ฅผ ํŽ˜์ด์ง€ ์ „ํ™˜ ํ›„์—๋„ ์œ ์ง€ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋Š”๊ฐ€?",
77
+ "options": [
78
+ "st.session_state์— ์ €์žฅ",
79
+ "์ „์—ญ ๋ณ€์ˆ˜๋กœ ์„ ์–ธ",
80
+ "st.cache_data์— ์ €์žฅ",
81
+ "ํŒŒ์ผ์— ๊ธฐ๋ก"
82
+ ],
83
+ "answer": "st.session_state์— ์ €์žฅ",
84
+ "explanation": "Streamlit์€ ๋งค ์ƒํ˜ธ์ž‘์šฉ๋งˆ๋‹ค ์Šคํฌ๋ฆฝํŠธ ์ „์ฒด๋ฅผ ์žฌ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ „์—ญ ๋ณ€์ˆ˜๋Š” ์žฌ์‹คํ–‰ ์‹œ ์ดˆ๊ธฐํ™”๋˜๋ฏ€๋กœ ์„ธ์…˜ ๊ฐ„ ๊ฐ’์„ ์œ ์ง€ํ•˜๋ ค๋ฉด `st.session_state`๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
85
+ "source_id": "official-docs-streamlit"
86
+ },
87
+ {
88
+ "id": "docs-q14",
89
+ "lesson_id": "docs-streamlit-cache",
90
+ "question_type": "multiple_choice",
91
+ "question": "`st.cache_data`์™€ `st.cache_resource`์˜ ์ฃผ๋œ ์ฐจ์ด๋Š”?",
92
+ "options": [
93
+ "cache_data๋Š” ๋ฐ์ดํ„ฐ(DataFrame ๋“ฑ) ์ง๋ ฌํ™” ์บ์‹œ, cache_resource๋Š” DB ์—ฐ๊ฒฐยท๋ชจ๋ธ ๊ฐ™์€ ๊ณต์œ  ๋ฆฌ์†Œ์Šค ์บ์‹œ",
94
+ "๋‘ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์™„์ „ํžˆ ๋™์ผํ•˜๋‹ค",
95
+ "cache_resource๋Š” ํŒŒ์ผ์„ ์บ์‹œํ•˜๊ณ  cache_data๋Š” ์—ฐ๊ฒฐ์„ ์บ์‹œํ•œ๋‹ค",
96
+ "cache_data๊ฐ€ ํ•ญ์ƒ ๋” ๋น ๋ฅด๋‹ค"
97
+ ],
98
+ "answer": "cache_data๋Š” ๋ฐ์ดํ„ฐ(DataFrame ๋“ฑ) ์ง๋ ฌํ™” ์บ์‹œ, cache_resource๋Š” DB ์—ฐ๊ฒฐยท๋ชจ๋ธ ๊ฐ™์€ ๊ณต์œ  ๋ฆฌ์†Œ์Šค ์บ์‹œ",
99
+ "explanation": "`st.cache_data`๋Š” ๋ฐ˜ํ™˜๊ฐ’์„ ๋ณต์‚ฌํ•ด ๊ฐ ์„ธ์…˜์— ๋…๋ฆฝ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. `st.cache_resource`๋Š” ๊ฐ์ฒด๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ DB ์—ฐ๊ฒฐ, ML ๋ชจ๋ธ์ฒ˜๋Ÿผ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” ๋ฆฌ์†Œ์Šค์— ์”๋‹ˆ๋‹ค.",
100
+ "source_id": "official-docs-streamlit"
101
+ },
102
+ {
103
+ "id": "docs-q15",
104
+ "lesson_id": "docs-hf-spaces",
105
+ "question_type": "multiple_choice",
106
+ "question": "Hugging Face Spaces์—์„œ Streamlit ์•ฑ์„ ๋ฐฐํฌํ•  ๋•Œ README.md์— ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•ด์•ผ ํ•˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„๋“œ๋Š”?",
107
+ "options": [
108
+ "sdk: streamlit",
109
+ "runtime: python",
110
+ "framework: web",
111
+ "deploy: auto"
112
+ ],
113
+ "answer": "sdk: streamlit",
114
+ "explanation": "HF Spaces README ์ƒ๋‹จ YAML ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ `sdk: streamlit`์„ ์ง€์ •ํ•ด์•ผ Streamlit ์•ฑ์œผ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค. `sdk_version`์œผ๋กœ ๋ฒ„์ „๋„ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
115
+ "source_id": "official-docs-huggingface"
116
+ },
117
+ {
118
+ "id": "docs-q16",
119
+ "lesson_id": "docs-vectorstore",
120
+ "question_type": "multiple_choice",
121
+ "question": "LangChain์—์„œ Retriever์˜ ์—ญํ• ์€?",
122
+ "options": [
123
+ "์งˆ๋ฌธ๊ณผ ์œ ์‚ฌํ•œ ๋ฌธ์„œ๋ฅผ Vector Store์—์„œ ๊ฒ€์ƒ‰ํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค",
124
+ "LLM์„ ํ˜ธ์ถœํ•ด ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•œ๋‹ค",
125
+ "ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์„ ๊ด€๋ฆฌํ•œ๋‹ค",
126
+ "API ํ‚ค๋ฅผ ์ €์žฅํ•œ๋‹ค"
127
+ ],
128
+ "answer": "์งˆ๋ฌธ๊ณผ ์œ ์‚ฌํ•œ ๋ฌธ์„œ๋ฅผ Vector Store์—์„œ ๊ฒ€์ƒ‰ํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค",
129
+ "explanation": "Retriever๋Š” ์งˆ๋ฌธ ์ž„๋ฒ ๋”ฉ๊ณผ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ๋ฌธ์„œ ์ฒญํฌ๋ฅผ Vector Store์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. RAG ์ฒด์ธ์—์„œ `retriever | prompt | llm` ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ฒซ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.",
130
+ "source_id": "official-docs-langchain"
131
+ },
132
+ {
133
+ "id": "docs-q17",
134
+ "lesson_id": "docs-streamlit-state",
135
+ "question_type": "multiple_choice",
136
+ "question": "`st.form`์„ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ๋œ ์ด์œ ๋Š”?",
137
+ "options": [
138
+ "์—ฌ๋Ÿฌ ์ž…๋ ฅ ์œ„์ ฏ์„ ๋ฌถ์–ด Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ์—๋งŒ rerun์ด ๋ฐœ์ƒํ•˜๋„๋ก ํ•œ๋‹ค",
139
+ "ํผ ๋‚ด ์œ„์ ฏ์ด ๋” ๋น ๋ฅด๊ฒŒ ๋ Œ๋”๋ง๋œ๋‹ค",
140
+ "์ž…๋ ฅ๊ฐ’์„ ์ž๋™์œผ๋กœ DB์— ์ €์žฅํ•œ๋‹ค",
141
+ "ํผ์€ ๋ชจ๋ฐ”์ผ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค"
142
+ ],
143
+ "answer": "์—ฌ๋Ÿฌ ์ž…๋ ฅ ์œ„์ ฏ์„ ๋ฌถ์–ด Submit ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ์—๋งŒ rerun์ด ๋ฐœ์ƒํ•˜๋„๋ก ํ•œ๋‹ค",
144
+ "explanation": "์ผ๋ฐ˜ ์œ„์ ฏ์€ ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค rerun๋ฉ๋‹ˆ๋‹ค. `st.form`์œผ๋กœ ๋ฌถ์œผ๋ฉด Submit ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งŒ rerun๋˜์–ด ๋ถˆํ•„์š”ํ•œ ์žฌ์‹คํ–‰์„ ๋ง‰๊ณ  UX๋ฅผ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.",
145
+ "source_id": "official-docs-streamlit"
146
+ },
147
+ {
148
+ "id": "docs-q18",
149
+ "lesson_id": "docs-hf-spaces",
150
+ "question_type": "multiple_choice",
151
+ "question": "Hugging Face `pipeline()` ํ•จ์ˆ˜๋กœ ๊ฐ์„ฑ ๋ถ„์„ ํƒœ์Šคํฌ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด?",
152
+ "options": [
153
+ "pipeline('sentiment-analysis')",
154
+ "pipeline('classification')",
155
+ "pipeline('text-generation')",
156
+ "pipeline('translation')"
157
+ ],
158
+ "answer": "pipeline('sentiment-analysis')",
159
+ "explanation": "`from transformers import pipeline; classifier = pipeline('sentiment-analysis')` ๋กœ ๊ธฐ๋ณธ ๊ฐ์„ฑ ๋ถ„์„ ๋ชจ๋ธ์„ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” `[{'label': 'POSITIVE', 'score': 0.99}]` ํ˜•์‹์ž…๋‹ˆ๋‹ค.",
160
+ "source_id": "official-docs-huggingface"
161
+ },
162
+ {
163
+ "id": "docs-q19",
164
+ "lesson_id": "docs-lcel",
165
+ "question_type": "multiple_choice",
166
+ "question": "LangChain LCEL(LangChain Expression Language)์—์„œ `|` ์—ฐ์‚ฐ์ž์˜ ์—ญํ• ์€?",
167
+ "options": [
168
+ "์•ž ๋‹จ๊ณ„์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์ž…๋ ฅ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ฒด์ธ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค",
169
+ "๋‘ ์ฒด์ธ์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•œ๋‹ค",
170
+ "์ฒด์ธ์„ ์ทจ์†Œํ•œ๋‹ค",
171
+ "์ถœ๋ ฅ์„ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค"
172
+ ],
173
+ "answer": "์•ž ๋‹จ๊ณ„์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์ž…๋ ฅ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ฒด์ธ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค",
174
+ "explanation": "LCEL์˜ `|`๋Š” ์œ ๋‹‰์Šค ํŒŒ์ดํ”„์™€ ๊ฐ™์€ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. `chain = retriever | prompt | llm | parser`์ฒ˜๋Ÿผ ๊ฐ Runnable์˜ ์ถœ๋ ฅ์ด ๋‹ค์Œ ์ž…๋ ฅ์ด ๋ฉ๋‹ˆ๋‹ค.",
175
+ "source_id": "official-docs-langchain"
176
+ },
177
+ {
178
+ "id": "docs-q21",
179
+ "lesson_id": "docs-lcel",
180
+ "question_type": "multiple_choice",
181
+ "question": "LangChain LCEL์—์„œ `RunnableParallel`์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชฉ์ ์€?",
182
+ "options": [
183
+ "์—ฌ๋Ÿฌ Runnable์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ํ•ฉ์นœ๋‹ค",
184
+ "์ฒด์ธ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค",
185
+ "๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•œ๋‹ค",
186
+ "์—๋Ÿฌ๋ฅผ ๋ฌด์‹œํ•œ๋‹ค"
187
+ ],
188
+ "answer": "์—ฌ๋Ÿฌ Runnable์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ํ•ฉ์นœ๋‹ค",
189
+ "explanation": "`RunnableParallel({'context': retriever, 'question': RunnablePassthrough()})`์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ ๋ธŒ๋žœ์น˜๋ฅผ ๋ณ‘๋ ฌ ์‹คํ–‰ํ•ด dict๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค. RAG์—์„œ ์ปจํ…์ŠคํŠธ ๊ฒ€์ƒ‰๊ณผ ์งˆ๋ฌธ ์ „๋‹ฌ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
190
+ "source_id": "official-docs-langchain"
191
+ },
192
+ {
193
+ "id": "docs-q22",
194
+ "lesson_id": "docs-vectorstore",
195
+ "question_type": "multiple_choice",
196
+ "question": "FAISS Vector Store์—์„œ `similarity_search(query, k=4)`์˜ `k` ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์˜๋ฏธ๋Š”?",
197
+ "options": [
198
+ "๋ฐ˜ํ™˜ํ•  ์œ ์‚ฌ ๋ฌธ์„œ ์ฒญํฌ์˜ ์ตœ๋Œ€ ์ˆ˜",
199
+ "๊ฒ€์ƒ‰ ์ž„๋ฒ ๋”ฉ ์ฐจ์› ์ˆ˜",
200
+ "์ตœ์†Œ ์œ ์‚ฌ๋„ ์ ์ˆ˜",
201
+ "์ธ๋ฑ์Šค ํŒŒ์ผ ๋ฒ„์ „"
202
+ ],
203
+ "answer": "๋ฐ˜ํ™˜ํ•  ์œ ์‚ฌ ๋ฌธ์„œ ์ฒญํฌ์˜ ์ตœ๋Œ€ ์ˆ˜",
204
+ "explanation": "`k`๋Š” ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ์ƒ์œ„ k๊ฐœ์˜ ์ฒญํฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. k๊ฐ€ ํฌ๋ฉด ๋” ๋งŽ์€ ์ปจํ…์ŠคํŠธ๋ฅผ LLM์— ์ œ๊ณตํ•˜์ง€๋งŒ ํ”„๋กฌํ”„ํŠธ ๊ธธ์ด๊ฐ€ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค.",
205
+ "source_id": "official-docs-langchain"
206
+ },
207
+ {
208
+ "id": "docs-q23",
209
+ "lesson_id": "docs-streamlit-secrets",
210
+ "question_type": "command",
211
+ "question": "Streamlit ์•ฑ ์ฝ”๋“œ์—์„œ secrets.toml์˜ `OPENAI_API_KEY` ๊ฐ’์„ ์ฝ๋Š” ๋ฐฉ๋ฒ•์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
212
+ "options": [],
213
+ "answer": "st.secrets[\"OPENAI_API_KEY\"]",
214
+ "explanation": "`st.secrets[\"ํ‚ค์ด๋ฆ„\"]` ๋˜๋Š” `st.secrets.ํ‚ค์ด๋ฆ„`์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐํฌ ์‹œ Streamlit Cloud์˜ Secrets ๊ด€๋ฆฌ ํ™”๋ฉด์—์„œ ๋™์ผ ํ‚ค๋กœ ๋“ฑ๋กํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
215
+ "source_id": "official-docs-streamlit"
216
+ },
217
+ {
218
+ "id": "docs-q24",
219
+ "lesson_id": "docs-hf-docker-space",
220
+ "question_type": "multiple_choice",
221
+ "question": "Hugging Face Docker Space์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜(API ํ‚ค ๋“ฑ)๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
222
+ "options": [
223
+ "Space Settings์˜ Repository Secrets์— ๋“ฑ๋กํ•˜๋ฉด ๋นŒ๋“œ/๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…๋œ๋‹ค",
224
+ "Dockerfile์— ENV๋กœ ํ•˜๋“œ์ฝ”๋”ฉํ•œ๋‹ค",
225
+ "requirements.txt์— ์ถ”๊ฐ€ํ•œ๋‹ค",
226
+ "README ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— base64๋กœ ์ธ์ฝ”๋”ฉํ•ด ๋„ฃ๋Š”๋‹ค"
227
+ ],
228
+ "answer": "Space Settings์˜ Repository Secrets์— ๋“ฑ๋กํ•˜๋ฉด ๋นŒ๋“œ/๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…๋œ๋‹ค",
229
+ "explanation": "HF Space์˜ Repository Secrets๋Š” Docker ๋นŒ๋“œ์™€ ์ปจํ…Œ์ด๋„ˆ ๋Ÿฐํƒ€์ž„์— ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค. Dockerfile์ด๋‚˜ ์ฝ”๋“œ์— ๋น„๋ฐ€์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋ฉด ๊ณต๊ฐœ ์ €์žฅ์†Œ์—์„œ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค.",
230
+ "source_id": "official-docs-huggingface"
231
+ },
232
+ {
233
+ "id": "docs-q20",
234
+ "lesson_id": "docs-ollama-api",
235
+ "question_type": "command",
236
+ "question": "Ollama REST API๋กœ `llama3.2` ๋ชจ๋ธ์— 'Hello' ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ ์—†์ด ์š”์ฒญํ•˜๋Š” curl ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.",
237
+ "options": [],
238
+ "answer": "curl http://localhost:11434/api/generate -d '{\"model\":\"llama3.2\",\"prompt\":\"Hello\",\"stream\":false}'",
239
+ "explanation": "Ollama API๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. `\"stream\": false`๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์™„์„ฑ๋œ ์‘๋‹ต์„ ํ•œ ๋ฒˆ์— ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
240
+ "source_id": "official-docs-ollama"
241
+ },
242
+ {
243
+ "id": "docs-q25",
244
+ "lesson_id": "docs-fastapi",
245
+ "question_type": "multiple_choice",
246
+ "question": "FastAPI์—์„œ POST `/ask` ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ •์˜ํ•  ๋•Œ ์š”์ฒญ ๋ฐ”๋””๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€?",
247
+ "options": [
248
+ "Pydantic BaseModel์„ ์ƒ์†ํ•œ ํด๋ž˜์Šค๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•œ๋‹ค",
249
+ "request.body()๋ฅผ ์ˆ˜๋™์œผ๋กœ ํŒŒ์‹ฑํ•œ๋‹ค",
250
+ "์ „์—ญ ๋”•์…”๋„ˆ๋ฆฌ์— JSON์„ ์ €์žฅํ•œ๋‹ค",
251
+ "Flask์˜ request.json์„ ์‚ฌ์šฉํ•œ๋‹ค"
252
+ ],
253
+ "answer": "Pydantic BaseModel์„ ์ƒ์†ํ•œ ํด๋ž˜์Šค๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•œ๋‹ค",
254
+ "explanation": "FastAPI๋Š” ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— Pydantic ๋ชจ๋ธ์„ ํƒ€์ž… ํžŒํŠธ๋กœ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ JSON ๋ฐ”๋””๋ฅผ ํŒŒ์‹ฑยท๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์ž˜๋ชป๋œ ์ž…๋ ฅ์€ 422 ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.",
255
+ "source_id": "official-docs-fastapi"
256
+ },
257
+ {
258
+ "id": "docs-q26",
259
+ "lesson_id": "docs-fastapi",
260
+ "question_type": "multiple_choice",
261
+ "question": "FastAPI ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์ž๋™ ์žฌ์‹œ์ž‘(reload) ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์–ด๋Š”?",
262
+ "options": [
263
+ "uvicorn main:app --reload",
264
+ "python main.py --dev",
265
+ "fastapi run --watch main.py",
266
+ "gunicorn main:app --workers 1"
267
+ ],
268
+ "answer": "uvicorn main:app --reload",
269
+ "explanation": "`uvicorn main:app --reload`๋Š” ํŒŒ์ผ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด ์„œ๋ฒ„๋ฅผ ์ž๋™ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. `main`์€ ํŒŒ์ด์ฌ ํŒŒ์ผ๋ช…, `app`์€ FastAPI ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ช…์ž…๋‹ˆ๋‹ค.",
270
+ "source_id": "official-docs-fastapi"
271
+ },
272
+ {
273
+ "id": "docs-q27",
274
+ "lesson_id": "docs-langsmith",
275
+ "question_type": "multiple_choice",
276
+ "question": "LangSmith ์ถ”์ ์„ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒƒ์€?",
277
+ "options": [
278
+ "LANGCHAIN_MODEL",
279
+ "LANGCHAIN_TRACING_V2",
280
+ "LANGCHAIN_API_KEY",
281
+ "LANGCHAIN_PROJECT"
282
+ ],
283
+ "answer": "LANGCHAIN_MODEL",
284
+ "explanation": "LangSmith ์ถ”์ ์— ํ•„์š”ํ•œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” `LANGCHAIN_TRACING_V2=true`, `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT` ์„ธ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋ชจ๋ธ๋ช…์€ ์ฒด์ธ ์ฝ”๋“œ์—์„œ ์ง์ ‘ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
285
+ "source_id": "official-docs-langchain"
286
+ },
287
+ {
288
+ "id": "docs-q28",
289
+ "lesson_id": "docs-langsmith",
290
+ "question_type": "multiple_choice",
291
+ "question": "LangSmith์—์„œ ํ”„๋กœ๋•์…˜ ์˜ค๋ฅ˜ ์ผ€์ด์Šค๋ฅผ ์ˆ˜์ง‘ํ•ด ์ž๋™ํ™”๋œ ํ’ˆ์งˆ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ๋Šฅ์€?",
292
+ "options": [
293
+ "Dataset + Evaluator๋กœ ํ‰๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค",
294
+ "A/B ํ…Œ์ŠคํŠธ๋กœ ๋ชจ๋ธ์„ ๋น„๊ตํ•œ๋‹ค",
295
+ "ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž๋™ ์ตœ์ ํ™”ํ•œ๋‹ค",
296
+ "๋ชจ๋ธ์„ ํŒŒ์ธํŠœ๋‹ํ•œ๋‹ค"
297
+ ],
298
+ "answer": "Dataset + Evaluator๋กœ ํ‰๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค",
299
+ "explanation": "LangSmith์˜ Dataset์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค(์ž…์ถœ๋ ฅ ์Œ)๋ฅผ ์ €์žฅํ•˜๊ณ , Evaluator(LLM-as-judge ๋“ฑ)๋กœ ์ž๋™ ์ฑ„์ ํ•ฉ๋‹ˆ๋‹ค. CI์— ํ†ตํ•ฉํ•ด ํ”„๋กฌํ”„ํŠธ ๋ณ€๊ฒฝ ์‹œ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
300
+ "source_id": "official-docs-langchain"
301
+ },
302
+ {
303
+ "id": "docs-q29",
304
+ "lesson_id": "docs-guardrails",
305
+ "question_type": "multiple_choice",
306
+ "question": "Guardrails AI์—์„œ LLM ์ถœ๋ ฅ์ด ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ LLM์— ์žฌ์š”์ฒญํ•˜๋Š” `on_fail` ๋™์ž‘์€?",
307
+ "options": [
308
+ "reask",
309
+ "exception",
310
+ "noop",
311
+ "fix"
312
+ ],
313
+ "answer": "reask",
314
+ "explanation": "`on_fail='reask'`๋Š” ๊ฒ€์ฆ ์‹คํŒจ ์‹œ LLM์— ์ˆ˜์ • ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ๋ณด๋ƒ…๋‹ˆ๋‹ค. `exception`์€ ์˜ˆ์™ธ ๋ฐœ์ƒ, `noop`์€ ๊ทธ๋ƒฅ ํ†ต๊ณผ, `fix`๋Š” ์ž๋™ ์ˆ˜์ •์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.",
315
+ "source_id": "official-docs-guardrails"
316
+ },
317
+ {
318
+ "id": "docs-q30",
319
+ "lesson_id": "docs-guardrails",
320
+ "question_type": "multiple_choice",
321
+ "question": "Guardrails AI๋กœ LLM์ด ํ•ญ์ƒ ์ง€์ •ํ•œ JSON ์Šคํ‚ค๋งˆ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
322
+ "options": [
323
+ "Guard.from_pydantic(OutputModel)์œผ๋กœ Guard๋ฅผ ์ƒ์„ฑํ•œ๋‹ค",
324
+ "LLM ํ”„๋กฌํ”„ํŠธ์— JSON ์ถœ๋ ฅ์„ ์š”์ฒญํ•œ๋‹ค",
325
+ "json.loads()๋กœ ์ถœ๋ ฅ์„ ํŒŒ์‹ฑํ•œ๋‹ค",
326
+ "OpenAI์˜ function_calling ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์“ด๋‹ค"
327
+ ],
328
+ "answer": "Guard.from_pydantic(OutputModel)์œผ๋กœ Guard๋ฅผ ์ƒ์„ฑํ•œ๋‹ค",
329
+ "explanation": "`Guard.from_pydantic(OutputModel)`์€ Pydantic ๋ชจ๋ธ์„ JSON ์Šคํ‚ค๋งˆ๋กœ ๋ณ€ํ™˜ํ•ด LLM ์ถœ๋ ฅ์ด ์ด ์Šคํ‚ค๋งˆ๋ฅผ ๋”ฐ๋ฅด๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ํ˜•์‹์ด ๋งž์ง€ ์•Š์œผ๋ฉด reask ๋˜๋Š” exception์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.",
330
+ "source_id": "official-docs-guardrails"
331
+ },
332
+ {
333
+ "id": "docs-q31",
334
+ "lesson_id": "docs-gradio",
335
+ "question_type": "multiple_choice",
336
+ "question": "Gradio์—์„œ LLM ์ฑ—๋ด‡ UI๋ฅผ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€?",
337
+ "options": [
338
+ "gr.ChatInterface(fn=chat_function)์œผ๋กœ ์ฑ„ํŒ… ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ผ๋‹ค",
339
+ "gr.Interface(fn=chat)์— ํ…์ŠคํŠธ ์ž…์ถœ๋ ฅ์„ ์ง€์ •ํ•œ๋‹ค",
340
+ "gr.Blocks()์— gr.Textbox๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋ฐฐ์น˜ํ•œ๋‹ค",
341
+ "Streamlit์˜ st.chat_message๋ฅผ ์ž„ํฌํŠธํ•ด ์“ด๋‹ค"
342
+ ],
343
+ "answer": "gr.ChatInterface(fn=chat_function)์œผ๋กœ ์ฑ„ํŒ… ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ผ๋‹ค",
344
+ "explanation": "`gr.ChatInterface`๋Š” ์ฑ„ํŒ… UI์— ์ตœ์ ํ™”๋œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ `(message, history) -> str` ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋”ฐ๋ฅด๋ฉด ์ž๋™์œผ๋กœ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ UI์™€ Submit ๋ฒ„ํŠผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
345
+ "source_id": "official-docs-gradio"
346
+ },
347
+ {
348
+ "id": "docs-q32",
349
+ "lesson_id": "docs-gradio",
350
+ "question_type": "multiple_choice",
351
+ "question": "Gradio์—์„œ ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ(์‚ฌ์ด๋“œ๋ฐ”, ํƒญ ๋“ฑ)์„ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด?",
352
+ "options": [
353
+ "gr.Blocks() ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ € ์•ˆ์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐฐ์น˜ํ•œ๋‹ค",
354
+ "gr.Interface์— layout ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง€์ •ํ•œ๋‹ค",
355
+ "CSS๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•œ๋‹ค",
356
+ "Streamlit ์ปดํฌ๋„ŒํŠธ๋ฅผ Gradio์— ์ž„๋ฒ ๋“œํ•œ๋‹ค"
357
+ ],
358
+ "answer": "gr.Blocks() ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ € ์•ˆ์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐฐ์น˜ํ•œ๋‹ค",
359
+ "explanation": "`gr.Blocks()`๋Š” Gradio์˜ ์ €์ˆ˜์ค€ ๋ ˆ์ด์•„์›ƒ API์ž…๋‹ˆ๋‹ค. `with gr.Row():`, `with gr.Tab():` ๋“ฑ์„ ์ค‘์ฒฉํ•ด ์ž์œ ๋กญ๊ฒŒ UI๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ด๋ฒคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
360
+ "source_id": "official-docs-gradio"
361
+ },
362
+ {
363
+ "id": "docs-q33",
364
+ "lesson_id": "docs-airflow",
365
+ "question_type": "multiple_choice",
366
+ "question": "Airflow DAG์—์„œ `task_a >> task_b >> task_c`์˜ ์˜๋ฏธ๋Š”?",
367
+ "options": [
368
+ "task_a โ†’ task_b โ†’ task_c ์ˆœ์„œ๋กœ ์ˆœ์ฐจ ์‹คํ–‰๋œ๋‹ค",
369
+ "์„ธ ํƒœ์Šคํฌ๊ฐ€ ๋™์‹œ์— ๋ณ‘๋ ฌ ์‹คํ–‰๋œ๋‹ค",
370
+ "task_c๊ฐ€ ์‹คํŒจํ•˜๋ฉด task_a๋กœ ๋กค๋ฐฑ๋œ๋‹ค",
371
+ "task_b๋Š” task_a์™€ task_c์˜ ์ถœ๋ ฅ์„ ํ•ฉ์นœ๋‹ค"
372
+ ],
373
+ "answer": "task_a โ†’ task_b โ†’ task_c ์ˆœ์„œ๋กœ ์ˆœ์ฐจ ์‹คํ–‰๋œ๋‹ค",
374
+ "explanation": "`>>` ์—ฐ์‚ฐ์ž๋Š” ์‹คํ–‰ ์˜์กด์„ฑ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. `task_a >> task_b`๋Š” task_a๊ฐ€ ์„ฑ๊ณตํ•œ ๋’ค์—๋งŒ task_b๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ์‹คํ–‰์€ `[task_b, task_c] >> task_d`์ฒ˜๋Ÿผ ๋ฆฌ์ŠคํŠธ๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.",
375
+ "source_id": "official-docs-airflow"
376
+ },
377
+ {
378
+ "id": "docs-q34",
379
+ "lesson_id": "docs-airflow",
380
+ "question_type": "multiple_choice",
381
+ "question": "Airflow์—์„œ task ๊ฐ„ ์†Œ๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์€?",
382
+ "options": [
383
+ "XCom (Cross-Communication)",
384
+ "์ „์—ญ ๋ณ€์ˆ˜",
385
+ "ํŒŒ์ผ ์‹œ์Šคํ…œ์˜ ์ž„์‹œ ํŒŒ์ผ",
386
+ "Airflow Variable"
387
+ ],
388
+ "answer": "XCom (Cross-Communication)",
389
+ "explanation": "XCom์€ `ti.xcom_push(key, value)`๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋‹ค๋ฅธ task์—์„œ `ti.xcom_pull(task_ids, key)`๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋Š” S3/GCS ๊ฒฝ๋กœ๋ฅผ XCom์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค.",
390
+ "source_id": "official-docs-airflow"
391
+ },
392
+ {
393
+ "id": "docs-q35",
394
+ "lesson_id": "docs-mlflow",
395
+ "question_type": "multiple_choice",
396
+ "question": "MLflow์—์„œ ์‹คํ—˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฉ”ํŠธ๋ฆญ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
397
+ "options": [
398
+ "with mlflow.start_run(): ๋ธ”๋ก ์•ˆ์—์„œ mlflow.log_param()๊ณผ mlflow.log_metric()์„ ํ˜ธ์ถœํ•œ๋‹ค",
399
+ "mlflow.track(params={}, metrics={})๋ฅผ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค",
400
+ "print() ์ถœ๋ ฅ์ด ์ž๋™์œผ๋กœ MLflow์— ๊ธฐ๋ก๋œ๋‹ค",
401
+ "mlflow.autolog()๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ๊ธฐ๋ก๋œ๋‹ค"
402
+ ],
403
+ "answer": "with mlflow.start_run(): ๋ธ”๋ก ์•ˆ์—์„œ mlflow.log_param()๊ณผ mlflow.log_metric()์„ ํ˜ธ์ถœํ•œ๋‹ค",
404
+ "explanation": "`with mlflow.start_run():` ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €๊ฐ€ run์„ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ์•ˆ์—์„œ `log_param`(ํŒŒ๋ผ๋ฏธํ„ฐ), `log_metric`(๋ฉ”ํŠธ๋ฆญ), `log_artifact`(ํŒŒ์ผ)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.",
405
+ "source_id": "official-docs-mlflow"
406
+ },
407
+ {
408
+ "id": "docs-q36",
409
+ "lesson_id": "docs-mlflow",
410
+ "question_type": "multiple_choice",
411
+ "question": "MLflow UI๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๋ช…๋ น์–ด๋Š”?",
412
+ "options": [
413
+ "mlflow ui",
414
+ "mlflow server --start",
415
+ "python -m mlflow.dashboard",
416
+ "mlflow run --ui"
417
+ ],
418
+ "answer": "mlflow ui",
419
+ "explanation": "`mlflow ui` ๋ช…๋ น์œผ๋กœ localhost:5000์—์„œ ์‹คํ—˜ ๋น„๊ต ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‹คํ—˜ ๋ชฉ๋ก, ํŒŒ๋ผ๋ฏธํ„ฐยท๋ฉ”ํŠธ๋ฆญ ๋น„๊ต ์ฐจํŠธ, ์•„ํ‹ฐํŒฉํŠธ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์›น UI๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
420
+ "source_id": "official-docs-mlflow"
421
+ },
422
+ {
423
+ "id": "docs-q37",
424
+ "lesson_id": "docs-mcp",
425
+ "question_type": "multiple_choice",
426
+ "question": "MCP(Model Context Protocol)์—์„œ `tool`์˜ ์—ญํ• ์€?",
427
+ "options": [
428
+ "LLM์ด ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋กœ, ์ž…์ถœ๋ ฅ ์Šคํ‚ค๋งˆ๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค",
429
+ "LLM์ด ์ฝ๋Š” ์ •์  ๋ฐ์ดํ„ฐ ์†Œ์Šค",
430
+ "์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ",
431
+ "MCP ์„œ๋ฒ„์˜ ์„ค์ • ํŒŒ์ผ"
432
+ ],
433
+ "answer": "LLM์ด ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋กœ, ์ž…์ถœ๋ ฅ ์Šคํ‚ค๋งˆ๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค",
434
+ "explanation": "MCP `tool`์€ LLM์ด ๋Œ€ํ™” ์ค‘ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. FastMCP์˜ `@mcp.tool()` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋ถ™์ด๋ฉด ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์—์„œ JSON Schema๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
435
+ "source_id": "official-docs-mcp"
436
+ },
437
+ {
438
+ "id": "docs-q51",
439
+ "lesson_id": "docs-chroma",
440
+ "question_type": "multiple_choice",
441
+ "question": "FAISS์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ Chroma์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€?",
442
+ "options": [
443
+ "persist_directory๋งŒ ์ง€์ •ํ•˜๋ฉด ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™ ์œ ์ง€๋œ๋‹ค",
444
+ "๊ฒ€์ƒ‰ ์†๋„๊ฐ€ FAISS๋ณด๋‹ค 10๋ฐฐ ๋น ๋ฅด๋‹ค",
445
+ "GPU ์—†์ด ์ž„๋ฒ ๋”ฉ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค",
446
+ "OpenAI API ์—†์ด ๋™์ž‘ํ•œ๋‹ค"
447
+ ],
448
+ "answer": "persist_directory๋งŒ ์ง€์ •ํ•˜๋ฉด ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž๋™ ์œ ์ง€๋œ๋‹ค",
449
+ "explanation": "FAISS๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ `save_local()`์„ ์ˆ˜๋™ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Chroma๋Š” `persist_directory`๋ฅผ ์ง€์ •ํ•˜๋ฉด SQLite ๊ธฐ๋ฐ˜ ๋กœ์ปฌ DB์— ์ž๋™ ์ €์žฅ๋˜์–ด ์žฌ์‹œ์ž‘ ํ›„์—๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
450
+ "source_id": "official-docs-chromadb"
451
+ },
452
+ {
453
+ "id": "docs-q52",
454
+ "lesson_id": "docs-chroma",
455
+ "question_type": "multiple_choice",
456
+ "question": "๊ธฐ์กด Chroma DB์—์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐ๊ฑด์œผ๋กœ ๊ฒ€์ƒ‰ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
457
+ "options": [
458
+ "db.similarity_search(query, filter={\"source\": \"file.pdf\"})",
459
+ "db.search(query, where={\"source\": \"file.pdf\"})",
460
+ "db.similarity_search(query, metadata=\"file.pdf\")",
461
+ "Chroma๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค"
462
+ ],
463
+ "answer": "db.similarity_search(query, filter={\"source\": \"file.pdf\"})",
464
+ "explanation": "`filter` ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐ๊ฑด์„ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ฌธ์„œ ์ค‘์—์„œ๋งŒ ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰์„ ํ•ฉ๋‹ˆ๋‹ค. FAISS์—๋Š” ์—†๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.",
465
+ "source_id": "official-docs-chromadb"
466
+ },
467
+ {
468
+ "id": "docs-q53",
469
+ "lesson_id": "docs-sentence-transformers",
470
+ "question_type": "multiple_choice",
471
+ "question": "LangChain์—์„œ sentence-transformers ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค๋Š”?",
472
+ "options": [
473
+ "HuggingFaceEmbeddings",
474
+ "SentenceTransformerEmbeddings",
475
+ "LocalEmbeddings",
476
+ "OllamaEmbeddings"
477
+ ],
478
+ "answer": "HuggingFaceEmbeddings",
479
+ "explanation": "`from langchain_huggingface import HuggingFaceEmbeddings`๋กœ ๊ฐ€์ ธ์™€ `model_name`์— HuggingFace ๋ชจ๋ธ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. `sentence-transformers/all-MiniLM-L6-v2`๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๊ธฐ๋ณธ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.",
480
+ "source_id": "official-docs-huggingface"
481
+ },
482
+ {
483
+ "id": "docs-q54",
484
+ "lesson_id": "docs-sentence-transformers",
485
+ "question_type": "multiple_choice",
486
+ "question": "sentence-transformers ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ๊ต์ฒดํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์€?",
487
+ "options": [
488
+ "๊ธฐ์กด VectorStore ์ „์ฒด๋ฅผ ์ƒˆ ๋ชจ๋ธ๋กœ ์žฌ์ธ๋ฑ์‹ฑํ•œ๋‹ค",
489
+ "๋ชจ๋ธ ํŒŒ์ผ์„ ๋ฎ์–ด์“ฐ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค",
490
+ "VectorStore์˜ dimension ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค",
491
+ "์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์€ ์–ธ์ œ๋“  ์ž์œ ๋กญ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅํ•˜๋‹ค"
492
+ ],
493
+ "answer": "๊ธฐ์กด VectorStore ์ „์ฒด๋ฅผ ์ƒˆ ๋ชจ๋ธ๋กœ ์žฌ์ธ๋ฑ์‹ฑํ•œ๋‹ค",
494
+ "explanation": "์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์ด ๋‹ค๋ฅด๋ฉด ๋ฒกํ„ฐ ๊ณต๏ฟฝ๏ฟฝ์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ๊ธฐ์กด ์ธ๋ฑ์Šค์˜ ๋ฒกํ„ฐ์™€ ์ƒˆ ๋ชจ๋ธ๋กœ ์ƒ์„ฑํ•œ ์ฟผ๋ฆฌ ๋ฒกํ„ฐ๊ฐ€ ํ˜ธํ™˜๋˜์ง€ ์•Š์•„ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—‰ํ„ฐ๋ฆฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ „์ฒด ์žฌ์ธ๋ฑ์‹ฑ์ด ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.",
495
+ "source_id": "official-docs-huggingface"
496
+ },
497
+ {
498
+ "id": "docs-q55",
499
+ "lesson_id": "docs-ragas",
500
+ "question_type": "multiple_choice",
501
+ "question": "Ragas์˜ Faithfulness ๋ฉ”ํŠธ๋ฆญ์ด ์ธก์ •ํ•˜๋Š” ๊ฒƒ์€?",
502
+ "options": [
503
+ "LLM ๋‹ต๋ณ€์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์— ๊ทผ๊ฑฐํ•˜๋Š”๊ฐ€ (ํ™˜๊ฐ ํƒ์ง€)",
504
+ "๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ๊ฐ€ ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ ์žˆ๋Š”๊ฐ€",
505
+ "๋‹ต๋ณ€์ด ground_truth์™€ ์ผ์น˜ํ•˜๋Š”๊ฐ€",
506
+ "RAG ํŒŒ์ดํ”„๋ผ์ธ์˜ ์‹คํ–‰ ์†๋„"
507
+ ],
508
+ "answer": "LLM ๋‹ต๋ณ€์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์— ๊ทผ๊ฑฐํ•˜๋Š”๊ฐ€ (ํ™˜๊ฐ ํƒ์ง€)",
509
+ "explanation": "Faithfulness๋Š” LLM์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์— ์—†๋Š” ๋‚ด์šฉ์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”์ง€ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค. 1.0์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ํ™˜๊ฐ์ด ์—†์Šต๋‹ˆ๋‹ค. Answer Relevancy๋Š” ๋‹ต๋ณ€์ด ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ ์žˆ๋Š”์ง€, Context Precision์€ ๊ด€๋ จ ๋ฌธ์„œ๊ฐ€ ์ž˜ ๊ฒ€์ƒ‰๋๋Š”์ง€์ž…๋‹ˆ๋‹ค.",
510
+ "source_id": "official-docs-ragas"
511
+ },
512
+ {
513
+ "id": "docs-q56",
514
+ "lesson_id": "docs-ragas",
515
+ "question_type": "multiple_choice",
516
+ "question": "Ragas evaluate() ํ•จ์ˆ˜์— ํ•„์ˆ˜๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ํ‚ค๊ฐ€ ์•„๋‹Œ ๊ฒƒ์€?",
517
+ "options": [
518
+ "ground_truth (faithfulness๋งŒ ๊ณ„์‚ฐํ•  ๊ฒฝ์šฐ)",
519
+ "question",
520
+ "answer",
521
+ "contexts"
522
+ ],
523
+ "answer": "ground_truth (faithfulness๋งŒ ๊ณ„์‚ฐํ•  ๊ฒฝ์šฐ)",
524
+ "explanation": "`question`, `answer`, `contexts`๋Š” ๊ธฐ๋ณธ ๋ฉ”ํŠธ๋ฆญ์— ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. `ground_truth`๋Š” Answer Correctness ๋ฉ”ํŠธ๋ฆญ์—๋งŒ ํ•„์š”ํ•˜๋ฉฐ, faithfulness์™€ answer_relevancy๋งŒ ๊ณ„์‚ฐํ•  ๊ฒฝ์šฐ ์—†์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.",
525
+ "source_id": "official-docs-ragas"
526
+ },
527
+ {
528
+ "id": "docs-q57",
529
+ "lesson_id": "docs-langgraph",
530
+ "question_type": "multiple_choice",
531
+ "question": "LangGraph์—์„œ ๋…ธ๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š”?",
532
+ "options": [
533
+ "graph.add_conditional_edges()",
534
+ "graph.add_edge()",
535
+ "graph.set_next_node()",
536
+ "graph.branch()"
537
+ ],
538
+ "answer": "graph.add_conditional_edges()",
539
+ "explanation": "`graph.add_conditional_edges('node', router_fn, {'yes': 'next_node', 'no': END})`๋กœ ๋…ธ๋“œ ๋ฐ˜ํ™˜๊ฐ’์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ณ ์ •๋œ ๋‹ค์Œ ๋…ธ๋“œ๋Š” `add_edge()`๋ฅผ ์”๋‹ˆ๋‹ค.",
540
+ "source_id": "official-docs-langgraph"
541
+ },
542
+ {
543
+ "id": "docs-q58",
544
+ "lesson_id": "docs-langgraph",
545
+ "question_type": "multiple_choice",
546
+ "question": "LangGraph StateGraph์—์„œ State TypedDict๋ฅผ ์ •์˜ํ•˜๋Š” ์ด์œ ๋Š”?",
547
+ "options": [
548
+ "๋…ธ๋“œ ๊ฐ„ ๊ณต์œ ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด",
549
+ "๊ทธ๋ž˜ํ”„ ์‹คํ–‰ ์†๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด",
550
+ "LangChain ์ฒด์ธ๊ณผ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด",
551
+ "State๋Š” ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค"
552
+ ],
553
+ "answer": "๋…ธ๋“œ ๊ฐ„ ๊ณต์œ ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด",
554
+ "explanation": "State TypedDict๋Š” ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ ์ฝ๊ณ  ์“ฐ๋Š” ๊ณต์œ  ์ƒํƒœ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋…ธ๋“œ ํ•จ์ˆ˜๋Š” `state: State`๋ฅผ ๋ฐ›์•„ ์ผ๋ถ€ ํ‚ค๋ฅผ ์—…๋ฐ์ดํŠธํ•œ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.",
555
+ "source_id": "official-docs-langgraph"
556
+ },
557
+ {
558
+ "id": "docs-q59",
559
+ "lesson_id": "docs-litellm",
560
+ "question_type": "multiple_choice",
561
+ "question": "LiteLLM์—์„œ Ollama ๋กœ์ปฌ ๋ชจ๋ธ์„ ํ˜ธ์ถœํ•˜๋Š” model ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹์€?",
562
+ "options": [
563
+ "\"ollama/llama3.2\"",
564
+ "\"local/llama3.2\"",
565
+ "\"llama3.2\"",
566
+ "\"ollama:llama3.2\""
567
+ ],
568
+ "answer": "\"ollama/llama3.2\"",
569
+ "explanation": "LiteLLM์˜ ๋ชจ๋ธ๋ช… ํ˜•์‹์€ `\"ํ”„๋กœ๋ฐ”์ด๋”/๋ชจ๋ธ๋ช…\"`์ž…๋‹ˆ๋‹ค. `\"ollama/llama3.2\"`, `\"anthropic/claude-3-5-sonnet-20241022\"`, `\"gpt-4o-mini\"`(OpenAI๋Š” ์˜ˆ์™ธ๋กœ ์ ‘๋‘์‚ฌ ์—†์Œ) ํ˜•์‹์„ ์”๋‹ˆ๋‹ค.",
570
+ "source_id": "official-docs-litellm"
571
+ },
572
+ {
573
+ "id": "docs-q60",
574
+ "lesson_id": "docs-litellm",
575
+ "question_type": "multiple_choice",
576
+ "question": "LiteLLM์„ LangChain ์ฒด์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
577
+ "options": [
578
+ "from langchain_community.chat_models import ChatLiteLLM๋กœ ๊ฐ€์ ธ์™€ LLM์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค",
579
+ "litellm.completion()์„ LangChain ์ฒด์ธ์— ์ง์ ‘ ๋„ฃ๋Š”๋‹ค",
580
+ "LiteLLM์€ LangChain๊ณผ ํ†ตํ•ฉ๋˜์ง€ ์•Š๋Š”๋‹ค",
581
+ "LangChain์˜ ChatOpenAI์— litellm=True ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค"
582
+ ],
583
+ "answer": "from langchain_community.chat_models import ChatLiteLLM๋กœ ๊ฐ€์ ธ์™€ LLM์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค",
584
+ "explanation": "`ChatLiteLLM`์€ LangChain์˜ ChatModel ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ LCEL ํŒŒ์ดํ”„๋ผ์ธ์— ๋ฐ”๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. `chain = prompt | ChatLiteLLM(model='ollama/llama3.2') | parser` ํ˜•์‹์œผ๋กœ ์”๋‹ˆ๋‹ค.",
585
+ "source_id": "official-docs-litellm"
586
+ },
587
+ {
588
+ "id": "docs-q61",
589
+ "lesson_id": "docs-chainlit",
590
+ "question_type": "multiple_choice",
591
+ "question": "Chainlit์—์„œ ์‚ฌ์šฉ์ž ๋ฉ”๏ฟฝ๏ฟฝ์ง€๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š”?",
592
+ "options": [
593
+ "@cl.on_message",
594
+ "@cl.on_input",
595
+ "@cl.message_handler",
596
+ "@cl.chat"
597
+ ],
598
+ "answer": "@cl.on_message",
599
+ "explanation": "`@cl.on_message`๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ `message: cl.Message`๋ฅผ ๋ฐ›์•„ `message.content`๋กœ ํ…์ŠคํŠธ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
600
+ "source_id": "official-docs-chainlit"
601
+ },
602
+ {
603
+ "id": "docs-q62",
604
+ "lesson_id": "docs-chainlit",
605
+ "question_type": "multiple_choice",
606
+ "question": "Chainlit์—์„œ LangChain ์ฒด์ธ์„ ํ˜ธ์ถœํ•  ๋•Œ ๋™๊ธฐ invoke() ๋Œ€์‹  ์จ์•ผ ํ•˜๋Š” ์ด์œ ์™€ ๋Œ€์•ˆ์€?",
607
+ "options": [
608
+ "invoke()๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ๋ธ”๋กœํ‚นํ•˜๋ฏ€๋กœ ainvoke() ๋˜๋Š” astream()์„ ์จ์•ผ ํ•œ๋‹ค",
609
+ "invoke()๊ฐ€ ๋” ๋น ๋ฅด๋ฏ€๋กœ ํ•ญ์ƒ invoke()๋ฅผ ์“ด๋‹ค",
610
+ "Chainlit์€ LangChain๊ณผ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š”๋‹ค",
611
+ "invoke()์™€ ainvoke()์˜ ์ฐจ์ด๋Š” ์—†๋‹ค"
612
+ ],
613
+ "answer": "invoke()๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ๋ธ”๋กœํ‚นํ•˜๋ฏ€๋กœ ainvoke() ๋˜๋Š” astream()์„ ์จ์•ผ ํ•œ๋‹ค",
614
+ "explanation": "Chainlit์€ asyncio ๊ธฐ๋ฐ˜์ž…๋‹ˆ๋‹ค. `invoke()`(๋™๊ธฐ)๋ฅผ `async def` ํ•จ์ˆ˜ ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ธ”๋กœํ‚น๋˜์–ด ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ์ฒ˜๋ฆฌ ๋ชป ํ•ฉ๋‹ˆ๋‹ค. `await chain.ainvoke()`๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
615
+ "source_id": "official-docs-chainlit"
616
+ },
617
+ {
618
+ "id": "docs-q63",
619
+ "lesson_id": "docs-redis",
620
+ "question_type": "multiple_choice",
621
+ "question": "์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์œ ์ง€๋˜๋„๋ก ChatMessageHistory๋ฅผ ๊ต์ฒดํ•˜๋Š” ํด๋ž˜์Šค๋Š”?",
622
+ "options": [
623
+ "RedisChatMessageHistory",
624
+ "PersistentChatMessageHistory",
625
+ "SQLChatMessageHistory",
626
+ "FileChatMessageHistory"
627
+ ],
628
+ "answer": "RedisChatMessageHistory",
629
+ "explanation": "`RedisChatMessageHistory(session_id, url='redis://localhost:6379')`๋Š” ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ Redis์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด `ChatMessageHistory`(์ธ๋ฉ”๋ชจ๋ฆฌ)์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•ด `get_session_history` ํ•จ์ˆ˜๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
630
+ "source_id": "official-docs-redis"
631
+ },
632
+ {
633
+ "id": "docs-q64",
634
+ "lesson_id": "docs-redis",
635
+ "question_type": "multiple_choice",
636
+ "question": "Redis์—์„œ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ๋ฌดํ•œํžˆ ์Œ“์ด์ง€ ์•Š๋„๋ก ์ž๋™ ๋งŒ๋ฃŒ๋ฅผ ์„ค์ •ํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋Š”?",
637
+ "options": [
638
+ "ttl (์ดˆ ๋‹จ์œ„)",
639
+ "expire (๋ถ„ ๋‹จ์œ„)",
640
+ "max_messages",
641
+ "timeout"
642
+ ],
643
+ "answer": "ttl (์ดˆ ๋‹จ์œ„)",
644
+ "explanation": "`RedisChatMessageHistory(session_id, url, ttl=3600)`์œผ๋กœ 1์‹œ๊ฐ„(3600์ดˆ) ํ›„ ์ž๋™ ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค. TTL์ด ์—†์œผ๋ฉด ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ๋ฌดํ•œํžˆ ์Œ“์—ฌ ํ† ํฐ ์ดˆ๊ณผ์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.",
645
+ "source_id": "official-docs-redis"
646
+ },
647
+ {
648
+ "id": "docs-q65",
649
+ "lesson_id": "docs-unstructured",
650
+ "question_type": "multiple_choice",
651
+ "question": "Unstructured๋กœ PDF ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋ ค๋ฉด ์ถ”๊ฐ€๋กœ ์„ค์น˜ํ•ด์•ผ ํ•˜๋Š” ํŒจํ‚ค์ง€๋Š”?",
652
+ "options": [
653
+ "pip install unstructured[pdf]",
654
+ "pip install unstructured-pdf",
655
+ "pip install pdfminer",
656
+ "pip install unstructured๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค"
657
+ ],
658
+ "answer": "pip install unstructured[pdf]",
659
+ "explanation": "`pip install unstructured`๋Š” ๊ธฐ๋ณธ ํŒจํ‚ค์ง€๋งŒ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. PDF ์ฒ˜๋ฆฌ๋Š” `unstructured[pdf]`, ์ด๋ฏธ์ง€ OCR์€ `unstructured[local-inference]`์ฒ˜๋Ÿผ ๋ณ„๋„ extra๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
660
+ "source_id": "official-docs-unstructured"
661
+ },
662
+ {
663
+ "id": "docs-q66",
664
+ "lesson_id": "docs-unstructured",
665
+ "question_type": "multiple_choice",
666
+ "question": "Unstructured๋ฅผ `mode='elements'`๋กœ ๋กœ๋“œํ•  ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ถ”๊ฐ€ ์ •๋ณด๋Š”?",
667
+ "options": [
668
+ "Title, NarrativeText, Table ๋“ฑ ์š”์†Œ ํƒ€์ž…์ด metadata์— ์ €์žฅ๋œ๋‹ค",
669
+ "OCR ์ •ํ™•๋„๊ฐ€ ๋†’์•„์ง„๋‹ค",
670
+ "์ด๋ฏธ์ง€ ํŒŒ์ผ๋„ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜๋œ๋‹ค",
671
+ "์ฒญํฌ ๋ถ„ํ• ์ด ์ž๋™์œผ๋กœ ๋œ๋‹ค"
672
+ ],
673
+ "answer": "Title, NarrativeText, Table ๋“ฑ ์š”์†Œ ํƒ€์ž…์ด metadata์— ์ €์žฅ๋œ๋‹ค",
674
+ "explanation": "`mode='elements'`๋กœ ๋กœ๋“œํ•˜๋ฉด ๊ฐ Document์— `metadata['category']`๋กœ ์š”์†Œ ํƒ€์ž…์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํ—ค๋”๋ฅผ ์ฒญํฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜ ํ…Œ์ด๋ธ”๋งŒ ๋ณ„๋„ ์ถ”์ถœํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
675
+ "source_id": "official-docs-unstructured"
676
+ },
677
+ {
678
+ "id": "docs-q45",
679
+ "lesson_id": "docs-document-loaders",
680
+ "question_type": "multiple_choice",
681
+ "question": "LangChain `DirectoryLoader`๋กœ ํ•˜์œ„ ํด๋”๊นŒ์ง€ ๋ชจ๋“  `.md` ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋Š” glob ํŒจํ„ด์€?",
682
+ "options": [
683
+ "**/*.md",
684
+ "*.md",
685
+ "./**md",
686
+ "**md"
687
+ ],
688
+ "answer": "**/*.md",
689
+ "explanation": "`**/*.md`๋Š” ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์™€ ๋ชจ๋“  ํ•˜์œ„ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ `.md` ํŒŒ์ผ์„ ์žฌ๊ท€ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. `*.md`๋Š” ํ˜„์žฌ ํด๋”๋งŒ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
690
+ "source_id": "official-docs-langchain"
691
+ },
692
+ {
693
+ "id": "docs-q46",
694
+ "lesson_id": "docs-document-loaders",
695
+ "question_type": "multiple_choice",
696
+ "question": "LangChain Document ๊ฐ์ฒด์—์„œ ํŒŒ์ผ ๊ฒฝ๋กœ(์ถœ์ฒ˜)๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋Š”?",
697
+ "options": [
698
+ "doc.metadata['source']",
699
+ "doc.source",
700
+ "doc.file_path",
701
+ "doc.metadata.path"
702
+ ],
703
+ "answer": "doc.metadata['source']",
704
+ "explanation": "Document Loader๋Š” ๋กœ๋“œ๋œ ๋ฌธ์„œ์˜ `metadata` ๋”•์…”๋„ˆ๋ฆฌ์— `source` ํ‚ค๋กœ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. RAG ๋‹ต๋ณ€์— ์ถœ์ฒ˜๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ์ด ๊ฐ’์„ ์”๋‹ˆ๋‹ค.",
705
+ "source_id": "official-docs-langchain"
706
+ },
707
+ {
708
+ "id": "docs-q47",
709
+ "lesson_id": "docs-summarization",
710
+ "question_type": "multiple_choice",
711
+ "question": "LangChain์—์„œ ์—ฌ๋Ÿฌ ๋‰ด์Šค ๊ธฐ์‚ฌ์ฒ˜๋Ÿผ ์ปจํ…์ŠคํŠธ ์ฐฝ๋ณด๋‹ค ๊ธด ๋ฌธ์„œ๋ฅผ ์š”์•ฝํ•  ๋•Œ ์ ํ•ฉํ•œ chain_type์€?",
712
+ "options": [
713
+ "map_reduce",
714
+ "stuff",
715
+ "direct",
716
+ "compress"
717
+ ],
718
+ "answer": "map_reduce",
719
+ "explanation": "`map_reduce`๋Š” ๊ฐ ์ฒญํฌ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝ(map)ํ•œ ํ›„ ํ•ฉ์ณ์„œ ์ตœ์ข… ์š”์•ฝ(reduce)์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. `stuff`๋Š” ์ „์ฒด๋ฅผ ํ•œ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์œผ๋ฏ€๋กœ ๊ธด ๋ฌธ์„œ์—์„œ ํ† ํฐ ์ดˆ๊ณผ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.",
720
+ "source_id": "official-docs-langchain"
721
+ },
722
+ {
723
+ "id": "docs-q48",
724
+ "lesson_id": "docs-summarization",
725
+ "question_type": "multiple_choice",
726
+ "question": "`load_summarize_chain` ์‹คํ–‰ ๊ฒฐ๊ณผ์—์„œ ์ตœ์ข… ์š”์•ฝ ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ‚ค๋Š”?",
727
+ "options": [
728
+ "result['output_text']",
729
+ "result['summary']",
730
+ "result['text']",
731
+ "result['answer']"
732
+ ],
733
+ "answer": "result['output_text']",
734
+ "explanation": "`chain.invoke(docs)`์˜ ๋ฐ˜ํ™˜๊ฐ’ ๋”•์…”๋„ˆ๋ฆฌ์—์„œ ์š”์•ฝ ๊ฒฐ๊ณผ๋Š” `output_text` ํ‚ค์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
735
+ "source_id": "official-docs-langchain"
736
+ },
737
+ {
738
+ "id": "docs-q49",
739
+ "lesson_id": "docs-sql-chain",
740
+ "question_type": "multiple_choice",
741
+ "question": "LangChain์—์„œ SQLite DB๋ฅผ `SQLDatabase`๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ๋Š”?",
742
+ "options": [
743
+ "SQLDatabase.from_uri('sqlite:///./mydb.db')",
744
+ "SQLDatabase.connect('./mydb.db')",
745
+ "sqlite3.connect('./mydb.db')",
746
+ "SQLDatabase('./mydb.db')"
747
+ ],
748
+ "answer": "SQLDatabase.from_uri('sqlite:///./mydb.db')",
749
+ "explanation": "`SQLDatabase.from_uri()`๋Š” SQLAlchemy ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. SQLite๋Š” `sqlite:///๊ฒฝ๋กœ`, PostgreSQL์€ `postgresql://user:pass@host/db` ํ˜•์‹์ž…๋‹ˆ๋‹ค.",
750
+ "source_id": "official-docs-langchain"
751
+ },
752
+ {
753
+ "id": "docs-q50",
754
+ "lesson_id": "docs-sql-chain",
755
+ "question_type": "multiple_choice",
756
+ "question": "LangChain Text-to-SQL์—์„œ `SQLDatabase`๊ฐ€ LLM์— ์ž๋™์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋Š”?",
757
+ "options": [
758
+ "DB์˜ ํ…Œ์ด๋ธ”๋ช…๊ณผ ์ปฌ๋Ÿผ ์Šคํ‚ค๋งˆ",
759
+ "DB์— ์ €์žฅ๋œ ์‹ค์ œ ๋ฐ์ดํ„ฐ ์ƒ˜ํ”Œ",
760
+ "SQL ์‹คํ–‰ ๊ฒฐ๊ณผ ํžˆ์Šคํ† ๋ฆฌ",
761
+ "DB ์ ‘์† ๋น„๋ฐ€๋ฒˆํ˜ธ"
762
+ ],
763
+ "answer": "DB์˜ ํ…Œ์ด๋ธ”๋ช…๊ณผ ์ปฌ๋Ÿผ ์Šคํ‚ค๋งˆ",
764
+ "explanation": "`SQLDatabase`๋Š” ์—ฐ๊ฒฐ๋œ DB์˜ ํ…Œ์ด๋ธ”๋ช…๊ณผ ๊ฐ ์ปฌ๋Ÿผ์˜ ์ด๋ฆ„ยทํƒ€์ž…์„ ์ž๋™์œผ๋กœ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. ๋•๋ถ„์— LLM์ด ์‹ค์ œ ์กด์žฌํ•˜๋Š” ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ๋งŒ ์ฐธ์กฐํ•˜๋Š” SQL์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
765
+ "source_id": "official-docs-langchain"
766
+ },
767
+ {
768
+ "id": "docs-q39",
769
+ "lesson_id": "docs-dotenv",
770
+ "question_type": "multiple_choice",
771
+ "question": "python-dotenv์—์„œ `.env` ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€?",
772
+ "options": [
773
+ "from dotenv import load_dotenv; load_dotenv()",
774
+ "import dotenv; dotenv.read('.env')",
775
+ "os.environ.load('.env')",
776
+ ".env ํŒŒ์ผ์€ ์ž๋™์œผ๋กœ ๋กœ๋“œ๋œ๋‹ค"
777
+ ],
778
+ "answer": "from dotenv import load_dotenv; load_dotenv()",
779
+ "explanation": "`load_dotenv()`๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•ด์•ผ `.env` ํŒŒ์ผ์˜ ๋ณ€์ˆ˜๊ฐ€ `os.environ`์— ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ƒ๋‹จ์—์„œ ๊ฐ€์žฅ ๋จผ์ € ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.",
780
+ "source_id": "official-docs-python"
781
+ },
782
+ {
783
+ "id": "docs-q40",
784
+ "lesson_id": "docs-dotenv",
785
+ "question_type": "multiple_choice",
786
+ "question": ".env ํŒŒ์ผ์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ณด์•ˆ์„ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€?",
787
+ "options": [
788
+ ".env๋ฅผ .gitignore์— ์ถ”๊ฐ€ํ•˜๊ณ  .env.example์„ ์ปค๋ฐ‹ํ•œ๋‹ค",
789
+ ".env๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ์ €์žฅ์†Œ์— ์ปค๋ฐ‹ํ•œ๋‹ค",
790
+ ".env ํŒŒ์ผ๋ช…์„ ๋‹ค๋ฅด๊ฒŒ ์ง€์–ด ์ˆจ๊ธด๋‹ค",
791
+ "API ํ‚ค๋ฅผ ์งง๊ฒŒ ์ค„์—ฌ์„œ ์ €์žฅํ•œ๋‹ค"
792
+ ],
793
+ "answer": ".env๋ฅผ .gitignore์— ์ถ”๊ฐ€ํ•˜๊ณ  .env.example์„ ์ปค๋ฐ‹ํ•œ๋‹ค",
794
+ "explanation": "`.env`๋Š” ์ ˆ๋Œ€ Git์— ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `.env.example`์—๋Š” ํ‚ค ์ด๋ฆ„๋งŒ(๊ฐ’ ์—†์ด) ์ ์–ด ์–ด๋–ค ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•œ์ง€ ๋ฌธ์„œํ™”ํ•˜๋Š” ์šฉ๋„๋กœ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.",
795
+ "source_id": "official-docs-python"
796
+ },
797
+ {
798
+ "id": "docs-q41",
799
+ "lesson_id": "docs-prompt-template",
800
+ "question_type": "multiple_choice",
801
+ "question": "LangChain์—์„œ ์‹œ์Šคํ…œ ์—ญํ• ๊ณผ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ๋ถ„ํ•ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ํด๋ž˜์Šค๋Š”?",
802
+ "options": [
803
+ "ChatPromptTemplate",
804
+ "PromptTemplate",
805
+ "SystemPrompt",
806
+ "MessageTemplate"
807
+ ],
808
+ "answer": "ChatPromptTemplate",
809
+ "explanation": "`ChatPromptTemplate.from_messages([(\"system\", \"...\"), (\"human\", \"{question}\")])` ํ˜•์‹์œผ๋กœ ์—ญํ• ๋ณ„ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ์กฐํ™”ํ•ฉ๋‹ˆ๋‹ค. `PromptTemplate`์€ ์—ญํ•  ๊ตฌ๋ถ„ ์—†๋Š” ๋‹จ์ˆœ ๋ฌธ์ž์—ด ํฌ๋งท์šฉ์ž…๋‹ˆ๋‹ค.",
810
+ "source_id": "official-docs-langchain"
811
+ },
812
+ {
813
+ "id": "docs-q42",
814
+ "lesson_id": "docs-prompt-template",
815
+ "question_type": "multiple_choice",
816
+ "question": "ChatPromptTemplate์—์„œ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
817
+ "options": [
818
+ "(\"placeholder\", \"{chat_history}\") ๋˜๋Š” MessagesPlaceholder(variable_name=\"chat_history\")๋ฅผ messages์— ์ถ”๊ฐ€ํ•œ๋‹ค",
819
+ "chat_history ๋ฌธ์ž์—ด์„ system ๋ฉ”์‹œ์ง€์— ์ง์ ‘ ํฌํ•จํ•œ๋‹ค",
820
+ "ํžˆ์Šคํ† ๋ฆฌ๋Š” LLM์ด ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ๋ณ„๋„ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋‹ค",
821
+ "HistoryTemplate ํด๋ž˜์Šค๋ฅผ ๋ณ„๋„๋กœ ์‚ฌ์šฉํ•œ๋‹ค"
822
+ ],
823
+ "answer": "(\"placeholder\", \"{chat_history}\") ๋˜๋Š” MessagesPlaceholder(variable_name=\"chat_history\")๋ฅผ messages์— ์ถ”๊ฐ€ํ•œ๋‹ค",
824
+ "explanation": "`MessagesPlaceholder`๋Š” ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ ์ „์ฒด๋ฅผ ๊ทธ ์ž๋ฆฌ์— ํŽผ์ณ์ค๋‹ˆ๋‹ค. RunnableWithMessageHistory์˜ `history_messages_key`์™€ placeholder์˜ ๋ณ€์ˆ˜๋ช…์ด ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
825
+ "source_id": "official-docs-langchain"
826
+ },
827
+ {
828
+ "id": "docs-q43",
829
+ "lesson_id": "docs-conversation-memory",
830
+ "question_type": "multiple_choice",
831
+ "question": "LangChain์—์„œ ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ฒด์ธ์— ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ํด๋ž˜์Šค๋Š”?",
832
+ "options": [
833
+ "RunnableWithMessageHistory",
834
+ "ConversationChain",
835
+ "ChatMemoryBuffer",
836
+ "HistoryRunnable"
837
+ ],
838
+ "answer": "RunnableWithMessageHistory",
839
+ "explanation": "`RunnableWithMessageHistory`๋Š” ๊ธฐ์กด ์ฒด์ธ์„ ๊ฐ์‹ธ `session_id`๋ณ„๋กœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `get_session_history` ํ•จ์ˆ˜๋กœ ์ €์žฅ์†Œ๋ฅผ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ด์ „ ๋Œ€ํ™”๊ฐ€ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.",
840
+ "source_id": "official-docs-langchain"
841
+ },
842
+ {
843
+ "id": "docs-q44",
844
+ "lesson_id": "docs-conversation-memory",
845
+ "question_type": "multiple_choice",
846
+ "question": "RunnableWithMessageHistory์—์„œ ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๋™์ผํ•œ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์›์ธ์€?",
847
+ "options": [
848
+ "invoke ์‹œ config์— ๋™์ผํ•œ session_id๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค",
849
+ "ChatMessageHistory๋ฅผ ํด๋ž˜์Šค ๋ ˆ๋ฒจ์—์„œ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ–ˆ๋‹ค",
850
+ "get_session_history ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š์•˜๋‹ค",
851
+ "RunnableWithMessageHistory ๋Œ€์‹  ์ฒด์ธ์„ ์ง์ ‘ ์‚ฌ์šฉํ–ˆ๋‹ค"
852
+ ],
853
+ "answer": "invoke ์‹œ config์— ๋™์ผํ•œ session_id๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค",
854
+ "explanation": "`config={\"configurable\": {\"session_id\": \"fixed-id\"}}`์ฒ˜๋Ÿผ ๊ณ ์ • ID๋ฅผ ์“ฐ๋ฉด ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ™์€ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์”๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ณ ์œ ํ•œ session_id(UUID ๋“ฑ)๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
855
+ "source_id": "official-docs-langchain"
856
+ },
857
+ {
858
+ "id": "docs-q38",
859
+ "lesson_id": "docs-mcp",
860
+ "question_type": "multiple_choice",
861
+ "question": "Claude Desktop์— ๋กœ์ปฌ MCP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด ์–ด๋””์— ๋“ฑ๋กํ•ด์•ผ ํ•˜๋Š”๊ฐ€?",
862
+ "options": [
863
+ "~/Library/Application Support/Claude/claude_desktop_config.json",
864
+ "~/.claude/mcp_servers.json",
865
+ "CLAUDE.md ํŒŒ์ผ",
866
+ "ํ™˜๊ฒฝ๋ณ€์ˆ˜ MCP_SERVER_PATH"
867
+ ],
868
+ "answer": "~/Library/Application Support/Claude/claude_desktop_config.json",
869
+ "explanation": "Claude Desktop์˜ MCP ์„œ๋ฒ„ ๋ชฉ๋ก์€ `claude_desktop_config.json`์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ด๋ฆ„, ์‹คํ–‰ ๋ช…๋ น์–ด(`command`), ์ธ์ˆ˜(`args`)๋ฅผ JSON์œผ๋กœ ์ •์˜ํ•˜๋ฉด ์•ฑ ์žฌ์‹œ์ž‘ ํ›„ ์ž๋™ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
870
+ "source_id": "official-docs-mcp"
871
+ },
872
+ {
873
+ "id": "docs-q67",
874
+ "lesson_id": "docs-vectorstore",
875
+ "question_type": "multiple_choice",
876
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•  ๋•Œ๋งˆ๋‹ค ๋ฒกํ„ฐ ์ธ๋ฑ์Šค๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœํŒ€์—์„œ `save_local()` ํ˜ธ์ถœ์„ ์ข…์ข… ๋น ๋œจ๋ฆฝ๋‹ˆ๋‹ค. ์–ด๋–ค VectorStore๋ฅผ ์„ ํƒํ•˜๊ณ  ๊ทธ ์ด์œ ๋Š”?",
877
+ "options": [
878
+ "Chroma โ€” persist_directory๋งŒ ์ง€์ •ํ•˜๋ฉด save_local() ์—†์ด ์ž๋™ ์ €์žฅ๋˜๋ฏ€๋กœ",
879
+ "FAISS โ€” ์„ฑ๋Šฅ์ด ๋” ๋น ๋ฅด๊ณ  ํด๋ผ์šฐ๋“œ ๋ฐฐํฌ์— ์œ ๋ฆฌํ•˜๋ฏ€๋กœ",
880
+ "pgvector โ€” PostgreSQL ๊ธฐ๋ฐ˜์ด๋ผ ์•ˆ์ •์„ฑ์ด ๋†’์œผ๋ฏ€๋กœ",
881
+ "Qdrant โ€” ๋Œ€๊ทœ๋ชจ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ"
882
+ ],
883
+ "answer": "Chroma โ€” persist_directory๋งŒ ์ง€์ •ํ•˜๋ฉด save_local() ์—†์ด ์ž๋™ ์ €์žฅ๋˜๋ฏ€๋กœ",
884
+ "explanation": "์ด ์ƒํ™ฉ์˜ ๊ทผ๋ณธ ๋ฌธ์ œ๋Š” ์ˆ˜๋™ ์ €์žฅ ํ˜ธ์ถœ์„ ๋น ๋œจ๋ฆฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Chroma๋Š” `persist_directory='./chroma_db'`๋งŒ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ €์žฅ๋˜๋ฏ€๋กœ ์‹ค์ˆ˜ ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค. FAISS๋Š” ์„ฑ๋Šฅ์ด ์ข‹์ง€๋งŒ `save_local()`์„ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœํ•ด์•ผ ํ•ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.",
885
+ "source_id": "official-docs-chromadb",
886
+ "difficulty": "medium"
887
+ },
888
+ {
889
+ "id": "docs-q68",
890
+ "lesson_id": "docs-sentence-transformers",
891
+ "question_type": "multiple_choice",
892
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** RAG ์‹œ์Šคํ…œ์— 20๋งŒ ๊ฐœ์˜ ๋ฌธ์„œ๋ฅผ ์ธ๋ฑ์‹ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. OpenAI ์ž„๋ฒ ๋”ฉ API๋ฅผ ์“ฐ๋ฉด ๋น„์šฉ์ด ์˜ˆ์ƒ๋ณด๋‹ค 10๋ฐฐ ๋‚˜์˜ฌ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์„ ํƒ์ด ๊ฐ€์žฅ ์ ํ•ฉํ•œ๊ฐ€?",
893
+ "options": [
894
+ "sentence-transformers์˜ all-MiniLM-L6-v2๋กœ ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ โ€” ๋ฌด๋ฃŒ์ด๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•˜๋ฏ€๋กœ",
895
+ "OpenAI ์ž„๋ฒ ๋”ฉ ์œ ์ง€ โ€” ํ’ˆ์งˆ์ด ์ค‘์š”ํ•˜๋ฏ€๋กœ ๋น„์šฉ์„ ๊ฐ์ˆ˜ํ•œ๋‹ค",
896
+ "์ž„๋ฒ ๋”ฉ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰๋งŒ ์“ด๋‹ค",
897
+ "Chroma๋กœ ๊ต์ฒดํ•˜๋ฉด ์ž„๋ฒ ๋”ฉ ๋น„์šฉ์ด ์ ˆ๊ฐ๋œ๋‹ค"
898
+ ],
899
+ "answer": "sentence-transformers์˜ all-MiniLM-L6-v2๋กœ ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ โ€” ๋ฌด๋ฃŒ์ด๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•˜๋ฏ€๋กœ",
900
+ "explanation": "`HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')`๋Š” OpenAI Embeddings์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•ด ์ฝ”๋“œ๋ฅผ ํ•œ ์ค„๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์‹คํ–‰์ด๋ฏ€๋กœ API ๋น„์šฉ์ด ์—†๊ณ , ๋Œ€์šฉ๋Ÿ‰ ์ธ๋ฑ์‹ฑ์— ๊ฒฝ์ œ์ ์ž…๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด ๋งŽ์œผ๋ฉด `jhgan/ko-sroberta-multitask`๋„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.",
901
+ "source_id": "official-docs-huggingface",
902
+ "difficulty": "medium"
903
+ },
904
+ {
905
+ "id": "docs-q69",
906
+ "lesson_id": "docs-summarization",
907
+ "question_type": "multiple_choice",
908
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ๋‰ด์Šค ๊ธฐ์‚ฌ 50๊ฐœ๋ฅผ ๋™์‹œ์— LLM์— ๋„ฃ์–ด ์š”์•ฝํ•˜๋ ค๋‹ค ํ† ํฐ ์ดˆ๊ณผ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ ์ ˆํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€?",
909
+ "options": [
910
+ "load_summarize_chain(llm, chain_type='map_reduce') ์‚ฌ์šฉ โ€” ๊ธฐ์‚ฌ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝ ํ›„ ํ•ฉ์น˜๋ฏ€๋กœ ํ† ํฐ ์ดˆ๊ณผ๊ฐ€ ์—†์Œ",
911
+ "๋ชจ๋“  ๊ธฐ์‚ฌ๋ฅผ ํ•˜๋‚˜์˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ๋Š” stuff ๋ฐฉ์‹์„ ์œ ์ง€ํ•˜๋˜ LLM ๋ชจ๋ธ์„ ๊ต์ฒด",
912
+ "๊ธฐ์‚ฌ ๊ฐœ์ˆ˜๋ฅผ 5๊ฐœ๋กœ ์ค„์—ฌ stuff ๋ฐฉ์‹ ์œ ์ง€",
913
+ "summarization ๋Œ€์‹  RAG๋กœ ๊ธฐ์‚ฌ๋ฅผ ๊ฒ€์ƒ‰ํ•ด ์š”์•ฝ"
914
+ ],
915
+ "answer": "load_summarize_chain(llm, chain_type='map_reduce') ์‚ฌ์šฉ โ€” ๊ธฐ์‚ฌ๋ฅผ ๊ฐœ๋ณ„ ์š”์•ฝ ํ›„ ํ•ฉ์น˜๋ฏ€๋กœ ํ† ํฐ ์ดˆ๊ณผ๊ฐ€ ์—†์Œ",
916
+ "explanation": "`map_reduce`๋Š” ๊ฐ ๊ธฐ์‚ฌ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์š”์•ฝ(map)ํ•œ ๋’ค ๊ฐœ๋ณ„ ์š”์•ฝ๋“ค์„ ํ•ฉ์ณ ์ตœ์ข… ์š”์•ฝ(reduce)์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ธฐ์‚ฌ๊ฐ€ 100๊ฐœ๋“  1000๊ฐœ๋“  ํ† ํฐ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ ๊ต์ฒด๋‚˜ ๊ธฐ์‚ฌ ์ˆ˜ ๊ฐ์†Œ๋Š” ๊ทผ๋ณธ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹™๋‹ˆ๋‹ค.",
917
+ "source_id": "official-docs-langchain",
918
+ "difficulty": "medium"
919
+ },
920
+ {
921
+ "id": "docs-q70",
922
+ "lesson_id": "docs-langsmith",
923
+ "question_type": "multiple_choice",
924
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** RAG ์ฑ—๋ด‡์ด ์—‰๋šฑํ•œ ๋‹ต์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๊ฒ€์ƒ‰ ๋‹จ๊ณ„์ธ์ง€, ํ”„๋กฌํ”„ํŠธ ๋ Œ๋”๋ง ๋‹จ๊ณ„์ธ์ง€, LLM ์ƒ์„ฑ ๋‹จ๊ณ„์ธ์ง€ ํŒŒ์•…์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. `print()`๋กœ ๋””๋ฒ„๊น… ์ค‘์ธ๋ฐ ์–ด๋А ๋‹จ๊ณ„์ธ์ง€ ๊ตฌ๋ถ„์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋„๊ตฌ๋Š”?",
925
+ "options": [
926
+ "LangSmith โ€” ์ฒด์ธ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅ์„ ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ์‹œ๊ฐํ™”ํ•˜๋ฏ€๋กœ",
927
+ "Python logging ๋ชจ๋“ˆ๋กœ ๋” ์ƒ์„ธํ•œ ๋กœ๊ทธ ์ถœ๋ ฅ",
928
+ "LLM ๋ชจ๋ธ์„ ๋” ๊ฐ•๋ ฅํ•œ ๊ฒƒ์œผ๋กœ ๊ต์ฒด",
929
+ "chunk_size๋ฅผ ์ค„์—ฌ ๊ฒ€์ƒ‰ ์ •ํ™•๋„ ํ–ฅ์ƒ"
930
+ ],
931
+ "answer": "LangSmith โ€” ์ฒด์ธ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅ์„ ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ์‹œ๊ฐํ™”ํ•˜๋ฏ€๋กœ",
932
+ "explanation": "LangSmith๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3๊ฐœ(`LANGCHAIN_TRACING_V2=true`, `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT`) ์„ค์ •๋งŒ์œผ๋กœ ์ฒด์ธ ์‹คํ–‰๋งˆ๋‹ค retriever ์ž…์ถœ๋ ฅ, ๋ Œ๋”๋ง๋œ ํ”„๋กฌํ”„ํŠธ, LLM ์‘๋‹ต์„ ๋‹จ๊ณ„๋ณ„๋กœ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์–ด๋А ๋‹จ๊ณ„๊ฐ€ ๋ฌธ์ œ์ธ์ง€ ์ฆ‰์‹œ ํŠน์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
933
+ "source_id": "official-docs-langchain",
934
+ "difficulty": "medium"
935
+ },
936
+ {
937
+ "id": "docs-q71",
938
+ "lesson_id": "docs-guardrails",
939
+ "question_type": "multiple_choice",
940
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ์„œ๋น„์Šค ์‚ฌ์šฉ์ž๊ฐ€ \"์š•์„ค์„ ์„ž์–ด ์š”์•ฝํ•ด์ค˜\"๋ผ๊ณ  ์ž…๋ ฅํ–ˆ๋”๋‹ˆ LLM์ด ๊ทธ๋Œ€๋กœ ๋”ฐ๋ž์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์— '์š•์„ค ๊ธˆ์ง€'๋ฅผ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ์šฐํšŒ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๊ฐ•๋ ฅํ•œ ๋Œ€์‘์€?",
941
+ "options": [
942
+ "Guardrails AI์˜ ToxicLanguage Guard๋ฅผ LLM ์ถœ๋ ฅ์— ์ ์šฉ โ€” ํ™•๋ฅ ์ ์œผ๋กœ ์œ ํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ์‹ค์งˆ์ ์œผ๋กœ ์ฐจ๋‹จ",
943
+ "์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋” ๊ธธ๊ณ  ์ž์„ธํ•˜๊ฒŒ ์ž‘์„ฑ",
944
+ "๋” ๊ฐ•๋ ฅํ•œ LLM ๋ชจ๋ธ๋กœ ๊ต์ฒด",
945
+ "์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ธธ์ด๋ฅผ 100์ž๋กœ ์ œํ•œ"
946
+ ],
947
+ "answer": "Guardrails AI์˜ ToxicLanguage Guard๋ฅผ LLM ์ถœ๋ ฅ์— ์ ์šฉ โ€” ํ™•๋ฅ ์ ์œผ๋กœ ์œ ํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ์‹ค์งˆ์ ์œผ๋กœ ์ฐจ๋‹จ",
948
+ "explanation": "์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ๋Š” ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜์œผ๋กœ ์šฐํšŒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Guardrails AI๋Š” LLM ์ถœ๋ ฅ์„ ๋ณ„๋„ ๋ถ„๋ฅ˜ ๋ชจ๋ธ๋กœ ๊ฒ€์ฆํ•ด ์ž„๊ณ„๊ฐ’ ์ด์ƒ์ด๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. `on_fail='reask'`๋กœ ์„ค์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์žฌ์ž‘์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ๋ณด๋‹ค ์‹ค์งˆ์ ์ธ ๋ณดํ˜ธ ์ˆ˜๋‹จ์ž…๋‹ˆ๋‹ค.",
949
+ "source_id": "official-docs-guardrails",
950
+ "difficulty": "hard"
951
+ },
952
+ {
953
+ "id": "docs-q72",
954
+ "lesson_id": "docs-redis",
955
+ "question_type": "multiple_choice",
956
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ์ฑ—๋ด‡ ์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ–ˆ๋”๋‹ˆ ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ๋Œ€ํ™” ๊ธฐ๋ก์ด ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ์ธ๋ฉ”๋ชจ๋ฆฌ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€?",
957
+ "options": [
958
+ "get_session_history ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ RedisChatMessageHistory๋กœ ๊ต์ฒด โ€” ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ",
959
+ "RunnableWithMessageHistory๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋งค ์š”์ฒญ๋งˆ๋‹ค ํžˆ์Šคํ† ๋ฆฌ๋ฅผ DB์— ์ง์ ‘ ์ €์žฅ",
960
+ "Streamlit session_state์— ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ €์žฅ",
961
+ "๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ์—†์ด ๋‹จ์ผ ํ„ด์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ์žฌ์„ค๊ณ„"
962
+ ],
963
+ "answer": "get_session_history ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ RedisChatMessageHistory๋กœ ๊ต์ฒด โ€” ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ",
964
+ "explanation": "`RunnableWithMessageHistory`๋Š” `get_session_history` ํ•จ์ˆ˜๋งŒ ๋ณด๊ณ  ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ `RedisChatMessageHistory(session_id, url='redis://localhost:6379')`๋กœ ๋ฐ”๊พธ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ฝ”๋“œ๋Š” ์ „ํ˜€ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.",
965
+ "source_id": "official-docs-redis",
966
+ "difficulty": "hard"
967
+ },
968
+ {
969
+ "id": "docs-q73",
970
+ "lesson_id": "docs-litellm",
971
+ "question_type": "multiple_choice",
972
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ๋กœ์ปฌ Ollama๋กœ ๊ฐœ๋ฐœํ•˜๊ณ  ํ”„๋กœ๋•์…˜์—๋Š” Anthropic Claude๋ฅผ ์“ธ ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ๋งค๋ฒˆ LLM ๊ต์ฒด ์‹œ ์ฝ”๋“œ๋ฅผ ์ตœ์†Œ๋กœ ์ˆ˜์ •ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š”๊ฐ€?",
973
+ "options": [
974
+ "LiteLLM completion()์„ ์‚ฌ์šฉํ•˜๊ณ  model ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ณ€๊ฒฝ โ€” ์ฝ”๋“œ ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋Œ€๋กœ",
975
+ "OllamaLLM๊ณผ ChatAnthropic์„ if๋ฌธ์œผ๋กœ ๋ถ„๊ธฐ",
976
+ "๊ฐœ๋ฐœ/ํ”„๋กœ๋•์…˜๋งˆ๋‹ค ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์œ ์ง€",
977
+ "LangChain์˜ ChatOpenAI๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ Ollama๋Š” ๋ณ„๋„ ๋ธŒ๋žœ์น˜์—์„œ ๊ด€๋ฆฌ"
978
+ ],
979
+ "answer": "LiteLLM completion()์„ ์‚ฌ์šฉํ•˜๊ณ  model ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ณ€๊ฒฝ โ€” ์ฝ”๋“œ ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋Œ€๋กœ",
980
+ "explanation": "LiteLLM์€ `completion(model='ollama/llama3.2', ...)`๊ณผ `completion(model='anthropic/claude-3-5-sonnet-20241022', ...)`์˜ ์ฝ”๋“œ๊ฐ€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. model ํŒŒ๋ผ๋ฏธํ„ฐ ํ•œ ์ค„๋งŒ ๋ฐ”๊พธ๋ฉด ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. if๋ฌธ ๋ถ„๊ธฐ๋‚˜ ๋ณ„๋„ ๋ธŒ๋žœ์น˜ ๊ด€๋ฆฌ๋Š” ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์ด ๋†’์Šต๋‹ˆ๋‹ค.",
981
+ "source_id": "official-docs-litellm",
982
+ "difficulty": "medium"
983
+ },
984
+ {
985
+ "id": "docs-q74",
986
+ "lesson_id": "docs-langgraph",
987
+ "question_type": "multiple_choice",
988
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** RAG ์ฑ—๋ด‡์— ๋‹ค์Œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: \"๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํ’ˆ์งˆ์ด ๋‚ฎ์œผ๋ฉด ๋‹ค๋ฅธ ๊ฒ€์ƒ‰์–ด๋กœ ์žฌ์‹œ๋„ํ•˜๊ณ , 3๋ฒˆ ์ด์ƒ ์‹คํŒจํ•˜๋ฉด '์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค'๋ฅผ ๋ฐ˜ํ™˜.\" LCEL ์ฒด์ธ๋งŒ์œผ๋กœ ์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?",
989
+ "options": [
990
+ "๋ถˆ๊ฐ€๋Šฅ โ€” LCEL์€ ์„ ํ˜•์ด๋ผ ๋ฃจํ”„์™€ ์กฐ๊ฑด ๋ถ„๊ธฐ๊ฐ€ ์—†๋‹ค. LangGraph๋ฅผ ์จ์•ผ ํ•œ๋‹ค",
991
+ "๊ฐ€๋Šฅ โ€” LCEL์˜ RunnableBranch๋กœ ์กฐ๊ฑด ๋ถ„๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค",
992
+ "๊ฐ€๋Šฅ โ€” Python while๋ฌธ์„ chain.invoke() ๋ฐ”๊นฅ์— ์“ฐ๋ฉด ๋œ๋‹ค",
993
+ "๊ฐ€๋Šฅ โ€” retry ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ chain์— ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค"
994
+ ],
995
+ "answer": "๋ถˆ๊ฐ€๋Šฅ โ€” LCEL์€ ์„ ํ˜•์ด๋ผ ๋ฃจํ”„์™€ ์กฐ๊ฑด ๋ถ„๊ธฐ๊ฐ€ ์—†๋‹ค. LangGraph๋ฅผ ์จ์•ผ ํ•œ๋‹ค",
996
+ "explanation": "LCEL์€ Aโ†’Bโ†’C ์„ ํ˜• ํŒŒ์ดํ”„๋ผ์ธ์ž…๋‹ˆ๋‹ค. 'ํ’ˆ์งˆ ํŒ๋‹จ โ†’ ์žฌ์‹œ๋„ ์—ฌ๋ถ€ ๊ฒฐ์ • โ†’ ๋ฃจํ”„' ๊ฐ™์€ ๋ฐ˜๋ณต/๋ถ„๊ธฐ ๋กœ์ง์€ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. LangGraph์˜ `add_conditional_edges`์™€ ๋ฃจํ”„ ๊ตฌ์กฐ๊ฐ€ ์ด๋Ÿฐ ์—์ด์ „ํŠธ ํ–‰๋™ ํŒจํ„ด์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
997
+ "source_id": "official-docs-langgraph",
998
+ "difficulty": "hard"
999
+ },
1000
+ {
1001
+ "id": "docs-q75",
1002
+ "lesson_id": "docs-airflow",
1003
+ "question_type": "multiple_choice",
1004
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** ๋‰ด์Šค ์š”์•ฝ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์™„์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งค์ผ ์˜ค์ „ 6์‹œ์— ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜๊ณ  ์‹คํŒจ ์‹œ ์•Œ๋ฆผ์„ ๋ฐ›๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‹ด๋‹น์ž ์—†์ด๋„ ์ฃผ๋ง์— ๋™์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋„๊ตฌ๋Š”?",
1005
+ "options": [
1006
+ "Airflow โ€” DAG๋กœ ์Šค์ผ€์ค„ ์ •์˜, ์‹คํŒจ ์•Œ๋ฆผ, ์ด์ „ ์‹คํ–‰ ์ด๋ ฅ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ",
1007
+ "cron ์Šคํฌ๋ฆฝํŠธ โ€” ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ์ถ”๊ฐ€ ์„ค์น˜๊ฐ€ ์—†์Œ",
1008
+ "Streamlit ๋ฒ„ํŠผ โ€” ์ˆ˜๋™ ์‹คํ–‰์ด์ง€๋งŒ UI๊ฐ€ ์žˆ์Œ",
1009
+ "GitHub Actions โ€” CI/CD ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅ"
1010
+ ],
1011
+ "answer": "Airflow โ€” DAG๋กœ ์Šค์ผ€์ค„ ์ •์˜, ์‹คํŒจ ์•Œ๋ฆผ, ์ด์ „ ์‹คํ–‰ ์ด๋ ฅ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ",
1012
+ "explanation": "๋‹จ์ˆœ cron๋„ ์Šค์ผ€์ค„๋ง์€ ๋˜์ง€๋งŒ ์‹คํŒจ ์‹œ ์•Œ๋ฆผ, ์ด์ „ ์‹คํ–‰ ์ด๋ ฅ ํ™•์ธ, ์žฌ์‹คํ–‰ ๊ธฐ๋Šฅ์ด ์—†์Šต๋‹ˆ๋‹ค. Airflow๋Š” `@daily` ์Šค์ผ€์ค„๋กœ ์ž๋™ ์‹คํ–‰๋˜๊ณ , ์‹คํŒจ ์‹œ ์ด๋ฉ”์ผ/Slack ์•Œ๋ฆผ, ๊ณผ๊ฑฐ ์‹คํ–‰ ์ด๋ ฅ ์กฐํšŒ, ์ˆ˜๋™ ์žฌ์‹คํ–‰์ด ๋ชจ๋‘ ์ง€์›๋ฉ๋‹ˆ๋‹ค. ๋‹ด๋‹น์ž ์—†์ด ์šด์˜ํ•˜๋Š” ํ”„๋กœ๋•์…˜ ํŒŒ์ดํ”„๋ผ์ธ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
1013
+ "source_id": "official-docs-airflow",
1014
+ "difficulty": "medium"
1015
+ },
1016
+ {
1017
+ "id": "docs-q76",
1018
+ "lesson_id": "docs-fastapi",
1019
+ "question_type": "multiple_choice",
1020
+ "question": "**[์ƒํ™ฉ ํŒ๋‹จ]** RAG ์ฑ—๋ด‡์„ ์™„์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ ํŒ€(React), ๋ชจ๋ฐ”์ผ ํŒ€(Flutter), ๋ฐ์ดํ„ฐ ํŒ€(Python)์ด ๋ชจ๋‘ ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Streamlit ์•ฑ ๊ทธ๋Œ€๋กœ ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ์š”?",
1021
+ "options": [
1022
+ "์•„๋‹ˆ์˜ค โ€” FastAPI๋กœ REST API๋ฅผ ๋งŒ๋“ค์–ด ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์ด ์ ํ•ฉ. ๊ฐ ํŒ€์ด ์ž๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ",
1023
+ "์˜ˆ โ€” Streamlit UI๋ฅผ iframe์œผ๋กœ ๊ฐ ํŒ€ ์„œ๋น„์Šค์— ์‚ฝ์ž…ํ•˜๋ฉด ๋จ",
1024
+ "์˜ˆ โ€” Streamlit์€ API ๊ธฐ๋Šฅ๋„ ์ž๋™์œผ๋กœ ์ œ๊ณตํ•จ",
1025
+ "์•„๋‹ˆ์˜ค โ€” Gradio๋กœ ๊ต์ฒดํ•ด์•ผ ๋‹ค๋ฅธ ํŒ€๊ณผ ๊ณต์œ  ๊ฐ€๋Šฅ"
1026
+ ],
1027
+ "answer": "์•„๋‹ˆ์˜ค โ€” FastAPI๋กœ REST API๋ฅผ ๋งŒ๋“ค์–ด ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์ด ์ ํ•ฉ. ๊ฐ ํŒ€์ด ์ž๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ",
1028
+ "explanation": "Streamlit์€ UI์™€ ๋กœ์ง์ด ๊ฒฐํ•ฉ๋œ ๋‹จ์ผ ์•ฑ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํŒ€์ด LLM ๊ธฐ๋Šฅ์„ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋ฉด API๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. FastAPI๋กœ `POST /ask` ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด React, Flutter, Python ์–ด๋””์„œ๋“  HTTP ์š”์ฒญ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Streamlit์€ ๊ฐœ์ธ/ํŒ€ ๋‚ด๋ถ€ ๋„๊ตฌ, FastAPI๋Š” ์„œ๋น„์Šค ํ†ตํ•ฉ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
1029
+ "source_id": "official-docs-fastapi",
1030
+ "difficulty": "medium"
1031
+ }
1032
+ ]
cert_study_app/services/docs_source_service.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Iterable
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class DocsSource:
9
+ id: str
10
+ track: str
11
+ provider: str
12
+ title: str
13
+ url: str
14
+ category: str
15
+ tags: tuple[str, ...] = ()
16
+ role: str = "reference"
17
+ enabled: bool = True
18
+
19
+
20
+ DOCS_SOURCES: tuple[DocsSource, ...] = (
21
+ DocsSource(
22
+ "manual-az104-basic",
23
+ "azure",
24
+ "Microsoft Learn",
25
+ "AZ-104 study guide",
26
+ "https://learn.microsoft.com/en-us/credentials/certifications/resources/study-guides/az-104",
27
+ "exam-guide",
28
+ ("az-104", "study-guide"),
29
+ ),
30
+ DocsSource(
31
+ "azure-vnet-overview",
32
+ "azure",
33
+ "Microsoft Learn",
34
+ "Azure Virtual Network overview",
35
+ "https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview",
36
+ "network",
37
+ ("vnet", "subnet", "networking"),
38
+ ),
39
+ DocsSource(
40
+ "azure-nsg-overview",
41
+ "azure",
42
+ "Microsoft Learn",
43
+ "Network security groups",
44
+ "https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview",
45
+ "network",
46
+ ("nsg", "security"),
47
+ ),
48
+ DocsSource(
49
+ "azure-private-endpoint",
50
+ "azure",
51
+ "Microsoft Learn",
52
+ "Private Endpoint overview",
53
+ "https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview",
54
+ "network",
55
+ ("private-link", "private-endpoint"),
56
+ ),
57
+ DocsSource(
58
+ "azure-load-balancer",
59
+ "azure",
60
+ "Microsoft Learn",
61
+ "Azure Load Balancer overview",
62
+ "https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-overview",
63
+ "network",
64
+ ("load-balancer",),
65
+ ),
66
+ DocsSource(
67
+ "azure-vm-overview",
68
+ "azure",
69
+ "Microsoft Learn",
70
+ "Azure Virtual Machines overview",
71
+ "https://learn.microsoft.com/en-us/azure/virtual-machines/overview",
72
+ "compute",
73
+ ("vm", "compute"),
74
+ ),
75
+ DocsSource(
76
+ "azure-storage-account",
77
+ "azure",
78
+ "Microsoft Learn",
79
+ "Storage account overview",
80
+ "https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview",
81
+ "storage",
82
+ ("storage-account", "blob"),
83
+ ),
84
+ DocsSource(
85
+ "azure-rbac-overview",
86
+ "azure",
87
+ "Microsoft Learn",
88
+ "Azure RBAC overview",
89
+ "https://learn.microsoft.com/en-us/azure/role-based-access-control/overview",
90
+ "identity",
91
+ ("rbac", "role"),
92
+ ),
93
+ DocsSource(
94
+ "azure-monitor-overview",
95
+ "azure",
96
+ "Microsoft Learn",
97
+ "Azure Monitor overview",
98
+ "https://learn.microsoft.com/en-us/azure/azure-monitor/overview",
99
+ "monitor",
100
+ ("monitor", "log-analytics"),
101
+ ),
102
+ DocsSource(
103
+ "azure-backup-overview",
104
+ "azure",
105
+ "Microsoft Learn",
106
+ "Azure Backup overview",
107
+ "https://learn.microsoft.com/en-us/azure/backup/backup-overview",
108
+ "backup",
109
+ ("backup", "recovery"),
110
+ ),
111
+ DocsSource(
112
+ "azure-policy-overview",
113
+ "azure",
114
+ "Microsoft Learn",
115
+ "Azure Policy overview",
116
+ "https://learn.microsoft.com/en-us/azure/governance/policy/overview",
117
+ "governance",
118
+ ("policy", "initiative", "compliance"),
119
+ ),
120
+ DocsSource(
121
+ "azure-app-gateway-overview",
122
+ "azure",
123
+ "Microsoft Learn",
124
+ "Application Gateway overview",
125
+ "https://learn.microsoft.com/en-us/azure/application-gateway/overview",
126
+ "network",
127
+ ("application-gateway", "waf", "layer-7"),
128
+ ),
129
+ DocsSource(
130
+ "azure-dns-overview",
131
+ "azure",
132
+ "Microsoft Learn",
133
+ "Azure DNS overview",
134
+ "https://learn.microsoft.com/en-us/azure/dns/dns-overview",
135
+ "network",
136
+ ("dns", "private-dns", "resolver"),
137
+ ),
138
+ DocsSource(
139
+ "azure-blob-access-tiers",
140
+ "azure",
141
+ "Microsoft Learn",
142
+ "Blob access tiers",
143
+ "https://learn.microsoft.com/en-us/azure/storage/blobs/access-tiers-overview",
144
+ "storage",
145
+ ("blob", "hot", "cool", "archive"),
146
+ ),
147
+ DocsSource(
148
+ "azure-vmss-overview",
149
+ "azure",
150
+ "Microsoft Learn",
151
+ "Virtual Machine Scale Sets overview",
152
+ "https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview",
153
+ "compute",
154
+ ("vmss", "autoscale", "availability"),
155
+ ),
156
+ DocsSource(
157
+ "azure-log-analytics-overview",
158
+ "azure",
159
+ "Microsoft Learn",
160
+ "Log Analytics overview",
161
+ "https://learn.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-overview",
162
+ "monitor",
163
+ ("log-analytics", "kql", "monitor"),
164
+ ),
165
+ DocsSource(
166
+ "azure-recovery-services-vault",
167
+ "azure",
168
+ "Microsoft Learn",
169
+ "Recovery Services vault overview",
170
+ "https://learn.microsoft.com/en-us/azure/backup/backup-azure-recovery-services-vault-overview",
171
+ "backup",
172
+ ("recovery-services-vault", "backup-policy"),
173
+ ),
174
+ DocsSource(
175
+ "azure-bastion-overview",
176
+ "azure",
177
+ "Microsoft Learn",
178
+ "Azure Bastion overview",
179
+ "https://learn.microsoft.com/en-us/azure/bastion/bastion-overview",
180
+ "network",
181
+ ("bastion", "ssh", "rdp"),
182
+ ),
183
+ DocsSource(
184
+ "azure-nat-gateway",
185
+ "azure",
186
+ "Microsoft Learn",
187
+ "Azure NAT Gateway overview",
188
+ "https://learn.microsoft.com/en-us/azure/nat-gateway/nat-overview",
189
+ "network",
190
+ ("nat-gateway", "outbound"),
191
+ ),
192
+ DocsSource(
193
+ "azure-app-service-overview",
194
+ "azure",
195
+ "Microsoft Learn",
196
+ "App Service overview",
197
+ "https://learn.microsoft.com/en-us/azure/app-service/overview",
198
+ "compute",
199
+ ("app-service", "web-app"),
200
+ ),
201
+ DocsSource(
202
+ "azure-container-instances",
203
+ "azure",
204
+ "Microsoft Learn",
205
+ "Azure Container Instances overview",
206
+ "https://learn.microsoft.com/en-us/azure/container-instances/container-instances-overview",
207
+ "compute",
208
+ ("aci", "container"),
209
+ ),
210
+ DocsSource(
211
+ "azure-key-vault-overview",
212
+ "azure",
213
+ "Microsoft Learn",
214
+ "Azure Key Vault overview",
215
+ "https://learn.microsoft.com/en-us/azure/key-vault/general/overview",
216
+ "security",
217
+ ("key-vault", "secret", "key"),
218
+ ),
219
+ DocsSource(
220
+ "manual-linux-basic",
221
+ "linux",
222
+ "GNU Coreutils Manual",
223
+ "GNU Coreutils basic operations",
224
+ "https://www.gnu.org/software/coreutils/manual/coreutils.html",
225
+ "essential-commands",
226
+ ("pwd", "ls", "mkdir", "touch", "chmod"),
227
+ "command-reference",
228
+ ),
229
+ DocsSource(
230
+ "linux-bash-cd",
231
+ "linux",
232
+ "GNU Bash Manual",
233
+ "Bash builtins",
234
+ "https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html",
235
+ "shell",
236
+ ("cd", "shell-builtin"),
237
+ "command-reference",
238
+ ),
239
+ DocsSource(
240
+ "linux-grep-manual",
241
+ "linux",
242
+ "GNU Grep Manual",
243
+ "GNU Grep manual",
244
+ "https://www.gnu.org/software/grep/manual/grep.html",
245
+ "text-processing",
246
+ ("grep", "search"),
247
+ "command-reference",
248
+ ),
249
+ DocsSource(
250
+ "linux-systemd-systemctl",
251
+ "linux",
252
+ "freedesktop.org",
253
+ "systemctl manual",
254
+ "https://www.freedesktop.org/software/systemd/man/latest/systemctl.html",
255
+ "services",
256
+ ("systemctl", "service"),
257
+ "command-reference",
258
+ ),
259
+ DocsSource(
260
+ "linux-man7-ip",
261
+ "linux",
262
+ "man7.org",
263
+ "ip command manual",
264
+ "https://man7.org/linux/man-pages/man8/ip.8.html",
265
+ "networking",
266
+ ("ip", "networking"),
267
+ "command-reference",
268
+ ),
269
+ DocsSource(
270
+ "ubuntu-server-docs",
271
+ "linux",
272
+ "Ubuntu Server Docs",
273
+ "Ubuntu Server documentation",
274
+ "https://ubuntu.com/server/docs/",
275
+ "server-operations",
276
+ ("ubuntu", "server", "operations"),
277
+ "practice-guide",
278
+ ),
279
+ DocsSource(
280
+ "ubuntu-package-management",
281
+ "linux",
282
+ "Ubuntu Server Docs",
283
+ "Install and manage packages",
284
+ "https://ubuntu.com/server/docs/how-to/software/package-management/",
285
+ "package-management",
286
+ ("apt", "package", "software"),
287
+ "practice-guide",
288
+ ),
289
+ DocsSource(
290
+ "ubuntu-user-management",
291
+ "linux",
292
+ "Ubuntu Server Docs",
293
+ "User management",
294
+ "https://ubuntu.com/server/docs/how-to/security/user-management/",
295
+ "users-groups",
296
+ ("user", "group", "sudo"),
297
+ "practice-guide",
298
+ ),
299
+ DocsSource(
300
+ "ubuntu-networking",
301
+ "linux",
302
+ "Ubuntu Server Docs",
303
+ "Networking",
304
+ "https://ubuntu.com/server/docs/how-to/networking/",
305
+ "networking",
306
+ ("netplan", "dns", "network-tools"),
307
+ "practice-guide",
308
+ ),
309
+ DocsSource(
310
+ "ubuntu-firewall",
311
+ "linux",
312
+ "Ubuntu Server Docs",
313
+ "Firewall",
314
+ "https://ubuntu.com/server/docs/how-to/security/firewalls/",
315
+ "security",
316
+ ("ufw", "firewall", "port"),
317
+ "practice-guide",
318
+ ),
319
+ DocsSource(
320
+ "ubuntu-storage",
321
+ "linux",
322
+ "Ubuntu Server Docs",
323
+ "Storage",
324
+ "https://ubuntu.com/server/docs/how-to/storage/",
325
+ "storage",
326
+ ("lvm", "storage", "iscsi"),
327
+ "practice-guide",
328
+ ),
329
+ DocsSource(
330
+ "official-docs-ollama",
331
+ "tool_docs",
332
+ "Ollama Docs",
333
+ "Ollama documentation",
334
+ "https://docs.ollama.com/",
335
+ "ollama",
336
+ ("ollama", "local-model"),
337
+ ),
338
+ DocsSource(
339
+ "official-docs-langchain",
340
+ "tool_docs",
341
+ "LangChain Docs",
342
+ "LangChain documentation",
343
+ "https://python.langchain.com/docs/",
344
+ "langchain",
345
+ ("langchain", "rag"),
346
+ ),
347
+ DocsSource(
348
+ "official-docs-streamlit",
349
+ "tool_docs",
350
+ "Streamlit Docs",
351
+ "Streamlit documentation",
352
+ "https://docs.streamlit.io/",
353
+ "streamlit",
354
+ ("streamlit", "session-state"),
355
+ ),
356
+ DocsSource(
357
+ "official-docs-huggingface",
358
+ "tool_docs",
359
+ "Hugging Face Docs",
360
+ "Hugging Face documentation",
361
+ "https://huggingface.co/docs",
362
+ "huggingface",
363
+ ("huggingface", "spaces"),
364
+ ),
365
+ )
366
+
367
+
368
+ def active_docs_sources(track_id: str | None = None) -> list[DocsSource]:
369
+ sources = [source for source in DOCS_SOURCES if source.enabled]
370
+ if track_id and track_id != "all":
371
+ sources = [source for source in sources if source.track == track_id]
372
+ return sources
373
+
374
+
375
+ def doc_source_by_id(source_id: str | None) -> DocsSource | None:
376
+ if not source_id:
377
+ return None
378
+ return next((source for source in DOCS_SOURCES if source.id == source_id), None)
379
+
380
+
381
+ def doc_source_urls(track_id: str | None = None) -> list[str]:
382
+ return [source.url for source in active_docs_sources(track_id)]
383
+
384
+
385
+ def docs_source_options() -> list[tuple[str, str]]:
386
+ return [
387
+ ("all", "์ „์ฒด Docs"),
388
+ ("azure", "Azure Docs"),
389
+ ("linux", "Linux Docs"),
390
+ ("tool_docs", "Tool Docs"),
391
+ ]
392
+
393
+
394
+ def urls_from_sources(sources: Iterable[DocsSource]) -> list[str]:
395
+ return [source.url for source in sources]
cert_study_app/services/official_docs_service.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import re
5
+ from datetime import datetime
6
+ from html.parser import HTMLParser
7
+ from typing import Optional
8
+
9
+ import requests
10
+
11
+ from cert_study_app.models import AzureDocsSync
12
+ from cert_study_app.services.docs_source_service import DocsSource, active_docs_sources
13
+ from cert_study_app.services.vector_service import DEFAULT_EMBEDDING_MODEL, QuestionVectorStore
14
+
15
+
16
+ OFFICIAL_DOC_URLS = [source.url for source in active_docs_sources()]
17
+ OFFICIAL_DOCS_COLLECTION = "official_docs"
18
+ LEGACY_AZURE_DOCS_COLLECTION = "azure_docs"
19
+
20
+
21
+ class _MainTextParser(HTMLParser):
22
+ def __init__(self):
23
+ super().__init__()
24
+ self.title = ""
25
+ self._in_title = False
26
+ self._skip_depth = 0
27
+ self._chunks: list[str] = []
28
+
29
+ def handle_starttag(self, tag, attrs):
30
+ if tag == "title":
31
+ self._in_title = True
32
+ if tag in {"script", "style", "noscript", "svg"}:
33
+ self._skip_depth += 1
34
+ if tag in {"p", "li", "h1", "h2", "h3", "h4", "td", "th"}:
35
+ self._chunks.append("\n")
36
+
37
+ def handle_endtag(self, tag):
38
+ if tag == "title":
39
+ self._in_title = False
40
+ if tag in {"script", "style", "noscript", "svg"} and self._skip_depth:
41
+ self._skip_depth -= 1
42
+ if tag in {"p", "li", "h1", "h2", "h3", "h4", "tr"}:
43
+ self._chunks.append("\n")
44
+
45
+ def handle_data(self, data):
46
+ text = re.sub(r"\s+", " ", data or "").strip()
47
+ if not text:
48
+ return
49
+ if self._in_title and not self.title:
50
+ self.title = text
51
+ if not self._skip_depth:
52
+ self._chunks.append(text)
53
+
54
+ def text(self) -> str:
55
+ text = " ".join(self._chunks)
56
+ text = re.sub(r"\s*\n\s*", "\n", text)
57
+ text = re.sub(r"[ \t]{2,}", " ", text)
58
+ return text.strip()
59
+
60
+
61
+ def _chunk_text(text: str, chunk_size: int = 1400, overlap: int = 180) -> list[str]:
62
+ clean = re.sub(r"\n{3,}", "\n\n", text or "").strip()
63
+ if not clean:
64
+ return []
65
+ chunks = []
66
+ start = 0
67
+ while start < len(clean):
68
+ end = min(len(clean), start + chunk_size)
69
+ chunk = clean[start:end].strip()
70
+ if chunk:
71
+ chunks.append(chunk)
72
+ if end >= len(clean):
73
+ break
74
+ start = max(0, end - overlap)
75
+ return chunks
76
+
77
+
78
+ def _doc_id(url: str, index: int, text: str) -> str:
79
+ digest = hashlib.sha1(f"{url}|{index}|{text}".encode("utf-8")).hexdigest()[:16]
80
+ return f"official_docs:{digest}"
81
+
82
+
83
+ def _category_from_url(url: str) -> str:
84
+ if "virtual-network" in url or "private-link" in url or "load-balancer" in url or "application-gateway" in url:
85
+ return "network"
86
+ if "virtual-machines" in url or "app-service" in url or "container-instances" in url:
87
+ return "compute"
88
+ if "storage" in url:
89
+ return "storage"
90
+ if "role-based-access-control" in url or "entra" in url or "key-vault" in url:
91
+ return "identity"
92
+ if "monitor" in url:
93
+ return "monitor"
94
+ if "backup" in url:
95
+ return "backup"
96
+ return "docs"
97
+
98
+
99
+ class OfficialDocsService:
100
+ def __init__(
101
+ self,
102
+ db,
103
+ embedding_model: Optional[str] = None,
104
+ urls: Optional[list[str]] = None,
105
+ track_id: str | None = None,
106
+ sources: Optional[list[DocsSource]] = None,
107
+ collection_name: str = OFFICIAL_DOCS_COLLECTION,
108
+ ):
109
+ self.db = db
110
+ self.embedding_model = embedding_model or DEFAULT_EMBEDDING_MODEL
111
+ if sources is not None:
112
+ self.sources = sources
113
+ elif urls:
114
+ self.sources = self._sources_from_urls(urls)
115
+ else:
116
+ self.sources = active_docs_sources(track_id)
117
+ self.vector_store = QuestionVectorStore(
118
+ collection_name=collection_name,
119
+ embedding_model=self.embedding_model,
120
+ )
121
+
122
+ @staticmethod
123
+ def _sources_from_urls(urls: Optional[list[str]]) -> list[DocsSource]:
124
+ return [
125
+ DocsSource(
126
+ id=f"custom-doc-{index}",
127
+ track="custom",
128
+ provider="Custom Docs",
129
+ title=url.rsplit("/", 1)[-1] or url,
130
+ url=url,
131
+ category="custom",
132
+ )
133
+ for index, url in enumerate(urls or [], 1)
134
+ ]
135
+
136
+ def latest_sync(self) -> AzureDocsSync | None:
137
+ return self.db.query(AzureDocsSync).order_by(AzureDocsSync.id.desc()).first()
138
+
139
+ def sync(self, limit: Optional[int] = None) -> dict:
140
+ sync = AzureDocsSync(
141
+ embedding_model=self.embedding_model,
142
+ status="running",
143
+ message="๊ณต์‹ Docs ๋™๊ธฐํ™”๋ฅผ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.",
144
+ started_at=datetime.utcnow(),
145
+ )
146
+ self.db.add(sync)
147
+ self.db.commit()
148
+
149
+ sources = self.sources[: int(limit)] if limit else self.sources
150
+ checked = 0
151
+ docs_indexed = 0
152
+ chunks_indexed = 0
153
+ try:
154
+ for source in sources:
155
+ checked += 1
156
+ response = requests.get(source.url, timeout=20, headers={"User-Agent": "cert-study-app/1.0"})
157
+ response.raise_for_status()
158
+ parser = _MainTextParser()
159
+ parser.feed(response.text)
160
+ title = parser.title or source.title or source.url.rsplit("/", 1)[-1]
161
+ chunks = _chunk_text(parser.text())
162
+ payloads = [
163
+ {
164
+ "id": _doc_id(source.url, index, chunk),
165
+ "text": f"Title: {title}\nURL: {source.url}\n\n{chunk}",
166
+ "source_type": f"{source.track}_docs",
167
+ "source": source.provider,
168
+ "source_id": source.id,
169
+ "track": source.track,
170
+ "role": source.role,
171
+ "title": title,
172
+ "url": source.url,
173
+ "category": source.category or _category_from_url(source.url),
174
+ }
175
+ for index, chunk in enumerate(chunks, 1)
176
+ ]
177
+ inserted = self.vector_store.upsert_documents(payloads)
178
+ if inserted:
179
+ docs_indexed += 1
180
+ chunks_indexed += inserted
181
+
182
+ sync.status = "success"
183
+ sync.message = f"{docs_indexed}๊ฐœ ๋ฌธ์„œ, {chunks_indexed}๊ฐœ chunk๋ฅผ ์ƒ‰์ธํ–ˆ์Šต๋‹ˆ๋‹ค."
184
+ except Exception as exc:
185
+ sync.status = "failed"
186
+ sync.error_message = str(exc)[:1000]
187
+ sync.message = "๊ณต์‹ Docs ๋™๊ธฐํ™” ์‹คํŒจ"
188
+ finally:
189
+ sync.urls_checked = checked
190
+ sync.documents_indexed = docs_indexed
191
+ sync.chunks_indexed = chunks_indexed
192
+ sync.completed_at = datetime.utcnow()
193
+ self.db.commit()
194
+
195
+ return {
196
+ "status": sync.status,
197
+ "urls_checked": checked,
198
+ "documents_indexed": docs_indexed,
199
+ "chunks_indexed": chunks_indexed,
200
+ "message": sync.message,
201
+ "error_message": sync.error_message,
202
+ }
203
+
204
+
205
+ class AzureDocsService(OfficialDocsService):
206
+ """Backward-compatible alias for older imports."""
207
+
208
+ def __init__(self, *args, **kwargs):
209
+ kwargs.setdefault("track_id", "azure")
210
+ super().__init__(*args, **kwargs)