profile image

L o a d i n g . . .

클린 코드란

We don't read code, we decode it
- Peter Seibel

우리는 코드를 읽는 것이 아니라 해석한다. 따라서 코드 해석에 드는 비용을 줄여야 한다.

클린코드에서는 코드를 짜는 것과 읽는 것의 비중이 1:10 정도라고 이야기한다. 

따라서, 코드 해석이 쉬우며, 해당 코드의 동작을 직관적으로 예측할 수 있는 코드가 좋은 코드라 할 수 있겠다.

 

깨끗한 코드를 위한 팁!

1.  함수명은 반드시 동사를 써라!

예를 들면,
function userData() {}
라는 함수명은 좋은 이름이 아니다.

function loadUserData() {}
라는 함수명이 좋은 이름이다.

이렇게 이름을 짓게 되면, 함수가 너무 많은 역할을 하는 것은 아닌지 알게 된다.

함수는 한 가지만을 해야하고, 그 한 가지를 잘 해야 한다.

위의 loadUserData() 함수는 유저데이터를 불러오는 역할만 해야한다.
만약 해당 함수가 유저 데이터를 불러오는 것 외에 다른 것도 한다면, 해당 함수를 쪼개야 할 수 있다.

함수의 이름을 결정해야 하는데 고민이 많다면? 함수가 하나의 역할만 수행하지 않는다는 것이다 -> 변경 필요.

 

2.  함수  인수 개수는 3개를 넘지 않도록 하자!

함수가 너무 많은 수의 인수를 필요로 한다면, 다른 사람이 봤을 때 혼란스러울 수 있다. 어떤 인수가 어떻게 쓰이고 어떤 역할을 하는지 말이다.

인수가 많이 필요할 경우에는 Object로 정리해서 parameter로 넘기자.

이렇게 하는 것이 함수의 역할을 파악하기가 좋다.

 

3.  함수  인수에 boolean 값을 넘기는 것을 피하자

boolean 값을 함수로 보낸다는 것은, 함수 안에 boolean 값에 따라 분기가 되는 if else가 있다는 것이다. 그 각각의 if, else는 다른 함수로 분리하는 것이 좋다.

왜냐면, 함수는 단 한 개의 액션만 해야하기 때문에!

 

4. 명령과 조회를 분리하라

앞서 말했다싶이, 함수는 뭔가를 수행하거나 뭔가를 조회하거나 하나의 역할만을 해야한다. 
두 개의 역할을 동시에 하면 이상한 함수가 탄생한다. 

명령과 조회를 한 번에 처리하는 함수가 있을 경우, 함수를 분리해서 작성하는 것이 읽기에 명확하다.

 

5. 오류 코드보다 예외를 사용하라

if (deletePage(page) == E_OK) {
    if (registry.deleteReference(page.name) == E_OK) {
        if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {
            logger.log("page deleted");
        } else {
            logger.log("configKey not deleted");
        }
    } else {
        logger.log("deleteReference from registry failed");
    }
} else {
    logger.log("deleted failed");
    return E_ERROR;
}

오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 하는 문제에 부딪힌다. 반면 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.

try {
    deletPage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(pagename.makeKey());
}
catch (Exception e) {
    logger.log(e.getMessage());
}

사실 try / catch 불록은 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 해당 블록을 별도 함수로 뽑아내는 편이 좋다.

public void delete(Page page) {
    try {
        deletePageAndAllReferences(page);
    }
    catch (Exception e) {
        logError(e);
    }
}

private void deletePageAndAllReferences(Page page) throws Exception {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deletKey(page.name.makeKey());
}

private void logError(Exception e) {
    logger.log(e.getMessage());
}

오류 처리도 한 가지 작업이다. 함수는 '한 가지' 작업만 해야 하므로 오류를 처리하는 함수는 오류만 처리해야 마땅하다.

 

6. 여러 예외가 발생하는 경우 Wrapper 클래스로 감싸자 

외부 라이브러리를 이용하면 다양한 예외 클래스를 마주하게 된다. 그리고 이러한 예외들을 처리하려면 다음과 같이 상당히 번거로워 진다. 

ACMEPort port = new ACMEPort(12);
try {
    port.open();
} catch (DeviceResponseException e) {
    log.error(e.getMessage());
} catch (ATM1212UnlockedException e) {
    log.error(e.getMessage());
} catch (GMXError e) {
    log.error(e.getMessage());
} finally {
    ...
}

 

이러한 상황에서 Wrapper 클래스를 이용해 감싸면 효율적으로 예외 처리를 할 수 있다.

LocalPort port = new LocalPort(12);
try {
    port.open();
} catch (PortDeviceFailure e) {
    log.error(e.getMessage());
} finally {
    ...
}

public class LocalPort {

    private ACMEPort innerPort;

    public LocalPort(int portNumber) {
        this.innerPort = new ACMEPort(portNumber);
    }

    public void open() {
        try {
            innerPort.open();
        } catch (DeviceResponseException e) {
            throw new PortDeviceFailure(e);
        } catch (ATM1212UnlockedException e) {
            throw new PortDeviceFailure(e);
        } catch (GMXError e) {
            throw new PortDeviceFailure(e);
        }
    }

}

 

7.  객체지향에 얽매이지 말기

시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하면, 클래스와 객체 지향 기법이 적합하다.

반면 새로운 함수, 새로운 동작을 추가하는 유연성이 필요한 경우라면, 절차적인 코드와 자료구조가 적합하다. 

