함수는 2개 이하가 이상적이다
매개변수의 개수를 제한 하는 것은 함수 테스팅을 쉽게 만들어 주기 때문에 중요하다. 만약 매개변수가 3개 이상일 경우엔 테스트 해야하는 경우의 수가 많아지고 각기 다른 인수들로 여러 사례들을 테스트 해야한다. 3개 이상일 경우는 객체로 매개변수를 만들어서 사용하는 방법이 있다.
BAD
function createMenu(title, body, buttonText, cancellable) {
// ...
}
GOOD
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
함수는 하나의 행동만 해야 한다
함수가 1개 이상의 행동을 한다면 작성하는 것도, 테스트하는 것도, 이해하는 것도 어려워진다. 하나의 함수에 하나의 행동을 정의하는 것이 가능해진다면 함수는 좀 더 고치기 쉬워지고 코드들은 읽기 쉬워질 것이다.
BAD
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
GOOD
function emailClients(clients) {
clients
.filter(isClientActive)
.forEach(email);
}
function isClientActive(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
함수명은 무엇을 하는지 직관적으로 정의해야 한다
BAD
function AddToDate(date, month) {
// ...
}
const date = new Date();
// 뭘 추가하는 건지 이름만 보고 알아내기 힘들다
AddToDate(date, 1);
GOOD
function AddMonthToDate(date, month) {
// ...
}
const date = new Date();
AddMonthToDate(date, 1);
함수는 단일 행동을 추상화해야 한다
추상화된 이름이 여러 의미를 내포하고 있다면 그 함수는 너무 많은 일을 하게끔 설계된 것이다. 함수들을 나누어서 재사용가능하고 테스트하기 쉽게 만들자.
BAD
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...
});
}
GOOD
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...
});
}
중복된 코드를 작성하지 말 것
BAD
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);
});
}
GOOD
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);
});
}
매개변수로 flag를 사용하지 말 것
플래그를 사용하는 것 자체가 그 함수가 한가지 이상의 역할을 하고 있다는 것을 뜻한다. boolean 기반으로 함수가 실행되는 코드가 나뉜다면 함수를 분리하자.
BAD
function createFile(name, temp) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
GOOD
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile(`./temp/${name}`);
}
전역 함수를 사용하지 말자
전역 환경을 사용하는 것은 JavaScript에서 나쁜 관행이다. 왜냐하면 다른 라이브러리들과의 충돌이 일어날 수 있고, 당신의 API를 쓰는 유저들은 운영환경에서 예외가 발생하기 전까지는 문제를 인지하지 못할 것이기 때문이다.
BAD
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
GOOD
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
명령형 프로그래밍보다 함수형 프로그래밍을 지향하자
JavaScript는 Haskell처럼 함수형 프로그래밍 언어는 아니지만 함수형 프로그래밍처럼 작성할 수 있다. 함수형 언어는 더 깔끔하고 테스트하기 쉽다.
BAD
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;
}
GOOD
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);
조건문을 캡슐화하자
※ 캡슐화란?
객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고 실제 구현 내용 일부를 내부에 감추어 은닉하는 방법을 말한다.
BAD
if (fsm.state === 'fetching' && isEmpty(listNode)) {
// ...
}
GOOD
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
과도한 최적한 지양 하자
오래된 브라우저의 경우 캐시되지 않은 `list.length`를 통한 반복문은 높은 코스트를 가졌다. 그 이유는 `list.length`를 매번 계산해야만 했기 때문인데, 최신 브라우저에서는 이것이 최적화 되어 사용하는 것 자체가 불필요해졌다.
BAD
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
GOOD
for (let i = 0; i < list.length; i++) {
// ...
}
※ 출처 : Robert C. Martin's Clean Code 명시함
'프런트엔드 > 클린코드' 카테고리의 다른 글
JavaScript - 객체 다루기 (0) | 2022.04.15 |
---|---|
JavaScript - 배열 다루기 (0) | 2022.04.15 |
JavaScript - 분기 다루기 (0) | 2022.04.14 |
JavaScript - 경계 다루기 (0) | 2022.04.14 |
JavaScript - 타입 다루기 (0) | 2022.04.14 |