[Clean Code] 클린코드-2

Junho Bae·2021년 3월 8일
0

Clean Code

목록 보기
1/3

clean code -2

“클린코드 - 애자일 소프트웨어 장인정신”, 로버트 C.마틴 님의 책을 읽고 정리한 내용입니다.

chapter4. 주석

주석은 순수하게 선하지 못하다.*
주석은 필요악이다. 프로그래밍 언어 자체가 표현력이 풍부하고, 이를 치밀하게 사용해서 의도를 표현할 능력이 있다면 우리에게 주석은 사실상 필요하지 않다.

저자는 코드를 쓸 때 마다 표현력이 없는 자신에 대해 푸념해야 한다고 합니다. 즉, 주석을 통해서 불필요한 오해를 남기거나 잘못된 정보를 전달할 바에는, 코드 자체를 명확하게 사용해서 독자를 이해시켜야 한다는 것입니다.

주석은 업데이트가 보장되어 있지 않기 때문에, 주석을 엄격하게 관리하기 보다는 코드를 깔끔하게 정리하여 표현력을 기르는 것이라고 합니다,

주석은 나쁜 코드를 보완하지 못한다.*
지저분한 모듈을 보고 주석을 달 생각을 할 것이 아니라, 코드를 정리해야 합니다.

코드로 의도를 표현하라

// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if ((employee.flags & HOURLY_FLAG) && (employee.age>65))

보다는

if(employee.isEligibleForFullBenefits())
와 같은 방식으로 표현되어야 한다는 것입니다.

좋은 주석
1) 법적인 주석 : 회사가 정한 표준에 맞춰서, 각 소스파일 첫 머리에 저작권 정보와 소유권 정보는 필요할 수 있습니다.

2) 정보를 제공하는 주석 : 기본적인 정보를 주석으로 제공한다면 편리하지만, 가능하다면 함수 이름에 정보를 담는 것이 더 좋습니다.

3) 의도를 설명하는 주석

4) 의미를 명료하게 밝히는 주석 : 인수나 반환값 자체를 명확하게 만드는 것이 가장 좋으나, 인수나 반환값이 만약 표준 라이브러리 혹은 변경할 수 없는 코드에 속한다면 이를 주석으로 밝히는 것이 좋습니다.

assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b

사실 이러한 주석도 결국 주석이 올바른지 검증하는 것이 쉽지 않기 때문에 주의해야 합니다.

5) 결과를 경고하는 주석

6) TODO 주석 : 프로그래머가 필요하다 여기지만 당장 구현하기 어려운 업무를 기술합니다. 더 이상 필요없는 기능을 삭제하라는 알림, 누군가에게 문제를 봐달라는 요청, 더 좋은 이름을 떠올려달라는 부탁 등등. 하지만 나쁜 코드를 남겨 놓는 핑계가 되어서는 안됩니다.

7) 중요성을 강조하는 주석 : 자칫 대수롭지 않다고 여겨질 만한 뭔가의 중요성을 강조하기 위해서도 사용.

좋은 주석*
1) 주절거리는 주석 : 특별한 이유 없이 의무감으로 혹은 프로세스에서 하라고 하니까 마지못해 다는 주석.

2) 같은 이야기를 반복하는 주석 : 어메이징한 톰캣의 주석

3) 오해할 여지가 있는 주석 : 의도는 좋았으나, 프로그래머가 딱 맞을 정도로 엄밀하게 주석을 달아 놓지는 못할 수도 있습니다.

4) 의무적으로 다는 주석 : 모든 함수에 Javadocs를 달거나 모든 변수에 주석을 달아야 한다는 규칙등을 말합니다. 이러한 주석은 코드를 오히려 복잡하게 만들고, 거짓말을 퍼트려서 무질서를 초래한다고 합니다.

/**
*
* @param title CD 제목
* @param author CD 저자
* @param tracks CD 트랙 숫자
* @param durationInMinutes CD길이
*/

public void addCD(String title, String author, int tracks, int durationInMinutes) {
	
	CD cd = new CD();
	cd.title = title;
	cd.author = author;
	cd.tracks = trakcs;
	cd.duration = durationInMinutes;
	cdList.add(cd);
	
}

매우 불필요하죠.

5) 이력을 기록하는 주석 : 이력은 소스코드 관리 시스템에 맡깁시다.

6) 있으나 마나 한 주석 : 너무 당연한 사실을 언급하며 새로운 정보를 제공하지 않는 주석입니다.

  • 이런 주석은 오히려 개발자가 주석을 무시하는 습관에 빠지게 할 수 있습니다.

