๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐ŸŒˆ Chapter 7: ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜

๐Ÿ“š ๋‹จ์œ„ ํ…Œ์ŠคํŠธโ€‹

๊ณง ์—ด๋ฆด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ˜ํผ๋Ÿฐ์Šค ํ–‰์‚ฌ์žฅ์€ ์ˆ˜์ฒœ ๋ช…์˜ ๊ตถ์ฃผ๋ฆฐ ๊ฐœ๋ฐœ์ž๋กœ ๊ฐ€๋“ ์ฐฐ ์ „๋ง์ด๋‹ค. ์ฝ˜ํผ๋Ÿฐ์Šค๊ฐ€ ์—ด๋ฆฌ๋ฉด ํ–‰์‚ฌ์žฅ์€ ๋ถ์ƒˆํ†ต์„ ์ด๋ฃฐ ํ…Œ๊ณ  ์ฐธ๊ฐ€์ž๋“ค์€ ์‹์‚ฌ ๊ฑฐ๋ฆด ์ฐพ์•„๋‹ค๋‹ ์‹œ๊ฐ„์กฐ์ฐจ ์•„๊นŒ์šฐ๋‹ˆ ๊ฝค ์ค‘์š”ํ•œ ๋ฌธ์ œ๋‹ค.

์Šคํ˜„์€ ์šฐ์„  ํ–‰์‚ฌ์žฅ ๊ทผ์ฒ˜์— ์žˆ๋Š” ์Œ์‹์  ์œ„์น˜๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์„œ๋“œํŒŒํ‹ฐ ์›น ์„œ๋น„๋ฅด๋ฅผ ์ฐพ์•„๋ณด๊ณ , ์ƒฌ๋Ÿฟ์€ ์ด ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•ด์„œ UI๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ์—…๋ฌด๋ฅผ ๋ถ„๋‹ดํ–ˆ๋‹ค. ์Šนํ˜„์€ ์„œ๋“œํŒŒํ‹ฐ API ์ฝ”๋“œ๋ฅผ ๋ฐ”๋กœ ์ฐพ์•˜๋‹ค.

var ThirdParty = ThirdParty || {};
ThirdParty.restaurantApi = function() {
'use strict';

return {
// ์ฃผ์–ด์ง„ ์ฃผ์†Œ(address) ๊ธฐ์ค€ ๋ฐ˜๊ฒฝ radiusMiles ๋งˆ์ผ ์ด๋‚ด์— ์œ„์น˜ํ•œ
// ์›ํ•˜๋Š” ์š”๋ฆฌ(cuisine)๋ฅผ ๋จน์„ ์ˆ˜ ์žˆ๋Š” ์Œ์‹์  ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ”„๋ผ๋ฏธ์Šค๋ฅผ ๋ฐ˜ํ™˜
getRestaurantsWithinRadius: function(address, radiusMiles, cuisine) {
// ํ”„๋ผ๋ฏธ์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ์ฒด์˜ ๋ฐฐ์—ด๋กœ ๊ท€๊ฒฐ๋œ๋‹ค.
// {
// name: '๋Œ€์„ฑ๊ฐ',
// address: '์šธ์‚ฐ ๋‚จ๊ตฌ ์‹ ์ •๋กœ 20๋ฒˆ๊ธธ 988',
// }
}
};
};

๊ดœ์ฐฎ์€ API์ธ ๊ฒƒ ๊ฐ™์€๋ฐ, ํ•„์š” ์ด์ƒ ์žก๋‹คํ•œ ๋ถ€๋ถ„๋„ ์žˆ๋‹ค. ๋จผ์ €, address ์ธ์ž๋Š” ์ฝ˜ํผ๋Ÿฐ์Šค ํ–‰์‚ฌ์žฅ ์ฃผ์†Œ๋ผ์„œ ๊ฐ’์ด ๋ฐ”๋€” ์ผ์ด ์—†๊ณ , ์š”๊ฑด์ƒ ๊ทผ์ฒ˜ ์Œ์‹์ ์€ ํ–‰์‚ฌ์žฅ ๊ธฐ์ค€ 3km ์ด๋‚ด์— ์žˆ๋Š” ์Œ์‹์ ์ด๋ฏ€๋กœ radiusMiles ํŒŒ๋ผ๋ฏธ์Šคํ„ฐ๊ฐ’ ๋˜ํ•œ ์ผ์ •ํ•˜๋‹ค.

