프로그래머의 뇌 본문

Programming

프로그래머의 뇌

halatha 2022. 1. 18. 14:59

프로그래밍과 직접 연관된 부분은 거의 없지만, 여러가지 시사점을 제공하며, 좀 더 나은 프로그래밍을 하는 데 많은 도움을 줄 수 있는 주제들을 다룬다. 프로그래밍, 소프트웨어 공학이 발전하면서 세부적인 주제들, 일견 관련이 없어 보이는 부분들을 다루는 쪽으로도 나아가는데, 이 책 역시 그런 종류의 책이다. 예를 들어, 코드를 작성하는 것만큼, 어쩌면 그보다 더 코드를 읽는 게 중요하다는 사실은 최소한 경력이 있는 개발자들에겐 잘 알려진 사실이다. 하지만 이런 작업이 뇌와 어떻게 연결되고, 어떻게 동작하는지에 대해 쓴 책이 아마 이 책이 최초가 아닐까? 개인적으로는 비개발자들에게 개발자의 작업을 이해시키는데 유용하게 쓸만한 근거 자료들을 찾을 수 있어서 당장 직접적인 도움도 되었고, 프로그래밍을 이해하고 새로운 걸 배우거나 익히는데 조금 더 효율적으로 나를 훈련시킬 수 있지 않을까 하는 기대를 갖게 되었다.

“프로그램은 사람이 읽을 수 있도록 작성해야만 한다. 기계가 실행하는 것은 부차적인 일이다.”

코드를 읽을 때 정보가 처음 저장되는 곳이 STM이라는 사실을 1장에서 살펴봤다. 이번 장 도입부에서는 코드에 있는 많은 정보를 처리하는 것이 왜 어려운 일인지 설명한다. 코드를 빨리 읽을 때 두뇌에서 무슨 일이 일어나는지 알면 코드를 얼마나 잘 이해하고 있는지 스스로 확인해보기가 더 쉽다. 그다음 몇 가지 예제 코드를 빨리 읽는 것을 연습해보고, 이를 통해 코드 읽는 법을 향상하는 방법을 설명할 것이다. 이 장을 다 읽고 나면 코드 분석이 왜 어려운지, 그리고 어떻게 하면 코드를 더 빨리 읽을 수 있는지 이해하게 될 것이다. 또한 코드를 읽는 기술을 계속 향상하기 위해 사용할 수 있는 방법에 대해서도 알게 될 것이다.
해럴드 아벨슨, 제럴드 제이 서스먼, 줄리 서스먼이 쓴 《컴퓨터 프로그램의 구조와 해석》(인사이트, 2016)에 다음과 같은 유명한 문장이 있다. “프로그램은 사람이 읽을 수 있도록 작성해야만 한다. 기계가 실행하는 것은 부차적인 일이다.” 이 말이 사실임에도 현실적으로 프로그래머들은 코드를 읽는 법보다 작성하는 법을 훨씬 더 많이 연습한다.
…프로그래밍을 처음 배울 때는 코드를 만들어내는 것에 관심을 많이 갖는다…코드를 읽는 연습은 거의 전무할 것이다. 이 장을 통해 코드를 읽는 기술을 향상하길 바란다.
코드를 읽는 목적은 다양하다. 예를 들어 기능을 추가하거나 버그를 발견하기 위해서 혹은 코드가 실행될 더 큰 시스템을 이해하기 위해서다. 이 모든 상황에서 한 가지 공통점은 코드를 읽을 때 우리는 그 코드에 존재하는 특정한 정보를 찾는다는 점이다. 찾는 정보의 예로는 새로운 기능을 추가할 적당한 부분, 마지막으로 수정한 코드 중 특정 버그가 있을 만한 곳, 특정 메서드가 어떻게 구현됐는지 등이 있다.

“주석문은 초급 개발자가 코드를 이해하는 데도 도움이 되지만, 개발자가 코드를 청킹하는 방식에도 영향을 미친다.”

두뇌가 작업을 하다 업무 중단interruption 을 받게 되면, 우리가 생각한 것보다 훨씬 더 좋지 못한 결과를 초래하기 때문이다.

