[Clean Code] 5장 형식 맞추기

Bam·2023년 1월 30일
0

책꽃이

목록 보기
5/8
post-thumbnail

우리가 어떤 글을 볼 때, 가독성이 좋다고 느끼는 경우가 있다. 그런 경우 대부분 글의 형식이 잘 갖춰져 있다는 전제가 성립된 채로 글이 작성되었기 때문이다.

소스 코드도 마찬가지로 일종의 글이기 때문에 형식을 잘 갖춰서 작성한다면 깨끗한 코드를 작성하는데 탄탄한 기반이 될 수 있을 것이다.


형식을 맞추는 목적

서론에서도 이야기했지만 형식에 맞춘 소스 코드는 개발자간의 의사소통을 하는데 있어 중요한 역할을 한다. 개발자 간의 협업에서 의사소통은 상당히 중요한 문제이기 때문에 형식을 잘 갖춰서 코드를 작성하는 것은 개발자의 의무라고 할 수도 있다. 아래 두 문장은 같은 말을 의미하지만, 읽었을 때 형식을 잘 갖춘 첫 번째 문장이 주는 의미가 더 확실하지 않는가?

"나에게 사과를 주세요"
"사과를 너는 건내라 나에게"

코드의 기능 자체는 앞으로도 변할 수 있다. 하지만 원본 코드는 아무리 수정을 가해도 그 스타일이 남아있기 마련이다. 그리고 그 코드의 품질에 따라 유지보수의 품질도 좌지우지된다.

이제 원활한 소통을 위한 코드 형식이 무엇인지 살펴보려고 한다.


적절한 행 길이를 유지하라

위 그래프는 7개의 자바 프로젝트에 대한 평균 소스 코드 길이(세로 기준)이다. 네모를 관통하는 세로 실선은 최댓값과 최솟값을 의미한다. 프로젝트 이름들을 보면 상당히 거대하고, 유명한 프로젝트임을 알 수 있다. 그러나 평균 코드 길이는 약 65줄이다. 파일 하나 당 40~100중 사이라는 것이다.

이 그래프가 의미하는 것은 장황하게 써진 코드보다 짧은 코드들로 충분히 완성도있고 거대한 프로젝트를 만들어 낼 수 있다는 것이다. 그리고 중요한 것은 긴 코드보다 짧은 코드가 이해하기 쉽다라는 것이다.

신문 기사처럼 작성하라

잘 써진 신문 기사를 보면, 독자는 위에서 아래로 읽는다. 그리고 그 글 위에는 기사 내용을 몇 단어로 요약한 표제가 나온다. 대부분의 독자는 표제를 먼저 읽고 그 기사 내용을 읽을지 말지 결정한다.

이처럼 소스 코드도 신문기사처럼 작성하는 것이 좋다. 이름은 간단하면서도 설명이 가능하도록 짓는다. 소스 파일 첫 부분엔 고차원 개념과 알고리즘 설명을 적고, 아래로 갈수록 의도를 세세하게 표현한다. 마지막에는 저차원 함수와 세부 내역이 나온다.

신문을 잘 생각해보면 소설, 수필, 칼럼처럼 길지 않다. 필요한 내용만 적어서 정보를 전달한다. 기사가 짧다는 것도 신문을 읽을만하다고 느끼게 하는데 도움을 준다.

개념은 빈 행으로 분리하라

대부분의 코드는 위에서 아래, 왼쪽에서 오른쪽으로 읽는다. 각 행은 수식이나 절을 표현하고, 일련의 행 묶음은 완결된 생각 하나를 표현한다. 그리고 생각과 생각 사이에는 빈 행을 넣어서 분리하는 것이 마땅하다.

빈 행은 새로운 개념을 시작하는 시각적 단서이다. 읽는 도중 빈 행을 마주친다면 그 다음에 나오는 행에 자연스럽게 주목하게 될 것이다.

