티스토리 뷰

이 포스팅은 프로그래머의 뇌를 읽고 작성하였습니다.

Overview

코딩 중에 발생하는 다양한 혼란의 방식과 그 차이점을 이해하고, 코딩에서 작동하는 세 가지 인지 과정을 비교합니다. 그리고 이 세 가지 인지 과정들이 어떻게 서로 보완적으로 작동하는지 이해합니다.

프로그래밍을 하다 보면 늘 혼란이 일어납니다. 새로운 언어나 개념 또는 프레임워크를 배울 때 지레 겁부터 먹는 경우도 있습니다. 익숙하지 않은 코드, 자신이 오래 전에 작성한 코드를 다시 확인할 때, 그 코드가 왜 그렇게 작성되었는지 이해가 안 갈 수도 있습니다. 또는 새로운 도메인에서 일을 시작할 때 새로운 용어나 누적되며 쌓여온 히스토리 때문에 파악이 어려운 경우도 있습니다.

이런 혼란을 필요 이상으로 오래 가져가면 안 되겠죠? 따라서 책에서는 이런 혼란을 어떻게 인식하고 해석할지를 다룹니다. 혼란은 세 가지 서로 다른 방식으로 일어나고, 기존에 겪었던 혼란(복잡한 알고리즘, 특정 영역에서 사용되는 개념 등)과는 서로 다른 종류의 혼란입니다.

여러 가지 종류의 혼란은 서로 다른 유형의 인지 과정과 연관이 있습니다. 혼란에 대해 자세히 살펴보고, 우리 인지 과정에서 어떻게 일어나는지 설명한 뒤, 그 이후 장에서부터는 어떻게 인지 과정들을 개선할 수 있을지를 다룹니다.

코드가 초래하는 세 가지 종류의 혼란

아래 세 가지 예제 코드는 모두 주어진 숫자를 이진수로 바꾸는 코드입니다. 각자 시간을 내서 분석해보시기 바랍니다. 코드를 읽을 때 어떤 종류의 지식을 사용하고, 그 지식은 서로 어떻게 다를까요? 분명 서로 다른 지식을 사용한다는 것을 느낄 것입니다.

다음은 APL로 된 코드입니다.

예제 1)

2 2 2 2 2 T n

이 코드를 읽을 때 혼란이 오는 부분은 어디인가요? 아마 T일 것입니다. T가 뭘 하는지 모르기 때문에 숫자를 이진수로 표현하는 코드라는 것을 유추하기 힘듭니다.

다음은 Java로 된 코드입니다.

예제 2)

public class BinaryCalculator {
  public static void mian(String[] args) {
    int n = Integer.parseInt(args[0]);
    System.out.println(Integer.toBinaryString(n));
  }
}

아마 Java를 아는 분들에게는 혼란스럽지 않을 수도 있겠습니다만, toBinaryString이라는 메서드가 어떻게 동작하는지 모른다면 이 코드 역시 혼란이 있을 수 있습니다.

다음은 BASIC으로 된 코드입니다.

예제 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$

이 코드는 각각의 단계들을 모두 이해하지 못하면 혼란스럽게 됩니다.

지식의 부족

예제 1에서 T가 의미하는 바를 모르는 것이 바로 지식의 부족입니다. 즉, 혼란스러운 이유는 T에 대한 지식이 없기 떄문입니다.

정보의 부족

예제 2의 경우 전문가가 아니더라도 어느 정도의 언어 지식을 갖추고 있는 사람이라면 이진수로 표현하기 위한 부분을 찾는 것은 어렵지 않을 것입니다. 하지만 메서드가 어떻게 동작하는지 몰라서 혼란스러울 수 있고, 이 때 혼란의 원인은 메서드에 대한 정보가 부족하다는 점입니다. 정확하게 알기 위해선 toBinaryString 메서드 내부를 살펴봐야 합니다.

처리 능력의 부족

