클린 코드 in TypeScript #1 - 변수와 함수편
https://github.com/ryanmcdermott/clean-code-javascript
위 링크에 있는 깃헙 ryanmcdermott 의 clean-code-javascript 에서 내용을 참고하여 만든 클린 코드 가이드를 TypeScript 에서 사용할 수 있도록 정리한 글입니다.
영어로 된 가이드 글을 번역하고 (고마워요 파파고) 제 생각에 중요하다 느낀 것들만 추려 모아 봤습니다.
이 곳에 적힌 모든 가이드를 따르긴 힘들겠지만, 이런게 있다라고 읽어보고 코드에 적용해보려는 노력을 하는게 클린 코드로 나아가는 한 걸음이라고 생각합니다!
이번 편은 변수와 함수에 대해 포스팅하겠습니다.
장문입니다..읽기 전 숨 한 번 쉬고 갈까요?
변수
의미 있고 발음하기 쉬운 변수 이름
안좋은 예:
function between<T>(a1: T, a2: T, a3: T): boolean {
return a2 <= a1 && a1 <= a3;
}
좋은 예:
function between<T>(value: T, left: T, right: T): boolean {
return left <= value && value <= right;
}
👆맨 위로
동일한 유형의 변수에 동일한 어휘를 사용하세요.
안좋은 예:
function getUserInfo(): User;
function getUserDetails(): User;
function getUserData(): User;
좋은 예:
function getUser(): User;
👆맨 위로
검색 가능한 이름을 사용하세요.
안좋은 예:
// 86400000 의 의미는 뭘까요?
setTimeout(blastOff, 86400000);
좋은 예:
const MILLISECONDS_IN_A_DAY = 8640000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
👆맨 위로
자신만 알아볼 수 있는 작명을 피하세요.
안좋은 예:
declare const users: Map<string, User>;
for (const keyValue of users) {
// iterate through users map
// keyValue 는 뭘 의미하나요?
}
좋은 예:
declare const users: Map<string, User>;
for (const [id, user] of users) {
// iterate through users map
}
👆맨 위로
암시적으로 이름을 짓지 마세요.
안좋은 예:
const u = getUser();
const s = getSubscription();
const t = charge(u, s);
좋은 예:
const user = getUser();
const subscription = getSubscription();
const transaction = charge(user, subscription);
👆맨 위로
문맥상 필요없는 것들을 쓰지 마세요.
안좋은 예:
type Car = {
carMake: string;
carModel: string;
carColor: string;
}
function print(car: Car): void {
console.log(`${car.carMake} ${car.carModel} (${car.carColor})`);
}
좋은 예:
type Car = {
make: string;
model: string;
color: string;
}
function print(car: Car): void {
console.log(`${car.make} ${car.model} (${car.color})`);
}
👆맨 위로
기본 매개변수가 short circuiting 트릭이나 조건문 보다 깔끔합니다
안좋은 예:
function loadPages(count?: number) {
const loadCount = count !== undefined ? count : 10;
// ...
}
좋은 예:
function loadPages(count: number = 10) {
// ...
}
👆맨 위로
enum 으로 의도를 코드에 표현해 주세요.
안좋은 예:
const GENRE = {
ROMANTIC: 'romantic',
DRAMA: 'drama',
COMEDY: 'comedy',
DOCUMENTARY: 'documentary',
}
projector.configureFilm(GENRE.COMEDY);
class Projector {
// delactation of Projector
configureFilm(genre) {
switch (genre) {
case GENRE.ROMANTIC:
// some logic to be executed
}
}
}
좋은 예:
enum GENRE {
ROMANTIC,
DRAMA,
COMEDY,
DOCUMENTARY,
}
projector.configureFilm(GENRE.COMEDY);
class Projector {
// delactation of Projector
configureFilm(genre) {
switch (genre) {
case GENRE.ROMANTIC:
// some logic to be executed
}
}
}
👆맨 위로
함수
함수 인수를 2개 이하로 줄이세요.
- 매개 변수가 3개 이상이 되면, 테스트 해야하는 경우의 수가 많아집니다.
- 1개나 2개의 인자를 가지고 있는 것이 가장 이상적인 케이스입니다.
- 2개 이상의 인자를 가진 함수라면 너무 많은 일을 하는 함수일 가능성이 있습니다.
안좋은 예:
function createMenu(title: string, body: string, buttonText: string, cancellable: boolean) {
// ...
}
createMenu('Foo', 'Bar', 'Baz', true);
좋은 예:
function createMenu(options: { title: string, body: string, buttonText: string, cancellable: boolean }) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
Type aliases 를 사용하면 가독성을 향상 시킬 수 있습니다.
type MenuOptions = { title: string, body: string, buttonText: string, cancellable: boolean };
function createMenu(options: MenuOptions) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
👆맨 위로
함수는 한 가지 일을 해야합니다.
- 소프트웨어 엔지니어링에서 가장 중요한 규칙입니다.
함수가 둘 이상의 작업을 수행하면 작성, 테스트 및 추론하기가 더 어렵습니다.
안좋은 예: function emailClients(clients: Client[]) { clients.forEach((client) => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); }
좋은 예:
function emailClients(clients: Client[]) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client: Client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
👆맨 위로
함수명은 함수가 무엇을 하는지 알 수 있어야 합니다
안좋은 예:
function addToDate(date: Date, month: number): Date {
// ...
}
const date = new Date();
// It's hard to tell from the function name what is added
addToDate(date, 1);
좋은 예:
function addMonthToDate(date: Date, month: number): Date {
// ...
}
const date = new Date();
addMonthToDate(date, 1);
👆맨 위로
함수는 단일 행동을 추상화 해야합니다.
안좋은 예:
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...
});
}
좋은 예:
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;
}
👆맨 위로
중복된 코드를 작성하지 마세요.
안좋은 예:
function showDeveloperList(developers: Developer[]) {
developers.forEach((developer) => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers: Manager[]) {
managers.forEach((manager) => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
좋은 예:
class Developer {
// ...
getExtraDetails() {
return {
githubLink: this.githubLink,
}
}
}
class Manager {
// ...
getExtraDetails() {
return {
portfolio: this.portfolio,
}
}
}
function showEmployeeList(employee: Developer | Manager) {
employee.forEach((employee) => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const extra = employee.getExtraDetails();
const data = {
expectedSalary,
experience,
extra,
};
render(data);
});
}
👆맨 위로
Object.assign을 사용해 기본 객체를 만드세요
안좋은 예:
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' });
좋은 예:
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' });
기본 값을 설정할 수도 있습니다.
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean }; function createMenu({ title = 'Foo', body = 'Bar', buttonText = 'Baz', cancellable = true }: MenuConfig) { // ... } createMenu({ body: 'Bar' });
👆맨 위로
매개 변수로 플래그를 사용하지 마세요.
안좋은 예:
function createFile(name: string, temp: boolean) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
좋은 예:
function createTempFile(name: string) {
createFile(`./temp/${name}`);
}
function createFile(name: string) {
fs.create(name);
}
👆맨 위로
전역 함수를 사용하지 마세요.
안좋은 예:
declare global {
interface Array<T> {
diff(other: T[]): Array<T>;
}
}
if (!Array.prototype.diff) {
Array.prototype.diff = function <T>(other: T[]): T[] {
const hash = new Set(other);
return this.filter(elem => !hash.has(elem));
};
}
좋은 예:
class MyArray<T> extends Array<T> {
diff(other: T[]): T[] {
const hash = new Set(other);
return this.filter(elem => !hash.has(elem));
};
}
👆맨 위로
조건문을 캡슐화하세요.
안좋은 예:
if (subscription.isTrial || account.balance > 0) {
// ...
}
좋은 예:
function canActivateService(subscription: Subscription, account: Account) {
return subscription.isTrial || account.balance > 0
}
if (canActivateService(subscription, account)) {
// ...
}
👆맨 위로
부정적인 조건문을 쓰지 마세요.
안좋은 예:
function isEmailNotUsed(email: string): boolean {
// ...
}
if (isEmailNotUsed(email)) {
// ...
}
좋은 예:
function isEmailUsed(email): boolean {
// ...
}
if (!isEmailUsed(node)) {
// ...
}
👆맨 위로
유형 검사를 하지마세요.
TypeScript 자체가 JS 의 구문적 슈퍼 세트입니다. TS 를 잘 사용하려면 변수와 매개 변수의 유형을 지정하세요.
안좋은 예: function travelToTexas(vehicle: Bicycle | Car) { if (vehicle instanceof Bicycle) { vehicle.pedal(currentLocation, new Location('texas')); } else if (vehicle instanceof Car) { vehicle.drive(currentLocation, new Location('texas')); } }
좋은 예:
type Vehicle = Bicycle | Car;
function travelToTexas(vehicle: Vehicle) {
vehicle.move(currentLocation, new Location('texas'));
}
👆맨 위로
여기까지 타입스크립트 클린 코드 가이드, 변수와 함수 편이였습니다.
다음 포스팅은 클래스에 대해 알아보도록 하겠습니다.
2020-03-22 14:38 +0900