์Šนํ˜„์€ getRestaurantsNearConference(cuisine) ํ•จ์ˆ˜๋กœ API๋ฅผ ํ™•์žฅํ•˜๊ธฐ๋กœ ํ•œ๋‹ค. address์™€ radius ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ณ ์ •ํ•œ ์ฑ„ getRestaurantsNearConference(cuisine)๊ฐ€ getRestaurantsWithinRadius(address, radius, cuisine) ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฌด์กฐ๊ฑด ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•˜๋Š” ๊ฑฐ๋‹ค. ์ผ๋‹จ ์ด๋ ‡๊ฒŒ ํ•ด์„œ ๋Œ€๋žต ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ๊ทธ๋ ค๋ณธ๋‹ค.

describe('ThirdParty.restaurantApi() ์• ์ŠคํŒฉํŠธ', () => {
var api = ThirdParty.restaurantApi();

describe('getRestaurantsNearConference(cuisine)', () => {
var returnFromUnderlyingFunction = '์•„๋ฌด๊ฑฐ';
var cuisine = '์ค‘ํ™”์š”๋ฆฌ';

beforeEach(() => {
spyOn(api, 'getRestaurantsWithinRadius')
.and.returnValue(returnFromUnderlyingFunction);
});

it('์˜ฌ๋ฐ”๋ฅธ ์ธ์ž๋กœ getRestaurantsWithinRadius๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค', () => {
api.getRestaurantsNearConference(cuisine);
expect(api.getRestaurantsWithinRadius).toHaveBeenCalledWith(
'์šธ์‚ฐ ๋‚จ๊ตฌ ์‹ ์ •๋กœ 20๋ฒˆ๊ธธ 988', 2.0, cuisine,
);
});

it('getRestaurantsWithinRadius๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค', () => {
var ret = api.getRestaurantsNearConference(cuisine);
expect(ret).toBe(returnFromUnderlyingFunction);
});
});
});

๐Ÿ“š ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜ ๋งŒ๋“ค๊ธฐโ€‹

๋ณธํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ์„œ ์ผ๋ถ€ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๊ณ ์ •ํ•˜๋Š”, ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ API์— ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค.

function getRestaurantsNearConference(cuisine) {
return api.getRestaurantsWithinRadius(
'์šธ์‚ฐ ๋‚จ๊ตฌ ์‹ ์ •๋กœ 20๋ฒˆ๊ธธ 988', 2.0, cuisine,
);
}

์Šนํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋”ฉํ•œ๋‹ค.

// ThirdParty.restaurantApi()์— getRestaurantsNearConference ๋ฉค๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
Aop.around(
// ๋ฐ˜ํ™˜๊ฐ’์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ํ•จ์ˆ˜
'restaurantApi',

// ๋ฐ˜ํ™˜๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ํ•จ์ˆ˜
function addGetRestaurantNearConference(targetInfo) {
'use strict';

// ThirdParty.restaurantApi()๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ์›๋ณธ API
var api = Aop.next.call(this, targetInfo);

// API์— ์ถ”๊ฐ€ํ•  ํ•จ์ˆ˜
function getRestaurantsNearConference(cuisine) {
return api.getRestaurantsWithinRadius(
'์šธ์‚ฐ ๋‚จ๊ตฌ ์‹ ์ •๋กœ 20๋ฒˆ๊ธธ 988', 2.0, cuisine,
);
}

// ์—†์œผ๋ฉด ์ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
api.getRestaurantsNearConference =
api.getRestaurantsNearConference || getRestaurantsNearConference;

// ์ˆ˜์ •ํ•œ API๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
return api;
},

// ๋ฐ˜ํ™˜๊ฐ’์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ํ•จ์ˆ˜์˜ ์ด๋ฆ„๊ณต๊ฐ„
ThirdParty
);

๐Ÿ“š ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜์™€ ์ปค๋ง ๊ตฌ๋ณ„ํ•˜๊ธฐโ€‹

๐ŸŽˆ ์ปค๋งโ€‹

์ปค๋ง์€ ์ธ์ž๋ฅผ ์—ฌ๋Ÿฟ ์ทจํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž ํ•˜๋‚˜๋งŒ ๋ฐ›์€ ํ•จ์ˆ˜ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ํ•ด์ฒดํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค. ์ฆ‰, ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ getRestaurantsWithinRadius(address, radius, cuisine)์„ getRestaurantsCurried(address)(radius)(cuisine)๊ณผ ๊ฐ™์ด ์“ฐ๋Š” ๊ฑธ ๋งํ•œ๋‹ค.

์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ๋ถ€ getRestaurantsCurried(address) ์‹คํ–‰์ด ๋๋‚˜๋ฉด radius๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜, ์ฆ‰ cuisine์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ฐ€์žฅ ๊นŠ์€ ์ค‘์ฒฉ ๋‹จ๊ณ„์˜ ํ•จ์ˆ˜๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ต์„ ๋‚ด์–ด์ฃผ๋Š” ๊ตฌ์กฐ๋‹ค.

