본문 바로가기
클린코드 샘플(JavaScript)

[2] 클린코드 Rule

by 찬찬2 2022. 11. 17.

 

[1] DRY(Don't Repeat Yourself) - 반복하지마라


[2] KISS(Keep It Simple, Stupid) - 심플하고, 멍청하게 유지하자
■ Code : 10줄 짜리 코드를 1줄로 줄이기 위해 화려한 테크닉을 이용해서 가독성을 떨어뜨리기 보다는 누구나 이해할 수 있도록 심플하고 간결하게 작성하는 것이 좋다.
 Function : 별도의 주석을 달지 않더라도 함수의 이름, 매개변수 그리고 구현된 사항의 코드를 읽었을 때 한 번에 이해할 수 있도록 한 가지의 기능을 수행하는 코드를 작성하는 것이 좋다.
 Class : 한 가지의 챔임만 담당하는 클래스를 심플하게 만드는 것이 좋다.
 View : 사용자에게 보여지는 UI를 담당하는 컴포넌트에는 별도의 비즈니스 로직을 포함하지 않고 최대한 심플하고 멍청하게 UI에 관련된 로직들만 담당하고 있어야 한다.
 Service : 여러 가지 기능을 복합적으로 담당하는 하나의 큰 서비스를 만들기보다는 단 하나의 기능을 담당하는 개별적인 심플한 서비스를 만드는 것이 좋다.


[3] YAGNI(You Ain't Gonna Need It) - 너 그거 필요 없어


[4] 식별자에 의미를 부여하라.
 식별자 이름을 줄여쓰지 말아라.
 boolean 타입의 변수는 접두사(prefix)를 사용해라. "isLoggedIn", hasFollowers" etc...
 함수는 동사를 넣어 사용해라."get, set, reset, fetch, add, remove, update, delete, etc...


[5] 기본 매개변수(default parameter)를 사용해라.


[6] 인수/인자를 3개로 제한해라. 만약 더 필요하다면 객체에 담아라.


[7] 함수의 매개변수로 boolean 타입을 사용하지 마라. 함수의 기능을 쪼개어 따로 관리해라.


[8] 조건문을 캡슐화해 함수로 사용하라.


KISS Examples

 

// dirty
function getFirst(array, isEven){
    return array.find(x => (isEven ? x % 2 === 0 : x % 2 !== 0));
}

// refactor case#1
function getFirst(){
    if(isEven){
        return array.find(x => x % 2 === 0);
    }else{
        return array.find(x => x % 2 !== 0);
    }
};

// refactor case#2
function getFirstOdd(){
    return array.find(x => x % 2 === 0);
}
function getFirstEven(){
    return array.find(x => x % 2 !== 0);
}

 

■ 3항연산자를 풀고, boolean 타입의 매개변수에 따라 true에 대한 로직, false에 대한 로직으로 분리했다.

 

// dirty
function updateAndPrint(rawData){
    // logic#1
    db.update(rowData);
    // logic#2
    printer.print(data);
}

// refactor case#1
function update(rawData){
    db.update(rawData);
    return data;
}
function print(data){
    printer.print(data);
};

 

■ 두 가지의 기능이 있던 updateAndPrint 함수를 분리하였다.

 

// dirty
class UserOrderService {
    userDb;
    orderDb;
    paymentClient;

    processUserOrder(userId, orderId){
        // logic#1
        const user = userDb.select(/* db query */);
        if(!user){
            throw Error("...");
        }
        // logic#2
        const order = orderDb.select(/* db query */);
        if(!order){
            throw Error("...");
        }
        // logic#3
        paymentClient
            .connect(/* url */)
            .then(/* process payment */)
            .catch(/* retry */);

        orderDb.updateOrder(order, PAID);
    }
}

// refactor case#1
class UserService {
    userDb;
    getUser = () => userDb.select(/* db query */);
}

class OrderService {
    orderDb;
    createOrder = (user, product) => {};
    getOrder = (orderId) => orderDb.select(/* db query */);
    updateOrder = (order) => {
        orderDb.updateOrder(order, PAID);
    }
}

class PaymentsService {
    paymentClient;
    processPayment(orderRequest){
        return paymentClient
            .connect(/* url */)
            .then(/* process payment */)
            .catch(/* retry */);
    }
}

 

■ userOrderService 클래스를 기능별로 3개의 클래스로 분리하였다.

 

// dirty
class LoginViwe {
    display(){
        // display view..
    }

    onLoginButtonClick(){
        fetch(/* url */)
            .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
            });
    }
}

// refactor case#1
class LoginView {
    constructor(userPresenter){
        this.userPresenter = userPresenter;
    }
    display(){
        // display view..
    }
    onLoginButtonClick(){
        this.userPresenter
            .login()
            .then(result => {
                // update text UI element
                // update button UI element
            });
    }
}

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 {
                        displayMessage: "Unable to login",
                        buttonText: "Ok",
                    }
                }
                
            })
            .catch(error => {
                // Something..
            });
    }
}

const userPresenter = new UserPresenter();
const login = new LoginView(userPresenter);

 

■  View와 관련된 class에서 비즈니스 로직부분을 분리해 따로 새로운 class를 만들었다.

댓글