많은 소프트웨어 개발자는 프로그래밍 언어의 문법을 모르더라도 인터넷에서 검색하면 되고, 따라서 문법에 대한 지식이 그리 중요한 것은 아니라고 생각한다. 모르는 것이 있을 경우, 검색이 그리 좋은 해결책이 되지 못하는 이유는 두 가지가 있다. 그 첫 번째 이유는 2장에서 다뤘듯, 관련 내용을 미리 알고 있는 것이 코드를 효율적으로 읽고 이해하는 데 상당한 영향을 미치기 때문이다. 개념, 자료구조, 문법을 더 많이 알수록 두뇌는 더 많은 코드를 쉽게 분리하고 기억하고 처리할 수 있다.
3.1.1 업무 중단이 미치는 나쁜 영향
두 번째 이유는 두뇌가 작업을 하다 업무 중단interruption 을 받게 되면, 우리가 생각한 것보다 훨씬 더 좋지 못한 결과를 초래하기 때문이다. 정보를 검색하기 위해 브라우저를 열면 이메일을 확인해보고 싶을 수도 있고 뉴스를 읽고 싶은 마음이 들 수도 있는데 이런 일들은 현재 하고 있는 작업과는 무관하다. 관련 정보를 검색하더라도 프로그래밍에 관련한 웹사이트에서 어떤 주제에 대해 너무 자세히 논의하는 내용에 빠지게 되면 자칫 원래 목적을 잃어버릴 수도 있다.

리팩터링refactoring은 코드가 외부적으로 제공하는 기능은 유지한 채 코드의 내부 구조를 개선하는 것을 의미한다.

위의 두 가지 코드는 같은 문제를 해결하는 코드이기 때문에 내재적 인지 부하는 동일하다. 하지만 외재적 인지 부하가 동일한지 여부는 이미 가지고 있는 지식에 달려 있다. 리스트 컴프리헨션을 잘 모르는 사람이 첫 번째 코드에서 겪는 외재적 인지 부하는 그것을 잘 아는 사람보다 훨씬 더 크다.
4.2 인지 부하를 줄이기 위한 기법
코드가 작업 기억 공간에 과부하를 초래하는 다양한 방식에 대해 살펴봤으므로 인지 부하를 줄이는 방법에 대해 알아보자. 이 장의 나머지 부분에서는 복잡한 코드를 쉽게 읽을 세 가지 방법에 대해 논의한다. 첫 번째 방법은 다른 맥락에서 이미 알고 있을지도 모르겠지만, 리팩터링이다.
4.2.1 리팩터링
리팩터링refactoring은 코드가 외부적으로 제공하는 기능은 유지한 채 코드의 내부 구조를 개선하는 것을 의미한다. 리팩터링의 예로는 어떤 코드 블록의 길이가 너무 길면 여러 개의 함수로 나누거나 재사용을 위해 중복된 코드를 하나로 통합하는 것이다. 다음과 같은 파이썬 예제 코드에서 같은 계산을 반복하는 코드는 하나의 메서드로 만들 수 있다.

코드를 읽을 때 사용하는 기술과 인간의 언어로 된 텍스트를 읽을 때 사용하는 기술이 밀접하게 관련되어 있다.

최근의 연구에 의하면 코드를 읽을 때 사용하는 기술과 인간의 언어로 된 텍스트를 읽을 때 사용하는 기술이 밀접하게 관련되어 있다. 따라서 텍스트를 깊이이해하기 위해 우리 두뇌가 하는 일을 살펴보면 프로그래머로서도 많은 것을 배울 수 있다.
코드에 대해 추론할 때는 변수가 중심적인 역할을 한다. 변수가 어떤 종류의 정보를 담고 있는지 이해하는 것은 코드를 추론하고 수정하는 데 결정적인 역할을 한다. 어떤 변수가 나타내고자 하는 것을 이해하지 못하면 코드에 대해 생각하는 것이 매우 어려워진다. 바로 이 점 때문에 적절한 변수명은 표식beacon으로 사용될 수 있고 읽고 있는 코드를 깊이 이해하는 데도 도움이 된다.
이스턴 핀란드 대학교의 요르마 사야니에미Jorma Sajaniemi 교수에 따르면, 변수를 이해하기 어려운이유는 대부분의 프로그래머가 변수를 연관 지을 좋은 스키마를 자신들의 LTM에 가지고 있지 않기 때문이다. 사야니에미에 따르면 우리는 ‘변수’나 ‘정수’처럼 너무 많은 것을 포함하는 청크를 사용하거나 혹은 number_of_customer 같은 너무 구체적인 변수명처럼 너무 적은 것을 포함하는 청크를사용하는 경향이 있다고 한다. 프로그래머는 그 사이의 중간이 필요한데, 이것이 사야니에미로 하여금 변수 역할role of variables이라는 프레임워크를 만든 동기가 됐다. 변수의 역할은 프로그램 내에서변수가 하고자 하는 바를 나타낸다.

