clean-code-javascript
- Updated date 2020.01.09
- νμ¬ μλ¬Έμ 1c0b20a κΉμ§ λ°μλμ΄ μμ΅λλ€.
λͺ©μ°¨
- μκ°(Introduction)
- λ³μ(Variables)
- ν¨μ(Functions)
- κ°μ²΄μ μλ£κ΅¬μ‘°(Objects and Data Structures)
- ν΄λμ€(Classes)
- SOLID
- ν μ€νΈ(Testing)
- λμμ±(Concurrency)
- μλ¬ μ²λ¦¬(Error Handling)
- ν¬λ§·ν (Formatting)
- μ£Όμ(Comments)
- λ²μ(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) ꡬ문μ μ¬μ©ν μ μκ³ μ΄ κ΅¬λ¬Έμλ λͺκ°μ§ μ₯μ μ΄ μμ΅λλ€.
- μ΄λ€ μ¬λμ΄ κ·Έ ν¨μμ μκ·Έλμ³(μΈμμ νμ , λ°νλλ κ°μ νμ λ±)λ₯Ό λ³Ό λ μ΄λ€ μμ±μ΄ μ¬μ©λλμ§ μ¦μ μ μ μμ΅λλ€.
- λν λΉκ΅¬μ‘°νλ ν¨μμ μ λ¬λ μΈμ κ°μ²΄μ μ§μ λ κΈ°λ³Ένμ κ°μ 볡μ νλ©° μ΄λ μ¬μ΄λμ΄ννΈκ° μΌμ΄λλ κ²μ λ°©μ§ν©λλ€. μ°Έκ³ λ‘ μΈμ κ°μ²΄λ‘λΆν° λΉκ΅¬μ‘°νλ κ°μ²΄μ λ°°μ΄μ 볡μ λμ§ μμ΅λλ€.
- 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)
μλ°μ€ν¬λ¦½νΈμμλ κΈ°λ³Ένμ
μλ£νμ κ°μ μ λ¬νκ³ κ°μ²΄μ λ°°μ΄μ μ°Έμ‘°λ₯Ό μ λ¬ν©λλ€.
κ°μ²΄μ λ°°μ΄μΈ κ²½μ°λ₯Ό νλ² μ΄ν΄λ΄
μλ€. μ°λ¦¬κ° λ§λ ν¨μλ μ₯λ°κ΅¬λ λ°°μ΄μ λ³νλ₯Ό μ£Όλ©°
μ΄ λ³νλ ꡬ맀λͺ©λ‘μ μ΄λ€ μνμ μΆκ°νλ κΈ°λ₯ κ°μ κ²μ λ§ν©λλ€.
λ§μ½ μ₯λ°κ΅¬λ
λ°°μ΄μ μ¬μ©νλ μ΄λ λ€λ₯Έ ν¨μκ° μλ€λ©΄ μ΄λ¬ν μΆκ°μ μν₯μ λ°μ΅λλ€.
μ΄κ²μ μ’μ μλ μμ§λ§, μμ’μ μλ μμ΅λλ€. μμ’μ μλ₯Ό νλ² μμν΄λ΄
μλ€.
μ μ κ° κ΅¬λ§€νκΈ° λ²νΌμ λλ¬ κ΅¬λ§€
ν¨μλ₯Ό νΈμΆν©λλ€. μ΄λ λ€νΈμν¬ μμ²μ μμ±νκ³ μλ²μ μ₯λ°κ΅¬λ
λ°°μ΄μ 보λ
λλ€.
νμ§λ§ λ€νΈμν¬ μ°κ²°μ΄ μ’μ§μμμ ꡬ맀
ν¨μλ λ€μνλ² λ€νΈμν¬ μμ²μ 보λ΄μΌ νλ μν©μ΄ μκ²Όμ΅λλ€.
μ΄λ, μ¬μ©μκ° λ€νΈμν¬ μμ²μ΄ μμλκΈ° μ μ μ€μλ‘ μνμ§ μλ μνμ "μ₯λ°κ΅¬λμ μΆκ°" λ²νΌμ μ€μλ‘ ν΄λ¦νλ©΄ μ΄λ»κ²λ κΉμ?
μ€μκ° μκ³ λ λ€, λ€νΈμν¬ μμ²μ΄ μμλλ©΄ μ₯λ°κ΅¬λμ μΆκ°
ν¨μ λλ¬Έμ μ€μλ‘ λ³κ²½λ μ₯λ°κ΅¬λ
λ°°μ΄μ μλ²μ 보λ΄κ² λ©λλ€.
κ°μ₯ μ’μ λ°©λ²μ μ₯λ°κ΅¬λμ μΆκ°
λ νμ μ₯λ°κ΅¬λ
λ°°μ΄μ 볡μ νμ¬ μμ νκ³ λ³΅μ λ³Έμ λ°ννλ κ²μ
λλ€.
μ΄λ κ²νλ©΄ μ₯λ°κ΅¬λ μ°Έμ‘°λ₯Ό 보μ νκ³ μλ λ€λ₯Έ ν¨μκ° λ€λ₯Έ λ³κ²½ μ¬νμ μν₯μ λ°μ§ μκ²λ©λλ€.
μ΄ μ κ·Όλ²μλν΄ λ§νκ³ μΆμ κ²μ΄ λκ°μ§ μμ΅λλ€.
- μ€μ λ‘ μ λ ₯λ κ°μ²΄λ₯Ό μμ νκ³ μΆμ κ²½μ°κ° μμ μ μμ§λ§ μ΄λ¬ν μμ λ₯Ό μκ°ν΄λ³΄κ³ μ μ©ν΄λ³΄λ©΄ κ·Έλ° κ²½μ°λ κ±°μ μλ€λ κ²μ κΉ¨λ¬μ μ μμ΅λλ€. κ·Έλ¦¬κ³ λλΆλΆμ κ²λ€μ΄ μ¬μ΄λ μ΄ννΈ μμ΄ λ¦¬ν©ν λ§ λ μ μμ΅λλ€.
- ν° κ°μ²΄λ₯Ό 볡μ νλ κ²μ μ±λ₯ μΈ‘λ©΄μμ κ°μ΄ λ§€μ° λΉμλλ€. μ΄μ’κ²λ μ΄λ°κ² ν° λ¬Έμ κ° λμ§λ μμ΅λλ€. μλνλ©΄ μ΄λ¬ν νλ‘κ·Έλλ° μ κ·Όλ²μ κ°λ₯νκ²ν΄μ€ μ’μ λΌμ΄λΈλ¬λ¦¬κ° μκΈ° λλ¬Έμ λλ€. μ΄λ κ°μ²΄μ λ°°μ΄μ μλμΌλ‘ 볡μ νλ κ²μ²λΌ λ©λͺ¨λ¦¬ μ§μ½μ μ΄μ§ μκ² ν΄μ£Όκ³ λΉ λ₯΄κ² 볡μ ν΄μ€λλ€.
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μμ μ λͺ ν μ λ΅μΌλ‘ λΉμ μ κ°λ₯νλ€λ©΄ μμ보λ€λ μ‘°ν©μ μ¬μ©ν΄μΌ ν©λλ€. μμμ μ¬μ©νμ λ μ»μ μ μλ μ΄λλ³΄λ€ μ‘°ν©μ μ¬μ©νμ λ μ»μ μ μλ μ΄λμ΄ λ§κΈ° λλ¬Έμ λλ€. μ΄ μμΉμ μμ μ λΉμ μ΄ κ³μ μμμ μ¬μ©ν΄μ μ½λλ₯Ό μμ±νκ³ μ ν λ, λ§μ½ μ‘°ν©μ μ΄μ©νλ©΄ λ μ½λλ₯Ό μ 지 μ μμ§ μμκΉ μκ°ν΄λ³΄λΌλ κ²μ μμ΅λλ€. λλλ‘λ μ΄κ²μ΄ λ§λ μ λ΅μ΄κΈ° λλ¬Έμ΄μ£ .
"κ·ΈλΌ λ체 μμμ μΈμ μ¬μ©ν΄μΌ λλ 건κ°μ?"λΌκ³ λ¬Όμ΄ λ³Ό μ μμ΅λλ€. μ΄κ±΄ λΉμ μ΄ μ§λ©΄ν λ¬Έμ μν©μ λ¬λ €μμ§λ§ μ‘°ν©λ³΄λ€ μμμ μ°λκ² λ μ’μ λ§ν μμλ₯Ό λͺ κ° λ€μ΄ λ³΄κ² μ΅λλ€.
- λΉμ μ μμκ΄κ³κ° "has-a" κ΄κ³κ° μλλΌ "is-a" κ΄κ³μΌ λ (μ¬λ->λλ¬Ό vs. μ μ ->μ μ μ 보)
- κΈ°λ° ν΄λμ€μ μ½λλ₯Ό λ€μ μ¬μ©ν μ μμ λ (μΈκ°μ λͺ¨λ λλ¬Όμ²λΌ μμ§μΌ μ μμ΅λλ€.)
- κΈ°λ° ν΄λμ€λ₯Ό μμ νμ¬ νμλ ν΄λμ€ λͺ¨λλ₯Ό μμ νκ³ μΆμ λ (μ΄λμ λͺ¨λ λλ¬Όμ΄ μλΉνλ μΉΌλ‘리λ₯Ό λ³κ²½νκ³ μΆμ λ)
μμ’μ μ:
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)
μ΄ μμΉμ λκ°μ§ μ€μν μμλ₯Ό κ°μ§κ³ μμ΅λλ€.
- μμ λͺ¨λμ νμ λͺ¨λμ μ’ μλμ΄μλ μλ©λλ€. λ λ€ μΆμνμ μμ‘΄ν΄μΌ ν©λλ€.
- μΆμνλ μΈλΆμ¬νμ μμ‘΄νμ§ μμ΅λλ€. μΈλΆμ¬νμ μΆμνμ μν΄ λ¬λΌμ ΈμΌ ν©λλ€.
μ²μμλ μ΄κ²μ μ΄ν΄νλλ° μ΄λ €μΈ μ μμ΅λλ€. νμ§λ§ λ§μ½ 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() {
// ...
};
λ²μ(Translation)
λ€λ₯Έ μΈμ΄λ‘λ μ½μ μ μμ΅λλ€:
- French: GavBaros/clean-code-javascript-fr
- Brazilian Portuguese: fesnt/clean-code-javascript
- Spanish: andersontr15/clean-code-javascript
- Spanish: tureey/clean-code-javascript
- Simplified Chinese:
- Traditional Chinese: AllJointTW/clean-code-javascript
- German: marcbruederlin/clean-code-javascript
- Korean: qkraudghgh/clean-code-javascript-ko
- Polish: greg-dev/clean-code-javascript-pl
- Russian:
- Vietnamese: hienvd/clean-code-javascript/
- Japanese: mitsuruog/clean-code-javascript/
- Indonesia: andirkh/clean-code-javascript/
- Italian: frappacchio/clean-code-javascript/
- Bangla(বাΰ¦ΰ¦²ΰ¦Ύ): InsomniacSabbir/clean-code-javascript/