세로 밀집도

줄바꿈이 개념 분리를 의미한다면, 세로 밀집도는 연관성을 의미한다. 다시말해, 서로 밀접한 코드 행은 세로로 가까이 놓아야 한다라는 것이다. 아래 코드는 (쓸데없는)주석으로 밀접한 개념을 떨어뜨려놓은 코드와 붙여놓은 코드이다. 무엇이 더 잘 읽히는지는 말하지 않아도 알 수 있을 것이다.

const checkOrderAndCook = order => {
  /*
  	사용자로 부터 입력받은 order 배열의 인덱스 0 메뉴를 메인디쉬로 저장
  */
  let maindishOrder = order[0];
  /*
  	사용자로 부터 입력받은 order 배열의 인덱스 1 메뉴를 디저트로 저장
  */
  let dessertOrder = order[1];
  
  cookMaindish(maindishOrder);
  setTimeout(() => cookDessert(dessertOrder), 100000);
}

const checkOrderAndCook = order => {
  let maindish = order[0];
  let dessert = order[1];
  
  cookMaindish(maindishOrder);
  setTimeout(() => cookDessert(dessertOrder), 100000);
}

수직 거리

서로 밀접한 개념은 한 파일에 두고 세로 거리로 연관성을 표시한다.

함수의 동작 방식과 연관 관계를 살피다보면 소스 코드를 위아래로 뒤지게 된다. 이 작업은 꽤 많은 시간과 노력을 들이게 만든다. 이런 경우를 피하기 위해 밀접한 개념은 한 파일에 두는 것이 좋다.

또 그 중에서 서로 연관성이 깊은 함수끼리는 세로 거리를 가까이 두는게 좋다. 그렇지 않다면 함수를 찾기위해 코드를 위아래로 뒤져보아야하기 때문이다.

자바에서는 변수는 사용하는 위치에 최대한 가까이 두어야한다. 단, 함수가 짧을 경우 지역 변수는 함수 맨 위에 선언하도록 한다. 단 루프 변수(i, j, k)는 일반적으로 루프문 내부에서 선언하고 사용한다.

💬 자바스크립트는 호이스팅으로 인해서 가급적이면 해당 블록의 최상단으로 올려 쓰는 것이 좋습니다.

인스턴스 변수는 클래스 맨 처음(상단)에 선언한다. 변수간 세로 거리를 따로 두지 않는다.

종속 함수, 한 함수가 다른 함수를 호출하면 그 두 함수는 서로 붙여놓는다. 가능하다면 호출하는 함수를 호출되는 함수보다 위에 놓는다.

개념적 유사성, 개념적인 친화도가 높다면 그 코드들은 서로 끌어당긴다. 그렇기에 친화도가 높을수록 가까이에 배치한다. 친화도가 높아지는 요인에는 여러가지가 있는데, 대표적으로 방금 전에 소개한 종속 함수가 그 예이다. 또는 변수와 그 변수를 사용하는 함수, 비슷한 동작을 수행하는 함수들이 그 예이다.

세로 순서

함수 호출 종속성은 아래 방향으로 유지한다. 즉, 호출되는 함수를 호출하는 함수보다 나중에 배치한다. 이렇게하면 자연스럽게 소스 코드 모듈이 고차원에서 저차원으로 자연스럽게 내려가게된다.

기사처럼 중요한 개념을 표현할 때는 세세한 사항을 최대한 배제하며 가장 먼저 표현하도록 한다. 그렇게하면 읽을 때 소스 코드 파일에서 처음의 함수 몇 가지만 읽어도 전체 내용을 파악할 수 있게 된다.


가로 형식 맞추기

아까 세로 형식에서 봤던 7개의 프로젝트를 이번엔 가로 길이로 분석한 표이다. 표를 보면 20~60자인 행 수가 1%대를 차지하고 있다. 다시말하다면 전체 코드에서 약 40% 정도가 20~60글자 사이를 갖고있다는 말이다. 이처럼 대다수는 짧은 가로 길이를 사용하면서 역시 좋은 프로젝트들을 만들 수 있음을 의미한다.

