์ํ๊ณผ ์ปดํจํฐ ๊ณผํ์์ ์ปค๋ง์ด๋ ๋ค์ค ์ธ์ ์ ๊ฐ๋ ํจ์๋ฅผ ๋จ์ผ ์ธ์๋ฅผ ๊ฐ๋ ํจ์๋ค์ ํจ์์ด๋ก ๋ฐ๊พธ๋ ๊ฒ์ ๋งํ๋ค. ๋ชจ์ง์ฆ ์คํํด์ ์ํด ๋์ ๋์๊ณ , ์ดํ ํ์ค์ผ ์ปค๋ฆฌ์ ์ํด ๋ฐ์ ํ์๋ค. ์๋ฅผ ๋ค์ด, ์ธ ๊ฐ์ ์ธ์๋ฅผ ๊ฐ์ง๋ ํจ์๋ฅผ ์ปค๋งํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ธ ๊ฐ์ ํจ์๊ฐ ๋ง๋ค์ด์ง๋ค.
์ปค๋ง์ ํํ(tuple)์์ ์ฌ๋ฌ ๊ฐ์ ์ธ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ํจ์๋ฅผ, ๋จ ํ๋์ ์ธ์๋ง ๋ฐ๊ณ ๋๋จธ์ง ํํ์์ ์๋ ํจ์๊ฐ ๋ฐ์ ์ถ๊ฐ ์ธ์๋ฅผ ํ๋์ฉ ๋ฐ๋ ๋ค๋ฅธ ํจ์๋ฅผ ๋ฐํํ๋ ํจ์๋ก ๋ณํํ๋ ํ๋ก์ธ์ค์ ๋๋ค.
// ์ปค๋ง ๋ณํ์ ํ๋ curry(f) ํจ์ (์ผ๋ฐํจ์ ver1)
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// ์ปค๋ง ๋ณํ์ ํ๋ curry(f) ํจ์ (ํ์ดํํจ์ ver)
const curry = f => a => b => f(a, b);
// f์ ์ ๋ฌ๋ ํจ์
const sum = (a, b) => a + b;
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)); // 3
// ์ปค๋ง ๋ณํ์ ํ๋ curry(f) ํจ์ (์ผ๋ฐํจ์ ver2)
function curryLog(date) {
return function(level) {
return function(message) {
console.log(`[${date.toLocaleTimeString()}] [${level}] ${message}`);
};
};
}
const now = new Date();
const logNow = curryLog(now);
const logError = logNow('ERROR');
const logWarning = logNow('WARNING');
logError('์๋ฒ ์ฐ๊ฒฐ ์คํจ');
logWarning('๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋์');
์์ 1์ ํจ์๋ ๋ค์์ ์์๋ก ๋์ ๋ฉ๋๋ค.
์ปค๋ง์ ์ผ๋ถ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฏธ๋ฆฌ ์ค์ ํ์ฌ ํจ์์ ๋ณํ์ ๋ง๋ค๊ณ ์ถ์ ๋ ํนํ ์ ์ฉํฉ๋๋ค.
const todos = [
{ id: 3, content: 'HTML', completed: false },
{ id: 2, content: 'CSS', completed: true },
{ id: 1, content: 'Javascript', completed: false }
];
const getTodosIdArr = todos => todos.map(todo => todo.id);
const getTodosContentArr = todos => todos.map(todo => todo.content);
const getTodosCompletedArr = todos => todos.map(todo => todo.completed);
console.log(getTodosIdArr(todos)); // [ 3, 2, 1 ]
console.log(getTodosContentArr(todos)); // [ 'HTML', 'CSS', 'Javascript' ]
console.log(getTodosCompletedArr(todos)); // [ false, true, false ]
์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ์์ ๊ฐ์ด ์์ฑํ๊ฒ ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ์ปค๋ง์ ์ ์ฉํ๋ฉด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
const todos = [
{ id: 3, content: 'HTML', completed: false },
{ id: 2, content: 'CSS', completed: true },
{ id: 1, content: 'Javascript', completed: false }
];
const get = property => object => object[property];
const getId = get('id');
const getContent = get('content');
const getCompleted = get('completed');
const getTodosIdArr = todos => todos.map(getId);
const getTodosContentArr = todos => todos.map(getContent);
const getTodosCompletedArr = todos => todos.map(getCompleted);
console.log(getTodosIdArr(todos)); // [ 3, 2, 1 ]
console.log(getTodosContentArr(todos)); // [ 'HTML', 'CSS', 'Javascript' ]
console.log(getTodosCompletedArr(todos)); // [ false, true, false ]
์ปค๋ง์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ธ์์ ์์๋ ์ค์ํ๋ฐ, ์์ ์กด์ฌํ๋ ์ธ์์ผ ์๋ก ๋ณ๋๊ฐ๋ฅ์ฑ์ด ์ ๊ณ ๋ค์ ์๋ ์ธ์์ผ ์๋ก ๋ณ๋๊ฐ๋ฅ์ฑ์ด ๋๊ธฐ ๋๋ฌธ์ ์ด ์์๋ฅผ ๊ณ ๋ คํ์ฌ ์ฝ๋๋ฅผ ์ค๊ณํ๋ ๊ฒ์ด ์ค์ํ๋ค.
์ ๋ณด๋ฅผ ํ์ํํ๊ณ ์ถ๋ ฅํ๋ ๋ก๊ทธํจ์๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๊ณ , ์ค์ ํ๋ก์ ํธ์์ ์ด๋ฌํ ํจ์๋ ๋คํธ์ํฌ๋ฅผ ํตํด ๋ก๊ทธ๋ฅผ ๋ณด๋ด๋ ๊ฒ๊ณผ ๊ฐ์ ๋ง์ ์ ์ฉํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
function log(date, importance, message) {
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
lodash๋ฅผ ์ด์ฉํ ์ปค๋ง
log = _.curry(log);
log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
์ด ๊ฒฝ์ฐ ๋๋ค ์ ์์ ์ผ๋ก ์๋์ด ๋ฉ๋๋ค.
์ด ์ดํ ์๋์ฒ๋ผ ํ์ฌ ์๊ฐ์ผ๋ก ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋๋ฐ ํธ๋ฆฌํ๋๋ก logํจ์๋ฅผ ์์ฑํด์ ์ฌ์ฉํ ์ ์์ต๋๋ค
// logNow๋ log์ ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ ๊ณ ์ ๋ partial
const logNow = log(new Date());
logNow("INFO", "message"); // [HH:mm] INFO message
const debugNow = logNow("DEBUG");
debugNow("message"); // [HH:mm] DEBUG message
์ต์ข
์ ์ผ๋ก ์ปค๋งํ ํ์ ์์ ๊ฒ์ ์์ผ๋ฉฐ, log๋ ์์ง ๋ณดํต๋์ฒ๋ผ ํธ์ถํ ์ ์์ต๋๋ค. ๋ํ์ฌ partialํจ์๋ฅผ ์ฝ๊ฒ ์์ฑํ์ฌ ๊ณ ์ ๊ฐ๋ค์ ๊ณ ์ ์์ผ ์ํ๋ ๊ฐ๋ง ๋์ถ๋๊ฒํ์ฌ ๋๋ฒ๊น
ํ๋๋ฐ ํธ๋ฆฌํ๊ฒ ํด์ค๋๋ค.
lodash๋ฅผ ์ฌ์ฉํ๋ฉด ์ข ๋ ๊ฐ๋จํ๊ฒ ์ปค๋ง์ ๊ตฌํํ ์ ์์ต๋๋ค.
_.curry(func, [arity=func.length])
func (Function) โ The function to curry.
[arity=func.length] (number) โ The arity of func.
(Function) โ Returns the new curried function.
const _ = require('lodash');
const getArray = function(a, b, c) {
return [a, b, c];
};
const curried = _.curry(getArray);
console.log(curried(1)(2)(3));
console.log(curried(1, 2)(3));
console.log(curried(1, 2, 3));
lodash ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ curryํจ์๋ฅผ ์ฌ์ฉ์๊ฐ ์ง์ ์ ์ํ ํ์ ์์ด, _.curry๋ฅผ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌํ์ฌ curriedSum์ ์ ์ํ์์ต๋๋ค.
1) ๋ฌธ์์ด ์ฒ๋ฆฌ ํจ์
const _ = require('lodash');
function concatenateStrings(a, b, c) {
return `${a} ${b} ${c}`;
}
const curriedConcat = _.curry(concatenateStrings);
// ๋ถ๋ถ ์ ์ฉ ์์
const greet = curriedConcat('์๋
ํ์ธ์');
const greetTo = greet('๊น์ฒ ์๋');
console.log(greetTo('๋ฐ๊ฐ์ต๋๋ค!')); // "์๋
ํ์ธ์ ๊น์ฒ ์๋ ๋ฐ๊ฐ์ต๋๋ค!"
2) API ์์ฒญ ํจ์
const _ = require('lodash');
const axios = require('axios');
function fetchAPI(baseURL, endpoint, params) {
return axios.get(`${baseURL}/${endpoint}`, { params });
}
const curriedFetch = _.curry(fetchAPI);
// API ๊ธฐ๋ณธ ์ค์
const fetchMyAPI = curriedFetch('https://api.example.com');
// ํน์ ์๋ํฌ์ธํธ์ ๋ํ ํจ์ ์์ฑ
const getUser = fetchMyAPI('users');
const getProduct = fetchMyAPI('products');
// ์ฌ์ฉ ์
getUser({ id: 123 })
.then(response => console.log(response.data))
.catch(error => console.error(error));
getProduct({ category: 'electronics' })
.then(response => console.log(response.data))
.catch(error => console.error(error));
3) ์ธ์ ์์ ๋ณ๊ฒฝ (placeholder ์ฌ์ฉ)
const _ = require('lodash');
function divide(a, b) {
return a / b;
}
const curriedDivide = _.curry(divide);
// ์ผ๋ฐ์ ์ธ ์ฌ์ฉ
const divideBy10 = curriedDivide(_, 10); // ์ฒซ ๋ฒ์งธ ์ธ์ ๋์ค์ ๋ฐ์
console.log(divideBy10(100)); // 10 (100 / 10)
4) ๊ฐ์ฒด ๋ณํ ํจ์
const _ = require('lodash');
function createUser(role, name, age) {
return {
role,
name,
age,
id: Math.random().toString(36).substr(2, 9)
};
}
const curriedCreateUser = _.curry(createUser);
// ๊ด๋ฆฌ์ ์์ฑ ํจ์
const createAdmin = curriedCreateUser('admin');
// ํน์ ์ฐ๋ น๋ ๊ด๋ฆฌ์ ์์ฑ
const createAdminIn30s = createAdmin(_, 35);
console.log(createAdminIn30s('๊น๊ด๋ฆฌ์'));
/*
{
role: 'admin',
name: '๊น๊ด๋ฆฌ์',
age: 35,
id: '4f6g7h8j9'
}
*/
5) ๋ฉ๋ชจ์ด์ ์ด์ ๊ณผ ํจ๊ป ์ฌ์ฉ
const _ = require('lodash');
function expensiveCalculation(a, b, c) {
console.log('๊ณ์ฐ ์ํ ์ค...');
// ๋ณต์กํ ๊ณ์ฐ ๊ฐ์
return a * b * c;
}
const curriedCalculation = _.curry(expensiveCalculation);
const memoizedCalculation = _.memoize(curriedCalculation);
// ์ฒซ ํธ์ถ - ๊ณ์ฐ ์ํ
console.log(memoizedCalculation(2)(3)(4)); // 24, "๊ณ์ฐ ์ํ ์ค..." ์ถ๋ ฅ
// ๋์ผ ์ธ์๋ก ์ฌํธ์ถ - ์บ์๋ ๊ฒฐ๊ณผ ๋ฐํ
console.log(memoizedCalculation(2)(3)(4)); // 24, ์ถ๋ ฅ ์์
6) ํจ์ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ
const _ = require('lodash');
// ๋ณํ ํจ์๋ค
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
function square(n) { return n * n; }
// ์ปค๋ง ์ ์ฉ
const curriedAdd = _.curry(add);
const curriedMultiply = _.curry(multiply);
// ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ
const calculate = _.flow([
curriedAdd(10), // ์ฒซ ๋ฒ์งธ ์ธ์์ 10์ ๋ํจ
curriedMultiply(5), // ๊ฒฐ๊ณผ์ 5๋ฅผ ๊ณฑํจ
square // ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณฑํจ
]);
console.log(calculate(5)); // ((5 + 10) * 5)^2 = 5625
7) ๊ฒ์ ํํฐ๋ง
const _ = require('lodash');
// ์ ํ ๋ฐ์ดํฐ
const products = [
{ id: 1, name: '๋
ธํธ๋ถ', category: '์ ์์ ํ', price: 1200000 },
{ id: 2, name: '์ค๋งํธํฐ', category: '์ ์์ ํ', price: 800000 },
{ id: 3, name: '์์', category: '๊ฐ๊ตฌ', price: 150000 },
{ id: 4, name: '์ฑ
์', category: '๊ฐ๊ตฌ', price: 200000 }
];
// ํํฐ ํจ์
function filterByCategory(category, items) {
return items.filter(item => item.category === category);
}
function filterByMaxPrice(maxPrice, items) {
return items.filter(item => item.price <= maxPrice);
}
// Lodash ์ปค๋ง ์ ์ฉ
const curriedFilterByCategory = _.curry(filterByCategory);
const curriedFilterByPrice = _.curry(filterByMaxPrice);
// ์นดํ
๊ณ ๋ฆฌ ํํฐ ์์ฑ
const filterElectronics = curriedFilterByCategory('์ ์์ ํ');
const filterFurniture = curriedFilterByCategory('๊ฐ๊ตฌ');
// ๊ฐ๊ฒฉ ํํฐ ์์ฑ
const filterUnder1Million = curriedFilterByPrice(1000000);
// ํํฐ ์กฐํฉ
const getAffordableElectronics = _.flow([
filterElectronics,
filterUnder1Million
]);
console.log(getAffordableElectronics(products));
/*
[
{ id: 2, name: '์ค๋งํธํฐ', category: '์ ์์ ํ', price: 800000 }
]
*/
Lodash์ _.curry ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด:
์ปค๋ง์ ํนํ ๋ณต์กํ ํจ์ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ๊ฑฐ๋ ๊ณ ์ฐจ ํจ์๋ฅผ ๋ง๋ค ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋๋ถ๋ถ์ ๊ธฐ๋ณธ ์ฐ์ฐ์์ ๋ค์ดํฐ๋ธ JavaScript๊ฐ ๋ ๋น ๋ฅธ ์ฑ๋ฅ์ ๋ณด์
๋จ, .uniq(), .throttle() ๋ฑ ์ผ๋ถ ํจ์๋ ์ฌ์ ํ Lodash๊ฐ ์ฐ์ํ ๊ฒฝ์ฐ ์์59
ํธ๋ฆฌ ์์ดํน์ด ์๋ฒฝํ์ง ์์ ์ฌ์ฉํ์ง ์๋ ํจ์๋ ๋ฒ๋ค์ ํฌํจ๋ ์ ์์6
ํนํ ํ๋ก ํธ์๋ ํ๊ฒฝ์์ ๋ฒ๋ค ํฌ๊ธฐ ๋ฏผ๊ฐํ ๋ ๋ฌธ์ ๋จ
๊ธฐ์กด์ Lodash์ ์์กดํ๋ ๋ํ ํ๋ก์ ํธ๋ค์ ๋ง์ด๊ทธ๋ ์ด์ ๋น์ฉ ๋๋ฌธ์ ๊ณ์ ์ฌ์ฉ
์ปค๋ง์ ์ฌ์ฉํ์ง ์์ ์ผ๋ฐ ํจ์๊ฐ ๋ ์ง๊ด์ ์ด๊ณ ๊ฐ๋จํ ๊ฒฝ์ฐ๊ฐ ๋ง์ง๋ง, ์ปค๋ง์ ํจ์์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์กฐํฉ์ฑ์ ๋์ฌ์ฃผ๋ ์ฅ์ ์ด ์์ต๋๋ค.
Lodash๋ ์ ์ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ "์ ํ์ " ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ณํ ์ค์ ๋๋ค. 2025๋ ํ์ฌ๋ ์ ์ฒด ๋ฒ๋ค์ ์ค์นํ๊ธฐ๋ณด๋ค๋ ํ์ํ ๊ธฐ๋ฅ๋ง ์ ํ์ ์ผ๋ก ์ฌ์ฉํ๊ฑฐ๋, ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ์ผ๋ก ๋์ฒดํ๋ ์ถ์ธ์ ๋๋ค. ํนํ ์ ๊ท ํ๋ก์ ํธ์์๋ ์ต์ JavaScript ๊ธฐ๋ฅ์ ์ฐ์ ์ ์ผ๋ก ๊ณ ๋ คํ ํ, ๋ถ์กฑํ ๋ถ๋ถ๋ง Lodash๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ณด์ํ๋ ์ ๊ทผ์ด ๊ถ์ฅ๋ฉ๋๋ค.
"๊ณผ๊ฑฐ์๋ Lodash๊ฐ ํ์์์ง๋ง, ์ด์ ๋ ์ ํ์ ๋ฌธ์ ๊ฐ ๋์์ต๋๋ค. ํ๋ก์ ํธ ์๊ตฌ์ฌํญ๊ณผ ํ์ ์๋ จ๋๋ฅผ ๊ณ ๋ คํด ํ๋ช ํ๊ฒ ์ ํํด์ผ ํฉ๋๋ค."
https://ko.javascript.info/currying-partials
https://wiki.haskell.org/index.php?title=Currying
https://velog.io/@hustle-dev/Javascript-%EC%BB%A4%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90
https://www.tutorialspoint.com/lodash/lodash_curry.htm
https://if1live.github.io/posts/escape-from-lodash-just/