7) 함수나 변수로 표현할 수 있다면 주석을 달지 마라.

8) 위치를 표시할 수 있는 주석 : 극히 드물지만 특정한 배너 아래에 함수들을 모아 놓으면 유용한 경우가 있을 수도 있지만, 가독성이 떨어지니까 너무 자주 쓰지 말고, 드물게 사용하는 것이 좋다고 합니다.

9) 닫는 괄호에 다는 주석 : 장황한 함수라면 의미가 있을 수도 있지만, 작고 캡슐화된 함수에는 의미가 없습니다. 잡음일 뿐..

10) 주석으로 처리한 코드 : 극혐으로 쓰면 안됩니다.

11) html 주석 : 이것도 극혐이라 쓰면 안됩니다.

12) 전역 정보 : 주석을 달아야 한다면 근처의 코드만! 코드 일부에 주석을 달면서 시스템의 전반적인 정보를 기술하면 안됩니다.

13) 너무 많은 정보 : 거의 불가사의한 정보. 역사 이런거 쓰지 말라고 합니다.

14) 모호한 관계 : 주석을 쓸거면 명확해야 합니다. 주석이 주석을 요구하면 안됩니다.

chapter5. 형식 맞추기

프로그래머라면 형식을 깔끔하게 맞춰 코드를 짜야 한다. 코드 형식을 맞추기 위한 간단한 규칙을 정하고, 그 규칙을 착실히 따라야 한다. 팀으로 일한다면 팀이 합의해 규칙을 정하고 모두가 그 규칙을 따라야 한다,.

코드의 형식은 너무 중요하다고 합니다. 코드 형식은 의사소통의 일환이기 때문입니다.

오늘 구현한 기능이 다음 버전에서 바뀔 확률은 아주 높다. 그런데 오늘 구현한 코드의 가독성은 앞으로도 바뀔 코드의 품질에 지대한 영향을 미친다. 오랜 시간이 지나 원래 코드의 흔적을 더이상 찾아보기 어려울 정도로 코드가 바뀌어도, 맨 처음 잡아놓은 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다. 원래 코드는 사라질지라도 개발자의 스타일과 규율은 사라지지 않는다.**

적절한 행 길이를 유지하자.
Junit, FiteNess, Tomcat 등등의 커다란 시스템도 대부분 파일들이 500줄을 넘어가지 않고 대다수가 200줄 미만입니다. 일반적으로 큰 파일이 작은 파일보다 이해하기가 좋으니, 크기를 너무 키우지 않는 것이 좋다고 합니다.

신문기사처럼 쓰자
좋은 신문기사처럼 쭉 읽어 내려갔을 때 고차원에서 저차원으로 이동해야 합니다.

개념은 빈 행으로 분리
개념적으로 연관이 있는 코드는 빈 행을 두어 구분을 해야합니다

세로밀집도
세로 밀집도는 연관성을 의미합니다. 서로 밀접한 관련을 갖는 코드는 세로로 가까이 놓아여 합니다.

수직거리
타당한 근거가 없다면, 서로 밀접한 개념은 한 파일 안에 있어야 하고 따라서 protected 선언은 지양해야 합니다.

변수선언
변수는 사용하는 위치에서 최대한 가까이 선언해야 합니다.

가로형식
명백하게 짧은 행이 바람직 합니다. 저자는 120자 정도로 행 길이를 제한합니다.

가로 공백과 밀집도
가로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현합니다.

chapter 6. 객체와 자료구조

변수를 private으로 정의하는 이유가 있다. 남들이 변수에 의존하지 않게 만들고 싶어서이다. 충동이든 변덕이든, 변수 타입이나 구현을 맘대로 바꾸고 싶어서다. 그렇다면 어째서 수많은 프로그래머가 getter, setter를 당연하게 public으로 외부에 노출할까?

자료와 객체는 다릅니다. 객체의 구현을 감추기 위해서는 추상화가 필요합니다. 자료를 세세하게 공개하는 것 보다는 추상적인 개념으로 표현하는 편이 더 좋다고 저자는 말합니다. 또한, 저자는 객체와 자료의 차이를 이해하고 맹목적으로 한쪽만을 추구해서는 안된다고 합니다.

자료/객체 비대칭

객체 : 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개
자료구조 : 자료를 그대로 공개하며 별다른 함수는 제공하지 않음.