가로 공백과 밀집도

가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다. 다음은 아주 간단한 코드이다.

const add = (num1, num2) => {
  let result = 0;
  
  result = num1 + num2;
  
  return result;
};

애로우 함수의 인수 부분에서 쉼표 뒤에 공백을 주었다. 이는 인수가 별개(2개)임을 확실하게 나타내준다. 또한 연산 부분에서도 연산자 사이에 앞뒤로 공백을 넣음으로써 피연산자와 연산자의 구분을 명확하게 해준다.

이처럼 적절한 공백은 코드나 수식의 해석에 도움을 준다.

가로 정렬

어셈블리어 같은 가로 정렬은 엉뚱한 부분을 강조해서, 진짜 의도를 가릴 수가 있다.

public class ExampleClass {
	private   Player 	Player
    private   int       HealthPoint
    private   int       level;
    protected float     exp;
}

들여쓰기

소스 코드에서 스코프를 표현하기 위해서 들여쓰기를 이용한다. 들여쓰기의 정도는 코드가 위치한 계층 수준에 따라서 달라진다.

  • 클래스 정의같은 파일 수준은 들여쓰기를 하지 않는다.
  • 클래스 내 메소드는 클래스보다 한 수준 들여쓰기를 한다.
  • 메소드 코드는 메소드 선언보다 한 수준 들여쓴다.
  • 블록 코드는 블록을 포함하는 코드보다 한 수준 들여쓴다.

다음은 for문 으로구구단 2을 출력하는 예제인데, 들여쓰기가 없다면 얼마나 보기가 어려운지 직접 확인해보자.

const getTwoTimesTable = () => {let result = []; for(let i = 1; i < 10; i++){ result[i-1] = i * 2} return result;}

종종 한 줄짜리 if, for 등을 만나면 들여쓰기를 생략하고 싶은 유혹에 빠지게된다. 겉으로 보면 코드가 더 짧아지니까. 이 책에서 저자는 이러한 부분에서도 확실하게 들여쓰기하는 코드를 선호한다고 한다.

💬개인적으로도 저도 책의 저자 처럼 한 줄 짜리 코드에 대해 들여쓰기를 적용하는 것을 선호합니다.

let total = 0;

for (let i = 0; i < 100; i++) total += i;

보다는

let total = 0;

for (let i = 0; i < 100; i++) {
  total += i;
}

를 좋아합니다.

가짜 범위

종종 빈 while, for를 만나게된다. 이런 경우는 피하는 것이 좋지만 피하지 못 할 때에는 빈 블록으로 들여쓰고 괄호로 감싼다.

특히 while문에서 세미 콜론;은 새 행에 써서 넣는다.


팀 규칙

팀에 속해있다면 팀이 합의해서 정해진 규칙에 따라 코드를 작성해야한다.

온갖 스타일을 섞으면 나중에 골치가 아파진다. 팀에 속한 팀원들은 반드시 팀에서 정한 규칙에 따라 소스 코드를 작성해야한다.


후기

이번 장은 좀 쉬웠다.(?)

무슨 이야기냐면 여지껏 소개한 방식 중에서 가장 따라하기 쉬운 부분이 아닐까 생각한다. 물론 따라하기가 쉽다는 거지 팀 규칙 또는 형식 만들기가 쉽다는 의미는 아니다. 대부분의 IDE는 자동 정렬을 지원하고 있기 때문에 포맷만 정해놓는다면 단축키로 쉽게 정렬할 수 있다. 그렇기에 우리는 정해진 팀 규칙을, 개인 프로젝트라면 특정 코딩 컨벤션이나 혼자서 만든 형식을 지켜서 작성하려는 노력을 해야한다.

0개의 댓글