예제 3의 경우 변수 이름이나 연산자를 통해 무슨 일을 하는지 대략적으로 유추할 순 있습니다. 하지만 코드를 따라가다 보면 머리 속에서 모든 과정을 따라가기가 어렵습니다. 각각의 단계가 실행되는 것을 파악하기 어렵기 때문입니다. 필요에 따라 디버깅을 하는 것 처럼 종이에 값을 기록해가면서 파악하기도 합니다.

이러한 혼란은 처리 능력이 부족하기 때문입니다. 변수에 임시로 저장되는 값을 기억하거나 어떤 동작들을 수행하는지 동시에 알기 어렵습니다.

이렇게 세 가지 예제를 통해 어떤 혼란의 종류가 있는지 확인해보았습니다. 지식이 없거나, 정보가 없거나, 처리 능력이 부족한 경우 우리는 혼란을 겪게 됩니다.

인지 과정

위에서 다룬 세 가지 다른 종류의 혼란은 각각 다른 종류의 인지 과정과 연관되고 이 과정은 모두 기억과 관련이 있습니다.

지식이 없다는 것은 두뇌의 장기 기억 공간(LTM, long-term memory)에 해당 내용이 없다는 것이고, 정보가 부족할 때는 단기 기억 공간(STM, short-term memory)에 해당 내용이 없다는 것이며, 많은 정보를 처리할 때는 작업 기억 공간에 영향을 미칩니다.

혼란과 관련된 인지 과정을 다시 한 번 요약하면,

  • 지식의 부족 = LTM의 문제
  • 정보의 부족 = STM의 문제
  • 처리 능력의 부족 = 작업 기억 공간의 문제

이렇게 나타낼 수 있고, 이 세 가지 인지 과정은 모든 종류의 인지 활동에서 나타납니다.

LTM과 프로그래밍

프로그래밍과 관련된 첫 번째 인지 과정인 LTM은 아주 오랫동안 기억이 보관됩니다. 몇 년 전이나 몇십 년 전에 일어난 일들이 기억나는 것처럼 어떤 일을 할 때든 LTM이 사용됩니다. 생활에서 사용되는 근육의 기억이나, 프로그램을 작성할 때 대략적인 알고리즘과 언어의 문법 등을 기억하고, 키보드로 입력하는 동작을 기억하는 일을 모두 LTM이 합니다.

LTM은 프로그래밍과 관련해서 여러 다른 종류의 정보를 저장하는데, 예를 들면 어떤 기술을 성공적으로 적용한 순간, 언어에서의 키워드, 데이터 타입이 나타낼 수 있는 범위, 영어 단어 등이 이에 해당합니다.

오랜 시간 동안 저장한다는 점에서 컴퓨터의 하드 드라이브에 비유할 수 있습니다.

예제 코드를 읽을 때 가장 많이 사용하는 게 바로 LTM 입니다. 우리가 예제 1에서의 T의 의미를 알고 있다면 LTM에서 그것을 찾게 됩니다. 언어의 문법에 대한 지식도 중요합니다. T가 의미하는 바를 알지 못하면 코드 자체를 이해할 수 없기 때문입니다. 하지만 T가 어떤 수를 다른 진법의 수로 변환해주는 함수라는 것을 알면 코드 분석은 간단해집니다. 다른 부가적인 설명이 필요 없고 각 단계에 대해 이해할 필요도 없어집니다.

STM과 프로그래밍

STM은 들어오는 정보를 잠시 보관하기 위해 사용됩니다. 누군가 통화 중에 전화번호를 알려줄 때가 여기 해당합니다. 크기에 제한이 있고 최대 12개를 넘지 못한다는 것에 대부분의 학자가 동의하고 있습니다.

코드에서 키워드, 변수명, 자료구조 등이 STM에 일시적으로 저장됩니다.

예제 2에서는 주로 STM을 이용한 인지 과정이 일어납니다. 각 라인을 읽으면서 변수를 파악하고, 함수의 역할은 아직 모르지만 변수를 전달했다는 것을 기억하면서 계속 읽어나가게 됩니다. 그리고 이 함수가 하는 일을 이해하고 나서는 STM의 내용은 기억에서 지워집니다.