코드를 이해하려고 할 때 사용하는 인지적 능력은 자연언어를 읽을 때 사용하는 것과 유사하다.

코드를 이해하려고 할 때 사용하는 인지적 능력은 자연언어를 읽을 때 사용하는 것과 유사하다. 이것은 곧 자연언어 텍스트 이해에 대한 연구에서 얻은 통찰을 코드 읽기에도 적용할 수 있다는 것을 의미한다.
효과적인 읽기 전략과 그 학습법에 대한 연구가 지금까지 많이 이루어졌다. 텍스트 이해에 대한 전략은 대략 다음과 같은 7개의 범주로 나뉜다.
활성화 — 관련된 것들을 적극적으로 생각해서 이미 가지고 있는 지식을 활성화하는 것
모니터링 — 텍스트를 읽으면서 자신이 이해한 것(그리고 이해하지 못하는 것까지)을 관찰하고 기록하는 것
중요도 결정 — 텍스트에서 어느 부분이 중요한지 결정하는 것
추론 — 텍스트에서 명시적으로 주어지지 않은 사실을 유추하는 것
시각화 — 깊이 있는 이해를 위해 텍스트에 대한 도표를 만드는 것.
질문 — 텍스트에 대해 질문하는 것
요약 — 텍스트를 짧게 요약하는 것
코드를 읽는 것과 텍스트를 읽는 것 사이에 존재하는 인지적 유사성 때문에 자연언어를 읽기 위한 전략이 코드 읽기에서도 유용하다고 생각할 수 있다.

때때로 무언가를 배울 때, 이미 배운 지식은 다른 영역에서도 유용하다. 이것을 전이transfer라고 부른다.

LTM에 저장된 키워드 및 정신 모델이 코드를 이해하는 데 도움이 된다는 것을 앞서 배웠다. 때때로 무언가를 배울 때, 이미 배운 지식은 다른 영역에서도 유용하다. 이것을 전이transfer라고 부른다. 이러한 지식 전달은 이미 알고 있는 정보가 새로운 것을 하는 데 도움이 될 때 일어난다. 예를 들어 체커checkers를 이미 할 줄 아는 사람은 몇몇 규칙이 비슷하기 때문에 체스를 더 쉽게 배울 수 있다. 마찬가지로, 자바를 이미 알고 있다면 변수, 루프, 클래스, 메서드와 같은 기본 프로그래밍 개념을 이미 알고 있기 때문에 파이썬을 더 쉽게 배울 수 있다. 또한 디버거나 프로파일러와 같이 프로그래밍 작업을 하면서 습득한 기술 중 일부는 또 다른 프로그래밍 언어를 배울 때 유용하게 사용할 수 있다.
LTM에 저장된 프로그래밍 지식은 새로운 프로그래밍 개념을 배우는 데 두 가지 방식으로 도움이 될 수 있다. 첫째, 프로그래밍(또는 다른 과목)에 대해 이미 많이 알고 있다면 그것에 대해 더 많이 학습하는 것이 쉬워진다. LTM에 저장된 정보를 사용해서 새로운 내용을 쉽게 배우는 이 과정을 학습도중 전이transfer during learning라고 부른다.

여기서 중요한 점은 하나의 프로그래밍 언어를 숙달했다는 사실이 새로운 언어를 배우는 데 항상 도움이 되는 것은 아니라는 것이다.

