๋ฐ˜์‘ํ˜•

TL;DR


RORO ํŒจํ„ด์€ Receive an Object, Return and Object์˜ ์•ฝ์ž๋‹ค. ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฆฌํ„ด๊ฐ’ ๋ชจ๋‘ ๊ฐ์ฒด์ธ๊ฑธ ๋งํ•œ๋‹ค. ES6๋ถ€ํ„ฐ ์ง€์›ํ•˜๋Š” ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น ๋•๋ถ„์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋„ RORO ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ ์ •์˜ ๋ฐฉ์‹(RORO ํŒจํ„ด)๋งŒ ์‚ด์ง ๋ฐ”๊ฟจ์ง€๋งŒ ๊ฐ€๋…์„ฑ์€ ๋งค์šฐ ์ข‹์•„์ง„๋‹ค. RORO ํŒจํ„ด์˜ ์žฅ์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

  • ์ด๋ฆ„์ด ์ง€์ •๋œ ํŒŒ๋ผ๋ฏธํ„ฐ (Named parameters)
  • ๋ช…์‹œ์ ์ธ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ณธ๊ฐ’ (Cleaner default parameters)
  • ๋” ๋งŽ์€ ์ •๋ณด ๋ฐ˜ํ™˜ (Richer return values)
  • ํ•จ์ˆ˜ ํ•ฉ์„ฑ ์šฉ์ด (Easy function composition)

 

์ด๋ฆ„์ด ์ง€์ •๋œ ํŒŒ๋ผ๋ฏธํ„ฐ


ํŒŒ๋ผ๋ฏธํ„ฐ์— {} ์ค‘๊ด„ํ˜ธ๋งŒ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ RORO ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๊ฐ์ฒด ํ˜•ํƒœ์ด๋ฏ€๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

// RORO ํŒจํ„ด ์‚ฌ์šฉ ์ „
const getSquare = function (xPosition, yPosition, width, height) {
  /*...*/
};

// RORO ํŒจํ„ด ์‚ฌ์šฉ ํ›„
const getSquare = function ({ xPosition, yPosition, width, height }) {
  /*...*/
};

// ํ•จ์ˆ˜ ํ˜ธ์ถœ
getSquare({ xPosition: 60, yPosition: 80, width: 88, height: 188 });

 

์•„๋ž˜์ฒ˜๋Ÿผ ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด ์›ํ•˜๋Š” ์ด๋ฆ„์„ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const getSquare = function ({
  xPosition: x,
  yPosition: y,
  width: w,
  height: h,
}) {
  /*...*/
};
// getSquare ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ (์™ผ์ชฝ๋ถ€ํ„ฐ) x, y, w, h ๋ณ€์ˆ˜๋ช…์œผ๋กœ ์‚ฌ์šฉ

 

๋ช…์‹œ์ ์ธ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ณธ๊ฐ’


์•„๋ž˜์ฒ˜๋Ÿผ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋น„์–ด์žˆ๋Š” ์ƒํƒœ๋กœ ํ˜ธ์ถœํ•˜๋ฉด Cannot desctructure ... ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ตฌ์กฐ๋ถ„ํ•ด๋ฅผ ์‹คํ–‰ํ•  ๋Œ€์ƒ(ํŒŒ๋ผ๋ฏธํ„ฐ)๊ฐ€ undefined์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

getSquare(); // Cannot desctructure ...

 

์ด๋• ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ {} ๋นˆ ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

const getSquare = function ({
  xPosition: x,
  yPosition: y,
  width: w,
  height: h,
} = {}) {
  /* ... */
};

 

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์ง€ ์•Š์•˜์„ ๋•Œ์˜ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

const getSquare = function ({
  xPosition: x = 100, // xPosition(x)์˜ ๊ธฐ๋ณธ๊ฐ’์„ 100์œผ๋กœ ์„ค์ •
  yPosition: y = 200, // yPosition(y)์˜ ๊ธฐ๋ณธ๊ฐ’์„ 200์œผ๋กœ ์„ค์ •
  width: w,
  height: h,
} = {}) {
  /* ... */
};

 

๋” ๋งŽ์€ ์ •๋ณด ๋ฐ˜ํ™˜