이 코드를 이해하는 데는 STM이 주된 역할을 하지만 사실 우리가 행하는 모든 인지과정에 LTM이 관여하게 됩니다. 예시를 확인한 대부분의 사람들은 자바에 익숙할 것이고 어떤 일을 하는지 설명해야하면 public class나 public static void main은 굳이 설명하지 않을 것입니다. 심지어 예제 코드에서 main을 일부러 mian으로 바꿔 적어놔도 미처 알아보지 못하는 분들도 많이 있을 것입니다.

mian을 읽을 당시에는 STM에 저장되지만 과거의 경험으로 인해 main 메서드가 사용될 것이라는 것을 알고 있는 경우 LTM에서 main이라는 이름을 꺼내서 사용하게 됩니다. 두 가지 인지 과정이 서로 상호작용하고 있음을 알 수 있습니다.

STM은 값을 일시적으로 저장하므로 컴퓨터로 비유하면 캐시나 메모리라고 할 수 있습니다.

작업 기억 공간과 프로그래밍

세 번째 인지 과정이 일어나는 곳은 작업 기억 공간입니다. STM과 LTM이 기억장치라고 할 수 있다면 실제 사고 작용은 작업 기억 공간에서 발생합니다.

생각, 아이디어, 해결책 같은 것들이 여기서 만들어지므로, 프로세서로 비유할 수 있습니다.

예제 3을 보면 LET, EXIT 같은 키워드를 기억하기 위해 LTM을, B$가 빈 문자열로 시작된다는 정보를 STM을 이용합니다. 하지만 이 코드를 그냥 보는 것이 아니라 머리 속으로 코드를 실행해보면서 이해하려고 하는 트레이싱 과정이 일어납니다. 머릿속으로 코드를 직접 컴파일하고 실행하는 과정으로 작업 내용을 기억 공간에 지속적으로 저장하게 됩니다.

머리속으로 계속 계산해나가다가 작업 기억 공간이 꽉 차게되면 다른 도구를 이용해 별도로 값을 메모하게 되는데 이를 정보 과부하 상태라고 부릅니다.

인지 과정들의 상호작용

어떻게 상호작용 하는가?

우리가 사고할 때는 세 가지 인지 과정 모두 어느 정도 활성화 됩니다. 예제 2번 코드를 읽었을 때 세 가지 인지 과정을 다 경험했을 것입니다. n이 정수로 변형되어 과정은 LTM에, 저장된 정보는 STM에, 프로그램이 무슨 일을 하는지 파악하는 것은 작업 기억 공간에서 이루어집니다.

지금까지는 코드를 분석할 때 일어나는 인지 과정에 대해 살펴봤지만, 이 과정들은 프로그래밍과 관련된 다른 업무에서도 일어납니다.

프로그래밍과 관련한 인지 과정

버그 리포트를 받는 상황을 고려해보면, 버그 리포트는 시각이나 청각 같은 지각을 통해 뒤뇌로 입력됩니다. 버그를 고치기 위해선 몇 개월 전에 작성한 코드를 읽어야 합니다. 코드를 분석할 때 읽는 내용은 STM에 저장되고, 몇 개월 전에 구현한 내용을 LTM에서 가져옵니다. 과거에 경험했던 오류의 경우 역시 LTM에 저장되어 있습니다. 여기서 이 모든 정보들이 작업 기억 공간에 모이면 현재 마주한 문제에 대해 생각할 수 있게 됩니다.

프로그램을 주의 깊게 읽고 어떤 일을 하는지 파악하면서 두뇌에서 어떤 메커니즘을 사용하는지 생각해 볼 수 있는 자가 진단 해볼 수 있습니다.

  1. LTM에서 관련 지식을 인출하는가?
  2. 어떤 정보를 가져왔는가?
  3. STM에 정보를 저장하는가?
  4. 어떤 정보를 저장하는가?
  5. 관련 없어 보여 무시하고 넘어간 정보는 없는가?
  6. 코드의 특정 부분을 광범위하게 작업 기억 공간을 사용해 분석하는가?
  7. 코드의 어떤 부분이 작업 공간에 과부하를 주는가?
  8. 코드의 그 부분들이 작업 기억 공간을 어떻게 사용하는지 이해하는가?