geometry 예제를 보면 알 수 있듯이 각각은 장단이 있습니다.
객체 - 새로운 클래스를 추가하더라도 메서드의 변경이 필요 없음. 자유로움
대신, 새로운 함수를 추가하기 위해서는 도형 클래스 전부를 고쳐야 함.

자료구조 - 새로운 함수를 추가하고 싶더라도 도형을 변경할 필요가 없음
대신, 새로운 도형을 추가하기 위해서는 함수를 전부 변경해야 함.,

절차적인 코드는 기존 자료구조를 변경하지 않으면서 새 함수를 추가하기 쉽다. 반면, 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.

-> 복잡한 시스템을 짜다 보면, 새로운 함수가 아니라 새로운 자료 타입이 필요한 경우가 생길수도 있고, 이 떄는 객체지향 기법이 적합합니다.
-> 반면, 새로운 자료 타입이 아니라 새로운 함수가 필요한 경우도 생깁니다. 이 래든 절차적인 코드와 자료구조가 더 적합합니다.

=> 따라서, 한 쪽을 맹목적으로 따르기 보다는 적합한 코드를 짜야 합니다.

디미터 법칙

모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다.
정확하게는, 클래스 C의 메서드 f는 다음과 같은 객체의 메서드만 호출해야 한다 는 법칙입니다.

  • 클래스 C
  • f가 생성한 객체
  • f 인수로 넘어온 객체
  • c 인스턴스 변수에 저장된 객체

final String outputDir = ctxt.getOptions().getScracthDir().getAbsolutePath();
이렇게 많이 짜는 것 같은ㄷ,,

이러한 코드를 기차충돌 이라고 부릅니다. 이는 일반적으로 조잡하다 여겨지는 방식이기 때문에 피하는 편이 좋다고 합니다. 이는

Options opts = ctxt.getOptions();
File scatchDir = opts.getScaratchDir();
final String ouputDir = scatchDir.getAbsolutePath();
와 같이 나누는 편이 좋다고 합니다. 하지만 이러한 코드를 사용하는 함수는 많은 객체를 탐색할 줄 알아야 합니다.

결론
객체와 자료구조를 잘 구분해서 잘 씁시다!

chapter7. 오류처리

오류처리는 중요하다. 하지만 오류 처리로 인해 코프로그램 논리를 이해하기 어려워진다면 깨끗한 코드라 부르기 어렵다.

오류 코드 보다 예외를 사용하라 - Try,Catch,Finally 부터 작성해라
try 블록은 마치 트랜잭션과 같아서, try 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 합니다.

코드가 예외를 던지기 때문에, 테스트를 성공시킬 수 있습니다. 그리고 이 때부터 리펙터링이 가능합니다. catch 블록에서 에러의 유형을 좁혀냅니다.

이후부터 TDD를 사용해 핋요한 나머지 논리를 추가할 수 있습니다.

미확인 예외를 사용하라
과거 메서드를 선언할 때는 메서드가 반환할 예외를 모두 열거했습니다. 이를 확인된 예외라고 부릅니다. 이는 멋진 아이디어로 여겨졌으나, 현재 파이썬 루비 등의 언어는 이를 지원하지 않는데도 별 무리가 없습니다. 그 비용과 편익을 계산해봐야 합니다.

확인된 예외는 OCP를 위반합니다. 메서드에서 확인된 예외를 던졌는데, catch블록이 세 단계 위에 있다면, 그 사이 메서드 모두가 선언부에 해당 예외를 정의해야 하는데, 이는 하위 단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 한다는 것입니다.

예외에 의미를 제공하라

null을 반환하지 마라
오류처리에서 오히려 오류를 유발하는 해우이가 바로 null을 반환하는 습관입니다.

public void registerItem(Item item) {
	if(item!=null) {
	ItemRegistry registry = persistentStore.getItemRegistry();
	if(registry!=null) {
		Item existing = registry.getItem(item.getID());
		if(existing.getBillingPeriod().hasRetailOwner()) {
			existing.register(item)	
			}	
		}
	}
}

이는 호출자에게 일을 떠넘기는 거고, 누구 하나라도 null을 빼먹는다면 어플리케이션은 통제 불능 상태에 빠질 것입니다.

심지어, 이 코드에서 둘째 중의 persistenceStore가 null인 경우도 처리가 되어있지 않습니다. 바로 널포인터익셉션.

메서드에서 null을 반환하고 싶은 유혹이 든다면, 그 대신 예외를 던지거나 특수 사례 객체를 반환하는 것이 좋습니다.