function getRestaurantsCurried(address) {
var self = this;
return function(radius) {
return function(cuisine) {
return self.getRestaurantsWithinRadius(address, radius, cuisine);
}
}
}

ํ•˜์Šค์ผˆ(Haskell)๊ณผ ML์ฒ˜๋Ÿผ ์ปค๋ง ํ•จ์ˆ˜๊ฐ€ ์ผ์ƒ์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ๋ชจ๋“  ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜์˜ ์ธ์ž๋งŒ ๋ฐ›๋Š”๋‹ค. ๊ทธ๋ฐ–์— ๋žŒ๋‹ค ๋Œ€์ˆ˜ํ•™(lambda calculus) ์ดํ›„ ๋“ฑ์ž์•ˆ ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•์ด ์žˆ๋‹ค.

๐ŸŽˆ ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜โ€‹

๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜๋Š” ์–ธ๋œป ์ธ์ž๋ฅผ ์—ฌ๋Ÿฟ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋” ์ ์€ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋Š” ์ปค๋ง๊ณผ ๋น„์Šทํ•ด ๋ณด์ธ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ getRestaurantsNearConference ๊ตฌํ˜„๋ถ€๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ์˜คํžˆ๋ ค ์ •๋ฐ˜๋Œ€์— ๊ฐ€๊น๋‹ค. ์ฆ‰, ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜๋Š” ์ด์ „ ๋‹จ๊ณ„์—์„œ ์ƒ์„ฑ๋œ ์ปค๋ง ์š”์†Œ์— ๋ญ”๊ฐ€ ๋” ๋ณดํƒœ์„œ ๊ฒฐ๊ตญ ์ด์žฅ ์•ž๋ถ€๋ถ„์—์„œ ์†Œ๊ฐœํ–ˆ๋˜ ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜ ๋น„์ „๊ณผ ๊ธฐ๋Šฅ์ด ๊ฐ™์€ ํ•จ์ˆ˜๋กœ ๋งŒ๋“  ๊ฒƒ์ด๋‹ค.

function getRestaurantsNearConference(cuisine) {
return getRestaurantsCurried('์šธ์‚ฐ ๋‚จ๊ตฌ ์‹ ์ •๋กœ 20๋ฒˆ๊ธธ 988')(2.0)(cuisine);
}

๐Ÿ“š ์ •๋ฆฌํ•˜๊ธฐโ€‹

๊ฐ’์ด ๋ถˆ๋ณ€์ธ ์ƒ์ˆ˜ ์ธ์ž๋ฅผ ์ง€๋‹Œ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ถ€๋Š” ์ƒ์ˆ˜์„ฑ์„ ์บก์Šํ™”ํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ๊ฒŒ ์ข‹๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜ ๊ธฐ๋ฒ•์ด๋‹ค.
์˜๋„ํ•œ ๋Œ€๋กœ ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ •ํ™•ํ•˜๊ณ  DRYํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋กœ ํ™•์ธํ•˜๋ผ. ๋ฐ˜ํ™˜ ํƒ€์ž…์€ ๋ฌผ๋ก , ๋ณธํ•จ์ˆ˜์— ๋Œ€ํ•œ ์–ด๋–ค ๊ฐ€์ •์ด๋‚˜ ์ถ”์ •๋„ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.(๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ๋„ ์•ˆ ๋œ๋‹ค.)
๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜๋Š” ์ปค๋ง๊ณผ ํ˜ผ๋™ํ•˜๊ธฐ ์‰ฝ๋‹ค. ์ง„์งœ ์ปค๋ง์€ ์ธ์ž๋ฅผ ๋ถ€๋ถ„์ ์œผ๋กœ, ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ ์šฉํ•˜๋Š” ์ผ์ด ์—†๋‹ค. ์™ธ๋ ค ์—ฌ๋Ÿฌ ์ธ์ž๋ฅผ ๊ฑฐ๋Š๋ฆฐ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋ฅผ ํ•˜๋‚˜๋งŒ ์ทจํ•˜๋Š” ์—ฌ๋Ÿฌ ๋‹จ๊ป˜์˜ ํ•จ์ˆ˜๋“ค๋กœ ์ชผ๊ฐ ๋‹ค. ๋ถ€๋ถ„ ์ ์šฉ ํ•จ์ˆ˜๋กœ ์ธ์ž๋ฅผ ๋˜ํ’€์ดํ•˜์ง€ ์•Š๋Š” ์›๋ฆฌ๊ฐ€ ๋งˆ์Œ์— ๋“ ๋‹ค๋ฉด ํ•จ์ˆ˜ ๋ณธ์ฒด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜(memoization)๊ธฐ๋ฒ• ์—ญ์‹œ ํก์กฑํ•  ๊ฒƒ์ด๋‹ค.