20_Oct_2021 ๐ฐ ์๋ฆฌ์ค AI ํธ๋ TIL: ์ํ๊ด๋ฆฌ
์ํ ๊ด๋ฆฌ
- ์ํ ๊ด๋ฆฌ ๊ธฐ์ ์ด๋ ์ฑ ์์์์ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ ๋ฑ์ ์ ์ฅํ๊ณ ํ๋ ์ด์์ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
- ํ ์ปดํฌ๋ํธ ์์์์ ์ํ, ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ์ ์ํ, ์ ์ฒด ์ฑ์ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ชจ๋ ํฌํจํ๋ค.
MPA์ SPA์์์ ์ํ ๊ด๋ฆฌ
- MPA์์๋ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํด ํ์ด์ฆ๋ฅผ ๋ ๋๋งํ๋ฏ๋ก, ํด๋ผ์ด์ธํธ์ ๋ฐ์ดํฐ์ ์๋ฒ์ ๋ฐ์ดํฐ๊ฐ ํฐ ์ฐจ์ด๋ฅผ ๊ฐ์ง์ง ์๋๋ค.
- SPA์์๋ ์์ฒด์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ , ์๋ฒ์์ ๋๊ธฐํ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ง์ ์ฒ๋ฆฌํ๋ค. ๊ทธ ์ธ ๋ฐ์ดํฐ๋ Client๋ง์ผ๋ก ๋ฐ์ดํฐ ์ ์ง.
์ํ ๊ด๋ฆฌ ๊ธฐ์ ์ ๋์
- ์ํ๊ฐ ๋ง์ง ์๊ฑฐ๋, ์ ์ ์์ ์ธํฐ๋ ์ ์ด ๋ง์ง ์๋ค๋ฉด ๋งค ์์ ์ ์๋ฒ์ ๋๊ธฐํํ๋๋ผ๋ ์ถฉ๋ถํ์ง๋ง
- ์ฑ์ด ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๊ฐ ์ ์ ๋ง์์ง๊ณ , ์ ์ ์์ ์ธํฐ๋ ์ ์ ์์๋ก ์ ์ฅํ๋ ๋ฐ์ดํฐ๊ฐ ๋ง์์ง๋ ๊ฒฝ์ฐ ์ํ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ ค.
- ํ๋ก ํธ์๋ ๋ฟ๋ง ์๋๋ผ, ๋ฐฑ์๋์์ ๋ฐ์ดํฐ ํต์ ๋ ์ถฉ๋ถํ ๊ณ ๋ คํด์ผ ํ๋ค.
ex) GraphQL
์ํ ๊ด๋ฆฌ ๊ธฐ์ ์ ์ฅ์ :
- ๋์ ํ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐ ์ ๋ฆฌํ๋ค.
- ์ฑ๋ฅ ์ต์ ํ, ๋คํธ์ํฌ ์ต์ ํ ๋ฑ์ ์ ๋ฆฌํ๋ค.
- ๋ฐ์ดํฐ ๊ด๋ฆฌ์ ๊ณ ๋ํ๊ฐ ๊ฐ๋ฅํ๋ค.
ex) localStorage๋ฅผ ํ์ฉํ persist state
์ํ ๊ด๋ฆฌ ๊ธฐ์ ์ ๋จ์
- Boilerplate ๋ฌธ์ .
- ํ์ ํด์ผ ํ ๋ก์ง๊ณผ ๋ ์ด์ด๊ฐ ๋ง์์ง.
- ์๋ชป ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ฑ์ ๋ณต์ก๋ ๋ง์ ๋์ด๊ฑฐ๋, ์ฑ๋ฅ์ ์
ํ.
ex) global state์ ์๋ชป๋ ํ์ฉ์ ์ฑ ์ ์ฒด ๋ฆฌ๋ ๋๋ง ๋ฐ์.
์ํ ๊ด๋ฆฌ ๊ธฐ์ ์ด ํด๊ฒฐํ๋ ๋ฌธ์
๋ฐ์ดํฐ ์บ์ฑ๊ณผ ์ฌํ์ฉ
- SPA์์ ํ์ด์ง ๋ก๋ฉ ์๋ง๋ค ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ฉํ๋ค๋ฉด, ์ฌ์ฉ์ ๊ฒฝํ ์ธก๋ฉด์์ MPA๋ฅผ ํฌ๊ฒ ๋์ด์๊ธฐ ํ๋ค์ด์ง๋ค.
- ์คํ๋ ค ๋คํธ์ํฌ ์์ฒญ ์๊ฐ ๋ง์์ ธ ๋ ๋๋ฆด ์๋ ์๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋ณ๊ฒฝ์ด ์ฆ๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ๋ฉด, ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ๊ณ ์ฌํ์ฉํด์ผ ํ๋ค.
- ๋ณ๊ฒฝ์ด ์ฆ๋ค๋ฉด, ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์์ ์ ํ์
ํด ์ต์ ํํด์ผ ํ๋ค.
ex) ์ผ์ ์๊ฐ๋ง๋ค ์๋ฒ์ ์ ์ฅ, ํ์ดํ 5์ด ํ ์๋ฒ์ ์ ์ฅ. (๋ณ๊ฒฝ์ด ์ฆ๋ค)
Prop Drilling
- ์ปดํฌ๋ํธ๊ฐ ๋ณต์กํด์ง๋ ๊ฒฝ์ฐ, ์์ ๋ถ๋ชจ์ ์์ ์ปดํฌ๋ํธ ๊ฐ์ ๊น์ด๊ฐ ์ปค์ง.
- ์ตํ๋จ์ ์์ ์ปดํฌ๋ํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ธฐ์ํด ์ต์์ ์ปดํฌ๋ํธ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ผ ํ๋ ์ํ์ด ๋ฐ์ํ๋ค. (์ฆ ์ค๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ์ง ์์๋ ์ ๋ฌํ๊ธฐ ์ํด prop๋ฅผ ๋ฐ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธด๋ค.)
- Context API ๋ฑ์ ํ์ฉํด์, ํ์ํ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- Context API๋ฅผ ํ์ฉํ๋ฉด ์ปดํฌ๋ํธ ๊ฐ์ ๊ฒฐํฉ์ฑ์ ๋ฎ์ถ ์ ์๋ค.
Flux Pattern
- 2014๋
Facebook์์ ์ ์ํ ์น ์ ํ๋ฆฌ์ผ์ด์
์ํคํ
์ฒ ํจํด.
- ์ผ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ํ์ฉํ์ฌ ๋ฐ์ดํฐ ์
๋ฐ์ดํธ์ UI๋ฐ์์ ๋จ์ํ ํ์๋ค.
- React์ UI ํจํด์ธ ํฉ์ฑ ์ปดํฌ๋ํธ์ ์ด์ธ๋ฆฌ๋๋ก ์ค๊ณ๋จ
- redux, react-redux ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ํ ํจํด์ด๋ค.
unidirectional data flow
Store(Data Source)๋ฅผ ์
๋ฐ์ดํธํ๋ฉด View๊ฐ ์
๋ฐ์ดํธ ๋๋ ๋ฑ ํ ๋ฐฉํฅ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅด๋ ๊ฐ๋
์ด๋ค.
Flux ํจํด๊ณผ MVC ํจํด ๋น๊ต
MVC
- MVC ํจํด์์๋ View์ ํน์ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ์ฐ์์ ์ธ ์
๋ฐ์ดํธ๊ฐ ์ผ์ด๋ ์ฑ์ด ์ปค์ง์๋ก ์
๋ฐ์ดํธ์ ํ๋ฆ์ ํ์
ํ๊ธฐ ํ๋ค๋ค.
- View์์ ํน์ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ์ฐ์์ ์ธ ์
๋ฐ์ดํธ๊ฐ ์ผ์ด๋๋ค.
- ํน์ ์ ์ ์ ์ธํฐ๋ ์
์ด ์ฌ๋ฌ UI ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ์ ์ํฅ์ ์ค ๋, ์ฑ์ ๋ณต์ก๋์ ์
๋ฐ์ดํธ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ๊ธฐ ํ๋ค๋ค.
MVC ํจํด์ Model์ ๋ฐ์ดํฐ ์์ค๋ก ๋ณผ ์ ์๊ณ , View๋ ์ฌ์ฉ์๊ฐ ๋ณด๋ ํ๋ฉด๋จ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค. ํ๋์ Model์์ ๋ค์์ View๋ค์ด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ํํ์ด๋ค. ์ฌ๊ธฐ์์ MVC ํจํด์ Bidirectional(์๋ฐฉํฅ) ๋ฐ์ดํฐ ํ๋ฆ์ ๊ฐ๋ ๊ตฌ์กฐ๋ฅผ ํ๊ณ ์๋ค. ์ฆ, ์๋ก ์ฐ๊ฒฐ๋ ์ํ์ธ ๊ฒฝ์ฐ View๊ฐ ์
๋ฐ์ดํธ๋๋ฉด Model ๋ํ ์
๋ฐ์ดํธ ๋๋ค.
Flux
- ๋ฐ๋ฉด Flux๋ ํ๋์ Action์ด ํ๋์ Update๋ง์ ๋ง๋ค๋๋ก ํ๋ค.
- ํ๋์ ์ ์ ์ธํฐ๋ ์
๋น ํ๋์ Update๋ง์ ๋ง๋ค๋๋ก ํ๋ค.
- Data์ ์
๋ฐ์ดํธ๊ฐ ํ ๋ฐฉํฅ์ผ๋ก ํ๋ฅด๋ฏ๋ก UI์ ์
๋ฐ์ดํธ๋ฅผ ์์ธกํ๊ธฐ ์ฝ๋ค.
Store(๋ฐ์ดํฐ ์์ค) -> ๋ค์์ View. View๊ฐ ์
๋ฐ์ดํธ ๋์ด๋ Store๋ ์
๋ฐ์ดํธ ๋์ง ์๋ unidirectional data flow
Flux์ ๊ตฌ์กฐ
- Action -> Dispatcher -> Store -> View ์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅธ๋ค.
Action: View๊ฐ Store๋ฅผ ์
๋ฐ์ดํธ์ํค๊ธฐ ์ํด ์์ฑํ ๊ฒ. Reducer์ ๋๊ฒจ์ง ํ, Store๋ฅผ ์
๋ฐ์ดํธํ๋ค.
- Store๋ ๋ฏธ๋ฆฌ Dispatcher์ callback์ ๋ฑ๋กํด, ์์ ์ด ์ฒ๋ฆฌํ Action์ ์ ์ํ๋ค.
callback: ์ด๋ฒคํธ์ ๋ฐ๋ผ ์คํ๋ ํจ์
- Action Creator๋ Action์ ์์ฑํ์ฌ Dispatcher๋ก ๋ณด๋ธ๋ค.
- Dispatcher๋ Action์ Store๋ฅผ ๋๊ธด๋ค.
- Store๋ Action์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๊ณ , ๊ด๋ จ View๋ก ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค.
- View๋ ๊ทธ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ใ
๋ฐ์์ ์๋ก์ด UI๋ฅผ ๋ง๋ ๋ค.
- ์ ์ ์ธํฐ๋ ์
์ด ๋ฐ์ํ๋ฉด View๋ Action์ ๋ฐ์์ํจ๋ค.
React Hooks์ ํตํ ์ํ ๊ด๋ฆฌ
์ํ ๊ด๋ฆฌ์ ์ฌ์ฉ๋๋ Hooks
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด React๊ฐ ์ ๊ณตํ๋ Hook ๋ง์ผ๋ก ์ํ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
- ํจ์ํ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ๋๊ณ , ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ ๋ฐ์ดํฐ์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ํจ์๋ฅผ ๊ณต์ ํ๋ ๋ฐฉ์์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌ.
useState
- ๋จ์ํ ํ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ์ ์ ํฉ
- const [state, seState] = useState(initState | initFn)
- state๊ฐ ๋ฐ๋๋ฉด, state๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋ํ๋ค.
- useEffect์ ํจ๊ป, state์ ๋ฐ์ํ๋ Hook์ ๊ตฌ์ถํ๋ค.
useRef
- ์ํ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด, ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ ์ด์ ์๊ด์์ด, ์ํ๊ฐ ๋ฐ๋์ด๋ ๋ฆฌ๋ ๋๋งํ์ง ์๋ ์ํ๋ฅผ ์ ์ํ ๋ ์ฌ์ฉํ๋ค.
- ์ฆ, ์ํ๊ฐ UI์ ๋ณ๊ฒฝ๊ณผ ๊ด๊ณ์์ ๋ ์ฌ์ฉ.
setTimout์ timerId๋ฅผ ์ ์ฅํ๋ ๊ฒ ๋ฑ์ด ํด๋น
- uncontrolled component์ ์ํ๋ฅผ ์กฐ์ํ๋ ๋ฑ ๋ฆฌ๋ ๋๋ง์ ์ต์ํํ๋ ์ํ ๊ด๋ฆฌ์ ์ฌ์ฉ๋๋ค.
Dynamic Form์ ์์๋ฅผ ๋ค ์ ์๋ค.
useContext
- ์ปดํฌ๋ํธ์ ์ปดํฌ๋ํธ ๊ฐ ์ํ๋ฅผ ๊ณต์ ํ ๋ ์ฌ์ฉ.
- ๋ถ๋ถ์ ์ธ ์ปดํฌ๋ํธ๋ค์ ์ํ ๊ด๋ฆฌ, ์ ์ฒด ์ฑ์ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ชจ๋ ๊ตฌํํ๋ค.
- Context Provider ์์์ ๋ ๋๋ง ๋๋ ์ปดํฌ๋ํธ๋, useContext๋ฅผ ์ด์ฉํด ๊น์ด nested ๋ ์ปดํฌ๋ํธ๋ผ๋ ๋ฐ๋ก context value๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- context vlaue๊ฐ ๋ฐ๋๋ฉด ๋ด๋ถ ์ปดํฌ๋ํธ๋ ๋ชจ๋ ๋ฆฌ๋ ๋๋ง๋๋ค.
useReducer
- useState๋ณด๋ค ๋ณต์กํ ์ํ๋ฅผ ๋ค๋ฃฐ ๋ ์ฌ์ฉํ๋ค.
- ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด flux pattern์ ๊ธฐ๋ฐํ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ๋ค.
- const [state, dispatch] = useReducer(reducer, initState)
- nested state ๋ฑ ๋ณต์กํ ์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ํ๊บผ๋ฒ์ ๊ด๋ฆฌํ๊ฑฐ๋, ์ด๋ค ์ํ์ ์ฌ๋ฌ ๊ฐ์ง ์ฒ๋ฆฌ๋ฅผ ์ ์ฉํ ๋ ์ ์ฉํ๋ค.
- ์ํ ๋ณต์กํ๋ค๋ฉด, useState์ ๊ดํ callback์ ๋ด๋ ค์ฃผ๋ ๊ฒ๋ณด๋ค dispatch๋ฅผ prop์ผ๋ก ๋ด๋ ค ๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
useState๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ
- ์์ ์ปดํฌ๋ํธ์์ state์ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ ์ํ๋ค. ๊ทธ state๋ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๊น์ง prop์ผ๋ก ๋ด๋ ค์ฃผ๋ ํจํด์ด๋ค.
- state๊ฐ ๋ณ๊ฒฝ๋๋ฉด, ์ค๊ฐ์ state๋ฅผ ๋๊ธฐ๊ธฐ๋ง ํ๋ ์ปดํฌ๋ํธ๋ค๋ ๋ชจ๋ ๋ฆฌ๋ ๋๋ง๋๋ค.
- ์ํ์ ์ํ์ ๋ํ ๋ณํ๊ฐ ๋จ์ํ๊ฑฐ๋, ์๋์ ์ผ๋ก ์๊ท๋ชจ ์ฑ์์ ์ฌ์ฉํ๊ธฐ์ ์ ํฉํ๋ค.
TodoApp
function TodoApp() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const [globalId, setGlobalId] = useState(3000);
const toggleTodo = (id) => {
setTodos((todos)=>{
todos.map((todo)=>{
todo.id === id ? {...todo, completed: !todo.completed}
}: todo)
})
}
const deleteTodo = (id) => {
setTodos((todos)=> todos.filter((todo)=> todo.id !== id));
};
const addTodo = (title => {
setTodos((todos)=> [{title, id: globalId + 1}, ... todos]);
setGlobalId((id)=> id+1);
return <TodosPage
state ={{todos, filter}}
toggleTodo={toggleTodo}
addTodo={addTodo}
deleleTodo={deleteTodo}
changeFilter={setFilter}
/>
})
}
TodosPage
function TodosPage({state, addTodo, deleteTodo, toggleTodo, changeFilter}){
const filterTodos = state.todos.filter((todo)=>{
const {filter} = state;
return (
filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'todo' && !todo.completed)
);
})
return (
<div>
<h3>TodosPage</h3>
<TodoForm onSubmit={addTodo}/>
<TodoFilter filter={state.filter}
changeFilter={changeFilter}/>
<ToDoList
todos={filteredTodos}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
/>
</div>
);
}
TodoForm
function TodoForm({onSubmit}) {
const [title, setTitle] = useState('');
return (
<form
onSubmit={(e) =>{
e.preventDefault();
onSubmit(title);
setTitle('');
}}
>
<label htmlFor ='todo-title'>Title</label>
<input id='todo-title' type='text'
name='todo-title' onchange={(e)=>
setTitle(e.target.value)} value={title} />
<button type='submit'>Make</button>
</form>
);
}
todoList
function TodoList({todos, toggleTodo, deleteTodo}) {
return (
<ul>
{todos.map(({title, completed, id}) => (
<li onClick{()=> toggleTodo(id)}>
<h5>{title}</h5>
<div>
{completed ? ์ฒดํฌ์์ด์ฝ: ์ฐ๊ธฐ ์์ด์ฝ}
<button onClick={()=>
deleteTodo(id)}>Delete</button>
</div>
</li>
))}
</ul>
);
}
TodoFilter
function TodoFilter({filter, changeFilter}) {
return (
<div>
<label htmlFor='filter'>Filter</label>
<select
onChange{(e)=> changeFilter(e.target.value)}
id='filter'
name='filter'
>
{filterList.map((filterText)=> (
<option selected={filter === filterText} value={filterText}>
</option>
))};
</select>
</div>
);
}
useContext๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ
- context API๋ฅผ ํ์ฉ. Provider ๋จ์์ ์ํ๋ฅผ ์ ์ํ๊ณ , ์ง์ ์ํ์ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์์ useContext๋ฅผ ์ด์ฉํด ๋ฐ๋ก ์ํ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฉํ๋ ํจํด
- useReducer์ ํจ๊ป, ๋ณต์กํ ์ํ์ ์ํ์ ๋ํ ๋ณ๊ฒฝ ๋ก์ง์
๋ ๊ฐ ์ด์์ ์ปดํฌ๋ํธ์์ ํ์ฉํ๋๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
- state๋ ํ์ํ ๊ณณ์์๋ง ์ฌ์ฉํ๋ฏ๋ก, ๋ถํ์ํ ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๋ฉฐ, Prop Drilling(Plumbing)์ ๋ฐฉ์งํ์ฌ ์ปดํฌ๋ํธ ๊ฐ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ค.
ex) ์ผ์ ์๊ฐ๋ง๋ค ์๋ฒ์ ์ ์ฅ, ํ์ดํ 5์ด ํ ์๋ฒ์ ์ ์ฅ. (๋ณ๊ฒฝ์ด ์ฆ๋ค)
- 2014๋ Facebook์์ ์ ์ํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ํคํ ์ฒ ํจํด.
- ์ผ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ํ์ฉํ์ฌ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ์ UI๋ฐ์์ ๋จ์ํ ํ์๋ค.
- React์ UI ํจํด์ธ ํฉ์ฑ ์ปดํฌ๋ํธ์ ์ด์ธ๋ฆฌ๋๋ก ์ค๊ณ๋จ
- redux, react-redux ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ํ ํจํด์ด๋ค.
unidirectional data flow
Store(Data Source)๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด View๊ฐ ์ ๋ฐ์ดํธ ๋๋ ๋ฑ ํ ๋ฐฉํฅ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅด๋ ๊ฐ๋ ์ด๋ค.
Flux ํจํด๊ณผ MVC ํจํด ๋น๊ต
MVC
- MVC ํจํด์์๋ View์ ํน์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด ์ฐ์์ ์ธ ์ ๋ฐ์ดํธ๊ฐ ์ผ์ด๋ ์ฑ์ด ์ปค์ง์๋ก ์ ๋ฐ์ดํธ์ ํ๋ฆ์ ํ์ ํ๊ธฐ ํ๋ค๋ค.
- View์์ ํน์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด ์ฐ์์ ์ธ ์ ๋ฐ์ดํธ๊ฐ ์ผ์ด๋๋ค.
- ํน์ ์ ์ ์ ์ธํฐ๋ ์ ์ด ์ฌ๋ฌ UI ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ์ ์ํฅ์ ์ค ๋, ์ฑ์ ๋ณต์ก๋์ ์ ๋ฐ์ดํธ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ๊ธฐ ํ๋ค๋ค.
MVC ํจํด์ Model์ ๋ฐ์ดํฐ ์์ค๋ก ๋ณผ ์ ์๊ณ , View๋ ์ฌ์ฉ์๊ฐ ๋ณด๋ ํ๋ฉด๋จ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค. ํ๋์ Model์์ ๋ค์์ View๋ค์ด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ํํ์ด๋ค. ์ฌ๊ธฐ์์ MVC ํจํด์ Bidirectional(์๋ฐฉํฅ) ๋ฐ์ดํฐ ํ๋ฆ์ ๊ฐ๋ ๊ตฌ์กฐ๋ฅผ ํ๊ณ ์๋ค. ์ฆ, ์๋ก ์ฐ๊ฒฐ๋ ์ํ์ธ ๊ฒฝ์ฐ View๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด Model ๋ํ ์ ๋ฐ์ดํธ ๋๋ค.
Flux
- ๋ฐ๋ฉด Flux๋ ํ๋์ Action์ด ํ๋์ Update๋ง์ ๋ง๋ค๋๋ก ํ๋ค.
- ํ๋์ ์ ์ ์ธํฐ๋ ์ ๋น ํ๋์ Update๋ง์ ๋ง๋ค๋๋ก ํ๋ค.
- Data์ ์ ๋ฐ์ดํธ๊ฐ ํ ๋ฐฉํฅ์ผ๋ก ํ๋ฅด๋ฏ๋ก UI์ ์ ๋ฐ์ดํธ๋ฅผ ์์ธกํ๊ธฐ ์ฝ๋ค.
Store(๋ฐ์ดํฐ ์์ค) -> ๋ค์์ View. View๊ฐ ์ ๋ฐ์ดํธ ๋์ด๋ Store๋ ์ ๋ฐ์ดํธ ๋์ง ์๋ unidirectional data flow
Flux์ ๊ตฌ์กฐ
- Action -> Dispatcher -> Store -> View ์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅธ๋ค.
Action: View๊ฐ Store๋ฅผ ์ ๋ฐ์ดํธ์ํค๊ธฐ ์ํด ์์ฑํ ๊ฒ. Reducer์ ๋๊ฒจ์ง ํ, Store๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
- Store๋ ๋ฏธ๋ฆฌ Dispatcher์ callback์ ๋ฑ๋กํด, ์์ ์ด ์ฒ๋ฆฌํ Action์ ์ ์ํ๋ค.
callback: ์ด๋ฒคํธ์ ๋ฐ๋ผ ์คํ๋ ํจ์
- Action Creator๋ Action์ ์์ฑํ์ฌ Dispatcher๋ก ๋ณด๋ธ๋ค.
- Dispatcher๋ Action์ Store๋ฅผ ๋๊ธด๋ค.
- Store๋ Action์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ , ๊ด๋ จ View๋ก ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค.
- View๋ ๊ทธ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ใ ๋ฐ์์ ์๋ก์ด UI๋ฅผ ๋ง๋ ๋ค.
- ์ ์ ์ธํฐ๋ ์ ์ด ๋ฐ์ํ๋ฉด View๋ Action์ ๋ฐ์์ํจ๋ค.
React Hooks์ ํตํ ์ํ ๊ด๋ฆฌ
์ํ ๊ด๋ฆฌ์ ์ฌ์ฉ๋๋ Hooks
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด React๊ฐ ์ ๊ณตํ๋ Hook ๋ง์ผ๋ก ์ํ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
- ํจ์ํ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ๋๊ณ , ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ ๋ฐ์ดํฐ์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ํจ์๋ฅผ ๊ณต์ ํ๋ ๋ฐฉ์์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌ.
useState
- ๋จ์ํ ํ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ์ ์ ํฉ
- const [state, seState] = useState(initState | initFn)
- state๊ฐ ๋ฐ๋๋ฉด, state๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋ํ๋ค.
- useEffect์ ํจ๊ป, state์ ๋ฐ์ํ๋ Hook์ ๊ตฌ์ถํ๋ค.
useRef
- ์ํ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด, ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ ์ด์ ์๊ด์์ด, ์ํ๊ฐ ๋ฐ๋์ด๋ ๋ฆฌ๋ ๋๋งํ์ง ์๋ ์ํ๋ฅผ ์ ์ํ ๋ ์ฌ์ฉํ๋ค.
- ์ฆ, ์ํ๊ฐ UI์ ๋ณ๊ฒฝ๊ณผ ๊ด๊ณ์์ ๋ ์ฌ์ฉ.
setTimout์ timerId๋ฅผ ์ ์ฅํ๋ ๊ฒ ๋ฑ์ด ํด๋น
- uncontrolled component์ ์ํ๋ฅผ ์กฐ์ํ๋ ๋ฑ ๋ฆฌ๋ ๋๋ง์ ์ต์ํํ๋ ์ํ ๊ด๋ฆฌ์ ์ฌ์ฉ๋๋ค.
Dynamic Form์ ์์๋ฅผ ๋ค ์ ์๋ค.
useContext
- ์ปดํฌ๋ํธ์ ์ปดํฌ๋ํธ ๊ฐ ์ํ๋ฅผ ๊ณต์ ํ ๋ ์ฌ์ฉ.
- ๋ถ๋ถ์ ์ธ ์ปดํฌ๋ํธ๋ค์ ์ํ ๊ด๋ฆฌ, ์ ์ฒด ์ฑ์ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ชจ๋ ๊ตฌํํ๋ค.
- Context Provider ์์์ ๋ ๋๋ง ๋๋ ์ปดํฌ๋ํธ๋, useContext๋ฅผ ์ด์ฉํด ๊น์ด nested ๋ ์ปดํฌ๋ํธ๋ผ๋ ๋ฐ๋ก context value๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- context vlaue๊ฐ ๋ฐ๋๋ฉด ๋ด๋ถ ์ปดํฌ๋ํธ๋ ๋ชจ๋ ๋ฆฌ๋ ๋๋ง๋๋ค.
useReducer
- useState๋ณด๋ค ๋ณต์กํ ์ํ๋ฅผ ๋ค๋ฃฐ ๋ ์ฌ์ฉํ๋ค.
- ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด flux pattern์ ๊ธฐ๋ฐํ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ๋ค.
- const [state, dispatch] = useReducer(reducer, initState)
- nested state ๋ฑ ๋ณต์กํ ์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ํ๊บผ๋ฒ์ ๊ด๋ฆฌํ๊ฑฐ๋, ์ด๋ค ์ํ์ ์ฌ๋ฌ ๊ฐ์ง ์ฒ๋ฆฌ๋ฅผ ์ ์ฉํ ๋ ์ ์ฉํ๋ค.
- ์ํ ๋ณต์กํ๋ค๋ฉด, useState์ ๊ดํ callback์ ๋ด๋ ค์ฃผ๋ ๊ฒ๋ณด๋ค dispatch๋ฅผ prop์ผ๋ก ๋ด๋ ค ๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
useState๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ
- ์์ ์ปดํฌ๋ํธ์์ state์ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ ์ํ๋ค. ๊ทธ state๋ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๊น์ง prop์ผ๋ก ๋ด๋ ค์ฃผ๋ ํจํด์ด๋ค.
- state๊ฐ ๋ณ๊ฒฝ๋๋ฉด, ์ค๊ฐ์ state๋ฅผ ๋๊ธฐ๊ธฐ๋ง ํ๋ ์ปดํฌ๋ํธ๋ค๋ ๋ชจ๋ ๋ฆฌ๋ ๋๋ง๋๋ค.
- ์ํ์ ์ํ์ ๋ํ ๋ณํ๊ฐ ๋จ์ํ๊ฑฐ๋, ์๋์ ์ผ๋ก ์๊ท๋ชจ ์ฑ์์ ์ฌ์ฉํ๊ธฐ์ ์ ํฉํ๋ค.
TodoApp
function TodoApp() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const [globalId, setGlobalId] = useState(3000);
const toggleTodo = (id) => {
setTodos((todos)=>{
todos.map((todo)=>{
todo.id === id ? {...todo, completed: !todo.completed}
}: todo)
})
}
const deleteTodo = (id) => {
setTodos((todos)=> todos.filter((todo)=> todo.id !== id));
};
const addTodo = (title => {
setTodos((todos)=> [{title, id: globalId + 1}, ... todos]);
setGlobalId((id)=> id+1);
return <TodosPage
state ={{todos, filter}}
toggleTodo={toggleTodo}
addTodo={addTodo}
deleleTodo={deleteTodo}
changeFilter={setFilter}
/>
})
}
TodosPage
function TodosPage({state, addTodo, deleteTodo, toggleTodo, changeFilter}){
const filterTodos = state.todos.filter((todo)=>{
const {filter} = state;
return (
filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'todo' && !todo.completed)
);
})
return (
<div>
<h3>TodosPage</h3>
<TodoForm onSubmit={addTodo}/>
<TodoFilter filter={state.filter}
changeFilter={changeFilter}/>
<ToDoList
todos={filteredTodos}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
/>
</div>
);
}
TodoForm
function TodoForm({onSubmit}) {
const [title, setTitle] = useState('');
return (
<form
onSubmit={(e) =>{
e.preventDefault();
onSubmit(title);
setTitle('');
}}
>
<label htmlFor ='todo-title'>Title</label>
<input id='todo-title' type='text'
name='todo-title' onchange={(e)=>
setTitle(e.target.value)} value={title} />
<button type='submit'>Make</button>
</form>
);
}
todoList
function TodoList({todos, toggleTodo, deleteTodo}) {
return (
<ul>
{todos.map(({title, completed, id}) => (
<li onClick{()=> toggleTodo(id)}>
<h5>{title}</h5>
<div>
{completed ? ์ฒดํฌ์์ด์ฝ: ์ฐ๊ธฐ ์์ด์ฝ}
<button onClick={()=>
deleteTodo(id)}>Delete</button>
</div>
</li>
))}
</ul>
);
}
TodoFilter
function TodoFilter({filter, changeFilter}) {
return (
<div>
<label htmlFor='filter'>Filter</label>
<select
onChange{(e)=> changeFilter(e.target.value)}
id='filter'
name='filter'
>
{filterList.map((filterText)=> (
<option selected={filter === filterText} value={filterText}>
</option>
))};
</select>
</div>
);
}
useContext๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ
- context API๋ฅผ ํ์ฉ. Provider ๋จ์์ ์ํ๋ฅผ ์ ์ํ๊ณ , ์ง์ ์ํ์ ๋ณ๊ฒฝ ํจ์๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์์ useContext๋ฅผ ์ด์ฉํด ๋ฐ๋ก ์ํ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฉํ๋ ํจํด
- useReducer์ ํจ๊ป, ๋ณต์กํ ์ํ์ ์ํ์ ๋ํ ๋ณ๊ฒฝ ๋ก์ง์
๋ ๊ฐ ์ด์์ ์ปดํฌ๋ํธ์์ ํ์ฉํ๋๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
- state๋ ํ์ํ ๊ณณ์์๋ง ์ฌ์ฉํ๋ฏ๋ก, ๋ถํ์ํ ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๋ฉฐ, Prop Drilling(Plumbing)์ ๋ฐฉ์งํ์ฌ ์ปดํฌ๋ํธ ๊ฐ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ค.
setTimout์ timerId๋ฅผ ์ ์ฅํ๋ ๊ฒ ๋ฑ์ด ํด๋น
Dynamic Form์ ์์๋ฅผ ๋ค ์ ์๋ค.
function TodoApp() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const [globalId, setGlobalId] = useState(3000);
const toggleTodo = (id) => {
setTodos((todos)=>{
todos.map((todo)=>{
todo.id === id ? {...todo, completed: !todo.completed}
}: todo)
})
}
const deleteTodo = (id) => {
setTodos((todos)=> todos.filter((todo)=> todo.id !== id));
};
const addTodo = (title => {
setTodos((todos)=> [{title, id: globalId + 1}, ... todos]);
setGlobalId((id)=> id+1);
return <TodosPage
state ={{todos, filter}}
toggleTodo={toggleTodo}
addTodo={addTodo}
deleleTodo={deleteTodo}
changeFilter={setFilter}
/>
})
}
function TodosPage({state, addTodo, deleteTodo, toggleTodo, changeFilter}){
const filterTodos = state.todos.filter((todo)=>{
const {filter} = state;
return (
filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'todo' && !todo.completed)
);
})
return (
<div>
<h3>TodosPage</h3>
<TodoForm onSubmit={addTodo}/>
<TodoFilter filter={state.filter}
changeFilter={changeFilter}/>
<ToDoList
todos={filteredTodos}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
/>
</div>
);
}
function TodoForm({onSubmit}) {
const [title, setTitle] = useState('');
return (
<form
onSubmit={(e) =>{
e.preventDefault();
onSubmit(title);
setTitle('');
}}
>
<label htmlFor ='todo-title'>Title</label>
<input id='todo-title' type='text'
name='todo-title' onchange={(e)=>
setTitle(e.target.value)} value={title} />
<button type='submit'>Make</button>
</form>
);
}
function TodoList({todos, toggleTodo, deleteTodo}) {
return (
<ul>
{todos.map(({title, completed, id}) => (
<li onClick{()=> toggleTodo(id)}>
<h5>{title}</h5>
<div>
{completed ? ์ฒดํฌ์์ด์ฝ: ์ฐ๊ธฐ ์์ด์ฝ}
<button onClick={()=>
deleteTodo(id)}>Delete</button>
</div>
</li>
))}
</ul>
);
}
function TodoFilter({filter, changeFilter}) {
return (
<div>
<label htmlFor='filter'>Filter</label>
<select
onChange{(e)=> changeFilter(e.target.value)}
id='filter'
name='filter'
>
{filterList.map((filterText)=> (
<option selected={filter === filterText} value={filterText}>
</option>
))};
</select>
</div>
);
}
๋ ๊ฐ ์ด์์ ์ปดํฌ๋ํธ์์ ํ์ฉํ๋๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
TodoContext
const TodoContext = createContext(null);
const initialState ={
todos: [],
filter: 'all',
globalId: 3000,
};
function useTodoContext(){
const context = useContext(TodoContext);
if (!context){
throw new Error('Use TodoContext inside Provider.');
}
return context;
}
function TodoContextProvider({children}){
const values = useTodoState();
return <TodoContext.Provider
value={values}>{children}</TodoContext.Provider>;
}
function reducer(state, action) {
switch(action.type) {
case 'change.filter':
return {...state, filter:
action.payload.filter};
case 'init.todos':
return {...state, todos:
action.payload.todos};
case 'add.todo': {
return {...state, todos:
[{title: action.payload.title,
id: state.globalId + 1}],
globalId: state.globalId + 1};
}
case 'delete.todo': {
return {...state, todo:
state.todos.filter((todo)=> todo.id !==
action.payload.id)};
}
case 'toggle.todo': {
return {...state, todos: state.todos.map((t)
=> t.id === action.payload.id ?
{...t, completed: !t.compeleted}: t)};
}
deafault: return state;
}
}
function useTodoState() {
const [state, dispatch] = useReducer(reducer, initialState);
const toggleTodo = useCallback((id)=> dispatch({type: 'toggle.todo', payload: {id} }, []);
const deleteTodo = useCallback((id)=> dispatch({type: 'delete.todo', payload: {id} }, []);
const addTodo = useCallback((title)=> dispatch({type: 'add.todo', payload: {title} }, []);
const changeFilter = useCallback((filter)=> dispatch({type: 'change.filter', payload: {title} }, []);
const initializeTodos = useCallback((todos)=> dispatch({type: 'init.todos', payload: {todos} }, []);
return {state, toggleTodo, deleteTodo, addTodo, changeFilter, initializeTodos};
}
todoApp
function TodoApp() {
return (
<TodoContextProvider>
<TodosPage/>
</TodoContextProvider>
);
}
TodosPage
function TodosPage() {
const {initializeTodos} = useTodoContext();
// ์ฒ์ ๋ง์ดํธ ๋์์ ๋, todo๋ฅผ ๋ฐ์์ ์
๋ฐ์ดํธํ๋ค.
useEffect(()=>{
console.log('useEffect');
fetchTodos().then(initializeTodos);
}, [initializeTodos]);
return (
<div>
<TodoForm/>
<TodoFilter/>
<TodoList/>
</div>
);
}
TodoForm
function TodoForm(){
const {addTodo} = useContext();
const [title, setTitle] = useState('');
return (
<form
onSubmit={(e)=>{
e.preventDefault();
addTodo(title);
setTitle('');
}}
>
<label htmlFor='todo-title>Title</label>
<input
id='todo-title'
type='text'
name='todo-title'
onChange={(e)=> setTitle(e.target.value)}
value={title}/>
<button type='submit'>Make</button>
</form>
);
}
TodoList
function TodoList() {
const {state, toggleTodo, deleleTodo} = useTodoContext();
const { todos, filter } = state;
const filteredTodos = todos.filter((todo)=>{
return (
filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'todo' && !todo.completed)
);
})
return (
<ul>
{filteredTodos.map(({title, completed, id}) => (
<li key={id} onClick={()=> toggleTodo(id)}>
<h5>{title}</h5>
<div>
{completed ? ์ฒดํฌ๋ฐ์ค์์ด์ฝ :ํ ์์ด์ฝ}
<button onClick={()=> deleteTodo(id)}>Delete</button>
</div>
))}
</ul>
)
}
Author And Source
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(20_Oct_2021 ๐ฐ ์๋ฆฌ์ค AI ํธ๋ TIL: ์ํ๊ด๋ฆฌ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://velog.io/@juanito_y247/20Oct2021-์๋ฆฌ์ค-AI-ํธ๋-TIL-์ํ๊ด๋ฆฌ์ ์ ๊ท์: ์์์ ์ ๋ณด๊ฐ ์์์ URL์ ํฌํจ๋์ด ์์ผ๋ฉฐ ์ ์๊ถ์ ์์์ ์์ ์ ๋๋ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค