Skip to content

Latest commit

ย 

History

History
2059 lines (1694 loc) ยท 61.1 KB

File metadata and controls

2059 lines (1694 loc) ยท 61.1 KB

clean-code-javascript

  • Updated date 2020.01.09
  • ํ˜„์žฌ ์›๋ฌธ์˜ 1c0b20a ๊นŒ์ง€ ๋ฐ˜์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชฉ์ฐจ

  1. ์†Œ๊ฐœ(Introduction)
  2. ๋ณ€์ˆ˜(Variables)
  3. ํ•จ์ˆ˜(Functions)
  4. ๊ฐ์ฒด์™€ ์ž๋ฃŒ๊ตฌ์กฐ(Objects and Data Structures)
  5. ํด๋ž˜์Šค(Classes)
  6. SOLID
  7. ํ…Œ์ŠคํŠธ(Testing)
  8. ๋™์‹œ์„ฑ(Concurrency)
  9. ์—๋Ÿฌ ์ฒ˜๋ฆฌ(Error Handling)
  10. ํฌ๋งทํŒ…(Formatting)
  11. ์ฃผ์„(Comments)
  12. ๋ฒˆ์—ญ(Translation)

์†Œ๊ฐœ(Introduction)

์ฝ”๋“œ๋ฅผ ์ฝ์„ ๋•Œ ์†Œ๋ฆฌ ์ง€๋ฅด๋Š” ์ˆซ์ž๋กœ ์†Œํ”„ํŠธ์›จ์–ด ํ’ˆ์งˆ์„ ์ถ”์ •ํ•˜๋Š” ์œ ๋จธ ์‚ฌ์ง„

์ด ๊ธ€์€ ์†Œํ”„ํŠธ์›จ์–ด ๋ฐฉ๋ฒ•๋ก ์— ๊ด€ํ•œ ์ฑ…๋“ค ์ค‘ Robert C. Martin's์˜ ์ฑ…์ธ Clean Code์— ์žˆ๋Š” ๋‚ด์šฉ์„ JavaScript ์–ธ์–ด์— ์ ์šฉ์‹œ์ผœ ์ ์€ ๊ธ€ ์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์€ ๋‹จ์ˆœํžˆ Style Guide๊ฐ€ ์•„๋‹ˆ๋ผ JavaScript๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ์ฝ๊ธฐ ์‰ฝ๊ณ , ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ ๋ฆฌํŒฉํ† ๋ง ๊ฐ€๋Šฅํ•˜๊ฒŒ๋” ์ž‘์„ฑํ•˜๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

์—ฌ๊ธฐ ์žˆ๋Š” ๋ชจ๋“  ์›์น™์ด ์—„๊ฒฉํžˆ ์ง€์ผœ์ ธ์•ผํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ, ๋ณดํŽธ์ ์œผ๋กœ ํ†ต์šฉ๋˜๋Š” ์›์น™์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ๋“ค์€ ์ง€์นจ์ผ ๋ฟ์ด๋ฉฐ Clean Code์˜ ์ €์ž๊ฐ€ ์ˆ˜๋…„๊ฐ„ ๊ฒฝํ—˜ํ•œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ •๋ฆฌํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

ํ•œ๊ฐ€์ง€ ๋” ๋ง๋ถ™์ด์ž๋ฉด, ์ด ์›์น™๋“ค์„ ์•Œ๊ฒŒ๋œ๋‹คํ•ด์„œ ๋‹น์žฅ ๋” ๋‚˜์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์‹ค์ˆ˜๋ฅผ ํ•˜์ง€ ์•Š๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํ›Œ๋ฅญํ•œ ๋„์ž๊ธฐ๋“ค์ด ์ฒ˜์Œ์—” ๋ง๋ž‘ํ•œ ์ ํ† ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋“ฏ์ด ๋ชจ๋“  ์ฝ”๋“œ๋“ค์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹น์‹ ์€ ํŒ€์›๋“ค๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ทฐํ•˜๋ฉฐ ์ ์  ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ์ฒ˜์Œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ๊ณ ์น  ๋•Œ ์ ˆ๋Œ€๋กœ ์ž์‹ ์„ ์งˆํƒ€ํ•˜์ง€ ๋งˆ์„ธ์š”. ๋Œ€์‹  ์ฝ”๋“œ๋ฅผ ๋ถ€์ˆ˜๊ณ  ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ์„ธ์š”!

๋ณ€์ˆ˜(Variables)

์˜๋ฏธ์žˆ๊ณ  ๋ฐœ์Œํ•˜๊ธฐ ์‰ฌ์šด ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

const yyyymmdstr = moment().format('YYYY/MM/DD');

์ข‹์€ ์˜ˆ:

const currentDate = moment().format('YYYY/MM/DD');

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋™์ผํ•œ ์œ ํ˜•์˜ ๋ณ€์ˆ˜์— ๋™์ผํ•œ ์–ดํœ˜๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

getUserInfo();
getClientData();
getCustomerRecord();

์ข‹์€ ์˜ˆ:

getUser();

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ฒ€์ƒ‰๊ฐ€๋Šฅํ•œ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์„ธ์š”

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

์•ˆ์ข‹์€ ์˜ˆ:

// ๋Œ€์ฒด 86400000 ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š” ๊ฑธ๊นŒ์š”?
setTimeout(blastOff, 86400000);

์ข‹์€ ์˜ˆ

// ๋Œ€๋ฌธ์ž๋กœ `const` ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜์„ธ์š”
const MILLISECONDS_IN_A_DAY = 86400000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);

โฌ† ์ƒ๋‹จ์œผ๋กœ

์˜๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ณ€์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•˜์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]);

์ข‹์€ ์˜ˆ:

const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ž์‹ ๋งŒ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ์ž‘๋ช…์„ ํ”ผํ•˜์„ธ์š”

๋ช…์‹œ์ ์ธ ๊ฒƒ์ด ์•”์‹œ์ ์ธ ๊ฒƒ๋ณด๋‹ค ์ข‹์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

const locations = ['์„œ์šธ', '์ธ์ฒœ', '์ˆ˜์›'];
locations.forEach(l => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // ์ž ๊น, `l`์€ ๋˜ ๋ญ˜๊นŒ์š”?
  dispatch(l);
});

์ข‹์€ ์˜ˆ:

const locations = ['์„œ์šธ', '์ธ์ฒœ', '์ˆ˜์›'];
locations.forEach(location => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋ฌธ๋งฅ์ƒ ํ•„์š”์—†๋Š” ๊ฒƒ๋“ค์„ ์“ฐ์ง€ ๋งˆ์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

const Car = {
  carMake: 'BMW',
  carModel: 'M3',
  carColor: 'ํŒŒ๋ž€์ƒ‰'
};

function paintCar(car) {
  car.carColor = '๋นจ๊ฐ„์ƒ‰';
}

์ข‹์€ ์˜ˆ:

const Car = {
  make: 'BMW',
  model: 'M3',
  color: 'ํŒŒ๋ž€์ƒ‰'
};

function paintCar(car) {
  car.color = '๋นจ๊ฐ„์ƒ‰';
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ธฐ๋ณธ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ short circuiting ํŠธ๋ฆญ์ด๋‚˜ ์กฐ๊ฑด๋ฌธ ๋ณด๋‹ค ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค

๊ธฐ๋ณธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ข…์ข… short circuiting ํŠธ๋ฆญ๋ณด๋‹ค ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ undefined์ผ๋•Œ๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. '', "", false, null, 0, NaN ๊ฐ™์€ falsyํ•œ ๊ฐ’๋“ค์€ ๊ธฐ๋ณธ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function createMicrobrewery(name) {
  const breweryName = name || 'Hipster Brew Co.';
  // ...
}

์ข‹์€ ์˜ˆ:

function createMicrobrewery(name = 'Hipster Brew Co.') {
  // ...
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ•จ์ˆ˜(Functions)

ํ•จ์ˆ˜ ์ธ์ž๋Š” 2๊ฐœ ์ดํ•˜๊ฐ€ ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค

๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œ ํ•˜๋Š” ๊ฒƒ์€ ํ•จ์ˆ˜ ํ…Œ์ŠคํŒ…์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ 3๊ฐœ ์ด์ƒ์ผ ๊ฒฝ์šฐ์—” ํ…Œ์ŠคํŠธ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๊ณ  ๊ฐ๊ธฐ ๋‹ค๋ฅธ ์ธ์ˆ˜๋“ค๋กœ ์—ฌ๋Ÿฌ ์‚ฌ๋ก€๋“ค์„ ํ…Œ์ŠคํŠธ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

1๊ฐœ๋‚˜ 2๊ฐœ์˜ ์ธ์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ์ผ€์ด์Šค์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  3๊ฐœ์˜ ์ธ์ž๋Š” ๊ฐ€๋Šฅํ•œ ํ”ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ๋‹ค๋ฉด ํ†ตํ•ฉ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด 2๊ฐœ ์ด์ƒ์˜ ์ธ์ž๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ทธ ํ•จ์ˆ˜์—๊ฒŒ ๋„ˆ๋ฌด ๋งŽ์€ ์—ญํ• ์„ ํ•˜๊ฒŒ ๋งŒ๋“  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ๋ผ๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ƒ์œ„ ๊ฐ์ฒด๋Š” 1๊ฐœ์˜ ์ธ์ž๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

JavaScript๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋งŽ์€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์—†์ด ๋ฐ”๋กœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋‹น์‹ ์ด ๋งŒ์•ฝ ๋งŽ์€ ์ธ์ž๋“ค์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•จ์ˆ˜๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ์†์„ฑ์„ ์ข€๋” ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ ์œ„ํ•ด์„œ es6์˜ ๋น„๊ตฌ์กฐํ™”(destructuring) ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ์ด ๊ตฌ๋ฌธ์—๋Š” ๋ช‡๊ฐ€์ง€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์–ด๋–ค ์‚ฌ๋žŒ์ด ๊ทธ ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ณ(์ธ์ž์˜ ํƒ€์ž…, ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์˜ ํƒ€์ž… ๋“ฑ)๋ฅผ ๋ณผ ๋•Œ ์–ด๋–ค ์†์„ฑ์ด ์‚ฌ์šฉ๋˜๋Š”์ง€ ์ฆ‰์‹œ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๋˜ํ•œ ๋น„๊ตฌ์กฐํ™”๋Š” ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ์ธ์ˆ˜ ๊ฐ์ฒด์˜ ์ง€์ •๋œ ๊ธฐ๋ณธํƒ€์ž… ๊ฐ’์„ ๋ณต์ œํ•˜๋ฉฐ ์ด๋Š” ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๊ฐ€ ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์ธ์ˆ˜ ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ๋น„๊ตฌ์กฐํ™”๋œ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ๋ณต์ œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  3. Linter๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉํ•˜์ง€์•Š๋Š” ์ธ์ž์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ•ด์ฃผ๊ฑฐ๋‚˜ ๋น„๊ตฌ์กฐํ™” ์—†์ด ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์—†๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function createMenu(title, body, buttonText, cancellable) {
  // ...
}

์ข‹์€ ์˜ˆ:

function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}

createMenu({
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true
});

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ•จ์ˆ˜๋Š” ํ•˜๋‚˜์˜ ํ–‰๋™๋งŒ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

์ด๊ฒƒ์€ ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด๋ง์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ทœ์น™์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ 1๊ฐœ ์ด์ƒ์˜ ํ–‰๋™์„ ํ•œ๋‹ค๋ฉด ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋„, ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ๋„, ์ดํ•ดํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ํ•˜๋‚˜์˜ ํ•จ์ˆ˜์— ํ•˜๋‚˜์˜ ํ–‰๋™์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค๋ฉด ํ•จ์ˆ˜๋Š” ์ข€ ๋” ๊ณ ์น˜๊ธฐ ์‰ฌ์›Œ์ง€๊ณ  ์ฝ”๋“œ๋“ค์€ ์ฝ๊ธฐ ์‰ฌ์›Œ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŽ์€ ์›์น™๋“ค ์ค‘ ์ด๊ฒƒ๋งŒ ์•Œ์•„๊ฐ„๋‹ค ํ•˜๋”๋ผ๋„ ๋‹น์‹ ์€ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์„ ์•ž์„ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function emailClients(clients) {
  clients.forEach(client => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

์ข‹์€ ์˜ˆ:

function emailClients(clients) {
  clients
    .filter(isClientActive)
    .forEach(email);
}

function isClientActive(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ•จ์ˆ˜๋ช…์€ ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

์•ˆ์ข‹์€ ์˜ˆ:

function AddToDate(date, month) {
  // ...
}

const date = new Date();

// ๋ญ˜ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฑด์ง€ ์ด๋ฆ„๋งŒ ๋ณด๊ณ  ์•Œ์•„๋‚ด๊ธฐ ํž˜๋“ญ๋‹ˆ๋‹ค.
AddToDate(date, 1);

์ข‹์€ ์˜ˆ:

function AddMonthToDate(date, month) {
  // ...
}

const date = new Date();
AddMonthToDate(date, 1);

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ•จ์ˆ˜๋Š” ๋‹จ์ผ ํ–‰๋™์„ ์ถ”์ƒํ™” ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

์ถ”์ƒํ™”๋œ ์ด๋ฆ„์ด ์—ฌ๋Ÿฌ ์˜๋ฏธ๋ฅผ ๋‚ดํฌํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๊ทธ ํ•จ์ˆ˜๋Š” ๋„ˆ๋ฌด ๋งŽ์€ ์ผ์„ ํ•˜๊ฒŒ๋” ์„ค๊ณ„๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋“ค์„ ๋‚˜๋ˆ„์–ด์„œ ์žฌ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“œ์„ธ์š”.

์•ˆ์ข‹์€ ์˜ˆ:

function parseBetterJSAlternative(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach(REGEX => {
    statements.forEach(statement => {
      // ...
    });
  });

  const ast = [];
  tokens.forEach(token => {
    // lex...
  });

  ast.forEach(node => {
    // parse...
  });
}

์ข‹์€ ์˜ˆ:

function tokenize(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach(REGEX => {
    statements.forEach(statement => {
      tokens.push( /* ... */ );
    });
  });

  return tokens;
}

function lexer(tokens) {
  const ast = [];
  tokens.forEach(token => {
    ast.push( /* ... */ );
  });

  return ast;
}

function parseBetterJSAlternative(code) {
  const tokens = tokenize(code);
  const ast = lexer(tokens);
  ast.forEach(node => {
    // parse...
  });
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”

์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ธฐ์œ„ํ•ด ์ตœ์„ ์„ ๋‹คํ•˜์„ธ์š”. ์ค‘๋ณต๋œ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ์–ด๋–ค ๋กœ์ง์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ์ผ์ด ์ƒ๊ฒผ์„ ๋•Œ ์ˆ˜์ • ํ•ด์•ผํ•  ์ฝ”๋“œ๊ฐ€ ํ•œ ๊ณณ ์ด์ƒ์ด๋ผ๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.

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

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

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

์•ˆ์ข‹์€ ์˜ˆ:

function showDeveloperList(developers) {
  developers.forEach(developers => {
    const expectedSalary = developer.calculateExpectedSalary();
    const experience = developer.getExperience();
    const githubLink = developer.getGithubLink();
    const data = {
      expectedSalary,
      experience,
      githubLink
    };

    render(data);
  });
}

function showManagerList(managers) {
  managers.forEach(manager => {
    const expectedSalary = manager.calculateExpectedSalary();
    const experience = manager.getExperience();
    const portfolio = manager.getMBAProjects();
    const data = {
      expectedSalary,
      experience,
      portfolio
    };

    render(data);
  });
}

์ข‹์€ ์˜ˆ:

function showEmployeeList(employees) {
  employees.forEach((employee) => {
    const expectedSalary = employee.calculateExpectedSalary();
    const experience = employee.getExperience();

    let portfolio = employee.getGithubLink();

    if (employee.type === 'manager') {
      portfolio = employee.getMBAProjects();
    }

    const data = {
      expectedSalary,
      experience,
      portfolio
    };

    render(data);
  });
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

Object.assign์„ ์‚ฌ์šฉํ•ด ๊ธฐ๋ณธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

const menuConfig = {
  title: null,
  body: 'Bar',
  buttonText: null,
  cancellable: true
};

function createMenu(config) {
  config.title = config.title || 'Foo';
  config.body = config.body || 'Bar';
  config.buttonText = config.buttonText || 'Baz';
  config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
}

createMenu(menuConfig);

์ข‹์€ ์˜ˆ:

const menuConfig = {
  title: 'Order',
  // ์œ ์ €๊ฐ€ 'body' key์˜ value๋ฅผ ์ •ํ•˜์ง€ ์•Š์•˜๋‹ค.
  buttonText: 'Send',
  cancellable: true
};

function createMenu(config) {
  config = Object.assign({
    title: 'Foo',
    body: 'Bar',
    buttonText: 'Baz',
    cancellable: true
  }, config);

  // config๋Š” ์ด์ œ ๋‹ค์Œ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}

createMenu(menuConfig);

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”

ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ํ•œ๊ฐ€์ง€ ์ด์ƒ์˜ ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. boolean ๊ธฐ๋ฐ˜์œผ๋กœ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋‚˜๋‰œ๋‹ค๋ฉด ํ•จ์ˆ˜๋ฅผ ๋ถ„๋ฆฌํ•˜์„ธ์š”.

์•ˆ์ข‹์€ ์˜ˆ:

function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}

์ข‹์€ ์˜ˆ:

function createFile(name) {
  fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ํ”ผํ•˜์„ธ์š” (part 1)

ํ•จ์ˆ˜๋Š” ๊ฐ’์„ ๋ฐ›์•„์„œ ์–ด๋–ค ์ผ์„ ํ•˜๊ฑฐ๋‚˜ ๊ฐ’์„ ๋ฆฌํ„ดํ•  ๋•Œ ์‚ฌ์ด๋“œ ์ดํŒฉํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. ์‚ฌ์ด๋“œ ์ดํŒฉํŠธ๋Š” ํŒŒ์ผ์— ์“ฐ์—ฌ์งˆ ์ˆ˜๋„ ์žˆ๊ณ , ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‹ค์ˆ˜๋กœ ๋ชจ๋“  ๋ˆ์„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹น์‹ ์€ ๋•Œ๋•Œ๋กœ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์ด๋“œ ์ดํŒฉํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๊นŒ ๋“ค์—ˆ๋˜ ์˜ˆ๋“ค ์ค‘ ํ•˜๋‚˜์ธ ํŒŒ์ผ์ž‘์„ฑ์„ ํ•  ๋•Œ์™€ ๊ฐ™์ด ๋ง์ด์ฃ . ์ด ๋•Œ ์—ฌ๋Ÿฌ๋ถ„์ด ํ•ด์•ผํ•  ์ผ์€ ํŒŒ์ผ ์ž‘์„ฑ์„ ํ•˜๋Š” ํ•œ ๊ฐœ์˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ์ผ ์ž…๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ์กด์žฌํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ํ•˜๋‚˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

์•ˆ์ข‹์€ ์˜ˆ:

// ์•„๋ž˜ ํ•จ์ˆ˜์— ์˜ํ•ด ์ฐธ์กฐ๋˜๋Š” ์ „์—ญ ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.
// ์ด ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋˜ ํ•˜๋‚˜์˜ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์„ธ์š”. ์ด์ œ ์ด ๋ณ€์ˆ˜๋Š” ๋ฐฐ์—ด์ด ๋  ๊ฒƒ์ด๊ณ , ํ”„๋กœ๊ทธ๋žจ์„ ๋ง๊ฐ€๋œจ๋ฆฌ๊ฒ ์ฃ .
let name = 'Ryan McDermott';

function splitIntoFirstAndLastName() {
  name = name.split(' ');
}

splitIntoFirstAndLastName();

console.log(name); // ['Ryan', 'McDermott'];

์ข‹์€ ์˜ˆ:

function splitIntoFirstAndLastName(name) {
  return name.split(' ');
}

const name = 'Ryan McDermott';
const newName = splitIntoFirstAndLastName(name);

console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];

โฌ† ์ƒ๋‹จ์œผ๋กœ

์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ํ”ผํ•˜์„ธ์š” (part 2)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๊ธฐ๋ณธํƒ€์ž… ์ž๋ฃŒํ˜•์€ ๊ฐ’์„ ์ „๋‹ฌํ•˜๊ณ  ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ์ฐธ์กฐ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ๋ฅผ ํ•œ๋ฒˆ ์‚ดํŽด๋ด…์‹œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํ•จ์ˆ˜๋Š” ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐฐ์—ด์— ๋ณ€ํ™”๋ฅผ ์ฃผ๋ฉฐ ์ด ๋ณ€ํ™”๋Š” ๊ตฌ๋งค๋ชฉ๋ก์— ์–ด๋–ค ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ ๊ฐ™์€ ๊ฒƒ์„ ๋งํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•˜๋Š” ์–ด๋А ๋‹ค๋ฅธ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด๋Ÿฌํ•œ ์ถ”๊ฐ€์— ์˜ํ–ฅ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ข‹์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์•ˆ์ข‹์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ˆ์ข‹์€ ์˜ˆ๋ฅผ ํ•œ๋ฒˆ ์ƒ์ƒํ•ด๋ด…์‹œ๋‹ค.

์œ ์ €๊ฐ€ ๊ตฌ๋งคํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๊ตฌ๋งค ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ƒ์„ฑํ•˜๊ณ  ์„œ๋ฒ„์— ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐฐ์—ด์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ์ข‹์ง€์•Š์•„์„œ ๊ตฌ๋งค ํ•จ์ˆ˜๋Š” ๋‹ค์‹œํ•œ๋ฒˆ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ, ์‚ฌ์šฉ์ž๊ฐ€ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์‹ค์ˆ˜๋กœ ์›ํ•˜์ง€ ์•Š๋Š” ์ƒํ’ˆ์˜ "์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€" ๋ฒ„ํŠผ์„ ์‹ค์ˆ˜๋กœ ํด๋ฆญํ•˜๋ฉด ์–ด๋–ป๊ฒŒ๋ ๊นŒ์š”? ์‹ค์ˆ˜๊ฐ€ ์žˆ๊ณ ๋‚œ ๋’ค, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์‹œ์ž‘๋˜๋ฉด ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€ ํ•จ์ˆ˜ ๋•Œ๋ฌธ์— ์‹ค์ˆ˜๋กœ ๋ณ€๊ฒฝ๋œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐฐ์—ด์„ ์„œ๋ฒ„์— ๋ณด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€๋Š” ํ•ญ์ƒ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐฐ์—ด์„ ๋ณต์ œํ•˜์—ฌ ์ˆ˜์ •ํ•˜๊ณ  ๋ณต์ œ๋ณธ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒํ•˜๋ฉด ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๊ณ ์žˆ๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๊ฐ€ ๋‹ค๋ฅธ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ฒŒ๋ฉ๋‹ˆ๋‹ค.

์ด ์ ‘๊ทผ๋ฒ•์—๋Œ€ํ•ด ๋งํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ด ๋‘๊ฐ€์ง€ ์žˆ์Šต๋‹ˆ๋‹ค.

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

Bad:

const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};

Good:

const addItemToCart = (cart, item) => {
  return [...cart, { item, date : Date.now() }];
};

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ „์—ญ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”

์ „์—ญ ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ JavaScript์—์„œ ๋‚˜์œ ๊ด€ํ–‰์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๊ณผ์˜ ์ถฉ๋Œ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๊ณ , ๋‹น์‹ ์˜ API๋ฅผ ์“ฐ๋Š” ์œ ์ €๋“ค์€ ์šด์˜ํ™˜๊ฒฝ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๋ฌธ์ œ๋ฅผ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ œ๋ฅผ ํ•˜๋‚˜ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค. JavaScript์˜ ๋„ค์ดํ‹ฐ๋ธŒ Array ๋ฉ”์„œ๋“œ๋ฅผ ํ™•์žฅํ•˜์—ฌ ๋‘ ๋ฐฐ์—ด ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜์žˆ๋Š” diff ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ์š”? ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ Array.prototype์— ์“ธ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋˜‘๊ฐ™์€ ์ผ์„ ์‹œ๋„ํ•œ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์ถฉ๋Œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ diff ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒซ๋ฒˆ์งธ ์š”์†Œ์™€ ๋งˆ์ง€๋ง‰ ์š”์†Œ์˜ ์ฐจ์ด์ ์„ ์ฐพ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์ด๊ฒƒ์ด ๊ทธ๋ƒฅ ES2015/ES6์˜ classes๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ „์—ญ Array๋ฅผ ์ƒ์†ํ•ด๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ๋‚˜์€ ์ด์œ ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter(elem => !hash.has(elem));
};

์ข‹์€ ์˜ˆ:

class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter(elem => !hash.has(elem));
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ๋ณด๋‹ค ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€ํ–ฅํ•˜์„ธ์š”

JavaScript๋Š” Haskell์ฒ˜๋Ÿผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ์•„๋‹ˆ์ง€๋งŒ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ํ˜• ์–ธ์–ด๋Š” ๋” ๊น”๋”ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด๋ณด์„ธ์š”.

์•ˆ์ข‹์€ ์˜ˆ:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];

let totalOutput = 0;

for (let i = 0; i < programmerOutput.length; i++) {
  totalOutput += programmerOutput[i].linesOfCode;
}

์ข‹์€ ์˜ˆ:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];

const totalOutput = programmerOutput
  .map(programmer => programmer.linesOfCode)
  .reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE);

โฌ† ์ƒ๋‹จ์œผ๋กœ

์กฐ๊ฑด๋ฌธ์„ ์บก์Аํ™” ํ•˜์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

if (fsm.state === 'fetching' && isEmpty(listNode)) {
  // ...
}

์ข‹์€ ์˜ˆ:

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === 'fetching' && isEmpty(listNode);
}

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋ถ€์ •์กฐ๊ฑด๋ฌธ์„ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”

์•ˆ์ข‹์€ ์˜ˆ:

function isDOMNodeNotPresent(node) {
  // ...
}

if (!isDOMNodeNotPresent(node)) {
  // ...
}

์ข‹์€ ์˜ˆ:

function isDOMNodePresent(node) {
  // ...
}

if (isDOMNodePresent(node)) {
  // ...
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์กฐ๊ฑด๋ฌธ ์ž‘์„ฑ์„ ํ”ผํ•˜์„ธ์š”

์กฐ๊ฑด๋ฌธ ์ž‘์„ฑ์„ ํ”ผํ•˜๋ผ๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด ์–˜๊ธฐ๋ฅผ ์ฒ˜์Œ ๋“ฃ๋Š” ์‚ฌ๋žŒ๋“ค์€ ๋Œ€๋ถ€๋ถ„ "If๋ฌธ ์—†์ด ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ๋‚˜์š”?"๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹คํ˜•์„ฑ์„ ์ด์šฉํ•œ๋‹ค๋ฉด ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘๋ฒˆ์งธ ์งˆ๋ฌธ์€ ๋ณดํ†ต "๋„ค ์ข‹๋„ค์š” ๊ทผ๋ฐ ๋‚ด๊ฐ€ ์™œ ๊ทธ๋ ‡๊ฒŒ ํ•ด์•ผํ•˜๋‚˜์š”?"์ด์ฃ . ๊ทธ์— ๋Œ€ํ•œ ๋Œ€๋‹ต์€, ์•ž์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ณต๋ถ€ํ–ˆ๋˜ clean code ์ปจ์…‰์— ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋Š” ๋‹จ ํ•˜๋‚˜์˜ ์ผ๋งŒ ์ˆ˜ํ–‰ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค์— if๋ฌธ์„ ์“ด๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ๊ทธ ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๊ฐ€ ํ•œ๊ฐ€์ง€ ์ด์ƒ์˜ ์ผ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ธฐ์–ตํ•˜์„ธ์š”, ํ•˜๋‚˜์˜ ํ•จ์ˆ˜๋Š” ๋”ฑ ํ•˜๋‚˜์˜ ์ผ๋งŒ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class Airplane {
  // ...
  getCruisingAltitude() {
    switch (this.type) {
      case '777':
        return this.getMaxAltitude() - this.getPassengerCount();
      case 'Air Force One':
        return this.getMaxAltitude();
      case 'Cessna':
        return this.getMaxAltitude() - this.getFuelExpenditure();
    }
  }
}

์ข‹์€ ์˜ˆ:

class Airplane {
  // ...
}

class Boeing777 extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getPassengerCount();
  }
}

class AirForceOne extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude();
  }
}

class Cessna extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getFuelExpenditure();
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํƒ€์ž…-์ฒดํ‚น์„ ํ”ผํ•˜์„ธ์š” (part 1)

JavaScript๋Š” ํƒ€์ž…์ด ์ •ํ•ด์ ธ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋‹น์‹ ์˜ ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ค ํƒ€์ž…์˜ ์ธ์ž๋“  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ JavaScript์˜ ์ž์œ ๋กœ์›€ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์—ˆ๊ณ  ์ด ๋•Œ๋ฌธ์— ๋‹น์‹ ์˜ ํ•จ์ˆ˜์— ํƒ€์ž…-์ฒดํ‚น์„ ์‹œ๋„ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํƒ€์ž…-์ฒดํ‚น ๋ง๊ณ ๋„ ์ด๋Ÿฌํ•œ ํ™”๋ฅผ ํ”ผํ•  ๋งŽ์€ ๋ฐฉ๋ฒ•๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ์ผ๊ด€์„ฑ ์žˆ๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function travelToTexas(vehicle) {
  if (vehicle instanceof Bicycle) {
    vehicle.pedal(this.currentLocation, new Location('texas'));
  } else if (vehicle instanceof Car) {
    vehicle.drive(this.currentLocation, new Location('texas'));
  }
}

์ข‹์€ ์˜ˆ:

function travelToTexas(vehicle) {
  vehicle.move(this.currentLocation, new Location('texas'));
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํƒ€์ž…-์ฒดํ‚น์„ ํ”ผํ•˜์„ธ์š” (part 2)

๋‹น์‹ ์ด ๋ฌธ์ž์—ด, ์ •์ˆ˜, ๋ฐฐ์—ด๋“ฑ ๊ธฐ๋ณธ ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉํ•˜๊ณ  ๋‹คํ˜•์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ ์—ฌ์ „ํžˆ ํƒ€์ž…-์ฒดํ‚น์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ๋А๊ปด์ง„๋‹ค๋ฉด TypeScript๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. TypeScript๋Š” ํ‘œ์ค€ JavaScript ๊ตฌ๋ฌธ์— ์ •์  ํƒ€์ž…์„ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์ผ๋ฐ˜ JavaScript์˜ ๋Œ€์•ˆ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ์— ์ข‹์Šต๋‹ˆ๋‹ค. JavaScript์—์„œ ํƒ€์ž…-์ฒดํ‚น์„ ํ•  ๋•Œ ๋ฌธ์ œ์ ์€ ๊ฐ€์งœ type-safety ๋ฅผ ์–ป๊ธฐ์œ„ํ•ด ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŽ์€ ์ฃผ์„์„ ๋‹ฌ์•„์•ผํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. JavaScript๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ• ๋• ๊น”๋”ํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ์ข‹์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ์•ผํ•˜๋ฉฐ ์ข‹์€ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๊ธฐ ์‹ซ๋‹ค๋ฉด ๊ทธ๋ƒฅ TypeScript(์ด๊ฑด ์ œ๊ฐ€ ๋งํ–ˆ๋“ฏ์ด, ์ข‹์€ ๋Œ€์ฒด์žฌ์ž…๋‹ˆ๋‹ค!)๋ฅผ ์“ฐ์„ธ์š”.

์•ˆ์ข‹์€ ์˜ˆ:

function combine(val1, val2) {
  if (typeof val1 === 'number' && typeof val2 === 'number' ||
      typeof val1 === 'string' && typeof val2 === 'string') {
    return val1 + val2;
  }
  
  throw new Error('Must be of type String or Number');
}

์ข‹์€ ์˜ˆ:

function combine(val1, val2) {
  return val1 + val2;
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ณผ๋„ํ•œ ์ตœ์ ํ™”๋ฅผ ์ง€์–‘ํ•˜์„ธ์š”

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

์•ˆ์ข‹์€ ์˜ˆ:

// ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฒฝ์šฐ ์บ์‹œ๋˜์ง€ ์•Š์€ `list.length`๋ฅผ ํ†ตํ•œ ๋ฐ˜๋ณต๋ฌธ์€ ๋†’์€ ์ฝ”์ŠคํŠธ๋ฅผ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค.
// ๊ทธ ์ด์œ ๋Š” `list.length`๋ฅผ ๋งค๋ฒˆ ๊ณ„์‚ฐํ•ด์•ผ๋งŒ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ, ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ์ด๊ฒƒ์ด ์ตœ์ ํ™” ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}

์ข‹์€ ์˜ˆ:

for (let i = 0; i < list.length; i++) {
  // ...
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ฃฝ์€ ์ฝ”๋“œ๋ฅผ ์ง€์šฐ์„ธ์š”

์ฃฝ์€ ์ฝ”๋“œ๋Š” ์ค‘๋ณต๋œ ์ฝ”๋“œ ๋งŒํผ์ด๋‚˜ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฃฝ์€ ์ฝ”๋“œ๋Š” ๋‹น์‹ ์˜ ์ฝ”๋“œ์— ๋‚จ์•„์žˆ์„ ์–ด๋– ํ•œ ์ด์œ ๋„ ์—†์Šต๋‹ˆ๋‹ค. ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ทธ ์ฝ”๋“œ๋Š” ์ง€์šฐ์„ธ์š”! ๊ทธ ์ฝ”๋“œ๊ฐ€ ์—ฌ์ „ํžˆ ํ•„์š”ํ•ด๋„ ๊ทธ ์ฝ”๋“œ๋Š” ๋ฒ„์ „ ํžˆ์Šคํ† ๋ฆฌ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋‚จ์•„์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function oldRequestModule(url) {
  // ...
}

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');

์ข‹์€ ์˜ˆ:

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ฐ์ฒด์™€ ์ž๋ฃŒ๊ตฌ์กฐ(Objects and Data Structures)

getter์™€ setter๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”

JavaScript๋Š” ์ธํ„ฐํŽ˜์ด์Šค์™€ ํƒ€์ž…์„ ๊ฐ€์ง€๊ณ ์žˆ์ง€ ์•Š๊ณ  ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ์ ์šฉํ•˜๊ธฐ๊ฐ€ ํž˜๋“ญ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด public์ด๋‚˜ private๊ฐ™์€ ํ‚ค์›Œ๋“œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— getter ๋ฐ setter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์ฐพ๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋‚ซ์Šต๋‹ˆ๋‹ค. "์™œ์š”?"๋ผ๊ณ  ๋ฌผ์œผ์‹ค ์ˆ˜๋„ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค. ์™œ ๊ทธ๋Ÿฐ์ง€์— ๋Œ€ํ•ด์„œ ๋ช‡ ๊ฐ€์ง€ ์ด์œ ๋ฅผ ๋‘์„œ์—†์ด ์ ์–ด๋ดค์Šต๋‹ˆ๋‹ค.

  • ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์–ป๋Š” ๊ฒƒ ์ด์ƒ์˜ ๋งŽ์€ ๊ฒƒ์„ ํ•˜๊ณ ์‹ถ์„ ๋•Œ, ์ฝ”๋“œ์—์„œ ๋ชจ๋“  ์ ‘๊ทผ์ž๋ฅผ ์ฐพ์•„ ๋ฐ”๊พธ๊ณ  ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • setํ• ๋•Œ ๊ฒ€์ฆ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ฝ”๋“œ๋ฅผ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ๋‚ด๋ถ€์šฉ API๋ฅผ ์บก์Аํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • getting๊ณผ settingํ•  ๋•Œ ๋กœ๊ทธ๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์—๋Ÿฌ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„์—์„œ ๊ฐ์ฒด ์†์„ฑ์„ ๋ฐ›์•„์˜ฌ ๋•Œ lazy load ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function makeBankAccount() {
  // ...

  return {
    // ...
    balance: 0
  };
}

const account = makeBankAccount();
account.balance = 100;

์ข‹์€ ์˜ˆ:

function makeBankAccount() {
  // private์œผ๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜
  let balance = 0;

  // ์•„๋ž˜ return์„ ํ†ตํ•ด public์œผ๋กœ ์„ ์–ธ๋œ "getter"
  function getBalance() {
    return balance;
  }

  // ์•„๋ž˜ return์„ ํ†ตํ•ด public์œผ๋กœ ์„ ์–ธ๋œ "setter"
  function setBalance(amount) {
    // ... balance๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์ „ ๊ฒ€์ฆ๋กœ์ง
    balance = amount;
  }

  return {
    // ...
    getBalance,
    setBalance
  };
}

const account = makeBankAccount();
account.setBalance(100);

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ฐ์ฒด์— ๋น„๊ณต๊ฐœ ๋ฉค๋ฒ„๋ฅผ ๋งŒ๋“œ์„ธ์š”

ํด๋กœ์ ธ๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (ES5 ์ดํ•˜์—์„œ๋„)

์•ˆ์ข‹์€ ์˜ˆ:

const Employee = function(name) {
  this.name = name;
};

Employee.prototype.getName = function getName() {
  return this.name;
};

const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined

์ข‹์€ ์˜ˆ:

function makeEmployee(name) {
  return {
    getName() {
      return name;
    },
  };
}

const employee = makeEmployee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํด๋ž˜์Šค(Classes)

ES5์˜ ํ•จ์ˆ˜๋ณด๋‹ค ES2015/ES6์˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”

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

์•ˆ์ข‹์€ ์˜ˆ:

const Animal = function(age) {
  if (!(this instanceof Animal)) {
    throw new Error("Instantiate Animal with `new`");
  }
    
  this.age = age;
};

Animal.prototype.move = function() {};

const Mammal = function(age, furColor) {
  if (!(this instanceof Mammal)) {
    throw new Error("Instantiate Mammal with `new`");
  }

  Animal.call(this, age);
  this.furColor = furColor;
};

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};

const Human = function(age, furColor, languageSpoken) {
  if (!(this instanceof Human)) {
    throw new Error("Instantiate Human with `new`");
  }

  Mammal.call(this, age, furColor);
  this.languageSpoken = languageSpoken;
};

Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};

์ข‹์€ ์˜ˆ:

class Animal {
  constructor(age) {
    this.age = age;
  }

  move() { /* ... */ }
}

class Mammal extends Animal {
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  }

  liveBirth() { /* ... */ }
}

class Human extends Mammal {
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  }

  speak() { /* ... */ }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ•˜์„ธ์š”

JavaScript์—์„œ ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์€ ๋งค์šฐ ์œ ์šฉํ•œ ํŒจํ„ด์ด๋ฉฐ jQuery๋‚˜ Lodash๊ฐ™์€ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ด ํŒจํ„ด์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ด์œ ๋“ค๋กœ ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์„ ์“ฐ๋Š” ๊ฒƒ์„ ๊ถŒํ•˜๊ณ , ์‚ฌ์šฉํ•ด๋ณธ๋’ค ์–ผ๋งˆ๋‚˜ ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์กŒ๋Š”์ง€ ๊ผญ ํ™•์ธ ํ•ด๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ํด๋ž˜์Šค ํ•จ์ˆ˜์—์„œ ๋‹จ์ˆœํžˆ ๋ชจ๋“  ํ•จ์ˆ˜์˜ ๋์— 'this'๋ฅผ ๋ฆฌํ„ดํ•ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class Car {
  constructor() {
    this.make = 'Honda';
    this.model = 'Accord';
    this.color = 'white';
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car();
car.setColor('pink');
car.setMake('Ford');
car.setModel('F-150');
car.save();

์ข‹์€ ์˜ˆ:

class Car {
  constructor() {
    this.make = 'Honda';
    this.model = 'Accord';
    this.color = 'white';
  }

  setMake(make) {
    this.make = make;
    // ๋ฉ”๋ชจ: ์ฒด์ด๋‹์„ ์œ„ํ•ด this๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
    return this;
  }

  setModel(model) {
    this.model = model;
    // ๋ฉ”๋ชจ: ์ฒด์ด๋‹์„ ์œ„ํ•ด this๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
    return this;
  }

  setColor(color) {
    this.color = color;
    // ๋ฉ”๋ชจ: ์ฒด์ด๋‹์„ ์œ„ํ•ด this๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    // ๋ฉ”๋ชจ: ์ฒด์ด๋‹์„ ์œ„ํ•ด this๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
    return this;
  }
}

const car = new Car()
  .setColor('pink')
  .setMake('Ford')
  .setModel('F-150')
  .save();

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ƒ์†๋ณด๋‹จ ์กฐํ•ฉ(composition)์„ ์‚ฌ์šฉํ•˜์„ธ์š”

Gang of four์˜ Design Patterns์—์„œ ์œ ๋ช…ํ•œ ์ „๋žต์œผ๋กœ ๋‹น์‹ ์€ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์ƒ์†๋ณด๋‹ค๋Š” ์กฐํ•ฉ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์†์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด๋“๋ณด๋‹ค ์กฐํ•ฉ์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด๋“์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ์›์น™์˜ ์š”์ ์€ ๋‹น์‹ ์ด ๊ณ„์† ์ƒ์†์„ ์‚ฌ์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ ์ž ํ•  ๋•Œ, ๋งŒ์•ฝ ์กฐํ•ฉ์„ ์ด์šฉํ•˜๋ฉด ๋” ์ฝ”๋“œ๋ฅผ ์ž˜ ์งค ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•ด๋ณด๋ผ๋Š” ๊ฒƒ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋•Œ๋•Œ๋กœ๋Š” ์ด๊ฒƒ์ด ๋งž๋Š” ์ „๋žต์ด๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

"๊ทธ๋Ÿผ ๋Œ€์ฒด ์ƒ์†์„ ์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผ ๋˜๋Š” ๊ฑด๊ฐ€์š”?"๋ผ๊ณ  ๋ฌผ์–ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑด ๋‹น์‹ ์ด ์ง๋ฉดํ•œ ๋ฌธ์ œ ์ƒํ™ฉ์— ๋‹ฌ๋ ค์žˆ์ง€๋งŒ ์กฐํ•ฉ๋ณด๋‹ค ์ƒ์†์„ ์“ฐ๋Š”๊ฒŒ ๋” ์ข‹์„ ๋งŒํ•œ ์˜ˆ์‹œ๋ฅผ ๋ช‡ ๊ฐœ ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. ๋‹น์‹ ์˜ ์ƒ์†๊ด€๊ณ„๊ฐ€ "has-a" ๊ด€๊ณ„๊ฐ€ ์•„๋‹ˆ๋ผ "is-a" ๊ด€๊ณ„์ผ ๋•Œ (์‚ฌ๋žŒ->๋™๋ฌผ vs. ์œ ์ €->์œ ์ €์ •๋ณด)
  2. ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค์˜ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๋•Œ (์ธ๊ฐ„์€ ๋ชจ๋“  ๋™๋ฌผ์ฒ˜๋Ÿผ ์›€์ง์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)
  3. ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ํŒŒ์ƒ๋œ ํด๋ž˜์Šค ๋ชจ๋‘๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์‹ถ์„ ๋•Œ (์ด๋™์‹œ ๋ชจ๋“  ๋™๋ฌผ์ด ์†Œ๋น„ํ•˜๋Š” ์นผ๋กœ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์„ ๋•Œ)

์•ˆ์ข‹์€ ์˜ˆ:

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // ...
}

// ์ด ์ฝ”๋“œ๊ฐ€ ์•ˆ์ข‹์€ ์ด์œ ๋Š” Employees๊ฐ€ tax data๋ฅผ "๊ฐ€์ง€๊ณ " ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
// EmployeeTaxData๋Š” Employee ํƒ€์ž…์ด ์•„๋‹™๋‹ˆ๋‹ค.
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

์ข‹์€ ์˜ˆ:

class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }
  
  // ...
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

SOLID

๋‹จ์ผ ์ฑ…์ž„ ์›์น™ (Single Responsibility Principle, SRP)

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

์•ˆ์ข‹์€ ์˜ˆ:

class UserSettings {
  constructor(user) {
    this.user = user;
  }

  changeSettings(settings) {
    if (this.verifyCredentials()) {
      // ...
    }
  }

  verifyCredentials() {
    // ...
  }
}

์ข‹์€ ์˜ˆ:

class UserAuth {
  constructor(user) {
    this.user = user;
  }

  verifyCredentials() {
    // ...
  }
}


class UserSettings {
  constructor(user) {
    this.user = user;
    this.auth = new UserAuth(user);
  }

  changeSettings(settings) {
    if (this.auth.verifyCredentials()) {
      // ...
    }
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๊ฐœ๋ฐฉ/ํ์‡„ ์›์น™ (Open/Closed Principle, OCP)

Bertrand Meyer์— ๋ง์— ์˜ํ•˜๋ฉด "์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ์ฒด(ํด๋ž˜์Šค, ๋ชจ๋“ˆ, ํ•จ์ˆ˜ ๋“ฑ)๋Š” ํ™•์žฅ์„ ์œ„ํ•ด ๊ฐœ๋ฐฉ์ ์ด์–ด์•ผ ํ•˜๋ฉฐ ์ˆ˜์ •์‹œ์—” ํ์‡„์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค." ์ด๊ฒƒ์— ์˜๋ฏธ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”? ์ด ์›๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ .js ์†Œ์Šค ์ฝ”๋“œ ํŒŒ์ผ์„ ์—ด์–ด ์ˆ˜๋™์œผ๋กœ ์กฐ์ž‘ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ชจ๋“ˆ์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๋„๋ก ํ—ˆ์šฉํ•ด์•ผํ•œ๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'ajaxAdapter';
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'nodeAdapter';
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    if (this.adapter.name === 'ajaxAdapter') {
      return makeAjaxCall(url).then((response) => {
        // transform response and return
      });
    } else if (this.adapter.name === 'httpNodeAdapter') {
      return makeHttpCall(url).then((response) => {
        // transform response and return
      });
    }
  }
}

function makeAjaxCall(url) {
  // request and return promise
}

function makeHttpCall(url) {
  // request and return promise
}

์ข‹์€ ์˜ˆ:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'ajaxAdapter';
  }

  request(url) {
    // request and return promise
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'nodeAdapter';
  }

  request(url) {
    // request and return promise
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    return this.adapter.request(url).then((response) => {
      // transform response and return
    });
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ (Liskov Substitution Principle, LSP)

์ด๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ์›์น™์ž…๋‹ˆ๋‹ค. ๋ฆฌ์Šค์ฝ”ํ”„ ์›์น™์ด๋ž€ ์ž๋ฃŒํ˜• S๊ฐ€ ์ž๋ฃŒํ˜• T์˜ ํ•˜์œ„ํ˜•์ด๋ผ๋ฉด, ํ”„๋กœ๊ทธ๋žจ์ด ๊ฐ–์ถ”์–ด์•ผ ํ•  ์†์„ฑ๋“ค(์ •ํ™•์„ฑ, ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—… ๋“ฑ)์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์—†์ด, ์ž๋ฃŒํ˜• T์˜ ๊ฐ์ฒด๋ฅผ ์ž๋ฃŒํ˜• S์˜ ๊ฐ์ฒด๋กœ ๊ต์ฒด(์น˜ํ™˜)ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ž…๋‹ˆ๋‹ค.

์ด ์›์น™์„ ์˜ˆ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•˜์ž๋ฉด ๋‹น์‹ ์ด ๋ถ€๋ชจ ํด๋ž˜์Šค์™€ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ ๋ฒ ์ด์Šค ํด๋ž˜์Šค์™€ ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ ์—†์ด ์„œ๋กœ ๊ตํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ์ดํ•ด๊ฐ€ ์•ˆ๊ฐ„๋‹ค๋ฉด ์ •์‚ฌ๊ฐํ˜•-์ง์‚ฌ๊ฐํ˜• ์˜ˆ์ œ๋ฅผ ๋ด…์‹œ๋‹ค. ์ˆ˜ํ•™์ ์œผ๋กœ ์ •์‚ฌ๊ฐํ˜•์€ ์ง์‚ฌ๊ฐํ˜•์ด์ง€๋งŒ ์ƒ์†์„ ํ†ตํ•ด "is-a" ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋ธ๋งํ•œ๋‹ค๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class Rectangle {
  constructor() {
    this.width = 0;
    this.height = 0;
  }

  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }

  setWidth(width) {
    this.width = width;
  }

  setHeight(height) {
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  setWidth(width) {
    this.width = width;
    this.height = width;
  }

  setHeight(height) {
    this.width = height;
    this.height = height;
  }
}

function renderLargeRectangles(rectangles) {
  rectangles.forEach((rectangle) => {
    rectangle.setWidth(4);
    rectangle.setHeight(5);
    const area = rectangle.getArea(); // ์ •์‚ฌ๊ฐํ˜•์ผ๋•Œ 25๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ 20์ด์–ด์•ผ ํ•˜๋Š”๊ฒŒ ๋งž์Šต๋‹ˆ๋‹ค.
    rectangle.render(area);
  });
}

const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);

์ข‹์€ ์˜ˆ:

class Shape {
  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

class Square extends Shape {
  constructor(length) {
    super();
    this.length = length;
  }

  getArea() {
    return this.length * this.length;
  }
}

function renderLargeShapes(shapes) {
  shapes.forEach((shape) => {
      const area = shape.getArea();
      shape.render(area);
    });
  }

const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeShapes(shapes);

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (Interface Segregation Principle, ISP)

JavaScript๋Š” ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์›์น™๋“ค์ฒ˜๋Ÿผ ๋”ฑ ๋งž๊ฒŒ ์ ์šฉํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, JavaScript์— ํƒ€์ž… ์‹œ์Šคํ…œ์ด ์—†๋‹ค ํ•˜๋”๋ผ๋„ ์ค‘์š”ํ•˜๊ณ  ๊ด€๊ณ„์žˆ๋Š” ์›์น™์ž…๋‹ˆ๋‹ค.

ISP์— ์˜ํ•˜๋ฉด "ํด๋ผ์ด์–ธํŠธ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜๋„๋ก ๊ฐ•์š” ๋ฐ›์œผ๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค." ๋• ํƒ€์ดํ•‘ ๋•Œ๋ฌธ์— ์ธํ„ฐํŽ˜์ด์Šค๋Š” JavaScript์—์„œ๋Š” ์•”์‹œ์ ์ธ ๊ณ„์•ฝ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

JavaScript์—์„œ ์ด๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๋Š” ๊ฐ€์žฅ ์ข‹์€ ์˜ˆ๋Š” ๋ฐฉ๋Œ€ํ•œ ์–‘์˜ ์„ค์ • ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐฉ๋Œ€ํ•œ ์–‘์˜ ์˜ต์…˜์„ ์„ค์ •ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์„ค์ •๋“ค์ด ์ „๋ถ€ ๋‹ค ํ•„์š”ํ•œ ๊ฑด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์„ค์ •์„ ์„ ํƒ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด "๋ฌด๊ฑฐ์šด ์ธํ„ฐํŽ˜์ด์Šค(fat interface)"๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.animationModule.setup();
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName('body'),
  animationModule() {} // ์šฐ๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ DOM์„ ํƒ์ƒ‰ํ•  ๋•Œ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  // ...
});

์ข‹์€ ์˜ˆ:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.options = settings.options;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.setupOptions();
  }

  setupOptions() {
    if (this.options.animationModule) {
      // ...
    }
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName('body'),
  options: {
    animationModule() {}
  }
});

โฌ† ์ƒ๋‹จ์œผ๋กœ

์˜์กด์„ฑ ์—ญ์ „ ์›์น™ (Dependency Inversion Principle, DIP)

์ด ์›์น™์€ ๋‘๊ฐ€์ง€ ์ค‘์š”ํ•œ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ƒ์œ„ ๋ชจ๋“ˆ์€ ํ•˜์œ„ ๋ชจ๋“ˆ์— ์ข…์†๋˜์–ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ถ”์ƒํ™”๋Š” ์„ธ๋ถ€์‚ฌํ•ญ์— ์˜์กดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„ธ๋ถ€์‚ฌํ•ญ์€ ์ถ”์ƒํ™”์— ์˜ํ•ด ๋‹ฌ๋ผ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฒ˜์Œ์—๋Š” ์ด๊ฒƒ์„ ์ดํ•ดํ•˜๋Š”๋ฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์•ฝ Angular.js๋กœ ์ž‘์—…ํ•ด๋ณธ์ ์ด ์žˆ๋‹ค๋ฉด ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection) ํ˜•ํƒœ๋กœ ์ด ์›๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์„ ๋ณด์•˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. DIP๋Š” ๋™์ผํ•œ ๊ฐœ๋…์€ ์•„๋‹ˆ์ง€๋งŒ ์ƒ์œ„ ๋ชจ๋“ˆ์ด ํ•˜์œ„ ๋ชจ๋“ˆ์˜ ์„ธ๋ถ€์‚ฌํ•ญ์„ ์•Œ์ง€ ๋ชปํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DI์˜ ์žฅ์ ์€ ๋ชจ๋“ˆ ๊ฐ„์˜ ์˜์กด์„ฑ์„ ๊ฐ์†Œ์‹œํ‚ค๋Š” ๋ฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“ˆ๊ฐ„์˜ ์˜์กด์„ฑ์ด ๋†’์„์ˆ˜๋ก ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋ง ํ•˜๋Š”๋ฐ ์–ด๋ ค์›Œ์ง€๊ณ  ์ด๊ฒƒ์€ ๋งค์šฐ ๋‚˜์œ ๊ฐœ๋ฐœ ํŒจํ„ด๋“ค ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์•ž์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ JavaScript์—๋Š” ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์ถ”์ƒํ™”์— ์˜์กดํ•˜๋Š” ๊ฒƒ์€ ์•”์‹œ์ ์ธ ์•ฝ์†์ž…๋‹ˆ๋‹ค. ์ด๋ง์ธ์ฆ‰์Šจ, ๋‹ค๋ฅธ ๊ฐ์ฒด๋‚˜ ํด๋ž˜์Šค์— ๋…ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์™€ ์†์„ฑ์ด ๋ฐ”๋กœ ์•”์‹œ์ ์ธ ์•ฝ์†(์ถ”์ƒํ™”)๊ฐ€ ๋œ๋‹ค๋Š” ๊ฒƒ์ด์ฃ . ์•„๋ž˜ ์˜ˆ์ œ์—์„œ ์•”์‹œ์ ์ธ ์•ฝ์†์€ InventoryTracker์—๋Œ€ํ•œ ๋ชจ๋“  ์š”์ฒญ ๋ชจ๋“ˆ์ด requestItems ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์งˆ ๊ฒƒ์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class InventoryRequester {
  constructor() {
    this.REQ_METHODS = ['HTTP'];
  }

  requestItem(item) {
    // ...
  }
}

class InventoryTracker {
  constructor(items) {
    this.items = items;

    // ์•ˆ์ข‹์€ ์ด์œ : ํŠน์ • ์š”์ฒญ๋ฐฉ๋ฒ• ๊ตฌํ˜„์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
    // requestItems๋Š” ํ•œ๊ฐ€์ง€ ์š”์ฒญ๋ฐฉ๋ฒ•์„ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค.
    this.requester = new InventoryRequester();
  }

  requestItems() {
    this.items.forEach(item => {
      this.requester.requestItem(item);
    });
  }
}

const inventoryTracker = new InventoryTracker(['apples', 'bananas']);
inventoryTracker.requestItems();

์ข‹์€ ์˜ˆ:

class InventoryTracker {
  constructor(items, requester) {
    this.items = items;
    this.requester = requester;
  }