책에는 세 가지 예시가 있지만 전 자바 언어만 사용하여 자가 진단을 실시해봤습니다.

public class Luhn {

  public static void main(String[] args) {
    System.out.println(luhnTest("49927398716")); // 1, 2: 출력 메서드, 3, 4: 전달하는 파라미터
  }

  public static boolean luhnTest(String number) {
    int s1 = 0, s2 = 0; // 3, 4: 변수 선언
    String reverse = new StringBuffer(number).reverse().toString(); // 1, 2: StringBuffer를 이용해 문자열을 역순으로 뒤집음
    for (int i = 0; i < reverse.length(); i++) { // 6, 7: 길이가 길어져서 결과를 다 외우기 힘듦, 8: 각 자리 문자를 숫자로 변환하여 짝수일 때 s1에 더하고 홀수일 때 s2에 두 배 곱해서 더하는데 홀수의 값이 5 이상이면 다시 9를 빼줌 
      int digit = Character.digit(reverse.charAt(i), 10); // 1, 2: i번째 문자를 10진수로 변환, 3, 4: digit에 10진수 저장
      if (i % 2 == 0) { // 6, 8
        s1 += digit; // 3
      } else { // 6, 8
        s2 += 2 * digit; // 3
        if (digit >= 5) { // 8
          s2 -= 9;
        }
      }
    }
    return (s1 + s2) % 10 == 0; // 1, 2: 문법 및 연산, 3, 4: 연산 결과, 8: 결과를 계산하여 반환
  }
}

코드를 인지 하면서 자가 진단한 내용을 주석에 직접 적어보았습니다.

이 코드는 숫자로 구성된 문자열이 주어지면 해당 문자열을 뒤집고, 각 문자마다 짝수일 때, 홀수일 때 다르게 계산한 뒤 계산한 것이 10의 배수인지 확인하는 코드입니다.

StringBuffer, Character의 메서드를 호출할 때 LTM을 사용하였고, 그 외 변수나 계산한 값들을 저장할 때 STM을 사용하였습니다. 반복문 안쪽에서는 작업 기억 공간을 활용하여 어떤 연산이 일어나는지 이해하고 계산하여 값을 갱신해 나갔고, 결과를 구할 수 있었습니다.

순차적으로 계산하다보니 홀, 짝을 왔다갔다 하는 게 헷갈려서 직관적으로 문자열 중 짝수만 먼저 다 더해보았고, 여기까지는 추가 기억을 위한 다른 도구가 필요하진 않았습니다. 다음으로 홀수를 더할 때도 5 미만인 홀수만 먼저 두 배해서 더해보았는데 여기까지도 머리만 써서 가능했습니다. 마지막으로 5이상의 홀수들도 모두 더한뒤 두 배 하고 더한 갯수만큼 9를 곱해 빼주었더니 69가 나왔고 결과를 false로 유추했는데 직접 코딩해서 확인해보니 70이고 true가 나오는군요. 7번에 해당하는 기억의 과부하가 문제였던 거 같습니다. 사실 코드대로 따라갔으면 더 헷갈렸을텐데 꼼수를 써서 짝수, 5이상 홀수, 5미만 홀수로 나눠서 계산했는데도 실패했네요. (옆에서 와이프가 말을 걸었기 때문이라고 위로해봅니다.)

이렇게 진단해보았는데 어떻게 발전시킬 수 있을지도 알아봐야겠죠? 이후 포스팅부터 순차적으로 알아보도록 하겠습니다.

요약

  • 코드를 읽을 때 혼란이 생기는 이유는 지식 부족, 정보의 부족, 두뇌 처리 능력의 부족 이렇게 세 가지 입니다.
  • 코드를 읽거나 작성할 때 세 가지 인지 과정(LTM, STM, 작업 기억 공간)이 일어납니다.
  • 코드를 읽는 동안 세 가지 인지 과정은 서로 보완적으로 작용합니다.
댓글