고차함수 (Highter-Order Function )와 커링(Currying) 기법이란?
이 두가지 개념은 리액트에서 정말 많이 사용되는 개념입니다.
우선 고차 함수는 함수를 인자로 전달받거나 리턴값으로 함수를 반환하는 함수를 의미합니다.
커링 기법은 인자가 여러개인 함수의 일부 인자를 고정시키는 새로운 함수를 만드는 기법을 의미합니다.
인자가 n개인 함수를 n개로 분리하여 사용하게끔 만드는 기법입니다.
일단 이정도로만 알고, 자세한 내용은 천천히 설명 하겠습니다.
앞서 말씀드렸듯이 고차 함수는 함수를 인자로 전달받거나 리턴값으로 함수를 반환하는 함수를 의미합니다.
고차 함수는 리액트에서 고차 컴포넌트 (High-Order Component)의 형태로 자주 사용되며 , 반복되는 특정 로직을 쉽게 재사용하기 위해 사용합니다.
const sayHello = function () { #1
console.log("Hello");
};
const func1 = function (func) { #2
function innerFunc(word) {
func(); #3
console.log(word);#4
}
return innerFunc; #5
};
const worldHello = func1(sayHello); #6
worldHello("world"); #7
// Hello
// world 출력
고차 함수의 특징을 모두 보여주는 간단한 예제 입니다.
#1. 우선 고차 함수의 인자로 선언할 함수를 하나 선언했습니다. 이 함수는 단순히 console.log를 통해 Hello 를 출력합니다.
#2. 고차 함수 func1 을 선언했습니다. 인자로 func 라는 함수를 받고 내부에 함수를 하나 가지고 있습니다. 그리고 그 내부 함수를 리턴 합니다.
#3. func1 의 내부 함수 innerFunc 에서 인자로 받았던 func 함수를 실행 합니다.
#4. 그 후 console.log 를 통해 innerFunc 인자로 받은 word 를 출력 합니다 . 이 과정읋 통해 처음으로 인자로 받은 func 함수에 새로운 로직을 추가해 주었습니다.
#5. innerFunc 를 리턴함으로써 고차 함수를 사용할 수 있게 만듭니다. 이 innerFunc 는 func 를 클로저로 가지게 됩니다.
#6. worldHello 라는 상수에 함수를 할당 합니다. 그 값은 func1 에 인자로 sayHello 를 전달한 결과입니다.
#7. wolrdHello 에 인자로 world 라는 문자열을 전달합니다. 이 문자열은 아까 보았던 innerFunc 의 word 인자로 들어가게 됩니다.
.
const func1 = (func) => (word) => {
func();
console.log(`${word}`);
};
위에서 본 func1 의 화살표 함수 버전입니다. 동작은 아까와 같으나 훨씬 가독성이 좋은것 같네요
위에서 잠시 말씀 드렸듯이 커링(Currying) 기법은 인자가 여러개인 함수의 일부 인자를 고정시키는 새로운 함수를 만드는 기법입니다.
도대체 무슨 말 인지 이해가 안되실테니 코드로 살펴봅시다.
function helloFunc(word,name) {
console.log(`${word},${name}`);
}
word 와 name 이라는 두개의 인자를 받아서 출력해주는 단순한 형태의 함수입니다.
이 함수에 커링을 적용해 봅시다.
function helloFunc(word) {
return function (name) {
console.log(`${word}, ${name}`);
};
}
const printHello = helloFunc("hello");
printHello("Tibetan Fox"); // hello, Tibetan Fox
printHello("Tiger"); // hello, Tiger
아까의 함수에 커링을 적용하면 이렇게 됩니다 .
n(2) 개의 인자를 받던 함수가 n(2)개로 쪼개진 것을 볼 수 있습니다 .
또한 첫 번째로 받던 인자인 word 를 hello 라는 값으로 고정하고 name 만 변경하면서 사용가능한 것 또한 볼 수 있습니다.
즉 커링 기법은 일부 인자에 같은 값을 반복적으로 사용할 때 그 반복되는 인자를 고정함으로 써 중복을 최소화 하기에 적합한 기법입니다.
function helloFunc(word, name, word2, name2) {
console.log(`${word}, ${name} || ${word2}, ${name2}`);
}
좀더 어려운 예제를 살펴봅시다 .
위 함수는 인자가 무려 4개나 됩니다. 여기에 커링을 적용한다면 ?
function helloFunc(word){
return function(name){
return function(word2){
return function(name2){
console.log(`${word}, ${name}, || ${word2},${name2}`)
}
}
}
}
정말 변태같은 모습으로 변했습니다 . 커링 기법 자체가 부분 부분 나눈 함수를 체인으로 생성하여 사용하게 끔 하는 방식이다 보니 어쩔수 없이 이런 변태 같은 모습이 되어버린 것 같습니다.
const printHello = helloFunc("hello")("Tibetan Fox")("Good morning");
printHello("Tiger"); // hello, Tibetan Fox || Good morning, Tiger 출력
printHello("Ant"); // hello , Tibetan Fox || Good morning, Ant 출력
다행히 사용법은 어렵지 않습니다 . 위와같이 체이닝을 통해 3개의 인자를 고정하고 마지막 인자만 유동적으로 사용할 수 있습니다 ..
const printHello = helloFunc("hello")("Tibetan Fox")("Good morning")("Tiger") ; // 직접 호출
const printHello = helloFunc("hello")("Tibetan Fox") ; // 두개의 인자만 고정
printHello("Good morning")("Tiger") ;
printHello ("Good evening")("Crow") ;
물론 이렇게 직접 커링 함수를 호출하거나 인자를 증감할수도 있습니다 .
const helloFunc = (word) => (name) => (word2) => (name2) => {
console.log(`${word}, ${name} || ${word2}, ${name2}`);
참고로 위에서 잠시 봤던 화살표 함수를 사용하면 좀 덜 변태같은 모습으로 만들어 줄 수 있습니다 .
주의할 점
커링 기법을 적용할 때는 인자의 순서가 매우 중요합니다. 변동 가능성이 적은 인자는 앞에, 변동 가능성이 높은 인자는 뒤에 배치해야 합니다. 반드시 이 점을 고려하면서 커링을 사용해야 합니다.