ํจํด ๋งค์นญ ๋ฐ ๋์์ ๋ฐ์ดํฐ ํ์ ์ ์ฌ์ฉํ์ฌ JavaScript์์ ๊ฐ๋ ฅํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ตฌํํ์ธ์. Option, Result, RemoteData ํจํด์ ๋ง์คํฐํ์ฌ ๊ฒฌ๊ณ ํ๊ณ ๊ฐ๋ ์ฑ์ด ๋์ผ๋ฉฐ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํ ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ์ธ์.
JavaScript ํจํด ๋งค์นญ ๋ฐ ๋์์ ๋ฐ์ดํฐ ํ์ : ์ ์ธ๊ณ ๊ฐ๋ฐ์๋ฅผ ์ํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ํจํด ํฅ์
์ํํธ์จ์ด ๊ฐ๋ฐ์ ์ญ๋์ ์ธ ์ธ๊ณ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ธ๊ณ ์ฒญ์ค์๊ฒ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ฉฐ ๋นํ ๋ฐ ์๋ ๊ฒฌ๊ณ ์ฑ, ๊ฐ๋ ์ฑ ๋ฐ ์ ์ง๋ณด์์ฑ์ ์๊ตฌํฉ๋๋ค. JavaScript๋ ๊ณ์ ์งํํ๊ณ ์์ต๋๋ค. ์ ์ธ๊ณ ๊ฐ๋ฐ์๋ค์ด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ(FP)๊ณผ ๊ฐ์ ํจ๋ฌ๋ค์์ ์ฑํํจ์ ๋ฐ๋ผ, ๋์ฑ ํํ๋ ฅ์ด ํ๋ถํ๊ณ ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋ฎ์ ์ฝ๋๋ฅผ ์์ฑํ๋ ค๋ ๋ ธ๋ ฅ์ ๋งค์ฐ ์ค์ํด์ก์ต๋๋ค. JavaScript๋ ์ค๋ซ๋์ ํต์ฌ FP ๊ฐ๋ ์ ์ง์ํด ์์ง๋ง, Haskell, Scala ๋๋ Rust์ ๊ฐ์ ์ธ์ด์ ํจํด ๋งค์นญ ๋ฐ ๋์์ ๋ฐ์ดํฐ ํ์ (ADT)๊ณผ ๊ฐ์ ์ผ๋ถ ๊ณ ๊ธ ํจํด์ ์ญ์ฌ์ ์ผ๋ก ์ฐ์ํ๊ฒ ๊ตฌํํ๊ธฐ ์ด๋ ค์ ์ต๋๋ค.
์ด ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ ์ด๋ฌํ ๊ฐ๋ ฅํ ๊ฐ๋ ์ JavaScript์ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ณ , ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๋๊ตฌํท์ ํฌ๊ฒ ํฅ์์ํค๋ฉฐ, ์์ธก ๊ฐ๋ฅํ๊ณ ๋ณต์๋ ฅ์ด ๋ฐ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์ด์ด์ง๋๋ค. ์ ํต์ ์ธ ์กฐ๊ฑด๋ถ ๋ก์ง์ ๊ณ ์ ํ ๊ณผ์ , ํจํด ๋งค์นญ ๋ฐ ADT์ ๋ฉ์ปค๋์ฆ์ ๋ถ์ํ๊ณ , ์ด ๋์ ์๋์ง๊ฐ ๋ค์ํ ๋ฐฐ๊ฒฝ๊ณผ ๊ธฐ์ ํ๊ฒฝ์ ๊ฐ๋ฐ์๋ค์๊ฒ ๊ณต๊ฐํ ์ ์๋ ๋ฐฉ์์ผ๋ก ์ํ ๊ด๋ฆฌ, ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ง์ ๋ํ ์ ๊ทผ ๋ฐฉ์์ ํ์ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค ๊ฒ์ ๋๋ค.
JavaScript์์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ ์
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ณ์ฐ์ ์ํ์ ํจ์์ ํ๊ฐ๋ก ์ทจ๊ธํ๋ฉฐ, ๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ํ์ ๋ถ์์ฉ์ ์ ์คํ๊ฒ ํผํ๋ ํจ๋ฌ๋ค์์ ๋๋ค. JavaScript ๊ฐ๋ฐ์์๊ฒ FP ์์น์ ์ฑํํ๋ ๊ฒ์ ์ข ์ข ๋ค์์ ์๋ฏธํฉ๋๋ค:
- ์์ ํจ์: ๋์ผํ ์ ๋ ฅ์ด ์ฃผ์ด์ก์ ๋ ํญ์ ๋์ผํ ์ถ๋ ฅ์ ๋ฐํํ๊ณ ๊ด์ฐฐ ๊ฐ๋ฅํ ๋ถ์์ฉ์ ์์ฑํ์ง ์๋ ํจ์. ์ด๋ฌํ ์์ธก ๊ฐ๋ฅ์ฑ์ ์์ ์ ์ธ ์ํํธ์จ์ด์ ์ด์์ ๋๋ค.
- ๋ถ๋ณ์ฑ: ํ ๋ฒ ์์ฑ๋ ๋ฐ์ดํฐ๋ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ๋์ , ๋ชจ๋ "์์ "์ ์๋ก์ด ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์์ฑํ์ฌ ์๋ณธ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์กดํฉ๋๋ค.
- ์ผ๊ธ ํจ์: ํจ์๋ ๋ค๋ฅธ ๋ณ์์ ๋์ผํ๊ฒ ์ทจ๊ธ๋ฉ๋๋ค. ๋ณ์์ ํ ๋นํ๊ฑฐ๋, ๋ค๋ฅธ ํจ์์ ์ธ์๋ก ์ ๋ฌํ๊ฑฐ๋, ํจ์์์ ๊ฒฐ๊ณผ๋ก ๋ฐํํ ์ ์์ต๋๋ค.
- ๊ณ ์ฐจ ํจ์: ํ๋ ์ด์์ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๊ฒฐ๊ณผ๋ก ๋ฐํํ๋ ํจ์๋ก, ๊ฐ๋ ฅํ ์ถ์ํ ๋ฐ ๊ตฌ์ฑ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
์ด๋ฌํ ์์น์ ํ์ฅ ๊ฐ๋ฅํ๊ณ ํ ์คํธ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฐ์ ์ ๊ณตํ์ง๋ง, ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ค์ํ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ ์ข ์ข ์ ํต์ ์ธ JavaScript์์ ๋ณต์กํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ด๋ ค์ด ์กฐ๊ฑด๋ถ ๋ก์ง์ผ๋ก ์ด์ด์ง๋๋ค.
์ ํต์ ์ธ ์กฐ๊ฑด๋ถ ๋ก์ง์ ๋ฌธ์ ์
JavaScript ๊ฐ๋ฐ์๋ ๋ฐ์ดํฐ ๊ฐ์ด๋ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์๋๋ฆฌ์ค๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด if/else if/else ๋ฌธ ๋๋ switch ์ผ์ด์ค์ ์์ฃผ ์์กดํฉ๋๋ค. ์ด๋ฌํ ๊ตฌ์ฑ ์์๋ ๊ธฐ๋ณธ์ ์ด๊ณ ์ด๋์๋ ์กด์ฌํ์ง๋ง, ํนํ ํฌ๊ณ ์ ์ธ๊ณ์ ์ผ๋ก ๋ถ์ฐ๋ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํฉ๋๋ค:
- ์ฅํฉํจ ๋ฐ ๊ฐ๋
์ฑ ๋ฌธ์ : ๊ธด
if/else์ฒด์ธ ๋๋ ๊น์ด ์ค์ฒฉ๋switch๋ฌธ์ ์ฝ๊ณ , ์ดํดํ๊ณ , ์ ์ง๋ณด์ํ๊ธฐ๊ฐ ์ด๋ ค์์ ธ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๋ชจํธํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. - ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ์ฑ: ํน์ ์ผ์ด์ค๋ฅผ ๋์น๊ฑฐ๋ ์ฒ๋ฆฌํ์ง ์๋ ๊ฒ์ ๋๋ผ์ธ ์ ๋๋ก ์ฝ์ต๋๋ค. ์ด๋ ํ๋ก๋์ ํ๊ฒฝ์์ ๋ํ๋ ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ์ํฅ์ ๋ฏธ์น๋ ์์์น ๋ชปํ ๋ฐํ์ ์ค๋ฅ๋ก ์ด์ด์ง๋๋ค.
- ์ด์ฒด์ฑ ๊ฒ์ฌ์ ๋ถ์กฑ: ํ์ค JavaScript์๋ ์ฃผ์ด์ง ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ํ ๋ชจ๋ ๊ฐ๋ฅํ ์ผ์ด์ค๊ฐ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์๋์ง ํ์ธํ ์ ์๋ ๋ด์ฅ ๋ฉ์ปค๋์ฆ์ด ์์ต๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์๊ตฌ ์ฌํญ์ด ๋ฐ์ ํจ์ ๋ฐ๋ผ ์ด๋ ์ผ๋ฐ์ ์ธ ๋ฒ๊ทธ์ ์์ธ์ด ๋ฉ๋๋ค.
- ๋ณ๊ฒฝ์ ๋ํ ์ทจ์ฝ์ฑ: ๋ฐ์ดํฐ ์ ํ์ ์๋ก์ด ์ํ ๋๋ ์๋ก์ด ๋ณํ์ ๋์ ํ๋ ๊ฒ์ ์ข ์ข ์ฝ๋๋ฒ ์ด์ค ์ ์ฒด์์ ์ฌ๋ฌ `if/else` ๋๋ `switch` ๋ธ๋ก์ ์์ ํด์ผ ํฉ๋๋ค. ์ด๋ ํ๊ท๋ฅผ ๋์ ํ ์ํ์ ์ฆ๊ฐ์ํค๊ณ ๋ฆฌํฉํ ๋ง์ ์ด๋ ต๊ฒ ๋ง๋ญ๋๋ค.
์๋ฅผ ๋ค์ด, ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ค์ํ ์ง์ญ์์ ์ค๋ ๋ค์ํ ์ ํ์ ์ฌ์ฉ์ ์์ ์ ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด ๋ณผ ์ ์์ต๋๋ค. ๊ฐ ์์ ์๋ ๊ณ ์ ํ ์ฒ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค:
function handleUserAction(action) {
if (action.type === 'LOGIN') {
// ๋ก๊ทธ์ธ ๋ก์ง ์ฒ๋ฆฌ, ์: ์ฌ์ฉ์ ์ธ์ฆ, IP ๊ธฐ๋ก ๋ฑ
console.log(`์ฌ์ฉ์ ๋ก๊ทธ์ธ: ${action.payload.username} (IP: ${action.payload.ipAddress})`);
} else if (action.type === 'LOGOUT') {
// ๋ก๊ทธ์์ ๋ก์ง ์ฒ๋ฆฌ, ์: ์ธ์
๋ฌดํจํ, ํ ํฐ ์ง์ฐ๊ธฐ
console.log('์ฌ์ฉ์ ๋ก๊ทธ์์.');
} else if (action.type === 'UPDATE_PROFILE') {
// ํ๋กํ ์
๋ฐ์ดํธ ์ฒ๋ฆฌ, ์: ์ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ
console.log(`์ฌ์ฉ์ ${action.payload.userId} ํ๋กํ ์
๋ฐ์ดํธ๋จ`);
} else {
// ์ด 'else' ์ ์ ์ ์ ์๊ฑฐ๋ ์ฒ๋ฆฌ๋์ง ์์ ๋ชจ๋ ์์
์ ํ์ ์ก์ต๋๋ค.
console.warn(`์ฒ๋ฆฌ๋์ง ์์ ์์
์ ํ ๊ฐ์ง๋จ: ${action.type}. ์์
์ธ๋ถ ์ ๋ณด: ${JSON.stringify(action)}`);
}
}
handleUserAction({ type: 'LOGIN', payload: { username: 'alice', ipAddress: '192.168.1.100' } });
handleUserAction({ type: 'LOGOUT' });
handleUserAction({ type: 'VIEW_DASHBOARD', payload: { userId: 'alice123' } }); // ์ด ์ผ์ด์ค๋ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌ๋์ง ์์ else๋ก ๋์ด๊ฐ๋๋ค.
์ด ์ ๊ทผ ๋ฐฉ์์ ๊ธฐ๋ฅ์ ์ด์ง๋ง, ์์ญ ๊ฐ์ ์์ ์ ํ๊ณผ ์ ์ฌํ ๋ก์ง์ ์ ์ฉํด์ผ ํ๋ ์๋ง์ ์์น๋ก ์ธํด ๋น ๋ฅด๊ฒ ๋ค๋ฃจ๊ธฐ ํ๋ค์ด์ง๋๋ค. 'else' ์ ์ ์ ํจํ์ง๋ง ์ฒ๋ฆฌ๋์ง ์์ ๋น์ฆ๋์ค ๋ก์ง ์ผ์ด์ค๋ฅผ ์จ๊ธธ ์ ์๋ ํฌ๊ด์ ์ธ ์ ์ด ๋ฉ๋๋ค.
ํจํด ๋งค์นญ ์๊ฐ
๊ธฐ๋ณธ์ ์ผ๋ก ํจํด ๋งค์นญ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ถํดํ๊ณ ๋ฐ์ดํฐ์ ๋ชจ์ ๋๋ ๊ฐ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ฝ๋ ๊ฒฝ๋ก๋ฅผ ์คํํ ์ ์๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ๋๋ค. ์ด๋ ์ ํต์ ์ธ ์กฐ๊ฑด๋ฌธ๋ณด๋ค ๋ ์ ์ธ์ ์ด๊ณ ์ง๊ด์ ์ด๋ฉฐ ํํ๋ ฅ์ด ํ๋ถํ ๋์์ผ๋ก, ๋ ๋์ ์์ค์ ์ถ์ํ์ ์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
ํจํด ๋งค์นญ์ ์ฅ์
- ํฅ์๋ ๊ฐ๋ ์ฑ ๋ฐ ํํ๋ ฅ: ๋ค๋ฅธ ๋ฐ์ดํฐ ํจํด๊ณผ ๊ด๋ จ ๋ก์ง์ ๋ช ์์ ์ผ๋ก ์ค๋ช ํ์ฌ ์ฝ๋๊ฐ ํจ์ฌ ๋ ๊น๋ํ๊ณ ์ดํดํ๊ธฐ ์ฌ์์ ธ ์ธ์ง ๋ถํ๊ฐ ์ค์ด๋ญ๋๋ค.
- ํฅ์๋ ์์ ์ฑ ๋ฐ ๊ฒฌ๊ณ ์ฑ: ํจํด ๋งค์นญ์ ๋ณธ์ง์ ์ผ๋ก ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ํ์ฑํํ์ฌ ๋ชจ๋ ๊ฐ๋ฅํ ์ผ์ด์ค๊ฐ ์ฒ๋ฆฌ๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ ๋ฐํ์ ์ค๋ฅ ๋ฐ ์ฒ๋ฆฌ๋์ง ์์ ์๋๋ฆฌ์ค์ ๊ฐ๋ฅ์ฑ์ ํฌ๊ฒ ์ค์ ๋๋ค.
- ๊ฐ๊ฒฐ์ฑ ๋ฐ ์ฐ์ํจ: ๊น์ด ์ค์ฒฉ๋
if/else๋๋ ๋ณต์กํswitch๋ฌธ์ ๋นํด ๋ ๊ฐ๊ฒฐํ๊ณ ์ฐ์ํ ์ฝ๋๊ฐ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ๊ฐ๋ฐ์ ์์ฐ์ฑ์ด ํฅ์๋ฉ๋๋ค. - ๊ฐ๋ ฅํ ๋ถํด: JavaScript์ ๊ธฐ์กด ๋ถํด ํ ๋น ๊ฐ๋ ์ ์์ ํ ์กฐ๊ฑด๋ถ ์ ์ด ํ๋ฆ ๋ฉ์ปค๋์ฆ์ผ๋ก ํ์ฅํฉ๋๋ค.
ํ์ฌ JavaScript์์์ ํจํด ๋งค์นญ
ํฌ๊ด์ ์ด๊ณ ๋ค์ดํฐ๋ธํ ํจํด ๋งค์นญ ๊ตฌ๋ฌธ์ด ํ๋ฐํ ๋ ผ์ ๋ฐ ๊ฐ๋ฐ ์ค์ด์ง๋ง(TC39 ํจํด ๋งค์นญ ์ ์์ ํตํด), JavaScript๋ ์ด๋ฏธ ๊ธฐ์ด์ ์ธ ์กฐ๊ฐ์ธ ๋ถํด ํ ๋น์ ์ ๊ณตํฉ๋๋ค.
const userProfile = { id: 101, name: 'Lena Petrova', email: 'lena.p@example.com', country: 'Ukraine' };
// ๊ฐ์ฒด ๋ถํด๋ฅผ ์ฌ์ฉํ ๊ธฐ๋ณธ ํจํด ๋งค์นญ
const { name, email, country } = userProfile;
console.log(`์ฌ์ฉ์ ${name} (๊ตญ๊ฐ: ${country})์ ์ด๋ฉ์ผ์ ${email}์
๋๋ค.`); // Lena Petrova from Ukraine has email lena.p@example.com.
// ๋ฐฐ์ด ๋ถํด ๋ํ ๊ธฐ๋ณธ ํจํด ๋งค์นญ์ ํ ํํ์
๋๋ค.
const topCities = ['Tokyo', 'Delhi', 'Shanghai', 'Sao Paulo'];
const [firstCity, secondCity] = topCities;
console.log(`๊ฐ์ฅ ํฐ ๋ ๋์๋ ${firstCity}์ ${secondCity}์
๋๋ค.`); // The two largest cities are Tokyo and Delhi.
์ด๋ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ฐ ๋งค์ฐ ์ ์ฉํ์ง๋ง, ๋จ์ํ ์ถ์ถ๋ ๋ณ์์ ๋ํ if ๊ฒ์ฌ๋ฅผ ๋์ด ์ ์ธ์ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ฐ๋ผ ์คํ์ ๋ถ๊ธฐํ๋ ๋ฉ์ปค๋์ฆ์ ์ง์ ์ ์ผ๋ก ์ ๊ณตํ์ง๋ ์์ต๋๋ค.
JavaScript์์ ํจํด ๋งค์นญ ์๋ฎฌ๋ ์ด์
๋ค์ดํฐ๋ธ ํจํด ๋งค์นญ์ด JavaScript์ ํฌํจ๋๊ธฐ ์ ๊น์ง ๊ฐ๋ฐ์๋ค์ ์ข ์ข ๊ธฐ์กด ์ธ์ด ๊ธฐ๋ฅ์ด๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ์ด ๊ธฐ๋ฅ์ ์๋ฎฌ๋ ์ด์ ํ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฐฝ์์ ์ผ๋ก ๊ณ ์ํ์ต๋๋ค:
1. switch (true) ํดํน (์ ํ๋ ๋ฒ์)
์ด ํจํด์ switch ๋ฌธ๊ณผ true๋ฅผ ํํ์์ผ๋ก ์ฌ์ฉํ์ฌ case ์ ์ ์์์ ๋ถ์ธ ํํ์์ ํฌํจํ ์ ์๋๋ก ํฉ๋๋ค. ๋ก์ง์ ํตํฉํ์ง๋ง, ์ฃผ๋ก ํ์ฅ๋ if/else if ์ฒด์ธ์ผ๋ก ์๋ํ๋ฉฐ ์ง์ ํ ๊ตฌ์กฐ์ ํจํด ๋งค์นญ์ด๋ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ ๊ณตํ์ง๋ ์์ต๋๋ค.
function getGeometricShapeArea(shape) {
switch (true) {
case shape.type === 'circle' && typeof shape.radius === 'number' && shape.radius > 0:
return Math.PI * shape.radius * shape.radius;
case shape.type === 'rectangle' && typeof shape.width === 'number' && typeof shape.height === 'number' && shape.width > 0 && shape.height > 0:
return shape.width * shape.height;
case shape.type === 'triangle' && typeof shape.base === 'number' && typeof shape.height === 'number' && shape.base > 0 && shape.height > 0:
return 0.5 * shape.base * shape.height;
default:
throw new Error(`์๋ชป๋ ๋ชจ์ ๋๋ ์น์๊ฐ ์ ๊ณต๋์์ต๋๋ค: ${JSON.stringify(shape)}`);
}
}
console.log(getGeometricShapeArea({ type: 'circle', radius: 7 })); // ์ฝ 153.93
console.log(getGeometricShapeArea({ type: 'rectangle', width: 6, height: 8 })); // 48
console.log(getGeometricShapeArea({ type: 'square', side: 5 })); // ์ค๋ฅ ๋ฐ์: ์๋ชป๋ ๋ชจ์ ๋๋ ์น์๊ฐ ์ ๊ณต๋์์ต๋๋ค.
2. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธฐ๋ฐ ์ ๊ทผ ๋ฐฉ์
์ฌ๋ฌ ๊ฐ๋ ฅํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ JavaScript์ ๋ ์ ๊ตํ ํจํด ๋งค์นญ์ ์ ๊ณตํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ฉฐ, ์ข
์ข
TypeScript๋ฅผ ํ์ฉํ์ฌ ํฅ์๋ ์ ํ ์์ ์ฑ๊ณผ ์ปดํ์ผ ํ์ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ํ์ ์ธ ์๋ก๋ ts-pattern์ด ์์ต๋๋ค. ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ๊ณผ ์ผ๋ จ์ ํจํด์ ๋ฐ๋ match ํจ์ ๋๋ fluent API๋ฅผ ์ ๊ณตํ์ฌ ์ฒซ ๋ฒ์งธ ์ผ์นํ๋ ํจํด๊ณผ ๊ด๋ จ๋ ๋ก์ง์ ์คํํฉ๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ ์ ์๋ ๊ฒ๊ณผ ๊ฐ๋
์ ์ผ๋ก ์ ์ฌํ match ์ ํธ๋ฆฌํฐ๋ฅผ ์ฌ์ฉํ์ฌ handleUserAction ์์ ๋ฅผ ๋ค์ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
// ๊ฐ์ํ๋, ์ค๋ช
์ ์ธ 'match' ์ ํธ๋ฆฌํฐ. 'ts-pattern'๊ณผ ๊ฐ์ ์ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํจ์ฌ ๋ ์ ๊ตํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
const functionalMatch = (value, cases) => {
for (const [pattern, handler] of Object.entries(cases)) {
// ์ด๊ฒ์ ๊ธฐ๋ณธ์ ์ธ ํ๋ณ์ ๊ฒ์ฌ์
๋๋ค. ์ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ฌ์ธต ๊ฐ์ฒด/๋ฐฐ์ด ๋งค์นญ, ๊ฐ๋ ๋ฑ์ ์ ๊ณตํ ๊ฒ์
๋๋ค.
if (value.type === pattern) {
return handler(value);
}
}
// ์ ๊ณต๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
if (cases._ && typeof cases._ === 'function') {
return cases._(value);
}
throw new Error(`์ผ์นํ๋ ํจํด์ ์ฐพ์ ์ ์์ต๋๋ค: ${JSON.stringify(value)}`);
};
function handleUserActionWithMatch(action) {
return functionalMatch(action, {
LOGIN: (a) => `์ฌ์ฉ์ '${a.payload.username}' (${a.payload.ipAddress})๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ก๊ทธ์ธํ์ต๋๋ค.`,
LOGOUT: () => `์ฌ์ฉ์ ์ธ์
์ด ์ข
๋ฃ๋์์ต๋๋ค.`,
UPDATE_PROFILE: (a) => `์ฌ์ฉ์ '${a.payload.userId}'์ ํ๋กํ์ด ์
๋ฐ์ดํธ๋์์ต๋๋ค.`,
_: (a) => `๊ฒฝ๊ณ : ์ ์ ์๋ ์์
์ ํ '${a.type}'. ๋ฐ์ดํฐ: ${JSON.stringify(a)}` // ๊ธฐ๋ณธ ๋๋ ๋์ฒด ์ผ์ด์ค
});
}
console.log(handleUserActionWithMatch({ type: 'LOGIN', payload: { username: 'Maria', ipAddress: '10.0.0.50' } }));
console.log(handleUserActionWithMatch({ type: 'LOGOUT' }));
console.log(handleUserActionWithMatch({ type: 'VIEW_DASHBOARD', payload: { userId: 'maria456' } }));
์ด๋ ํจํด ๋งค์นญ์ ์๋๋ฅผ ๋ณด์ฌ์ค๋๋ค. ์ฆ, ๋ค๋ฅธ ๋ฐ์ดํฐ ๋ชจ์์ด๋ ๊ฐ์ ๋ํด ๋ณ๋์ ๋ถ๊ธฐ๋ฅผ ์ ์ํ๋ ๊ฒ์ ๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ค์ฒฉ๋ ๊ฐ์ฒด, ๋ฐฐ์ด ๋ฐ ์ฌ์ฉ์ ์ ์ ์กฐ๊ฑด(๊ฐ๋)์ ๋ํ ๊ฐ๋ ฅํ๊ณ ์ ํ ์์ ํ ์ผ์น๋ฅผ ์ ๊ณตํ์ฌ ์ด๋ฅผ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
๋์์ ๋ฐ์ดํฐ ํ์ (ADT) ์ดํด
๋์์ ๋ฐ์ดํฐ ํ์ (ADT)์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ ๋ํ ๊ฐ๋ ฅํ ๊ฐ๋ ์ผ๋ก, ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐํ๊ณ ์ด์ฒด์ ์ผ๋ก ๋ชจ๋ธ๋งํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ค์ ์ ํ์ ๋์์ ํฉ ๋ฐ ๊ณฑ ์ฐ์ฐ๊ณผ ์ ์ฌํ ์ฐ์ฐ์ ์ฌ์ฉํ์ฌ ๊ฒฐํฉํ๊ธฐ ๋๋ฌธ์ "๋์์ "์ด๋ผ๊ณ ๋ถ๋ฆฌ๋ฉฐ, ๊ฐ๋จํ ์ ํ์์ ๋ณต์กํ ์ ํ ์์คํ ์ ๊ตฌ์ถํ ์ ์๊ฒ ํฉ๋๋ค.
ADT์๋ ๋ ๊ฐ์ง ์ฃผ์ ํํ๊ฐ ์์ต๋๋ค:
1. ๊ณฑ์งํฉ ํ์ (Product Types)
๊ณฑ์งํฉ ํ์ ์ ์ฌ๋ฌ ๊ฐ์ ๋จ์ผํ๊ณ ์ผ๊ด๋ ์ ์ ํ์ผ๋ก ๊ฒฐํฉํฉ๋๋ค. ์ด๋ "AND"์ ๊ฐ๋ ์ ๊ตฌํํฉ๋๋ค. ์ด ์ ํ์ ๊ฐ์ A ์ ํ์ ๊ฐ๊ณผ B ์ ํ์ ๊ฐ๋ฑ์ ๊ฐ์ต๋๋ค. ๊ด๋ จ ๋ฐ์ดํฐ ์กฐ๊ฐ์ ํจ๊ป ๋ฌถ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
JavaScript์์ ์ผ๋ฐ ๊ฐ์ฒด๋ ๊ณฑ์งํฉ ํ์ ์ ๋ํ๋ด๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ๋๋ค. TypeScript์์๋ ์ฌ๋ฌ ์์ฑ์ ๊ฐ์ง ์ธํฐํ์ด์ค ๋๋ ์ ํ ๋ณ์นญ์ ์ฌ์ฉํ์ฌ ๊ณฑ์งํฉ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ ์ํ๋ฉฐ, ์ปดํ์ผ ํ์ ๊ฒ์ฌ ๋ฐ ์๋ ์์ฑ์ ์ ๊ณตํฉ๋๋ค.
์: GeoLocation (์๋ AND ๊ฒฝ๋)
GeoLocation ๊ณฑ์งํฉ ํ์
์ latitude์ longitude๋ฅผ ๊ฐ์ต๋๋ค.
// JavaScript ํํ
const currentLocation = { latitude: 34.0522, longitude: -118.2437, accuracy: 10 }; // ๋ก์ค์ค์ ค๋ ์ค
// ๊ฐ๋ ฅํ ์ ํ ๊ฒ์ฌ๋ฅผ ์ํ TypeScript ์ ์
type GeoLocation = {
latitude: number;
longitude: number;
accuracy?: number; // ์ ํ์ ์์ฑ
};
interface OrderDetails {
orderId: string;
customerId: string;
itemCount: number;
totalAmount: number;
currency: string;
orderDate: Date;
}
์ฌ๊ธฐ์ GeoLocation์ ์ฌ๋ฌ ์ซ์ ๊ฐ(๋ฐ ์ ํ์ ๊ฐ)์ ๊ฒฐํฉํ๋ ๊ณฑ์งํฉ ํ์
์
๋๋ค. OrderDetails๋ ์ฃผ๋ฌธ์ ์์ ํ ์ค๋ช
ํ๊ธฐ ์ํด ๋ค์ํ ๋ฌธ์์ด, ์ซ์ ๋ฐ Date ๊ฐ์ฒด๋ฅผ ๊ฒฐํฉํ๋ ๊ณฑ์งํฉ ํ์
์
๋๋ค.
2. ํฉ์งํฉ ํ์ (Sum Types, ํ๋ณ ์ ๋์ธ)
ํฉ์งํฉ ํ์ (์ข ์ข "ํ๊ทธ๋ ์ ๋์ธ" ๋๋ "ํ๋ณ ์ ๋์ธ"์ผ๋ก๋ ์ ๋ช ํฉ๋๋ค)์ ์ฌ๋ฌ ๋ค๋ฅธ ์ ํ ์ค ํ๋์ผ ์ ์๋ ๊ฐ์ ๋ํ๋ ๋๋ค. ์ด๋ "OR"์ ๊ฐ๋ ์ ํฌ์ฐฉํฉ๋๋ค. ์ด ์ ํ์ ๊ฐ์ A ์ ํ์ด๊ฑฐ๋ B ์ ํ์ด๊ฑฐ๋ C ์ ํ์ ๋๋ค. ํฉ์งํฉ ํ์ ์ ์ํ, ์์ ์ ๋ค๋ฅธ ๊ฒฐ๊ณผ ๋๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ณํ์ ๋ชจ๋ธ๋งํ๋ ๋ฐ ๋งค์ฐ ๊ฐ๋ ฅํ๋ฉฐ ๋ชจ๋ ๊ฐ๋ฅ์ฑ์ ๋ช ์์ ์ผ๋ก ๊ณ ๋ คํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
JavaScript์์๋ ์ผ๋ฐ์ ์ผ๋ก ๊ณตํต "ํ๋ณ์" ์์ฑ(์ข
์ข
type, kind ๋๋ _tag๋ผ๊ณ ํจ)์ ๊ณต์ ํ๋ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ํฉ์งํฉ ํ์
์ ์๋ฎฌ๋ ์ด์
ํ๋ฉฐ, ์ด ์์ฑ์ ๊ฐ์ ๊ฐ์ฒด๊ฐ ๋ํ๋ด๋ ์ ๋์ธ์ ํน์ ๋ณํ์ ์ ํํ๊ฒ ๋ํ๋
๋๋ค. ๊ทธ๋ฐ ๋ค์ TypeScript๋ ์ด ํ๋ณ์๋ฅผ ํ์ฉํ์ฌ ๊ฐ๋ ฅํ ์ ํ ์ถ์ ๋ฐ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ํํฉ๋๋ค.
์: TrafficLight ์ํ (๋นจ๊ฐ OR ๋
ธ๋ OR ์ด๋ก)
TrafficLight ์ํ๋ Red์ด๊ฑฐ๋ Yellow์ด๊ฑฐ๋ Green์
๋๋ค.
// ๋ช
์์ ์ธ ์ ํ ์ ์ ๋ฐ ์์ ์ฑ์ ์ํ TypeScript
type RedLight = {
kind: 'Red';
duration: number; // ๋ค์ ์ํ๊น์ง์ ์๊ฐ
};
type YellowLight = {
kind: 'Yellow';
duration: number;
};
type GreenLight = {
kind: 'Green';
duration: number;
isFlashing?: boolean; // Green์ ์ ํ์ ์์ฑ
};
type TrafficLight = RedLight | YellowLight | GreenLight; // ์ด๊ฒ์ด ํฉ์งํฉ ํ์
์
๋๋ค!
// ์ํ์ JavaScript ํํ
const currentLightRed: TrafficLight = { kind: 'Red', duration: 30 };
const currentLightGreen: TrafficLight = { kind: 'Green', duration: 45, isFlashing: false };
// ํฉ์งํฉ ํ์
์ ์ฌ์ฉํ์ฌ ํ์ฌ ์ ํธ๋ฑ ์ํ๋ฅผ ์ค๋ช
ํ๋ ํจ์
function describeTrafficLight(light: TrafficLight): string {
switch (light.kind) { // 'kind' ์์ฑ์ด ํ๋ณ์ ์ญํ ์ ํฉ๋๋ค.
case 'Red':
return `์ ํธ๋ฑ์ด ๋นจ๊ฐ์์
๋๋ค. ${light.duration}์ด ํ์ ๋ณ๊ฒฝ๋ฉ๋๋ค.`;
case 'Yellow':
return `์ ํธ๋ฑ์ด ๋
ธ๋์์
๋๋ค. ${light.duration}์ด ํ์ ์ ์ง ์ค๋น๋ฅผ ํ์ธ์.`;
case 'Green':
const flashingStatus = light.isFlashing ? ' ๊น๋นก์ด๊ณ ์์' : '';
return `์ ํธ๋ฑ์ด ์ด๋ก์์
๋๋ค${flashingStatus}. ${light.duration}์ด ๋์ ์์ ํ๊ฒ ์ด์ ํ์ธ์.`;
default:
// TypeScript๋ฅผ ์ฌ์ฉํ๋ฉด 'TrafficLight'๊ฐ ์ด์ฒด์ ์ด๋ฉด ์ด 'default' ์ผ์ด์ค๋ฅผ
// ๋๋ฌํ ์ ์๊ฒ ๋ง๋ค์ด ๋ชจ๋ ์ผ์ด์ค๊ฐ ์ฒ๋ฆฌ๋๋๋ก ๋ณด์ฅํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ผ๊ณ ํฉ๋๋ค.
// const _exhaustiveCheck: never = light; // TS์์ ์ปดํ์ผ ํ์ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ํด ์ฃผ์ ํด์
throw new Error(`์ ์ ์๋ ์ ํธ๋ฑ ์ํ: ${JSON.stringify(light)}`);
}
}
console.log(describeTrafficLight(currentLightRed));
console.log(describeTrafficLight(currentLightGreen));
console.log(describeTrafficLight({ kind: 'Yellow', duration: 5 }));
TypeScript ํ๋ณ ์ ๋์ธ๊ณผ ํจ๊ป ์ฌ์ฉ๋๋ ์ด switch ๋ฌธ์ ๊ฐ๋ ฅํ ํจํด ๋งค์นญ์
๋๋ค! kind ์์ฑ์ "ํ๊ทธ" ๋๋ "ํ๋ณ์" ์ญํ ์ ํ์ฌ TypeScript๊ฐ ๊ฐ case ๋ธ๋ก ๋ด์ ํน์ ์ ํ์ ์ถ๋ก ํ๊ณ ๋งค์ฐ ๊ท์คํ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ํํ ์ ์๋๋ก ํฉ๋๋ค. TrafficLight ์ ๋์ธ์ ์๋ก์ด BrokenLight ์ ํ์ ์ถ๊ฐํ์ง๋ง describeTrafficLight์ case 'Broken'์ ์ถ๊ฐํ๋ ๊ฒ์ ์์ผ๋ฉด TypeScript๋ ์ปดํ์ผ ํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์์ผ ์ ์ฌ์ ์ธ ๋ฐํ์ ๋ฒ๊ทธ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
ํจํด ๋งค์นญ ๋ฐ ADT ๊ฒฐํฉ์ผ๋ก ๊ฐ๋ ฅํ ํจํด ๋ง๋ค๊ธฐ
๋์์ ๋ฐ์ดํฐ ํ์ ์ ์ง์ ํ ํ์ ํจํด ๋งค์นญ๊ณผ ๊ฒฐํฉ๋ ๋ ๊ฐ์ฅ ๋น์ ๋ฐํฉ๋๋ค. ADT๋ ๊ตฌ์กฐํ๋๊ณ ์ ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๊ณ , ํจํด ๋งค์นญ์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ๋ถํดํ๊ณ ์๋ํ๋ ์ฐ์ํ๊ณ ์ด์ฒด์ ์ด๋ฉฐ ์ ํ ์์ ํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค. ์ด ์๋์ง๋ ์ฝ๋ ๋ช ํ์ฑ์ ํฌ๊ฒ ๊ฐ์ ํ๊ณ , ์์ฉ๊ตฌ ์ฝ๋๋ฅผ ์ค์ด๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฌ๊ณ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
์ด ๊ฐ๋ ฅํ ์กฐํฉ์ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋ ์ผ๋ฐ์ ์ด๊ณ ๋งค์ฐ ํจ๊ณผ์ ์ธ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ํจํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ด๋ ๋ค์ํ ๊ธ๋ก๋ฒ ์ํํธ์จ์ด ์ปจํ ์คํธ์ ์ ์ฉํ ์ ์์ต๋๋ค.
1. Option ํ์
: null ๋ฐ undefined ํผ๋ ๊ธธ๋ค์ด๊ธฐ
JavaScript์ ๊ฐ์ฅ ์
๋ช
๋์ ํจ์ ์ค ํ๋์ด๋ฉฐ ๋ชจ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์๋ง์ ๋ฐํ์ ์ค๋ฅ์ ์์ฒ์ null ๋ฐ undefined์ ๊ด๋ฒ์ํ ์ฌ์ฉ์
๋๋ค. ์ด๋ฌํ ๊ฐ์ ๊ฐ์ ๋ถ์ฌ๋ฅผ ๋ํ๋ด์ง๋ง, ์์์ ํน์ฑ์ผ๋ก ์ธํด ์์์น ๋ชปํ ๋์๊ณผ ๋๋ฒ๊น
ํ๊ธฐ ์ด๋ ค์ด TypeError: Cannot read properties of undefined๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์ ๋น๋กฏ๋ Option(๋๋ Maybe) ํ์
์ ๊ฐ์ ์กด์ฌ ๋๋ ๋ถ์ฌ๋ฅผ ๋ช
ํํ๊ฒ ๋ชจ๋ธ๋งํจ์ผ๋ก์จ ๊ฐ๋ ฅํ๊ณ ๋ช
์์ ์ธ ๋์์ ์ ๊ณตํฉ๋๋ค.
Option ํ์
์ ๋ ๊ฐ์ ๋ณ๋ ๋ณํ์ ๊ฐ์ง ํฉ์งํฉ ํ์
์
๋๋ค:
Some<T>: T ์ ํ์ ๊ฐ์ด ์กด์ฌํจ์ ๋ช ์์ ์ผ๋ก ๋ํ๋ ๋๋ค.None: ๊ฐ์ด ์กด์ฌํ์ง ์์์ ๋ช ์์ ์ผ๋ก ๋ํ๋ ๋๋ค.
๊ตฌํ ์ (TypeScript)
// Option ํ์
์ ํ๋ณ ์ ๋์ธ์ผ๋ก ์ ์
type Option<T> = Some<T> | None;
interface Some<T> {
readonly _tag: 'Some'; // ํ๋ณ์
readonly value: T;
}
interface None {
readonly _tag: 'None'; // ํ๋ณ์
}
// ๋ช
ํํ ์๋๋ก Option ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๋์ฐ๋ฏธ ํจ์
const Some = <T>(value: T): Option<T> => ({ _tag: 'Some', value });
const None = (): Option<never> => ({ _tag: 'None' }); // 'never'๋ ํน์ ์ ํ์ ๊ฐ์ ๋ณด์ ํ์ง ์์์ ์๋ฏธํฉ๋๋ค.
// ์: ๋น์ด ์์ ์ ์๋ ๋ฐฐ์ด์์ ์์๋ฅผ ์์ ํ๊ฒ ๊ฐ์ ธ์ค๊ธฐ
function getFirstElement<T>(arr: T[]): Option<T> {
return arr.length > 0 ? Some(arr[0]) : None();
}
const productIDs = ['P101', 'P102', 'P103'];
const emptyCart: string[] = [];
const firstProductID = getFirstElement(productIDs); // Some('P101')๋ฅผ ํฌํจํ๋ Option
const noProductID = getFirstElement(emptyCart); // None๋ฅผ ํฌํจํ๋ Option
console.log(JSON.stringify(firstProductID)); // {"_tag":"Some","value":"P101"}
console.log(JSON.stringify(noProductID)); // {"_tag":"None"}
Option์ ์ฌ์ฉํ ํจํด ๋งค์นญ
์ด์ if (value !== null && value !== undefined) ๊ฒ์ฌ๋ฅผ ์ํ ์์ฉ๊ตฌ ์ฝ๋ ๋์ , ํจํด ๋งค์นญ์ ์ฌ์ฉํ์ฌ Some ๋ฐ None์ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํ์ฌ ๋ ๊ฒฌ๊ณ ํ๊ณ ๊ฐ๋
์ฑ ๋์ ๋ก์ง์ ์ป์ต๋๋ค.
// Option์ ์ํ ์ผ๋ฐ 'match' ์ ํธ๋ฆฌํฐ. ์ค์ ํ๋ก์ ํธ์์๋ 'ts-pattern' ๋๋ 'fp-ts'์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ถ์ฅํฉ๋๋ค.
function matchOption<T, R>(
option: Option<T>,
onSome: (value: T) => R,
onNone: () => R
): R {
if (option._tag === 'Some') {
return onSome(option.value);
} else {
return onNone();
}
}
const displayUserID = (userID: Option<string>) =>
matchOption(
userID,
(id) => `์ฌ์ฉ์ ID ๋ฐ๊ฒฌ: ${id.substring(0, 5)}...`,
() => `์ฌ์ฉ์ ID๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.`
);
console.log(displayUserID(Some('user_id_from_db_12345'))); // "์ฌ์ฉ์ ID ๋ฐ๊ฒฌ: user_i..."
console.log(displayUserID(None())); // "์ฌ์ฉ์ ID๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค."
// ๋ ๋ณต์กํ ์๋๋ฆฌ์ค: Option์ ์์ฑํ ์ ์๋ ์์
์ฒด์ธ
const safeParseQuantity = (s: string): Option<number> => {
const num = parseInt(s, 10);
return isNaN(num) ? None() : Some(num);
};
const calculateTotalPrice = (price: number, quantity: Option<number>): Option<number> => {
return matchOption(
quantity,
(qty) => Some(price * qty),
() => None() // ์๋์ด None์ด๋ฉด ์ด ๊ฐ๊ฒฉ์ ๊ณ์ฐํ ์ ์์ผ๋ฏ๋ก None ๋ฐํ
);
};
const itemPrice = 25.50;
// console.log(displayUserID(calculateTotalPrice(itemPrice, safeParseQuantity('5'))).toString()); // ์ด์ ๋ ์ซ์์ ๋ํ ๋ค๋ฅธ ๋์คํ๋ ์ด ํจ์๋ฅผ ์ ์ฉํด์ผ ํฉ๋๋ค.
// ์ด์ ์ซ์์ ๋ํ ์๋ ๋์คํ๋ ์ด
const total1 = calculateTotalPrice(itemPrice, safeParseQuantity('5'));
console.log(matchOption(total1, (val) => `์ด์ก: ${val.toFixed(2)}`, () => '๊ณ์ฐ ์คํจ.')); // ์ด์ก: 127.50
const total2 = calculateTotalPrice(itemPrice, safeParseQuantity('invalid_input'));
console.log(matchOption(total2, (val) => `์ด์ก: ${val.toFixed(2)}`, () => '๊ณ์ฐ ์คํจ.')); // ๊ณ์ฐ ์คํจ.
const total3 = calculateTotalPrice(itemPrice, None());
console.log(matchOption(total3, (val) => `์ด์ก: ${val.toFixed(2)}`, () => '๊ณ์ฐ ์คํจ.')); // ๊ณ์ฐ ์คํจ.
Some ๋ฐ None ์ผ์ด์ค๋ฅผ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ๊ฐ์ ํจ์ผ๋ก์จ Option ํ์
๊ณผ ํจํด ๋งค์นญ์ ์กฐํฉ์ null ๋๋ undefined ๊ด๋ จ ์ค๋ฅ์ ๊ฐ๋ฅ์ฑ์ ํฌ๊ฒ ์ค์
๋๋ค. ์ด๋ ํนํ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ด ์ค์ํ ์์คํ
์์ ๋ ๊ฒฌ๊ณ ํ๊ณ ์์ธก ๊ฐ๋ฅํ๋ฉฐ ์์ฒด ๋ฌธ์ํ๋๋ ์ฝ๋๋ก ์ด์ด์ง๋๋ค.
2. Result ํ์
: ๊ฐ๋ ฅํ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ๋ช
์์ ๊ฒฐ๊ณผ
์ ํต์ ์ธ JavaScript ์ค๋ฅ ์ฒ๋ฆฌ๋ ์์ธ์ ๋ํด `try...catch` ๋ธ๋ก์ ์ฌ์ฉํ๊ฑฐ๋ ์คํจ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ๋จ์ํ `null`/`undefined`๋ฅผ ๋ฐํํ๋ ๋ฐ ์์กดํฉ๋๋ค. `try...catch`๋ ์ง์ ์ผ๋ก ์์ธ์ ์ด๊ณ ๋ณต๊ตฌ ๋ถ๊ฐ๋ฅํ ์ค๋ฅ์ ํ์์ ์ด์ง๋ง, ์์๋๋ ์คํจ์ ๋ํด `null` ๋๋ `undefined`๋ฅผ ๋ฐํํ๋ ๊ฒ์ ๋ฌด์๋๊ธฐ ์ฌ์ ๋ค์ด์คํธ๋ฆผ์์ ์ฒ๋ฆฌ๋์ง ์์ ์ค๋ฅ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. `Result`(๋๋ `Either`) ํ์ ์ ์ฑ๊ณต๊ณผ ์คํจ๋ฅผ ๋ ๋ค ์ ํจํ์ง๋ง ๋ถ๋ช ํ ๊ตฌ๋ณ๋๋ ๋ ๊ฐ์ง ๊ฒฐ๊ณผ๋ก ์ทจ๊ธํ์ฌ ์ฑ๊ณตํ๊ฑฐ๋ ์คํจํ ์ ์๋ ์์ ์ ๋ ํจ์์ ์ด๊ณ ๋ช ์์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
Result ํ์
์ ๋ ๊ฐ์ ๋ณ๋ ๋ณํ์ ๊ฐ์ง ํฉ์งํฉ ํ์
์
๋๋ค:
Ok<T>: ์ฑ๊ณต์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ฉฐ, T ์ ํ์ ์ฑ๊ณต ๊ฐ์ ๋ณด์ ํฉ๋๋ค.Err<E>: ์คํจํ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ฉฐ, E ์ ํ์ ์ค๋ฅ ๊ฐ์ ๋ณด์ ํฉ๋๋ค.
๊ตฌํ ์ (TypeScript)
type Result<T, E> = Ok<T> | Err<E>;
interface Ok<T> {
readonly _tag: 'Ok'; // ํ๋ณ์
readonly value: T;
}
interface Err<E> {
readonly _tag: 'Err'; // ํ๋ณ์
readonly error: E;
}
// Result ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ธฐ ์ํ ๋์ฐ๋ฏธ ํจ์
const Ok = <T>(value: T): Result<T, never> => ({ _tag: 'Ok', value });
const Err = <E>(error: E): Result<never, E> => ({ _tag: 'Err', error });
// ์: ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํํ๊ณ ์คํจํ ์ ์๋ ํจ์
type PasswordError = 'TooShort' | 'NoUppercase' | 'NoNumber';
function validatePassword(password: string): Result<string, PasswordError> {
if (password.length < 8) {
return Err('TooShort');
}
if (!/[A-Z]/.test(password)) {
return Err('NoUppercase');
}
if (!/[0-9]/.test(password)) {
return Err('NoNumber');
}
return Ok('Password is valid!');
}
const validationResult1 = validatePassword('MySecurePassword1'); // Ok('Password is valid!')
const validationResult2 = validatePassword('short'); // Err('TooShort')
const validationResult3 = validatePassword('nopassword'); // Err('NoUppercase')
const validationResult4 = validatePassword('NoPassword'); // Err('NoNumber')
Result์ ์ฌ์ฉํ ํจํด ๋งค์นญ
Result ํ์
์ ๋ํ ํจํด ๋งค์นญ์ ํตํด ์ฑ๊ณต์ ์ธ ๊ฒฐ๊ณผ์ ํน์ ์ค๋ฅ ์ ํ ๋ชจ๋๋ฅผ ๊น๋ํ๊ณ ์กฐํฉ ๊ฐ๋ฅํ ๋ฐฉ์์ผ๋ก ๊ฒฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
function matchResult<T, E, R>(
result: Result<T, E>,
onOk: (value: T) => R,
onErr: (error: E) => R
): R {
if (result._tag === 'Ok') {
return onOk(result.value);
} else {
return onErr(result.error);
}
}
const handlePasswordValidation = (validationResult: Result<string, PasswordError>) =>
matchResult(
validationResult,
(message) => `์ฑ๊ณต: ${message}`,
(error) => `์ค๋ฅ: ${error}`
);
console.log(handlePasswordValidation(validatePassword('StrongPassword123'))); // ์ฑ๊ณต: Password is valid!
console.log(handlePasswordValidation(validatePassword('weak'))); // ์ค๋ฅ: TooShort
console.log(handlePasswordValidation(validatePassword('weakpassword'))); // ์ค๋ฅ: NoUppercase
// Result๋ฅผ ๋ฐํํ๋ ์์
์ฒด์ธ, ์ ์ฌ์ ์ผ๋ก ์คํจํ๋ ๋จ๊ณ์ ์ํ์ค๋ฅผ ๋ํ๋
๋๋ค.
type UserRegistrationError = 'InvalidEmail' | 'PasswordValidationFailed' | 'DatabaseError';
function registerUser(email: string, passwordAttempt: string): Result<string, UserRegistrationError> {
// ๋จ๊ณ 1: ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ
if (!email.includes('@') || !email.includes('.')) {
return Err('InvalidEmail');
}
// ๋จ๊ณ 2: ์ด์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ ์ ํจ์ฑ ๊ฒ์ฌ
const passwordValidation = validatePassword(passwordAttempt);
if (passwordValidation._tag === 'Err') {
// PasswordError๋ฅผ ๋ ์ผ๋ฐ์ ์ธ UserRegistrationError๋ก ๋งคํ
return Err('PasswordValidationFailed');
}
// ๋จ๊ณ 3: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์์ฑ ์๋ฎฌ๋ ์ด์
const success = Math.random() > 0.1; // ์์ฐ์ ์ํ 90% ์ฑ๊ณต ํ๋ฅ
if (!success) {
return Err('DatabaseError');
}
return Ok(`์ฌ์ฉ์ '${email}'์ด(๊ฐ) ์ฑ๊ณต์ ์ผ๋ก ๋ฑ๋ก๋์์ต๋๋ค.`);
}
const processRegistration = (email: string, passwordAttempt: string) =>
matchResult(
registerUser(email, passwordAttempt),
(successMsg) => `๋ฑ๋ก ์ํ: ${successMsg}`,
(error) => `๋ฑ๋ก ์คํจ: ${error}`
);
console.log(processRegistration('test@example.com', 'SecurePass123!')); // ๋ฑ๋ก ์ํ: ์ฌ์ฉ์ 'test@example.com'์ด(๊ฐ) ์ฑ๊ณต์ ์ผ๋ก ๋ฑ๋ก๋์์ต๋๋ค. (๋๋ DatabaseError)
console.log(processRegistration('invalid-email', 'SecurePass123!')); // ๋ฑ๋ก ์คํจ: InvalidEmail
console.log(processRegistration('test@example.com', 'short')); // ๋ฑ๋ก ์คํจ: PasswordValidationFailed
Result ํ์
์ "ํ๋ณตํ ๊ฒฝ๋ก" ์คํ์ผ์ ์ฝ๋๋ฅผ ์ฅ๋ คํฉ๋๋ค. ์ฌ๊ธฐ์ ์ฑ๊ณต์ ๊ธฐ๋ณธ๊ฐ์ด๊ณ , ์คํจ๋ ์์ธ์ ์ธ ์ ์ด ํ๋ฆ์ด ์๋ ๋ช
์์ ์ธ 1๊ธ ๊ฐ์ผ๋ก ์ทจ๊ธ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฝ๋๋ฅผ ํจ์ฌ ๋ ์ฝ๊ฒ ์ถ๋ก ํ๊ณ , ํ
์คํธํ๊ณ , ์กฐํฉํ ์ ์์ผ๋ฉฐ, ํนํ ๋ช
์์ ์ธ ์ค๋ฅ ์ฒ๋ฆฌ๊ฐ ํ์์ ์ธ ์ค์ ๋น์ฆ๋์ค ๋ก์ง ๋ฐ API ํตํฉ์ ์ ์ฉํฉ๋๋ค.
3. ๋ณต์กํ ๋น๋๊ธฐ ์ํ ๋ชจ๋ธ๋ง: RemoteData ํจํด
๋์ ์ฌ์ฉ์์ธต์ด๋ ์ง์ญ์ ๊ด๊ณ์์ด ํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ข ์ข ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ(์: API ํธ์ถ, ๋ก์ปฌ ์คํ ๋ฆฌ์ง ์ฝ๊ธฐ)๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ์๊ฒฉ ๋ฐ์ดํฐ ์์ฒญ์ ๋ค์ํ ์ํ(์์ง ์์๋์ง ์์, ๋ก๋ฉ ์ค, ์คํจ, ์ฑ๊ณต)๋ฅผ ๊ฐ๋จํ ๋ถ์ธ ํ๋๊ทธ(`isLoading`, `hasError`, `isDataPresent`)๋ฅผ ์ฌ์ฉํ์ฌ ๊ด๋ฆฌํ๋ ๊ฒ์ ๋น ๋ฅด๊ฒ ๋ฒ๊ฑฐ๋กญ๊ณ ์ผ๊ด์ฑ์ด ์์ผ๋ฉฐ ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋๋ค. `RemoteData` ํจํด, ์ฆ ADT๋ ์ด๋ฌํ ๋น๋๊ธฐ ์ํ๋ฅผ ๊น๋ํ๊ณ ์ผ๊ด๋๋ฉฐ ์ด์ฒด์ ์ธ ๋ฐฉ์์ผ๋ก ๋ชจ๋ธ๋งํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
RemoteData<T, E> ํ์
์ ์ผ๋ฐ์ ์ผ๋ก ๋ค ๊ฐ์ง ๋ณ๋ ๋ณํ์ ๊ฐ์ต๋๋ค:
NotAsked: ์์ฒญ์ด ์์ง ์์๋์ง ์์์ต๋๋ค.Loading: ์์ฒญ์ด ํ์ฌ ์งํ ์ค์ ๋๋ค.Failure<E>: E ์ ํ์ ์ค๋ฅ๋ก ์์ฒญ์ด ์คํจํ์ต๋๋ค.Success<T>: T ์ ํ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ฉฐ ์์ฒญ์ด ์ฑ๊ณตํ์ต๋๋ค.
๊ตฌํ ์ (TypeScript)
type RemoteData<T, E> = NotAsked | Loading | Failure<E> | Success<T>;
interface NotAsked {
readonly _tag: 'NotAsked';
}
interface Loading {
readonly _tag: 'Loading';
}
interface Failure<E> {
readonly _tag: 'Failure';
readonly error: E;
}
interface Success<T> {
readonly _tag: 'Success';
readonly data: T;
}
const NotAsked = (): RemoteData<never, never> => ({ _tag: 'NotAsked' });
const Loading = (): RemoteData<never, never> => ({ _tag: 'Loading' });
const Failure = <E>(error: E): RemoteData<never, E> => ({ _tag: 'Failure', error });
const Success = <T>(data: T): RemoteData<T, never> => ({ _tag: 'Success', data });
// ์: ์ ์ ์๊ฑฐ๋ ํ๋ซํผ์ ์ํ ์ ํ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
type Product = { id: string; name: string; price: number; currency: string };
type FetchProductsError = { code: number; message: string };
let productListState: RemoteData<Product[], FetchProductsError> = NotAsked();
async function fetchProductList(): Promise<void> {
productListState = Loading(); // ์ฆ์ ์ํ๋ฅผ ๋ก๋ฉ ์ค์ผ๋ก ์ค์
try {
const response = await new Promise<Product[]>((resolve, reject) => {
setTimeout(() => {
const shouldSucceed = Math.random() > 0.2; // ์์ฐ์ ์ํ 80% ์ฑ๊ณต ํ๋ฅ
if (shouldSucceed) {
resolve([
{ id: 'prd-001', name: 'Wireless Headphones', price: 99.99, currency: 'USD' },
{ id: 'prd-002', name: 'Smartwatch', price: 199.50, currency: 'EUR' },
{ id: 'prd-003', name: 'Portable Charger', price: 29.00, currency: 'GBP' }
]);
} else {
reject({ code: 503, message: '์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋์ค์ ๋ค์ ์๋ํ์ญ์์ค.' });
}
}, 2000); // 2์ด์ ๋คํธ์ํฌ ์ง์ฐ ์๋ฎฌ๋ ์ด์
});
productListState = Success(response);
} catch (err: any) {
productListState = Failure({ code: err.code || 500, message: err.message || '์์์น ๋ชปํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.' });
}
}
๋์ UI ๋ ๋๋ง์ ์ํ RemoteData๋ฅผ ์ฌ์ฉํ ํจํด ๋งค์นญ
RemoteData ํจํด์ ๋น๋๊ธฐ ๋ฐ์ดํฐ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค ๋ ๋๋ง์ ํนํ ํจ๊ณผ์ ์ด๋ฉฐ, ์ ์ธ๊ณ์ ์ผ๋ก ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ณด์ฅํฉ๋๋ค. ํจํด ๋งค์นญ์ ์ฌ์ฉํ๋ฉด ๊ฐ ๊ฐ๋ฅํ ์ํ์ ๋ํด ํ์ํ ๋ด์ฉ์ ์ ํํ๊ฒ ์ ์ํ์ฌ ๊ฒฝ์ ์กฐ๊ฑด์ด๋ ์ผ๊ด์ฑ ์๋ UI ์ํ๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
function renderProductListUI(state: RemoteData<Product[], FetchProductsError>): string {
switch (state._tag) {
case 'NotAsked':
return `<p>ํ์ํฉ๋๋ค! '์ ํ ๋ก๋'๋ฅผ ํด๋ฆญํ์ฌ ์นดํ๋ก๊ทธ๋ฅผ ์ฐพ์๋ณด์ธ์.</p>`;
case 'Loading':
return `<div><em>์ ํ ๋ก๋ฉ ์ค... ์ ์๋ง ๊ธฐ๋ค๋ ค ์ฃผ์ธ์.</em></div><div><small>ํนํ ์ฐ๊ฒฐ ์๋๊ฐ ๋๋ฆฐ ๊ฒฝ์ฐ ์๊ฐ์ด ๊ฑธ๋ฆด ์ ์์ต๋๋ค.</small></div>`;
case 'Failure':
return `<div style="color: red;"><strong>์ ํ ๋ก๋ ์ค๋ฅ:</strong> ${state.error.message} (์ฝ๋: ${state.error.code})</div><p>์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํ๊ฑฐ๋ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ์ญ์์ค.</p>`;
case 'Success':
return `<h3>์ฌ์ฉ ๊ฐ๋ฅํ ์ ํ:</h3>
<ul>
${state.data.map(product => `<li>${product.name} - ${product.currency} ${product.price.toFixed(2)}</li>`).join('
')}
</ul>
<p>${state.data.length}๊ฐ ํญ๋ชฉ ํ์ ์ค.</p>`;
default:
// TypeScript ์ด์ฒด์ฑ ๊ฒ์ฌ: RemoteData์ ๋ชจ๋ ์ผ์ด์ค๊ฐ ์ฒ๋ฆฌ๋์๋์ง ํ์ธํฉ๋๋ค.
// RemoteData์ ์ ํ๊ทธ๊ฐ ์ถ๊ฐ๋์์ง๋ง ์ฌ๊ธฐ์ ์ฒ๋ฆฌ๋์ง ์์ ๊ฒฝ์ฐ TS๊ฐ ํ๋๊ทธ๋ฅผ ์ง์ ํฉ๋๋ค.
const _exhaustiveCheck: never = state;
return `<div style="color: orange;">๊ฐ๋ฐ ์ค๋ฅ: ์ฒ๋ฆฌ๋์ง ์์ UI ์ํ!</div>`;
}
}
// ์ฌ์ฉ์ ์ํธ ์์ฉ ๋ฐ ์ํ ๋ณ๊ฒฝ ์๋ฎฌ๋ ์ด์
console.log('
--- ์ด๊ธฐ UI ์ํ ---
');
console.log(renderProductListUI(productListState)); // NotAsked
// ๋ก๋ฉ ์๋ฎฌ๋ ์ด์
productListState = Loading();
console.log('
--- ๋ก๋ฉ ์ค UI ์ํ ---
');
console.log(renderProductListUI(productListState));
// ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์๋ฃ ์๋ฎฌ๋ ์ด์
(Success ๋๋ Failure๊ฐ ๋ ๊ฒ์
๋๋ค)
fetchProductList().then(() => {
console.log('
--- ๊ฐ์ ธ์ค๊ธฐ ํ UI ์ํ ---
');
console.log(renderProductListUI(productListState));
});
// ์์ ๋ฅผ ์ํ ๋ ๋ค๋ฅธ ์๋ ์ํ
setTimeout(() => {
console.log('
--- ๊ฐ์ ์คํจ UI ์ํ ์์ ---
');
productListState = Failure({ code: 401, message: '์ธ์ฆ์ด ํ์ํฉ๋๋ค.' });
console.log(renderProductListUI(productListState));
}, 3000); // ์ ์ ํ, ๋ค๋ฅธ ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด
์ด ์ ๊ทผ ๋ฐฉ์์ ํจ์ฌ ๋ ๊น๋ํ๊ณ , ๋ ์์ ์ ์ด๋ฉฐ, ๋ ์์ธก ๊ฐ๋ฅํ UI ์ฝ๋๋ก ์ด์ด์ง๋๋ค. ๊ฐ๋ฐ์๋ ๋ชจ๋ ๊ฐ๋ฅํ ์๊ฒฉ ๋ฐ์ดํฐ ์ํ๋ฅผ ๊ณ ๋ คํ๊ณ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ๋ฏ๋ก, UI๊ฐ ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ํ์ํ๊ฑฐ๋ ์๋ชป๋ ๋ก๋ฉ ํ์๊ธฐ๋ฅผ ํ์ํ๊ฑฐ๋ ์ฌ์ผ๋ฐํธํ๊ฒ ์คํจํ๋ ๋ฒ๊ทธ๋ฅผ ๋์ ํ๊ธฐ๊ฐ ํจ์ฌ ๋ ์ด๋ ค์์ง๋๋ค. ์ด๋ ๋ค์ํ ๋คํธ์ํฌ ์กฐ๊ฑด์ ๊ฐ์ง ๋ค์ํ ์ฌ์ฉ์๋ฅผ ๋์์ผ๋ก ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํนํ ์ ์ฉํฉ๋๋ค.
๊ณ ๊ธ ๊ฐ๋ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก
์ด์ฒด์ฑ ๊ฒ์ฌ: ๊ถ๊ทน์ ์์ ๋ง
ADT๋ฅผ ํจํด ๋งค์นญ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ด์ ์ค ํ๋(ํนํ TypeScript์ ํตํฉ๋ ๋)๋ **์ด์ฒด์ฑ ๊ฒ์ฌ**์
๋๋ค. ์ด ์ค์ํ ๊ธฐ๋ฅ์ ํฉ์งํฉ ํ์
์ ๊ฐ๋ฅํ ๋ชจ๋ ๊ฒฝ์ฐ๋ฅผ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํ๋์ง ํ์ธํฉ๋๋ค. ADT์ ์๋ก์ด ๋ณํ์ ๋์
ํ์ง๋ง ์ด๋ฅผ ์ฒ๋ฆฌํ๋ switch ๋ฌธ์ด๋ match ํจ์๋ฅผ ์
๋ฐ์ดํธํ๋ ๊ฒ์ ์์ผ๋ฉด TypeScript๋ ์ฆ์ ์ปดํ์ผ ํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค. ์ด ๊ธฐ๋ฅ์ ๋ฐฐํฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ฐ๊ฒฌํ๊ธฐ ์ด๋ ต๊ณ ๋น์ฉ์ด ๋ง์ด ๋ค ์ ์๋ ํ๊ดด์ ์ธ ๋ฐํ์ ๋ฒ๊ทธ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
TypeScript์์ ์ด๋ฅผ ๋ช
์์ ์ผ๋ก ํ์ฑํํ๊ธฐ ์ํด ์ผ๋ฐ์ ์ธ ํจํด์ ๊ธฐ๋ณธ ์ผ์ด์ค๋ฅผ ์ถ๊ฐํ์ฌ ์ฒ๋ฆฌ๋์ง ์์ ๊ฐ์ never ์ ํ์ ๋ณ์์ ํ ๋นํ๋ ๊ฒ์
๋๋ค:
function assertNever(value: never): never {
throw new Error(`์ฒ๋ฆฌ๋์ง ์์ ํ๋ณ ์ ๋์ธ ๋ฉค๋ฒ: ${JSON.stringify(value)}`);
}
// switch ๋ฌธ์ default ์ผ์ด์ค ๋ด ์ฌ์ฉ๋ฒ:
// default:
// return assertNever(someADTValue);
// 'someADTValue'๊ฐ ๋ค๋ฅธ ์ผ์ด์ค์์ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌ๋์ง ์์ ์ ํ์ด ๋ ์ ์๋ค๋ฉด
// TypeScript๋ ์ฌ๊ธฐ์ ์ปดํ์ผ ํ์ ์ค๋ฅ๋ฅผ ์์ฑํฉ๋๋ค.
์ด๋ ์ ์ฌ์ ์ธ ๋ฐํ์ ๋ฒ๊ทธ๋ฅผ ๊ฐ๋ฐ ์ฃผ๊ธฐ์ ๊ฐ์ฅ ์ด๊ธฐ ๋จ๊ณ์์ ๋ฌธ์ ๋ฅผ ํฌ์ฐฉํ๋ ์ปดํ์ผ ํ์ ์ค๋ฅ๋ก ๋ณํํฉ๋๋ค.
ADT ๋ฐ ํจํด ๋งค์นญ์ ์ฌ์ฉํ ๋ฆฌํฉํ ๋ง: ์ ๋ต์ ์ ๊ทผ ๋ฐฉ์
๊ธฐ์กด JavaScript ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ด๋ฌํ ๊ฐ๋ ฅํ ํจํด์ ํตํฉํ๋๋ก ๋ฆฌํฉํ ๋งํ ๋, ํน์ ์ฝ๋ ์ค๋ฉ๊ณผ ๊ธฐํ์ ์ฃผ๋ชฉํ์ญ์์ค:
- ๊ธด `if/else if` ์ฒด์ธ ๋๋ ๊น์ด ์ค์ฒฉ๋ `switch` ๋ฌธ: ADT ๋ฐ ํจํด ๋งค์นญ์ผ๋ก ๋์ฒดํ ์ ์๋ ์ต์ ์ ํ๋ณด๋ก, ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
- ์คํจ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด `null` ๋๋ `undefined`๋ฅผ ๋ฐํํ๋ ํจ์:
Option๋๋Resultํ์ ์ ๋์ ํ์ฌ ๋ถ์ฌ ๋๋ ์ค๋ฅ์ ๊ฐ๋ฅ์ฑ์ ๋ช ์์ ์ผ๋ก ๋ง๋์ญ์์ค. - ์ฌ๋ฌ ๋ถ์ธ ํ๋๊ทธ (์: `isLoading`, `hasError`, `isSuccess`): ์ด๋ค์ ์ข
์ข
๋จ์ผ ์ํฐํฐ์ ๋ค๋ฅธ ์ํ๋ฅผ ๋ํ๋
๋๋ค. ์ด๋ฅผ ๋จ์ผ
RemoteData๋๋ ์ ์ฌํ ADT๋ก ํตํฉํ์ญ์์ค. - ๋ ผ๋ฆฌ์ ์ผ๋ก ์ฌ๋ฌ ๊ฐ์ง ๊ณ ์ ํ ํํ๋ฅผ ๊ฐ์ง ์ ์๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ: ์ด๋ฅผ ํฉ์งํฉ ํ์ ์ผ๋ก ์ ์ํ์ฌ ๋ณํ์ ๋ช ํํ๊ฒ ์ด๊ฑฐํ๊ณ ๊ด๋ฆฌํฉ๋๋ค.
์ ์ง์ ์ธ ์ ๊ทผ ๋ฐฉ์์ ์ฑํํ์ญ์์ค. TypeScript ํ๋ณ ์ ๋์ธ์ ์ฌ์ฉํ์ฌ ADT๋ฅผ ์ ์ํ ๋ค์, ์ฌ์ฉ์ ์ง์ ์ ํธ๋ฆฌํฐ ํจ์ ๋๋ ๊ฐ๋ ฅํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธฐ๋ฐ ์๋ฃจ์ ์ ์ฌ์ฉํ์ฌ ์กฐ๊ฑด๋ถ ๋ก์ง์ ์ ์ง์ ์ผ๋ก ๋์ฒดํ์ญ์์ค. ์ด ์ ๋ต์ ํตํด ์ ๋ฉด์ ์ด๊ณ ๋ฐฉํด์ ์ธ ์ฌ์์ฑ ์์ด ์ด์ ์ ๋์ ํ ์ ์์ต๋๋ค.
์ฑ๋ฅ ๊ณ ๋ ค ์ฌํญ
๋๋ถ๋ถ์ JavaScript ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฒฝ์ฐ ADT ๋ณํ(์: Some({ _tag: 'Some', value: ... }))์ ๋ํ ์์ ๊ฐ์ฒด ์์ฑ์ ์ฌ์ํ ์ค๋ฒํค๋๋ ๋ฌด์ํ ์ ์์ต๋๋ค. ์ต์ JavaScript ์์ง(V8, SpiderMonkey, Chakra ๋ฑ)์ ๊ฐ์ฒด ์์ฑ, ์์ฑ ์ก์ธ์ค ๋ฐ ๊ฐ๋น์ง ์ปฌ๋ ์
์ ๋ํด ๊ณ ๋๋ก ์ต์ ํ๋์ด ์์ต๋๋ค. ์ฝ๋ ๋ช
ํ์ฑ, ์ ์ง๋ณด์์ฑ ํฅ์ ๋ฐ ๋ฒ๊ทธ ๊ฐ์๋ก ์ธํ ์๋นํ ์ด์ ์ ์ผ๋ฐ์ ์ผ๋ก ๋ง์ดํฌ๋ก ์ต์ ํ ์ฐ๋ ค๋ฅผ ํจ์ฌ ๋ฅ๊ฐํฉ๋๋ค. ์๋ฐฑ๋ง ๋ฒ์ ๋ฐ๋ณต์ด ํฌํจ๋ ๊ทน๋๋ก ์ฑ๋ฅ์ด ์ค์ํ ๋ฃจํ์์ ๋ชจ๋ CPU ์ฃผ๊ธฐ๊ฐ ์ค์ํ๋ค๋ฉด ํด๋น ์ธก์ ์ ์ธก์ ํ๊ณ ์ต์ ํํ๋ ๊ฒ์ ๊ณ ๋ คํ ์ ์์ง๋ง, ์ผ๋ฐ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์์๋ ์ด๋ฌํ ์๋๋ฆฌ์ค๊ฐ ๋๋ญ
๋๋ค.
๋๊ตฌ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋๋งน
๊ธฐ๋ณธ ADT ๋ฐ ๋งค์นญ ์ ํธ๋ฆฌํฐ๋ฅผ ์ง์ ๊ตฌํํ ์ ์์ง๋ง, ํ๋ฆฝ๋๊ณ ์ ์ ์ง ๊ด๋ฆฌ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ก์ธ์ค๋ฅผ ํฌ๊ฒ ๊ฐ์ํํ๊ณ ๋ ์ ๊ตํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ณด์ฅํ ์ ์์ต๋๋ค:
ts-pattern: TypeScript๋ฅผ ์ํ ๋งค์ฐ ๊ถ์ฅ๋๋ ๊ฐ๋ ฅํ๊ณ ์ ํ ์์ ํ ํจํด ๋งค์นญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. Fluent API, ์ฌ์ธต ๋งค์นญ ๊ธฐ๋ฅ(์ค์ฒฉ ๊ฐ์ฒด ๋ฐ ๋ฐฐ์ด), ๊ณ ๊ธ ๊ฐ๋ ๋ฐ ํ๋ฅญํ ์ด์ฒด์ฑ ๊ฒ์ฌ๋ฅผ ์ ๊ณตํ์ฌ ์ฌ์ฉํ๊ธฐ ์ฆ๊ฒ์ต๋๋ค.fp-ts: TypeScript๋ฅผ ์ํ ํฌ๊ด์ ์ธ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก,Option,Either(Result์ ์ ์ฌ),TaskEither๋ฐ ๊ธฐํ ๋ง์ ๊ณ ๊ธ FP ๊ตฌ์ฑ์ ์ํ ๊ฐ๋ ฅํ ๊ตฌํ์ ํฌํจํ๋ฉฐ, ์ข ์ข ๋ด์ฅ ํจํด ๋งค์นญ ์ ํธ๋ฆฌํฐ ๋๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.purify-ts: ๊ด์ฉ์ ์ธMaybe(Option) ๋ฐEither(Result) ํ์ ๊ณผ ํจ๊ป ์๋ํ๋ ๋ค์ํ ์ค์ฉ์ ์ธ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๋ ๋ค๋ฅธ ํ๋ฅญํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ๋ฉด ์ ํ ์คํธ๋๊ณ , ๊ด์ฉ์ ์ด๋ฉฐ, ๊ณ ๋๋ก ์ต์ ํ๋ ๊ตฌํ์ ์ ๊ณตํ์ฌ ์์ฉ๊ตฌ ์ฝ๋๋ฅผ ์ค์ด๊ณ ๊ฐ๋ ฅํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์น ์ค์๋ฅผ ๋ณด์ฅํ์ฌ ๊ฐ๋ฐ ์๊ฐ๊ณผ ๋ ธ๋ ฅ์ ์ ์ฝํ ์ ์์ต๋๋ค.
JavaScript ํจํด ๋งค์นญ์ ๋ฏธ๋
JavaScript ์ปค๋ฎค๋ํฐ๋ TC39(JavaScript๋ฅผ ์งํ์ํค๋ ์ฑ
์์ด ์๋ ๊ธฐ์ ์์ํ)๋ฅผ ํตํด ๋ค์ดํฐ๋ธ **ํจํด ๋งค์นญ ์ ์**์ ๋ํด ์ ๊ทน์ ์ผ๋ก ์์
ํ๊ณ ์์ต๋๋ค. ์ด ์ ์์ match ํํ์(๋ฐ ์ ์ฌ์ ์ผ๋ก ๋ค๋ฅธ ํจํด ๋งค์นญ ๊ตฌ๋ฌธ)์ ์ธ์ด ์์ฒด์ ์ง์ ๋์
ํ์ฌ ๊ฐ์ ๋ถํดํ๊ณ ๋ก์ง์ ๋ถ๊ธฐํ๋ ๋ณด๋ค ์ธ์ฒด ๊ณตํ์ ์ด๊ณ ์ ์ธ์ ์ด๋ฉฐ ๊ฐ๋ ฅํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค. ๋ค์ดํฐ๋ธ ๊ตฌํ์ ์ต์ ์ ์ฑ๋ฅ๊ณผ ์ธ์ด ํต์ฌ ๊ธฐ๋ฅ๊ณผ์ ์ํํ ํตํฉ์ ์ ๊ณตํ ๊ฒ์
๋๋ค.
์์ง ๊ฐ๋ฐ ์ค์ธ ์ ์๋ ๊ตฌ๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
const serverResponse = await fetch('/api/user/data');
const userMessage = match serverResponse {
when { status: 200, json: { data: { name, email } } } => `์ฌ์ฉ์ '${name}' (${email}) ๋ฐ์ดํฐ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ก๋๋์์ต๋๋ค.`,
when { status: 404 } => '์ค๋ฅ: ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.',
when { status: s, json: { message: msg } } => `์๋ฒ ์ค๋ฅ (${s}): ${msg}`,
when { status: s } => `์์์น ๋ชปํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ํ: ${s}.`,
when r => `์ฒ๋ฆฌ๋์ง ์์ ๋คํธ์ํฌ ์๋ต: ${r.status}` // ์ต์ข
ํฌ๊ด์ ํจํด
};
console.log(userMessage);
์ด๋ฌํ ๋ค์ดํฐ๋ธ ์ง์์ ํจํด ๋งค์นญ์ JavaScript์ 1๊ธ ์๋ฏผ์ผ๋ก ๊ฒฉ์์์ผ ADT ์ฑํ์ ๋จ์ํํ๊ณ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ํจํด์ ๋์ฑ ์์ฐ์ค๋ฝ๊ณ ๋๋ฆฌ ์ ๊ทผ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค ๊ฒ์
๋๋ค. ์ด๋ ์ฌ์ฉ์ ์ง์ match ์ ํธ๋ฆฌํฐ ๋๋ ๋ณต์กํ switch (true) ํดํน์ ๋ํ ํ์์ฑ์ ํฌ๊ฒ ์ค์ด๊ณ , JavaScript๋ฅผ ๋ณต์กํ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์ธ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฅ๋ ฅ ์ธก๋ฉด์์ ๋ค๋ฅธ ์ต์ ํจ์ํ ์ธ์ด์ ๋ ๊ฐ๊น๊ฒ ๋ง๋ค ๊ฒ์
๋๋ค.
๋ํ **`do expression` ์ ์**๋ ๊ด๋ จ์ด ์์ต๋๋ค. do expression์ ๋ฌธ ๋ธ๋ก์ด ๋จ์ผ ๊ฐ์ผ๋ก ํ๊ฐ๋ ์ ์๋๋ก ํ์ฌ ๋ช
๋ นํ ๋ก์ง์ ํจ์ํ ์ปจํ
์คํธ์ ํตํฉํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค. ํจํด ๋งค์นญ๊ณผ ๊ฒฐํฉ๋๋ฉด ๋จ์ผ ๊ฐ์ ๊ณ์ฐํ๊ณ ๋ฐํํด์ผ ํ๋ ๋ณต์กํ ์กฐ๊ฑด๋ถ ๋ก์ง์ ๋ ๋ง์ ์ ์ฐ์ฑ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
TC39์ ์ง์์ ์ธ ๋ ผ์ ๋ฐ ์ ๊ทน์ ์ธ ๊ฐ๋ฐ์ ๋ช ํํ ๋ฐฉํฅ์ ์ ์ํฉ๋๋ค. JavaScript๋ ๋ฐ์ดํฐ ์กฐ์ ๋ฐ ์ ์ด ํ๋ฆ์ ์ํ ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ธ์ ์ธ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ ๋ฐฉํฅ์ผ๋ก ๊พธ์คํ ๋์๊ฐ๊ณ ์์ต๋๋ค. ์ด๋ฌํ ์งํ๋ ํ๋ก์ ํธ ๊ท๋ชจ๋ ๋๋ฉ์ธ์ ๊ด๊ณ์์ด ๊ฐ๋ฐ์๋ค์ด ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ํํ๋ ฅ์ด ํ๋ถํ๋ฉฐ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
๊ฒฐ๋ก : ํจํด ๋งค์นญ ๋ฐ ADT์ ํ ํ์ฉ
์ํํธ์จ์ด ๊ฐ๋ฐ์ ๊ธ๋ก๋ฒ ํ๊ฒฝ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณต์๋ ฅ์ด ๋ฐ์ด๋๊ณ ํ์ฅ ๊ฐ๋ฅํ๋ฉฐ ๋ค์ํ ํ์์ ์ดํดํ ์ ์์ด์ผ ํ๋ฏ๋ก, ๋ช ํํ๊ณ ๊ฒฌ๊ณ ํ๋ฉฐ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋์ ๋ํ ํ์์ฑ์ด ์ค์ํฉ๋๋ค. ์น ๋ธ๋ผ์ฐ์ ๋ถํฐ ํด๋ผ์ฐ๋ ์๋ฒ๊น์ง ๋ชจ๋ ๊ฒ์ ์ง์ํ๋ ๋ณดํธ์ ์ธ ์ธ์ด์ธ JavaScript๋ ํต์ฌ ๊ธฐ๋ฅ์ ํฅ์์ํค๋ ๊ฐ๋ ฅํ ํจ๋ฌ๋ค์๊ณผ ํจํด์ ์ฑํํจ์ผ๋ก์จ ํฐ ์ด์ ์ ์ป์ต๋๋ค.
ํจํด ๋งค์นญ๊ณผ ๋์์ ๋ฐ์ดํฐ ํ์
์ JavaScript์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ดํ์ ์ฌ์คํ๊ฒ ํฅ์์ํค๋ ์ ๊ตํ์ง๋ง ์ ๊ทผ ๊ฐ๋ฅํ ์ ๊ทผ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค. Option, Result ๋ฐ RemoteData์ ๊ฐ์ ADT๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ํ๋ฅผ ๋ช
์์ ์ผ๋ก ๋ชจ๋ธ๋งํ๊ณ ํจํด ๋งค์นญ์ ์ฌ์ฉํ์ฌ ์ด๋ฌํ ์ํ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํจ์ผ๋ก์จ ๋๋ผ์ด ๊ฐ์ ์ ๋ฌ์ฑํ ์ ์์ต๋๋ค:
- ์ฝ๋ ๋ช ํ์ฑ ํฅ์: ์๋๋ฅผ ๋ช ์์ ์ผ๋ก ๋ง๋ค์ด ์ฝ๋๋ฅผ ๋ณดํธ์ ์ผ๋ก ๋ ์ฝ๊ฒ ์ฝ๊ณ , ์ดํดํ๊ณ , ๋๋ฒ๊น ํ ์ ์๋๋ก ํ์ฌ ๊ตญ์ ํ ๊ฐ์ ํ์ ์ ์ด์งํฉ๋๋ค.
- ๊ฒฌ๊ณ ์ฑ ํฅ์: ํนํ TypeScript์ ๊ฐ๋ ฅํ ์ด์ฒด์ฑ ๊ฒ์ฌ์ ๊ฒฐํฉ๋ ๋
nullํฌ์ธํฐ ์์ธ ๋ฐ ์ฒ๋ฆฌ๋์ง ์์ ์ํ์ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์ค๋ฅ๋ฅผ ๋ํญ ์ค์ ๋๋ค. - ์ ์ง๋ณด์์ฑ ํฅ์: ์ํ ์ฒ๋ฆฌ๋ฅผ ์ค์ ์ง์คํํ๊ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ ์ฌํญ์ด ์ฒ๋ฆฌํ๋ ๋ก์ง์ ์ผ๊ด๋๊ฒ ๋ฐ์๋๋๋ก ๋ณด์ฅํ์ฌ ์ฝ๋ ์งํ๋ฅผ ๋จ์ํํฉ๋๋ค.
- ํจ์ํ ์์์ฑ ์ด์ง: ๋ถ๋ณ ๋ฐ์ดํฐ ๋ฐ ์์ ํจ์ ์ฌ์ฉ์ ์ฅ๋ คํ์ฌ ํต์ฌ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์น๊ณผ ์ผ์น์์ผ ๋ ์์ธก ๊ฐ๋ฅํ๊ณ ํ ์คํธ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ๋ง๋ญ๋๋ค.
๋ค์ดํฐ๋ธ ํจํด ๋งค์นญ์ด ์๋ฐํ์ง๋ง, TypeScript์ ํ๋ณ ์ ๋์ธ๊ณผ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฌํ ํจํด์ ํจ๊ณผ์ ์ผ๋ก ์๋ฎฌ๋ ์ด์ ํ ์ ์๋ค๋ ๊ฒ์ ์ค๋๋ ์๋ ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ด๋ฌํ ๊ฐ๋ ์ ํ๋ก์ ํธ์ ์ง๊ธ ํตํฉํ์ฌ ๋์ฑ ๋ณต์๋ ฅ ์๊ณ ์ฐ์ํ๋ฉฐ ์ ์ธ๊ณ์ ์ผ๋ก ์ดํด ๊ฐ๋ฅํ JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ์ญ์์ค. ํจํด ๋งค์นญ ๋ฐ ADT๊ฐ ์ ๊ณตํ๋ ๋ช ํ์ฑ, ์์ธก ๊ฐ๋ฅ์ฑ ๋ฐ ์์ ์ฑ์ ์์ฉํ๊ณ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ฌ์ ์ ์๋ก์ด ์ฐจ์์ผ๋ก ๋์ด์ฌ๋ฆฌ์ญ์์ค.
๋ชจ๋ ๊ฐ๋ฐ์๋ฅผ ์ํ ์คํ ๊ฐ๋ฅํ ํต์ฐฐ๋ ฅ ๋ฐ ํต์ฌ ์์
- ์ํ๋ฅผ ๋ช ์์ ์ผ๋ก ๋ชจ๋ธ๋ง: ํญ์ ๋์์ ๋ฐ์ดํฐ ํ์ (ADT), ํนํ ํฉ์งํฉ ํ์ (ํ๋ณ ์ ๋์ธ)์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ์ ๋ชจ๋ ๊ฐ๋ฅํ ์ํ๋ฅผ ์ ์ํ์ญ์์ค. ์ด๋ ์ฌ์ฉ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์ํ, API ํธ์ถ ๊ฒฐ๊ณผ ๋๋ ์์์ ์ ํจ์ฑ ๊ฒ์ฌ ์ํ์ผ ์ ์์ต๋๋ค.
- `null`/`undefined` ์ํ ์ ๊ฑฐ:
Optionํ์ (Some๋๋None)์ ์ฑํํ์ฌ ๊ฐ์ ์กด์ฌ ๋๋ ๋ถ์ฌ๋ฅผ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํ์ญ์์ค. ์ด๋ ๋ชจ๋ ๊ฐ๋ฅ์ฑ์ ๊ณ ๋ คํ๋๋ก ๊ฐ์ ํ๊ณ ์๊ธฐ์น ์์ ๋ฐํ์ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํฉ๋๋ค. - ์ค๋ฅ๋ฅผ ์ฐ์ํ๊ณ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌ: ์คํจํ ์ ์๋ ํจ์์ ๋ํด
Resultํ์ (Ok๋๋Err)์ ๊ตฌํํ์ญ์์ค. ์ค๋ฅ๋ฅผ ์์๋๋ ์คํจ ์๋๋ฆฌ์ค์๋ง ์์กดํ๋ ๊ฒ์ด ์๋๋ผ ๋ช ์์ ์ธ ๋ฐํ ๊ฐ์ผ๋ก ์ทจ๊ธํ์ญ์์ค. - ํฅ์๋ ์์ ์ฑ์ ์ํด TypeScript ํ์ฉ: TypeScript์ ํ๋ณ ์ ๋์ธ ๋ฐ ์ด์ฒด์ฑ ๊ฒ์ฌ(์:
assertNeverํจ์ ์ฌ์ฉ)๋ฅผ ํ์ฉํ์ฌ ์ปดํ์ผ ์๊ฐ์ ๋ชจ๋ ADT ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํ์ฌ ๋ฐํ์ ๋ฒ๊ทธ์ ์ ์ฒด ๋ฒ์ฃผ๋ฅผ ๋ฐฉ์งํ์ญ์์ค. - ํจํด ๋งค์นญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์: ํ์ฌ JavaScript/TypeScript ํ๋ก์ ํธ์์ ๋ ๊ฐ๋ ฅํ๊ณ ์ธ์ฒด ๊ณตํ์ ์ธ ํจํด ๋งค์นญ ๊ฒฝํ์ ์ํด
ts-pattern๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ๋ ฅํ๊ฒ ๊ณ ๋ คํ์ญ์์ค. - ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ ์์ธก: JavaScript ์์ฒด ๋ด์์ ์ด๋ฌํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ํจํด์ ๋์ฑ ๊ฐ์ํํ๊ณ ํฅ์์ํฌ ๋ฏธ๋ ๋ค์ดํฐ๋ธ ์ธ์ด ์ง์์ ์ํด TC39 ํจํด ๋งค์นญ ์ ์์ ์ฃผ๋ชฉํ์ญ์์ค.