BookReview [프로그래머의 뇌]
The Programmer’s Brain(프로그래머의 뇌)
훌룡한 프로그래머가 알아야 할 인지과학의 모든 것
친구의 추천으로 읽게된 책인데 3월에 시작해서 지금에서야 다 읽었다…
이유는 다른 우선순위에 있는 책에 밀려서 그렇게 된 것도 있고 내용자체도 딱딱한 내용이라 재밌게 읽지 못했다.
좋은 내용이지만 관심사가 아니라 숙제하는 기분으로 읽은..
앞 부분은 흥미로운 내용이라 신기해 하면서 읽었다면 중반부터는 실전 적용에 대한 이야기가 담겨 있어서 조금 어지러웠다.
대부분 모각코에서 의도적으로 읽어서 해결했다..
이미 중간까지 읽은 책이라 그만 둘 수 없었다..
머리말
지금 읽고 있는 책(좋은 코드, 나쁜 코드)을 옮긴 번역가인 차건회님의 비슷한 표지와 비슷한 계열의 책이다.
모쪼록 이 책을 통해 프로그래밍을 공부하는 사람이 자신이 겪는 어려움의 본질을 이해하고 이런 바탕으로 난관을 이겨내 프로그래밍 언어 학습을 완료할 수 있기를 바란다.
추천 서문으로 NodaTime이 나오는데 존 스키드라는 분이 쓴 글이 나온다.(닷넷 프로젝트의 주 개발자)
좋은 코드, 나쁜 코드에서는 노다타임이 라이브러리 사용의 좋은 예로 나왔는데 첫 서문부터 등장한다니 신기하다..
// NodaTime
tomorrow = today.PlusDays(1);
//.Net DateTime
date.AddDays(1);
두 코드다 버그는 아니지만 가독성측면에서 다른 결과를 가져온다.
이 책은 뇌가 어떻게 작동하는지, 그리고 프로그래밍 기술과 습관을 어떻게 향상할 수 있는지 거 깊이 이해하고 싶은 모든 수준의 프로그래머들을 위한 책이다.
1장 코딩 주 겪는 혼란에 대한 이해
핵심 주제
- 코딩 중에 혼란이 발생하는 다양한 방식의 차이점 이해
- 코딩에서 작동하는 세 가지 인지 과정을 비교
- 세 가지 인지 과정들이 어떻게 서로 보완적으로 작동하는지에 대한 이해
책 내용 및 정리
프로그래밍을 하다 보면 늘 혼란이 일어난다.
새로운 프로그래밍 언어나 개념, 프레임워크 등을 배울 때 새로 접하는 아이디어 때문에 겁먹을 수 있다.
익숙하지 않은 코드나 과거의 자신이 작성한 코드를 열어볼 때, 무슨일을 하는지 어떻게 동작하는지에 대한 이해가 안될 수 있다.
잠시의 혼란은 문제가 되지 않지만, 필요 이상의 혼란은 곤란하다.
이러한 혼란들을 어떻게 인식하고 해석할 것인지를 다룬다.
혼란은 세가지 방식으로 일어난다.
복잡한 알고리즘을 단계적으로 이해하려고 노력할 때 일어나는 혼란과 어떤 특정 영역에서 사용되는 개념을 잘 이해하지 못해서 일어나는 혼란은 서로 다른 종류의 혼란이다.
코드가 초래하는 세 가지 종류의 혼란
주어진 n을 이진수로 바꾸는 세가지 코드를 보고 어떤 종류의 지식을 사용하고 어떻게 다른지 알아보자.
혼란의 첫 번째 유형: 지식의 부족
2 2 2 2 2 T n
APL로 된 코드이고 숫자 n을 이진수로 변경하는 코드이다.
코드를 읽을 때 오는 혼란은 T가 뭔지 모르기 때문이다.
즉 코드가 혼란스러운 이유는 T에 대한 지식이 없기 때문이다.
혼란의 두 번째 유형: 정보의 부족
public class BinaryCalculator{
public static void main(Integer n){
System.out.println(Integer.toBinaryString(n));
}
}
이 프로그램이 어떤 일을 하는지는 메서드의 이름으로 유추할 수 있지만, 이 메서드가 구체적으로 어떤 일을 수행하는지 이해하려면 메서드 내부를 더 살펴봐야 한다.
따라서 여기서 발생하는 혼란의 원인은 정보가 부족하다는 점이다.
혼란의 세 번째 유형: 처리 능력의 부족
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$
변수 이름이나 연산자를 통해 코드가 무슨일을 하는지 유추할 수 있다.
하지만 머릿속에서 모든 과정을 처리하기가 어렵다.
이러한 혼란은 처리 능력이 부족하기 때문이다.
변수로 저장되는 값을 모두다 기억하거나 각각의 경우 어떤 동작들이 수행되는지를 동시에 알기가 쉽지 않다.
지금까지 나온 세가지 혼란에 대해서 정리해보면
- 프로그래밍 언어나 알고리즘 혹은 업무 영역에 대한 지식이 없는 경우 혼란이 생길 수 있다.
- 코드를 이해하기 위해 필요한 정보를 충분히 가지고 있지 못하는 경우에도 그렇다.
- 코드가 너무 복잡해서 혼란이 생기는 경우(두뇌의 처리 용량이 부족하기 때문)
코딩에 영향을 주는 인지 과정
세 가지 예제와 같이 두뇌가 수행하는 세 가지 인지 과정을 좀 더 구체적으로 살펴보자.
세 가지 서로 다른 종류의 혼란은 각각 서로 다른 종류의 인지 과정과 연관되고, 이 과정은 기억과 관련 있다.
지식이 없다는 것은 두뇌의 장기 기억 공간
에 해당 내용이 없다는 것을 뜻한다.
반면 정보가 부족할 때는 단기 기억 공간
에 해당 내용이 없기 때문이다.
정보를 수집할 때 단기 기억 장소에 일시적으로 저장하지만, 다른 정보를 찾는 과정에서 이미 수집해놓은 정보 중 일부는 잊어버린다.
마지막으로 많은 정보를 처리할 때는 작업 기억 공간
에 영향을 미치는데 우리는 사고할 때 이 영역을 사용한다.
요약하면 우리가 여러 종류의 혼란을 겪을 때 다음과 같은 서로 다른 종류의 인지 과정이 관련되어 있다.
- 지식의 부족 = LTM(long-term Memory)
- 정보의 부족 = STM(short-term Memory)
- 처리 능력의 부족 = 작업 기억 공간의 문제
이러한 세 가지 인지 과정은 코드를 분석할 때뿐만 아니라 코드를 작성하거나 시스템을 설계할 때 혹은 문서를 작성할 때와 같이 모든 종류의 인지 활동에 나타난다.
LTM과 프로그래밍
프로그래밍과 관련된 첫 번째 인지 과정은 LTM이다.
여기에 있는 기억은 아주 오랫동안 보관된다.
이진 검색을 하는 프로그램을 작성할 때 추상적인 알고리즘과 프로그래밍 언어의 문법을 기억하고 나아가 키보드를 입력하는 동작 모드 LTM이 한다.
컴퓨터의 하드 드라이브와 비슷하다.
STM과 프로그래밍
STM은 들어오는 정보를 잠시 보관하기 위해 사용된다.
항목의 최대치가 대부분 12개를 넘지 않으며 코드 분석의 경우 변수명, 키워드, 자료구조등 STM에 일시적으로 저장된다.
자바코드의 예로 n이라는 정수형 숫자을 우리는 임시 자료형(Temp)에 담아두고 해당 코드 라인에서 이해할 때 사용한다.
그 함수가 종료된다면 해당 n이라는 변수를 지워버리게 된다.
컴퓨터의 캐시나 메인 메모리와 비슷하다
작업 기억 공간과 프로그래밍
STM과 LTM은 거의 기억장치라고 볼 수 있다.
실제 사고 작용은 LTM이나 STM이 아닌 작업 기억 공간에서 일어난다.
생각, 아이디어, 해결책 같은 것들이 여기에서 만들어진다.
컴퓨터의 프로세서와 비슷하다.
인지 과정들의 상호작용
앞 서 설명한 이들은 서로 밀접하게 연관되어 있다.
인지 과정들이 어떻게 상호작용하는지에 대한 간단한 설명
우리는 사고할 때는 이 세 가지 인지 과정을 모두 어느정도 활성화한다.
자바 코드를 예로 n이 정수형이라는 정보는 STM에 저장된다.
동시에 정수 그 자체에 대한 정보는 LTM에서 인출한다.
그리고 프로그램이 무엇을 하는지 파악하는 일은 작업 공간에서 이루어진다.
이러한 과정은 사실 프로그래밍뿐만 아니라 일상적인 문제를 해결하기 위해서도 동작하며 기본적인 흐름이라고 생각한다.
프로그래밍 업무와 관련된 인지 과정
고객으로부터 버그 리포트를 받는 상황을 고려해보자.
버그가 오프바이원 오류(off-by-one error)에 기인한 것이라고 할 때 이 버그 리포트는 시각이나 청각 같은 지각을 통해 두뇌로 입력된다.
오프바이원오류: 프로그래밍 루프에서 한번 더 또는 한번 덜 실행되는 경우 비교조건에 작거나 같다에 작다를 쓰거나 인덱스를 1이나 0을 고려하지 못했을 경우 발생한다.
버그를 고치기 위해서는 몇 개월 전에 작성한 코드를 다시 읽어야 한다.
코드를 분석할 때 현재 읽는 내용은 STM에 저장되며 동시에 과거에 구현한 내용은 LTM에서 가져올 것이다.
이 두가지 정보를 들고 작업 기억 공간에서 문제를 해결할 수 있게 된다.
연습 문제
세가지 인지 과정을 연습해보기 위해 세가지 프로그램이 준비되어 있다.
각각의 프로그램은 다른언어, 다른 기능을 하며 주의 깊게 읽고 무슨일을 하는지 파악해보라
코드 1 | 코드 2 | 코드 3 | |
---|---|---|---|
LTM에서 관련된 지식을 인출하는가? | X | O | X |
LTM에서 인출했다면, 어떤 정보를 가져왔는가? | X | Java문법 | X |
STM에 정보를 저장하는가? | X | O | O |
STM에 구체적으로 어떤 정보를 저장하는가? | X | 변수명 | 변수명 |
관련이 없어 보여 무시하고 넘어가는 정보는 없는가? | X | X | X |
코드의 특정 부분을 광범위하게 작업 기억 공간을 사용해서 분석하는가? | X | O | X |
코드의 어떤 부분이 작업 기억 공간에 과부화를 주는가? | X | 조건문 | LTM에 없는 정보 |
코드의 그 부분들이 작업 기억 공간을 어떻게 사용하는지 이해하는가? | X | O | O |
코드를 보자마자 멘붕..
느낀점
LTM, STM, 작업 기억 공간에 대한 이해를 높이고 시작하는 파트같다.
연습문제를 보자마자 머리가 어지러웠다..
LTM의 정보와 STM의 정보를 가지고 작업기억공간에서 해결을 봐야하는데
둘중 정보가 부족하거나 용량이 초과되면 작업기억공간에서는 혼란만 중첩되는 것 같다.
2장 신속한 코드 분석
핵심주제
- 경험 많은 개발자조차 코드를 빨리 이해하는 것이 어려운 이유
- 두뇌가 정보들을 어떻게 인식 가능한 부분으로 나누는지에 대한 이해
- 단어와 코드 같은 정보를 분석할 때 LTM과 STM 사이의 상호작용
- 코드 분석 시 영상 기억 공간의 역할
- 코드 기억을 통한 코딩 수준의 자가 진단
- 읽기 쉬운 코드를 작성하는 방법
1장에서는 세 가지 인지 과정에 대해서 알아봤다.
어디 연구인지 모르지만 연구에 따르면 프로그래머의 시간 중 60%는 코드를 이해하는 데 사용한다고 한다..!
앞서 좋은 코드에서는 나와 남들이 코드를 쉽고 좋게 이해시키기 위한 방법이라면, 이 책은 쉽게 읽는 방법..?!
코드를 신속하게 읽기
프로그램은 사람이 읽을 수 있도록 작성해야만 한다.
기계가 실행하는 것은 부차적인 일이다.
현실적으로 프로그래머들은 코드를 읽는 법보다 작성하는 법을 훨씬 더 많이 연습한다.
읽는 방법과 작성하는 방법은 생각보다 밀접하게 연관되어 있는 것 같다.
좋은 코드, 나쁜 코드를 같이 읽어서 인지 쉽게 읽기 위해선 좋은 코드를 읽어야 쉽게 읽을 수 있고..
인터넷 소설을 많이 보다가 책을 읽을려고 하면 글자가 눈에 안들어 오는 것 처럼 좋은 코드를 읽기 위해선 좋은 코드를 써야하고, 좋은 코드를 쓰기 위해선 좋은 코드를 읽어야 하는 것 같다.
코드를 읽는 과정은 가장 첫 번째 과정이다.
기능을 추가하거나, 시스템 자체를 파악하기 위해선 코드를 읽어야한다.
코드를 읽는 일련의 과정의 가장 큰 공통점은 그 코드에 존재하는 특정한 정보를 찾는다는 점이다..
새로운 기능을 추가할 적당한 부분, 수정한 코드 중 특정 버그가 있는 부분, 특정 메서드가 어떻게 구현됐는지 등이 있다.
이러한 정보를 찾는 능력을 향상하면 다시 찾아보는 횟수를 줄일 수 있다.
첫 번째 실험
간단하게 LTM을 사용해서 C#으로 삽입정렬을 구현해보자.
namespace ProgrammerBrain
{
class Program
{
static void Main(string[] args)
{
int[] array = {45, 12, 85, 32, 89, 39, 69, 44, 42, 1, 6, 8};
int temp;
for (int = 1; i < array.Length; i++)
{
int key = array[i];
int j;
for (j = i - 1; j >= -0; j--)
{
if (key <array[j + 1])
{
array[j + 1] = array[j];
}
else
{
break;
}
}
array[j + 1] = key;
}
return array;
}
}
}
내 두뇌에선 무슨일이..?
하나씩 생각을 해보면 LTM엔 삽입정렬에 대한 기억이 가장 크게 작용한 것 같고..
STM은 코드를 작성하면서 변수명이나 반복문의 결과를 가지고 있었다.
LTM과 STM을 조합하여 작업 기억 공간에서 작업을 했다.
두 번째 실험
이번에는 예제로 주어진 코드를 3분동안 보고 절대로 보지 말고 코딩을 해본다.
namespace ProgrammerBrain
{
class Program
{
public void aa(int[] x)
{
int b = x.Lenght;
for (int v = b / 2 - 1; v >= 0; v--)
{
func(x, v, b);
}
for (int l = b - 1; l > 0; l--)
{
int temp = x[l];
x[l] = l[0];
l[0] = temp;
func(x, l, 0);
}
}
}
}
생각보다 많이 다른 모습..
확실히 LTM에 이 코드에 대한 지식이 없기 때문에 어려웠고..
의도적으로 이상한 변수들이 사용되어서 혼란을 가져왔다.
전체적으로 LTM은 하나도 사용하지 못하고 STM에 의존하여 3분의 기억만 사용하니 문제가 된다.
코드를 읽기전 문서나 주석을 보고 이해하고 LTM에 비슷한 정보나 비슷한 패턴을 기억해냈다면 수월했을 것으로 추측한다.
이러한 STM을 사용하여 외우는 것이 안좋은 이유는 STM은 용량의 제한이 있기 때문이다.
앞 장에서 STM의 정보의 항목이 12개를 넘지 못한다고 했는데 기억의 시간도 30초를 넘지 못한다.
그 이후는 LTM으로 넘어가거나 삭제된다.
기억의 크기 제한을 극복하기
저명한 20세기 인지과학자 조지 아미티지 밀러는 STM이 2개에서 6개의 사이정도로 추정된다고 한다.
그렇지만 코드를 기억할 때 6개가 넘는 문자를 기억했다..
이론에 의하면 6개까지 읽고 나면 잊어버려야 하지만 당연하게 우리는 6개가 넘는 문자를 기억한다.
여기서 등장하는 개념이 청크(chunk)
이다.
자신이 가지고 있는 LTM의 개념을 사용하여 정보의 단위를 묶는 것이다.
체스의 예제와 같이 자신이 가지고 있는 데이터와 엮어서 생각하면 2~6개를 적절하게 잘 사용가능하다.
단어의 예제와 같이 처음 보는 생소한 몽골어 10자리를 외우는 것과 한국어 단어(10글자)를 외우는 것에 대한 차이는 크다.
읽는 것보다 보는 것이 더 많다.
정보는 STM에 도달하기 전에 감각 기억 공간이라는 영역을 통과한다.
감각 기억 공간은 컴퓨터의 입출력 버퍼 I/O라고 볼 수 있다.
이러한 감각 기억 공간에는 각 감각마다 임시 저장 공간이 있으며 이 장에서는 영상 기억 공간이라는 시각 관련 기억 공간에 대해서만 다룬다.
영상 기억 공간
우리는 코드를 읽을 때 눈을 통해 정보가 들어와 영상 기억 공간에 잠시 저장된다.
우리가 무언가 읽을 때 영상 기억 공간에 저장된다는 것을 알았지만 그 공간에 모든 정보를 STM이 처리할 수 없기 때문에, 코드를 읽을 때는 처리할 수 있는 정보를 선택해야 한다.
이러한 정보의 판단은 대부분 무의식적으로 이루어지기 때문에 해당 정보를 선택할 때 일상적이고 예쌍 가능한 상황을 청크로 묶어서 STM으로 전달하게 된다.
디자인 패턴도 좋은 방법 중 하나로 코드를 읽을 때 모든 구조를 이해하기 힘들지만 디자인 패턴과 같이 비슷한 구조로 이루어진 코드를 보면 쉽게 이해가 된다.
저수준이 아닌 주석문은 청킹하는데 도움이 될 수 있다.
좋은 코드 나쁜 코드에서도 나온 내용으로 모든 코드에 주석은 가독성과 코드의 오용을 남발할 수 있기 때문에 저수준 코드, 하위문제들에는 주석을 달지 않고 상위클래스의 문서나 주석정도로 관리하는 것이 좋다고 한다.
표식에 대한 예가 있는데 C#의 region키워드를 사용하여 같은 성격의 코드끼리 합쳐놓으면 청킹에 도움이 된다.
청킹 연습
청킹에 대한 의도적 연습
은 적극적으로 코드를 기억해내는 것을 훈련하면 아주 좋다.
코드베이스에서 하나를 골라서 코드를 파악하고 재현하고 회고를 진행한다.
이러한 과정은 의도적으로 청킹을 기르는데 좋은 방법이다.
요약
- STM은 두개에서 여섯 개 사이의 항목을 저장할 수 있는 용량을 갖는다.
- 정보를 기억할 때 STM은 크기에 대한 제약을 극복하기 위해 LTM과 협업한다.
- 새로운 정보를 읽을 때 우리 두뇌는 그 정보를 청크라는 몇 개의 묶음으로 나눈다.
- LTM에 지식이 부족하면 코드를 읽을 때 하위 수준의 정보들 이를테면 문자나 키워드 같은 것에 의존해야 한다. 이럴 때 STM의 공간이 빠르게 소진된다.
- LTM이 코드와 관련 있는 지식을 충분히 가지고 있다면 코드의 하위 수준의 요소들을 STM에 저장하는 대신
자바의 for 루프
,선택 정렬 알고리즘
의 형태로 추상 개념을 기억하기 때문에 공간이 절약된다. - 코드를 읽을 때 그 내용은 우선 영상 기억 공간에 저장된다. 그 후 코드 중 아주 적은 일부만이 STM으로 보내진다.
- 코드를 기억해내는 일은 프로그래밍에 대한 자식이 어느 정도인지 가늠해볼 수 있는 자가 진단 도구로 사용될 수 있다.
- 코드는 우리 두뇌에서 처리하기 쉽게 만드는 특징들 가령 디자인 패턴, 주석문, 명확한 표식 같은 것들을 가질 수 있다.
3장 프로그래밍 문법 빠르게 배우기
핵심주제
- 프로그래밍 언어 문법에 대한 폭넓은 지식이 중요한 이유와 고찰
- 프로그래밍 언어 문법을 기억하기 위한 방법의 선택
- 문법을 잊어버리지 않기 위해 할 수 있는 방법
- 문법과 개념을 언제 공부하면 가장 큰 효과를 볼 수 있는지에 대한 유추
- 문법과 프로그래밍에 대한 개념이 어떻게 LTM에 저장되는지에 대한 이해
- 프로그래밍 개념을 좀 더 잘 기억할 수 있기 위한 정교화 연습
프로그래밍의 개념을 더 잘, 그리고 더 쉽게 외우기 위해 유용하게 사용할 수 있는 네가지 중요한 방법을 소개한다.
문법을 기억하기 위한 팁
많은 소프트웨어 개발자는 프로그래밍 언어의 문법을 모르더라도 인터넷에 검색하면 되고, 따라서 문법에 대한 지식이 그리 중요한 것이 아니라고 생각한다.
모르는 것이 있을 경우 검색이 그렇게 좋은 해결책이 되지 못하는 이유가 두 가지가 있다.
첫 번째는 관련 내용을 미리 알고 있는 것이 코드를 효율적으로 읽고 이해하는 데 상당한 영향을 미치기 때문이다.
개념, 자료구조, 문법을 더 많이 알수록 두뇌는 더 많은 코드를 분리하고 기억하고 처리할 수 있기 때문이다.
두 번째 이유는 두뇌가 작업을 하다 업무 중단을 받게 되면, 우리가 생각한 것보다 휠씬 더 좋지 못한 결과를 초래하기 때문이다.
정보를 검색하기 위해 들어간 웹사이트에서 부터 다른 일을 하고 싶거나, 주제에 대해 너무 깊이 파고들게 되어버릴 수 있다.
검색에 대한 생각 나의 생각은 구글링은 개발자에게 필수적이지만 반복되는 경우는 조심해야 하는 것 같다.
문법에 대해서 궁금하여 검색을 해보고 이후에 같은 내용의 검색을 반복한다는 것은 해당 정보를 STM에서 LTM으로 옮기지 못한 것..
플래시카드 사용해 문법 배우기
플래시카드를 활용한 문법배우기..?
플래시카드를 프로그래밍에 활용할 때는 앞면에 개념을 적어놓고 뒷면에 해당하는 코드를 적는다.
파이썬의 컴프리헨션에 대한 플래시카드면 아래와 같이 만들 수 있다.
기본 문법 <-> numbers = [x for x in numbers]
플래시카드는 프롬프트가 있는 면을 읽고 나서 해당하는 문법을 기억하는 방식이다.
문법을 다른 종이헤 적거나 에디터에서 입력한 다음 카드를 뒤집어 맞았는지 확인한다.
프로그래밍 언어는 기본적인 문법을 외우는 것은 인간의 언어와 비교했을 때 상대적으로 적은 노력이 들어간다.
언제 플래시카드를 사용해야 하는가
어플을 이용하여 플래시 카드를 만드는 것도 좋은 방법이다.
플래시카드의 확장
플래시카드를 사용하기 적절한 때는 새로운 프로그래밍 언어나 프레임워크 혹은 라이브러리를 배울 때 새로운 개념을 접하면 해당 내용으로 플래시카드를 만들어 볼 수 있을 것이다.
플래시카드 개수 줄이기
사용하다 보면 내용을 잘 안다고 생가할 때가 온다.
이럴 때 카드를 줄이고 나중에 다시 사용할 수 있다.
여기까지가 플래시카드의 내용인데.. 문법이나 프레임워크를 위해 플래시카드를 사용한다면 그냥 시험을 통과하기 위한 암기 정도에 불과하다고 본다.
LTM이 아닌 STM 그 사이 어딘가.. 기억의 유지도 1~3일 정도만 남을 것 같고 그렇게 살아왔기 때문에 알 수 있다.
문법, 프레임워크, 라이브러리 사용법에 대한 지식은 직접 사용해보는게 가장 효율이 좋음을 알고있다.
사실 현재 프로젝트에 도입할 께 아니라면 특별한 케이스를 제외하고 플래시카드까지 써가면서 외울 필요가 있을까..?
어떻게 하면 잊어버리지 않을 수 있을까?
STM의 용량에 한계가 있다는 것을 이미 배웠는데, STM은 한꺼번에 많은 정보를 저장할 수 없을뿐더러 저장된 정보도 오랫동안 유지하지 않는다.
LTM도 제한이 있긴 하지만 STM과는 좀 다르다.
추가로 연습하지 않고는 내용을 오랫동안 기억할 수 없다는 것이 LTM의 가장 큰 문제점이다.
무엇인가 읽고 듣고 보고난 후 STM에 저장된 정보는 LTM으로 옮겨진다.
하지만 LTM에 영원히 저장되는 것은 아니다.
그런 점에서 인간은 LTM은 상대적으로 안전하고 오랫동안 저장되는 컴퓨터 하드 드라이브와는 큰 차이가 있다.
2일이 지나면 원래 지식의 25% 정도가 LTM에 남는다.
기억을 잃어버리는 이유
두뇌가 기억을 저장하는 방법은 컴퓨터와 동일하지 않지만 부호화
라는 용어는 동일하다.
기억이 뉴런에 의해 형성될 때 두뇌에서 일어나는 변화를 의미한다.
두뇌의 기억은 네트워크 구조로 되어 있다.
즉, 하나의 사실이 다른 많은 사실과 연결되어 있다.
- 망각 곡선
유명한 망각곡선..
사실 앞의 두뇌 기억이 네트워크 구조 즉, 그래프의 형태를 가지기 때문에 진정한 테스트가 될 수 없다.
파이썬의 컴프리헨션 문법을 기억하려고 할 때 for 루프 문법을 알고 있다면 이것의 도움을 받기 때문이다.
오랫동안 학습한 만큼 더 오래 기억한다.
잊어버리지 않기 위한 최선의 방안은 정기적으로 꾸준히 연습해야 한다는 사실이다.
반복할 때마다 기억은 강화된다.
긴 간격을 두고 반복하고 나면 LTM에 영구적으로 남아 있게 될 것이다.
대학에서 배운 내용을 너무 많이 잊어버려 의아한 적이 있다면 바로 이것 때문이다.
배운 지식을 계속해서 반복해서 다시 일깨우지 않으면 기억을 잃어버릴 것이다.
문법을 더 오랫동안 기억하기
프로그래밍의 언어의 문법을 외우는 것이 중요한 이유는, 그것이 코드를 청킹할 때 유용하고 검색 시간도 줄여주기 때문이라는 것을 살펴봤다.
기억을 강화하는 두 가지 테크닉에 대해서 다룰텐데 적극적으로 무언가를 일부러 기억해보려고 애쓰는 인출 연습과 기존 기억에 새로운 지식을 연결시키는 정교화다.
정보를 기억하는 두 가지 형태
캘리포니아 대학교의 심리학과 교수인 로버트 비요크와 엘리자베스 비요크는 LTM으로부터 기억을 가져오는 두 가지 서로 다른 기제, 즉 저장강도와 인출강도를 구분했다.
- 저장강도
저장강도란, 무언가를 LTM에 얼마나 잘 저장하고 있는가를 나타낸다.
어떤 것을 더 많이 학습할수록 그 내용을 잊어버리는 것이 불가능해질 때까지 기억은 점점 더 강해진다.
- 인출강도
인출 강도는 무언가를 쉽게 기억할 수 있는지를 나타낸다.
잘 알고 있다고 확신하고 있던 무엇인가를 (ex 노래제목, 함수 문법 등등) 막상 기억이 잘 나지 않는 경험, 입에서 맴돌기만 하고 기억이 나지 않았던 경험
이것이 해당 정보에 대해 저장 강도는 높지만 인출 강도는 낮다는 것을 의미한다.
최근 연구 결과에서는 기억은 결코 소실되지 않고 인출강도만 약해지는 것으로 알려져 있다.
단지 보기만 해서는 안 된다.
이전에 여러 번 봤던 코드라고 해도 단지 보기만 하는 것으로는 나중에 그 코드를 기억할 수 없다.
LTM 어딘가에 정보가 저장되어 있지만 그것을 필요로 할 때는 정작 가져오지 못하는 것이다.
정보를 외우는 것은 기억을 강화한다.
LTM에 정보를 저장하는 것만으로는 충분하지 않다..
저장된 정보를 쉽게 인출할 수 있어야 한다.
우리의 삶의 많은 것이 그렇듯이 인출을 더 쉽게 하기 위해서는 많이 연습해야 한다.
무언가를 능동적이고 의도적으로 기억해내려는 노력이 기억을 강화한다는 사실은 고대 아리스토텔레스 시대까지 거슬러 올라가 알려져 있던 기술이다.
따라서 다음번에 구글에서 프로그래밍 문법에 대해 검색하려고 할 때, 검색 이전에 먼저 그것을 능동적이고 의도적으로 기억하려고 시도해보기 바란다.
당장 기억이 나지 않더라도 이런 기억하려는 노력은 기억을 강화하고 다음번에 기억해내는 데 도움이 될 것이다.
능동적 사고를 통한 기억력 강화
기억을 강화하는 두 번째 방법은 정보에 대해 능동적으로 생각하고 그것을 반추해보는 것이다.
정보에 대해 생각하는 과정을 정교화라고 부른다.
정교화 작업은 복잡한 프로그래밍 개념을 학습할 때 효과가 좋다.
두뇌에서 저장을 다루는 방법
- 스키마타
우리 두뇌에서 기억은 다른 기억과 사실에 연결되는 연관된 네트워크 형태라는 것을 살펴봤다.
사고나 생각이 서로 관련되어 조직된 방식을 스키마 혹은 스키마타라고 부른다.
새로운 정보를 학습할 때 정보는 LTM에 저장하기 전에 먼저 스키마의 형태로 만들어진다.
이미 존재하는 스키마에 잘 맞는 정보일수록 더 쉽게 기억할 수 있다.
예를 들어 5 12 91 54 102중 세 개의 숫자를 기억하면 원하는 멋진 상품을 준다고 한다면 이 숫자 목록은 원하는 상품을 받기 위해 기억해야할 숫자 모음
이라는 새로운 이름의 스키마에 저장될 것이다.
반면 3, 12, 15, 27, 30이러한 숫자는 더 외우기 쉬울 것이다.
조금만 생각해도 숫자가 속한 범주를 알 수 있다.
- 정교화를 이용해 새로운 프로그래밍 개념 학습하기
정교화는 기억하고자 하는 내용을 기존 기억과 연관 지으면서 생각하는 것을 뜻하고, 이렇게 한 결과 LTM에 이미 저장되어 있는 스키마타에 맞춰서 새로운 기억이 저장된다.
새로운 문법을 학습한다고 할 때 문법을 좀 더 잘 기억하려면 관련된 개념을 생각하면서 정교화하는 게 큰 도움이 된다.
다른 프로그래밍의 대체 문법을 생각해보거나 다른 패러다임과의 관련성을 따지는 것
새로운 프로그래밍 개념을 배울 때 이 연습을 사용해볼 것
다음과 같은 질문들에 답하면 정교화와 새로운 기억을 강화하는 데 도움이 될 것이다.
- 새로운 개념이 다른 개념을 생각나게 했는가? 모든 관련된 개념을 적어보라
- 위에서 관련된 개념에 대해 다음과 같은 질문들에 답해보라
- 새로운 개념은 왜 이미 알고 있는 그 개념을 생각나게 했을까?
- 문법에 공통적인 점이 있는가?
- 비슷한 환경에서 사용될 수 있는가?
- 이 새로운 개념은 이미 알고 있는 그 개념 대신 사용될 수 있는가?
- 동일한 목적을 달성하기 위해 작성할 수 있는 다른 방법의 코드를 알고 있는가? 같은 결과를 갖는 비슷한 코드를 최대한 많이 만들어보라
- 다른 프로그래밍 언어에서는 같은 개념이 있는가? 비슷한 동작을 수행하는 다른 언어의 예제 코드를 작성할 수 있는가? 그것들은 서로 어떻게 다른가?
- 이 개념은 어떤 패러다임, 도메인, 라이브러리 혹은 프레임 워크와 잘 맞는가?
요약
- 문법에 대한 지식이 더 많을 수록 청킹을 쉽게 할 수 있기 때문에 문법을 외우는 것이 중요하다.
- 앞면에는 프롬프트를, 뒷면에는 코드를 적어놓은 플래시카드를 사용해서 새로운 문법을 연습하고 기억할 수 있다.
- 기억이 없어지는 것을 방지하기 위해, 새로운 정보를 기억하는 연습을 정기적으로 하는 것이 중요하다.
- 최상의 연습은 기억한 것을 두뇌로부터 인출하는 연습이다. 다른 곳에서 해당 정보를 찾기 전에 기억해내려고 노력해야 한다.
- 기억하는 지식의 양을 최대로 하기 위해서는 연습을 긴 시간에 걸쳐 고르게 해야 한다.
- LTM 속 정보는 관련된 사실이 서로 연결되어 있는 네트워크의 형태로 저장된다.
- 새로운 정보를 능동적으로 정교화하면 그 새로운 기억이 연결할 기억의 네트워크를 강화하고 이는 인출을 쉽게 하는 데 도움이 된다.
4장 복잡한 코드 읽는 방법
핵심주제
- 작업 기억 공간이 복잡한 코드에 의해 과부하가 걸릴 때 어떤 일이 일어나는지 분석
- 프로그래밍에서 두 가지 종류의 작업 기억 공간 과부하
- 과부화가 걸린 작업 기억 공간을 보상하기 위해 읽기 쉬운 코드로 리팩터링하는 법
- 복잡한 코드를 읽을 때 작업 기억 공간을 지원하기 위한 상태표와 의존 그래프 생성하기
코드가 너무 복잡해서 완전히 이해하지 못하는 경우가 가끔 있다.
대부분의 프로그래머가 코드 읽는 연습을 많이 해보지 않았기 때문에 이해되지 않는 코드를 다루는 방법론을 잘 모를 수 있다.
작업 기억 공간의 기저에 있는 인지 과정을 알아본다.
복잡한 코드를 이해하는 것이 왜 어려울까?
내부의 복잡한 로직을 머리속으로 따라가다 보면 현재 변수값을 어디에 적고 싶은 욕구가 들 것이다.
이는 우리의 두뇌가 처리할 용량이 부족하다는 것을 의미한다.
작업 기억 공간과 STM의 차이
STM의 역할이 정보를 기억하는 것인 반면, 작업 기억 공간의 역할은 정보를 처리하는 것이다.
작업 기억 공간도 STM과 같이 용량의 제한이 있다.
이를 인지 부화
라고 부르며 너무 많은 요소가 있어 청크로 나뉘지 않는 문제를 풀려고 할 때 작업 기억 공간은 과부화
상태가 된다.
프로그래밍과 관련한 인지 부화의 종류
인지부화의 종류는 크게 3가지로 나뉜다.
- 내재적 부하: 문제 자체가 얼마나 복잡한지
- 외재적 부하: 외부적 요인에 의해 문제에 추가된 것
- 본유적 부하: 생각을 LTM에 저장하는 과정에서 일어나는 인지 부하
내저적 인지 부화(intrinsic cognitive load)는 문제 그 자체가 갖는 특성 때문에 발생하는 인지 부화이다.
예를 들어 직각삼각형의 빗변의 길이를 계산하는 문제의 경우 피타고라스의 정리를 사용해서 풀어야 한다.
이 외에는 다른 방법이 존재하지 않으며 이러한 문제를 내저적 인지 부화라고 한다.
프로그래밍에서는 내재적 복잡성이라는 용어를 사용하여 문제의 내재적 측면을 설명한다.
외재적 인지 부화(extraneous cognitive load)는 내재적인 부하에 더해서
문제에 추가되는 인지 부화다.
직각 삼각형의 빗변의 길이를 계산할 때 밑변과 높이에 대한 값을 변수로 한번 매핑한 경우 이를 연결하는 과정을 외재적 업무로 추가 수행해야 한다.
연습 문제
익숙하지 않은 코드를 읽을 때 인지 부하를 모니터링해보기.
코드를 읽을 때 잘 이해가 안 되고 뭔가 적어놓거나 단계별로 실행 순서를 따라가고 싶은 마음이 들면 인지 부하가 높은 것이다.
인지 부하가 높을 때는 코드의 어느 부분이 다른 종류의 인지 부하를 유발하는지 확인해보면 좋다.
인지 부화를 줄이기 위한 기법
코드가 작업 기억 공간에 과부하를 초래하는 다양한 방식에 대해 살펴봤으므로 인지 부하를 줄이는 방법에 대해 알아보자.
리팩터링
리팩터링이란, 코드가 외부적으로 제공하는 기능은 유지한 채 내부 구조를 개선하는 것을 의미한다.
간단한 예로 코드 블럭의 길이가 길면 여러 함수로 나누거나 재사용을 위해 중복된 코드를 하나로 통합하는 것을 말한다.
하지만 유지 보수하기 좋게 수정됐다고 해서 가독성이 반드시 좋아지는 것은 아니다.
메서드의 호출이 많은 코드를 생각해보면 메서드가 같은 파일 혹은 여러 파일에 걸쳐서 존재하는 메서드라면 모든 코드의 로직이 독립적이기 때문에 유지보수하기에는 좋은 코드이다.
하지만 탈국지화된 코드는 여러 군데에서 메서드의 내부 구현을 찾아봐야 하기 때문에 작업 기억공간에는 어려움을 줄 수 있다.
따라서 유지 보수하기 좋은 코드를 작성하기보다는 장기적으로 가독성이 높은 코드를 작성하도록 리팩터링하는 것이 좋을 수도 있다.
이런 방식을 인지적 리팩터링
이라고 정의한다.
유지보수하기 좋은 코드로 변경하는 것이 아닌 현 시점에서 개발자의 관점으로 읽기 쉬운 코드로 변경하는 것
인지적 리팩터링은 때로는 역 리팩터링을 수반할 수 있다.
즉 오히려 코드의 유지 보수성을 더 낮추는 결과를 가져올 수 있다.
책에서 나온 IDE의 추천이나 인라인 메서드, 메서드의 순서등 좋은 인지적 리팩터링 방법이다.
내가 좋게 봤던 경우는 선언 부에서 중요도에 따라서 선언을 해두고 순서를 맞춰두니 좋았던 경우가 있다.
생소한 언어 구성 요소를 다른 것으로 대치하기
코드를 읽을 때 혼란이 되는 세 가지 원인, 즉 지식 부족, 정보 부족, 처리 능력 부족을 극복하는데 도움이 되는 기법에 대해 이제부터 다뤄보겠다.
C#, Java의 람다식 그리고 파이썬의 컴프리헨션은 간단하고 가독성 높은 코드를 만들 때 유용하지만 많은 프로그래머는 람다에 익숙하지 않으며 for루프나 while루프를 읽고 이해하는 것보다 어려워한다.
간단한 람다는 문제가 되지 않지만 복잡한 경우에는 작업 기억 공간에 과부화가 발생한다.
익숙하지 않은 언어 구성 요소는 작업 기억공간에 외재적 인지 부하를 늘리기 때문에 복잡한 코드를 읽을 때는 이들로 인한 부하가 늘어나지 않게 하는 것이 좋다.
작업 기억 공간에 부하학 오면 쓸 수 있는 기억 보조 수단
리팩터링을 통화 인지 부하를 줄이는 방법을 소개했지만 여전히 코드의 구조가 복잡하다면 작업 기억 공간에게는 과부하다.
- 정확히 코드의 어디를 파악해야 하는지 모를 때이다.
이 경우에는 필요 이상으로 많은 코드를 읽게 되고 이것은 작업 기억 공간이 처리할 수 있는 것보다 많은 양이 될 수 있다.
- 코드가 서로 밀접하게 연결되어 있는 경우 두뇌는 두 가지 작업을 동시에 수행한다.
코드의 개별 라인을 이해하면서 어느 부분을 계속 읽어야 하는지 판단하기 위해 코드의 구조를 이해해야 한다.
예를 들어 정확히 무슨 일을 하는지 알지 못하는 메서드에 대한 호출이 코드에서 이루어진다면 코드 파악을 계속하기 전에 우선 그 메서드를 찾아서 읽어야 한다.
의존 그래프 생성
코드를 바탕으로 의존 그래프를 만들면 흐름을 이해하고 논리적 흐름에 따라 코드를 읽는 데 도움이 된다.
코드를 프린트하거나 PDF로 만들어서 디지털 주석을 달면 좋다..?
비슷한 변수나 함수의 호출 정의를 연결하는 방법인데… 플래시카드와 마찬가지로.. 생소한 방법이라고 생각한다.
상태표
리팩터링을 통해 어려운 코드를 자신의 지식에 맞는 쉬운 형태로 바꾸고 코드에 의존관계를 표시한 후에도 코드가 여전히 이해가 안 될 수 있다.
때로는 코드의 구조 때문에 이해가 어려운 것이 아니라 코드에서 수행하는 계산 로직 때문에 어려울 수 있다.
의존 그래프와 상태표의 혼용
앞서 설명한 두가지 방법을 혼용해서 사용하면 구조와 결과에 대한 정보를 쉽게 이해할 수 있다.
요약
- 인지 부하는 작업 기억 공간이 처리할 수 있는 한계를 나타낸다. 인지 부하가 너무 크면 두뇌가 코드를 적절하게 처리할 수 없다.
- 프로그래밍과 관련해 두 가지 종류의 인지 부하가 있다. 내재적 인지 부하는 코드에 존재하는 복잡성에 기인하고 외재적 인지 부하는 우발적으로 혹은 코드를 읽는 개발자의 지식의 부족에 기인한다.
- 리팩터링은 코드를 읽는 사람이 이미 가지고 있는 지식에 맞춰 코드를 변경함으로써 외재적 인지 부하를 줄이는 방법이다.
- 의존 그래프는 복잡하고 서로 밀접하게 연결되어 있는 코드를 이해하는 데 도움이 된다.
- 코드의 실행 도중에 변수가 갖는 값을 보여주는 상태표는 계산이 많이 수행되는 코드를 파악하는 데 유용하다.
아직은 코드를 읽는데 어려움이 없었던건지 그냥 읽고싶은대로 읽은건지 책에서 제시하는 여러가지 방법에 대한 생각이 크게 좋은 방향은 아닌 것 같다.
5장 코드에 대해 생각하기
핵심주제
- 변수가 프로그램에서 수행하는 여러가지 역할
- 코드의 표면적 지식과 코드 작성자의 의도를 이해하는 것의 비교
- 인간의 언어와 프로그래밍 언어를 읽고 이해하는 것의 비교
- 코드를 깊이 있게 이해하기 위한 다양한 전략
‘변수 역할’ 프레임워크
코드에 대한 추론할 때는 변수가 중심적인 역할을 한다.
변수가 어떤 종류의 정보를 담고 있는지 이해하는 것은 코드를 추론하고 수정하는 데 결정적인 역할을 한다.
대부분의 프로그래머들이 변수를 연관 지을 좋은 스키마를 자신들의 LTM에 가지고 있지 않기 때문이다.
사야니에미는 변수가 프로그램에서 수행하는 여러가지 역할을 정리한 프레임워크를 제안했다.
변수 역할
변수의 역할은 프로그램 내에서 변수가 하고자 하는 바를 나타낸다.
변수는 각자 다른 일을 한다
upperbound = int(input('Upper bound?'))
max_prime_factors = 0
for counter in range(upperbound):
factors = prime_factors(counter)
if factors > max_prime_factors:
max_prime_factors = factors
변수 역할 프래임워크를 변수들의 동작에 존재하는 차이점을 포착한다.
- upperbound: 최근값 보유자역할을 가지고 있다(가장 최근에 입력된 상한 값을 보유하기 때문이다.)
- counter: 스테퍼역할로, 루프를 따라 반복한다.
- max_prime_factors: 목적값 보유자역할로, 프로그램이 계산을 통해 찾고자 하는 값을 저장한다.
- factors: 최근값 보유자 역할을 한다.
11가지 역할
- 고정값: 초기화를 통해 값이 할당된 이후 값이 변경되지 않는 변수다.
- 스테퍼: 루프를 반복 실행하며 값이 단계적으로 변하는 변수가 스테퍼 역할을 한다.
- 플래그: 무엇인가 발생했거나 어떤 경우에 해당하는지를 나타내는 변수
- 워커: 워커는 스테퍼와 유사하게 자료구조를 순회한다.
- 최근갑 보유자: 어떤 값이 변해갈 때 가장 최근에 변경된 값을 갖는 변수다.
- 목적값 보유자: 어떤 값에 대해 반복할 때는 그 목적이 어떤 특정한 값을 찾는 것일 수 있다.
- 모집자: 모집자는 데이터를 모으거나 모은 데이터에 대해 어떤 연산을 수행하는 얻은 값을 저장하는 변수다.
- 컨테이너: 값을 새로 추가하거나 삭제할 수 있는 자료구조라면 어떤 것이라도 컨테이너 변수이다.
- 추적자: 어떤 알고리즘에서는 이전 값 혹은 다음 값을 추적해야 할 필요가 있다.
- 조직자: 때론 추가적인 처리를 위해 변수의 값을 변환해야 한다.
- 임시: 임수 변수는 잠시만 사용하기 위한 변수
역할과 패러다임
역할은 특정한 프로그래밍 패러다임에만 제한되지 않고 모든 패러다임에 나타난다.
public class Dog{
String name;
int age;
public Dog(String name, int age){
this.name = name;
this.age = age;
}
public void birthday(){
age++;
}
}
- name: 고정값 (name은 설정한 후 변경되지 않음)
- age: 스테퍼 역할을 수행
역할의 이점
변수 역할 프레임워크는 프로그래머가 코드를 읽고 이해하는 데 도움을 준다.
- 변수 역할 프레임워크 연습
친숙하지 않은 코드를 고르고 변수에 살펴보면서 당므과 같은 사항을 기록해볼 것
import java.util.LinkedList;
import java.util.List;
public class Deck {
private List<Card> cards;
Deck() {
this.cards = generateCards();
}
private List<Card> generateCards() {
LinkedList<Card> cards = new LinkedList<>();
for (Pattern p : Pattern.values()) {
for (int i = 1; i <= 13; i++) {
Card c = new Card(p, i);
cards.add(c);
}
}
return cards;
}
public Card pick() {
int randomIndex = (int) (Math.random() * cards.size());
return cards.remove(randomIndex);
}
}
C#
을 주로 사용하다 보니 너무 익숙해서 java코드를 들고 왔다.
변수명 | 타입 | 연산 | 역할 |
---|---|---|---|
cards | List |
생성자에서 초기화, 이후 add되며 card를 추가 | 컨테이너 |
p | Pattern | for문에서 초기화 | 스테퍼 |
i | int | for문에서 초기화 | 스테퍼 |
c | Card | for문에서 초기화 | 최근값 보유자 |
randomIndex | int | pick()에서 초기화 | 최근값 보유자 |
- 변수 역할에 대한 실제적인 팁
코드를 프린트하거나 PDF로 저장하여 주석을 달면 도움이 된다.
처음에는 거부감이 들 수 있지만 생각 이상으로 깊어지고 코드를 다른 수준으로 파악하는 것이 가능해진다.
헝가리안 표기법
헝가리안 표기법은 변수의 타입을 변수명에 나타내는 방법이다.
ex) string strName = “홍길동”; <- strName은 타입을 변수명에 노출함
프로그램에 대해 깊이 있는 지식을 얻으려면
텍스트 지식 대 계획 지식
텍스트 구조 지식은 키워드가 하는 일이나 변수의 역할 같은 프로그램의 표면적인 이해와 관련되어 있다.
반면 계획 지식은 프로그래머가 프로그램을 작성할 때 계획한 것이 무엇인지 혹은 무엇을 달성하려고 했는지를 나타낸다.
프로그램 이해의 여러 단계
표면적 지식으로부터 좀 더 갚은 이해로의 진행은 다음과 같은 4가지 단계를 거친다.
- 초점을 찾는다.
- 초점으로부터 지식을 확장한다.
- 관련된 개체로부터 개념을 확장한다.
- 여러 개체에 걸쳐 있는 개념을 이해한다.
코드를 읽을 때 초점은 중요한 개념이다.
어디서부터 읽기 시작해야 할지 알아야 한다는 것이다.
하지만 어떤 기술, 스택, 프레임워크 의존성 주입등으로 인해 초점을 찾기 어려울 수 있다.
따라서 어디서 부터 시작하는지 알려면 프레임워크에서 코드가 어떻게 연결되는지 알아야 한다.
깊은 코드 이해 4단계 적용해보기
4장에서 살펴본 인지부화를 줄이기 위한 방법을 적용해보자.
- 모든 변수를 원으로 표시한다.
- 비슷한 변수들을 연결한다.
- 모든 메서드나 함수 호출을 원으로 표시한다.
- 메서드나 함수 호출을 정의와 연결한다.
- 클래스의 모든 인스턴스를 원으로 표시한다.
- 클래스와 그 클래스의 인스턴스를 연결한다.
이러한 방법을 최대로 활용하는 법은 위에서 나온 PDF로 출력하여 손으로 직접 그리며 주석을 달고 코드를 이해하는 과정이다.
- 연습: 익숙하지 않은 코드를 찾아서 위의 과정을 진행해보자
텍스트를 읽는 것과 코드를 읽는 것은 유사하다
프로그래머는 평균적으로 자신읭 업무 시간의 60%를 코드를 읽는데 사용한다.
이 부분은 읽기 힘들었다.. 인지과학적인 내용이 많아서 일까?
코드를 읽을 때 우리 뇌에서는 무슨 일이 일어나는가?
브로드만 영역
프랑스어를 배울 수 있다면 파이썬도 배울 수 있다
컴퓨터 과학은 종종 (대부분) STEM(과학, 기술, 엔지니어링, 수학) 분야의 일부로 간주된다.
하지만 프랫의 연구는 산술 능력이 프로그래밍 능력에 대해 예측력이 별로 없음을 보여준다.
사람들은 어떻게 코드를 읽는가?
텍스트를 어떻게 읽는지 부터 생각을 해본다면 여러가지 방법이 있다.
- 자세히 읽기 전에 훑어보고 전체적인 흐름을 파악한다.
- 의도적으로 테스트에 딸린 이미지만 볼 수 있다.
- 읽고 있는 내용을 요약/강조한다
- 텍스트만 훑는 동시에 그림을 같이 본다.(텍스트 이해 전략)
음.. 나는 3번을 주로 사용한다..
정리하면서 읽는 스타일이라 전체적으로 한번 읽고 다시 정리하면서 같이 읽는 편이다.
프로그래머는 코드를 읽을 때 스캔을 먼저 한다
사람들이 시각을 통해 무엇을 보는지 이해하고자 할 때 시각 추적기가 사용된다.
실제 연구에서는 코드를 검토하는 데 사용한 시간의 처음 30%동안 코드의 70%를 훑어본다는 것을 발견했다.
초급 프로그래머와 숙련된 프로그래머는 코드를 읽는 방식이 다르다
초급 프로그래머의 경우 텍스트를 읽을 때 시야의 움직임의 약 80%는 순차적인 것이었고, 코드를 읽을 때는 75% 정도가 순차적이었다.
코드 읽기에 적용해볼 수 있는 텍스트 이해 전략
- 활성화: 관련된 것들을 적극적으로 생각해서 이미 가지고 있는 지식을 활성화하는 것
- 모니터링: 텍스트를 읽으면서 자신이 이해한 것(이해하지 못하는 것까지)을 관찰하고 기록하는 것
- 중요도 결정: 텍스트에서 어느 부분이 중요한지 결정하는 것
- 추론: 텍스트에서 명시적으로 주어지지 않는 사실을 유추하는 것
- 시각화: 깊이 있는 이해를 위해 텍스트에 대한 도표를 만드는 것
- 질문: 텍스트에 대해 질문하는 것
- 요약: 텍스트를 짧게 요약하는 것
기존 지식의 활성화
프로그래머는 코드를 읽기 전에 먼저 스캔을 한다.
이는 코드 내에 존재하는 개념과 문법적 요소들에 대한 일차적인 이해가 가능하기 때문이다.
-
익숙하지 않은 코드를 미리 정한 시간동안 공부하라 그리고 다음과 같은 질문에 답해볼 것
- 가장 먼저 시선을 끈 코드의 구성 요소(변수, 클래스, 프로그래밍 개념 등)는 무엇인가?
- 왜 그런가?
- 두 번째로 주의를 끈 것은 무엇인가?
- 왜 그런가?
- 그 두 가지(변수, 클래스, 프로그래밍 개념 등)는 서로 관련이 있는가?
- 코드에 어떤 개념들이 존재하는가? 그 문법 요소들을 다 알고 있는가?
- 코드에 어떤 도메인 개념들이 존재하는가? 그 도메인 개념들을 다 알고 있는가?
모니터링
코드를 읽을 때 현재 무엇을 읽고 있는지, 이해는 하고 있는지를 계속 추적하는 것이 중요하다
코드를 프린트해서 이해되는 라인과 이해되지 않는 라인을 표시하는 것도 좋은 방법이다.
코드에서 중요한 라인을 결정하기
코드를 읽을 때 어떤 라인이 중요한지 파악하는 것이 유용할 때가 있다.
이것은 의도적인 연습을 통해 가능하다.
-
익숙하지 않은 코드의 일부를 선택해서 중요한 라인을 찾아보라
- 중요한 라인으로 선택한 이유는 무엇인가?
- 그 라인의 역할은 무엇인가? 예를 들면 초기화나 입출력 혹은 데이터를 수행하는 라인인가?
- 그 라인은 프로그램 전체 목적과 어떻게 관련되는가?
변수명의 의미를 추론하기
프로그램의 의미가 코드의 구조 자체에 담겨 있을 때가 많다.
예를 들면 루프나 조건문 사용 같은 것이다.
또한 변수 등 프로그램 구성 요소의 이름으로부터 의미를 유추할 수 있는 경우도 있다.
이미 살펴봤지만 변수 이름은 중요한 표식이다.
코드가 하는 일에 대한 힌트를 제공하는 기능을 한다.
- 코드의 일부를 선택한 후에 거기에 나오는 모든 변수의 이름을 세밀하게 나열해보라
변수 이름 | 도메인 | 개념 | 코드를 보지 않고 이해할 만한가? |
---|---|---|---|
변수 이름에 대한 테이블을 이용해서 다음 질문들에 답할 수 있다.
- 코드의 도메인 혹은 주제는 무엇인가?
- 어떤 프로그래밍 개념들이 사용되었는가?
- 이름으로부터 알 수 있는 사실은 무엇인가?
- 서로 관련되어 있는 이름은 무엇인가?
- 배경을 알지 않으면 의미가 모호한 이름이 있는가?
- 모호한 변수명이 해당 코드에서 가질 만한 의미는 무엇이 있을까?
시각화
매우 복잡한 코드에서 유용한 한 가지 방법은 변수가 연관된 모든 연산을 나열하는 것이다.
질문하기
코드를 읽을 때 스스로에게 질문하는 것이 코드의 목적과 기능에 대해 이해하는 데 도움이 된다.
아래와 같은 질문의 예를 살펴보자
- 코드에서 다섯 가지의 중심 개념은 무엇인가? 이 중심 개념이 식별자, 테마, 클래스 혹은 주석문내의 정보로 나타나는가?
- 중심 개념을 찾기 위해 어떤 전략을 사용했는가? 예를 들어 메서드 이름, 문서 혹은 변수몀을 살펴봤다거나 아니면 시스템에 대해 이미 가지고 있는 지식을 활용했는가?
- 코드에서 발견되는 가장 중심적인 컴퓨터 과학의 중심 개념 다섯가지는 무엇인가? 알고리즘, 자료구조, 가정, 사용된 기술 등이 이에 해당할 수 있다.
- 코드 작성자가 내린 결정 사항이 무엇인가? 예를 들면 특정 버전의 알고리즘을 구현하기로 한 결정, 특정 디자인 패턴을 사용하기로 한 결정, 특정 기술을 사용하기로 한 결정 등이 있다.
- 그런 결정을 내리는 데 상정한 가정은 무엇인가?
- 그 결정의 효과는 무엇인가?
- 그 결정의 잠재적 위험 요소는 무엇인가?
- 다른 해결책으로 어떤 것이 있을까?
이런 질문들은 텍스트의 구조 지식 너머로 더 깊이 파고들어 계획 지식에 도달하고 코드를 이해하는 데 도움이 될 수 있다.
코드 요약
코드 이해를 위해 적용할 수 있는 텍스트 읽기 전략 가운데 마지막으로 방금 전에 읽은 코드를 요약하는 것이 있다.
- 짧은 코드를 하나 선택하고 테이블을 채우면서 요약해보라.
항목 | 요약 |
---|---|
코드의 목적: 달성하고자 하는 바? | |
가장 중요한 라인 | |
가장 관련 있는 도메인 개념 | |
가장 관련 있는 프로그래밍 구성 요소 | |
코드 작성시 내려진 결정 |
요약
- 생소한 코드를 읽을 때는 스테퍼나 목적값과 같은 변수의 역할을 이해하는 것이 코드를 깊이 이해하는 데 도움이 된다.
- 코드의 이해에 관해서는 텍스트 구조 지식과 계획 지식 사이에 차이가 있다.
- 코드를 읽는 것은 자연언어 텍스트를 읽는 것 사이에는 유사한 점이 많다.
- 시각화, 요약과 같은 자연언어 텍스트를 심도 있게 이해하기 위해 사용하는 전략들을 코드의 이해를 돕기 위해 사용할 수 있다.
6장 코딩 문제 해결을 더 잘하려면
- 핵심 주제
- 프로그래밍 문제를 더 효과적으로 숙고하기 위해 모델을 적용하는 것
- 문제에 대해 다른 방식으로 생각하면 해결하는 방식도 영향을 받는다는 사실의 발견
- 코드에 대해 생각하고 문제를 해결하기 위해 모델을 사용하는 방법에 대한 탐구
- LTM을 개선하서 문제를 해결하기 위한 새로운 방법을 학습하는 여러 기법의 고찰
- 문제 해결을 위해 작업 기억 공간을 돕는 모델을 사용하는 기법의 연습
- 중요하지 않은 사항은 추상화하고 중요한 사항은 포함시켜 문제의 범위를 올바르게 찾도록 분석
모델을 사용해서 코드에 대해 생각해보기
사람들은 문제를 풀 때 대부분 모델을 만든다.
모델은 실재를 간단하게 표현한 것으로, 주된 목적은 문제에 대해 생각하고 해결하는 데 도움을 주기 위한 것이다.
모델의 유익함
문제를 풀 때 코드의 모델을 명시적으로 사용하는 것은 두 가지 장점이 있다.
첫 째, 모델은 프로그램에 대한 정보를 다른 사람에게 제공할 때 유용하다.
UML같은 클래스 다이어그램 모델이 그 예이다.
둘 째, 모델은 문제를 해결하는 데 도움이 된다.
두뇌에서 처리하는 인지부화를 줄일 수 있다.
모델은 LTM이 관련된 기억을 찾는 데 도움이 된다.
모든 모델이 동일하게 유용한 것은 아니다
각 문제에 맞는 모델을 적용하는 것이 유용하며 쉬운 문제의 경우에는 모델이 역효과를 낼 수 있다.
정신 모델
앞서 다룬 모델은 두뇌 바깥의 영역으로 상태표, 그래프, 객체 관계등등의 모델들이 있다.
하지만 문제에 대해 생각할 때 두뇌의 외부에서 만들어지지 않는 모델을 사용할 수 있다.
이러한 모델을 정신 모델이라고 한다.
코드를 위해 사용할 정신 모델의 예는 트리 순회에 대해 생각해보는 것이다.
코드와 컴퓨터에 우리가 순회하는 실제 트리는 존재하지 않는다.
우리가 트리 구조라고 생각하는 메모리 내의 값이 있을 뿐이다..!
정신 모델은 풀어야 할 문제에 대해 추론하기 위해 사용할 수 있는 작업 기억 공간 내의 추상화다.
자료구조나 파일단계에서 생각하고 코딩할 때 우리는 이러한 정신 모델을 자주 사용하게 된다.
정신 모델 자세히 살펴보기
특징 | 예 |
---|---|
불완전하다. 정신 모델은 나타내고자 하는 원래 시스템에 대한 완전한 모델일 필요는 없다. | |
마치 어떤 면에서 스케일 모델이 모델링하는 물리적 객체를 단순화한 것 처럼,, | 변수를 값이 들어간 박스로 생각하는 것은 재할당에 대해 적절하게 설명해주지 못한다. |
정신 모델을 같은 상태로 유지할 필요가 없다. 자주 변경됨 | 초기에 박스에 대한 비유가 도움이 되지만 cs를 알게되면 이름표같은 비유가 더 적절하다 |
모순이 되더라도 여러개의 정신 모델이 공존할 수 있다. | 이름표와 박스 두가지가 공존할 수 있고 상황에 따라 장점이 다르다. |
미신, 가짜처럼 느껴질 수 있다. | 추상적인 개념이기 때문에 |
사람들은 가급적이면 정신 모델을 사용하지 않으려고 한다. | 디버깅을 할 때 정신모델을 만드는 것보다 코드를 바로 수정하는 것이 더 유리하다고 생각해서 |
새로운 정신 모델 배우기
프로그래밍을 배울 때 종종 새로운 모델을 배우게 된다.
이러한 정신모델은 유산처럼 남아 부정확하거나 불완전한 정신 모델로 사용될 위험이 있다.
특히 인지부화가 높은 상황에서 위험하다.
눈사람이 스웨터를 입는 예제와 같이 복잡한 코드에서도 단순한 정신 모델을 사용한다.
코드에 대해 생각할 때 정신 모델을 효율적으로 사용하는 방법
인지 과정과 정신 모델이 어떤 연관이 있는지 궁금할 수 있다.
이러한 모델은 LTM에 저장되고 필요할 때 인출되는가? 아니면 코드를 생각할 때 작업 기억 공간이 만드는가?
작업 기억 공간에서의 정신 모델
존슨-레어드는 두뇌가 추론하는 동안 정신 모델이 사용되고 작업 기억 속에 존재한다고 주장했다.
책에서 나오는 실험과 같이 생각해본다면 프로그래밍에서 추상적인 코드 모델을 갖는 것이 도움이 된다는 것이다.
구체적인 모델이 더 효과적이다
실험의 뒷 이야기를 알려주며 상황에 따라서는 구체적인 모델을 만다는 것이 더 효과적이라는 것을 말해준다.
이를 생각해본다면 실제 객체지향 코딩에서 추상화 수준과 응집성 수준을 고려해서 설계, 모델링하는 것이 좋은 방법이라는 것을 알려주는 사례이다.
소스 코드에 대한 정신 모델을 작업 기억 공간에 생성하기
정신 모델이 정확하고 구체적이면 복잡한 시스템에 대해 생각할 때 도움이 된다는 것을 알았다.
코드가 단순하다면 문제가 되지 않지만 복잡하거나 코드 베이스에 대한 도메인 지식이 없을 때는 정확한 정신 모델을 만드는 것은 더 많은 노력이 필요하다.
그렇지만 이런 모험..? 노력을 하여 만들어 둔 모델은 훌룡한 자산이 되기 때문에 노력을 들이는 것이 좋다.
- 국지적 모델을 만든다.
상태표나 의존 그래프를 그리는 모델은 코드 베이스의 일부만 나타내는 국지적 모델이지만 더 큰 규모의 정신 모델을 만드는 것에 몇 가지 도움이 된다
작업 기억 공간의 인지 부화를 낮추는 데 도움이 되기 때문에 더 큰 규모의 정신 모델을 만드는 데 쉽게 집중할 수 있다.
이러한 작은 모델을 더 큰 규모의 정신 모델을 구성 요소로 사용될 수 있다.
- 코드에서 관련된 모든 객체와 객체간의 관계를 나열한다.
객체간의 상호작용을 이해하려면 도구를 사용하여 구성요소를 나열하고 그들 사이의 관계를 찾는다.
- 시슽메에 대한 질문을 만들고 이 질무의 답을 사용해서 모델을 개선한다.
LTM의 정신 모델
작업 기억 공간이 아닌 LTM에 저장된다고 주장한 데드레 켄트너, 앨버트 스티븐스가 있다.
프로그래밍에서는 트리를 탐색하는 것에 대한 추상적인 표현을 저장할 수 있다.
즉, 탐색을 시작할 루트가 있고, 주어진 노드의 모든 자식 노드를 확인하는 우선 탐색을 하거나 자식 노드 하나를 선택하고 더 이상 진행할 수 없을 때까지 계속 자식 노드를 확인하는 우선 탐색을 할 수 있다.
LTM에 소스 코드에 대한 정신 모델 생성
플래시 카드를 사용한 예제.
개념적 기계
정신 모델은 일반적인 부분이라 실생활이나 모든 영역에서 볼 수 있는데 프로그래밍 언어에서도 비슷한 개념이 있다.
이를 개념적 기계라고 한다.
정신 모델은 일반적으로 세상 모든 것의 모델이 될 수 있지만, 개념적 기계는 컴퓨터가 코드를 실행하는 방법에 대해 추론할 때 사용하는 모델이다.
더 정확한 표현으론 개념적 기계는 컴퓨터가 무엇을 하고 있는지에 대한 추상적인 표현으로 두뇌가 생각하는 과정에 이것을 사용한다.
프로그램 또는 프로그래밍 언어가 어떻게 동작하는지 이해하고자 할 때, 대부분의 경우 컴퓨터가 물리적으로 어떻게 동작하는지 세부 사항에 대한 관심은 거의 없다.
실제 물리적 기계와 그 기계가 추상적인 수준에서 수행하는 작업의 차이를 나타내기 위해 개념적 기계
라는 용어를 사용한다.
자바 파이썬은 참조라는 개념은 있어도 메모리 주소는 존재하지 않을 수 있다.
프로그래밍 언어에 대해 더 많이 배울수록, 정신 모델은 점점 더 개념적 기계에 가까워진다.
개념적 기계란?
개념적 기계는 기계
를 뜻하므로, 자유롭게 상호작용할 수 있다는 것이다.
개념적이라는 단어는 제시, 추정, 이론에 근거하거나 존재하지만 실제로는 존재하지 않는다라는 뜻이다.
컴퓨터가 어떻게 작동하는지에 대해 생각할 때, 모든 세부 사항에 관심이 있는 것은 아니다.
개념적 기계는 추상적 수준에서 컴퓨터 동작에 대해 추론하는 것이 필요한 경우에 사용된다.
개념적 기계의 예
이미 살펴본 바와 같이, 개념적 기계는 코드를 실제로 실행하는 기계의 동작을 설명하기 위해 만들어졌기 때문에 몇 가지 특성은 기계와 동일하다.
예를 들어 물리적 기계와 마찬가지로 개념적 기계는 상태(state)라는 개념을 가지고 있다.
개념적 기계의 층위
개념적 기계는 프로그래밍 언어 수준에서 작동하며 기저에 있는 시스템의 모든 세부 사항은 추상화한다.
- 프로그래밍 언어 -> 계산(대입)
- 컴퍼일러/인터프리터 -> 변수(박스)
- 가상 머신/바이트 코드 -> 상호 배타적 연산(분기 스위치)
- 운영체제 -> 스레드(인간 협업)
개념적 기계와 언어
기계의 작동 방시에 대해 추론할 때뿐만 아니라 코드에 대해 이야기할 때도 종종 기계를 사용한다.
예를 들어 값을 보유(hold)한다고 말한다.
물론 변수 안에 숫자가 저장된 물리적 객체는 존재하지 않는다.
프로그래밍에 관해 말할 때는 암시적으로 개념적 기계가 그 바탕에 깔려 있고 특정 정신 모델로 이어지는 경우가 많다.
우리는 일반적으로 포인터
라는 단어를 사용하고 포인터가 특정 값을 가리킨다
고 흔히 말하며, 함수가 스택에 새로운 값을 넣어 함수 호출차가 사용할 수 있게 될 때 함수가 값을 반환
한다고 말한다.
개념적 기계의 확장
프로그래밍 언어에는 전체 범위를 아우르는 하나의 개념적 기계만 있는 것이 아니라 서로 겹치는 여러 개의 개념적 기계들이 있다.
프로그래밍 언어에서 등장하는 개념을 이해하기 위해 우리가 사용하는 추상화 집합을 확장하는 예는 수없이 많다.
여러 개념적 기계는 서로 충돌하는 정신 모델을 만들 수 있다
개념적 기계가 만들어내는 정신 모델이 서로 충돌할 수 있다..!
변수를 상자로 보는 개념과 이름표로 보는 개념적 기계는 다르다.
이 두 개념적 모델를 일관성 있는 하나의 정신 모델로 합칠 수 없다.
이러한 개념적 기계는 각각의 장단점이 있으므로 잘 생각해서 사용해야 한다.
개념적 기계와 스키마타
개념적 기계는 단점이 있지만, 프로그래밍에 관해 생각할 때 일반적으로 효과적인 수단이다.
왜 스키마타가 중요한가?
스키마타는 LTM이 정보를 저장하는 방식이다.
그래프 같은 네트워크망으로 형성됨
예를 들어 상자에 대해 사람들이 가지고 있는 생각은 강환 연관성을 갖는 개념일 가능성이 매우 높다.
물건을 넣고, 빼고, 확인하는 행동은 사람들에게 익숙한 작업이라 변수로 생각하면 추가적인 인지 부하가 발생하지 않는다.
변수를 외발자전거에 비유한다면 전혀 도움이 되지 않는다.
이는 외발자전거의 동작에 대한 밀접한 정신 모델을 가지고 있지 않기 때문이다.
개념적 기계는 의미론인가?
의미론은 문법이라는 프로그램의 외관보다는 의미를 연구하는 컴퓨터 과학의 하위 분야이다.
이러한 의미론은 수학 방정식, 정밀도 등의 컴퓨터 동작을 공식화하는 것을 목표로 하기에 개념적 기계와 다르다.
요약
- 문제를 표현하는 방법은 문제에 대한 생각에 큰 영향을 미칠 수 있다.
- 정신 모델은 우리가 문제를 생각할 때 형성하는 정신적 표상이다.
- 개념적 기계는 실제 컴퓨터가 어떻게 기능하는지를 추상적으로 표현한 것으로, 프로그래밍 개념을 설명하고 프로그래밍에 대한 추론을 할 때 사용한다.
- 개념적 기계는 기존의 스키마타를 프로그래밍에 적용할 수 있기 때문에 프로그래밍에 도움이 된다.
- 여러 다른 개념적 기계는 때로는 서로 훌룡하게 보완하지만 상충되는 정신 모델을 만들 수 있다.
7장 생각의 버그
- 핵심 주제
- 한 가지 프로그래밍 언어를 알고 있으면 새로운 언어를 배울 때 어떻게 도움이 되는지
- 새로운 프로그래밍 언어를 배울 때 발생하는 문제의 방지
- 두뇌가 어떻게 잘못된 개념을 갖게 되고 이것이 어떻게 버그를 초래하는지에 대한 이해
- 생각할 때 잘못된 개념을 피하고 버그를 방지하는 방법
앞서 다룬 기술들이 아무리 유용해도 우리는 종종 실수를 한다.
이번 장에서 다룰 내용은 버그다.
파일을 닫지 않거나, 이름을 잘못 기입하거나 엉성한 결과를 내는 등의 실수를 말한다.
하지만 더 많은 경우, 버그는 생각에 착오가 있을 때 발생한다.
왜 두 번째 프로그래밍 언어가 첫 번째보다 쉬울까?
LTM에 저장된 키워드 및 정신 모델이 코드를 이해하는 데 도움이 된다는 것을 알았다.
때때로 이미 배운 지식은 다른 지식을 배울 때도 유용하게 작용한다.
이를 전이(transfer)라고 한다.
전이는 이미 알고 있는 정보가 새로운 것을 하는 데 도움이 될 때 일어난다.
자바를 이미 알고 있다면 파이썬을 쉽게 배울 수 있듯이.
LTM에 저장된 프로그래밍 지식은 새로운 프로그래밍 개념을 배우는 데 두 가지 방식으로 도움이 될 수 있다.
첫째, 프로그래밍에 대해 이미 많이 알고 있다면 그것에 대해 더 많이 학습하는 것이 쉬워진다.
LTM에 저장된 정보를 사용해서 새로운 내용을 쉽게 배우는 이 과정을 학습 도중 전이라고 한다.
새로운 정보를 습득하면, 먼저 감각 기억 공간과 STM에 의해 처리된다.
그다음에 작업 기억 공간으로 전송되며, 작업 기억 공간은 이에 대해 생각하는 동시에 LTM에서 관련 정보를 검색한다.
관련 정보가 발견되면 작업 기억 공간에 전송되어 새로운 정보에 대해 사고하는 것을 돕는다.
- Q. 최근에 배운 새로운 프로그래밍 개념이나 라이브러리를 생각해보라. 이미 알고 있는 개념 중에 그 새로운 개념을 배우는 데 도움이 된 것은 무엇인가?
- A. 객체지향의 개념이 적용되는 모든 프로그래밍 영역..
두번 째는 학습 전이다.
학습 전이는 완전히 낯선 상황에 이미 알고 있는 내용을 적용할 때 일어난다.
인지과학에서 전이라는 용어를 사용할 때는, 거의 대부분의 경우 학습 전이를 의미한다.
학습 전이는 일상 생활에서도 끊임없이 일어난다.
노트북을 구매하고 키보드를 사용하는 방법을 알듯이 무의식적인 부분이 있고 이와 다르게 학습 전달이 의식적으로 이루지는 경우도 있다.
학습 전이와 학습 도중 전이는 유사한데 두 가지 모두 뇌가 작용할 만한 관련 전략을 찾기 위해 LTM을 검색하기 때문이다.
기존 프로그래밍 지식을 활용할 가능성을 높이는 방법
프로그래머로써 지식 전이가 가능함에도 그렇게 해보지 못한 상황이 있을 것이다.
예를 들어 어떤 라이브러리의 특정 기능이 어떻게 동작하는지 이해가 되지 않다가 나중에 알고 보니 원래 잘 알고 있던 라이브러리였다는 식이다.
안타깝지만 유용한 모든 지식이 자동으로 새로운 상황으로 전이되는 것은 아니다.
한 작업에서 다른 작업으로 전이할 수 있는 학습의 양은 달라질 수 있고 많은 요인에 의해 영향을 받는다.
숙달
LTM에 이미 저장되어 있는 지식과 관련한 작업을 얼마나 잘 숙달했는지에 대핸 것이다.
작업을 더 잘 알수록 다른 도메인에 더 잘 적용할 수 있다.
유사성
두 작업 간의 공통점이다.
이미 구현해본 알고리즘의 경우 다른 언어로 작성하더라도 더 구현이 쉽다.
배경
환경이 얼마나 비슷한지에 대한 것이다.
작업 간의 유사성뿐만 아니라 작업을 실행하는 환경 역시 중요하다.
다른 언어일지라도 같은 IDE를 사용하는 등..
중요 특성
어떤 지식이 효과적인지에 대해 분명하게 알고 있는지에 대한 것이다.
따라서 배우기전에 공통점을 적극적으로 찾고 생각해보는 것이 중요하다.
스스로 찾아야 한다. 질문하고 답변하고 ,회고
연관
두 작업이 비슷하다고 얼마나 강하게 느끼는지에 대한 것이다.
유사성과 비슷하게 생각할 수 있지만 의식적인 부분이 강하다.
같은 강의실에서 배운 자바와 C#같은 느낌
감정
작업에 대해 어떻게 느끼는지에 대한 것이다.
감정 또한 학습 전이에 영향을 미칠 수 있다.
전이의 다른 형태
전이에 대해 몇 가지 다른 관점이 있다.
프로그래머들은 때때로 언어 간의 문법 차이는 사소하다고 생각하며 한 언어를 알면 두 번째 언어는 쉽게 배울 수 있고, 세 번째 언어는 노력을 하지 않아도 배울 수 있다고 가정한다.
물론 한 언어를 배우면 새로운 언어를 쉽게 배우는 것은 사실이지만, 항상 도움이 되는 것은 아니다.
다른 형태의 전이를 이해하면 새로운 언어나 프레임워크를 더 효과적으로 배울 수 있다..!
고도 전이와 저도 전이
자동화된 기술의 전이와 의식적으로 습득한 기술의 전이는 차이가 있다.
자동화된 기술을 이전하는 것을 저도 전이라고 한다.
프로그래밍 도중 복사 붙여넣기와 같이 아무 생각 없이 사용할 때 일어날 수 있다.
이에 반해 보다 복잡한 작업이 전이되는 것을 고도 전이라고 한다.
고도 전이가 일어나는 경우에는 그러한 상황이 발생하고 있음을 인지하는 경우가 많다.
많은 프로그래밍 언어에서 변수를 사용하려면 선언을 해야 하기 때문에 새로운 프로그래밍 언어를 배울 때 변수 선언을 먼저 해야 한다고 가정할 수 있다.
근거리 전이와 원거리 전이
앞에서 두 영역이 비슷하면 전이의 양에 영향을 미친다는 것을 배웠다.
영역 간의 거리에 따라 전이의 형태를 나눌 수 있다.
미적분학과 대수학, 자바와 C#과 같이 서로 가까운 영역 사이에서 지식이 전이될 때 근거리 전이가 일어난다.
라틴어와 논리학, 자바와 프롤로그와 같이 서로 먼 영역 간에 일어나는 전이를 원거리 전이라고 한다.
- Q. 전이를 경험했던 상황을 생각해보라. 어떤 형태의 전이가 일어났는가?
- A.
상황 | 고도 전이 | 저도 전이 | 근거리 전이 | 원거리 전이 |
---|---|---|---|---|
책을 읽고 정리하는 상황 | 지금 md파일로 표를 그리는 과정에서 표의 형태를 생각 | IDE, 코파일럿이 자동으로 만들어줘서 스페이스로 자동 완성 | 마크다운이기에 깃허브 이슈 | 뇌 구조를 상상 LTM이나 STM이 일어나는 과정.. |
이미 알고 있다는 것은 저주인가 축복인가?
고도/저도 그리고 근거리/원거리 전이 외에 또 다른 범주의 두 가지 전이가 있다.
우리가 지금까지 살펴본 바와 같이, 무언가를 알고 있어 새로운 것을 배우거나 새로운 작업을 할 때 도움이 되는 전이를 긍정적 전이라고 부른다.
긍정적인 전이가 일어나면, 새로운 정신 모델을 처음부터 만들 필요가 없다.
대신, 우리 두뇌는 LTM에 저장된 다른 영역에 대한 정신 모델을 바탕으로 새로운 상황에 대한 정신 모델을 형성한다.
예를 들어 자바를 알고 있다면 루프에 관한 정신 모델을 이미 가지고 있고, 루프에는 카운터 변수, 본문, 중지 조건이 있다는 것을 알고 있다.
새로 접하는 어떠한 프로그래밍 언어라도 루프는 이러한 측면이 있기 때문에 LTM에서 무엇을 찾아야 하는지 아는 것이다.
하지만 이런 전이가 항상 긍정적인 측면만 있는 것은 아니다.
기존 지식이 새로운 지식을 배우는 데 방해가 될 때 이를 부정적 전이라고 한다.
C#을 먼저 배운 사람이 자바에서 예외 처리가 C#과 다르다는 것을 인식하지 못할 수 있다.
하나의 예로 객체지향을 배워 익숙해진 사람은 F#과 같은 함수형 언어를 배울 때 어려움을 겪는다.
함수가 객체지향과 함수형 언어에 다 있지만 다른 방식으로 동작하기 때문이다.
- Q. 어떤 프로그래밍 언어의 개념에 대해 잘못된 가정을 했던 적이 있다면 그 상황을 생각해보라. 한 언어에서 다른 언어로 부정적인 전이가 원인이였을까?
- A. C#을 주언어로 쓰는 나는 학교 데이터베이스 실습 중 자바를 사용한 적이 있는데 namespace와 package의 차이를 모르고 있었다..
전이의 어려움
상황이 서로 비슷해야 전이 가능성이 높아진다.
지식이 한 영역에서 원래 영역과는 유사하지 않은 다른 영역으로 전이되는 원거리 전이는 우발적으로 일어날 가능성이 높다.
안타깝게도, 연구에 의하면 전이는 일어나기 매우 어렵고 대부분의 사람에게 전이가 자동으로 일어나지는 않는다.
몇 가지 사례로 그 예를 볼 수 있는데 아이들이 성공적으로 프로그래밍 기술을 습득하더라도 그러한 기술은 다른 인지 영역으로 전이되지 않는 것으로 나타났다.
여기서 중요한 점은 하나의 프로그래밍 언어를 숙달했다는 사실이 새로운 언어를 배우는 데 항상 도움이 되는 것은 아니라는 것이다.
즉, 공통점과 차이점에 의식적으로 주의를 기울이면 새로운 언어를 배우는 일이 쉬워질 것이다.
- Q. 배우고 있는 프로그래밍 언어 또는 배우고 싶은 프로그래밍 언어를 생각해보라. 그 언어를 이미 알고 있는 언어와 비교해보라. 무엇이 어떻게 다른가?
- A. C++
유사점 | 차이점 | 비고 | |
---|---|---|---|
문법 | 비슷한 컨벤션 | 좀 더 낮은 레벨? 기계적, 가독성 떨어짐 | |
타입 | 타입은 거의 같음 | ||
프로그래밍 개념 | 거의 같음(객체지향) | ||
런타임 | 동적언어 차이점..? | 더 빠름 | |
IDE | 거의 vscode 사용 | ||
테스트 환경/방식 |
오개념: 생각의 버그
앞의 섹션에서는 한 상황에서 다른 상황으로 지식 전이가 어떻게 일어나는지에 중점을 두고 살펴봤다.
이번 섹션에서는 부정적 전이의 결과에 대해 알아본다.
자신이 작업한 프로그램에 버그가 있던 상황을 살펴보면 인스턴스를 초기화하지 않았거나, 잘못된 함수를 호출했거나, 인덱스 오류 등 버그를 만든 상황을 생각해보자
수정해야할 코드를 실수로 잊어버리고 배포하거나, 메서드를 잘못 선택했거나, 경계값을 잘못 계산하면 버그가 발생할 수 있다.
그러나 버그는 더 근본적인 이유로 발생하기도 한다.
작업 중인 코드에 대해 잘못된 가정을 할 때 일어날 수 있다.
코드의 다른 곳에서 인스턴스가 초기화될 거라고 생각하거나, 선택한 메서드가 맞는다고 확신하거나 해당 범위를 벗어나는 요소에 액세스하는 것을 자료구조가 알아서 잘 막아줄 거라고 가정할 수 있다.
코드가 작동한다고 확심함에도 불구하고 여전히 오류가 발생한다면 코드에 대한 오개념이 문제일 가능성이 있다.
일상의 대화에서 오개념이라는 단어는 종종 실수나 혼동의 용어로 사용되지만, 형식적인 정의는 약간 다르다.
어떤 신념이 오개념이 되려면, 다음과 같아야 한다.
- 사실과 다르다.
- 서로 다른 상황에서 일관되게 유지된다.
- 확신에 사로잡혀 있다.
흔한 오개념의 한 예로 고추 씨앗이 가장 매운 부분이라고 생각하는 것(실제로는 전혀 맵지 않다.)
- 사실과 다르다.
- 만약 사람들이 한 종류의 고추의 씨앗이 맵다고 믿는다면, 그들은 모든 종류의 고추의 씨앗이 맵다고 믿을 것이다.
- 그들은 그것이 사실이라고 굳게 믿고, 가령 고추의 씨를 제거하고 요리한다.
프로그래밍에서도 이런 오개념을 흔하게 목격할 수 있다.
temperature와 같은 변수는 변경할 수 없으며 하나의 값만 가질 수 있다고 가정한다.
수학적인 지식으로부터 전이되어 증명이나 문제 범위 내에서 변수들이 변하지 않다고 생각하기에 그럴 수 있다.
또 다른 원인으로는 프로그래밍 자체에 있다.
개념 변화를 통한 오개념 디버깅
오개념은 강한 확신 속에 있는 잘못된 사고방식이다.
오개념은 너무 강한 확신 때문에 교정하기가 어려울 수 있다.(교조주의/편향적인 사고방식)
이미 알고 있는 프로그래밍 언어 때문에 생긴 오개념을 현재 학습 중인 새로운 언어에 맞는 정신 모델로 대체하는 과정을 개념 변화라고 한다.
이 패러다임에서 기존의 개념은 근본적으로 바뀌거나 대체되거나 새로운 지식에 동화된다.
기존의 스키마에 새로운 지식을 추가하는 대신 지식 자체를 바꾼다는 것이 개념 변화가 다른 유형의 학습과 구별되는 점이다.
이미 학습한 지식을 LTM에서 변경해야 하기 때문에 개념 변화 학습은 일반적인 학습보다 더 어렵다.
오개념 제압하기
눈사람에게 스웨터를 입히면 더 빨리 녹을까?
이 경우에는 두뇌가 기존의 개념, 즉 스웨터는 몸을 따듯하게 해준다는 개념을 즉각적으로 활성화시켰을 가능성이 높다.
눈사람의 상황에 대해 인간에게 들어맞는 개념이 부정적으로 전이된 것이지, 우리가 똑똑하지 못해서 그런 것이 아니다.
사람들은 무언가를 새로 배우게 되면, 오래되고 잘못된 관념들이 기억에서 완전히 삭제되고 더 좋은 올바른 관념으로 대체된다고 오랫동안 가정해왔다.
하지만 잘못된 사고방식의 오래된 기억은 여전히 남아 있고, 그 기억으로 가는 길은 우리가 원하지 않더라도 촉발될 수 있다.
두뇌가 어떤 저장된 개념을 사용할지 결정하는 기제는 정확하게 알려져 있지 않지만 억제가 일정 역할을 한다는 것은 알려져 있다.
우리는 억제를 자의식, 머뭇거림, 수줍음과 연관시킨다.
그러나 최근 연구 결과에 따르면 억제의 통제 메커니즘이 활성화되면 잘못된 개념이 올바른 개념과의 경쟁에서 질 수 있다는 것이 밝혀졌다.
프로그래밍 언어에 대한 오개념
프로그래밍 분야에서 특히 초보 프로그래머들이 가지는 오개념에 대해, 광범위하게 연구가 이루어졌다.
알토 대학교의 수석 강사인 유하 소르바는 몇가지 오개념을 나열했다.
- 오개념 15: 원시 데이터 타입 변수에 대한 할당은 수식 또는 아직 연산 실행이 되지 않은 표현식을 저장한다.
- 오개념 33: 조건이 거짓으로 변경되는 즉시 while 루프가 종료된다.
- 오개념 46: 매개변수 전달에는 호출과 정의에 서로 다른 변수 이름이 필요하다.
새로운 프로그래밍 언어를 배울 때 오개념 방지하기
오개념에 대해 우리가 할 수 있는 것이 많지 않다.
새로운 프로그래밍 언어나 시스템을 배울 때, 부정적 전이를 피할 수는 없다.
하지만 몇 가지 도움이 될 방법이 있다.
첫째, 자신이 옳다고 확신하더라도 여전히 틀릴 수도 있다는 것을 아는 것이 중요하다. (열린 마음)
둘째, 흔하게 발생하는 오개념에 대해 의도적으로 연구함으로써 그런 오개념에 빠지는 것을 방지할 수 있다.
마지막으로 같은 순서로 프로그래밍 언어를 학습한 다른 프로그래머에게 조언을 구하는 것이다.
새로운 코드베이스에서의 오개념 진단
코드베이스에서도 오개념을 가질 수 있다.
프로그래밍 언어, 프레임 워크, 라이브러리나 코드의 도메인, 변수의 의미, 코드 작성자의 의도에 대해 이미 가지고 있는 지식을 바탕으로 코드에 대한 가정을 할 때마다 오개념의 위험이 도사리고 있다.
오개념 탐지 방법으로 짝 프로그래밍과 같은 방법을 추천하며 전문 프로그래머 일 수록 더욱 조심해야 한다.
요약
- LTM에 이미 저장된 지식은 새로운 상황으로 전이될 수 있다.
- 때로는 기존의 지식이 학습 속도를 높이거나 새로운 작업을 더 잘 수행하는 데 방해가 된다.
- LTM에서 관련 정보를 적극적으로 검색하면 긍정적인 전이가 일어나 새로운 것을 보다 효과적으로 배울 수 있다.
- 우리는 오개념을 가질 수 있다. 자신이 옳다고 확신하지만 실제로는 틀릴 때 오개념을 갖게 된다.
- 오개념은 단순히 자신이 틀렸다는 것을 깨닫거나 듣는 것만으로는 해결되지 않는다. 오개념을 바로 잡기 위해선 새로운 정신 모델이 필요하다.
- 올바른 모델을 배웠더라도 오개념을 다시 사용할 위험이 있다.
- 오개념을 방지하는 데 도움이 되기 위해 코드베이스 내의 테스트 및 문서화를 사용하자
8장 명명을 잘하는 방법
- 핵심 주제
- 좋은 이름 짓기에 대한 여러 가지 관점의 비교
- 이름과 인지 과정 간의 관계 파악
- 다양한 명명법의 효과
- 잘못된 이름이 버그 및 오류에 미치는 영향
- 변수 이름을 구조화하여 이해도를 극대화하는 방법
이 장에서는 변수, 클래스 및 메서드와 같은 코드 내 여러 구성 요소들의 이름을 가장 잘 명명하는 방법을 연구해보자고 한다.
두뇌가 코드를 어떻게 처리하는지 어느 정도 알고 있기 때문에 코드를 파악하는 데 명명법이 왜 중요한지 더 깊이 이해할 수 있다.
좋은 이름을 사용하면 LTM을 활성화하여 코드 도메인에 대해 이미 알고 있는 관련 정보를 찾을 수 있다.
반면, 나쁜 이름은 코드에 대한 잘못된 추측을 하게 하고 오개념을 유발할 수 있다.
코딩에서 어렵다고 평가 받는 이름짓기.. 책에서도 매우 중요하고 어렵다고 강조한다.
작업 기억의 용량을 생각하면 쉬운 이름을 선택하는 것이 인지적 관점에서 타당하다.
이 책은 인지적인 관점에서 바라보고, 클린 코드나 좋은 코드 나쁜 코드는 이런 시선과는 다른 것 같다.
이름이 중요한 이유
좋은 변수 이름을 고르는 것은 어렵다.
컴퓨터 과학에는 난제가 두 가지 있는데 하나는 캐시 무효화이고 다른 하나는 변수 명명이다. (필 칼튼)
클래스나 자료구조가 수행하는 모든 작업을 모호하지 않은 하나의 단어로 표현하는 것은 쉽지 않은 작업이다.
생각
객체지향 관점에서 클래스를 쪼개는 행위, 즉 더 낮은 레벨로 분할하는 행위가 이름을 지을 때 도움이 될 것 같다.
이름을 짓는 일은 어렵지만, 코드에서 우리가 추론하는 객체에 맞는 이름을 고르는 것은 중요하다.
뇌의 명명 과정과 인지 과정 사이의 연관성에 대해 살펴보기 전에, 왜 명명 과정이 중요한지 알아본다.
명명이 중요한 이유
코드베이스에서 식별자의 이름은 프로그래머가 명명한다.
식별자에는 타입(클래스, 인터페이스, 구조체, 대리자, 열거형), 변수, 메서드, 함수, 모듈, 라이브러리, 네임스페이스가 포함되고 프로그래머는 이들의 이름을 짓는다.
식별자의 이름이 중요한 이유는 크게 네 가지다.
이름은 코드베이스의 상당 부분을 차지한다
변수 이름이 중요한 첫 번째 이유는 대부분의 코드베이스에서 읽을 내용의 상당 부분이 이름이기 때문이다.
예를 들어 200만 줄의 코드로 구성된 이클립스 소스 코드에서 토큰의 33%, 문자의 72%가 식별자에 해당된다.
코드 리뷰 시 이름의 역할
이름은 문서화의 가장 쉬운 형태
이름은 코드베이스 내에서 바로 사용할 수 있기 때문에 일종의 중요한 문서로 기능한다.
이름이 표식 역할을 해줄 수 있음
변수 이름은 주석문 외에도 코드를 이해하는 데 도움이되는 표식이다.
이외에도 이름의 중요성은 다른 책에서도 언급되기에 Clean Code
참고.
명명에 대한 다양한 관점
- Q.
- 좋은 식별자 이름을 어떻게 정의하는가?
- 좋은 이름의 예를 생각할 수 있는가?
- 나쁜 이름을 어떻게 정의할 수 있는가?
- 단순히 좋은 이름과 반대되는 것일까?
- 아니면 지금까지 본 적이 있는 나쁜 이름의 특징을 생각할 수 있는가?
- 자기 자신이 작성한 나쁜 이름의 예를 알고 있는가?
- A.
- 좋은 식별자 이름은 중복이 없고 직관적인 식별자 이름이라고 생각한다.
- GetMyName()과 같은 메서드는 좋은 식별자라고 생각한다..
- GetString()과 같이 지역적으로 다룬 식별자
- 아니다
- a,b,c 등과 같은?
- 앞서 다룬 해당 클래스내에서만 사용할 변수를 지역적으로 다룬 식별자
좋은 이름에 대한 규약목록이 있다.
- 식별자는 대문자를 올바르게 사용해야한다.
- 식별자는 연속된 여러 개의 밑줄을 가져서는 안된다.
- 식별자는 단어로 만들어야 하고 역어는 원래의 명칭보다 더 자주 사용될 경우에만 사용해야 한다.
- 식별자에 사용되는 단어는 두 갸에서 네 개 사이여야 한다.
- 식별자의 길이는 c, d, e, g, i, in, inOut, j, k, m, n, o, out, t, x, y, z를 제외하고 8글자보다 작으면 안 된다.
- 분명한 이유가 없다면, 열거형은 알파벳 순서로 선언되어야 한다.
- 식별자는 밑줄로 시작하거나 끝나서는 안된다.
- 헝가리은 표기법 등으로 식별자의 이름에 타입 정보를 나타내면 안 된다.
- 긴 식별자 이름은 가능한 한 피해야 한다.
- 식별자는 대문자와 소문자를 표준적이지 않은 방법으로 섞어서 사용해서는 안된다.
- 식별자는 숫자만을 나타내는 단어나 수를 사용하면 안 된다.
이름은 코드베이스 내에서 일관성이 있어야 한다
좋은 이름에 대한 또 다른 관점은 일관성이다.
코드베이스 전반에 걸쳐 유사한 객체에 동일한 단어를 사용하면 뇌가 LTM에 저장된 관련 정보를 더 쉽게 찾을 수 있다.
초기 몀영 관행은 지속적인 영향을 미친다
연구 결과에 따르면 “프로그램 개발 초기에 만들어진 식별자의 품질이 계속 유지된다.”라고 한다.
즉, 새 프로젝트의 경우 초기 단계의 이름을 만드는 방식이 그 이후로도 계속 사용될 가능성이 높기 때문에 좋은 이름을 선택하는 데 특히 주의를 기울여야 한다.
시간 경과에 따른 명명 방식에 대한 조사 결과
최신 코드는 명명 지침을 더 잘 따른다.
그러나 동일한 코드베이스 내에서 명명 관행은 일정하게 유지된다.
명명 관행 면에서 작은 코드 베이스와 큰 코드베이스 사이에 차이는 없다.
명명의 인지적 측면
형식이 있는 이름은 STM을 돕는다
변수 이름의 형식을 지정하는 방법에 대한 명확한 규칙을 가지고 있으면 STM이 코드에 있는 이름을 이해하는 데 도움이 될 수 있다.
명확한 이름이 LTM에 도움이 된다
우리가 선택하는 단어들은 특히 인지적인 관점에서 중요하다.
앞에서 두뇌가 코드를 처리할 때 작업 기억 공간은 두 가지 유형의 정보를 처리한다는 것을 살펴보았는데, 먼저 변수 이름은 감각 기억에 의해 처리되고 STM으로 전송된다.
STM은 크기가 제한되어 있기 때문에 변수 이름을 단어별로 구분하려고 한다.
이름이 체계적일수록 변수명의 각 부분을 식별하기 쉽다.
변수 이름은 이해에 도움이 되는 다양한 유형의 정보를 포함할 수 있다
- 코드의 도메인에 대해 생각할 때 이름이 도움이 된다.
- customer같은 도메인 단어에 대해 LTM에는 모든 종류의 연관 관계가 저장되어 있다. ex) 고객은 제품을 주문한다, 주소와 이름이 필요하다 등..
- 프로그래밍에 대해 생각할 때도 이름이 도움이 된다.
- 트리와 같은 프로그래밍 개념도 LTM에서 정보를 가져오는데 일조한다. 트리는 루트 노드가 있고, 순회 가능하고 등등
- 경우에 따라 변수에 LTM이 이미 알고 있는 규약에 대한 정보가 포함될 수 있다.
- j라는 변수는 루프에서 카운터 변수로 종종 사용된다.
- Q. 익숙하지 않은 소스 코드를 하나 선택하라. 코드를 살펴보고 식별자 이름을 나열하라 각각 어떻게 인지 처리를 지원할지 생각해보라
이름의 품질 평가 시기
코딩과 관련된 인지 과정 때문에 명명하는 것은 어렵다.
문제 해결에 몰두해 있을때는 높은 인지 부하를 겪기 십상이기 때문이다.
부하가 너무 높으면 좋은 변수 이름을 생각할 여유가 없을 수 있다.
따라서 코딩 이외의 시간에 이름의 품질을 숙고하는 것이 바람직하다..
어떤 종류의 이름이 더 이해하기 쉬운가?
축약할 것인가, 하지 않을 것인가?
변수의 이름을 기억하기 어렵게 만드는 것은 길이 자체가 아니라 이름에 포함된 음절의 수였다.
이것은 인지적 관점에서 이해할만 하며 이름이 길면 STM에서 더 많은 청크를 사용할 수 있고, 각 음절 의미의 단어를 사용해야 하는 반면, 기억을 잘하기 위해서라면 간결한 약자를 사용해야 한다.
좋은 변수 이름을 명명하기 위해서는 이 둘 사이의 주의 깊은 균형이 필요하다.
단일 문자가 변수로 흔히 사용된다
다양한 언어에서 사용된 단일 문자 변수를 그래프로 보여주는데.. 역시 i가 제일 많다. (펄 제외)
i는 반복문 x,y는 좌표로 이해가 되지만 b, f, s, t는 프로그래머가 공통으로 연관 짓는 의미가 있을까?
이러한 오개념을 방지하기 위해 변수 이름을 축약하지 않는 것이 좋다..(루프문이나 명확한 것은 제외)
스네이크 케이스냐, 캐멀 케이스냐?
C계열의 언어는 캐멀 케이스를 사용한다. 모두 첫 글자는 소문자시작해 그 다음 단어의 첫 글자는 대문자로 시작한다.
반면 파이썬은 식별자 이름의 단어를 밑줄로 구분하는 스네이크 케이스를 사용한다.
엄청난 차이가 있지는 않지만 캐멀케이스의 경우가 연구 결과 상 좀 더 유리한 부분이 있다고 한다.
이름이 버그에 미치는 영향
나쁜 이름을 가진 코드에 버그가 더 많다
연구 결과에 따르면 명명 문제와 코드 품질 사이에 통계적으로 유의미한 연관성을 발견했다.
물론 버그 위치와 나쁜 이름의 위치 간의 상관관계가 반드시 그 둘 사이의 인과관계를 의미하는지는 않는다.
버그와 좋지 않은 이름 모두 초보 프로그래머 혹은 실력 없는 개발자가 작성한 코드의 결과일 수도 있다.
이 외에도 다양한 인지적 문제일 수 있기도 하다.
따라서 잘못된 이름 문제를 해결하는 것이 반드시 버그를 고치거나 예방하지는 않지만, 코드베이스를 검사하여 잘못된 이름이 발생하는 위치를 찾아내는 일은 코드를 개선하고 버그 발생 가능성이 있는 위치를 찾는 데 도움이 될 수 있다.
이름을 개선하면 간접적으로 버그가 줄어들거나, 더 나은 이름을 사용한 코드는 이해하기 쉽기 때문에 최소한 수정 시간이 단축될 수 있다.
더 나은 이름을 선택하는 법
이름 툴
많이 선택받은 이름을 나열해주는데 사실, 깃허브 코파일럿이나 네이밍 구름같은 툴을 사용하면 된다.
더 나은 변수명에 대한 페이텔슨 3단계 모델
- 이름에 포함할 개념을 선택한다.
- 각 개념을 나타낼 단어를 선택한다.
- 이 단어들을 사용하여 이름을 구성한다.
요약
- 캐멀 케이스 같은 문법 규칙부터 코드베이스 내의 일관성까지, 좋은 이름에 대한 다양한 관점이 있다.
- 다른 차이가 없다면 스네이크 케이스로 작성된 캐멀 케이스 변수가 기억하기 쉽다. 하지만 사람들은 스네이크 케이스를 더 빨리 인식한다.
- 잘못된 이름이 있는 코드에서 버그가 발생할 가능성이 높다. 다만 이 둘 사이에 반드시 인과관계가 있는 것은 아니다.
- 다양한 형식의 변수명을 만드는 데 사용할 수 있는 이름 틀이 있으므로, 틀의 수를 줄이면 코드를 이해하는 데 도움이 된다.
- 페이텔슨 3단계 모델을 적용하면 고품질의 이름을 만들 수 있다.
9장 나쁜 코드와 인지 부하를 방지하는 두 가지 프레임 워크
- 핵심 주제
- 코드 스멜과 인지 부하의 연관성
- 나쁜 이름과 인지 부하의 연관성
앞서 다룬 STM, LTM, 작업 기억 공간이 코드를 읽을 때 어떻게 작용하는지, 왜 어려운지에 대해 다뤘다.
즉, 코드를 읽을 때 너무 많은 인지 부하가 발생하기 때문이다.
인지 부하는 작업 기억 공간이 꽉 차서 뇌가 더 이상 제대로 처리하지 못할 때 발생한다.
이번 장에서는 인지적 관점에서 코드를 작성하는 방법에 대해 알아본다.
어떤 종류의 코드가 인지 부하를 많이 발생시키는지 살펴보고, 인지적으로 처리하기 쉽게 하기 위해 코드를 개선할 방법을 논의할 것이다.
특히 코드가 인지 부하를 초래할 수 있는 두 가지 이유에 대해 자세히 살펴보려고 한다.
코드 스멜이 인지 부하를 초래하는 이유
코드가 혼란을 초래하는 이유를 살펴보기 위해 사용할 첫 번째 프레임워크는 코드 스멜이다.
코드 스멜은 읽고있는 리팩터링, 읽은 클린 코드등에 등장하는 개념이다.
코드 스멜에 대한 간략한 소개
파울러는 다양한 코드 스멜과 함께 이것을 해소하기 위한 전략을 함께 목록화하여 설명하였고, 이를 리팩터링이라고 불렀다.
코드 스멜의 예는 매우 긴 메서드, 복잡한 스위치문 등등 다양하게 있다.
메서드 수준 코드 스멜
클래스 수준 코드 스멜
코드베이스 수준 코드 스멜
코드 스멜의 영향
코드 스멜이 있다고 해서 코드에 반드시 오류가 있는 것은 아니다.
다만 코드 스멜을 가지고 있는 코드는 오류가 있을 가능성이 높은 것으로 알려져 있다.
코드 스멜이 인지 과정에 악영향을 미치는 방식
코드 작성 시 코드 스멜이 들어가지 않게 하려면 코드 스멜이 코드 이해에 미치는 악영향을 이해해야 한다.
따라서 코드 스멜과 뇌의 인지 과정, 특히 인지 부하와의 연관성을 탐구할 것이다.
긴 매개변수 목록, 복잡한 스위치 문: 작업 기억 공간의 용량 초과
앞서 작업 기억 공간에 대해 살펴본 내용에 의하면 긴 매개변수 목록과 복잡한 스위치문이 왜 읽기 어려운지 이해할 수 있다.
두 가지 코드 스멜 모두 작업 기억 공간의 과부화와 관련이 있다.
작업 기억 공간의 용량이 6개 정도로 작기 때문에 6개를 넘는 매개변수 리스트는 사람들이 기억하기에 무리가 있다고 설명했다.
이런 코드를 읽는 동안 모든 매개변수를 작업 기억 공간에 저장할 수 없다.
따라서 메서드에 매개변수가 많을수록 이해하기는 더 어려울 것이다.
신의 클래스, 긴 메서드: 효율적인 청깅이 불가능
코드로 작업할 때 우리는 끊임없이 추상화를 생성한다.
모든 기능을 하나의 main() 함수에 두기보다는, 의미 있는 이름을 갖는 별도의 작은 기능으로 나누는 것을 선호한다.
서로 관련된 속성과 함수들은 클래스 안에 모아 놓는다. 개별 함수, 클래스, 메서드로 기능을 분할할 때의 이점은 이름이 문서 역할을 한다는 것이다.
코드 클론: 청킹이 잘못됨
코드 클론 또는 중복 코드 스멜은 별 차이 없는 코드가 계속해서 코드베이스에 추가될 때 발생한다.
작업 기억 공간에 대해 우리가 알고 있는 것에 기초해 생각해보면 중복 코드가 왜 코드 스멜로 간주되는지 그 이유를 알 수 있다.
즉 명확한 개념으로 떨어지도록 세분화를 해야함
나쁜 이름이 인지 부하에 미치는 영향
코드 스멜은 구조적 안티패턴 문자기 있는 코드이다.
아것은 코드가 잘 작성되었으나 파악하기 어려운 구조로 만들어졌다는 것을 의미한다.
즉, 동작은 문제 없지만 리딩에서 큰 문제가 발생한다.
그러나 코드에는 개념적
안티패턴이 있을 수 있다.
코드가 짧은 메서드와 깔끔한 클래스로 올바르게 구성되어 있지만 혼동되는 이름을 갖는 경우다.
이러한 코드 문제는 두 번째 프레임워크인 언어적 안티패턴으로 설명할 수 있다.
두 프레임워크는 코드의 서로 다른 측면을 다루기 때문에 서로 잘 보완한다.
언어적 안티패턴
언어적 안티패턴을 코드의 언어적 요소와 그 역할 사이의 불일치로 묘사한다.
코드의 언어적 요소란 메서드 입출력, 설명 문서, 속성 이름, 데이터 타입, 주석문 등을 포함하는 코드의 자연어 부분으로 정의한다.
안티패턴은 언어적 요소가 수행하는 역할과 일치하지 않을 때 항상 발생한다.
- 아나우도바의 6가지 언어적 안티패턴
- 이름이 나타내는 것보다 더 많은 일을 하는 메서드
- 이름이 나타내는 것보다 더 적은 일을 하는 메서드
- 이름과 정반대의 일을 하는 메서드
- 개체에 포함된 것보다 더 많은 것을 가지고 있는 것처럼 보이는 식별자 이름
- 개체에 포함된 것을 누락하는 식별자 이름
- 개체에 포함된 것과 반대되는 식별자 이름
인지 부하 측정
인지 부하에 대한 파스 척도
인지 부하를 측정하기 위해 파스 척도를 자주 사용한다.
파스 척도는 한 가지 질문으로 구성된 상대적으로 간단한 설문을 사용한다.
이를 개선하여 생소한 코드를 파악하는 작업을 할 때 파스 척도를 사용하면 코드뿐만 아니라 코드를 읽는 사람과 코드 사이의 관계에 대해서도 돌아보는데 도움이 된다.
눈 기반 측정
특정 작업에 대한 신체의 반응을 측정함으로써 그 순간 그 사람이 무엇을 하고 있든 그 일에서 발생하는 인지 부하를 추정할 수 있다.
생체 측정의 예로 시선 추적이 있다.
눈을 얼마나 깜빡이는 지, 동공의 크기 같은 지표로 두뇌가 어려운 일을 최대한으로 파악하려 하고, 이에 따라 가능한 한 많은 시각적 자극을 받기 위해 노력한다는 것이다.
피부 기반 측정
사람의 피부로도 인지 부화를 알 수 있으며, 피부의 온도와 땀이 인지 부하의 지표다.
뇌 기반 측정
fMRI 스캐너를 사용하여 인지 부하를 체크할 수 있지만 해당 기계를 사용하면 움직임에 제약이 생겨 코드를 수정하지 못한다.
뇌전도
뇌 활동을 측정하는 다른 방법으로는 뇌전도가 있다.
언어적 안티패턴 및 인지 부하
언어적 안티패턴이 혼란을 일으키는 이유
언어적 안티패턴을 많이 가지고 있는 코드는 인지 부하를 더 많이 유발한다.
이러한 연관성을 확인하기 위해서는 뇌 측정을 이용한 연구가 더 많이 필요하지만, 작업 기억과 LTM에 대한 지식을 바탕으로 언어적 안티패턴의 영향에 대해 추측해볼 수 있다.
요약
- 긴 메서드 등 코드 스멜은 코드의 구조적 문제를 의미한다. 코드 스멜이 인지 부하를 높이는 데 에는 여러 인지적 이유가 있다. 예를 들어 중복 코드는 코드를 제대로 청킹하기 어렵게 만들고, 긴 매개변수 목록은 작업 기억 공간을 많이 차지한다.
- 생체 인식 센서 등 인지 부하 측정 방법에는 눈 깜빡임 비율, 피부 온도 측정 등 다양한 방법이 있다. 자신의 인지 부하를 측정하려는 경우 일반적으로 파스 척도는 신뢰할 수 있는 도구다.
- 언어적 안티패턴이란 코드가 실제 수행하는 작업과 맞지 않는 이름을 의미하며, 높은 인지 부하를 초래한다. 이는 우리가 사고할 때 LTM이 돕는 과정에서 잘못된 사실을 발견하기 때문에 일어날 수 있다. 언어적 안티패턴은 실제로 구현되지 않은 코드의 의미를 가정하기 때문에 잘못된 청킹으로 이어질 수 있다.
10장 복잡한 문제 해결을 더 잘하려면
- 핵심 주제
- 문제 해결 시 여러 가지 기억 체계가 수행하는 역할의 비교
- 작은 기술을 자동화하는 것이 어떻게 크고 어려운 문제를 해결하는 데 도움이 되는지 분석
- LTM을 강화함으로써 문제를 쉽게 해결하는 방법
이번 장은 문제 해결에 대한 내용을 답고 있는데 먼저, 문제를 해결한다는 것 자체를 살펴본다.
10.1 문제 해결이란 무엇인가?
이 장의 목표는 문제를 해결하는 데 LTM이 수행하는 역할을 살펴보고 문제 해결 능력을 개선하는데 도움이 될 만한 전략을 배우는 것이다.
10.1.1 문제 해결 요소
- 목표 상태, 즉 우리가 달성하고자 하는 것. 목표 상타에 도달하면 문제가 해결된 것으로 간주한다.
- 문제를 해결해야 하는 시작 상태
- 시작 상태에서 목표 상태에 도달하는 방법을 규정하는 규칙
10.1.2 상태 공간
프로그램을 해결할 때 고려할 수 있는 모든 단계를 문제의 상태 공간이라고 한다.
틱택토 게임을 예로 모든 가능한 칸의 경우가 상태 공간이다.
문제 해결은 가능한 한 적은 단계로 목표 상태에 도달하기 위해 상태 공간을 최적의 방식으로 탐색해나가는 것을 의미한다.
10.2 프로그래밍 문제를 해결할 때 LTM의 역할은 무엇인가?
10.2.1 문제 해결은 그 자체로 인지 과정인가?
어떤 사람들은 문제 해결이 일반적인 기술이며, 따라서 두뇌 속의 특정한 과정이라고 생각한다.
사고 체계
- 문제 이해
- 계획 수립
- 계획 실행
그러나 일반적 접근법의 인기에도 불구하고, 문제 해결은 일반적 기술도, 인지 과정도 아니라는 것이 지금까지의 연구 결과가 일관되게 보여주는 사실이다.
문제 해결 시 LTM 사용
첫째, 우리는 어떤 문제를 풀 때 원하는 목표 상태와 적용해야할 규칙에 대한 지식을 염두에 둔다.
우리가 설계할 수 있는 해결책은 문제 자체에 영향을 받는다.
- 문제를 이해한다.
- 계획을 세운다. (해석)
- 계획을 실행한다. (해결)
두뇌는 익숙한 문제를 해결하는 것이 더 쉽다
일반적인 문제 해결 방법이 효과적이지 않고 LTM의 작업과 관련이 있는 두 번째 이유가 있다.
3장에서 LTM에 있는 기억은 서로에 대한 네트워크로 지정된다고 설명했다.
두뇌가 문제를 해결하려고 할 때 이 전략을 인출하려고 한다.
그러나 일반적인 방법으로 문제를 해결하려고 하면, 관련 전략이 발견되지 않을 수 있다.
이에 단서가 필요.. 단서가 구체적일수록 올바른 기억을 찾을 가능성이 높다.
10.2.2 문제 해결을 위한 LTM 교육 방법
앞서 문제 해결이 인지 과정이 아니라는 것을 알아봤다.
따라서 문제 해결을 위해 어떻게 훈련을 해야할까?
10.2.3 문제 해결에 역할을 하는 두 가지 유형의 기억
첫째, 운동 능력이나 의식하지 않고 발휘하는 기술에 대한 기억인 절차적 기억이 있다.
절차적 기억의 예로는 신발 끈을 묶거나 자전거를 타는 법 등이 있다.
기억의 종류는 다양하고, 절차적 기억은 무언가를 하는 방법을 보여준다. 선언적 기억은 우리가 명확하게 알고 있는 기억으로 구성된다.
선언적 기억은 두 가지 유형으로 나뉘는데 일화적 기억에 저장되는 경험과 의미적 기억에 저장되는 사실로 구분된다.
문제를 해결할 때 역할을 수행하는 두 번째 유형의 기억은 선언적 기억이다.
자바로 for 루프를 쓰는 방법이 그 예다.
선언적 기억은 다시 일화적 기억과 의미적 기억으로 나뉜다.
일화적 기억은 기억 그자체를 의미한다. (여름 캠프에서 배우자를 만난 기억)
다른 범주로 의미적 기억이 있는데, 의미적 기억은 으미, 개념 또는 사실에 대한 기억이다.
문제를 해결할 때 어떤 유형의 기억이 역할을 수행하는가?
이러한 모든 형태의 기억은 프로그래밍을 수행하는 역할이 있다.
가장 먼저 떠오르는 기억은 명시적 기억일 것이다.
해당 언어에 대한 자신의 기억을 인출한다.
일화적 기억은 과거에 문제를 어떻게 해결했는지 기억할 때 사용한다. (게층 구조에는 트리를 사용한 기억)
전문가들은 문제를 해결할 때 특히 순간적인 기억력에 크게 의존하는 것이라고 한다.
절차적(암시적)기억은 아무렇지 않게 ctrl+c, ctrl+v를 사용한다.
탈학습
암시적, 절차적 기억이 작업을 신속하게 하는데 도움이 된다는 것을 알았다.
하지만 이러한 기억은 학습을 방해할 수 있다.
쿼티 키보드를 쓰다 드보락 키보드를 쓴다던가, C#언어의 문법을 너무 많이 쓰다 다른 언어를 배울 때 의식적으로 C#언어의 문법을 사용하는 것을 막아야 한다.
10.3 자동화: 암시적 기억 생성
이제 문제 해결 능력을 높이는 방법에 대해서 알아본다.
먼저 첫 번째 기술은 자동화다.
걷기, 독서, 신발 끈 묶기와 같이 어떤 기술을 여러 번 연습한 후에 아무 생각 없이 할 수 있을 정도가 되면 이 기술을 자동화했다고 한다.
게임에서 새로운 기술을 익히는 과정으로 생각할 수 있는데, 더블점프를 익히면 이전에는 도달하지 못했던 영역에 도달할 수 있기 때문이다.
따라서 프로그래밍 기술의 자동화는 더 크고 복잡한 문제를 해결할 수 있는 열쇠다.
하지만 어떻게 해야 자동화된 기술을 가질 수 있을까?
10.3.1 시간 경과에 따른 암시적 기억
프로그래밍에 대한 암시적 기억이 많을수록 인지 부하를 더 많이 절약할 수 있기 때문에 더 큰 문제를 해결하기 쉽다.
어떤 사실을 명시적 기억으로 저장하려면 명시적 주의가 필요하다.
자바의 for 루프를 사용하는 방법을 배우려면 명시적으로 이를 기억해야 한다.(스스로 배우고 싶어 해야함)
반면 암시적 기억은 다른 방식, 즉 반복에 의해 생성된다.
밥을 먹을 때 국물을 숟가락으로 떠먹는 등 시간이 지나면서 차츰 익히게 된다.
인지 단계
인지 단계는 무언가 새로운 것을 배우는 때로 새로운 정보를 더 작은 부분으로 나누고 당면한 작업에 대해 명시적으로 생각한다.
연상 단계
이 단계에서는 응답 패턴이 나타날 때까지 새 정보를 적극적으로 반복해야 한다.
자율 단계
마지막으로, 기술이 완벽한 자율 단계에 도달한다.
10.3.2 자동화를 통해 보다 신속하게 프로그램을 실행할 수 있는 이유
기술에 관한 대규모 저장소를 만들면 새로운 기술을 계속 습득해나갈 수 있는 일종의 툴박스를 만들 수 있ㄷ.
이는 LTM의 일화적 기억이 인출함으로써 이루어진다고 주장하며 ‘인수분해에 대한 기억’이라는 클래스의 한 인스턴스로 간주되기 때문에, 이 이론을 인스턴스 이론이라고 부른다.
객체지향적으로 유사한 작업을 만났을 때 해당 인스턴스 기억을 많이 가지고 있다면 이전에 수행했던 방법으로 동일한 방법을 적용할 수 있다.
10.3.3 암시적 기억 개선
프로그래밍에서는 일반적으로 의도적 연습을 하지 않는다.
오류 없는 루프를 작성하는 데 어려움을 겪고 있다고 해서, 의도적으로 for루프를 100번 작성하는 것은 개발자 세계에서 없는 일이다.
의도적 연습은 다른 방법으로도 가능하다.
첫째, 연습하고자 하는 기술이 필요한, 유사하지만 다른 프로그램을 많이 작성해보는 것이다.
10.4 코드와 해설에서 배우기
문제 해결 능력을 향상하기 위해 사용할 수 있는 두 번째 방법은 다른 사람들이 문제를 어떻게 해결했는지 의도적으로 연구하는 것이다.
다른 사람들이 문제를 어떻게 해결했는지 연구함으로써 얻는 해결책은 종종 풀이된 예제라고 부른다.
10.4.1 새로운 유형의 인지 부하: 본유적 부하
본유적 부하는 두뇌가 LTM에 다시 저장하기 위해 수행하는 노력을 의미한다.
모든 인지 부하가 내재적 부하와 외재적 부하로 가득 차면, 본유적 부하를 위한 여지는 남아 있지 않게 된다.
힘든 코딩 작업을 마친 후 때때로 자신이 한 일을 기억하지 못하는 경우가 있을 것이다.
이것이 바로 이런 이유 때문이다.
두뇌가 해결책을 저장할 수 없을 정도로 몰입해 있었던 것이다.
10.4.2 개발 작업 시 풀이된 예제 활용하기
동료와의 협업
첫째, 코드를 혼자 공부할 필요는 없다.
누군가와 함께 공부하는 것이 더 유용하다.
깃허브 탐구
혼자서 코드를 읽어야 한다면 온라인에 소스 코드와 설명서가 많이 있다.
소스 코드에 대한 책 또는 블로그 읽기
마찬가지로 블로그도 좋은 게시물들이 있다.
요약
- 프로그래밍에 종사하는 많은 사람은 문제 해결이 일반적인 기술이라고 주장하지만, 그렇지 않다. 프로그래밍에 대한 사전 지식이 현재 해결 중인 문제와 경합해 프로그래밍 문제를 얼마나 빨리 해결할 수 있는지에 영향을 미친다.
- LTM은 다양한 유형의 기억을 저장하는데, 이들은 문제를 해결할 때 모두 각자 다른 역할을 한다. 기억의 저장의 가장 중요한 두 범주는 암시적 기억과 명시적 기억이다. 암시적 기억은 ‘근육 기억’으로 타자 타이핑 같은 작업을 말하고 명시적 기억은 for루프 문법과 같은 능동적으로 불러와야 하는 기억이다.
- 프로그래밍과 관련된 암시적 기억을 강화하려면 터치 타이핑, 관련 단축키 암기 같은 관련 기술을 자동화하는 것이 가장 효과적이다.
- 프로그래밍과 관련된 명시적 기억을 강화하려면 기존 코드, 가급적이면 코드 설계 방법에 대한 설명이 포함된 코드를 탐구하라
11장 코드를 작성하는 행위
- 이 장에서는 다음과 같은 내용을 다룬다.
- 코드와 상호작용할 때 수행하는 여러 가지 다른 활동의 비교
- 여러 가지 활동을 더 효과적으로 수행하도록 두뇌를 지원하는 방안
- 업무 중단이 개발자 업무에 미치는 영향
- 업무 중단 이후 효과적으로 업무를 다시 시작하기 위해 기억을 사용하는 방법
이 장에서는 코드 자체에서 벗어나 프로그래밍을 실행할 때 인지 과정이 어떤 역할을 하는지 살펴볼 것이다.
11.1 프로그래밍 중 이루어지는 다양한 활동
토머스 그린에 의해 인지적 차원 표기법이라는 프레임워크로 처음 고안되었다.
활동 | 실행 | 코딩 | 테스트 | 읽기 | 리팩터링 | 부하를 가하는 기억 공간 |
---|---|---|---|---|---|---|
검색 | v | v | STM | |||
이해 | v | v | v | v | 작업 기억 공간 | |
전사 | v | LTM | ||||
증가 | v | v | v | v | v | 세 곳 모두 |
탐구 | v | v | v | v | v | 세 곳 모두 |
11.1.1 검색
검색은 코드베이스를 살펴보고 특정 정보를 검색하는 작업이다.
해결해야 하는 버그의 정확한 위치나 특정 메서드의 모든 호출 혹은 변수가 초기화되는 위치일 수 있다.
검색하는 동안 코드를 읽고 실행하는 것이 주된 일이지만, 중단점과 디버거를 사용할 수 있고, 코드를 실행해보며 출력문으로 확인하기도 한다.
검색은 STM에 무리를 가한다.
11.1.2 이해
이해 활동을 수행할 때는 코드를 읽고 실행해봄으로써 그 기능을 이해하게 된다.
검색과 비슷하지만 코드가 작성된 지 오래되었거나 다른 사용자가 코드를 작성했기 때문에 코드의 세부기능에 대한 이해가 부족할 수 있다.
5장에서 살펴본 바와 같이 개발자는 평균적으로 읽고 이해하는 과정에 가장 많은 시간을 소비한다.
11.1.3 전사
전사란 ‘단순히 코딩’하는 활동이다.
코드베이스에 추가 또는 변경할 내용에 대한 구체적인 계획이 있을 때, 단지 그 계획을 수행하는 것이다.
이 활동의 가장 단순한 형태는 내용을 단지 코드로 옮기는 것일 뿐 그 밖의 다른 일은 없다.
11.1.4 증가
증가는 검색, 이해, 전사가 합쳐진 활동이다.
새 기능을 추가하는 것은 코드가 늘어나는 것이다.
이 활동에는 코드를 추가할 위치를 검색하는 것, 어디에 코드를 추가할지 그리고 어떻게 할지 이해하기 위해 기존 코드를 파악하는 것, 그리고 아이디어를 문법으로 실제로 전사하는 활동이 모두 포함된다.
11.1.5 탐구
탐구 활동의 본질은 코드를 사용하여 스케치하는 것과 같다.
무엇을 해야 할지 분명한 생각이 떠오르지 않을 때, 프로그래밍을 통해 문제의 도메인과 사용해야 하는 프로그래밍 구성 요소를 명확히 알 수 있다.
11.1.6 디버깅은 어떤가?
디버깅 작업은 왜 없을까??
디버깅을 할 때는 종종 이 5가지 활동을 모두 수행한다.
따라서 디버깅은 탐구, 검색, 이해, 코드 작성의 순서이며, 다섯 가지 활동이 혼합된 것으로 묘사할 수 있다.
11.2 프로그래머의 업무 중단
프로그래머의 업무 중단 사례를 심각한 문제라고 생각하고 있다.
11.2.1 프로그래밍 작업에는 워밍업이 필요하다
11.2.2 중단 후에는 어떻게 되는가?
업무 중단 후 발생하는 상황은 생산성 측면에서 상당한 손해라고 본다.
작업 기억 공간이 원래 작업하던 코드에 대한 중요한 정보를 잃어버린다는 것을 연구 결과로 부터 알 수 있다.
생각
뽀모도르 같이 25분 집중 5분 휴식과 같이 집중하기 위한 방법도 5분 휴식에 의해 업무가 중단된다고 볼 수 있을까?
장기적인 측면에서 시간을 효율적으로 관리하는 방법론은 좋다고 평가되지만 업무 중단에 있어서는 과연 어떤 효과가 있을까..?
11.2.3 중단에 잘 대비하는 방법
업무 중단이 흔하고 다시 돌아와 원래 하던 업무를 계속하기가 어렵다는 것을 알았으니, 중단에 더 잘 대비할 수 있도록, 업무 중단 시 우리 두뇌에서 무슨 일이 일어나는지 더 자세히 알아본다.
따라서 이런 중단에 대처하는 유용한 세 가지 방법을 소개한다.
정신 모델 저장
어떤 결정을 내렸는지 문서화해놓으면 다른 사람이 코드를 읽을 때 매우 유용할 뿐만 아니라 자기 자신의 정신 모델을 일시적으로 저장하는데 도움이 되고 나중에 작업을 쉽게 재개할 수 있다.
코드에 대한 최신 정신 모델을 주석문의 형태로 ‘브레인 덤프’하는 것이 아주 유용할 수 있다.
미래 기억 향상
두 가지 유형의 기억 말고도 미래에 관련된 기억의 종류가 있다.
미래 기억은 미래에 무언가를 할 것에 대한 기억이다.
이러한 유형의 기억은 계획 및 문제 해결과 밀접한 관련이 있다.
집에 오는 길에 가게에 들러 우유를 사는 것을 기억해야 한다고 자신에게 말하는 것, 혹은 나중에 어떤 코드를 리팩터링할지를 자신에게 상기할 때, 우리는 미래 기억을 사용하는 것이다.
하위 목표 라벨 붙이기
문제를 어떤 작은 단계로 나눌 수 있는지 명시적으로 기록한다.
예를 들어 텍스트에 대해 구문 분석하고 재구성하는 작업 단계로 나누면 다음과 같다.
- 텍스트를 구문 분석하고 구문 분석 트리를 생성한다.
- 구문 분석 트리를 필터링한다.
- 트리를 선형 구조로 변환해 텍스트를 만든다.
이 단계들 자체는 상상하거나 기억하기 어렵지 않지만, 도중에 중단되고 나서 다시 코드로 돌아가서 무엇을 하려고 했는지 기억하는 것은 번거로울 수 있다.
11.2.4 프로그래머를 방해할 때
작업이 유발하는 인지 부하에 다른 식으로 접근하는 방법들도 있다.
한 가지는 이중 과제 척도
로 실험 참가자가 원래 작업을 하는 동안 수행하는 두 번째 작업이다.
멀티태스킹에 대한 이야기로 당연하게 몰입하고 있는 작업은 하나로 한정되고 완료 이후에 다른 작업이 더 효율적이라는 이야기
11.2.5 멀티태스킹에 대한 고찰
우리 두뇌는 멀티코어 프로세서처럼 동시에 여러 가지 일을 할 수 없는가?
멀티태스킹 및 자동화
안타깝게도, 사람들이 깊은 인지 작업을 하는 동안 여러 가지 일을 할 수 없다는 증거는 넘친다.
정보를 저장하는 세 단계, 즉 인지, 연상, 자율을 기억해보면 자율 단계에 도달하지 않은 경우 두 개 이상의 작업을 동시에 수행할 수 없다.
멀티태스킹에 관한 연구
연구 결과 역시 불가능에 가깝다는 이야기..
요약
- 프로그래밍을 할 때 검색, 이해, 전사, 증가, 탐구 등 다양한 프로그래밍 활동을 함께 수행한다. 각각의 활동은 서로 다른 기억 체계에 부담을 준다. 따라서 여러 다른 기법을 사용해 이 활동들을 지원해야 한다.
- 프로그래밍 중 중단은 성가실 뿐만 아니라 코드에 대한 정신 모델을 재구성하는 데 시간이 걸리기 때문에 생산성에 나쁜 영향을 미친다.
- 중단에 잘 대처하려면 정신 모델을 노트, 문서 또는 주석으로 작성해놓는다.
- 작업을 완료할 수 없다면 계획을 문서화해서 미래 기억을 의도적으로 도와주어야 한다.
- 방해 받더라도 가급적이면 인지 부하가 낮을 때 중단이 일어나도록 해야 한다.
12장 대규모 시스템의 설계와 개선
- 핵심 주제
- 설계에 대한 여러 가지 결정이 코드베이스의 이해도에 미치는 영향
- 설계에 대한 여러 가지 결정 사이의 트레이드 오프
- 더 나은 인지 처리를 위한 기존 코드베이스 설계의 개선
라이브러리, 프레임워크, 모듈에 대해 이야기할 때, 우리는 그것들이 작성된 프로그래밍 언어와 같은 기술적 측면에 대해 이야기 한다.
그러나 코드베이스를 인지적 관점을 통해서도 볼 수 있다.
이 장에서는 인지적 관점에서 코드베이스를 조사하는 기술인 CDN에 대해 논할 것이다.
CDN은 기존의 코드베이스에 대해”이 코드를 사람들이 쉽게 변경할 수 있는가?” 혹은 “이 코드베이스에서 정보를 쉽게 찾을 수 있는가?”와 같은 질문에 답을 찾는 데 도움이 된다.
기술적 관점보다는 인지적 관점에서 코드베이스를 검사하면 사람들이 코드와 어떻게 상호작용하는지 더 잘 알 수 있다.
1.1. 12.1 코드베이스의 특성 조사
프로그래밍 언어를 논할 때, 우리는 종종 기술적 영역, 예를 들어 패러다임(객체 지향, 함수형, 또는 혼합), 타입 시스템 여부, 언어가 바이트 코드로 컴파일 되는지 또는 다른 언어로 된 프로그램에 의해 인터프리터되는지 살펴본다.
언어, 프레임워크, 라이브러리를 실행하는 환경도 확인하곤 한다.
브라우저 또는 가상 머신에서 실행되는가?
이러한 모든 측면은 기술적 영역, 즉 프로그래밍 언어가 할 수 있는 것에 관한 사항이다.
1.1.1. 12.1.1 인지적 차원
CDN을 이 책에서는 좀 더 일반화하여 프로그래밍 언어가 아닌 코드베이스에 대해서도 CDN을 사용하려고 한다.
이 일반화된 버전을 코드베이스의 인지 차원(CDCB)이라고 부르고, 이것을 통해 코드베이스를 검토하고, 코드베이스를 어떻게 이해하고 개선할 수 있을지 살펴볼 것이다.
1.1.1.1. 오류 경향성
첫 번째 차원은 오류 경향성이라고 불린다.
자바 스크립트 및 기타 동적 유형 언어에서는 변수가 생성될 때 데이터 타입이 정해지지 않는다.
런타임에 객체의 타입이 명확하지 않기 때문에, 프로그래머는 변수의 타입에 대해 혼동할 수 있고 이로 인해 오류가 발생할 수 있다.
프로그래밍 언어가 아닌 코드베이스 역시 오류 경향성이 있는데, 예를 들어 일관성 없는 규칙, 문서 부족, 모호한 이름 때문이다.
1.1.1.2. 일관성
일관성은 비슷한 것들은 서로 얼마나 유사한가? 를 말하며, 예로는 이름은 항상 동일한 방식으로 만들어지는가? 를 들 수 있다.
많은 프로그래밍 언어에서 일관성이 있는지 알 수 있는 방법은 함수 정의가 있다.
이런 생각을 해본 적이 없겠지만 내장 함수는 일반적으로 사용자 정의 함수와 동일한 사용자 인터페이스를 가지고 있다.
이름과 규약을 일관성 없이 사용하는 프레임워크나 언어는 인지 부하를 더 많이 가져올 수 있다.
1.1.1.3. 분상성
너무 많은 라인으로 작성되어 있는 긴 메서드는 앞에서 코드 스멜이라는 말로 다뤘다.
메서드를 불필요하게 복잡하게 만들거나 한 메서드에 너무 많은 기능을 쑤셔넣는다면 이는 프로그래머의 잘못일 수 있다.
그러나 일부 프로그래밍 언어는 동일한 기능에 대해 다른 프로그래밍 언어보다 더 많은 공간을 차지한다.
분산성 차원은 이에 대한 것이다.
c++과 파이썬의 반복문을 비교하면 길이도 파이썬이 더 짧고 청크도 파이썬이 더 적다.
1.1.1.4. 숨겨진 의존성
숨겨진 의존성차원은 의존 항목이 사용자에게 어느 정도로 가시적으로 나타나는지를 보여준다.
숨겨진 의존성이 높은 시스템의 예를 들자면 HTML 페이지에 자바스크립트로 제어되는 버튼이 있고 자바스크립트는 다른 파일에 저장되어 있는 경우다.
일반적으로, 다른 함수나 클래스 안에서 어떤 함수가 호출되는지 아는 것과 그 반대의 경우, 즉 주어진 함수를 어떤 클래스나 함수가 호출하는지 아는 것 중에서 전자가 후자보다 더 쉽다.
1.1.1.5. 잠정성
잠정성 차원은 도구를 사용하는 동안 생각하는 것이 얼마나 쉬운지에 대한 것이다.
자신이 무엇을 만들고 있는지 확신하지 못하면 탐구적인 방식으로 프로그래밍을 하게 된다.
코드베이스나 프로그래밍 언어가 매우 엄격하다면 코드를 사용해 생각을 표현하는 것은 어려울 수 있다.
이런 경우 잠정성이 낮다고 말한다.
1.1.1.6. 점도
점도는 특정 시스템을 변경하는 것이 얼마나 어려운가에 대한 차원으로, 잠정성과 관련이 있다.
일반적으로 동작 타입 언어로 작성된 코드를 변경하는 것이 조금 더 쉽다.
코드만 변경하면 되고 모든 해당 타입 정의를 변경할 필요가 없다.
모듈로 이루어지지 않은 코드나 블록으로 된 코드는 직접 변경할 수 있고 함수나 클래스에서 변경할 필요가 없기 때문에 변경이 용이하다.
1.1.1.7. 점진적 평가
점진적 평가 역시 잠정성과 관련이 있다.
이 차원은 주어진 시스템에서 부분적인 작업을 확인하거나 실행하는 것이 얼마나 쉬운지에 대한 것이다.
우리가 살펴본 바와 같이, 잠정성이 높은 시스템은 사용자가 불완전한 아이디어를 스케치할 수 있는 것이 가능하다.
점진적 평가를 통해 사용자는 완성되지 않은 코드나 불완전한 코드를 실행할 수 있다.
1.1.1.8. 역할 표현력
역할 표현력의 차원은 프로그램에서 여러 가지 다른 부분의 역할을 얼마나 쉽게 알 수 있는지를 나타낸다.
역할 표현력의 간단한 예는 file.open()
처럼 거의 모든 프로그래밍 언어에서 매개변수가 없는 함수를 호출할 때 여전히 끝에 두 개의 괄호를 가지고 있다는 점이다.
언어 설계자가 매개변수가 없는 함수의 경우 괄호를 생략해도 되게끔 만들 수도 있지만, 괄호가 있음으로 해서 open()이 함수임을 알 수 있다.
함수의 끝에 있는 괄호는 역할 표현력의 일례다.
1.1.1.9. 매핑 근접성
매핑 근접성 차원은 프로그래밍 언어 또는 코드가 문제의 해결 영역에 얼마나 가까운지를 의미한다.
어떤 프로그래밍 언어들은 매핑 근접성이 높다.
예를 들어, SQL은 데이터베이스를 다루는 언어이다.
APL은 수학에 근접성이 높고 현대의 언어들은 범용적 언어이기에 근접성이 낮다.
1.1.1.10. 힘든 정신 활동
어떤 시스템에서 사용자가 시스템의 외부에서 힘든 정신 활동을 수행할 때 생각하는 것이 매우 어려울 수 있다.
예를 들어 하스켈과 같은 언어는 사용자가 모든 함수와 매개변수의 타입을 고려해야 한다.
하스켈에서는 함수의 형식을 무시할 수 없는데 만일 무시하게 되면 실행되는 코드를 작성하는 것은 거의 불가능하다.
마찬가지로 C++는 사용자가 여러 상황에서 포인터를 사용해야 하고 객체보다는 포인터로 추론할 것을 요구한다.
물론 힘든 정신활동이 모두 나쁜 것만은 아니다.
생각하게 하는 것이 성과를 거둘 수도 있다.
예를 들면, 엄격한 타입 시스템에서는 오류 수가 적다던지 포인터를 사용하면 성능이 더 우수하거나 메모리 사용이 더 효율적이다.
1.1.1.11. 보조 표기법
보조 표기법 차원은 프로그래머가 공식 규격에는 없는 의미를 코드에 추가할 가능성을 타나낸다.
가장 일반적인 예로 소스 코드에 주석문을 추가할 가능성이다.
주석문은 프로그램의 동작을 변경하는 측면으로 보면 프로그래밍 언어의 일부가 아니다.
C#의 명명된 매개변수와 같음
1.1.1.12. 추상화
추상화 차원은 시스템의 사용자가 기본적으로 제공되는 추상화만큼 강력한 추상화를 만들 수 있는지 여부다.
대부분의 프로그래밍 언어가 허용하는 추상화의 예로는 함수, 객체, 클래스를 만드는 것이다.
1.1.1.13. 가시성
가시성은 시스템의 다른 부분을 얼마나 쉽게 볼 수 있는지를 나타낸다.
코드베이스가 어떤 클래스로 구성되어 있는지 확인하기 어려울 수 있는데, 특히 코드가 여러 파일로 나뉘어 있는 경우에는 더욱 그렇다.
1.1.2. 12.1.2 코드베이스 개선을 위해 CDCB 사용
지금까지 프로그램이 가질 수 있는 다양한 차원에 대해 살펴보았다.
이러한 차원의 차이는 사람들이 코드베이스와 상호작용하는 방식에 큰 영향을 미칠 수 있다.
예를 들어 코드베이스 점도가 높은 경우 코드베이스에서 작업하는 미래의 개발자는 변경을 꺼릴 수 있다.
이로 인해 코드베이스 구조가 크게 변경되기보다는 패치가 더해져서 코드가 점점 복잡해진다.
1.1.3. 12.1.3 설계 기둥 및 트레이드오프
코드베이스에서 특정 차원을 개선하기 위해 코드베이스를 변경하는 것을 설계 기둥이라고 한다.
예를 들어 코드베이스에 타입을 추가하는 것은 오류 경향성을 개선하기 위한 설계 기동이며, 함수 이름을 코드의 영역에 더 부합하도록 변경하는 것은 매핑 근접성을 개선하는 설계 기동이다.
1.1.3.1. 오류 경향성 vs 점도
라이브러리나 프레임워크의 사용자가 실수하지 않도록 하기 위해 사용자가 추가 정보를 입력하도록 하는 경우가 많다.
이와 같은 오류 경향성을 감소하기 위한 방법 중 가장 잘 알려진 예는 타입을 추가하는 것이다.
컴파일러가 어떤 개체의 타입을 알고 있다면 이 정보를 이용해서 문자열에 리스트를 추가하는 것과 같은 실수를 방지할 수 있다.
그러나 시스템의 모든 항목에 대해 타입을 추가하는 것은 사용자에게 추가 작업의 부담을 준다.
1.1.3.2. 점정성 및 점진적 평가 vs 오류 경향성
잠정성과 점진적 평가가 높은 시스템에서는 미완성되거나 불완전한 코드라도 스케치하고 실행할 수 있다.
이러한 차원은 당면한 문제에 대해 생각하는 데 도움이 될 수 있지만, 프로그램이 완성되지 않은 채 그대로 남아 있거나, 불완전한 프로그램이 개선되지 않은 채 남아 있어 이해하기 어렵고 이는 디버깅하기 어려운 코드로 이어져 오류 경향성에 영향을 미칠 수 있다.
1.1.3.3. 역할 표현력 vs 분산성
명명된 매개변수 같은 문법 요소를 추가하면 역할 표현력이 개선될 수 있다는 것과 추가적인 레이블로 인해 코드가 길어진다는 것은 변수의 역할을 나타내기도 하지만 코드베이스의 크기를 늘리는 타입 애너테이션도 마찬가지다.
1.2. 12.2 차원 및 활동
이전 장에서 다섯 가지 프로그래밍 활동 즉, 검색, 이해, 전사, 증가, 탐구에 대해 알아봤다.
각 활동은 코드베이스가 최적화해야 하는 인지 차원에 서로 다른 제약을 가한다.
1.2.1. 12.2.1 차원이 활동에 미치는 영향
1어떤 활동은 특정 차원이 높아야 하는 반면, 어떤 활동은 특정 차원이 낮을 때 가장 잘 작동한다.
1.2.1.1. 검색
검색할 때 일부 차원은 중요한 역할을 한다.
예를 들어 숨겨진 종속성은 검색 활동에 악영향을 미친다.
분산성은 코드를 더 길게 만들어 검색에 부정적인 영향을 준다.
반면 보조 표기법은 검색에 도움이 된다.
1.2.1.2. 이해
코드베이스의 가시성이 낮으면 클래스와 기능이 서로 어떻게 관련되는지 파악하기 어렵기 때문에 이해에 부정적인 영향을 준다.
반면 역할 표현력은 이해에 긍정적인 영향을 준다. (변수 및 기타 개체의 유형과 역할이 명확하다면 이해하기가 더 쉬워질 것이다.)
1.2.1.3. 전사
전사할 때 일관성 같은 일부 차원은 나빠질 수 있다.
일관된 코드베이스는 이해하기 쉽지만, 새로운 기능을 구현할 때는 코드베이스에 맞춰야 하므로 정신적 노력이 추가로 필요할 수 있다.
1.2.1.4. 증가
코드베이스에 새 기능을 추가할 때 가장 도움이 되는 것은 도메인에 대한 매핑 접근성이다.
코드베이스에서 프로그래밍 개념보다는 코드의 목표를 뚜렷이 알 수 있다면, 새로운 코드를 추가하는 것이 더 쉬울 것이다.
1.2.1.5. 탐구
새로운 설계 아이디어를 탐구할 때 가장 도움이 되는 차원은 잠정성과 점진적 평가이다.
힘든 정신 활동과 추상화하는 프로그래머에게는 높은 인지 부하를 유발하고 문제와 해결 공간을 탐구하는 데 사용되어야 할 부하를 제한하기에 탐구에 악영향을 미칠 수 있다.
1.2.2. 12.2.2 예상 활동에 대한 코드베이스 최적화
여러 다른 활동이 시스템에 서로 다른 제약을 가하는 것을 알아봤다.
따라서 코드베이스에 대해 다른 사람이 어떤 작업을 수행할지 이해하고 있어야 한다.(협업의 기본..!)
상대적으로 오래되고 안정적인 라이브러리는 증가 활동보다는 검색 활동이 더 많이 일어날 가능성이 높은 반면, 새로운 앱은 증가 및 전사 활동이 일어날 가능성이 더 높다.
즉 코드베이스가 유지되는 동안에는 코드베이스에 대해 일어날 가능성이 가장 높은 활동에 맞는 설계 기동이 필요할 수 있다.
1.3. 요약
- CDN은 프로그래밍 언어의 사용자들에게 미칠 인지적 영향을 예측하는 데 도움이 되는 프레임 워크이다.
- CDCB는 코드베이스, 라이브러리, 프레임워크가 사용자에게 미치는 영향을 이해하는데 도움이 되는 CDN의 확장이다.
- 대부분의 경우 서로 다른 차원 간의 트레이드오프가 이루어져야 한다. 한 차원을 개선하면 다른 차원이 감소할 수 있다.
- 설계 기동을 통해 프레임워크의 인지 차원에 따라 기존 코드베이스의 설계를 개선하는 것이 이뤄질 수 있다.
- 코드베이스가 최적화할 대상인 차원에 대한 요구 사항은 수행하려는 활동에 따라 다르다.
13장 새로운 개발자 팀원의 적응 지원
- 핵심 주제
- 전문가와 초보자가 생각하는 방식 비교
- 새 팀원에 대한 코드베이스 적응 지원의 개선
- 새 팀원의 프로그래밍 언어나 프레임워크 학습 시 지원 방안
상급 개발자로서 우리가 스스로 코드 파악에 어려움을 겪고 있거나 함께 일하는 다른 후배 개발자들도 어려움이 있을 수 있다.
많은 경우, 후배들이 더 효과적으로 학습할 수 있기 위해서는 그들이 경험하는 인지 부하를 관리해야 한다.
1.1. 13.1 적응 지원의 문제
대부분의 선임자와 후임자가 둘 다 처음이라면 적응 지원 과정에서 모두에게 좌절감을 줄 수 있다.
대부분의 과정은 아래와 같다.
- 선임 개발자가 새 팀원에게 많은 정보를 준다. 정보의 양이 많아서 높은 인지 부하를 유발한다.
- 소개가 끝난 후 선임 개발자는 새 팀원에게 질문을 하나 하거나 과제를 준다. (선임은 이를 간단한 일로 여긴다.)
- 도메인이나 프로그래밍 언어 혹은 두 가지 모두 관련 청크의 부족과 관련 자동화 기술 부족으로 인해 인지 부하가 높아지고 새 팀원은 좌절한다.
이 시나리오에서 문제점은 새 팀원에게 동시에 너무 많은 정보를 교육함으로써 작업 기억 공간의 용량을 과도하게 늘린다는 점이다.
새 팀원은 작업 기억 공간에 과부화가 걸리기 때문에 새로운 코드베이스에서 효과적으로 프로그래밍할 수 없고, 새로운 정보도 유지할 수 없다.
팀의 선임자들이 효과적으로 가르치고 설명하는 데 어려움을 겪는 이유 중 하나는 많은 경우 전문가의 저주
때문이다.
어떤 기술을 익히고 나면 그 기술이나 지식을 배우는 것이 얼마나 어려웠는지 잊어버린다.
따라서 새 팀원이 동시에 처리할 수 있는 새로운 작업의 수를 과대평가하게 된다.
1.2. 13.2 전문가와 초보자의 차이
종종 전문가들은 초보자들도 자신들과 같은 방식으로 추론할 수 있을 것이라고 생각하지만, 초보자는 전문가보다 추론하는 것이 느리거나 코드베이스 전체에 대한 이해가 불완전할 수 있다.
첫째, 전문가의 뇌는 LTM에 관련 기억을 많이 저장하는데 이 저장된 기억을 작업 기억 공간이 필요로 할 때마다 가져온다.
둘째, 전문가는 코드 및 코드와 관련 있는 모든 사항, 즉 오류 메세지, 테스트, 문제, 해결책 등을 매우 효과적으로 청킹할 수 있다.
댓글남기기