출처: 코딩 잘하는 팁 세가지 (이걸 알면 코드가 깔끔해 진다)
function greetings(user) {
return `Hi ${user.firstName} ${user.lastName}`
}
function goodbye(user) {
return `See you next time ${user.firstName} ${user.lastName}`
}
class를 선언하여 중복 로직을 하나의 함수를 호출하도록 개선이 가능하다.
function greetings(user) {
return `Hi ${user.fullName()}`
}
function goodbye(user) {
return `See you next time ${user.fullName()}`
}
class User {
fullName() {
return `${this.firstName} ${this.middleName} ${this.lastName}`
}
}
아래 코드는 중복적 요소로 보이지만 한 가지의 로직을 함수 안에서 처리하고 있고 이 로직이 다른 곳에서 중복적으로 사용되지 않고 있기 때문에 DRY라고 할 수 없다.
function validateBody(body) {
if (!body.id) {
throw new Error('Validation failed. The attribute id is missing.');
}
if (!body.name) {
throw new Error('Validation failed. The attribute name is missing.');
}
if (!body.count) {
throw new Error('Validation failed. The attribute count is missing.');
}
}
중복적 선언이라고 해서 DRY라고 판단하지 않고 로직, 의도, 비즈니스 로직을 고려하여 판단해야 한다.
위 소스는 아래와 같이 선언할 수는 있음. (참고용)
function validateBody(body) {
const attributes = ['id', 'name', 'count'];
attributes.forEach(attribute => {
if (!body[attribute]) {
throw new Error(
`Validation failed. The attribute "${attribute}" is missing.`
);
}
})
}
Most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design and unnecessary complexity should be avoided.
대부분의 시스템은 복잡하기보다 단순하게 유지했을때 가장 잘 동작한다.
따라서, 불필요한 복잡성은 피하고 단순함을 추구하는 것이 목표다.
function getFirst(array, isEven) {
return array.find(x => (isEven ? x % 2 === 0 : x % 2 !== 0));
}
위 소스는 한 줄로 깔끔하게 작성 되어져 있지만 가독성 측면에서 좋지 않을 수 있다. 한 눈에 이해하기가 어려울 수 있기 때문이다.
개선을 위한 코드를 살펴보자.
function getFirst(array, isEven) {
if (isEven) {
return array.find(x => x % 2 === 0);
} else {
return array.find(x => x % 2 !== 0);
}
}
위와 같이 소스를 조금 풀어서 가독성을 개선하는 방법을 생각할 수 있다.
하지만 전달 받은 파라미터를 판단하여 분기문으로 처리하는 방식은 단순하지 않을 수 있다.
function getFirstOdd(array) {
return array.find(x => x % 2 !== 0);
}
function getFirstEven(array) {
return array.find((x) => x % 2 === 0);
}
각각의 함수로 선언하여 처리할 경우 가독성을 높여 좀 더 단순화 할 수 있다.
UI View를 위한 목적의 로직에서 서버 통신과 같은 로직을 같이 선언하기 보다는
class LoginView {
display() {
// display view..
}
onLoginButtonClick() {
fetch('https://server.com')
.then(data => data.json())
.then(data => {
if (data.token) {
localStorage.setItem('TOKEN', data.token);
// update UI elements
} else {
// ...
}
})
.catch(error => {
if (error.statusCode === 500) {
// retry fetch?
} else if (error.statusCode === 400) {
// handle an error
}
// show error message
})
}
}
아래와 같이 View와 별도의 통신 로직을 class로 분리함으로써 코드의 복잡성을 줄이고 이해하기 쉽게 만들 수 있다.
또한 테스트 코드에서도 활용하기 더 좋다.
class LoginView {
constructor(userPresenter) {
this.userPresenter = userPresenter;
}
display() {
// display view..
}
onLoginButtonClick() {
this.userPresenter
.login()
.then(result => {
// update text UI element with result.displayMessage
// update button UI element with result.buttonText
})
}
}
class UserPresenter {
userService;
login() {
this.userService
.login()
.then(result => {
if (result.success) {
localStorage.setItem('TOKEN', result.token);
return {
displayMessage: result.message,
buttonText: 'Go Home',
};
} else {
return {
diplayMessage: 'Unable to login',
buttonText: 'Ok',
};
}
})
.catch(error => {
// Something really went wrong!
});
}
}
현재는 DB에서 사용자 정보만 삭제하면 되지만 나중을 위해 DB를 삭제하지 않고 삭제 처리된 것 처럼 처리할 수 있는 기능을 추가하였다.
function deleteUser(id, softDelete = false) {
if (softDelete) {
return this._softDelete(id);
}
return db.removeById(id);
}
위와 같은 기능을 추가할 경우 delete 이외의 다른 로직에서도 동일한 코딩을 필요하지 않음에도 추가하는 불필요한 작업이 추가 될 수 있다.
미래에 필요하다고 생각되는 기능을 불필요하게 추가하지 않도록 한다.