여기서 중요한 점은 하나의 프로그래밍 언어를 숙달했다는 사실이 새로운 언어를 배우는 데 항상 도움이 되는 것은 아니라는 것이다. 이미 자신을 전문가로 생각하고 초보자의 느린 속도나 문법을 배우기 위해 플래시카드를 사용하는 것 같은 초보자의 학습 활동이 자신에게는 불필요하다고 느끼는 사람에게는 이러한 사실이 당황스러울지도 모르겠다. 이와 관련하여 고려해야 할 조언은 사고방식을 확장하기 위해 새로운 언어를 배우기 시작했다면 이미 습득한 언어와는 근본적으로 다른 언어를 선택하는 것이 중요하다는 것이다. 즉 ‘컨트리 음악에서 ‘웨스턴 음악으로 자신의 취향을 잘못 넓히는 것을 피해야 한다.
그러나 이 섹션에서 이미 살펴본 대로 SQL에서 자바스크립트로의 원거리 전이는 일어날 가능성이 낮으며, 새로운 프로그래밍 언어에서도 전문가 수준을 갖추려면 새로운 전략뿐만 아니라 새로운 문법도 많이 배워야 한다. 예를 들어 자바스크립트에서의 재사용과 추상화에 대해 알고 있는 많은 사실을 SQL에서는 다르게 생각해야 한다.
공통점과 차이점에 의식적으로 주의를 기울이면 새로운 언어를 배우는 일이 쉬워질 것이다.

넷스케이프의 프로그래머 필 칼튼Phil Karlton은 컴퓨터 과학에는 난제가 딱 두 가지 있는데, 바로 캐시 무효화와 이름 짓기라는 유명한 말을 했다.

이름을 짓는 것은 중요하고 또 매우 어렵다. 해결책을 모델링하거나 또는 문제를 해결하는 과정에서 이름이 생성되는 경우가 많다. 이러한 활동 중에는 인지 부하가 높을 수 있다. 즉 작업 기억 공간이 정신 모델을 만들고 이 정신 모델을 사용해 추론하기 위해 부하가 최대로 올라가 있는 상태가 된다. 이런 상황에서 좋은 변수 이름을 생각하는 것은 과도한 인지 부하를 일으킬 수 있고, 두뇌는 이런 상태를 피하려고 한다. 이와 같이 작업 기억의 용량을 초과하지 않도록 쉬운 이름을 선택하는 것이 인지적 관점에서 타당하다.
이 장에서는 명명의 중요성뿐만 아니라 어려움에 대해서도 논의할 것이다. 명명 및 인지 처리의 기본적인 것을 다루고 나서, 프로그래밍 관점에서 두 가지 관점을 자세히 다룰 것이다. 먼저 어떤 종류의 이름이 코드를 이해하기 쉽게 하는지를 살펴본 후에, 나쁜 이름이 버그의 발생에 미치는 영향을 살펴볼 것이다. 마지막으로 좋은 이름에 대한 구체적인 지침을 제시하고 이 장을 마친다.
8.1 이름이 중요한 이유
좋은 변수 이름을 고르는 것은 어렵다. 넷스케이프의 프로그래머 필 칼튼Phil Karlton은 컴퓨터 과학에는 난제가 딱 두 가지 있는데, 바로 캐시 무효화와 이름 짓기라는 유명한 말을 했다. 그리고 실제로 많은 프로그래머는 이름을 짓는 것에 어려움을 겪는다.
클래스나 자료구조가 수행하는 모든 작업을 모호하지 않은 하나의 단어로 표현하는 것은 쉽지 않은 작업이다. 예루살렘 히브리 대학교의 전산학과 교수 드로 페이텔슨Dror Feitelson은 모호하지 않은 이름을 생각해내는 것이 얼마나 어려운지 보기 위해 한 가지 실험을 했다. 페이텔슨은 거의 350명의 피실험자들에게 여러 가지 다른 종류의 프로그래밍 작업에서 이름을 선택하는 실험을 수행했다. 대상은 평균 6년의 업무 경험을 가진 학생 및 프로그래밍 전문가들이었다. 참가자들은 변수, 상수, 자료구조의 이름과 함수 및 함수의 인수의 이름을 선택해야 했다. 페이텔슨은 실험을 통해 이름을 짓는 것이 어려운 일이고, 이름을 짓더라도 다른 사람들과 같은 이름을 선택하는 것이 어렵다는 것을 확인했다. 실험에서 두 개발자가 같은 이름을 선택할 확률은 낮았다. 전체적으로, 이름을 지정해야하는 47개 개체(변수, 상수, 자료구조, 함수, 매개변수 등)에 대해 두 사람이 동일한 이름을 선택한 경우는 7%에 불과했다.
이름 짓는 일은 어렵지만, 코드에서 우리가 추론하는 객체에 맞는 이름을 고르는 것은 중요하다. 뇌의 명명 과정과 인지 과정 사이의 연관성에 대해 살펴보기 전에, 왜 명명 과정이 중요한지 살펴보자.

