[Methodology] Clean Code와 Refactoring

개요

사내 세미나에 참석했다. 올해 처음 생긴 문화로, 시니어 분들께서 주기적으로 세미나를 진행해 주신다. 내가 속해 있는 곳은 상대적으로 시니어가 적은 곳이기도 하고, 여러 경험들을 공유받고도 싶어 되도록이면 참석할 수 있도록 노력한다.

이번 주제는 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을 처리해라.
  • 조건문에 대입문을 사용하는 경우는 지양하며, 별도 작성한다.
  • short-circuit은 디버깅과 프로그램 이해를 어렵게 한다.
  • break, continue 키워드로 흐름을 제어하고, 흐름 제어 boolean 변수 사용을 지양한다.

결론

  • 람다 쓰세요. 가독성이 올라가고, 명확한 코드를 작성할 수 있습니다.
  • logger의 레벨을 올리면 성능이 좋아집니다.

주석

  • 주석의 목적은 코드를 읽는 사람이 코드를 잘 이해하도록 돕는 수단이다.
  • 코드에서 빠르게 유추할 수 있는 애용은 주석으로 달지 않는다.
  • 이해하기 쉽고 깔끔한 코드가 복잡하고 주석이 많이 달린 코드보다 좋다.
  • 정규 표현식이나 인수 반환 값(compareTo)은 그 의미를 빠르게 파악할 수 있도록 주석으로 표현하면 좋다.
  • 중첩이 심하고 길이가 긴 블록은 닫는 불록에 // end of line 적어주는 것이 좋다.
  • 코드의 특정 위치를 나타내는 주석은 지양하고, FIXME, TODO, XXX를 사용한다.
  • 소스 변경 이력은 형상 관리 도구에서 관리하고 소스 코드 상단에 남기던 이력 관리 주석은 제거한다.
    • 날짜 쓰지 마세요. 날짜 추가하지 마세요.

결론

  • 프로젝트 표준이 없으면 만드세요.
  • 그것도 아니라면 일반적인 룰을 사용하세요.
  • 개인적인 취향을 따라가는 건 지양합시다.


Refactoring

  • 프로그램 코드의 실행 결과의 변화 없이 소프트웨어 내부 구조를 변경하는 것.
  • Bad small → Clean & Simple로 변경하는 것.

언제 해야 하는가?

  • 같은 문제점이 세 번째 반복되면, 리팩토링을 진지하게 고민.
  • 이해하기 쉬운 코드로 바꾸면, 기능 추가도 쉬워짐.
  • 안 보이던 버그가 보이기도 함.
  • 코드 리뷰와 함께 리팩토링하면 효과가 두 배!

어떻게 해야 하는가?

  1. 나쁜 냄새가 나는 코드를 찾는다.

  2. 코드를 어떻게 변경하는 것이 좋을지 고민해 본다.

  3. 리팩토리 수행 전, 테스트 케이스를 이용하여 테스트를 수행한다.

    • 리팩토링은 내부 구조만 변경하는 것이기에, 외부로 보이는 실행 결과가 바뀌면 안 된다.
    • 리팩토링 전/후 Testing 결과[1]가 같아야 함.
  4. 소나큐브라도 먼저 돌리세요. ^^;




  1. Testing을 하기 위해선 Test Code가 필요하다. 맨 처음의 내용과 일맥상통. ↩︎

Share