  requestItems() {
    this.items.forEach(item => {
      this.requester.requestItem(item);
    });
  }
}

class InventoryRequesterV1 {
  constructor() {
    this.REQ_METHODS = ['HTTP'];
  }

  requestItem(item) {
    // ...
  }
}

class InventoryRequesterV2 {
  constructor() {
    this.REQ_METHODS = ['WS'];
  }

  requestItem(item) {
    // ...
  }
}

// ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ๋งŒ๋“ค์–ด ์ฃผ์ž…ํ•ด์คŒ์œผ๋กœ์จ,
// ์š”์ฒญ ๋ชจ๋“ˆ์„ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“  ์›น์†Œ์ผ“ ์‚ฌ์šฉ ๋ชจ๋“ˆ๋กœ ์‰ฝ๊ฒŒ ๋ฐ”๊ฟ” ๋ผ์šธ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2());
inventoryTracker.requestItems();

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ…Œ์ŠคํŠธ(Testing)

ํ…Œ์ŠคํŠธ๋Š” ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์—†์ด ๋ฐฐํฌํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๋‹น์‹ ์ด ์งœ๋†“์€ ์ฝ”๋“œ๊ฐ€ ์–ธ์ œ๋“  ์˜ค์ž‘๋™ํ•ด๋„ ์ด์ƒํ•˜์ง€ ์•Š๋‹ค๋Š” ์–˜๊ธฐ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ์— ์–ผ๋งˆ๋‚˜ ์‹œ๊ฐ„์„ ํˆฌ์žํ• ์ง€๋Š” ๋‹น์‹ ์ด ํ•จ๊ป˜ ์ผํ•˜๋Š” ํŒ€์— ๋‹ฌ๋ ค์žˆ์ง€๋งŒ Coverage๊ฐ€ 100%๋ผ๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ๋†’์€ ์ž์‹ ๊ฐ๊ณผ ์•ˆ๋„๊ฐ์„ ์ค๋‹ˆ๋‹ค. ์ด ๋ง์€ ํ›Œ๋ฅญํ•œ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ๋ณด์œ ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ›Œ๋ฅญํ•œ Coverage ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์€ ๊ทธ ๋ฌด์—‡๋„ ๋ณ€๋ช…์ด ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ํ›Œ๋ฅญํ•˜๊ณ  ๋งŽ์€ JavaScript ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ํŒ€์˜ ๊ธฐํ˜ธ์— ๋งž๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ณ ๋ฅด๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ณจ๋ž๋‹ค๋ฉด ์ด์ œ๋ถ€ํ„ฐ๋Š” ํŒ€์˜ ๋ชฉํ‘œ๋ฅผ ๋ชจ๋“  ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ/๋ชจ๋“ˆ์„ ์งค ๋•Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์œผ๋กœ ํ•˜์„ธ์š”. ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก (Test Driven Development, TDD)์ด ๋‹น์‹ ์—๊ฒŒ ๋งž๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๋ฉด ๊ทธ๊ฑด ํ›Œ๋ฅญํ•œ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ค‘์š”ํ•œ ๊ฒƒ์€ ๋‹น์‹ ์ด ์–ด๋– ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋ง ํ•  ๋•Œ ๋‹น์‹ ์ด ์ •ํ•œ Coverage ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•˜๋Š” ๊ฒƒ์— ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ปจ์…‰

์•ˆ์ข‹์€ ์˜ˆ:

const assert = require('assert');

describe('MakeMomentJSGreatAgain', () => {
  it('handles date boundaries', () => {
    let date;

    date = new MakeMomentJSGreatAgain('1/1/2015');
    date.addDays(30);
    assert.equal('1/31/2015', date);

    date = new MakeMomentJSGreatAgain('2/1/2016');
    date.addDays(28);
    assert.equal('02/29/2016', date);

    date = new MakeMomentJSGreatAgain('2/1/2015');
    date.addDays(28);
    assert.equal('03/01/2015', date);
  });
});

์ข‹์€ ์˜ˆ:

const assert = require('assert');

describe('MakeMomentJSGreatAgain', () => {
  it('handles 30-day months', () => {
    const date = new MakeMomentJSGreatAgain('1/1/2015');
    date.addDays(30);
    assert.equal('1/31/2015', date);
  });

  it('handles leap year', () => {
    const date = new MakeMomentJSGreatAgain('2/1/2016');
    date.addDays(28);
    assert.equal('02/29/2016', date);
  });

  it('handles non-leap year', () => {
    const date = new MakeMomentJSGreatAgain('2/1/2015');
    date.addDays(28);
    assert.equal('03/01/2015', date);
  });
});

โฌ† ์ƒ๋‹จ์œผ๋กœ

๋™์‹œ์„ฑ(Concurrency)

Callback ๋Œ€์‹  Promise๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”

Callback์€ ๊น”๋”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€ ์ค‘๊ด„ํ˜ธ ์ค‘์ฒฉ์„ ๋งŒ๋“ค์–ด ๋ƒ…๋‹ˆ๋‹ค. ES2015/ES6์—์„  Promise๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑธ ์“ฐ์„ธ์š”!

์•ˆ์ข‹์€ ์˜ˆ:

require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) => {
  if (requestErr) {
    console.error(requestErr);
  } else {
    require('fs').writeFile('article.html', response.body, (writeErr) => {
      if (writeErr) {
        console.error(writeErr);
      } else {
        console.log('File written');
      }
    });
  }
});

์ข‹์€ ์˜ˆ:

