개요
사내 세미나에 참석했다. 올해 처음 생긴 문화로, 시니어 분들께서 주기적으로 세미나를 진행해 주신다. 내가 속해 있는 곳은 상대적으로 시니어가 적은 곳이기도 하고, 여러 경험들을 공유받고도 싶어 되도록이면 참석할 수 있도록 노력한다.
이번 주제는 Clean Code이며, 그간 들은 세미나 중 가장 깊이 와닿은 내용이었다. 발표자 분께선 'Clean Code 책에서 발췌한 내용이니 더 자세히 알고 싶으면 책을 읽으세요.'라고 하셨지만… 맞다, 나는 게으르다. 언제 읽을 지 모르니, 들을 수 있을 때 최선을 다해 들었다.
누군가 나에게 '깨끗한 코드를 작성하세요?'라고 묻는다면 '네!'라고 대답하긴 어렵겠지만, 그래도 난 꽤 많은 시간을 작명과 설계에 사용하는 편이다. 무엇보다 구조가 잡히지 못하면 코드를 한 줄도 작성하지 못하는 병(?)에 걸려 있기도 하고. 뭐, 그래서 더 인상깊었던 것 같다.
Clean Code의 개념
- Test Code를 작성하면 생산성이 안 나오는 것이 당연하다.
- 다만, 이후 어마어마한 이득을 얻을 수 있다.
- 코드의 클린함은 발견한 사람의 책임이다. 머문 자리가 깨끗하도록 노력하자.
“깨끗한 코드는 단순하고 직접적이다. 깨끗한 코드는 잘 쓴 문장처럼 읽힌다.” - 그래디 부치
- Code Golf - Simple TCP Server
- 각 언어로 누가 더 짧게 짜는지 대결.
- 간혹 Excel이 1등한다. (function 덕분)
- 어셈블리나 python이 1등할 때도 있음.
- 다른 사람이 이해하는 시간이 최소한이 되는 것이 Clean Code 이다.
Clean Naming, 어떻게 작성해야 하는가?
- 프로그래머가 가장 힘들어하는 일은 ‘작명’
- 보편적인 단어, 자주 사용되는 접미사는 단독으로 사용하지 않는다.
- 클래스 명, 객체 명은 명사로 사용한다.
- 동사 + 목적어(영문 소문자로 시작하는 동사 + 목적어)로 명명한다.
- 클래스, 메소드, 변수 등을 명명할 경우 주석으로 설명하기 보다는 코드 자체로 이해되게 작성한다.
- 보편적으로 사용하는 약어 외에, 모호한 의미의 약어를 사용하는 것은 좋지 않다.
- 접미사로 컨테이너가 포함된 단어는 복수형 단어로 대체하는 것이 좋다.
- List를 쓰지 말고 복수/단수로 구분하자.
(ㅠㅠ 반성)복수면 devices, 단수면 device! - tmp, sum, num 같은 보편적 이름 대신, 변수의 의미를 사용한다.
- 변수가 측정치를 담고 있다면, 변수명이 단위를 표시해 주는 것이 좋다.
- limit, start, end 대신 의미있는 변수명을 사용하자.
- 동일한 기능의 메소드가 서로 다른 동사를 사용하는 경우 혼란을 야기하므로, 일관성 있는 단어를 사용하자.
- 비슷한 단어라도 구체적인 상황에 맞는 구체적인 단어를 선택하도록 한다.
- send → deliver, dispatch, announce, distribute, route
- find → search, extract, locate, recover
- start → launcher, create, begin, open
- make → create, setup, build, generate, compose, add, new
- 연속된 숫자, 불필요한 단어(noise word)를 추가하는 것은 지양한다.
- 중첩 반복문에서 반복자로 사용되는 인덱스 i, j, k 보다는 prefix 등으로 명확한 의미를 드러낸다.
(요샌 generic을 사용한다.) - 검색하기 어려운 한 글자로 된 상수나 텍스트 코드는 지양한다. 변수의 의미를 전달할 수 있는 충분한 긴 이름을 사용.
- 의미없는 접두사, 접미어 사용하지 않는다. (m = 멤버변수)
- Boolean 타입의 변수명은 부정하는 의미의 용어를 피하는 것이 좋다.
- if - else문은 긍정적이고, 쉽고, 흥미로운 경우를 앞에 작성하는 것이 좋다.
- 소문자 L, 숫자 1, 대문자 O, 숫자 0은 혼동될 가능성이 있다. 소스코드 작성 시 개발 전용 폰트를 사용하는 것이 좋다.
Method
- 메소드명은 하는 일을 잘 표현하기 위해 충분히 긴 길이의 서술적인 이름으로 짓는다.
- 메소드는 작을수록 의미가 더 명확해지고, 하나의 주제만 다룰 수 있게 된다.
- 가급적이면 if - else, while에 들어가는 condition은 짧게 작성한다.
- 메소드는 한 가지 대표 기능만 수행해야 한다.
- 가능하면 메소드를 최대한 빨리 리턴한다. (fast return)
- 조건의 중첩이 있을 경우, 메소드 중간에서 반환하는 구문이 더 효율적이다. (validation)
- 조건은 눈에 뜨이는 곳에 미리 나타나도록 작성하는 것이 좋다.
- 삼항 연산자는 읽기 혼란스럽고 디버깅이 어렵다. (매우 간단한 경우라면 삼항 연산자를 써도 좋다.)
- 조건문에서 값이 더 유동적인 것을 왼쪽에, 고정적인 값을 오른쪽에 작성한다.
- null pointer exception 방지를 위해
"KIM".equals(studentName);
과 같이 작성하는데, 차라리 exception을 처리해라.
- null pointer exception 방지를 위해
- 조건문에 대입문을 사용하는 경우는 지양하며, 별도 작성한다.
- short-circuit은 디버깅과 프로그램 이해를 어렵게 한다.
- break, continue 키워드로 흐름을 제어하고, 흐름 제어 boolean 변수 사용을 지양한다.
결론
- 람다 쓰세요. 가독성이 올라가고, 명확한 코드를 작성할 수 있습니다.
- logger의 레벨을 올리면 성능이 좋아집니다.
주석
- 주석의 목적은 코드를 읽는 사람이 코드를 잘 이해하도록 돕는 수단이다.
- 코드에서 빠르게 유추할 수 있는 애용은 주석으로 달지 않는다.
- 이해하기 쉽고 깔끔한 코드가 복잡하고 주석이 많이 달린 코드보다 좋다.
- 정규 표현식이나 인수 반환 값(compareTo)은 그 의미를 빠르게 파악할 수 있도록 주석으로 표현하면 좋다.
- 중첩이 심하고 길이가 긴 블록은 닫는 불록에 // end of line 적어주는 것이 좋다.
- 코드의 특정 위치를 나타내는 주석은 지양하고, FIXME, TODO, XXX를 사용한다.
- 소스 변경 이력은 형상 관리 도구에서 관리하고 소스 코드 상단에 남기던 이력 관리 주석은 제거한다.
날짜 쓰지 마세요. 날짜 추가하지 마세요.
결론
- 프로젝트 표준이 없으면 만드세요.
- 그것도 아니라면 일반적인 룰을 사용하세요.
- 개인적인 취향을 따라가는 건 지양합시다.
Refactoring
- 프로그램 코드의 실행 결과의 변화 없이 소프트웨어 내부 구조를 변경하는 것.
- Bad small → Clean & Simple로 변경하는 것.
언제 해야 하는가?
- 같은 문제점이 세 번째 반복되면, 리팩토링을 진지하게 고민.
- 이해하기 쉬운 코드로 바꾸면, 기능 추가도 쉬워짐.
- 안 보이던 버그가 보이기도 함.
- 코드 리뷰와 함께 리팩토링하면 효과가 두 배!
어떻게 해야 하는가?
-
나쁜 냄새가 나는 코드를 찾는다.
-
코드를 어떻게 변경하는 것이 좋을지 고민해 본다.
-
리팩토리 수행 전, 테스트 케이스를 이용하여 테스트를 수행한다.
- 리팩토링은 내부 구조만 변경하는 것이기에, 외부로 보이는 실행 결과가 바뀌면 안 된다.
- 리팩토링 전/후 Testing 결과[1]가 같아야 함.
-
소나큐브라도 먼저 돌리세요. ^^;
Testing을 하기 위해선 Test Code가 필요하다. 맨 처음의 내용과 일맥상통. ↩︎