January 11, 2023
๋๋ ์ด๋ฒ์ ํ๋ก ํธ๋ ๊ฐ์ด ์๊ฐ์ ํ๋๋ฐ ์ ๋ง ์ด์ง๋ฌ์ ๋ค.
๋์ด๋๊ฐ ๋์๊ฒ ์๋๋ผ ์์๋ ํ๋ก ํธ ๋์ฐ๊ณ , ์์๋ ๋ฐฑ์๋ ๋์ฐ๋ ๋ ๊ฐ์ ๋ด์ฉ์ ์ซ๋๊ฑฐ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅํ ์ ๋์๋ค.
๊ทธ๋๋ ๋คํํ ํ๋ก ํธ ์ชฝ์ ์์ด์ค ๋ธ๋ ์ดํน์ ํ๋๋ผ ์ฃผ์ํ ๋ด์ฉ์ ์๋์๊ณ , ๋ฐฑ์๋ ์ชฝ์ ์ฝ๊ฐ ์ง์คํด์ ๋ค์๋ค.
์ฌ๊ธฐ TIL์ ๋ด๊ฐ ๋ค์ผ๋ฉด์ ๋ฐฐ์ฐ๊ฑฐ๋ ํ์ตํ ๋ด์ฉ์ ์ ๋ฆฌํ๋ ์์ฃผ๋ก ์ฌ๋ฆด ์์ ์ด๋ค.
๊ฐ์ธ ํ์ต ๋ด์ฉ์ ์ ๋ฆฌํ ๊ฒ์ด๋ผ ์ฝ๊ฐ ๋ถ์คํ ์๋ ์๋ค.
๊ฐ์ ์๋ฃ๋ ๊ฐ์ฌ๋์ ๊ฒ์ด๋ผ ๊ณต์ ๋ ๋ถ๊ฐ๋ฅํ๊ณ , ํน์ ์ ์๊ถ ์นจํด๋ ์ธ๋ถ ๋
ธ์ถ ๋ถ๊ฐ ์๋ฃ๊ฐ ์๋ค๋ฉด ์๋ ค์ฃผ์๋ฉด ๋ฐ๋ก ์์ ๋ฐ์ ์ฒ๋ฆฌํ๊ฒ ์ต๋๋ค.
์คํ๋ ๊นํ๋ธ์ ๊ฒฝ์ฐ ์น์์ ๋๊ตฌ๋ ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ธฐ์ ๊นํ๋ธ๋ ๊ณต๊ฐํ์์ต๋๋ค.
ํจ์๋ ํ๋์ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์ธ์ ๋ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ด์ผ ํ๋ค.
ํจ์ ๋ด์์ ์ก์
์ผ๋ก ์ธํด ์ฌ์ด๋ ์ดํฉํธ๊ฐ ๋๋ ๊ฒ์ ์ค์ฌ์ผ ํ๋ค. (์ข
์ ์ Side Effect ์ ๊ฑฐ)
๋ถ์ํจ๊ณผ ์์ด ๊ฒฐ๊ณผ๊ฐ์ด ์ธ์์๋ง ์์กดํ๋ ํจ์๋ฅผ ์์ํจ์๋ผ ํ๋ค. ๋ถ์ํจ๊ณผ๋ ํจ์์์ ๊ฒฐ๊ด๊ฐ์ ์ฃผ๋ ๊ฒ ์ธ์ ํ๋ ํ๋. (Side Effect)
//Bad case
const init: number = 0
const add = (number: number) => {
return number + init
}
add(5)
์ ์ฝ๋๋ ์ธ๋ถ์ init์ด๋ผ๋ ๊ฐ์ ์ํด ํจ์ ๋ฐ์ ์๋ ๊ฐ์ ์ํด ํจ์ ๊ฐ์ด ๊ฒฐ์ ๋๊ธฐ์ ์๋ชป๋ ์๋ผ ๋ณผ ์ ์๋ค.
๊ทธ๋ผ ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์์ํจ์ ํํ๋ก ๋ฐ๊พธ๋ ค๋ฉด ์๋์ ๊ฐ์ด ๋ฐ๊ฟ ์ ์๋ค.
//Good case
const init: number = 0
const add = (number: number, init: number = 0) => {
return number + init
}
add(5)
์ธ๋ถ์ ์๋ init์ด๋ผ๋ ๋ณ์๋ฅผ ์ ๋ฌ์ธ์๋ก ๋ฐ๊พธ๊ณ , ํด๋น ๊ฐ์ 0์ผ๋ก ๊ธฐ๋ณธ ๊ฐ์ ์ฃผ๋ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ๊พธ๋ฉด ์์ํจ์ ํํ๋ก ๋ฐ๊ฟ ์ ์๋ค.
๋ ๋ค๋ฅธ ์์ ๋ฅผ ๋ณด์
//Bad Case
const fruits: string[] = ['Apple', 'Orange', 'Blueberry']
const head = (arr: string[]) => {
return arr.shift()
}
console.log(head(fruits))
์ฌ๊ธฐ์ shift ํจ์๋ ์ ๋ฌ๋ ๋ฐฐ์ด์ ์ฒซ ์์๋ฅผ ๋ฐํํ๊ณ , ๋๋จธ์ง๋ ์ ๊ฑฐํ๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ ๋ฌ๋ ๋ฐฐ์ด์ ๊ฒฝ์ฐ ์กฐ์์ด ์ผ์ด๋๊ฒ ๋๋ค.
ํจ์๋ ์ก์
(๋ฌด์ธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์กฐ์,์ญ์ ๋ฑ์ ํ๋ ํ์)์ด ์ผ์ด๋๋ ๊ฒ์ ์ค์ด๊ณ ์์ ์ผ ํ๋ค.
๊ทธ๋ผ ์ด๊ฒ์ ๊ฐ์ ํ๋ค๋ฉด?
//Good Case
const fruits: string[] = ['Apple', 'Orange', 'Blueberry']
const head = (arr: string[]) => {
return arr.length < 1 ? undefined : arr[0]
}
console.log(head(fruits))
์์ ๊ฐ์ด ํ๋ค๋ฉด ์ ๋ฌ๋ ๋ฐฐ์ด์ ์กฐ์ํ์ง ์๊ณ ์์ํ๊ฒ ๊ฐ๋ง ๋๊ธฐ๋ ํํ๋ก ๊ตฌํ์ด ๋๋ค.
๋ ์์ ๊ฐ ์๋ค.
//Bad Case
const fruits: string[] = ['Apple', 'Orange', 'Blueberry']
fruits[2] = 'Tomato'
์ด๋ ๊ฒ ๋์
์ ํ ๊ฒฝ์ฐ ์๋ณธ ๋ฐ์ดํฐ์ ๋ํ ๋ณ๊ฒฝ์ด ์ผ์ด๋๊ฒ ๋๋ค.
ํจ์ํ ํ๋ฌ๋ค์์์๋ ์๋ณธ์ ๋ณต์ฌํด์ ์ฌ์ฉํด์ผ ํ๋ค.
๊ทธ๋์ ์๋์ ๊ฐ์ด ๊ณ ์ฐจํจ์์ธ map์ ์ด์ฉํด ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ค์ด ๋ธ๋ค.
//Good Case
const fruits: string[] = ['Apple', 'Orange', 'Blueberry']
const newFruits = fruits.map((fruit: string) =>
fruit === 'Blueberry' ? 'Tomato' : fruit
)
console.log('newFruites = ', newFruits)
๋์ ๊ฐ๋ ์ค ์๋ฐ๋๋ ๊ฐ๋ ์ ์๋ค.
No Side Effects
Higher Order Function
์ ์ ํด์ ํ Live Coding
๋จผ์ ๊ฐ๋จํ ์์ ๋ถํฐ ์์ํ๋ค.
const arr: number[] = [1, 2, 3, 4, 5]
// 1. ํ์๋ง ๊ฑธ๋ฌ์ฃผ์ธ์
// 2. ๊ฑธ๋ฌ์ง ์์์ ๊ณฑํ๊ธฐ 2๋ฅผ ํด์ฃผ์ธ์
// 3. ๋ชจ๋ ๋ค ๋ํด์ฃผ์ธ์
let sum = 0
for (const el of arr) {
if (el % 2 === 1) {
const newElement = el * 2
sum += newElement
}
}
์ ์ฝ๋๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ ๊ฒ์ด๊ณ , ์๋๋ ๊ณ ์ฐจํจ์๋ฅผ ์ด์ฉํด ๊ตฌํํ ๊ฒ์ด๋ค.
const sum2 = arr
.filter(el => el % 2 === 1)
.map(el => el * 2)
.reduce((prev, curr) => prev + curr)
์ด์ ๊ณ ์ฐจํจ์ ์ค map์ ์ง์ ๊ตฌํํ ์ฝ๋
/**
* map: ๋ฐฐ์ด์ ์ํํ๋ฉด์ func ์ ์ ์ฉํด์ ์๋ก์ด ๊ฒฐ๊ณผ ๊ฐ์ ๋ด์ ๋ฐฐ์ด์ ๋ฆฌํดํ๋ค.
* func: (el) => value
*/
const map = (func, iter) => {
const result = []
for (const el of iter) {
result.push(func(el))
}
return result
}
console.log(map(el => el * 2, arr))
/**
* filter: ๋ฐฐ์ด์ ์ํํ๋ฉด์ func ์ truthy ๊ฐ(์กฐ๊ฑด์ ๋ง๋ ๊ฐ)๋ง ๋ฐฐ์ด์ ๋ด์ ๋ฆฌํดํ๋ค.
* func: (el) => truthy | falsy
*/
const filter = (func, iter) => {
const result = []
for (const el of iter) {
if (func(el)) {
result.push(el)
}
}
return result
}
console.log(filter(el => el % 2 === 1, arr))
/**
* reduce: ๋ฐฐ์ด์ ์ํ๋ฉด์ func ์ ๋ฐ๋ณต ์ ์ฉํด์ ์๋ก์ด ๊ฒฐ๊ณผ ๊ฐ์ ์ป์ด๋ธ๋ค. (์ชผ๊ฐ๋ ํจ์)
* func: (acc, el) => acc
* func: (prev, curr) => acc
*/
const reduce = (func, acc, iter) => {
if (iter === undefined) {
iter = acc[Symbol.iterator]()
acc = iter.next().value
}
for (const el of iter) {
acc = func(acc, el)
}
return acc
}
console.log(reduce((prev, curr) => prev + curr, 0, arr))
console.log(reduce((prev, curr) => prev + curr, arr))
/**
* ํจ์์ ํฉ์ฑ, pipe
* ์ํ ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ๋ฐ์์ ํจ์์ ํ์ดํ๋ผ์ธ์ ํ๊ณ ์ต์ข
๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํดํ๋ค.
*/
const pipe = (iter, ...functions) =>
reduce((prev, func) => func(prev), iter, functions)
const arr = [1, 2, 3, 4, 5]
const sum2 = arr
.filter(el => el % 2 === 1)
.map(el => el * 2)
.reduce((prev, curr) => prev + curr)
console.log(sum2)
pipe(
arr,
arr => filter(el => el % 2 === 1, arr),
arr => map(el => el * 2, arr),
arr => reduce((prev, curr) => prev + curr, arr),
result => console.log(result)
)
์ฐ๋ฆฌ๊ฐ ๊ตฌํ๋ ๊ณ ์ฐจํจ์๋ฅผ ์ธ ๋ .์ผ๋ก ์ฒด์ด๋ ํ๋ฏ์ด ์ฐ๋ ๊ฒ์ ๊ตฌํ
๋ ์ปค๋ง์ ์ ๋ชฐ๋ผ์ ์ด ๋ถ๋ถ์ Javascript.Info ์์ ๋ง์ด ์ฐธ๊ณ ํ๋ค.
์ปค๋ง์ ์ ๋ฌ๋ ํจ์๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ฐ, ๋ค๋ฅธ ํจ์๊ฐ ์ค๋ฉด ๊ฐ์ด ํจ์๋ฅผ ์ฒ๋ฆฌ ๋ฐํํ๋ ํจ์๋ฅผ ์๋ฏธํ๋ค.(ํ๋ฆด ์ ์์ => ํฅํ ํฌ์คํ
์์ )
const curry = func => (a, ...args) =>
args.length > 0 ? func(a, ...args) : (...args) => func(a, ...args)
์ปค๋ง์ ๊ตฌํ์ฒด๋ ์์ ๊ฐ๋ค.
์ด๋ฅผ ํตํด ๊ฐ ๊ณ ์ฐจํจ์๋ฅผ ์ปค๋ง์ผ๋ก ๋ฌถ์ผ๋ฉด ์๋์ ๊ฐ๋ค.
๋จ์ํ๊ฒ ํจ์๋ฅผ ์ปค๋ง ์ ๋ฌ์ธ์๋ก ๋๊ธด๋ค.
const map = curry((func, iter) => {
const result = []
for (const el of iter) {
result.push(func(el))
}
return result
})
const filter = curry((func, iter) => {
const result = []
for (const el of iter) {
if (func(el)) {
result.push(el)
}
}
return result
})
const reduce = curry((func, acc, iter) => {
if (iter === undefined) {
iter = acc[Symbol.iterator]()
acc = iter.next().value
}
for (const el of iter) {
acc = func(acc, el)
}
return acc
})
์ปค๋ง์ ๋ํด ์ ๊น ์ง๊ณ ๋์ด๊ฐ๋ฉดโฆ ์๋๋ ์ปค๋ฆฌ๋ฅผ ์ฐ์ง ์์ ๋ฒ์
const arr = [1, 2, 3, 4, 5]
const noCurAdd = (a, b) => a + b
console.log(noCurAdd(1, 3))
console.log(noCurAdd(1)) //ํฌ์ธํธ 1
console.log(noCurAdd(1)(3)) //์๋ฌ ํฌ์ธํธ
const curAdd = curry((a, b) => a + b)
console.log(curAdd(1, 3))
console.log(curAdd(1)) //ํฌ์ธํธ 2
console.log(curAdd(1)(3))
์ ๊ฒฐ๊ณผ๋ฅผ ์คํํ๋ฉด ์๋ฌ์ ํจ๊ป ์๋์ ๊ฒ์ ๋ง์ดํ ๊ฒ์ด๋ค.
4
NaN
/Users/mhlab/Develop/study/Wanted/PreOnboard_Back/lecture/wanted-pre-onboarding-challenge-BE-task-JAN.2023/lecture-1/3.js:53
console.log(noCurAdd(1)(3)); //์๋ฌ ํฌ์ธํธ
^
TypeError: noCurAdd(...) is not a function
at Object.<anonymous> (/Users/mhlab/Develop/study/Wanted/PreOnboard_Back/lecture/wanted-pre-onboarding-challenge-BE-task-JAN.2023/lecture-1/3.js:53:24)
at Module._compile (node:internal/modules/cjs/loader:1155:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)
at Module.load (node:internal/modules/cjs/loader:1033:32)
at Function.Module._load (node:internal/modules/cjs/loader:868:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:22:47
๋ณด๋ฉด ์๊ฒ ์ง๋ง ์ปค๋ฆฌ ๋ฏธ์ฌ์ฉ์ ๋ ๋ฒ์งธ ํฌ์ธํธ1 ์์ญ์ ๊ฒฝ์ฐ NaN์ด ๋ฐ ๊ฒ์ด๋ค.
๋น์ฐํ ๊ฒ์ด ํจ์๊ฐ ๋ค์ ์ ๋ฌ์ธ์๊ฐ ์์ด ๋์ด ์๊ธฐ์ ์ซ์๊ฐ ์๋์ ํ์ํ๋ NaN์ด ๋จ๋ ๊ฒ์ด๊ณ , ์๋ฌ๊ฐ ๋๋ค.
์๋ง ์ ์ฃผ์์ ์๋ฌ ํฌ์ธํธ์์ ์๋ฌ๊ฐ ๋ ๊ฒ์ด๋ค. ์ด๊ฑด ๋น์ฐํ๊ฒ ์ ๋ฌ์ธ์๋ฅผ ๋ฐ๋ ๋ถ๋ถ์์ noCurAddํจ์์ ์ ๋ฌ์ธ์๋ฅผ ๋ฒ์ด๋ ๋ ๋ค๋ฅธ ๊ฐ์ด ์๋ค.
๊ทธ๋ฌ๋ฉด ์ ์ปค๋ง์ ์ ์ฉํ ๊ฒ์ ๋๋๊ฐ?
์ปค๋ง์ ๊ตฌํ์ฒด๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ค.
์ปค๋ง์ ๊ตฌํ์ฒด์์ ๋ณด๋ฉด ์๋์ ๊ฐ์ด ๋์ด ์๋ค.
func => (a, โฆargs) =>
์ฆ ์ ๋ฌ์ธ์๊ฐ ์คํ๋ ๋ ์ฐ์ฐ์(Spread Operator)๋ฅผ ํตํด ์ฌ๋ ค ์ธ์๋ฅผ ๋ฐ๊ธฐ์ ๋ค์ ์๋ ๊ฐ๋ ๊ฐ์ ์ ๋ฌ์ธ์๋ผ ํ๋จํ ๊ฒ์ด๋ค.
ํ์ง๋ง ํฌ์ธํธ2์ ๊ฒฝ์ฐ ์ธ์๊ฐ ํ๋๊ธฐ์ ์๋์ ๊ฐ์ด ํจ์๋ก ํํ๋์ด ๋ํ๋์ง๋ค.
4
[Function (anonymous)]
4
๊ทธ ์ธ ์ฒซ๋ฒ์งธ๋ ๋ฐ๋ก ์ ์ ์๊ณ , ๋ง์ง๋ง์ ๊ทธ๋ฅ ์ ๋ฌ์ธ์๋ผ ๋ณด๋ฉด ๋๋ค.
console.log(curAdd(1)(3)(5))
์ ์ฝ๋๋ ๋น์ฐํ ์๋ฌ๊ฐ ๋๋ค.
์๋ํ๋ฉด ์ปค๋ฆฌ ์์ ์๋ ํจ์์ ์ธ์๋ฅผ ๋ฒ์ด๋ฌ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ ์ธ์๋ ์ด๊ฒ์ ์์ฉํ ๊ฒ์ ์๊ฐํ๋ ์๊ฐ์ด์๋ค.
์ด๋ฒ ํฌ์คํ
์์๋ ํด๋น ๋ด์ฉ์ ๋ฐ๋ก ์ถ๊ฐํ์ง ์๊ฒ ์ง๋งโฆ
์๊ฐ์ด ๋๋ฉด ์
๋ฐ์ดํธ ํฌ์คํ
์ ํ ์์ ์ด๋ค.
์โฆ๊ฐ์ธ์ ์ผ๋ก ํ์
์ ๋ด๋ ค๋์์ง ์ฝ 2๋
? 3๋
์ ๋ ๋๊ฑฐ ๊ฐ์๋ฐ ๋ค์ ํ๋ฒ ์๋ฐ์
? ํ๋ ์๊ฐ์ด์๋ค.
๋ด๊ฐ ์ต๊ทผ์ ๋ง๋ , ๊ทธ๋ฆฌ๊ณ ๋ง๋ค๊ณ ์๋ ์๋น์ค ์ฝ๋๋ ์ง์ง ์๋ง์ธ๋ฐโฆ
์ด๋ฒ์ ํจ์ํ์ ๋ฐฐ์์ ์ข ๊ฐ์ ํด ๋๊ฐ๋ณด๊ณ ์ถ๋ค.
์ฝ๋์คํ
์ด์ธ ๋ธ๋ก์ฒด์ธ + ์จ๋ณด๋ฉ ๋ฐฑ์๋ + ์จ๋ณด๋ฉ ํ๋ก ํธ์๋ = ๊ณ ํต์ ์ผ์์ผ์ฒด
์ข ๋นก์ผ๋ฐโฆํด๋ณด์.
ํ๋ค์ด์ผ ์ฑ์ฅํ๋๋ผ(?)