우수한 소프트웨어 개발자는 편견없이 이 사실을 이해해 직면한 문제에 최적인 해결책을 선택한다. 

 

8. 디미터 법칙을 지키자

모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다. 객체의 내부 구현, 구조에 대해서 몰라야한다는 뜻이다.

String outputDir = ctxt.getOptions().getScrathDir().getAbsolutePath();

ctxt가 객체라면, 위의 코드는 아래 코드와 같이 나누는 것이 좋다. 객체의 내부 구조를 노출시키므로 디미터 법칙을 위반하기 때문이다.

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
String outputDir = scratchDir.getAbsolutePath();

만약 ctxt가 객체가 아닌, 자료구조라면 내부 구조를 숨길 필요가 없으므로 디미터 법칙이 적용되지 않는다.

 

9. 클래스는 작아야 한다. 그리고 맡은 책임이 하나여야 한다

클래스의 간결한 이름이 떠오르지 않는다면, 클래스에 여러 책임을 떠안기지 않았는지 의심해야 한다. 

단일 책임 원칙(SRP) 은 클래스나 모듈을 변경할 이유가 하나, 단 하나뿐이어야 한다는 원칙이다. 

많은 개발자는 자잘한 단일 책임 클래스가 많아지면 큰 그림을 이해하기 어려워진다고 우려한다. 큰 그림을 이해하려면 이 클래스 저 클래스 수없이 넘나들어야 한다고 걱정한다.

그러나 작은 클래스가 많은 시스템이든 큰 클래스 몇 개뿐인 시스템이든 돌아가는 부품 수는 그 수가 비슷하다.

규모가 어느 수준에 이르는 시스템은 논리가 많고도 복잡하다. 이런 복잡성을 다루려면 체계적인 정리가 필수다. 그래야 개발자가 무엇이 어디에 있는지 쉽게 찾는다. 큼직한 다목적 클래스 몇 개로 이뤄진 시스템은 (변경을 가할 때) 당장 알 필요가 없는 사실까지 들이밀어 독자를 방해한다.

따라서, 큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 바람직하다. 작은 클래스는 각자 맡은 책임이 아나며, 변경할 이유가 하나며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.

 

10. 클래스가 응집력을 잃는다면 쪼개라!

응집도를 유지하면 작은 클래스 여럿이 나온다.

 

11. 변경하기 쉬운 클래스를 생각하자.

요구사항은 수시로 변한다. 그리고 뭔가를 변경할 때마다 시스템이 의도대로 동작하지 않을 위험이 따른다.

깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반되는 위험을 낮춘다. 

즉, 시스템에 변경 사항이 있을 때 클래스에 '손대어' 고치는 것을 줄여 시스템을 망가뜨릴 위험을 낮춘다는 것이다.

따라서 새 기능을 수정하거나 기존 기능을 변경할 때 건드릴 코드가 최소인 시스템 구조가 바람직하다.
이상적인 시스템이라면 새 기능을 추가할 때 시스템을 확장할 뿐, 기존 코드를 변경하지는 않는다.

-> SRP(단일 책임 원칙), OCP(확장에 개방적이고 수정에 폐쇄적이어야 한다.)를 지키면 변경하기 쉬운 클래스가 따라온다!

 

12. 변경으로부터 격리

구현 클래스가 아닌, 인터페이스와 추상 클래스를 사용해 변경으로부터 격리할 수 있다.

이를 통해 시스템의 결합도를 낮출 수 있다. 결합도가 낮다는 것은 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미이다.

결합도를 최소로 줄이게 되면 자연스럽게 DIP(Dependency Inversion Priciple)를 따르는 클래스가 나온다. 
본질적으로 DIP는 클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙이다. 

 

13. 주석은 나쁜 코드를 보완하지 못한다. 

따라서 표현력이 풍부하고 깔끔한 코드를 만드는데 집중해야 한다.

복잡하고 어수선해서 주석이 필요한 코드가 있을 수도 있다.

좋은 주석은 다음에 해당한다. 

- 코드로 표현하지 못하는 정보를 제공하는 주석

- 의도를 설명하는 주석

- 결과를 경고하는 주석

- 중요성을 강조하는 주석

 

참고

https://velog.io/@tmdgh0221/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC

https://mangkyu.tistory.com/132

 

[리뷰] 클린 코드(Clean Code) 핵심 요약 및 정리

두 번째로 클린코드를 읽으면서 처음 읽었을때와 느꼈던 점이 조금 달라진 것 같습니다. 이번에도 역시 제가 제대로 적용하지 못했던 부분 혹은 개념적으로 부족했던 부분을 정리하고, 앞으로

mangkyu.tistory.com

https://shinsunyoung.tistory.com/124

 

클린 코드(Clean Code) 요약

읽은 지 꽤 된 책이지만 내용이 너무 좋아 다시 리마인드 할 겸 의미 있었던 내용들을 적어두려고 합니다. 때문에 해당 게시글에서는 책의 모든 내용을 다루지 않습니다. 🐰 깨끗한 코드란? 이

shinsunyoung.tistory.com

https://blog.naver.com/n_cloudplatform/222508039135

 

[네이버클라우드 개발자 스토리] 좋은 코드란 무엇일까?🤔 #클린코드 이야기

Intro 지난 8월, 프로젝트 설계가 끝난 후 개발 시작 전 내부적으로 '클린코드 세미나'를 진행...

blog.naver.com

http://www.yes24.com/Product/Goods/11681152

https://www.youtube.com/watch?v=Jz8Sx1XYb04 

복사했습니다!