잘못된 명명 방식은 단지 읽고이해하고 유지 보수하기 어려운 코드가 아니라 잘못된 코드일 가능성이 높다.

그런 다음 버틀러는 잘못된 이름의 위치를 파인드버그FindBugs(정적 분석을 사용해 잠재적 버그 위치를찾는 툴)로 찾은 버그의 위치와 비교했다. 흥미롭게도, 버틀러는 명명 문제와 코드 품질 사이에 통계적으로 유의미한 연관성을 발견했다. 버틀러의 연구 결과에 따르면 잘못된 명명 방식은 단지 읽고이해하고 유지 보수하기 어려운 코드가 아니라 잘못된 코드일 가능성이 높다.
물론 버그 위치와 나쁜 이름의 위치 간의 상관관계가 반드시 둘 사이의 인과관계를 의미하지는않는다. 버그와 좋지 않은 이름 모두 초보 프로그래머 혹은 실력 없는 개발자가 작성한 코드의 결과일 수도 있다. 복잡한 문제를 해결하느라 버그가 발생할 수도 있다. 이러한 복잡한 문제는 다른 방식으로 명명 오류와 관련이 있을 수 있다. 앞에서 논의했듯이 코드를 작성할 때는 어려운 문제를 해결해야 하기 때문에 프로그래머의 인지 부하가 매우 높았을 수도 있다. 코드의 도메인이 복잡해 좋은 이름을 떠올리기 어렵고, 적합한 이름을 찾기가 너무 복잡하고 혼란스러워 잘못된 이름을 지었을 가능성도 있다.
따라서 잘못된 이름 문제를 해결하는 것이 반드시 버그를 고치거나 예방하지는 않지만, 코드베이스를 검사하여 잘못된 이름이 발생하는 위치를 찾아내는 일은 코드를 개선하고 버그 발생 가능성이있는 위치를 찾는 데 도움이 될 수 있다. 이것이 바로 코드에서 나쁜 이름을 찾아야 하는 또 다른이유다. 이름을 개선하면 간접적으로 버그가 줄어들거나, 더 나은 이름을 사용한 코드는 이해하기쉽기 때문에 최소한 수정 시간이 단축될 수 있다.
8.5 더 나은 이름을 선택하는 방법
지금까지 잘못된 이름을 사용하면 코드에 미치는 영향이 심각하고, 코드 이해를 어렵게 하며, 버그발생 가능성도 높아진다는 것을 살펴봤다. 앞서 이름을 선택하는 것에 관한 연구에서 언급했던 페이텔슨은 개발자들이 어떻게 더 나은 이름을 선택할 수 있을지에 관해서도 연구했다.
8.5.1 이름 틀
페이텔슨은 설문 조사에서 개발자들에게 변수 이름을 선택하도록 했는데, 개발자들이 변수에 대해같은 이름을 선택하는 경우는 드물지만 다른 개발자들이 선택한 이름을 이해는 한다는 것을 발견했다. 페이텔슨의 실험에서 대부분의 개발자는 다른 개발자들이 선택한 이름을 이해했다. 모순처럼보이는 이러한 현상의 이유는 페이텔슨이 이름 틀name mold이라고 불렀던 개념을 개발자들이 사용하기 때문이다.