์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ•จ์ˆ˜๋Š” ํ•œ ๊ฐ€์ง€ ๊ฐ’๋งŒ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฐ˜ํ™˜๊ฐ’์ด ๊ฐ์ฒด๋ผ๋ฉด ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น์„ ํ†ตํ•ด ์›ํ•˜๋Š” ์†์„ฑ๋งŒ ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ RORO ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋” ๋งŽ์€ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ.

const { position, size } = getSquare({
  /* ... */
});

 

๐Ÿ”๏ธ ์ฐธ๊ณ ๋กœ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ ๊ฐ’์„ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํ•จ์ˆ˜ ํ•ฉ์„ฑ


2๊ฐœ ์ด์ƒ์˜ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์ณ์„œ ๋งŒ๋“  ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑ ํ•จ์ˆ˜๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ์—์„œ pipe ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋“ค์„ ์ธ์ž๋กœ ๋ฐ›์€ ํ›„ reduce ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ ํ•จ์ˆ˜๋“ค์„ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

function pipe(...fns) {
  // fns -> [fn1, fn2, fn3, ...]
  return (param) =>
    fns.reduce(
      (result, fn) => fn(result), // ์ด์ „ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ(result)๊ฐ€ ํ˜„์žฌ ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋“ค์–ด๊ฐ
      param,
    );
}

 

์•„๋ž˜ ๊ฐ™์€ ํ•ฉ์„ฑ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณธ๋‹ค. saveUser ํ•จ์ˆ˜์— ๋„˜๊ธด userInfo ๊ฐ’์€ pipe ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธด ๊ฐ ํ•จ์ˆ˜๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ(validate ~) ๊ฑฐ์น˜๊ฒŒ ๋œ๋‹ค.

const saveUser = pipe(validate, normalize, persist);
saveUser(userInfo);

 

ํ•ฉ์„ฑ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์‹คํ–‰๋œ๋‹ค.

persist(normalize(validate(userInfo)));

 

๋งŒ์•ฝ ๋ชจ๋“  ํ•จ์ˆ˜๊ฐ€ RORO ํŒจํ„ด์„ ์ ์šฉํ•ด ๊ฐ์ฒด๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ  ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด, ๊ฐ ํ•จ์ˆ˜๋Š” ์ธ์ž์—์„œ ํ•„์š”ํ•œ ๊ฐ’๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ๋‚˜๋จธ์ง€ ๊ฐ’์€ ...rest ํŒจํ„ด์œผ๋กœ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

function normalize({ email, username, ...rest }) {
  // normalize ๋กœ์ง(email๊ณผ username๋งŒ ์‚ฌ์šฉ)

  return {
    email, // normalized email(email ์ธ์ž์— normalize ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ)
    username, // normalized username(username ์ธ์ž์— normalized ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ)
    ...rest, // ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธ์ž๋Š” ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
  };
}
๋”๋ณด๊ธฐ
function validate({
  id,
  firstName,
  lastName,
  email = requiredParam(), // ํ•„์ˆ˜
  username = requiredParam(), // ํ•„์ˆ˜
  pass = requiredParam(), // ํ•„์ˆ˜
  address,
  ...rest
}) {
  // ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง
  return {
    id,
    firstName,
    lastName,
    email,
    username,
    pass,
    address,
    ...rest, // ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ํŒŒ๋ผ๋ฏธํ„ฐ
  };
}

function normalize({ email, username, ...rest }) {
  // email, username์— ๋Œ€ํ•œ normalize ๋กœ์ง
  return {
    email,
    username,
    ...rest, // ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ํŒŒ๋ผ๋ฏธํ„ฐ
  };
}

async function persist({
  upsert = true, // update and insert
  ...info
}) {
  // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— userInfo ์ €์žฅ
  return {
    operation,
    status, // ์ฒ˜๋ฆฌ ์ƒํƒœ
    saved: info,
  };
}

function pipe(...fns) {
  return (param) => fns.reduce((result, fn) => fn(result), param);
}

const saveUser = pipe(validate, normalize, persist);
saveUser(userInfo);

 

ํ•„์ˆ˜ ์ธ์ž


์•„๋ž˜ ์ฝ”๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ณด๋ฉด requiredParam() ํ•จ์ˆ˜์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ๊ฐ’์€ ๋ฆฌํ„ฐ๋Ÿด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

