출처
clean-code-javascript 번역본
clean-code-javascript 원본
을 읽고, 내가 리마인드 하고 싶고, 실제 현업에서 자주 리뷰했던 것들 정리
코드를 작성할 때 제일 중요한건(물론, 문제없는 테스트코드까지 빵빵한 코드를 짜는 것도 중요하지만!), 의도를 알 수 있는 이름과 로직을 짜는 것이라 생각한다. PR을 올릴때, 변수/함수 이름 짓는 것에서 코멘트를 정말정말.. 많이 받았다. (ㅠㅠ) 이해되는 것이, 나또한 리뷰시 이름부터 갸우뚱하면 바로 코멘트를 남기기 때문..
언제나, 나의 코드를 읽을 제3자를 생각하며.. 고통스러운 이름짓기 시간을 가져야 한다. 그리고 언제나 재사용성과 가독성을 고려하자.
남을 위한 코딩처럼 보이지만, 결국은 나에게 돌아오는 것들이다.
변수
의미있는 변수이름을 사용하자
의도를 알 수 있도록 이름을 짓자
검색할 수 있는 이름을 사용하자
클래스/타입/객체에서 이름에 의미가 담겨있다면, 이름에 반복하지 말자
PR올리면서 종종 지적받았던 사항들.
변수뿐만 아니라 함수이름, 컴포넌트이름을 지을때도 중요하다.
다른 개발자가 해당 코드를 읽었을 때 이름만 보고도 유추해낼 수 있도록 지어야 한다. 코드리뷰할 때도, 유지보수할 때도 좋다.
특히나, 아래와 같이 이미 타입명이 지어져 있는 경우, 해당 타입의 속성까지 이름을 반복할 필요는 없다.
Bad:
type Car = {
carMake: string;
carModel: string;
carColor: string;
}
function print(car: Car): void {
console.log(`${car.carMake} ${car.carModel} (${car.carColor})`);
}
Good:
type Car = {
make: string;
model: string;
color: string;
}
function print(car: Car): void {
console.log(`${car.make} ${car.model} (${car.color})`);
}
조건문대신 기본 매개변수를 고려하자
아래와 같이, 조건문에서 false일 때 셋팅해주는 경우 , 매개변수에서 기본값을 셋팅해주자.
가독성이 올라간다. 정말 간단한 사항인데 지키기 어렵다.
Bad:
function loadPages(count?: number) {
const loadCount = count !== undefined ? count : 10;
// ...
}
Good:
function loadPages(count: number = 10) {
// ...
}
ETC..
문서에선 의도를 위해 enum 을 사용하자 라고 했지만, 트리쉐이킹으로 굳이 사용해야할까 싶다.
Union Type 으로도 충분할 것 같다.
함수
함수의 매개변수는 2개 이하가 이상적이다
그 이상이 된다면 객체타입 매개변수를 받자.
함수가 무엇을 하는지 알 수 있도록 함수 이름을 짓자
함수는 한 가지만 해야 한다
하나의 함수에 여러가지 행동이 있다면, 재사용성과 테스트를 위해 쪼개보자
재사용성과 테스트를 위해 쪼갠다? 이해가 가지 않는다면 아래의 예시를 봐보자.
Bad:
function parseCode(code: string) {
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:
const REGEXES = [ /* ... */ ];
function parseCode(code: string) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach((node) => {
// parse...
});
}
function tokenize(code: string): Token[] {
const statements = code.split(' ');
const tokens: Token[] = [];
REGEXES.forEach((regex) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
return tokens;
}
function parse(tokens: Token[]): SyntaxTree {
const syntaxTree: SyntaxTree[] = [];
tokens.forEach((token) => {
syntaxTree.push( /* ... */ );
});
return syntaxTree;
}
Object.assign 을 고려하자
Object.assign
? 아래와 같은 예시를 보자.
Bad:
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean };
function createMenu(config: MenuConfig) {
config.title = config.title || 'Foo';
config.body = config.body || 'Bar';
config.buttonText = config.buttonText || 'Baz';
config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
// ...
}
createMenu({ body: 'Bar' });
Good:
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean };
function createMenu(config: MenuConfig) {
const menuConfig = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// ...
}
createMenu({ body: 'Bar' });
함수 매개변수로 플래그를 사용하지 말자 => 플래그를 사용한다는 건 해당 함수가 한 가지 이상의 일을 한다는 것 .. 쪼개보자.!
Bad:
function createFile(name: string, temp: boolean) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
Good:
function createTempFile(name: string) {
createFile(`./temp/${name}`);
}
function createFile(name: string) {
fs.create(name);
}
조건문
조건은 캡슐화하자
해당 조건이 무엇을 의미하는 건지 변수에 담거나, 아래와 같이 캡슐화하자.
그렇지않고, if 문에 조건만 나열하면 정말 알아보기 어렵다.
Bad:
if (subscription.isTrial || account.balance > 0) {
// ...
}
Good:
function canActivateService(subscription: Subscription, account: Account) {
return subscription.isTrial || account.balance > 0;
}
if (canActivateService(subscription, account)) {
// ...
}
부정 조건문을 피하자
정말사소한 것이긴 하지만 지켜보자. 사람은 관성상, 코드를 읽을 때 긍정적으로 읽게 된다. 무심코 아래 Bad 코드를 읽으면
isEmailNotUsed 가 if 조건문안에 있으니 true 라고 생각하기 쉽다. (안그럴 것 같아도.. 그런일이 생긴다. 하하)
Bad:
function isEmailNotUsed(email: string): boolean {
// ...
}
if (isEmailNotUsed(email)) {
// ...
}
Good:
function isEmailUsed(email): boolean {
// ...
}
if (!isEmailUsed(node)) {
// ...
}
조건문을 피하자!
함수는 한 가지일만 하도록 하는것이 베스트다. 조건문이 들어가면 해당 함수에 여러가지 일을 하게 되므로 해당 규칙이 깨진다.
쪼개고,쪼개고 쪼개보자.
Bad:
class Airplane {
private type: string;
// ...
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();
default:
throw new Error('Unknown airplane type.');
}
}
private getMaxAltitude(): number {
// ...
}
}
Good:
abstract class Airplane {
protected getMaxAltitude(): number {
// shared logic with subclasses ...
}
// ...
}
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();
}
}
'🌳Frontend > typescript' 카테고리의 다른 글
타입도 분기처리 할 수 있나요? 타입스크립트에서 조건부 타입 알아보기 (0) | 2024.02.16 |
---|---|
Typescript 에서 string union type을 type guard 하는 법 (typeof Array[number] 와 as const) (0) | 2023.09.17 |
[Typescript/TS] 배열에서 union type 만들기 (0) | 2023.07.13 |