코드가 클린 하면 왜 좋아?(feat. 뇌 인지 관점)

Pjj
직방 기술 블로그
9 min readMay 22, 2023

--

안녕하세요 BE APT 팀의 Jin 입니다.

개발자라면 누구나 한번 쯤 클린코드에 대해 들어본 적 있지 않으신가요?우리 모두 클린코드의 지침을 따르다 보면 읽기 쉬운 코드가 작성된다고 잘 알고 있습니다.

그렇다면 클린코드의 가독성이 왜 좋은지 생각해보신적이 있나요?저는 이번 포스팅에서 클린코드를 다른 시각으로 바라보고 개발자가 클린 코드를 왜 쉽게 이해하는지에 대해 얘기하려 합니다.

(참고로 제 얘기는 대부분 “프로그래머의 뇌” 라는 책을 바탕으로 합니다.)

먼저 본격적으로 시작하기에 앞서 우리는 언제 코드가 읽기 쉽다, 이해하기 쉽다고 느낄까요? 우리는 코드를 읽을 때 머릿속에 혼란(코드가 하는 일, 작동방식, 의미의 혼란)이 없을 때 코드를 이해하기 쉽다고 느낍니다.

그러면 우리는 코드를 읽을 때 도대체 어떤 혼란이 우리 뇌 속에서는 일어날까요?

코드를 읽을 때 일어나는 3가지 혼란

  1. 지식의 부족

아래 코드는 APL이라는 언어로 된 코드로 숫자 n을 이진수 표현으로 변경하는 코드입니다.

2 2 2 2 2 T N

코드를 읽으시면서 어떠셨나요? 혼란스럽지 않으셨나요? 여기서 우리가 혼란스러웠던 이유는 T에 대한 지식이 없기 때문입니다.

2. 정보의 부족

아래의 코드는 자바로 작성된 코드입니다.

public class BinaryCalculator {
  public static void main(Integer n) {
System.out.println(Integer.toBinaryString(n));
}

}

이번엔 코드를 읽으시면서 어떠셨나요? 이번에도 혼란스러웠나요? 이번 코드는 자바 언어 전문가가 아니어도 어떤 일을 하는지 메서드 이름으로 유추할 수는 있었을 거라 예상합니다. 하지만 toBinaryString() 메서드 내부적으로 어떻게 작동하는지 모른다면 이코드도 혼란스러울 수 있습니다. 그 혼란의 원인은 toBinaryString() 메서드에 관한 정보가 부족하다는 점입니다.

3. 처리 능력의 부족

아래 코드는 베이직으로 작성된 코드입니다.

LET N2 = ABS (INT(N))
LET B$ = ""
FOR N1 = N2 TO 0 step 0
LET N2 = INT(N1/2)
LET B$ = STR$(N1- N2 * 2) + B$
LET N1 = N2
NEXT N1
PRINT B$

이번엔 코드를 읽으시면서 어떠셨나요? 이번에도 혼란스러웠나요? 이번 코드에서는 변수 이름이나 연산자를 통해 무슨 일하는지 유추가 가능합니다. 하지만 코드의 각각의 단계가 실행되는 것을 한눈에 파악하기 어려워 혼란스러울 수 있습니다. 이번 혼란은 처리 능력이 부족하기 때문입니다. 이런 코드는 이해하기 위해서는 변수들의 중간값을 따로 적거나 하는 등의 노력이 필요할 수 있기 때문입니다.

이제까지 우리 머릿속에서 일어나는 혼란들에 대해 알아봤습니다. 그럼 이번엔 우리가 프로그래밍을 할 때 우리 두뇌에 어떤 일이 일어나는지 알아보겠습니다.

코딩을 할 때 우리 두뇌는 크게 3가지 기억과 관련된 영역이 사용됩니다.

1. 장기 기억 공간(long-term memory, LTM)

  • 오랜 시간에 걸쳐 얻은 정보를 저장하는 장소입니다.
  • 컴퓨터의 하드디스크 드라이브와 비슷합니다.
  • 여기에 저장된 기억은 아주 오랫동안 보관됩니다.
  • 이진 검색과 같은 추상적 알고리즘, 프로그래밍 언어 문법, 특정 언어의 키워드, 자전거 타는 법, 신발 끈을 묶는 법 등이 여기에 저장되어 있습니다.
  • 앞서 얘기한 혼란 중 지식이 없다는 것은 LTM에 해당 내용이 없다는 뜻입니다.