function validate({
  id,
  firstName,
  lastName,
  email = requiredParam(),
  username = requiredParam(),
  pass = requiredParam(),
  address,
  ...rest
}) {
  return {
    /*...*/
  };
}

 

ํ•จ์ˆ˜์— ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์ „๋‹ฌ๋๋‹ค๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง€์ •ํ•ด๋‘” requiredParam() ํ•จ์ˆ˜๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Ÿฐ ํŠน์„ฑ์„ ์ด์šฉํ•ด requiredParam() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ํ•ญ์ƒ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒํ•˜๋„๋ก ํ•ด์„œ, ํ•„์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์ง€ ์•Š์•˜์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

function requiredParam(param) {
  const requiredParamError = new Error(
    `Required parameter, "${param}" is missing.`,
  );

  // preserve original stack trace
  // Error.captureStackTrace๋Š” V8 ์—”์ง„์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋น„ํ‘œ์ค€์ด๋ฏ€๋กœ ์•„๋ž˜์ฒ˜๋Ÿผ ์กฐ๊ฑด ์ถ”๊ฐ€
  if (Error.captureStackTrace) {
    Error.captureStackTrace(requiredParamError, requiredParam);
    // requiredParam ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•œ ํ˜ธ์ถœ๋งŒ ํ‘œ์‹œ
  }

  throw requiredParamError;
}

 

์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•œ๋‹ค.

function validate({
  // ... ์ƒ๋žต
  email = requiredParam('email'),
} = {}) {
  /*...*/
}

// validate ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ํ•„์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์ธ email์„ ๋ˆ„๋ฝํ–ˆ๋‹ค๋ฉด ์•„๋ž˜ ์˜ค๋ฅ˜ ์ถœ๋ ฅ
// Uncaught Error: Required parameter, "email" is missing.

 

๋ฒˆ์™ธ — ์Šคํƒ ํŠธ๋ ˆ์ด์Šค(Stack Trace)


  • throw๋ฌธ์€ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ•จ์ˆ˜ ์‹คํ–‰์ด ์ค‘์ง€๋˜๊ณ (throw ์ดํ›„์˜ ์ฝ”๋“œ๋Š” ์‹คํ–‰ํ•˜์ง€ ์•Š์Œ) ์ œ์–ดํ๋ฆ„์€ ์ฝœ์Šคํƒ์˜ ์ฒซ๋ฒˆ์งธ catch ๋ธ”๋ก์œผ๋กœ ์ „๋‹ฌ๋œ๋‹ค. catch(e) ์ธ์ž๋Š” ์„ ํƒ์ ์ด๋‹ค.
  • ์—๋Ÿฌ(error) ๊ฐ์ฒด๋Š” ์•„๋ž˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง„๋‹ค
    • name : ์—๋Ÿฌ ์ด๋ฆ„
    • message : ์—๋Ÿฌ ์ƒ์„ธ ๋‚ด์šฉ
    • stack(๋น„ํ‘œ์ค€ ํ”„๋กœํผํ‹ฐ) : ์—๋Ÿฌ ๋ฐœ์ƒ ์›์ธ์ด ์–ด๋–ค ํŒŒ์ผ ๋•Œ๋ฌธ์ธ์ง€์— ๋Œ€ํ•œ ๊ธฐ๋ก(์ฝœ์Šคํƒ).

 

console.trace()

๐Ÿ’ก ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋Š” ์œ„ → ์•„๋ž˜ ์ˆœ์œผ๋กœ ์ฝ๋Š”๋‹ค.

 

console.trace() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋  ์‹œ์ ์˜ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ์ฝ˜์†”์— ์ถœ๋ ฅํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ์—์„  c ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์Šคํƒ์—์„œ ์ œ๊ฑฐ ๋์œผ๋ฏ€๋กœ console.trace()๋ฅผ ์‹คํ–‰ํ•˜๋ฉด b, a๋งŒ ์ถœ๋ ฅํ•œ๋‹ค.

function c() {
  console.log('c');
}

function b() {
  console.log('b');
  c();
  console.trace();
}

function a() {
  console.log('a');
  b();
}

a();
// b @VM3226:8 - console.trace ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// a @VM3226:13 - b ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// (์ต๋ช…) @VM3226:16 - a ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