require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then((response) => {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch((err) => {
    console.error(err);
  });

โฌ† ์ƒ๋‹จ์œผ๋กœ

Async/Await์€ Promise๋ณด๋‹ค ๋”์šฑ ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค

Promise๋„ Callback์— ๋น„ํ•ด ์ •๋ง ๊น”๋”ํ•˜์ง€๋งŒ ES2017/ES8์—์„  async์™€ await์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€ Callback์—๋Œ€ํ•œ ๋”์šฑ ๊น”๋”ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ค๋‹ˆ๋‹ค. ์˜ค์ง ํ•„์š”ํ•œ ๊ฒƒ์€ ํ•จ์ˆ˜์•ž์— async๋ฅผ ๋ถ™์ด๋Š” ๊ฒƒ ๋ฟ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ•จ์ˆ˜๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ๋”์ด์ƒ then์„ ์“ฐ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด ES2017/ES8 ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜์„ธ์š”!

์•ˆ์ข‹์€ ์˜ˆ:

require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then(response => {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch(err => {
    console.error(err);
  })

์ข‹์€ ์˜ˆ:

async function getCleanCodeArticle() {
  try {
    const response = await require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
    await require('fs-promise').writeFile('article.html', response);
    console.log('File written');
  } catch(err) {
    console.error(err);
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์—๋Ÿฌ ์ฒ˜๋ฆฌ(Error Handling)

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

๋‹จ์ˆœํžˆ ์—๋Ÿฌ๋ฅผ ํ™•์ธ๋งŒ ํ•˜์ง€๋งˆ์„ธ์š”

๋‹จ์ˆœํžˆ ์—๋Ÿฌ๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๊ทธ ์—๋Ÿฌ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ฑฐ๋‚˜ ๋Œ€์‘ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. console.log๋ฅผ ํ†ตํ•ด ์ฝ˜์†”์— ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๊ฒƒ์€ ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ์žƒ์–ด๋ฒ„๋ฆฌ๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋งŒ์•ฝ์— try/catch๋กœ ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ๊ฐ์ŒŒ๋‹ค๋ฉด ๊ทธ๊ฑด ๋‹น์‹ ์ด ๊ทธ ์ฝ”๋“œ์— ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋‚ ์ง€๋„ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์‹ผ ๊ฒƒ์ด๋ฏ€๋กœ ๊ทธ์—๋Œ€ํ•œ ๊ณ„ํš์ด ์žˆ๊ฑฐ๋‚˜ ์–ด๋– ํ•œ ์žฅ์น˜๋ฅผ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

์ข‹์€ ์˜ˆ:

try {
  functionThatMightThrow();
} catch (error) {
  // ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ console.error๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฑด console.log๋ณด๋‹ค ์กฐ๊ธˆ ๋” ์•Œ์•„์ฑ„๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  console.error(error);
  // ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ์œ ์ €์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  notifyUserOfError(error);
  // ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ์„œ๋น„์Šค ์ž์ฒด์— ์—๋Ÿฌ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  reportErrorToService(error);
  // ํ˜น์€ ๊ทธ ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

Promise๊ฐ€ ์‹คํŒจ๋œ ๊ฒƒ์„ ๋ฌด์‹œํ•˜์ง€ ๋งˆ์„ธ์š”

์œ„์˜ ์›์น™๊ณผ ๊ฐ™์€ ์ด์œ ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

getdata()
.then(data => {
  functionThatMightThrow(data);
})
.catch(error => {
  console.log(error);
});

์ข‹์€ ์˜ˆ:

getdata()
.then(data => {
  functionThatMightThrow(data);
})
.catch(error => {
  // ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ console.error๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฑด console.log๋ณด๋‹ค ์กฐ๊ธˆ ๋” ์•Œ์•„์ฑ„๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  console.error(error);
  // ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ์œ ์ €์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  notifyUserOfError(error);
  // ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ์„œ๋น„์Šค ์ž์ฒด์— ์—๋Ÿฌ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  reportErrorToService(error);
  // ํ˜น์€ ๊ทธ ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
});

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํฌ๋งทํŒ…(Formatting)

ํฌ๋งทํŒ…์€ ์ฃผ๊ด€์ ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์žˆ๋Š” ๋งŽ์€ ๊ทœ์น™๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋”ฐ๋ฅด๊ธฐ ์‰ฌ์šด ๊ทœ์น™๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์•Œ์•„์•ผ ํ•  ๊ฒƒ์€ ํฌ๋งทํŒ…์— ๋Œ€ํ•ด ๊ณผ๋„ํ•˜๊ฒŒ ์‹ ๊ฒฝ์“ฐ๋Š” ๊ฒƒ์€ ์˜๋ฏธ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํฌ๋งทํŒ… ์ฒดํฌ๋ฅผ ์ž๋™์œผ๋กœ ํ•ด์ฃผ๋Š” ๋งŽ์€ ๋„๊ตฌ๋“ค์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด์ค‘ ํ•˜๋‚˜๋ฅผ ๊ณจ๋ผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๊ฐœ๋ฐœ์ž๋“ค๋ผ๋ฆฌ ํฌ๋งทํŒ…์—๋Œ€ํ•ด ๋…ผ์Ÿํ•˜๋Š” ๊ฒƒ๋งŒํผ ์‹œ๊ฐ„๊ณผ ๋ˆ์„ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค.

์ž๋™์œผ๋กœ ์„œ์‹์„ ๊ต์ •ํ•ด์ฃผ๋Š” ๊ฒƒ(๋“ค์—ฌ์“ฐ๊ธฐ, ํƒญ์ด๋ƒ ์ŠคํŽ˜์ด์Šค๋ƒ, ์ž‘์€ ๋”ฐ์˜ดํ‘œ๋ƒ ํฐ๋”ฐ์˜ดํ‘œ๋ƒ)์— ํ•ด๋‹นํ•˜์ง€ ์•Š๋Š” ์‚ฌํ•ญ์— ๋Œ€ํ•ด์„œ๋Š” ๋ช‡๊ฐ€์ง€ ์ง€์นจ์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ผ๊ด€๋œ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”

JavaScript์—๋Š” ์ •ํ•ด์ง„ ํƒ€์ž…์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‹น์‹ ์˜ ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋ช… ๋“ฑ์—์„œ ๋งŽ์€ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ทœ์น™ ๋˜ํ•œ ์ฃผ๊ด€์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์‹ ์ด ํŒ€์ด ์„ ํƒํ•œ ๊ทœ์น™๋“ค์„ ๋”ฐ๋ฅด์„ธ์š” ์ค‘์š”ํ•œ๊ฑด ํ•ญ์ƒ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

const DAYS_IN_WEEK = 7;
const daysInMonth = 30;

const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles'];

function eraseDatabase() {}
function restore_database() {}

class animal {}
class Alpaca {}

์ข‹์€ ์˜ˆ:

const DAYS_IN_WEEK = 7;
const DAYS_IN_MONTH = 30;

const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const artists = ['ACDC', 'Led Zeppelin', 'The Beatles'];

function eraseDatabase() {}
function restoreDatabase() {}

class Animal {}
class Alpaca {}

โฌ† ์ƒ๋‹จ์œผ๋กœ

ํ•จ์ˆ˜ ํ˜ธ์ถœ์ž์™€ ํ•จ์ˆ˜ ํ”ผํ˜ธ์ถœ์ž๋Š” ๊ฐ€๊น๊ฒŒ ์œ„์น˜์‹œํ‚ค์„ธ์š”

์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ ํ•จ์ˆ˜๋“ค์€ ์†Œ์Šค ํŒŒ์ผ ์•ˆ์—์„œ ์„œ๋กœ ์ˆ˜์ง์œผ๋กœ ๊ทผ์ ‘ํ•ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ƒ์ ์œผ๋กœ๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ์ž๋ฅผ ํ•จ์ˆ˜ ํ”ผํ˜ธ์ถœ์ž ๋ฐ”๋กœ ์œ„์— ์œ„์น˜์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ฝ”๋“œ๋ฅผ ์ฝ์„๋•Œ ์‹ ๋ฌธ์„ ์ฝ๋“ฏ ์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•  ๋•Œ๋„ ์ฝ์„ ๋•Œ๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ์ž‘์„ฑ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }

  lookupManager() {
    return db.lookup(this.employee, 'manager');
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(user);
review.perfReview();

์ข‹์€ ์˜ˆ:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  lookupManager() {
    return db.lookup(this.employee, 'manager');
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(employee);
review.perfReview();

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ฃผ์„(Comments)

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ณต์žกํ•œ ๊ฒฝ์šฐ์—๋งŒ ์ฃผ์„์„ ๋‹ค์„ธ์š”

์ฃผ์„์„ ๋‹ค๋Š”๊ฒƒ์€ ์‚ฌ๊ณผํ•ด์•ผํ•  ์ผ์ด๋ฉฐ ํ•„์ˆ˜์ ์ธ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ข‹์€ ์ฝ”๋“œ๋Š” ์ฝ”๋“œ ์ž์ฒด๋กœ ๋งํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

function hashIt(data) {
  // ์ด๊ฑด ํ•ด์‰ฌ์ž…๋‹ˆ๋‹ค.
  let hash = 0;

  // lengh๋Š” data์˜ ๊ธธ์ด์ž…๋‹ˆ๋‹ค.
  const length = data.length;

  // ๋ฐ์ดํ„ฐ์˜ ๋ฌธ์ž์—ด ๊ฐœ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต๋ฌธ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  for (let i = 0; i < length; i++) {
    // ๋ฌธ์ž์—ด ์ฝ”๋“œ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.
    const char = data.charCodeAt(i);
    // ํ•ด์‰ฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    hash = ((hash << 5) - hash) + char;
    // 32-bit ์ •์ˆ˜๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    hash &= hash;
  }
}

์ข‹์€ ์˜ˆ:

function hashIt(data) {
  let hash = 0;
  const length = data.length;

  for (let i = 0; i < length; i++) {
    const char = data.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;

    // 32-bit ์ •์ˆ˜๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    hash &= hash;
  }
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ฃผ์„์œผ๋กœ ๋œ ์ฝ”๋“œ๋ฅผ ๋‚จ๊ธฐ์ง€ ๋งˆ์„ธ์š”

๋ฒ„์ „ ๊ด€๋ฆฌ ๋„๊ตฌ๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ์ฃผ์„์œผ๋กœ ๋‚จ๊ธธ ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์•ˆ์ข‹์€ ์˜ˆ:

doStuff();
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();

์ข‹์€ ์˜ˆ:

doStuff();

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ฝ”๋“œ ๊ธฐ๋ก์„ ์ฃผ์„์œผ๋กœ ๋‚จ๊ธฐ์ง€ ๋งˆ์„ธ์š”

๋ฒ„์ „ ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ์ด์šฉํ•ด์•ผํ•˜๋Š” ๊ฒƒ์„ ๊ผญ ๊ธฐ์–ตํ•˜์„ธ์š”. ์ฃฝ์€ ์ฝ”๋“œ๋„ ๋ถˆํ•„์š”ํ•œ ์„ค๋ช…๋„ ํŠนํžˆ ์ฝ”๋“œ์˜ ๊ธฐ๋ก์— ๋Œ€ํ•œ ์ฃผ์„๋„ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ๊ธฐ๋ก์— ๋Œ€ํ•ด ๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด git log๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”!

์•ˆ์ข‹์€ ์˜ˆ:

/**
 * 2016-12-20: ๋ชจ๋‚˜๋“œ ์ œ๊ฑฐํ–ˆ์Œ, ์ดํ•ด๋Š” ๋˜์ง€ ์•Š์Œ (RM)
 * 2016-10-01: ๋ชจ๋‚˜๋“œ ์“ฐ๋Š” ๋กœ์ง ๊ฐœ์„  (JP)
 * 2016-02-03: ํƒ€์ž…์ฒดํ‚น ํ•˜๋Š”๋ถ€๋ถ„ ์ œ๊ฑฐ (LI)
 * 2015-03-14: ๋ฒ„๊ทธ ์ˆ˜์ • (JR)
 */
function combine(a, b) {
  return a + b;
}

์ข‹์€ ์˜ˆ:

function combine(a, b) {
  return a + b;
}

โฌ† ์ƒ๋‹จ์œผ๋กœ

์ฝ”๋“œ์˜ ์œ„์น˜๋ฅผ ์„ค๋ช…ํ•˜์ง€ ๋งˆ์„ธ์š”

์ด๊ฑด ์ •๋ง ์“ธ๋ฐ ์—†์Šต๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ๋“ค์—ฌ์“ฐ๊ธฐ์™€ ํฌ๋งทํŒ…์„ ํ•˜๊ณ  ํ•จ์ˆ˜์™€ ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์— ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•˜์„ธ์š”.

์•ˆ์ข‹์€ ์˜ˆ:

////////////////////////////////////////////////////////////////////////////////
// ์Šค์ฝ”ํ”„ ๋ชจ๋ธ ์ •์˜
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
  menu: 'foo',
  nav: 'bar'
};

////////////////////////////////////////////////////////////////////////////////
// actions ์„ค์ •
////////////////////////////////////////////////////////////////////////////////
const actions = function() {
  // ...
};

์ข‹์€ ์˜ˆ:

$scope.model = {
  menu: 'foo',
  nav: 'bar'
};

const actions = function() {
  // ...
};

โฌ† ์ƒ๋‹จ์œผ๋กœ