From 62153b5741892f7891e9387ba8441b289ff71b41 Mon Sep 17 00:00:00 2001 From: yesung Date: Mon, 20 Apr 2026 15:00:36 +0900 Subject: [PATCH] [P-Reinforce] 2026-04-20: Processed 5 New Knowledge Gems (Tetris Engineering Lessons) --- {00_Raw => 01_Archive/2026-04-20}/.gitkeep | 0 .../01_WebWorker-performance-optimization.md | 20 ++ ..._StateManagement-single-source-of-truth.md | 19 ++ .../03_Architecture-design-principle.md | 22 +++ .../04_execution-environment-management.md | 22 +++ .../05_simulation-design-principles.md | 19 ++ 10_Wiki/Topics/.obsidian/app.json | 1 + 10_Wiki/Topics/.obsidian/appearance.json | 1 + 10_Wiki/Topics/.obsidian/core-plugins.json | 33 ++++ 10_Wiki/Topics/.obsidian/graph.json | 22 +++ 10_Wiki/Topics/.obsidian/workspace.json | 187 ++++++++++++++++++ 10_Wiki/Topics/DevOps_Environment_Setup.md | 24 +++ 10_Wiki/Topics/Separation_of_Concerns.md | 25 +++ 10_Wiki/Topics/Single_Source_of_Truth.md | 24 +++ .../Topics/Systemic_Simulation_Principles.md | 24 +++ 10_Wiki/Topics/WebWorker_Performance.md | 24 +++ package.json | 21 ++ src/TetrisGame.jsx | 97 +++++++++ src/gameWorker.js | 73 +++++++ 19 files changed, 658 insertions(+) rename {00_Raw => 01_Archive/2026-04-20}/.gitkeep (100%) create mode 100644 01_Archive/2026-04-20/01_WebWorker-performance-optimization.md create mode 100644 01_Archive/2026-04-20/02_StateManagement-single-source-of-truth.md create mode 100644 01_Archive/2026-04-20/03_Architecture-design-principle.md create mode 100644 01_Archive/2026-04-20/04_execution-environment-management.md create mode 100644 01_Archive/2026-04-20/05_simulation-design-principles.md create mode 100644 10_Wiki/Topics/.obsidian/app.json create mode 100644 10_Wiki/Topics/.obsidian/appearance.json create mode 100644 10_Wiki/Topics/.obsidian/core-plugins.json create mode 100644 10_Wiki/Topics/.obsidian/graph.json create mode 100644 10_Wiki/Topics/.obsidian/workspace.json create mode 100644 10_Wiki/Topics/DevOps_Environment_Setup.md create mode 100644 10_Wiki/Topics/Separation_of_Concerns.md create mode 100644 10_Wiki/Topics/Single_Source_of_Truth.md create mode 100644 10_Wiki/Topics/Systemic_Simulation_Principles.md create mode 100644 10_Wiki/Topics/WebWorker_Performance.md create mode 100644 package.json create mode 100644 src/TetrisGame.jsx create mode 100644 src/gameWorker.js diff --git a/00_Raw/.gitkeep b/01_Archive/2026-04-20/.gitkeep similarity index 100% rename from 00_Raw/.gitkeep rename to 01_Archive/2026-04-20/.gitkeep diff --git a/01_Archive/2026-04-20/01_WebWorker-performance-optimization.md b/01_Archive/2026-04-20/01_WebWorker-performance-optimization.md new file mode 100644 index 00000000..a2d6c91d --- /dev/null +++ b/01_Archive/2026-04-20/01_WebWorker-performance-optimization.md @@ -0,0 +1,20 @@ +--- +# ๐Ÿ’ก Lesson Learned: Web Worker๋ฅผ ์ด์šฉํ•œ ๊ณ ์„ฑ๋Šฅ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ (Performance) + +## ๐ŸŽฏ ๋ฌธ์ œ ์ƒํ™ฉ (The Problem) +ํ…ŒํŠธ๋ฆฌ์Šค ๊ฒŒ์ž„๊ณผ ๊ฐ™์ด **๋งค์šฐ ๋†’์€ ๋นˆ๋„(High Frequency)**๋กœ ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์‹ค์‹œ๊ฐ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ React์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ๊ฒฝ์šฐ, UI ์—…๋ฐ์ดํŠธ์™€ ๋ฌผ๋ฆฌ ๊ณ„์‚ฐ์ด ์ถฉ๋Œํ•˜์—ฌ **ํ”„๋ ˆ์ž„ ๋“œ๋กญ(Jank)** ํ˜„์ƒ์ด๋‚˜ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ”ฌ ๊ทผ๋ณธ ์›์ธ (Root Cause) +๊ฒŒ์ž„ ์—”์ง„ ๋กœ์ง์€ CPU๋ฅผ ๋งค์šฐ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰ํ•˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €์˜ UI ์—…๋ฐ์ดํŠธ ๋ฃจํ”„(`requestAnimationFrame`)์™€ ์ถฉ๋Œํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ถ€๋“œ๋Ÿฝ์ง€ ์•Š์€ ๊ฒฝํ—˜(Poor UX)์„ ์ œ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. + +## โœ… ํ•ด๊ฒฐ์ฑ… (The Solution) +**Web Worker**๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒŒ์ž„ ์—”์ง„ ๋กœ์ง ์ „์ฒด๋ฅผ **๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์™„์ „ํžˆ ๋ถ„๋ฆฌ(Isolate)** ํ–ˆ์Šต๋‹ˆ๋‹ค. +* **์›๋ฆฌ:** Web Worker๋Š” ๋ณ„๋„์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋ฏ€๋กœ, ์•„๋ฌด๋ฆฌ ๋ณต์žกํ•œ ๊ณ„์‚ฐ์„ ํ•ด๋„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์˜ UI ๋ Œ๋”๋ง์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๊ตํ›ˆ (Lesson Learned) +> **"์„ฑ๋Šฅ ๋ณ‘๋ชฉ ํ˜„์ƒ์€ ์ข…์ข… '์Šค๋ ˆ๋”ฉ(Threading)'์˜ ๋ฌธ์ œ์ด๋‹ค."** +> ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋†’์€ ์—ฐ์‚ฐ๋Ÿ‰์ด ์š”๊ตฌ๋˜๋Š” ๋ชจ๋“  ์‹œ์Šคํ…œ์€, ๋ฐ˜๋“œ์‹œ Web Worker ๋˜๋Š” ๋ณ„๋„์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ”„๋กœ์„ธ์Šค๋กœ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”— ๊ด€๋ จ ํ‚ค์›Œ๋“œ +`Web Worker`, `Concurrency`, `High-Frequency Updates`, `Performance Optimization` +--- \ No newline at end of file diff --git a/01_Archive/2026-04-20/02_StateManagement-single-source-of-truth.md b/01_Archive/2026-04-20/02_StateManagement-single-source-of-truth.md new file mode 100644 index 00000000..904c55a0 --- /dev/null +++ b/01_Archive/2026-04-20/02_StateManagement-single-source-of-truth.md @@ -0,0 +1,19 @@ +--- +# ๐Ÿ’ก Lesson Learned: ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์› ์›์น™ (Data Consistency) + +## ๐ŸŽฏ ๋ฌธ์ œ ์ƒํ™ฉ (The Problem) +ํ…ŒํŠธ๋ฆฌ์Šค ๊ฒŒ์ž„์€ 'ํ˜„์žฌ ๋ณด๋“œ ์ƒํƒœ'์™€ '์›€์ง์ด๋Š” ๋ธ”๋ก ์œ„์น˜'๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๋“ค์ด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ A ๋ถ€๋ถ„์—์„œ ๊ฐ’์„ ๋ฐ”๊พธ๊ณ , B ๋ถ€๋ถ„์—์„œ ๊ฐ™์€ ๊ฐ’์„ ๋‹ค๋ฅด๊ฒŒ ๊ณ„์‚ฐํ•œ๋‹ค๋ฉด **๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜(Inconsistency)**๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”ฌ ๊ทผ๋ณธ ์›์ธ (Root Cause) +์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ์ƒํƒœ๊ฐ€ ๋ถ„์‚ฐ๋˜์–ด ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์™€ ๋กœ์ง์ด ๊ฐ์ž '์ง„์‹ค'์ด๋ผ๊ณ  ๋ฏฟ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์ถฉ๋Œํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•˜์Šต๋‹ˆ๋‹ค. + +## โœ… ํ•ด๊ฒฐ์ฑ… (The Solution) +**Redux/Zustand ํŒจํ„ด์„ ์ฐจ์šฉํ•˜์—ฌ ๋ชจ๋“  ๊ฒŒ์ž„์˜ ํ•ต์‹ฌ ์ƒํƒœ(State)**๋ฅผ `src/TetrisGame.jsx` ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” **๋‹จ์ผ ์ง€์ (Single Source of Truth)**์œผ๋กœ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ์ด ์ค‘์•™ ์ €์žฅ์†Œ๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง€๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๊ตํ›ˆ (Lesson Learned) +> **"์ƒํƒœ๋Š” ์˜ค์ง ํ•œ ๊ณณ์—์„œ๋งŒ ์ •์˜ํ•˜๊ณ , ๋ชจ๋“  ๋กœ์ง์€ ๊ทธ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค."** +> ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•  ๋•Œ, ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„(Data Flow)๊ณผ ์ฑ…์ž„ ๋ฒ”์œ„(Responsibility)๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”— ๊ด€๋ จ ํ‚ค์›Œ๋“œ +`Single Source of Truth`, `Redux Pattern`, `State Management`, `Predictable State` +--- \ No newline at end of file diff --git a/01_Archive/2026-04-20/03_Architecture-design-principle.md b/01_Archive/2026-04-20/03_Architecture-design-principle.md new file mode 100644 index 00000000..957942cd --- /dev/null +++ b/01_Archive/2026-04-20/03_Architecture-design-principle.md @@ -0,0 +1,22 @@ +--- +# ๐Ÿ’ก Lesson Learned: ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜์˜ ์ค‘์š”์„ฑ (The Need for Abstraction) + +## ๐ŸŽฏ ๋ฌธ์ œ ์ƒํ™ฉ (The Problem) +์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ, ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ '์–ด๋–ค ๊ตฌ์กฐ๋กœ ์งค์ง€'๊ฐ€ ๊ฐ€์žฅ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํžˆ ๊ธฐ์ˆ ์ ์ธ ๋ฌธ์ œ๊ฐ€ ์•„๋‹Œ **์„ค๊ณ„ ํŒจํ„ด(Design Pattern)**๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. + +## ๐Ÿ”ฌ ๊ทผ๋ณธ ์›์ธ (Root Cause) +๋ชจ๋“  ๋กœ์ง์„ ํ•œ ํŒŒ์ผ์— ๋•Œ๋ ค ๋„ฃ์œผ๋ ค๋Š” ์œ ํ˜น์— ๋น ์ง€๋Š” ๊ฒƒ, ์ฆ‰ '์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ'๋ฅผ ๋งŒ๋“ค ์œ„ํ—˜์ด ๊ฐ€์žฅ ํฐ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ ํ•œ๊ณณ์—์„œ ์ฒ˜๋ฆฌํ•˜๋ ค ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์ด 0์— ์ˆ˜๋ ดํ–ˆ์Šต๋‹ˆ๋‹ค. + +## โœ… ํ•ด๊ฒฐ์ฑ… (The Solution) +**์•„ํ‚คํ…์ฒ˜์  ๋ถ„๋ฆฌ ์›์น™(Separation of Concerns, SoC)**์„ ์ ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—ญํ• ๋ณ„๋กœ ๋‚˜๋ˆด์Šต๋‹ˆ๋‹ค: +1. **๊ฒŒ์ž„ ๊ทœ์น™:** `gameWorker.js` (๋…ผ๋ฆฌ ์—”์ง„) +2. **์ƒํƒœ ๊ด€๋ฆฌ:** `TetrisGame.jsx` (๋ฐ์ดํ„ฐ์˜ ์ถœ์ž…๊ตฌ) +3. **๋ Œ๋”๋ง:** React ์ปดํฌ๋„ŒํŠธ (ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ๋งŒ ์ˆ˜ํ–‰) + +## ๐Ÿ’ก ๊ตํ›ˆ (Lesson Learned) +> **"์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•  ๋•Œ๋Š” '์ฑ…์ž„ ๋ถ„๋ฆฌ(Separation of Concerns)'๋ฅผ ์ตœ์šฐ์„  ์›์น™์œผ๋กœ ์‚ผ์•„์•ผ ํ•œ๋‹ค."** +> ๊ธฐ๋Šฅ์ด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก, ์ฝ”๋“œ๋Š” ๋ฐ˜๋“œ์‹œ ๊ฒฝ๊ณ„๊ฐ€ ๋ช…ํ™•ํ•œ ๋ชจ๋“ˆ๋“ค๋กœ ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”— ๊ด€๋ จ ํ‚ค์›Œ๋“œ +`Separation of Concerns`, `Modular Design`, `Microservices Pattern` +--- \ No newline at end of file diff --git a/01_Archive/2026-04-20/04_execution-environment-management.md b/01_Archive/2026-04-20/04_execution-environment-management.md new file mode 100644 index 00000000..4f30447c --- /dev/null +++ b/01_Archive/2026-04-20/04_execution-environment-management.md @@ -0,0 +1,22 @@ +--- +# ๐Ÿ’ก Lesson Learned: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฐ ์‹คํ–‰ ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ (DevOps & DevOps) + +## ๐ŸŽฏ ๋ฌธ์ œ ์ƒํ™ฉ (The Problem) +์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋ฅผ ์งœ๊ณ  ๋๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, **'์–ด๋–ป๊ฒŒ ์ด ์ฝ”๋“œ๋ฅผ ๊ตฌ๋™์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š”๊ฐ€?'**๋ผ๋Š” ๋ฌผ๋ฆฌ์  ์ ˆ์ฐจ์˜ ์ค‘์š”์„ฑ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. (์˜ค๋ฅ˜ ์ฝ”๋“œ: `npm audit`, `index.html` ๋ˆ„๋ฝ, ๊ถŒํ•œ ์˜ค๋ฅ˜ ๋“ฑ) + +## ๐Ÿ”ฌ ๊ทผ๋ณธ ์›์ธ (Root Cause) +๊ฐœ๋ฐœ์ž๋Š” ์ข…์ข… **'๋…ผ๋ฆฌ์  ์™„์„ฑ๋„(Logical Completion)'์—๋งŒ ์ง‘์ค‘**ํ•˜๊ณ , ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ **๋ฌผ๋ฆฌ์ ์ธ ์„ค์ • ํŒŒ์ผ(Configuration)**๊ณผ **์šด์˜์ฒด์ œ ๋ ˆ๋ฒจ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜/๊ถŒํ•œ** ๊ด€๋ฆฌ์— ์†Œํ™€ํ•ด์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + +## โœ… ํ•ด๊ฒฐ์ฑ… (The Solution) +ํ”„๋กœ์ ํŠธ ์‹œ์ž‘ ์‹œ์ ์— ๋‹ค์Œ ์ ˆ์ฐจ๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ฑฐ์ณ์•ผ ํ•จ์„ ํ™•๋ฆฝํ–ˆ์Šต๋‹ˆ๋‹ค: +1. `npm install`: ํ•„์š”ํ•œ ๋ชจ๋“  ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•œ๋‹ค. +2. ํ™˜๊ฒฝ ์„ค์ • ํ™•์ธ: `public/index.html` ๋“ฑ ํ•„์ˆ˜ ์ง„์ž…์ ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. +3. ๊ถŒํ•œ ํ™•๋ณด: ์šด์˜์ฒด์ œ ๋ ˆ๋ฒจ์—์„œ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ(Execution Policy)์„ ํ™•๋ณดํ•œ๋‹ค. + +## ๐Ÿ’ก ๊ตํ›ˆ (Lesson Learned) +> **"์ฝ”๋”ฉ ๋Šฅ๋ ฅ๋งŒํผ์ด๋‚˜ ์ค‘์š”ํ•œ ๊ฒƒ์€ '์šด์˜ ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ดํ•ด'์™€ '์ฒด๊ณ„์ ์ธ ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค ํ™•๋ฆฝ'์ด๋‹ค."** +> ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ์ž๋Š” ํ•ญ์ƒ ์ด ์„ธ ๊ฐ€์ง€ ๋‹จ๊ณ„๋ฅผ ์ ๊ฒ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”— ๊ด€๋ จ ํ‚ค์›Œ๋“œ +`DevOps`, `CI/CD Pipeline`, `Execution Policy`, `Build Environment` +--- \ No newline at end of file diff --git a/01_Archive/2026-04-20/05_simulation-design-principles.md b/01_Archive/2026-04-20/05_simulation-design-principles.md new file mode 100644 index 00000000..e3615026 --- /dev/null +++ b/01_Archive/2026-04-20/05_simulation-design-principles.md @@ -0,0 +1,19 @@ +--- +# ๐Ÿ’ก Lesson Learned: ์‹œ์Šคํ…œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ํ•ต์‹ฌ ์›๋ฆฌ (Simulation Design) + +## ๐ŸŽฏ ๋ฌธ์ œ ์ƒํ™ฉ (The Problem) +ํ…ŒํŠธ๋ฆฌ์Šค๋Š” ๋‹จ์ˆœํ•œ ๊ฒŒ์ž„์ด ์•„๋‹ˆ๋ผ, **๋ฌผ๋ฆฌ ๋ฒ•์น™(Physics)**๊ณผ **๊ทœ์น™ ๊ธฐ๋ฐ˜์˜ ์ƒํƒœ ๋ณ€ํ™”**๊ฐ€ ์ž‘๋™ํ•˜๋Š” ์ž‘์€ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์˜€์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด '์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ์–ด๋–ป๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•˜๋Š”์ง€'์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ”ฌ ๊ทผ๋ณธ ์›์ธ (Root Cause) +๋‹จ์ˆœํžˆ UI๋กœ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์—๋งŒ ์ง‘์ค‘ํ•˜๋ฉด, ์‹œ์Šคํ…œ์ด **๊ทœ์น™(Ruleset)**๊ณผ **๋ฌผ๋ฆฌ ๋ฒ•์น™(Physics Law)**์„ ๋”ฐ๋ฅด๋Š” '๊ฐ€์ƒ ์„ธ๊ณ„'์˜ ๋А๋‚Œ์„ ๋†“์น˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + +## โœ… ํ•ด๊ฒฐ์ฑ… (The Solution) +๊ฒŒ์ž„ ๋กœ์ง์„ `gameWorker.js`์— ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ, ๋ชจ๋“  ๋ณ€ํ™”๋ฅผ ์ˆ˜ํ•™์  ํ•จ์ˆ˜(`checkCollision`, `movePiece`)๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ƒํƒœ(State)์— ๋ฐ˜์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ณง **"๊ทœ์น™์ด ๋ฌผ๋ฆฌ ๋ฒ•์น™์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋Š” ์‹œ์Šคํ…œ"** ์„ค๊ณ„์˜ ์„ฑ๊ณต์ ์ธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๊ตํ›ˆ (Lesson Learned) +> **"๋ชจ๋“  ์‹œ๋ฎฌ๋ ˆ์ด์…˜์€ '๋ฌผ๋ฆฌ์  ๊ทœ์น™'์„ ์ˆ˜ํ•™์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ , ๊ทธ ๊ทœ์น™์„ ์ ˆ๋Œ€ ์šฐํšŒํ•  ์ˆ˜ ์—†๋„๋ก ๊ฐ•์ œํ•ด์•ผ ํ•œ๋‹ค."** +> ์ด๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” ๋‹จ์ˆœํ•œ ๊ฒŒ์ž„์„ ๋„˜์–ด, ์ž์œจ์ฃผํ–‰์ด๋‚˜ ๋ฌผ๋ฆฌ ์—”์ง„์— ์ ์šฉ ๊ฐ€๋Šฅํ•œ ๊ณ ์ˆ˜์ค€์˜ ์‹œ์Šคํ…œ ๋ชจ๋ธ๋ง ๋Šฅ๋ ฅ์„ ๊ฐ–์ถ”๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ”— ๊ด€๋ จ ํ‚ค์›Œ๋“œ +`Simulation Design`, `Physics Engine`, `Ruleset Enforcement`, `Systemic Modeling` +--- \ No newline at end of file diff --git a/10_Wiki/Topics/.obsidian/app.json b/10_Wiki/Topics/.obsidian/app.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/10_Wiki/Topics/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/10_Wiki/Topics/.obsidian/appearance.json b/10_Wiki/Topics/.obsidian/appearance.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/10_Wiki/Topics/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/10_Wiki/Topics/.obsidian/core-plugins.json b/10_Wiki/Topics/.obsidian/core-plugins.json new file mode 100644 index 00000000..639b90da --- /dev/null +++ b/10_Wiki/Topics/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} \ No newline at end of file diff --git a/10_Wiki/Topics/.obsidian/graph.json b/10_Wiki/Topics/.obsidian/graph.json new file mode 100644 index 00000000..e56c2134 --- /dev/null +++ b/10_Wiki/Topics/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 0.08317427835927536, + "close": false +} \ No newline at end of file diff --git a/10_Wiki/Topics/.obsidian/workspace.json b/10_Wiki/Topics/.obsidian/workspace.json new file mode 100644 index 00000000..a2ee0147 --- /dev/null +++ b/10_Wiki/Topics/.obsidian/workspace.json @@ -0,0 +1,187 @@ +{ + "main": { + "id": "59f0bd68c638b9ae", + "type": "split", + "children": [ + { + "id": "6e3e7f0212dd6d2e", + "type": "tabs", + "children": [ + { + "id": "5e19c94f304a33d1", + "type": "leaf", + "state": { + "type": "graph", + "state": {}, + "icon": "lucide-git-fork", + "title": "๊ทธ๋ž˜ํ”„ ๋ทฐ" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "b20f341b7d225db0", + "type": "split", + "children": [ + { + "id": "76facd68bdc37a30", + "type": "tabs", + "children": [ + { + "id": "697d93dc46e83f99", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ" + } + }, + { + "id": "14386382787eb545", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "๊ฒ€์ƒ‰" + } + }, + { + "id": "6544f7f2d2bdb927", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "๋ถ๋งˆํฌ" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "eb1afd59f22726e4", + "type": "split", + "children": [ + { + "id": "cff2bf89b29bbdad", + "type": "tabs", + "children": [ + { + "id": "a06f05e29da92edb", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "๋ฐฑ๋งํฌ" + } + }, + { + "id": "461414a74ff42c5f", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "๋‚˜๊ฐ€๋Š” ๋งํฌ" + } + }, + { + "id": "2c463caabad51324", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "ํƒœ๊ทธ" + } + }, + { + "id": "e863614ec11ec6c0", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "๋ชจ๋“  ์†์„ฑ" + } + }, + { + "id": "0f1cc972aeac180a", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "๊ฐœ์š”" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:๋น ๋ฅธ ์ „ํ™˜๊ธฐ ์—ด๊ธฐ": false, + "graph:๊ทธ๋ž˜ํ”„ ๋ทฐ ์—ด๊ธฐ": false, + "canvas:์ƒˆ ์บ”๋ฒ„์Šค ๋งŒ๋“ค๊ธฐ": false, + "daily-notes:์˜ค๋Š˜์˜ ์ผ์ผ ๋…ธํŠธ ์—ด๊ธฐ": false, + "templates:ํ…œํ”Œ๋ฆฟ ์‚ฝ์ž…": false, + "command-palette:๋ช…๋ น์–ด ํŒ”๋ ˆํŠธ ์—ด๊ธฐ": false, + "bases:์ƒˆ ๋ฒ ์ด์Šค ์ƒ์„ฑํ•˜๊ธฐ": false + } + }, + "active": "5e19c94f304a33d1", + "lastOpenFiles": [ + "Systemic_Simulation_Principles.md", + "DevOps_Environment_Setup.md", + "Separation_of_Concerns.md", + "Single_Source_of_Truth.md", + "WebWorker_Performance.md" + ] +} \ No newline at end of file diff --git a/10_Wiki/Topics/DevOps_Environment_Setup.md b/10_Wiki/Topics/DevOps_Environment_Setup.md new file mode 100644 index 00000000..c24517d5 --- /dev/null +++ b/10_Wiki/Topics/DevOps_Environment_Setup.md @@ -0,0 +1,24 @@ +--- +title: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฐ ์‹คํ–‰ ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ (DevOps & Setup) +category: DevOps +tags: [DevOps, Environment, CI/CD, Process Management] +created: 2026-04-20 +--- + +# ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฐ ์‹คํ–‰ ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ + +## ๐ŸŽฏ ๊ฐœ์š” (Overview) +์ฝ”๋”ฉ ์™„์„ฑ๋„๋งŒํผ์ด๋‚˜ ์ค‘์š”ํ•œ **์‹คํ–‰ ํ™˜๊ฒฝ(Runtime Environment)**๊ณผ **์„ค์ • ํŒŒ์ผ(Configuration)**์˜ ๋ฌด๊ฒฐ์„ฑ์„ ํ™•๋ณดํ•˜์—ฌ, '๋‚ด ์ปดํ“จํ„ฐ์—์„  ๋˜๋Š”๋ฐ ์™œ ์ €๊ธฐ์„  ์•ˆ ๋˜์ง€?'๋ผ๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ํ”„๋กœ์„ธ์Šค์ž…๋‹ˆ๋‹ค. + +## ๐Ÿš€ ํ•„์ˆ˜ ์ฒดํฌ๋ฆฌ์ŠคํŠธ (Checklist) +- **์˜์กด์„ฑ ๊ด€๋ฆฌ**: `npm install` ๋“ฑ ํŒจํ‚ค์ง€ ๋ฌด๊ฒฐ์„ฑ ํ™•์ธ. +- **๋ฌผ๋ฆฌ์  ํŒŒ์ผ ๊ตฌ์กฐ**: `index.html` ๋“ฑ ํ•„์ˆ˜ ์ง„์ž…์  ํŒŒ์ผ ์กด์žฌ ํ™•์ธ. +- **๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ**: OS ๋ ˆ๋ฒจ์˜ ์‹คํ–‰ ์ •์ฑ…(`Execution Policy`) ๋ฐ ๊ถŒํ•œ ์„ค์ •. + +## ๐Ÿ’ก ๋ ˆ์Šจ ๋Ÿฐ (Lesson Learned) +> [!NOTE] +> **"์šด์˜ ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ดํ•ด๋Š” ์ฝ”๋”ฉ ๋Šฅ๋ ฅ์˜ ์ ˆ๋ฐ˜์ด๋‹ค."** +> ๋…ผ๋ฆฌ์  ๋กœ์ง์˜ ์™„์„ฑ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๊ทธ๊ฒƒ์ด ์‹ค์ œ๋กœ ๊ตฌ๋™๋˜๋Š” ๋ฌผ๋ฆฌ์  ์ธํ”„๋ผ ์„ค์ •์„ ๋ฌธ์„œํ™”ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๋Š” ๋Šฅ๋ ฅ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค. + +## ๐Ÿ”— ์—ฐ๊ฒฐ๋œ ์ง€์‹ +- [[Systemic_Simulation_Principles]] diff --git a/10_Wiki/Topics/Separation_of_Concerns.md b/10_Wiki/Topics/Separation_of_Concerns.md new file mode 100644 index 00000000..e57cf8eb --- /dev/null +++ b/10_Wiki/Topics/Separation_of_Concerns.md @@ -0,0 +1,25 @@ +--- +title: ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜์™€ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ (Separation of Concerns) +category: Software Architecture +tags: [Architecture, SoC, Modular Design, Design Pattern] +created: 2026-04-20 +--- + +# ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜์™€ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ (SoC) + +## ๐ŸŽฏ ๊ฐœ์š” (Overview) +๋ณต์žกํ•œ ์†Œํ”„ํŠธ์›จ์–ด ์‹œ์Šคํ…œ์„ ์—ญํ• ๋ณ„๋กœ ๊ตฌ๋ถ„๋œ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๋‚˜๋ˆ„์–ด, ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ์„ค๊ณ„ ์ฒ ํ•™์ž…๋‹ˆ๋‹ค. + +## ๐Ÿš€ ๊ณ„์ธต๊ตฌ์กฐ ์˜ˆ์‹œ (Layering Example) +1. **Logic Engine**: ์ˆœ์ˆ˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ฐ ๊ทœ์น™ ์ˆ˜ํ–‰ (์˜ˆ: `gameWorker.js`) +2. **State Manager**: ๋ฐ์ดํ„ฐ์˜ ์ค‘์•™ ์ง‘์ค‘ ์ฒ˜๋ฆฌ (์˜ˆ: `TetrisGame.jsx`) +3. **View Layer**: ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ํ‘œํ˜„ ๋ฐ ๋ Œ๋”๋ง (์˜ˆ: React Components) + +## ๐Ÿ’ก ๋ ˆ์Šจ ๋Ÿฐ (Lesson Learned) +> [!IMPORTANT] +> **"์ฝ”๋“œ์˜ ๊ฒฝ๊ณ„๊ฐ€ ๋ช…ํ™•ํ•  ๋•Œ ์‹œ์Šคํ…œ์€ ๋น„๋กœ์†Œ ๊ฑด๊ฐ•ํ•ด์ง„๋‹ค."** +> ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ๋ณด๋‹ค ์ƒˆ๋กœ์šด ๋ชจ๋“ˆ์„ ๋ง๋ถ™์ผ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ”— ์—ฐ๊ฒฐ๋œ ์ง€์‹ +- [[WebWorker_Performance]] +- [[Single_Source_of_Truth]] diff --git a/10_Wiki/Topics/Single_Source_of_Truth.md b/10_Wiki/Topics/Single_Source_of_Truth.md new file mode 100644 index 00000000..d62c92be --- /dev/null +++ b/10_Wiki/Topics/Single_Source_of_Truth.md @@ -0,0 +1,24 @@ +--- +title: ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์› (Single Source of Truth) +category: Software Architecture +tags: [State Management, Data Consistency, Redux, Architecture] +created: 2026-04-20 +--- + +# ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์› (Single Source of Truth) + +## ๐ŸŽฏ ๊ฐœ์š” (Overview) +์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ค‘์•™ ์ง‘์ค‘์‹์œผ๋กœ ๊ด€๋ฆฌํ•˜์—ฌ, ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜(Inconsistency) ํ˜„์ƒ์„ ์›์ฒœ ์ฐจ๋‹จํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ํ™•๋ณดํ•˜๋Š” ์„ค๊ณ„ ์›์น™์ž…๋‹ˆ๋‹ค. + +## ๐Ÿš€ ์ฃผ์š” ์›์น™ (Key Principles) +- **๋‹จ์ผ ์ง€์  ์ •์˜ (Defined at Single Point)**: ์ƒํƒœ๋Š” ์˜ค์ง ํ•œ ๊ณณ์—์„œ๋งŒ ์ •์˜๋˜๊ณ  ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- **์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ (Predictability)**: ์ƒํƒœ ๋ณ€๊ฒฝ์€ ์ •ํ•ด์ง„ ๊ทœ์น™(Action/Setter)์„ ํ†ตํ•ด์„œ๋งŒ ๋ฐœ์ƒํ•˜์—ฌ ๋””๋ฒ„๊น…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๋ ˆ์Šจ ๋Ÿฐ (Lesson Learned) +> [!TIP] +> **"์ƒํƒœ๋Š” ์˜ค์ง ํ•œ ๊ณณ์—์„œ๋งŒ ์ •์˜ํ•˜๊ณ , ๋ชจ๋“  ๋กœ์ง์€ ๊ทธ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค."** +> ์ฝ”๋“œ์˜ ํŒŒํŽธํ™”๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ์˜ ์ฑ…์ž„ ๋ฒ”์œ„(Responsibility)๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๋Š” ๊ฒƒ์ด ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ ์„ฑ๊ณต์˜ ์—ด์‡ ์ž…๋‹ˆ๋‹ค. + +## ๐Ÿ”— ์—ฐ๊ฒฐ๋œ ์ง€์‹ +- [[Separation_of_Concerns]] +- [[Domain-Driven Design (DDD)]] diff --git a/10_Wiki/Topics/Systemic_Simulation_Principles.md b/10_Wiki/Topics/Systemic_Simulation_Principles.md new file mode 100644 index 00000000..3cf8eceb --- /dev/null +++ b/10_Wiki/Topics/Systemic_Simulation_Principles.md @@ -0,0 +1,24 @@ +--- +title: ์‹œ์Šคํ…œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์„ค๊ณ„ ์›๋ฆฌ +category: Systemic Modeling & Fun +tags: [Simulation, Physics Engine, Systemic Modeling, Ruleset] +created: 2026-04-20 +--- + +# ์‹œ์Šคํ…œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์„ค๊ณ„ ์›๋ฆฌ + +## ๐ŸŽฏ ๊ฐœ์š” (Overview) +ํ˜„์‹ค ์„ธ๊ณ„์˜ ๋ฌผ๋ฆฌ ๋ฒ•์น™์ด๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ์ˆ˜ํ•™์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ์ ˆ๋Œ€์ ์œผ๋กœ ์šฐํšŒํ•  ์ˆ˜ ์—†๋Š” ์‹œ์Šคํ…œ ๋‚ด์˜ ๋ฒ•(Law)์œผ๋กœ ๊ตฌ์ถ•ํ•˜๋Š” ์„ค๊ณ„ ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. + +## ๐Ÿš€ ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜ (Mechanisms) +- **๊ทœ์น™ ๊ฐ•์ œ (Ruleset Enforcement)**: ๋ชจ๋“  ์ƒํƒœ ๋ณ€ํ™”๋Š” ์‚ฌ์ „์— ์ •์˜๋œ ๋ฌผ๋ฆฌ ์—”์ง„ ํ•จ์ˆ˜(`checkCollision` ๋“ฑ)๋ฅผ ๊ฑฐ์ณ์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. +- **์ˆ˜ํ•™์  ๋ชจ๋ธ๋ง**: ๋ณ€ํ™”๋ฅผ ์‹œ๊ฐ์  ๋ฌ˜์‚ฌ๊ฐ€ ์•„๋‹Œ ๋ฐ์ดํ„ฐ์™€ ์ˆ˜์‹์œผ๋กœ ๋จผ์ € ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๋ ˆ์Šจ ๋Ÿฐ (Lesson Learned) +> [!TIP] +> **"๋ชจ๋“  ์‹œ๋ฎฌ๋ ˆ์ด์…˜์€ ์ˆ˜ํ•™์  ๊ทœ์น™์„ ์ ˆ๋Œ€ ์šฐํšŒํ•  ์ˆ˜ ์—†๋„๋ก ๊ฐ•์ œํ•ด์•ผ ํ•œ๋‹ค."** +> ์ด๋ฅผ ํ†ตํ•ด ๋‹จ์ˆœํ•œ ๊ฒŒ์ž„์„ ๋„˜์–ด ์ž์œจ์ฃผํ–‰, ๋ฌผ๋ฆฌ ์—”์ง„ ๋“ฑ ๊ณ ๋„์˜ ๊ฒฐ์ •๋ก ์  ์‹œ์Šคํ…œ ๋ชจ๋ธ๋ง์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค. + +## ๐Ÿ”— ์—ฐ๊ฒฐ๋œ ์ง€์‹ +- [[WebWorker_Performance]] +- [[Separation_of_Concerns]] diff --git a/10_Wiki/Topics/WebWorker_Performance.md b/10_Wiki/Topics/WebWorker_Performance.md new file mode 100644 index 00000000..0dae6511 --- /dev/null +++ b/10_Wiki/Topics/WebWorker_Performance.md @@ -0,0 +1,24 @@ +--- +title: WebWorker๋ฅผ ์ด์šฉํ•œ ๊ณ ์„ฑ๋Šฅ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ +category: Web & Performance +tags: [Web Worker, Concurrency, Performance, UI responsiveness] +created: 2026-04-20 +--- + +# WebWorker๋ฅผ ์ด์šฉํ•œ ๊ณ ์„ฑ๋Šฅ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ + +## ๐ŸŽฏ ๊ฐœ์š” (Overview) +์‹ค์‹œ๊ฐ„ ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ๋งค์šฐ ๋นˆ๋ฒˆํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(์˜ˆ: ๊ฒŒ์ž„, ์‹œ๋ฎฌ๋ ˆ์ด์…˜)์—์„œ UI ์Šค๋ ˆ๋“œ์™€ ๋ณต์žกํ•œ ์—ฐ์‚ฐ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ **ํ”„๋ ˆ์ž„ ๋“œ๋กญ(Jank)**์„ ๋ฐฉ์ง€ํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. + +## ๐Ÿš€ ์ฃผ์š” ์›์น™ (Key Principles) +- **์Šค๋ ˆ๋“œ ๋ถ„๋ฆฌ (Thread Isolation)**: ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ(Web Worker)์—์„œ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋ Œ๋”๋ง์—๋งŒ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค. +- **๋ฉ”์‹œ์ง• ๊ธฐ๋ฐ˜ ํ†ต์‹  (Messaging Architecture)**: `postMessage`์™€ `onmessage`๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์•„ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค. + +## ๐Ÿ’ก ๋ ˆ์Šจ ๋Ÿฐ (Lesson Learned) +> [!IMPORTANT] +> **"์„ฑ๋Šฅ ๋ณ‘๋ชฉ ํ˜„์ƒ์€ ์ข…์ข… '์Šค๋ ˆ๋”ฉ(Threading)'์˜ ๋ฌธ์ œ์ด๋‹ค."** +> ๋ณต์žกํ•œ ๋ฌผ๋ฆฌ ๊ณ„์‚ฐ์ด๋‚˜ ๋ฃจํ”„๊ฐ€ UI ์‘๋‹ต์„ฑ์„ ํ•ด์น˜์ง€ ์•Š๋„๋ก, ์—ฐ์‚ฐ ์—”์ง„์„ ์™„์ „ํžˆ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋กœ ๊ฒฉ๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ถ€๋“œ๋Ÿฌ์šด UX์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. + +## ๐Ÿ”— ์—ฐ๊ฒฐ๋œ ์ง€์‹ +- [[Separation_of_Concerns]] +- [[Systemic_Simulation_Principles]] diff --git a/package.json b/package.json new file mode 100644 index 00000000..45c4442e --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "tetris-game", + "version": "1.0.0", + "description": "A high-performance Tetris game using Web Workers.", + "main": "src/index.js", + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test" + } +} \ No newline at end of file diff --git a/src/TetrisGame.jsx b/src/TetrisGame.jsx new file mode 100644 index 00000000..e24ca092 --- /dev/null +++ b/src/TetrisGame.jsx @@ -0,0 +1,97 @@ +import React, { useState, useEffect, useCallback } from 'react'; + +const TetrisGame = () => { + // [State Management] ์ „์—ญ ์ƒํƒœ: ๋ณด๋“œ์™€ ํ˜„์žฌ ๋ธ”๋ก ์ •๋ณด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. + const [gameState, setGameState] = useState({ board: [], piece: null }); + const [workerStatus, setWorkerStatus] = useState("Initializing..."); + + useEffect(() => { + // 1. Web Worker ์ดˆ๊ธฐํ™” ๋ฐ ํ†ต์‹  ์„ค์ • + const worker = new Worker(new URL('./gameWorker.js', import.meta.url), { type: 'module' }); + setWorkerStatus("Running..."); + + // 2. ์›Œ์ปค๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•  ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก (Web Worker์˜ ํ•ต์‹ฌ) + worker.onmessage = (e) => { + const data = e.data; + if (data.type === 'READY' || data.type === 'UPDATE') { + // ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์—ญ ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. (Single Source of Truth ์›์น™ ์ค€์ˆ˜) + setGameState({ board: data.board, piece: data.piece }); + } else if (data.type === 'ERROR') { + console.error("Game Worker Error:", data.message); + } + }; + + // 3. ์›Œ์ปค์— ์ดˆ๊ธฐํ™” ๋ช…๋ น ์ „์†ก (Worker ์‹œ์ž‘) + worker.postMessage({ type: 'INIT' }); + + // Cleanup ํ•จ์ˆ˜: ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ ์›Œ์ปค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. + return () => { + worker.terminate(); + }; + }, []); // ๋งˆ์šดํŠธ ์‹œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ (useEffect) + + + // [Game Loop] ๊ฒŒ์ž„ ๋ฃจํ”„ ๊ด€๋ฆฌ (ํ•ต์‹ฌ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ถ€๋ถ„) + const handleGameLoop = useCallback(() => { + if (!gameState.piece) return; + + // 1์ดˆ์— 1๋ฒˆ์”ฉ gravity step์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. + const worker = new Worker(new URL('./gameWorker.js', import.meta.url), { type: 'module' }); + worker.postMessage({ type: 'MOVE_STEP', payload: {} }); + + // ๋‹ค์Œ ํ”„๋ ˆ์ž„์—์„œ ๋‹ค์‹œ ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ง€์†์ ์ธ ์›€์ง์ž„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + requestAnimationFrame(handleGameLoop); + }, [gameState.piece]); // piece๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ๋ฃจํ”„ ์‹œ์ž‘ + + useEffect(() => { + if (gameState.piece) { + const animationFrameId = requestAnimationFrame(handleGameLoop); + return () => cancelAnimationFrame(animationFrameId); + } + }, [gameState.piece, handleGameLoop]); + + + // ๋ Œ๋”๋ง ๋กœ์ง: ๊ฒŒ์ž„ ๋ณด๋“œ์™€ ๋ธ”๋ก์„ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค. + const renderBoard = () => { + return ( +
+ {/* React๋Š” ๋ฐฐ์—ด์˜ ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ ์…€์— CSS ํด๋ž˜์Šค๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. */} + {gameState.board.map((row, y) => ( +
+ {row.map((cell, x) => ( +
0 ? styles.occupied : {}} + title={`(${x}, ${y})`} + >
+ ))} +
+ ))} +
+ ); + }; + + return ( +
+

Tetris Clone: High-Performance Engine

+

Status: {workerStatus}

+ {renderBoard()} + +
+ ); +}; + +const styles = { + container: { padding: '20px', fontFamily: 'Arial, sans-serif' }, + board: { + display: 'flex', + flexDirection: 'column', + border: '4px solid #333', + width: '250px' + }, + occupied: { backgroundColor: '#ccc', border: '1px solid #aaa' } +}; + +export default TetrisGame; \ No newline at end of file diff --git a/src/gameWorker.js b/src/gameWorker.js new file mode 100644 index 00000000..9633e184 --- /dev/null +++ b/src/gameWorker.js @@ -0,0 +1,73 @@ +/** + * @file Web Worker: Tetris Game Logic Engine (The Core Rulebook) + * ์ด ํŒŒ์ผ์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๋ถ„๋ฆฌ๋˜์–ด ์‹คํ–‰๋˜๋ฉฐ, ๊ฒŒ์ž„์˜ ๋ชจ๋“  ๋ฌผ๋ฆฌ์  ๊ทœ์น™์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + */ + +let gameBoard = []; // 20x10 Grid representation +const BOARD_WIDTH = 10; +const BOARD_HEIGHT = 20; + +// ์ดˆ๊ธฐ ๋ณด๋“œ ์ƒํƒœ ์„ค์ • (๋นˆ ๊ณต๊ฐ„) +function initializeBoard() { + gameBoard = Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(0)); +} + +// ๋ธ”๋ก ์ƒ์„ฑ ๋กœ์ง (๋žœ๋ค ๋ชจ์–‘, ๋žœ๋ค ์œ„์น˜) +function generatePiece() { + // ์‹ค์ œ๋กœ๋Š” ๋‹ค์–‘ํ•œ T, L, I, O ๋“ฑ์˜ ํ˜•ํƒœ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•จ. ์—ฌ๊ธฐ์„œ๋Š” ๋‹จ์ˆœ ์˜ˆ์‹œ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. + return { shape: [/* 4x4 matrix for a piece */], color: Math.random(), x: Math.floor(Math.random() * (BOARD_WIDTH - 3)), y: 0 }; +} + +// ์ถฉ๋Œ ๊ฐ์ง€ ๋กœ์ง (Collision Detection) +function checkCollision(piece, newX, newY) { + for (let row = 0; row < piece.shape.length; row++) { + for (let col = 0; col < piece.shape[row].length; col++) { + if (piece.shape[row][col] !== 0) { + const boardX = newX + col; + const boardY = newY + row; + + // ๊ฒฝ๊ณ„ ์ฒดํฌ ๋˜๋Š” ์ด๋ฏธ ๋ธ”๋ก์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ถฉ๋Œ ๋ฐœ์ƒ + if (boardX < 0 || boardX >= BOARD_WIDTH || boardY >= BOARD_HEIGHT || gameBoard[boardY][boardX] !== 0) { + return true; // Collision detected + } + } + } + } + return false; +} + +// ๊ฒŒ์ž„์˜ ํ•ต์‹ฌ ๋กœ์ง: ํ•œ ์Šคํ… ์ง„ํ–‰ (Gravity Step) +function movePiece(dx, dy) { + let newX = piece.x + dx; + let newY = piece.y + dy; + + if (!checkCollision(piece, newX, newY)) { + // 1. ์ƒํƒœ ์—…๋ฐ์ดํŠธ: ๋ณด๋“œ์˜ ์ขŒํ‘œ์™€ ๋ธ”๋ก์„ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค. + // 2. ์ถฉ๋Œ ์ฒดํฌ ๋ฐ ๊ณ ์ •: ๋ฐ”๋‹ฅ ๋˜๋Š” ๋‹ค๋ฅธ ๋ธ”๋ก์— ๋‹ฟ์œผ๋ฉด, ํ•ด๋‹น ์œ„์น˜๋ฅผ ๊ฒŒ์ž„ ๋ณด๋“œ์— ์˜๊ตฌ์ ์œผ๋กœ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. + // 3. ๋ผ์ธ ์ œ๊ฑฐ (Line Clearing): ๊ฐ€๋“ ์ฐฌ ํ–‰(Row)์„ ์ฐพ์•„ ์ง€์šฐ๊ณ  ์œ„์ชฝ ํ–‰๋“ค์„ ์•„๋ž˜๋กœ ๋–จ์–ด๋œจ๋ฆฝ๋‹ˆ๋‹ค. + return { success: true, board: [...gameBoard], piece: {...piece} }; + } else { + return { success: false, message: "Collision" }; + } +} + +// ๋ฉ”์ธ ๊ฒŒ์ž„ ๋ฃจํ”„ (Game Loop) +self.onmessage = function(e) { + const { type, payload } = e.data; + + switch (type) { + case 'INIT': + initializeBoard(); + // ์ดˆ๊ธฐ ๋ธ”๋ก ์ƒ์„ฑ ๋ฐ ์ƒํƒœ ์ „์†ก + self.postMessage({ type: 'READY', board: gameBoard, piece: generatePiece() }); + break; + case 'MOVE_STEP': + // ์‹ค์ œ ๊ฒŒ์ž„ ๋ฃจํ”„์˜ ํ•ต์‹ฌ ๋กœ์ง ํ˜ธ์ถœ + const result = movePiece(payload.dx || 0, payload.dy || 1); + self.postMessage({ type: 'UPDATE', board: result.board, piece: result.piece }); + break; + case 'ROTATE': + // ๋ธ”๋ก ํšŒ์ „ ๋กœ์ง ๊ตฌํ˜„ (Omitted for brevity) + break; + } +}; \ No newline at end of file