Error.captureStackTrace()

๊ฐœ๋… ์ดํ•ด

๐Ÿ’ก ์—๋Ÿฌ๋Š” ํ˜ธ์ถœ์ž(caller) ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค. ์ฆ‰, ์ฝœ์Šคํƒ์˜ ์•„๋ž˜ ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค.

 

Error.captureStackTrace ํ•จ์ˆ˜๋Š” ํ˜„์žฌ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ๋Œ€์ƒ ๊ฐ์ฒด(์ฒซ๋ฒˆ์งธ ์ธ์ž) ์•ˆ์— stack ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด ์ €์žฅํ•œ๋‹ค. ์ฒซ๋ฒˆ์งธ ์ธ์ž๋Š” ๊ฐ์ฒด, (์„ ํƒ)๋‘๋ฒˆ์งธ ์ธ์ž๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค.

 

๋‘๋ฒˆ์งธ ์ธ์ž์— ์•„๋ฌด๊ฒƒ๋„ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด ํ˜„์žฌ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ๋ชจ๋‘ ์บก์ฒ˜ํ•œ๋‹ค. ๋‘๋ฒˆ์งธ ์ธ์ž์— ํ•จ์ˆ˜๋ฅผ ๋ช…์‹œํ•˜๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•œ ํ˜ธ์ถœ๋งŒ ํ‘œ์‹œํ•œ๋‹ค(ํ•ด๋‹น ํ•จ์ˆ˜์™€ ๊ทธ ๋’ค์— ์Œ“์ธ ํ˜ธ์ถœ ์Šคํƒ์„ ๋ชจ๋‘ ์ˆจ๊ธด๋‹ค).

 

์˜ˆ์‹œ) ๋‘๋ฒˆ์งธ ์ธ์ž๋ฅผ ๋ช…์‹œํ•˜์ง€ ์•Š์•˜์„ ๋•Œ

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ a ํ•จ์ˆ˜ ํ˜ธ์ถœ → b ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ›„ a, b ๊ฐ€ ์Šคํƒ์— ์Œ“์ธ๋‹ค. b ํ•จ์ˆ˜ ๋ณธ๋ฌธ์—์„œ c ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— Error.captureStackTrace ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด๋‹น ์‹œ์ ์˜ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ์บก์ฒ˜ํ•˜์—ฌ b, a๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

const myObj = {};

function c() {}

function b() {
  // Here we will store the current stack trace into myObj
  Error.captureStackTrace(myObj);
  c();
}

function a() {
  b();
}

// First we will call these functions
a();

// Now let's see what is the stack trace stored into myObj.stack
console.log(myObj.stack);
// at b (<anonymous>:7:9) - Error.captureStackTrace ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at a (<anonymous>:12:3) - b ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:16:1 - a ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

์˜ˆ์‹œ) ๋‘๋ฒˆ์งธ ์ธ์ž๋ฅผ ๋ช…์‹œํ–ˆ์„ ๋•Œ

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ ์Šคํƒ์€ abcd ์ˆœ์œผ๋กœ ์Œ“์ธ๋‹ค. ํ•˜์ง€๋งŒ Error.captureStackTrace์— ํ•จ์ˆ˜ b๋ฅผ ์ „๋‹ฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— b์™€ b์œ„์— ์Œ“์ธ ํ”„๋ ˆ์ž„(c, d)๋“ค์„ ๋ชจ๋‘ ์ˆจ๊ธด๋‹ค. ๋”ฐ๋ผ์„œ ์ฝ˜์†”์— ์ฐํžˆ๋Š” ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋Š” a๋งŒ ๋‚˜์˜จ๋‹ค.

const myObj = {};

function d() {
  // Here we will store the current stack trace into myObj
  // This time we will hide all the frames after `b` and `b` itself
  Error.captureStackTrace(myObj, b);
}

function c() {
  d();
}

function b() {
  c();
}

function a() {
  b();
}

// First we will call these functions
a();

// Now let's see what is the stack trace stored into myObj.stack
console.log(myObj.stack);
// at a (<anonymous>:18:3) - b ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:22:1 - a ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

์˜ˆ์‹œ) ํ•„์ˆ˜ ์ธ์ž ๊ตฌ๋ถ„ ํ•จ์ˆ˜์— ์ ์šฉ

