์ ์ง๋ณด์์ ํ์ฅ์ด ์ฉ์ดํ ํ๋ก ํธ์๋ ์ฑ์ ์ํ ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ๋ฅผ ํ์ํ์ธ์. ์์น, ์ด์ , ์ค์ ๊ตฌํ ์ ๋ต์ ์์๋ด ๋๋ค.
ํ๋ก ํธ์๋ ์ํคํ ์ฒ: ํ์ฅ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ
ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ณต์กํด์ง์ ๋ฐ๋ผ, ์ ์ ์๋ ์ํคํ ์ฒ๋ ์ ์ง๋ณด์์ฑ, ํ ์คํธ ์ฉ์ด์ฑ, ํ์ฅ์ฑ์ ์ํด ๋งค์ฐ ์ค์ํด์ก์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ ๊ฐ์ง ์ธ๊ธฐ ์๋ ์ํคํ ์ฒ ํจํด์ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ(ํฌํธ์ ์ด๋ํฐ๋ผ๊ณ ๋ ํจ)์ ํด๋ฆฐ ์ํคํ ์ฒ์ ๋๋ค. ๋ฐฑ์๋ ์ธ๊ณ์์ ์์๋์์ง๋ง, ์ด๋ฌํ ์์น๋ค์ ๊ฒฌ๊ณ ํ๊ณ ์ ์์ฑ ๋์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๋ง๋ค๊ธฐ ์ํด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉ๋ ์ ์์ต๋๋ค.
ํ๋ก ํธ์๋ ์ํคํ ์ฒ๋ ๋ฌด์์ธ๊ฐ?
ํ๋ก ํธ์๋ ์ํคํ ์ฒ๋ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ด์ ๋ค์ํ ๊ตฌ์ฑ ์์์ ๊ตฌ์กฐ, ์กฐ์ง ๋ฐ ์ํธ ์์ฉ์ ์ ์ํฉ๋๋ค. ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ด๋ป๊ฒ ๊ตฌ์ถ, ์ ์ง๋ณด์, ํ์ฅ๋๋์ง์ ๋ํ ์ฒญ์ฌ์ง์ ์ ๊ณตํฉ๋๋ค. ์ข์ ํ๋ก ํธ์๋ ์ํคํ ์ฒ๋ ๋ค์์ ์ด์งํฉ๋๋ค:
- ์ ์ง๋ณด์์ฑ: ์ฝ๋๋ฅผ ๋ ์ฝ๊ฒ ์ดํดํ๊ณ , ์์ ํ๊ณ , ๋๋ฒ๊น ํ ์ ์์ต๋๋ค.
- ํ ์คํธ ์ฉ์ด์ฑ: ๋จ์ ํ ์คํธ์ ํตํฉ ํ ์คํธ ์์ฑ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
- ํ์ฅ์ฑ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฆ๊ฐํ๋ ๋ณต์ก์ฑ๊ณผ ์ฌ์ฉ์ ๋ถํ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ํฉ๋๋ค.
- ์ฌ์ฌ์ฉ์ฑ: ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๋ฌ ๋ถ๋ถ์์ ์ฝ๋ ์ฌ์ฌ์ฉ์ ์ด์งํฉ๋๋ค.
- ์ ์ฐ์ฑ: ๋ณํํ๋ ์๊ตฌ์ฌํญ๊ณผ ์๋ก์ด ๊ธฐ์ ์ ์ ์ํฉ๋๋ค.
๋ช ํํ ์ํคํ ์ฒ๊ฐ ์์ผ๋ฉด ํ๋ก ํธ์๋ ํ๋ก์ ํธ๋ ๋น ๋ฅด๊ฒ ๋ชจ๋๋ฆฌ์ ๊ตฌ์กฐ๊ฐ ๋์ด ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง๊ณ , ๊ฐ๋ฐ ๋น์ฉ ์ฆ๊ฐ์ ๋ฏผ์ฒฉ์ฑ ๊ฐ์๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ ์๊ฐ
Alistair Cockburn์ด ์ ์ํ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค, UI ํ๋ ์์ํฌ, ์๋ํํฐ API์ ๊ฐ์ ์ธ๋ถ ์ข ์์ฑ์ผ๋ก๋ถํฐ ๋ถ๋ฆฌํ๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค. ์ด๋ ํฌํธ์ ์ด๋ํฐ๋ผ๋ ๊ฐ๋ ์ ํตํด ๋ฌ์ฑ๋ฉ๋๋ค.
ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ์ ํต์ฌ ๊ฐ๋ :
- ์ฝ์ด (๋๋ฉ์ธ): ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ ์ค ์ผ์ด์ค๋ฅผ ํฌํจํฉ๋๋ค. ์ธ๋ถ ํ๋ ์์ํฌ๋ ๊ธฐ์ ๋ก๋ถํฐ ๋ ๋ฆฝ์ ์ ๋๋ค.
- ํฌํธ: ์ฝ์ด๊ฐ ์ธ๋ถ ์ธ๊ณ์ ์ด๋ป๊ฒ ์ํธ ์์ฉํ๋์ง๋ฅผ ์ ์ํ๋ ์ธํฐํ์ด์ค์ ๋๋ค. ์ฝ์ด์ ์ ๋ ฅ ๋ฐ ์ถ๋ ฅ ๊ฒฝ๊ณ๋ฅผ ๋ํ๋ ๋๋ค.
- ์ด๋ํฐ: ์ฝ์ด๋ฅผ ํน์ ์ธ๋ถ ์์คํ ์ ์ฐ๊ฒฐํ๋ ํฌํธ์ ๊ตฌํ์ฒด์ ๋๋ค. ๋ ๊ฐ์ง ์ ํ์ ์ด๋ํฐ๊ฐ ์์ต๋๋ค:
- ์ฃผ๋ ์ด๋ํฐ (Primary Adapters): ์ฝ์ด์์ ์ํธ ์์ฉ์ ์์ํฉ๋๋ค. ์์๋ก๋ UI ์ปดํฌ๋ํธ, ๋ช ๋ น์ค ์ธํฐํ์ด์ค ๋๋ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ต๋๋ค.
- ํผ์ฃผ๋ ์ด๋ํฐ (Secondary Adapters): ์ฝ์ด์ ์ํด ํธ์ถ๋์ด ์ธ๋ถ ์์คํ ๊ณผ ์ํธ ์์ฉํฉ๋๋ค. ์์๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค, API ๋๋ ํ์ผ ์์คํ ์ด ์์ต๋๋ค.
์ฝ์ด๋ ํน์ ์ด๋ํฐ์ ๋ํด ์๋ฌด๊ฒ๋ ์์ง ๋ชปํฉ๋๋ค. ์ค์ง ํฌํธ๋ฅผ ํตํด์๋ง ์ด๋ํฐ์ ์ํธ ์์ฉํฉ๋๋ค. ์ด๋ฌํ ๋์ปคํ๋ง์ ํตํด ์ฝ์ด ๋ก์ง์ ์ํฅ์ ์ฃผ์ง ์๊ณ ๋ค๋ฅธ ์ด๋ํฐ๋ฅผ ์ฝ๊ฒ ๊ต์ฒดํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฃผ๋ ์ด๋ํฐ๋ฅผ ๊ต์ฒดํ๋ ๊ฒ๋ง์ผ๋ก ํ UI ํ๋ ์์ํฌ(์: React)์์ ๋ค๋ฅธ ํ๋ ์์ํฌ(์: Vue.js)๋ก ์ ํํ ์ ์์ต๋๋ค.
ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ์ ์ด์ :
- ํฅ์๋ ํ ์คํธ ์ฉ์ด์ฑ: ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ธ๋ถ ์ข ์์ฑ์ ์์กดํ์ง ์๊ณ ๊ฒฉ๋ฆฌํ์ฌ ์ฝ๊ฒ ํ ์คํธํ ์ ์์ต๋๋ค. ๋ชจ์ ์ด๋ํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ถ ์์คํ ์ ๋์์ ์๋ฎฌ๋ ์ด์ ํ ์ ์์ต๋๋ค.
- ์ฆ๊ฐ๋ ์ ์ง๋ณด์์ฑ: ์ธ๋ถ ์์คํ ์ ๋ณ๊ฒฝ์ด ์ฝ์ด ๋ก์ง์ ๋ฏธ์น๋ ์ํฅ์ ์ต์ํํฉ๋๋ค. ์ด๋ก ์ธํด ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ง๋ณด์ํ๊ณ ๋ฐ์ ์ํค๊ธฐ ๋ ์ฌ์์ง๋๋ค.
- ๋ ํฐ ์ ์ฐ์ฑ: ์ด๋ํฐ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ๊ต์ฒดํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ก์ด ๊ธฐ์ ๊ณผ ์๊ตฌ์ฌํญ์ ์ฝ๊ฒ ์ ์์ํฌ ์ ์์ต๋๋ค.
- ํฅ์๋ ์ฌ์ฌ์ฉ์ฑ: ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๋ค๋ฅธ ์ด๋ํฐ์ ์ฐ๊ฒฐํ์ฌ ๋ค์ํ ์ปจํ ์คํธ์์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํด๋ฆฐ ์ํคํ ์ฒ ์๊ฐ
Robert C. Martin (Uncle Bob)์ ์ํด ๋์คํ๋ ํด๋ฆฐ ์ํคํ ์ฒ๋ ๊ด์ฌ์ฌ ๋ถ๋ฆฌ์ ๋์ปคํ๋ง์ ๊ฐ์กฐํ๋ ๋ ๋ค๋ฅธ ์ํคํ ์ฒ ํจํด์ ๋๋ค. ์ด๋ ํ๋ ์์ํฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค, UI ๋ฐ ๋ชจ๋ ์ธ๋ถ ์์๋ก๋ถํฐ ๋ ๋ฆฝ์ ์ธ ์์คํ ์ ๋ง๋๋ ๋ฐ ์ค์ ์ ๋ก๋๋ค.
ํด๋ฆฐ ์ํคํ ์ฒ์ ํต์ฌ ๊ฐ๋ :
ํด๋ฆฐ ์ํคํ ์ฒ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ฌ์ ๊ณ์ธต์ผ๋ก ๊ตฌ์ฑํ๋ฉฐ, ๊ฐ์ฅ ์ถ์์ ์ด๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ฝ๋๋ ์ค์์, ๊ฐ์ฅ ๊ตฌ์ฒด์ ์ด๊ณ ๊ธฐ์ ์ ํนํ๋ ์ฝ๋๋ ์ธ๋ถ ๊ณ์ธต์ ์์นํฉ๋๋ค.
- ์ํฐํฐ: ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๋น์ฆ๋์ค ๊ฐ์ฒด์ ๊ท์น์ ๋ํ๋ ๋๋ค. ์ธ๋ถ ์์คํ ์ผ๋ก๋ถํฐ ๋ ๋ฆฝ์ ์ ๋๋ค.
- ์ ์ค ์ผ์ด์ค: ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ฌ์ฉ์๊ฐ ์์คํ ๊ณผ ์ํธ ์์ฉํ๋ ๋ฐฉ์์ ์ ์ํฉ๋๋ค. ์ํฐํฐ๋ฅผ ์กฐ์จํ์ฌ ํน์ ์์ ์ ์ํํฉ๋๋ค.
- ์ธํฐํ์ด์ค ์ด๋ํฐ: ์ ์ค ์ผ์ด์ค์ ์ธ๋ถ ์์คํ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ณํํฉ๋๋ค. ์ด ๊ณ์ธต์๋ ํ๋ ์ ํฐ, ์ปจํธ๋กค๋ฌ, ๊ฒ์ดํธ์จ์ด๊ฐ ํฌํจ๋ฉ๋๋ค.
- ํ๋ ์์ํฌ ๋ฐ ๋๋ผ์ด๋ฒ: UI ํ๋ ์์ํฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ๊ธฐํ ์ธ๋ถ ๊ธฐ์ ์ ํฌํจํ๋ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ ๊ณ์ธต์ ๋๋ค.
ํด๋ฆฐ ์ํคํ ์ฒ์ ์์กด์ฑ ๊ท์น์ ์ธ๋ถ ๊ณ์ธต์ด ๋ด๋ถ ๊ณ์ธต์ ์์กดํ ์๋ ์์ง๋ง, ๋ด๋ถ ๊ณ์ธต์ ์ธ๋ถ ๊ณ์ธต์ ์์กดํ ์ ์๋ค๊ณ ๋ช ์ํฉ๋๋ค. ์ด๋ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ด ์ธ๋ถ ํ๋ ์์ํฌ๋ ๊ธฐ์ ๋ก๋ถํฐ ๋ ๋ฆฝ์ ์์ ๋ณด์ฅํฉ๋๋ค.
ํด๋ฆฐ ์ํคํ ์ฒ์ ์ด์ :
- ํ๋ ์์ํฌ๋ก๋ถํฐ ๋ ๋ฆฝ์ : ์ํคํ ์ฒ๋ ๊ธฐ๋ฅ์ด ํ๋ถํ ์ํํธ์จ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์กด์ฌ์ ์์กดํ์ง ์์ต๋๋ค. ์ด๋ฅผ ํตํด ํ๋ ์์ํฌ๋ฅผ ๋๊ตฌ๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์์คํ ์ ํ๋ ์์ํฌ์ ์ ํ๋ ์ ์ฝ์ ์ต์ง๋ก ๋ง์ถ ํ์๊ฐ ์์ต๋๋ค.
- ํ ์คํธ ์ฉ์ด์ฑ: ๋น์ฆ๋์ค ๊ท์น์ UI, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์น ์๋ฒ ๋๋ ๋ค๋ฅธ ์ธ๋ถ ์์ ์์ด ํ ์คํธํ ์ ์์ต๋๋ค.
- UI๋ก๋ถํฐ ๋ ๋ฆฝ์ : UI๋ ์์คํ ์ ๋๋จธ์ง ๋ถ๋ถ์ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ์ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ์น UI๋ ๋น์ฆ๋์ค ๊ท์น์ ๋ณ๊ฒฝํ์ง ์๊ณ ์ฝ์ UI๋ก ๋์ฒด๋ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๋ ๋ฆฝ์ : Oracle์ด๋ SQL Server๋ฅผ Mongo, BigTable, CouchDB ๋๋ ๋ค๋ฅธ ๊ฒ์ผ๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค. ๋น์ฆ๋์ค ๊ท์น์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฝ๋งค์ด์ง ์์ต๋๋ค.
- ๋ชจ๋ ์ธ๋ถ ์์๋ก๋ถํฐ ๋ ๋ฆฝ์ : ์ค์ ๋ก ๋น์ฆ๋์ค ๊ท์น์ ์ธ๋ถ ์ธ๊ณ์ ๋ํด *์๋ฌด๊ฒ๋* ์์ง ๋ชปํฉ๋๋ค.
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ ์ ์ฉํ๊ธฐ
ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ์ ํด๋ฆฐ ์ํคํ ์ฒ๋ ์ข ์ข ๋ฐฑ์๋ ๊ฐ๋ฐ๊ณผ ์ฐ๊ด๋์ง๋ง, ๊ทธ ์์น๋ค์ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํคํ ์ฒ์ ์ ์ง๋ณด์์ฑ์ ๊ฐ์ ํ๊ธฐ ์ํด ํจ๊ณผ์ ์ผ๋ก ์ ์ฉ๋ ์ ์์ต๋๋ค. ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
1. ์ฝ์ด(๋๋ฉ์ธ) ์๋ณํ๊ธฐ
์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์๋ณํ๋ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์๋ UI ํ๋ ์์ํฌ๋ ์ธ๋ถ API์ ๋ ๋ฆฝ์ ์ธ ์ํฐํฐ, ์ ์ค ์ผ์ด์ค, ๋น์ฆ๋์ค ๊ท์น์ด ํฌํจ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์ ์์๊ฑฐ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฝ์ด๋ ์ํ, ์ฅ๋ฐ๊ตฌ๋, ์ฃผ๋ฌธ ๊ด๋ฆฌ ๋ก์ง์ ํฌํจํ ์ ์์ต๋๋ค.
์์: ์์ ๊ด๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฝ์ด ๋๋ฉ์ธ์ ๋ค์์ผ๋ก ๊ตฌ์ฑ๋ ์ ์์ต๋๋ค:
- ์ํฐํฐ: Task(์์ ), Project(ํ๋ก์ ํธ), User(์ฌ์ฉ์)
- ์ ์ค ์ผ์ด์ค: CreateTask(์์ ์์ฑ), UpdateTask(์์ ์์ ), AssignTask(์์ ํ ๋น), CompleteTask(์์ ์๋ฃ), ListTasks(์์ ๋ชฉ๋ก ์กฐํ)
- ๋น์ฆ๋์ค ๊ท์น: ์์ ์ ์ ๋ชฉ์ด ์์ด์ผ ํ๋ฉฐ, ํ๋ก์ ํธ์ ๋ฉค๋ฒ๊ฐ ์๋ ์ฌ์ฉ์์๊ฒ ์์ ์ ํ ๋นํ ์ ์์ต๋๋ค.
2. ํฌํธ์ ์ด๋ํฐ(ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ) ๋๋ ๊ณ์ธต(ํด๋ฆฐ ์ํคํ ์ฒ) ์ ์ํ๊ธฐ
๋ค์์ผ๋ก, ์ฝ์ด๋ฅผ ์ธ๋ถ ์์คํ ๊ณผ ๋ถ๋ฆฌํ๋ ํฌํธ์ ์ด๋ํฐ(ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ) ๋๋ ๊ณ์ธต(ํด๋ฆฐ ์ํคํ ์ฒ)์ ์ ์ํฉ๋๋ค. ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒ๋ค์ด ํฌํจ๋ ์ ์์ต๋๋ค:
- UI ์ปดํฌ๋ํธ (์ฃผ๋ ์ด๋ํฐ/ํ๋ ์์ํฌ ๋ฐ ๋๋ผ์ด๋ฒ): ์ฌ์ฉ์์ ์ํธ ์์ฉํ๋ React, Vue.js, Angular ์ปดํฌ๋ํธ.
- API ํด๋ผ์ด์ธํธ (ํผ์ฃผ๋ ์ด๋ํฐ/์ธํฐํ์ด์ค ์ด๋ํฐ): ๋ฐฑ์๋ API์ ์์ฒญ์ ๋ณด๋ด๋ ์๋น์ค.
- ๋ฐ์ดํฐ ์ ์ฅ์ (ํผ์ฃผ๋ ์ด๋ํฐ/์ธํฐํ์ด์ค ์ด๋ํฐ): ๋ก์ปฌ ์คํ ๋ฆฌ์ง, IndexedDB ๋๋ ๊ธฐํ ๋ฐ์ดํฐ ์ ์ฅ ๋ฉ์ปค๋์ฆ.
- ์ํ ๊ด๋ฆฌ (์ธํฐํ์ด์ค ์ด๋ํฐ): Redux, Vuex ๋๋ ๊ธฐํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ ์ฌ์ฉ ์์:
- ์ฝ์ด: ์์ ๊ด๋ฆฌ ๋ก์ง (์ํฐํฐ, ์ ์ค ์ผ์ด์ค, ๋น์ฆ๋์ค ๊ท์น).
- ํฌํธ:
TaskService(์์ ์์ฑ, ์์ , ์กฐํ๋ฅผ ์ํ ๋ฉ์๋ ์ ์). - ์ฃผ๋ ์ด๋ํฐ:
TaskService๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ์ด์ ์ํธ ์์ฉํ๋ React ์ปดํฌ๋ํธ. - ํผ์ฃผ๋ ์ด๋ํฐ:
TaskService๋ฅผ ๊ตฌํํ๊ณ ๋ฐฑ์๋ API์ ์์ฒญ์ ๋ณด๋ด๋ API ํด๋ผ์ด์ธํธ.
ํด๋ฆฐ ์ํคํ ์ฒ ์ฌ์ฉ ์์:
- ์ํฐํฐ: Task, Project, User (์์ JavaScript ๊ฐ์ฒด).
- ์ ์ค ์ผ์ด์ค: CreateTaskUseCase, UpdateTaskUseCase (์ํฐํฐ ์กฐ์จ).
- ์ธํฐํ์ด์ค ์ด๋ํฐ:
- ์ปจํธ๋กค๋ฌ: UI๋ก๋ถํฐ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ฒ๋ฆฌ.
- ํ๋ ์ ํฐ: UI์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ํฌ๋งท.
- ๊ฒ์ดํธ์จ์ด: API ํด๋ผ์ด์ธํธ์ ์ํธ ์์ฉ.
- ํ๋ ์์ํฌ ๋ฐ ๋๋ผ์ด๋ฒ: React ์ปดํฌ๋ํธ, API ํด๋ผ์ด์ธํธ (axios, fetch).
3. ์ด๋ํฐ(ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ) ๋๋ ๊ณ์ธต(ํด๋ฆฐ ์ํคํ ์ฒ) ๊ตฌํํ๊ธฐ
์ด์ ์ฝ์ด๋ฅผ ์ธ๋ถ ์์คํ ์ ์ฐ๊ฒฐํ๋ ์ด๋ํฐ๋ ๊ณ์ธต์ ๊ตฌํํฉ๋๋ค. ์ด๋ํฐ๋ ๊ณ์ธต์ด ์ฝ์ด๋ก๋ถํฐ ๋ ๋ฆฝ์ ์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ฝ์ด๊ฐ ์ค์ง ํฌํธ๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด์๋ง ์ด๋ค๊ณผ ์ํธ ์์ฉํ๋์ง ํ์ธํ์ธ์. ์ด๋ฅผ ํตํด ์ฝ์ด ๋ก์ง์ ์ํฅ์ ์ฃผ์ง ์๊ณ ๋ค๋ฅธ ์ด๋ํฐ๋ ๊ณ์ธต์ ์ฝ๊ฒ ๊ต์ฒดํ ์ ์์ต๋๋ค.
์์ (ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ):
// TaskService ํฌํธ
interface TaskService {
createTask(taskData: TaskData): Promise;
updateTask(taskId: string, taskData: TaskData): Promise;
getTask(taskId: string): Promise;
}
// API ํด๋ผ์ด์ธํธ ์ด๋ํฐ
class ApiTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
// ํ์คํฌ ์์ฑ์ ์ํ API ์์ฒญ
}
async updateTask(taskId: string, taskData: TaskData): Promise {
// ํ์คํฌ ์์ ์ ์ํ API ์์ฒญ
}
async getTask(taskId: string): Promise {
// ํ์คํฌ ์กฐํ๋ฅผ ์ํ API ์์ฒญ
}
}
// React ์ปดํฌ๋ํธ ์ด๋ํฐ
function TaskList() {
const taskService: TaskService = new ApiTaskService();
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// ํ์คํฌ ๋ชฉ๋ก ์
๋ฐ์ดํธ
};
// ...
}
์์ (ํด๋ฆฐ ์ํคํ ์ฒ):
// ์ํฐํฐ
class Task {
constructor(public id: string, public title: string, public description: string) {}
}
// ์ ์ค ์ผ์ด์ค
class CreateTaskUseCase {
constructor(private taskGateway: TaskGateway) {}
async execute(title: string, description: string): Promise {
const task = new Task(generateId(), title, description);
await this.taskGateway.create(task);
return task;
}
}
// ์ธํฐํ์ด์ค ์ด๋ํฐ - ๊ฒ์ดํธ์จ์ด
interface TaskGateway {
create(task: Task): Promise;
}
class ApiTaskGateway implements TaskGateway {
async create(task: Task): Promise {
// ํ์คํฌ ์์ฑ์ ์ํ API ์์ฒญ
}
}
// ์ธํฐํ์ด์ค ์ด๋ํฐ - ์ปจํธ๋กค๋ฌ
class TaskController {
constructor(private createTaskUseCase: CreateTaskUseCase) {}
async createTask(req: Request, res: Response) {
const { title, description } = req.body;
const task = await this.createTaskUseCase.execute(title, description);
res.json(task);
}
}
// ํ๋ ์์ํฌ ๋ฐ ๋๋ผ์ด๋ฒ - React ์ปดํฌ๋ํธ
function TaskForm() {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const apiTaskGateway = new ApiTaskGateway();
const createTaskUseCase = new CreateTaskUseCase(apiTaskGateway);
const taskController = new TaskController(createTaskUseCase);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await taskController.createTask({ body: { title, description } } as Request, { json: (data: any) => console.log(data) } as Response);
};
return (
);
}
4. ์์กด์ฑ ์ฃผ์ ๊ตฌํํ๊ธฐ
์ฝ์ด๋ฅผ ์ธ๋ถ ์์คํ ์ผ๋ก๋ถํฐ ๋์ฑ ๋ถ๋ฆฌํ๊ธฐ ์ํด, ์์กด์ฑ ์ฃผ์ ์ ์ฌ์ฉํ์ฌ ์ด๋ํฐ๋ ๊ณ์ธต์ ์ฝ์ด์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฝ์ด ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ๋ ์ด๋ํฐ๋ ๊ณ์ธต์ ๋ค๋ฅธ ๊ตฌํ์ฒด๋ฅผ ์ฝ๊ฒ ๊ต์ฒดํ ์ ์์ต๋๋ค.
์์:
// TaskList ์ปดํฌ๋ํธ์ TaskService ์ฃผ์
ํ๊ธฐ
function TaskList(props: { taskService: TaskService }) {
const { taskService } = props;
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// ํ์คํฌ ๋ชฉ๋ก ์
๋ฐ์ดํธ
};
// ...
}
// ์ฌ์ฉ๋ฒ
const apiTaskService = new ApiTaskService();
5. ๋จ์ ํ ์คํธ ์์ฑํ๊ธฐ
ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ์ ํต์ฌ ์ด์ ์ค ํ๋๋ ํฅ์๋ ํ ์คํธ ์ฉ์ด์ฑ์ ๋๋ค. ์ธ๋ถ ์ข ์์ฑ์ ์์กดํ์ง ์๊ณ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๋ํ ๋จ์ ํ ์คํธ๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค. ๋ชจ์ ์ด๋ํฐ๋ ๊ณ์ธต์ ์ฌ์ฉํ์ฌ ์ธ๋ถ ์์คํ ์ ๋์์ ์๋ฎฌ๋ ์ด์ ํ๊ณ ์ฝ์ด ๋ก์ง์ด ์์๋๋ก ์๋ํ๋์ง ๊ฒ์ฆํ์ธ์.
์์:
// ๋ชจ์ TaskService
class MockTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
return Promise.resolve({ id: '1', ...taskData });
}
async updateTask(taskId: string, taskData: TaskData): Promise {
return Promise.resolve({ id: taskId, ...taskData });
}
async getTask(taskId: string): Promise {
return Promise.resolve({ id: taskId, title: 'Test Task', description: 'Test Description' });
}
}
// ๋จ์ ํ
์คํธ
describe('TaskList', () => {
it('ํ์คํฌ๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค', async () => {
const mockTaskService = new MockTaskService();
const taskList = new TaskList({ taskService: mockTaskService });
const taskData = { title: 'New Task', description: 'New Description' };
const newTask = await taskList.handleCreateTask(taskData);
expect(newTask.title).toBe('New Task');
expect(newTask.description).toBe('New Description');
});
});
์ค์ฉ์ ์ธ ๊ณ ๋ ค์ฌํญ๊ณผ ๊ณผ์
ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ๋ ์๋นํ ์ด์ ์ ์ ๊ณตํ์ง๋ง, ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ ์ฉํ ๋ ์ผ๋์ ๋์ด์ผ ํ ๋ช ๊ฐ์ง ์ค์ฉ์ ์ธ ๊ณ ๋ ค์ฌํญ๊ณผ ๊ณผ์ ๋ ์์ต๋๋ค:
- ๋ณต์ก์ฑ ์ฆ๊ฐ: ์ด๋ฌํ ์ํคํ ์ฒ๋ ํนํ ์๊ฑฐ๋ ๊ฐ๋จํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ์ฝ๋๋ฒ ์ด์ค์ ๋ณต์ก์ฑ์ ๋ํ ์ ์์ต๋๋ค.
- ํ์ต ๊ณก์ : ๊ฐ๋ฐ์๋ ์ด๋ฌํ ์ํคํ ์ฒ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ตฌํํ๊ธฐ ์ํด ์๋ก์ด ๊ฐ๋ ๊ณผ ํจํด์ ๋ฐฐ์์ผ ํ ์ ์์ต๋๋ค.
- ๊ณผ์ ์์ง๋์ด๋ง: ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ณผ์ ์์ง๋์ด๋งํ๋ ๊ฒ์ ํผํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ฐ๋จํ ์ํคํ ์ฒ๋ก ์์ํ์ฌ ํ์์ ๋ฐ๋ผ ์ ์ฐจ ๋ณต์ก์ฑ์ ์ถ๊ฐํ์ธ์.
- ์ถ์ํ ๊ท ํ: ์ ์ ํ ์์ค์ ์ถ์ํ๋ฅผ ์ฐพ๋ ๊ฒ์ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ๋๋ฌด ๋ง์ ์ถ์ํ๋ ์ฝ๋๋ฅผ ์ดํดํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค ์ ์๊ณ , ๋๋ฌด ์ ์ ์ถ์ํ๋ ๊ฐํ ๊ฒฐํฉ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
- ์ฑ๋ฅ ๊ณ ๋ ค์ฌํญ: ๊ณผ๋ํ ์ถ์ํ ๊ณ์ธต์ ์ ์ฌ์ ์ผ๋ก ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ํ๋กํ์ผ๋งํ๊ณ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๊ตญ์ ์ ์ธ ์์ ๋ฐ ์ ์ฉ ์ฌ๋ก
ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ์ ์์น์ ์ง๋ฆฌ์ ์์น๋ ๋ฌธํ์ ๋งฅ๋ฝ์ ๊ด๊ณ์์ด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ ์ฉํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ ๋ฐ ์ ์ฉ ๋ฐฉ์์ ํ๋ก์ ํธ ์๊ตฌ์ฌํญ๊ณผ ๊ฐ๋ฐํ์ ์ ํธ๋์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.
์์ 1: ๊ธ๋ก๋ฒ ์ ์์๊ฑฐ๋ ํ๋ซํผ
๊ธ๋ก๋ฒ ์ ์์๊ฑฐ๋ ํ๋ซํผ์ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ๋ฅผ ์ฌ์ฉํ์ฌ ํต์ฌ ์ฅ๋ฐ๊ตฌ๋ ๋ฐ ์ฃผ๋ฌธ ๊ด๋ฆฌ ๋ก์ง์ UI ํ๋ ์์ํฌ ๋ฐ ๊ฒฐ์ ๊ฒ์ดํธ์จ์ด๋ก๋ถํฐ ๋ถ๋ฆฌํ ์ ์์ต๋๋ค. ์ฝ์ด๋ ์ํ ๊ด๋ฆฌ, ๊ฐ๊ฒฉ ๊ณ์ฐ, ์ฃผ๋ฌธ ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํฉ๋๋ค. ์ฃผ๋ ์ด๋ํฐ์๋ ์ํ ์นดํ๋ก๊ทธ, ์ฅ๋ฐ๊ตฌ๋, ๊ฒฐ์ ํ์ด์ง๋ฅผ ์ํ React ์ปดํฌ๋ํธ๊ฐ ํฌํจ๋ฉ๋๋ค. ํผ์ฃผ๋ ์ด๋ํฐ์๋ ๋ค์ํ ๊ฒฐ์ ๊ฒ์ดํธ์จ์ด(์: Stripe, PayPal, Alipay) ๋ฐ ๋ฐฐ์ก ์ ์ฒด(์: FedEx, DHL, UPS)๋ฅผ ์ํ API ํด๋ผ์ด์ธํธ๊ฐ ํฌํจ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ํ๋ซํผ์ ๋ค์ํ ์ง์ญ์ ๊ฒฐ์ ์๋จ๊ณผ ๋ฐฐ์ก ์ต์ ์ ์ฝ๊ฒ ์ ์ํ ์ ์์ต๋๋ค.
์์ 2: ๋ค๊ตญ์ด ์์ ๋ฏธ๋์ด ์ ํ๋ฆฌ์ผ์ด์
๋ค๊ตญ์ด ์์ ๋ฏธ๋์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ํด๋ฆฐ ์ํคํ ์ฒ๋ฅผ ์ฌ์ฉํ์ฌ ํต์ฌ ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ์ฝํ ์ธ ๊ด๋ฆฌ ๋ก์ง์ UI ๋ฐ ํ์งํ ํ๋ ์์ํฌ๋ก๋ถํฐ ๋ถ๋ฆฌํ ์ ์์ต๋๋ค. ์ํฐํฐ๋ ์ฌ์ฉ์, ๊ฒ์๋ฌผ, ๋๊ธ์ ๋ํ๋ ๋๋ค. ์ ์ค ์ผ์ด์ค๋ ์ฌ์ฉ์๊ฐ ์ฝํ ์ธ ๋ฅผ ์์ฑ, ๊ณต์ ํ๊ณ ์ํธ ์์ฉํ๋ ๋ฐฉ๋ฒ์ ์ ์ํฉ๋๋ค. ์ธํฐํ์ด์ค ์ด๋ํฐ๋ ์ฝํ ์ธ ๋ฅผ ๋ค๋ฅธ ์ธ์ด๋ก ๋ฒ์ญํ๊ณ ๋ค๋ฅธ UI ์ปดํฌ๋ํธ์ ๋ง๊ฒ ๋ฐ์ดํฐ๋ฅผ ํฌ๋งทํ๋ ๊ฒ์ ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ก์ด ์ธ์ด๋ฅผ ์ฝ๊ฒ ์ง์ํ๊ณ ๋ค์ํ ๋ฌธํ์ ์ ํธ๋์ ์ ์ํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
ํฅ์ฌ๊ณ ๋ ๋ฐ ํด๋ฆฐ ์ํคํ ์ฒ๋ ์ ์ง๋ณด์ ๊ฐ๋ฅํ๊ณ , ํ ์คํธ ๊ฐ๋ฅํ๋ฉฐ, ํ์ฅ ๊ฐ๋ฅํ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ท์คํ ์์น์ ์ ๊ณตํฉ๋๋ค. ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ธ๋ถ ์ข ์์ฑ์ผ๋ก๋ถํฐ ๋ถ๋ฆฌํจ์ผ๋ก์จ, ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋ฐ์ ํ๊ธฐ ๋ ์ฌ์ด ์ ์ฐํ๊ณ ์ ์์ฑ ๋์ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ด๋ฌํ ์ํคํ ์ฒ๋ ์ด๊ธฐ์ ์ฝ๊ฐ์ ๋ณต์ก์ฑ์ ๋ํ ์ ์์ง๋ง, ์ ์ง๋ณด์์ฑ, ํ ์คํธ ์ฉ์ด์ฑ, ํ์ฅ์ฑ ์ธก๋ฉด์์์ ์ฅ๊ธฐ์ ์ธ ์ด์ ์ ๋ณต์กํ ํ๋ก ํธ์๋ ํ๋ก์ ํธ์ ๋ํ ๊ฐ์น ์๋ ํฌ์๊ฐ ๋ฉ๋๋ค. ๊ฐ๋จํ ์ํคํ ์ฒ๋ก ์์ํ์ฌ ํ์์ ๋ฐ๋ผ ์ ์ฐจ ๋ณต์ก์ฑ์ ์ถ๊ฐํ๊ณ , ๊ด๋ จ๋ ์ค์ฉ์ ์ธ ๊ณ ๋ ค์ฌํญ๊ณผ ๊ณผ์ ๋ฅผ ์ ์คํ๊ฒ ๊ณ ๋ คํ๋ ๊ฒ์ ์์ง ๋ง์ธ์.
์ด๋ฌํ ์ํคํ ์ฒ ํจํด์ ์ฑํํจ์ผ๋ก์จ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ์ ์ธ๊ณ ์ฌ์ฉ์์ ๋ณํํ๋ ์๊ตฌ๋ฅผ ์ถฉ์กฑ์ํฌ ์ ์๋ ๋ ๊ฒฌ๊ณ ํ๊ณ ์ ๋ขฐํ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
๋ ์ฝ์๊ฑฐ๋ฆฌ
- ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ: https://alistaircockburn.com/hexagonal-architecture/
- ํด๋ฆฐ ์ํคํ ์ฒ: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html