Chapter 6: Limiting Scope Exposure - hochan222/Everything-in-JavaScript GitHub Wiki
์ง๊ธ๊น์ง ๋ณ์์ Scope์ ๋ํด ์์๋ณด์๋ค. ๊ธฐ์ด๋ฅผ ์์์ผ๋ ์กฐ๊ธ ๋ ํฐ ๋ฒ์์ธ ํ๋ก๊ทธ๋จ ์ ์ฒด ๊ด์ ๊ณผ ๋ฒ์์์ ๋ณด์! ๋ค์ํ ๋ก์ง์์ ๋ณ์์๋ํ Scope ์ ํ์ด ํ์ํ ์ด์ ๋ฅผ ์์๋ณด์.
Least Exposure
ํจ์๊ฐ ์๊ธฐ ์์ ์ Scope๋ฅผ ๊ฐ์ง๋๊ฑด ์ดํด๋๋ค. ํ์ง๋ง Scope๋ฅผ ์์ฑํ๊ธฐ์ํด block์ด ํ์ํ ์ด์ ๋ ๋ฌด์์ธ๊ฐ?
์ํํธ์จ์ด ์์ง๋์ด๋ง์ ์ผ๋ฐ์ ์ผ๋ก ์ํํธ์จ์ด ๋ณด์์ ์ ์ฉ๋๋ "์ต์ ๊ถํ์ ์์น"(POLP)์ด๋ผ๊ณ ํ๋ ๊ธฐ๋ณธ ์์น์ ๋ช ํํ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ฌ ๋ ผ์์ ์ ์ฉ๋๋ ์ด ์์น์ ๋ณํ์ ์ผ๋ฐ์ ์ผ๋ก "Least Exposure"(POLE)๋ก ํ์๋๋ค.
POLP๋ ์ํํธ์จ์ด ์ํคํ ์ฒ์ ๋ํ ๋ฐฉ์ด ํ์ธ๋ฅผ ํํํ๋ค. ์์คํ ๊ตฌ์ฑ ์์๋ ์ต์ํ์ ๊ถํ, ์ต์ํ์ ์ก์ธ์ค, ์ต์ํ์ ๋ ธ์ถ๋ก ์๋ํ๋๋ก ์ค๊ณ๋์ด์ผํ๋ค. ๊ฐ ๋ถ๋ถ์ด ์ต์ํ์ผ๋ก ํ์ํ ๊ธฐ๋ฅ์ผ๋ก ์ฐ๊ฒฐ๋๋ฉด ํ ๋ถ๋ถ์ ์์์ด๋ ์คํจ๊ฐ ๋๋จธ์ง ์์คํ ์ ๋ฏธ์น๋ ์ํฅ์ ์ต์ํํ๊ธฐ ๋๋ฌธ์ ๋ณด์ ๊ด์ ์์ ์ ์ฒด ์์คํ ์ด ๋ ๊ฐ๋ ฅํด์ง๊ธฐ ๋๋ฌธ์ด๋ค.
POLP๊ฐ ์์คํ ์์ค ๊ตฌ์ฑ ์์ ์ค๊ณ์ ๋ํ๊ฒ์ด๋ผ๋ฉด, POLE Exposure variant์ ๋ ๋ฎ์ ์์ค์ ์ด์ ์ ๋ง์ถ ๊ฒ์ด๋ค.
์ ํ๋ก๊ทธ๋จ์์ ์ ์ญ ๋ณ์ ์ ์ธ์ ํ๋ฉด ์๋ ๊น? ์ด์ ์ ๋ํด ์๊ฐํ ํ์๊ฐ ์๋ค. ๋ค์ ์ธ๊ฐ์ง ๋ฌธ์ ๊ฐ ์๋ค.
- Naming Collisions
- ์๋ณ์๊ฐ ์ ์ญ์ผ๋ก ์ ์ธ ๋ ๊ฒฝ์ฐ ๊ฒน์น ๊ฐ๋ฅ์ฑ์ด์๊ณ ๋ฒ๊ทธ์ ๊ฐ๋ฅ์ฑ์ ์ฆ๊ฐ์ํฌ ์ ์๋ค.
- Unexpected Behavior
- ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ์๋ํ์ง ์์ ๋ฐฉํฅ์ผ๋ก ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ์์์น ๋ชปํ ๋์์ด ์ผ์ด๋ ์ ์๋ค.
- Unintended Dependency
- ์ง๊ธ ๋น์ฅ์ ๋ฌธ์ ๊ฐ ์์ง๋ง ๋์ค์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค์ด ๋ฆฌํฉํ ๋งํ ๋ ํจ๋ถ๋ก ๊ฑด๋๋ฆด ์ ๊ฐ ์๋ค. ์ฝ๋ ๋ณต์ก๋๊ฐ ์ปค์ง๋ค.
๋ณ์/ํจ์ ๋ฒ์ ์ง์ ์ ์ ์ฉ๋๋ POLE์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ํ ์ต์๊ฐ๋ง ๋ ธ์ถํ๊ณ ๋๋จธ์ง๋ ๊ฐ๋ฅํ ํ ๋น๊ณต๊ฐ๋ก ์ ์งํ๋ค.
function diff(x,y) {
if (x > y) {
let tmp = x;
x = y;
y = tmp;
}
return y - x;
}
diff(3,7); // 4
diff(7,5); // 2
POLE ์์น์ ๋ฐ๋ฅด๋ฉด ๊ฐ์ฅ ๊ฐ๋ฅํ ๋ฒ์ ๋ด๋ถ์ ์จ๊ฒจ์ ธ์ผ ํ๋ค. ๊ทธ๋์ tmp๋ if๋ฌธ scope์์์ let์ผ๋ก ์ ์ธ ๋๋ค.
Hiding in Plain (Function) Scope
var cache = {};
function factorial(x) {
if (x < 2)
return 1;
if (!(x in cache)) {
cache[x] = x * factorial(x - 1);
}
return cache[x];
}
factorial(6);
// 720
cache;
// {
// "2": 2,
// "3": 6,
// "4": 24,
// "5": 120,
// "6": 720
// }
factorial(7);
// 5040
ํฉํ ๋ฆฌ์ผ ํจ์์ด๋ค. dp์ฌ์ฉ์ ์ํด cache๋ฅผ ์ ์ญ์ผ๋ก ์ค์ ํ๋ค. ์๋ ์ฝ๋๋ก ์ฐ๋ฆฌ๋ ํจ์ scope๋ก ์ ํํ๋ ๊ฒ์ด ์ ํ์ํ์ง๋ฅผ ์ ์ ์๋ค.
// outer/global scope
function hideTheCache() {
// "middle scope", where we hide `cache`
var cache = {};
return factorial;
// **********************
function factorial(x) {
// inner scope
if (x < 2) return 1;
if (!(x in cache)) {
cache[x] = x * factorial(x - 1);
}
return cache[x];
}
}
var factorial = hideTheCache();
factorial(6);
// 720
factorial(7);
// 5040
๋ณ์๋ฅผ ์จ๊ธฐ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉ๋๋ ํจ์๋ ์ ํจ์๋ฅผ ์ ์ํ๋ ๋์ ํจ์ ํํ์์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ๋์ ํด๊ฒฐ์ฑ ์ผ ์ ์๋ค.
var factorial = (function hideTheCache() {
var cache = {};
function factorial(x) {
if (x < 2) return 1;
if (!(x in cache)) {
cache[x] = x * factorial(x - 1);
}
return cache[x];
}
return factorial;
})();
factorial(6);
// 720
factorial(7);
// 5040
์... ์๊ฐ๋ ๋ชปํ๋ค. ๊น๋ํ๋ค...
Invoking Function Expressions Immediately
์ฆ์ ์คํ ํจ์ ํํ(IIFE, Immediately Invoked Function Expression)
// outer scope
(function(){
// inner hidden scope
})();
// more outer scope
๋จ, ํ์ดํ, ๋นํ์ดํ ํจ์์ ๋ฐ๋ผ this ๋ฐ์ธ๋ฉ ๋ณ๊ฒฝ๋ฑ return, this, break, continue๊ฐ ์๋ ๊ฒฝ์ฐ ์ํ๋ ๋์์ด ์ด๋ค์ง์ง ์์ ์ ์์ผ๋ ์ด ๊ฒฝ์ฐ ์กฐ์ฌํ๊ฑฐ๋ ์ฐ์ง ์๋๊ฒ์ ์ถ์ฒํ๋ค.
Scoping with Blocks
๋ชจ๋ {..} ์ค๊ดํธ ์์ด block์ ์์ฑํ๋ ๊ฒ์ ์๋๋ค. let๊ณผ const๊ฐ ์์ด์ผ block์ด ์์ฑ๋๋ค.
- ๊ฐ์ฒด ๋ฆฌํฐ๋ด์ {..} ์ค๊ดํธ ์์ ์ฌ์ฉํ์ฌ ํค-๊ฐ ๋ชฉ๋ก์ ๊ตฌ๋ถํ์ง๋ง ์ด๋ฌํ ๊ฐ์ฒด ๊ฐ์ block์ด ์๋๋ค.
- ํด๋์ค๋ ๋ณธ๋ฌธ์ {..} ์ค๊ดํธ๋ฅผ ์ฌ์ฉํ์ง๋ง ์ด๊ฒ์ block์ด๋ scope๊ฐ ์๋๋ค.
- ํจ์๋ ๋ณธ๋ฌธ ์ฃผ์์ {..}๋ฅผ ์ฌ์ฉํ์ง๋ง ๊ธฐ์ ์ ์ผ๋ก๋ block์ด ์๋๋ค. ํจ์ ๋ณธ๋ฌธ์ ๋ํ ๋จ์ผ ๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ function scope์ด๋ค.
- switch ๋ฌธ์ {..} ์ค๊ดํธ ์์ block/scope๋ฅผ ์ ์ํ์ง ์๋๋ค.
๋ช ์ ์ ๋ ๋ฆฝํ {..} ๋ธ๋ก์ ํญ์ ์ ํจํ JS ๊ตฌ๋ฌธ ์ด์์ง๋ง ES6์ let/const ์ด์ ์๋ ๋ฒ์๊ฐ ๋ ์ ์์๋ค.
๋ธ๋ก ๋ฒ์ ์ง์ ์ ์ง์ํ๋ ๋๋ถ๋ถ์ ์ธ์ด์์ ๋ช ์ ์ ๋ธ๋ก ๋ฒ์๋ ํ๋ ๋๋ ๋ช ๊ฐ์ ๋ณ์์ ๋ํด ์ข์ ๋ฒ์ ์กฐ๊ฐ์ ๋ง๋๋ ๋ฐ ๋งค์ฐ ์ผ๋ฐ์ ์ธ ํจํด์ด๋ค. ๋ฐ๋ผ์ POLE ์์น์ ๋ฐ๋ผ JS์์๋ ๋ ๋๋ฆฌ ํผ์ง์ด ํจํด์ ์์ฉํด์ผํ๋ค. (๋ช ์์ ) ๋ธ๋ก ๋ฒ์ ์ง์ ์ ์ฌ์ฉํ์ฌ ์๋ณ์ ๋ ธ์ถ์ ๊ฐ๋ฅํ ์ต์ํ์ผ๋ก ์ค์ด์.
if (somethingHappened) {
// this is a block, but not a scope
{
// this is both a block and an
// explicit scope
let msg = somethingHappened.message();
notifyOthers(msg);
}
// ..
recoverFromSomething();
}
๋ช ์์ ๋ธ๋ก ๋ฒ์๊ฐ ์๋ ๋ ๋ค๋ฅธ ์:
function getNextMonthStart(dateStr) {
var nextMonth, year;
{
let curMonth;
[ , year, curMonth ] = dateStr.match(
/(\d{4})-(\d{2})-\d{2}/
) || [];
nextMonth = (Number(curMonth) % 12) + 1;
}
if (nextMonth == 1) {
year++;
}
return `${ year }-${
String(nextMonth).padStart(2,"0")
}-01`;
}
getNextMonthStart("2019-12-25"); // 2020-01-01
์์ curMonth๋ ๋ค์ ๋ถ๋ถ๋ฐ์ ์ฌ์ฉํ์ง ์์๊ธฐ๋๋ฌธ์ ๋ช
์์ ์ผ๋ก ๋ฒ์๋ฅผ ์ขํ์ฃผ์๋ค.
POLE ์์น์ ์ด์ ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฒ์ ๋
ธ์ถ์ ์ต์ํํ๋ ค๋ ๋ง์๊ฐ์ง์ ์ต๊ด์ผ๋ก ์ฑํ ํ ๋ ๊ฐ์ฅ ์ ๋ฌ์ฑ๋๋ค. ํ๋ก๊ทธ๋จ์ด ํ์ฅ๋จ์ ๋ฐ๋ผ ์ด ์์น์ ์ด์ ์ ๋ง์ด์ง ๊ฒ์ด๋ค.
var and let
function diff(x,y) {
if (x > y) {
var tmp = x; // `tmp` is function-scoped
x = y;
y = tmp;
}
return y - x;
}
var๋ ๋ํ๋๋ ์์น์ ๊ด๊ณ์์ด ๊ฐ์ฅ ๊ฐ๊น์ด ๋๋ฌ์ธ๋ ํจ์ ๋ฒ์์ ์ฐ๊ฒฐ๋๋ค. var๊ฐ ๋ธ๋ก ๋ด๋ถ์ ๋ํ๋๋ ๊ฒฝ์ฐ์๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค. ๋ฐ๋ผ์, ์ ์์ ์ tmp๋ ํจ์ ๋ฒ์์ด๋ค.
์ฐ๋ฆฌ๋ let์ ์ฌ์ฉํ์ง์๊ณ var์ ์ฌ์ฉํ๋ฏ๋ก์จ ์๊ฐ์ ์ผ๋ก var์ ์ฌ์ฉํ ๋ณ์๊ฐ ํจ์ ๋ฒ์์์ ๊ฐ์ ์ ์ผ๋ก ์๋ฆด ์ ์๋ค. ๋ฐ๋ผ์, ๊ฐ์ฅ ํ๋ช ํ ๋ฐฉ๋ฒ์ let๊ณผ var์ ๋ ๋ค ์ ์ ํ ์ฌ์ฉํด์ ๊ฐ๋ ์ฑ์ ๋์ด๋ ๊ฒ์ด๋ค.
var์ let์ ๋ชจ๋ ์ฌ์ฉํ๋ผ๋ ์ด ์ฑ ์ ๊ถ๊ณ ๋ ๋ถ๋ช ํ ๋ ผ๋์ ์ฌ์ง๊ฐ ์์ผ๋ฉฐ ๋๋ค์์ ๋ชจ์๋๋ค. "var is broken, let fix it"๋ฐ "never use var, let is the replacement"์ ๊ฐ์ ์ฃผ์ฅ์ ๋ฃ๋ ๊ฒ์ด ํจ์ฌ ๋ ์ผ๋ฐ์ ์ด๋ค. ํ์ง๋ง, ์ด๊ฑด ์ฑ ์ ์๊ฒฌ๋ ์ผ๋ฆฌ๊ฐ ์๊ธดํ๋ค. ์ ๋ฐ ์ธก๋ฉด์ด๋ฉด var์ let์ ์์ด ์ฐ๋๊ฒ๋ ๋์์ง ์๋ค๊ณ ์๊ฐํ๋ค.
Where To let?
์ ๋ณด: ES6 ์ด์ ์๋ ํ์ฉํ์ง ์์์ผ๋ฏ๋ก ์ค์ ๋ก ๋ธ๋ก ๋ฒ์๋ฅผ ์ง์ ํ ์ ์์๋ค.
function diff(x, y) {
if (x > y) {
// `tmp` is still function-scoped, but
// the placement here semantically
// signals block-scoping
var tmp = x;
x = y;
y = tmp;
}
return y - x;
}
tmp๊ฐ ๋ช ์์ ์ผ๋ก if๋ฌธ์์์๋ง ์ฐ์ด๋ฏ๋ก let์ผ๋ก ์ ์ํด์ฃผ๋ ํธ์ด ์ข๋ค.
๋ํ, ๋ฐ๋์ let์ ์ฌ์ฉํด์ผํ๋ ๊ณณ์ด ๋ ์๋๋ฐ
for (var i = 0; i < 5; i++) {
// do something
}
for๋ฌธ์์ i๋ฅผ ์ ์ธํ ๋ ํญ์ let์ ์ฌ์ฉํด์ผํ๋ค. ๋ฃจํ๊ฐ ์ ์ ๋ ์์น์ ๊ด๊ณ์์ด i๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํญ์ ๋ฃจํ ๋ด์์๋ง ์ฌ์ฉํด์ผํ๋ค. ์ด ๊ฒฝ์ฐ POLE์ var ๋์ let์ผ๋ก ์ ์ธํด์ผ ํ๋ค.
for (let i = 0; i < 5; i++) {
// do something
}
var๋ฅผ let์ผ๋ก ๋ฐ๊ฟ ๋ ๋ฌธ์ ์ ์ด ๋๋ ๊ฒฝ์ฐ๋ ๋ค์๊ณผ๊ฐ์ ํ๊ฐ์ง ๊ฒฝ์ฐ ๋ฟ์ด๋ค.
for (var i = 0; i < 5; i++) {
if (checkValue(i)) {
break;
}
}
if (i < 5) {
console.log("The loop stopped early!");
}
ํ์ง๋ง ๋ฑ ๋ด๋ ์ฝ๋ ๊ตฌ์กฐ๊ฐ ์์ข์ ๋ณด์ธ๋ค. ๋ฐ๋ผ์ ์ด๋ฐ ๊ฒฝ์ฐ๋ ๋ฐ๋ก ๋ณ์๋ฅผ ๋์ด์ ํด๊ฒฐํ์. ๋ฐ์ ์ฝ๋ ์ฐธ์กฐ
var lastI;
for (let i = 0; i < 5; i++) {
lastI = i;
if (checkValue(i)) {
break;
}
}
if (lastI < 5) {
console.log("The loop stopped early!");
}
Whatโs the Catch?
์ง๊ธ๊น์ง var ๋ฐ ๋งค๊ฐ๋ณ์๋ function scope์ด๊ณ let๊ณผ const๋ block scope๋ผ๊ณ ํ๋ค. ๋จ, ํ ๊ฒฝ์ฐ์ ์์ธ๊ฐ์๋๋ฐ catch๋ฌธ์์ ์ด๋ค.
ES3(1999 ๋ )์ try..catch๊ฐ ๋์ ๋ ์ดํ๋ก catch ์ ์ ์ถ๊ฐ ๋ธ๋ก ๋ฒ์ ์ ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ค.
try {
doesntExist();
}
catch (err) {
console.log(err);
// ReferenceError: 'doesntExist' is not defined
// ^^^^ message printed from the caught exception
let onlyHere = true;
var outerVariable = true;
}
console.log(outerVariable); // true
console.log(err);
// ReferenceError: 'err' is not defined
// ^^^^ this is another thrown (uncaught) exception
catch ์ ์ ์ํด ์ ์ธ๋ err ๋ณ์๋ ํด๋น ๋ธ๋ก์ ๋ํ ๋ธ๋ก ๋ฒ์์ด๋ค. catch์ ๋ธ๋ก์ let์ ํตํด ๋ค๋ฅธ ๋ธ๋ก ๋ฒ์ ์ ์ธ์ ๋ณด์ ํ ์ ์๋ค. ๊ทธ๋ฌ๋ ์ด ๋ธ๋ก ๋ด๋ถ์ var ์ ์ธ์ ์ฌ์ ํ โโ์ธ๋ถ ํจ์/์ ์ญ ๋ฒ์์ ์ฐ๊ฒฐ๋๋ค.
Function Declarations in Blocks (FiB)
๋ธ๋ก ์์ ์ง์ ๋ํ๋๋ ํจ์ ์ ์ธ์ FiB๋ผ๊ณ ํ๋ค.
if (false) {
function ask() {
console.log("Does this run?");
}
}
ask();
์ ํ๋ก๊ทธ๋จ์ ๋์์ ๋ค์ ์ธ๊ฐ์ง ์ค์ ํ๋๋ก ์์ธก ํ ์ ์๋ค.
- ask() ํธ์ถ์ ReferenceError ์์ธ์ ํจ๊ป ์คํจํ๋ค. if๋ฌธ block์์์ ์ ์ธ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
- ask() ํธ์ถ์ด TypeError ์์ธ์ ํจ๊ป ์คํจํ๋ค. ask ์๋ณ์๊ฐ ์กด์ฌํ์ง๋ง ์ ์๋์ง ์์๊ธฐ ๋๋ฌธ์(if ๋ฌธ์ด ์คํ์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.) ํธ์ถ ๊ฐ๋ฅํ ํจ์๊ฐ ์๋๋ค.
- ask () ํธ์ถ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์คํ๋์ด "Does it run?"์ด ์ถ๋ ฅ๋๋ค.
ํผ๋์ค๋ฌ์ด ๋ถ๋ถ์ JS ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค ๋ค๋ฅด๊ฒ ๋์ํ๋ค. ์ด๊ฒ์ ๊ธฐ์กด์ ๋ ๊ฑฐ์ ํ๋์ด ์์ธก ๊ฐ๋ฅํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ๋ฐํ๋ ๋ช ์๋๋ ๋ฏธ์น ๋์ ์ค ํ๋์ด๋ค.
์ฐธ๊ณ : ํฌ๋กฌ์์๋ 2๋ฒ์ด ๋์จ๋ค!
JS ์ฌ์์ ๋ฐ๋ฅด๋ฉด ๋ธ๋ก ๋ด๋ถ์ ํจ์ ์ ์ธ์ ๋ธ๋ก ๋ฒ์์ด๋ฏ๋ก ๋ต์ (1)์ด์ด์ผ ํ๋ค.
๊ทธ๋ฌ๋ ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ JS ์์ง (Chrome์์ ์ ๊ณต๋์ง๋ง Node์์๋ ์ฌ์ฉ๋๋ v8 ํฌํจ)์ (2)์ฒ๋ผ ๋์ํ๋ค. ์ฆ, ์๋ณ์๋ if ๋ธ๋ก ์ธ๋ถ์ ์์ง๋ง ํจ์ ๊ฐ์ ์๋์ผ๋ก ์ด๊ธฐํ๋์ง ์๋๋ค.
๋ฐ๋ผ์, ๋ค์ ์ฝ๋๊ฐ ๊ฐ๋ฅํ๋ค.
if (typeof Array.isArray != "undefined") {
function isArray(a) {
return Array.isArray(a);
}
} else {
function isArray(a) {
return Object.prototype.toString.call(a)
}
}
์ฑ๋ฅ์ด ์ฝ๊ฐ ๋จ์ด์ง ์ ์์ง๋ง ์ ๋ฐ์ ์ผ๋ก ๋ ๋์ ์ ๊ทผ ๋ฐฉ์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
function isArray(a) {
if (typeof Array.isArray != "undefined") {
return Array.isArray(a);
}
else {
return Object.prototype.toString.call(a)
== "[object Array]";
}
}
๋ค์ ์ฝ๋๋ฅผ ๋ณด์.
if (true) {
function ask() {
console.log("Am I called?");
}
}
if (true) {
function ask() {
console.log("Or what about me?");
}
}
for (let i = 0; i < 5; i++) {
function ask() {
console.log("Or is it one of these?");
}
}
ask();
function ask() {
console.log("Wait, maybe, it's this one?");
}
๋ค์ ์ฝ๋์ ๋์์ ์ด๋ป๊ฒ ๋๋๊ฐ? ๊ฐ์ฅ ํ๋จ์ ask๊ฐ ํธ์ด์คํ
๋ผ์ "Wait, maybe, it's this one?"์ด ์ถ๋ ฅ๋ ๊ฒ ๊ฐ์ง๋ง "Or is it one of these?"์ด ์ถ๋ ฅ๋๋ค. ใ
ใ
ใ
ใ
ใ
ใ
ํ์คํ๋ค. FiB๋ ํผํ์. ์ด์ ๋ณด๋ค ๊ฒฐ์ ์ด ๋ง๋ค.
์ ๋๋ก ๋ธ๋ก ์์์ ํจ์๋ฅผ ์ ์ธํ์ง๋ง์.