์•„๋ž˜ ์ฝ”๋“œ์˜ requiredParam ํ•จ์ˆ˜๋Š” RORO ํŒจํ„ด์—์„œ ํ•„์ˆ˜ ์ธ์ž๋ฅผ ๋ฐ›์ง€ ์•Š์•˜์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ๋˜ ํ•จ์ˆ˜๋‹ค. func ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํ˜ธ์ถœ ์Šคํƒ์€ funcrequiredParam ์ˆœ์œผ๋กœ ์Œ“์ธ๋‹ค.

 

Error.captureStackTrace ๋‘๋ฒˆ์งธ ์ธ์ž์— ๋ช…์‹œํ•œ ํ•จ์ˆ˜๊ฐ€ requiredParam์ด๋ฏ€๋กœ requiredParam ํ•จ์ˆ˜์™€ ๊ทธ ์ดํ›„์— ์Œ“์ธ ํ”„๋ ˆ์ž„(ํ˜ธ์ถœ ์Šคํƒ)์€ ๋ชจ๋‘ ์ˆจ๊ธด๋‹ค. ๋”ฐ๋ผ์„œ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋Š” func๋งŒ ํ‘œ์‹œํ•œ๋‹ค.

 

๐Ÿ’ก Uncaught Error๋Š” ์—๋Ÿฌ๋ฅผ ์บ์น˜ํ•˜์ง€ ๋ชปํ–ˆ์„ ๋•Œ(์—๋Ÿฌ๋ฅผ ํ•ธ๋“ค๋งํ•˜๋Š” ๊ณณ์ด ์—†์„ ๋•Œ) ๋ฐœ์ƒํ•œ๋‹ค

function requiredParam(param) {
  const requiredParamError = new Error(
    `Required parameter, "${param}" is missing.`,
  );

  // preserve original stack trace
  // Error.captureStackTrace๋Š” V8 ์—”์ง„์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋น„ํ‘œ์ค€์ด๋ฏ€๋กœ ์•„๋ž˜์ฒ˜๋Ÿผ ์กฐ๊ฑด ์ถ”๊ฐ€
  if (Error.captureStackTrace) {
    Error.captureStackTrace(requiredParamError, requiredParam);
    // requiredParam ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•œ ํ˜ธ์ถœ๋งŒ ํ‘œ์‹œ
  }

  throw requiredParamError;
}

function func({ name = requiredParam('name'), age } = {}) {
  return { name, age };
}

func({ age: 30 });
// Uncaught Error: Required parameter, "name" is missing.
// at func (<anonymous>:13:24) - requiredParam ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:17:1 - func ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

Error.captureStackTrace ๋‘๋ฒˆ์งธ ์ธ์ž์— ์•„๋ฌด๊ฒƒ๋„ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ๋ชจ๋‘ ์ถœ๋ ฅํ•œ๋‹ค.

func({ age: 30 });
// Uncaught Error: Required parameter, "name" is missing.
// at requiredParam (<anonymous>:7:11) - captureStackTrace ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at func (<anonymous>:13:24) - requiredParam ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:17:1 - func ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

์—๋Ÿฌ ์ „ํŒŒ์™€ setTimeout

try/catch ์˜ˆ์‹œ

์—๋Ÿฌ๋Š” ํ˜ธ์ถœ์ž(caller) ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค(์ฝœ ์Šคํƒ์˜ ์•„๋ž˜ ๋ฐฉํ–ฅ; ํ˜„์žฌ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์ง์ „์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ๋ฐฉํ–ฅ). ์•„๋ž˜ ์ฝ”๋“œ๋Š” โžŠbaz ํ•จ์ˆ˜ ํ˜ธ์ถœ → โž‹bar ํ•จ์ˆ˜ ํ˜ธ์ถœ → โžŒfoo ํ•จ์ˆ˜ ํ˜ธ์ถœ → โžfoo ํ•จ์ˆ˜์—์„œ ์—๋Ÿฌ throw… ์ˆœ์„œ๋กœ ์‹คํ–‰๋˜๊ณ , foo ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” ์ด์ „ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ(โž → โžŠ ํ˜ธ์ถœ์ž ๋ฐฉํ–ฅ)๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค.