2. 단기 기억 공간(short-term memory, STM)

  • 들어오는 정보를 잠시 보관하는 곳입니다.
  • 최대 공간이 12개를 넘지 않습니다. (2–6 사이로 추정)
  • 우리의 두뇌는 정보들을 청크(chunk) 라는 묶음으로 나누려고하고 LTM과 협업을 통해 STM의 공간 제약을 극복합니다. (아래에서 좀 더 자세히 설명하겠습니다.)
  • 특정 코드에서의 키워드 , 변수명, 자료구조 등이 여기에 저장 됩니다.
  • 컴퓨터의 캐시나 메인 메모리와 비슷합니다.
  • 앞서 얘기한 혼란 중 정보가 부족하다 것은 STM에 해당 내용이 없다는 뜻입니다.

우리의 뇌가 청킹과 LTM과의 협업을 통해 공간의 제약을 극복하는 방법에 대해 좀 더 자세히 설명해 보겠습니다.

다음 문장을 5초간 본 후 어떤 문장인지 기억해 보세요

abk mrtpi gbar

어떠셨나요? 기억하기 쉬웠나요?

그럼 다음 문장을 똑같이 5초간 보고 기억해 보세요

cat loves cake

어떠셨나요? 훨씬 쉽지 않았나요? 그 이유는 LTM에 세 단어의 지식이 있어서 한 단위로 묶을 수 있기 때문입니다. 하지만 abk mrtpi gbar와 같은 문장은 각 단어를 기억해야 하고 STM 한도를 넘어섰기 때문에 어렵습니다

3. 작업 기억 공간(working memory)

  • 실제 LTM, STM 의 정보를 가지고 와서 정보를 처리하는 곳 입니다. (생각, 아이디어, 해결책 등이 여기서 만들어집니다.)
  • 컴퓨터의 프로세서와 비슷합니다.
  • STM과 동일하게 2–6까지 항목만 저장할 수 있고 청킹을 사용해서 더 많은 정보를 처리할 수 있습니다.
  • 앞서 얘기한 혼란 중 처리 능력이 부족하다는 것은 작업 기억 공간이 과부하 상태라는 뜻입니다.

그럼 어떻게 이런 공간들이 서로 상호 작용해 우리는 코드를 이해하게 되는 걸까요? 예를 하나 들어보겠습니다. 제가 몇 달 전 만든 코드에 버그가 생겨 코드를 분석해야 하는 상황입니다. 버그는 삽입정렬을 구현하는 부분에서 발생했다고 가정하겠습니다.

이때 코드를 분석하면 제 머릿속에서는

  • 코드를 읽으면서 분석하는 내용, 버그 리포트 내용 등이 STM에 저장됩니다.
  • 몇 개월 전에 구현한 내용, 삽입정렬에 관한 내용, 삽입정렬 구현 시 흔히 발생한 오류나 겪었던 경험, 잘 알려진 해결법 등을 LTM에서 가져오려고 합니다.
  • 관련된 LTM과 STM에 있는 내용들이 working memory에 들어오게 되고 버그에 대한 해결법을 도출하게 됩니다.

그럼 이제 까지 배운 내용을 드디어 클린코드에 적용 해봅시다.

  1. 함수의 인수를 최대한 작게 유지하라!

함수의 인수는 작을수록 좋다는 클린코드의 지침입니다.

앞에 말씀드렸다시피 우리의 작업 기억 공간의 용량은 6개 정도로 작아서 많은 매개변수 리스트를 기억하기에는 무리가 있습니다. 그래서 함수의 인수가 많을수록 우리는 인지부하를 느낄 게 될 가능성이 높습니다.

클린코드에서는 인수가 많을 때 줄이는 방법으로 인수 객체를 사용해서 개념을 표현하라고 합니다. 예를 들어 다음과 같은 메서드가 있습니다.

public void line(int xOrigin, yOrigin, xDestination, yDestination)

우리의 두뇌는 4개의 청크(xOrigin, yOrigin, xDestination, yDestination)로 인식하게 될 가능성이 큽니다.

이번엔 클린코드 지침대로 인수 객체를 도입해 보겠습니다.

public void line(Point origin, Point destination)

이렇게 되면 우리 두뇌는 2개의 청크(origin좌표, destination좌표)로 해당 메서드를 인식하게 될 가능성이 커지고 이로써 작업 기억공간의 부하를(4->2) 줄일 수 있게 됩니다. (물론 코드에 사전 지식을 얼마나 가지고 있냐 또는 개인에 역량에 따라 첫 번째 코드를 보고 머릿속에서 바로 2개의 청크로 인지할 수도 있습니다. )

2. 클래스/메서드를 작게 유지하라.