chapter8. 경계

소프트웨어 경계를 깔끔하게 처리하는 기법과 기교를 살펴본 장입니다.

외부 코드 사용하기
예제의 map과 같은 경우, map 혹은 이와 비슷한 경계 인터페이스를 여기 저기 넘기지 말고, 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의하여 캡슐화 하는 것이 좋습니다.

log4j 익히기

chapter 9. 단위 테스트

애자일과 TDD 덕분에 단위 테스트를 자동화하는 프로그래머들이 이미 많아졌으며 점점 더 늘어나고 있습니다. 하지만 이 분애에 제대로 테스트를 추가하려고 급하게 서두르는 와중에 많은 프로그래머들이 제대로 된 테스트 케이스를 작성해야 한다는 미묘한 사실을 놓쳐버렸습니다.

TDD 법칙 세 가지
1) 실패하는 단위 테스트를 작성할 떄 까지 실제 코드를 작성하지 않는다.
2) 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
3) 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

이와 같은 방식으로 일한다면, 매일 수십개, 매달 수백개, 매년 수천개에 달하는 테스트케이스가 나오고, 실제 코드와 맞먹을 정도로 방대한 테스트 코드는 심각한 관리 문제를 유발합니다!

깨끗한 테스트 코드 유지하기

하지만 팀은 지저분한 테스트 코드를 내놓으나 테스트를 안 하나 오십보 백보라는, 아니 오히려 더 못한다는 사실을 깨닫지 못했다. 문제는 실제코드가 진화하면 테스트 코드도 변해야 한다는데 있다. 그런데 테스트 코드가 지저분할 수록 변경하기 어려워진다.

깨끗한 단위 테스트 코드를 작성해야 합니다…

테스트는 유연성, 유지보수성, 재사용성을 제공한다
테스트 코드를 깨끗하게 유지하지 않으면 결국 잃어버립니다. 그리고 테스트 케이스가 없으면 실제 코드를 유연하게 만드는 버팀목도 사라집니다! 코드의 유연성, 유지 보수성, 재 사용성은 테스트코드에 의해 지탱됩니다.

깨끗한 테스트코드
깨끗한 테스트 코드는 가독성, 가독성,가독성 이 필요합니다. 실제 테스트 코드보다도 더. 명료성, 단순성, 풍부한 표현력이 가독성을 높여줍니다.

테스트당 assert 하나
JUnit으로 테스트 코드를 짤 때는, 함수마다 assert문을 단 하나만 사용해야 한다고 주장하기도 하는데, 가혹한 규칙일 수도 있지마ㅓㄴ 장점이 있습니다. 이해하기 쉽고 빠르기 때문입니다.

given-when-then의 관례를 사용하는 것도 좋습니다. 혹은, template method 패턴을 사용하여, given, when 부분을 부모 클래스에 두고 then 부분을 자식 클래스에 두는 것입니다. 혹은, @Before 함수에 given,when을 넣고,@Test 함수에 given을 넣어도 됩니다.

-> 사실 배보다 배꼽이 더 큽니다. 그냥 assert문을 여럿 사용하는 편이 좋다고 생각한다고 합니다..? 응..?

-> 결국, 필요하면 여러개 그냥 쓰되, 최대한 줄이는 것이 좋습니다.

테스트당 개념 하나
테스트 함수마다 한 개념만 테스트하라는 원칙입니다.

FIRST
Fast : 테스트는 빨라야 합니다! 자주 돌리지 않으면 초반에 문제를 찾아내지도 못하고 고치지도 못합니다!

Independent : 각 테스트는 서로 의존하지 않아야 합니다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안되고 언제 어떤 순서로 실행해도 괜찮아야 합니다.

Repeatable : 테스트는 어떤 환경에서도 반복 가능해야 합니다! 테스트가 돌아가지 않는 환경이 하나라도 있다면 테스트가 실패한 이유를 둘러댈 변명이 생기기 때문입니다.

Self-Validating : 테스트는 bool 값으로 결과를 내야 합니다. 성공, 아니면 실패. 통과 여부를 알려고 로그 파일을 읽게 만들어서는 안됩니다.

Timely : 테스트 코드는 적시에 작성해야 합니다. 단위 테스트는 실제 코드를 구현하기 직전에 구현합니다. 실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 발견할 지도 모릅니다!

결론
이 주제는 책 한권입니다… 더 공부합시다.

profile
SKKU Humanities & Computer Science

0개의 댓글