const foo = () => {
  throw Error('foo์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ'); // โ‘ท
};

const bar = () => {
  foo(); // โ‘ถ
};

const baz = () => {
  bar(); // โ‘ต
};

try {
  baz(); // โ‘ด
} catch (err) {
  console.error(err);
}

// at foo (<anonymous>:2:9) - ์—๋Ÿฌ๋ฅผ throw ํ•œ ๊ณณ
// at bar (<anonymous>:6:3) - foo ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at baz (<anonymous>:10:3) - bar ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:14:3 - baz ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

foo ํ•จ์ˆ˜์—์„œ throwํ•œ ์—๋Ÿฌ๋Š” bar baz์—์„œ ๋ชจ๋‘ catchํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ˜ธ์ถœ์ž๊นŒ์ง€ ์ „ํŒŒ๋œ ํ›„ ์ „์—ญ์—์„œ ์บ์น˜๋œ๋‹ค. ๋งŒ์•ฝ throwํ•œ ์—๋Ÿฌ๋ฅผ ์–ด๋””์—์„œ๋„ catchํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ํ”„๋กœ๊ทธ๋žจ์€ ๊ฐ•์ œ ์ข…๋ฃŒ๋œ๋‹ค.

 

์—๋Ÿฌ ์ „ํŒŒ ๋ฐฉํ–ฅ. foo ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋Š” ํ˜ธ์ถœ์ž ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค

 

๋น„๋™๊ธฐ ํ•จ์ˆ˜์ธ setTimeout๊ณผ ํ”„๋กœ๋ฏธ์Šค ํ›„์† ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ(then ๋“ฑ)์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ์ž๊ฐ€ ์—†๋‹ค. ์ด ๋‘˜์€ ํƒœ์Šคํฌ ํ(๋งคํฌ๋กœํƒœ์Šคํฌํ/๋งˆ์ดํฌ๋กœํƒœ์Šคํฌํ)์— ๋“ค์–ด๊ฐ€์„œ ์ฝœ์Šคํƒ์ด ๋น„์–ด์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•œ๋‹ค. ์ฝœ์Šคํƒ์ด ๋น„์—ˆ๋‹ค๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ์ฝœ์Šคํƒ์œผ๋กœ ํ‘ธ์‹œ๋œ ํ›„ ์‹คํ–‰๋œ๋‹ค. ์ด๋•Œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๋Š” ์ฝœ์Šคํƒ์˜ ๊ฐ€์žฅ ํ•˜๋‹จ์— ์œ„์น˜ํ•˜๊ฒŒ ๋œ๋‹ค(์ฝœ์Šคํƒ์ด ๋น„์–ด์ง„ ํ›„ ํ‘ธ์‹œ๋์œผ๋ฏ€๋กœ). ์ฆ‰ ์—๋Ÿฌ๋ฅผ ์ „ํŒŒํ•  ํ˜ธ์ถœ์ž๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.

try {
  setTimeout(() => {
    throw new Error('์—๋Ÿฌ ๋ฐœ์ƒ');
  }, 1000);
} catch (e) {
  console.error('์—๋Ÿฌ ์บ์น˜', e);
}

// Uncaught Error: ์—๋Ÿฌ ๋ฐœ์ƒ (์—๋Ÿฌ๋ฅผ ์บ์น˜ํ•˜์ง€ ๋ชปํ–ˆ์œผ๋ฏ€๋กœ Uncaught ์—๋Ÿฌ ๋ฐœ์ƒ)

 