많은 기능을 가지는 “만능 클래스” 또는 “만능 메서드”를 의미 있는 단위로 분리해서 작게 만들라는 클린 코드의 원칙입니다.

긴 클래스, 긴 메서드를 읽을 때는 해당 코드가 무슨 일을 하는지 코드를 한줄 한줄 읽어야 하고 이는 우리 두뇌에서 청킹 작업을 하는데 많은 시간을 쏟고 어렵게 만듭니다.

반면 긴 클래스, 메서드를 작은 단위로 분리하면 분리된 클래스, 메서드의 이름이 문서 역할을 합니다.그래서 코드를 읽을 때 그 이름을 가지고 효율적으로 청킹을 할 수 있게 되고 이는 우리의 작업 기억 공간과 STM의 용량 부하를 줄이는 효과로 이어지게 됩니다.

3. 반복하지 마라!

코드의 중복을 없애라는 클린코드의 지침입니다. 유지보수를 위해서도 있지만 가독성 측면에서도 중복은 혼란을 야기 하기 쉽습니다. 예를 들어 다음과 같은 두 메서드가 있습니다.

int foo(int j) {
if (j < 0)
return j;
else
return ++j;
}
int goo(int j) {
if (j < 0)
return j;
else
return j+2;
}

foo() 함수를 보고 난 뒤 goo() 함수를 보게 되면 우리 작업 기억 공간은 LTM에서 foo()에 대한 정보를 수집하게 될 것입니다. 그리고 나서는 goo() 함수를 foo() 함수와 동일하다고 잘못 인지하게 될 가능성이 높습니다.

4. 의미 있는 이름을 지어라!

이름에 의도를 분명히 밝히라는 클린코드의 지침입니다. 구체적으로는 아래와 같은 여러 지침이 있습니다.

  • 해법 영역에서 가져온 이름을 사용하라.
  • 문제의 영역에서 가져온 이름을 사용하라.
  • 의미 있는 맥락을 추가하라.

이렇게 많은 클린 코드 지침들은 LTM과 관련이 있습니다. 올바른 개념을 이름에 단어로 적절히 사용하면 코드를 읽을 때 관련 정보를 LTM에서 찾는 데 도움을 주기 때문입니다.

예를 들면 Visitor 디자인 패턴이 적용된 코드에 xxxVisitor라는 단어를 넣어주면 해당 코드가 Visitor 패턴이 적용되어있다는 걸 인지하고 관련 정보를 가지고 와서 이해하게 됩니다. 또는 지뢰 찾기 게임에서 게임판theList와 같은 함축적인 이름으로 명명 하는것보다 gameBord와 같은 이름을 지어주면 우리는 해당 코드를 읽을 때 도움이 되는 정보들을(지뢰 찾기 게임판은 여러 칸으로 구성되어 있다 등) LTM에서 가져올수 있습니다.

추가로 클린코드에는 없지만, 우리가 보편적으로 준수하는 네이밍(camelCase, snake_case) 관례 또한 인지과정과 연관이 있습니다. 아시다시피 STM의 크기는 제한적이기 때문에 효율적으로 저장하기 위해 우리 뇌는 이름을 청킹 하려고 하고 합니다. 이름이 체계적일수록 이름의 각 부분을 식별하고 청킹 하기 쉽습니다. 예를 들어 nmcntrlst 보다 name_counter_list 같은 이름이 관련된 내용(이름, 카운터, 리스트)을 훨씬 쉽게 우리는 인지 할 수 있습니다.

마치며

우리 모두 타인, 자신의 코드를 읽으며 좌절하고 괴로워한 경험이 있을것입니다. 저는 우리 모두 뇌의 동작방식을 이해하고 효과적으로 코드를 읽어나가길 기대하며 이 글을 작성했습니다.

사실 이 글이 그동안 모르셨던 엄청난 사실을 알려주지는 않았을 겁니다. 하지만 우리가 그동안 “그래 이런 코드가 읽기 쉽지” 라고 당연히 생각했던 부분에 대해서 과학적인 근거를 제시했을 거라 생각합니다.

제가 소개하지 않았지만, 책에서는 신속하게 코드를 읽는 법, 다른 언어를 빨리 배우는 법, 복잡한 문제를 더 잘 해결하는 법, 코드베이스를 평가하는 법, 설계 개선법 등 재밌고 유익한 내용이 많이 들어 있습니다. 궁금하시다면 직접 책을 읽어보면 좋을 것 같습니다.

Ref

http://www.yes24.com/Product/Goods/105911017 (프로그램의 뇌)

http://www.yes24.com/Product/Goods/11681152 (클린코드)

--

--