참고 링크 를 통해 꼭 알고 있었으면 하는 내용을 바탕으로 작성 및 인용하였습니다.
협업하는 과정에 따라 다르겠지만 camelCase, snake_case, PaskalCase 등등 하나의 방법으로 통일하여 개발하는 것을 추천한다.
변수명은 의미 있게, 검색할 수 있게 사용하자
recordId: number; // Id의 종류가 무엇인지 알 수 있도록
Bad: getTime(start, 86400000); // 86400000가 무엇을 의미하는 숫자일까?
Good: const MILLISECENDS_IN_A_DAY = 24 * 60 * 60 * 1000;
getTime(start, MILLISECENDS_IN_A_DAY);
Typescript enum은 그 자체로 객체이지만, enum의 속성을 변경할 수는 없습니다. 이름을 붙여서 코드의 가독성을 높일 때 enum을 사용하면 좋을 것 같습니다.
export enum Genre {
DRAMA = "drama",
MOVIE = "movie",
NEWS = "news",
}
projector.configureFilm(Genre.DRAMA);
class Projector {
configureFilm(genre) {
switch (genre) {
case GENRE.DRAMA:
// 실행되어야 하는 로직
}
}
}
함수의 매개변수는 2개 혹은 그 이하가 이상적이라고 알려져 있습니다. 함수 매개변수의 개수를 제한하는 것은 함수를 테스트하기 쉽게 만들어주기 때문에 정말 중요합니다. 함수 매개변수가 3개 이상인 경우, 각기 다른 케이스를 테스트해야 해서 경우의 수가 많아진다.
소프트웨어 공학에서 가장 중요한 규칙이다. 함수가 한 가지 이상의 역할을 수행하게 되면 테스트하고 추론하기가 어려워진다. 함수를 하나의 행동으로 정의해야 쉽게 리팩토링할 수 있으며 코드를 명료하게 읽을 수 있다. 따라서 꼭 자기의 것으로 만드는 것이 좋다.
Bad: function addMenu(title: string, body: string, writer: string, date: Date) {
// ...
}
addMenu("김치볶음밥", "김치와 스팸의 조합", "김태용", "2022-10-10");
Good: function addMenu(options: {
title: string;
body: string;
writer: string;
date: Date;
}) {
// ...
}
addMenu({
title: "김치볶음밥",
body: "김치와 스팸의 조합",
writer: "김태용",
date: "2022-10-10",
});
Better: type MenuOptions = {
title: string;
body: string;
writer: string;
date: Date;
};
function addMenu(options: MenuOptions) {
// ...
}
addMenu({
title: "김치볶음밥",
body: "김치와 스팸의 조합",
writer: "김태용",
date: "2022-10-10",
});
조건문 대신 기본 매개변수를 사용하는 것에 익숙해지자
Bad:
function makeLogic(count?: number){
const loadCount = count !== undefined ? count : 10;
}
Good:
function makeLogic(count: number = 10) {
}
코드가 중복되지 않게 최선을 다하여야 한다. 중복된 코드는 어떤 로직을 변경할 때 한 곳 이상을 변경해야 하기 때문에 좋지 않습니다.
클래스는 작아야 합니다! SOLID 원칙 중, 단일 책임 원칙에 따르면, 한 클래스는 하나의 책임만 가져야 합니다.
Bad:
class DashBoard {
getLanguage(): string { /* ... */ }
setLanguage(language: string): void { /* ... */ }
showProgress(): void { /* ... */ }
hideProgress(): void { /* ... */ }
isDirty(): boolean { /* ... */ }
disable(): void { /* ... */ }
enable(): void { /* ... */ }
addSubscription(subscription: Subscription): void { /* ... */ }
removeSubscription(subscription: Subscription): void { /* ... */ }
addUser(user: User): void { /* ... */ }
removeUser(user: User): void { /* ... */ }
goToHomePage(): void { /* ... */ }
updateProfile(details: UserDetails): void { /* ... */ }
getVersion(): string { /* ... */ }
// ...
}
Good:
class Dashboard {
disable(): void { /* ... */ }
enable(): void { /* ... */ }
getVersion(): string { /* ... */ }
// 다른 클래스에 남은 메소드를 이동시킴으로써 책임을 분산시키세요
// ...
}
높은 응집도와 낮은 결합도를 가져야 한다라는 말, 많이 들어보시지 않으셨나요?
응집도는 클래스 멤버가 서로에게 연관되어 있는 정도를 정의하는데, 이상적으로 클래스 안의 모든 필드는 각 메소드에 의해 사용되어야 한다. 그 때, 클래스가 최대한으로 응집되어 있다고 말합니다. 이것이 항상 가능한 것은 아니지만 응집도를 높이는 것을 선호해야 합니다.
Bad:
class DatabaseService {
constructor(
private readonly db: Database,
private readonly emailSender: EmailSender
) {}
async findUser(id: number): Promise<User> {
return await db.users.findOne({ id });
}
async sendMessage(): Promise<void> {
return await emailSender.send("Hello");
}
async sendNotification(text: string): Promise<void> {
return await emailSender.send(text);
}
}
Good:
class DatabaseService {
constructor(private readonly db: Database) {}
async findUser(id: number): Promise<User> {
return await db.users.findOne({ id });
}
}
class UserNotifier {
constructor(private readonly emailSender: EmailSender) {
}
async sendMessage(): Promise<void> {
return await emailSender.send("Hello");
}
async sendNotification(text: string): Promise<void> {
return await emailSender.send(text);
}
}