산소가 들어간 혈류량이 기존 프로그램을 읽을 때보다 복잡한 프로그램을 읽을 때 더 큰 것으로 나타났다.

2014년 일본 나라 선단과학기술대학원대학 연구원인 나카가와 다카오中川上는 웨어러블 INIRS 기기를 사용해 프로그램을 이해하는 동안의 뇌 활동을 측정했다. 나카가와의 실험 참가자들은 C로 작성된 알고리즘의 두 가지 버전을 읽었다. 한 버전은 원래 작성된 일반적인 코드였고, 두 번째 버전은 의도적으로 복잡하게 만든 것이었다. 예를 들어 루프 카운터 및 기타 값이 변경되어 변수가 불규칙적으로 자주 수정된다. 하지만 프로그램의 기능은 동일했다.
참가자들에게 INIRS 머리띠를 착용시키고 프로그램의 원본과 복잡한 버전을 각각 제시했다. 참가자들이 쉬운 버전에서 학습하여 더 복잡한 프로그램으로 학습 내용을 전이할 가능성을 배제하기 위해 순서는 임의로 했다. 즉 일부 참가자는 원본을 먼저 본 반면 다른 참가자는 수정된 프로그램을 먼저 봤다.
나카가와의 연구 결과는 참가자 10명 중 8명의 경우, 산소가 들어간 혈류량이 기존 프로그램을 읽을 때보다 복잡한 프로그램을 읽을 때 더 큰 것으로 나타났다. 이 결과는 INIRS 기기를 사용해 뇌혈류를 측정하면 프로그래밍 중 발생하는 인지 부하를 정량화할 수 있음을 시사한다.

판솔링언은 두 개의 다른 조직을 연구했고 두 조직 모두에서 놀라울 정도로 유사한 결과를 얻었다. 그가 발견한 바에 의하면 업무 중단이 두 조직 모두 흔히 일어났고 중단된 시간은 각각 15~20분이었다. 개발자의 업무 시간의 약 20%가 업무 중단에 쓰인다. 슬랙 등 메시징 앱의 사용이 증가하면서, 오늘날에는 업무 중단이 더 일반적인 현상이다.

언어적 안티패턴을 갖는 코드(예: 코드구현과 이름이 일치하지 않음)는 오류가 발생하기 쉽고 인지 부하가 커진다.

일관성
사람들이 프로그래밍 언어나 코드베이스와 어떻게 상호작용하는지 조사하는 또 다른 방법은 일관성consistency이다. 비슷한 것들은 서로 얼마나 유사한가? 이름은 항상 동일한 방식으로 만들어지는가?예를 들어 8장에서 논의한 것과 동일한 이름 틀을 사용하여 만들어지는가? 코드 파일의 레이아웃이 다른 클래스와 유사한가?
많은 프로그래밍 언어에서 일관성이 있는지 알 수 있는 한 가지 예로 함수 정의가 있다. 이런 생각을 해본 적이 없겠지만 내장 함수는 일반적으로 사용자 정의 함수와 동일한 사용자 인터페이스를가지고 있다. print() 또는 print_customer() 같은 함수를 호출하는 라인만 봐서는 함수를 작성한 사람이 프로그래밍 언어의 창시자인지 아니면 코드 작성자인지 알 수 없다.
이름과 규약을 일관성 없이 사용하는 프레임워크나 언어는 인지 부하를 더 많이 가져올 수 있다. 두뇌가 그것이 무엇에 대한 것인지 이해하는 데 더 많은 에너지를 소모하고, 관련 정보를 찾는 데 더많은 시간을 쓰기 때문이다.
일관성은 9장에서 보았던 것처럼 오류 경향성과 관련이 있다. 언어적 안티패턴을 갖는 코드(예: 코드구현과 이름이 일치하지 않음)는 오류가 발생하기 쉽고 인지 부하가 커진다.

여러분이 할 수 있는 첫 번째이자 가장 중요한 것은 교육을 받는 사람들의 인지 부담을 의도적으로 관리하는 것이다.

 

Comments