๐Ÿ’ก ์—๋Ÿฌ๋Š” ํ˜ธ์ถœ์ž ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋˜์ง€๋งŒ `setTimeout`์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ฒƒ์€ `setTimeout` ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ๋ฐœ์ƒ ์‹œํ‚จ ์—๋Ÿฌ๋Š” `catch` ๋ธ”๋ก์—์„œ ์บ์น˜๋˜์ง€ ์•Š๋Š”๋‹ค. `setTimeout` ํ•จ์ˆ˜๊ฐ€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ž๊ฐ€ ๋˜๊ธฐ ์œ„ํ•ด์„  ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์ด์ „์— `setTimeout` ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ `setTimeout`์€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ์‹คํ–‰ ์ฆ‰์‹œ ์ข…๋ฃŒ๋ผ์„œ ์ฝœ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.

 

  1. setTimeout ํ•จ์ˆ˜ ํ˜ธ์ถœ → setTimeout ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ → ์ฝœ์Šคํƒ์— ํ‘ธ์‹œ๋œ ํ›„ ์‹คํ–‰
  2. setTimeout์€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ์ข…๋ฃŒ → ์ฝœ์Šคํƒ์—์„œ ์ œ๊ฑฐ
  3. ํƒ€์ด๋จธ(1์ดˆ) ๋งŒ๋ฃŒ → setTimeout์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ํƒœ์Šคํฌ ํ๋กœ ๋“ค์–ด๊ฐ„ ํ›„ ์ฝœ์Šคํƒ์ด ๋น„์–ด์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
  4. ์ฝœ์Šคํƒ์ด ๋น„์—ˆ๋‹ค๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ์ฝœ์Šคํƒ์œผ๋กœ ํ‘ธ์‹œ
  5. ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰ → ์—๋Ÿฌ throw → ํ˜ธ์ถœ์ž๊ฐ€ ์—†์œผ๋ฏ€๋กœ catch ๋ธ”๋ก์—์„œ ์—๋Ÿฌ ์บ์น˜ ๋ชปํ•จ

 

Promise ์˜ˆ์‹œ

ํ”„๋กœ๋ฏธ์Šค์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์™€ ํ›„์† ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—” try/catch๊ฐ€ ์•”๋ฌต์ ์œผ๋กœ ๊ฐ์‹ธ๊ณ  ์žˆ๋‹ค. reject๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์—๋Ÿฌ๋ฅผ throw ํ•˜๋ฉด .catch ํ›„์† ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ์—์„œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

new Promise((resolve, reject) => {
  throw new Error('์—๋Ÿฌ ๋ฐœ์ƒ');
}).catch((e) => console.error('์—๋Ÿฌ ์บ์น˜', e));

// ์—๋Ÿฌ ์บ์น˜ ์„ฑ๊ณต. reject๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ ๋„ .catch ๋ฉ”์„œ๋“œ์—์„œ ์—๋Ÿฌ๋ฅผ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค
// at <anonymous>:2:9 - ์—๋Ÿฌ๋ฅผ throwํ•œ ๊ณณ
// at new Promise (<anonymous>) - Promise์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ
// at <anonymous>:1:1 - Promise ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

ํ•˜์ง€๋งŒ ์•„๋ž˜ ์ฝ”๋“œ๋Š” ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค. ์œ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ setTimeout์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์ฝœ์Šคํƒ์— ๋“ค์–ด๊ฐ„ ์‹œ์ ์—” ํ˜ธ์ถœ์ž๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค(์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ฝœ์Šคํƒ์˜ ๊ฐ€์žฅ ํ•˜๋‹จ์— ์œ„์น˜).

new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('์—๋Ÿฌ ๋ฐœ์ƒ');
  }, 1000);
}).catch((e) => console.error('์—๋Ÿฌ ์บ์น˜', e));

// Uncaught Error: ์—๋Ÿฌ ๋ฐœ์ƒ (์—๋Ÿฌ๋ฅผ ์บ์น˜ํ•˜์ง€ ๋ชปํ–ˆ์œผ๋ฏ€๋กœ Uncaught ์—๋Ÿฌ ๋ฐœ์ƒ)
// setTimeout์˜ ํƒ€์ด๋จธ๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•ด๋„ ๊ฒฐ๊ณผ๋Š” ๋™์ผํ•˜๋‹ค

 

ํ”„๋กœ๋ฏธ์Šค ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ 2๋ฒˆ์งธ ์ธ์ž์ธ reject๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์—๋Ÿฌ๋Š” ํ˜ธ์ถœ์ž ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋˜๋ฏ€๋กœ reject ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊นŒ์ง€ ์ „ํŒŒ๋œ ํ›„ catch ๋ธ”๋ก์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('์—๋Ÿฌ ๋ฐœ์ƒ'));
  }, 1000);
}).catch((e) => console.error('์—๋Ÿฌ ์บ์น˜', e));

// ์—๋Ÿฌ ์บ์น˜ ์„ฑ๊ณต
// at <anonymous>:3:12 - reject ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ

 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•