1. 프로그래밍이란?
프로그래밍이란 컴퓨터에게 실행을 요구하는 일종의 커뮤니케이션이다. 이를 위해 먼저 무엇을 실행하기 원하는지에 대한 정의가 필요하다. 다시 말해, 프로그래밍에 앞서 문제(요구사항)를 명확히 이해한 후 적절한 문제 해결 방안의 정의가 필요하다.
이때 요구되는 것이 문제 해결 능력이다. 혹자는 문제 해결 능력을 알고리즘과 동일시하려는 경향이 있지만 반드시 그런 것은 아니다. 물론 문제 해결 능력의 함양에 있어 알고리즘 학습은 큰 도움이 되지만 문제 해결 능력은 더 큰 차원의 능력이다.
대부분의 문제(요구사항)는 복잡하며 명확하지 않을 수도 있다. 따라서 문제(요구사항)를 명확히 이해하는 것이 우선되어야 하며 복잡함을 단순하게 분해(Decomposition)하고 자료를 정리하고 구분(Modeling)해야 하며 순서에 맞게 행위를 배열해야 한다.
즉, 프로그래밍이란 0과 1밖에 알지 못하는 기계가 실행할 수 있는 정도로 정확하고 상세하게 요구사항을 설명하는 작업이며 그 결과물이 바로 코드이다. 모호하고 대략적인 요구사항을 전달해도 우리의 머리 속에 있는 의도를 정확히 꿰뚫어 완벽히 이해하는 컴퓨터는 절대 존재할 수 없다.
우리는 문제 해결 방안을 고려할 때 컴퓨터의 입장에서 문제를 바라보아야 한다. 이때 필요한 것이 Computational thinking이다. 사람의 일반적인 사고 방식은 매우 포괄적이며 실생활에서 경험하고 있는 익숙한 사항에 대해 당연시하는 안이한 인식이 있다.
문제 해결 능력은 직감과 직관의 영역이라고 볼 수 있는데 이는 문제를 바라보는 우리의 사고와 경험에 영향을 받는다. 예를 들어 “듣다(Listen)”라는 행위를 사람은 하나의 간단하고 당연한 기능으로 생각한다. 하지만 컴퓨터에게 이 행위를 설명하는 것은 단순하지 않다. 그리고 사람은 소리의 크기를 “크다” 또는 “작다”고 표현한다. 하지만 크다 또는 작다는 의미는 상대적인 개념으로 기준이 불명확하다. 컴퓨터에게는 양적 개념인 숫자를 사용하여 “현재 볼륨보다 1단계 크게 조정하라” 또는 “볼륨을 60으로 조정하라”고 명령해야 한다. 또한 “좋다”, “붉다”, “사랑”과 같은 관념적 개념은 컴퓨터에게 매우 난해한 개념이다. 사람은 지인의 얼굴을 보고 누구인지 바로 인지하지만 컴퓨터에게 이것은 매우 어려운 일이다. 3479의 계산은 사람에게는 쉽지 않지만 컴퓨터에게는 매우 쉬운 작업이다.
이처럼 컴퓨터와 사람은 사고, 인지의 방식이 다르다. 따라서 해결 과제를 컴퓨터의 관점으로 사고(Computational thinking)해야 한다. 이에는 논리적, 수학적 사고가 필요하게 되며 해결 과제를 작은 단위로 분해하고 패턴화하여 추출하며 프로그래밍 내에서 사용될 모든 개념은 평가 가능하도록 정의되어야 한다.
예를 들어 사람처럼 두발로 걷는 로봇을 위해 “걷다”라는 기능을 디자인해 보자.
2족 보행 로봇의 디자인
“걷다”라는 기능을 디자인하려면 판단하여야 하는 상태와 그 상태를 판단하는 시기, 그리고 판단 기준을 정의하여야 하며 이를 바탕으로 분해한 처리(Process)의 실행 여부를 결정한다. 예를 들어 장애물이란 무엇(크기, 움직임…)인지 어떤 범위 내에 있는 것인지 명확히 수치화하여 정의해야 한다.
2. 프로그래밍 언어
이와 같이 문제 해결 능력을 바탕으로 정의된 문제 해결 방안은 컴퓨터에게 전달되어야 한다. 이때 명령을 수행할 주체는 컴퓨터이다. 따라서 인간이 이해할 수 있는 자연어가 아니라 컴퓨터가 이해할 수 있는 언어, 즉 기계어(Machine code)로 명령을 전달해야 한다.
인간이 기계어를 이해하여 직접 기계어로 명령을 전달하는 것은 매우 어려운 작업이다. 기계어는 우리가 사용하는 언어와는 너무나도 다른 체계를 가지기 때문이다. 심지어 비트 단위로 기술되어 있다.
아래는 x86 아키텍처 리눅스 환경에서 그 유명한 “Hello world”를 출력하는 기계어 코드이다.
7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 00 02 00 03 00 01 00 00 00 35 40 B3 04 2C 00 00 00 00 00 00 00 00 00 00 00 34 00 20 00 01 00 00 00 00 00 00 00 00 40 B3 04 B2 0C EB 1C 62 00 00 00 62 00 00 00 05 00 00 00 00 10 00 00 48 65 6C 6C 6F 20 77 6F 72 6C 64 0A B9 4C 40 B3 04 93 CD 80 EB FB
직접 기계어로 명령을 전달하는 것을 대신할 가장 유용한 대안은 인간이 이해할 수 있는 약속된 구문(Syntax, 문법)으로 구성된 “프로그래밍 언어(Programming Language)”를 사용하여 프로그램을 작성한 후, 그것을 컴퓨터가 이해할 수 있는 기계어로 변환하여 주는 일종의 번역기를 이용하는 것이다. 이 일종의 번역기를 컴파일러(compiler) 혹은 인터프리터(interpreter)라고 한다.
컴파일러는 개발자의 언어와 컴퓨터의 언어를 모두 이해하는 번역가
언어(Language)는 자신의 생각을 상대에게 전달하는 방법으로 언어 공동체 내에서 이해될 수 있는 말의 집합이다.
Communication Model
언어는 자연어와 인공어로 구분할 수 있다. 프로그래밍 언어란 컴퓨터와의 대화(명령)에 사용되는 일종의 표현 수단으로 인간과 컴퓨터(컴파일러 또는 인터프리터) 모두가 이해할 수 있는 약속된 형태의 인공어이다.
아래는 “Hello world”를 출력하는 자바스크립트 코드이다. 위의 기계어 코드보다 인간이 이해하기 쉬운, 즉 읽기 쉬운 코드가 되었다.
console.log('hello world');
프로그래밍은 프로그래밍 언어를 사용하여 컴퓨터에게 실행을 요구하는 일종의 커뮤니케이션이다. 프로그래밍 언어는 Syntax(구문)와 Semantics(의미)의 조합으로 표현된다.
3. Syntax & Semantics
프로그래밍 학습은 일반적으로 프로그래밍 언어의 문법을 배우는 것부터 시작한다. 이는 외국어 학습과 유사하다고 할 수 있다. 그러나 이미 경험을 통해 알고 있겠지만 문법을 잘 안다고 해서 외국어를 잘한다고 말할 수는 없다.
외국어를 잘하려면 외국어 화자의 말이나 문장을 정확히 이해한 후, 문맥에 따른 적절한 어휘 선택, 그리고 순차적으로 결론을 향해 나아가는 문장 구성이 필요하다. 즉, 문법에 맞는 문장을 구성하는 것은 물론 의미(Semantics)를 가지고 있어야 언어의 역할을 충실히 수행할 수 있다.
Colorless green ideas sleep furiously.
– 노엄 촘스키(Noam Chomsky)
MIT의 저명한 언어학자인 노엄 촘스키는 위 문장을 통해서 언어의 의미는 문맥에 있는 것이지 문법에 있는 것이 아니란 것을 지적하고 있다. 위 문장은 문법(Syntax)적으로 전혀 문제가 없지만 의미(Semantics)는 없다. 프로그래밍도 마찬가지다. 아래 예제를 보자.
const number = 'string';
console.log(number * number); // NaN
자바스크립트의 변수에는 어떠한 타입의 값이라도 할당할 수 있다. 따라서 위 예제는 문법적으로 전혀 문제가 없다. 하지만 의미적으로는 옳지 않다. number라는 변수 이름에 문자열이 할당되어 있기 때문이다. number라는 변수 이름에는 숫자를 할당하는 것이 의미적으로 옳다.
결국 문제 해결 능력을 통해 만들어낸 해결 방안은 프로그래밍 언어의 문법을 통해 표현한다. 즉, 작성된 코드는 해결 방안의 구체적 구현물이다. 그리고 이것은 프로그래밍 언어의 문법에 부합하는 것은 물론이고 수행하고자 하는 바를 정확히 수행하는 것, 즉 요구사항이 실현(문제가 해결)되어야 의미가 있다.
프로그래밍의 목적은 문제 해결이다.
대부분의 프로그래밍 언어는 “변수와 값”, “키워드”, “연산자”, “표현식과 문”, “조건문”과 “반복문”에 의한 “흐름제어(Control flow)”, “함수” 그리고 “객체”, “배열” 등의 “자료구조”와 같은 문법을 제공한다.
프로그래밍 언어가 제공하는 문법을 적절히 사용하여 변수를 통해 값을 저장하고 참조하며 연산자로 값을 연산, 평가하고 조건문과 반복문에 의한 흐름제어로 코드의 실행 순서를 제어하고 함수로 재사용이 가능한 문의 집합을 만들며 객체, 배열 등으로 자료를 구조화한다.
결국 프로그래밍은 요구사항의 집합을 분석하여 적절한 자료구조와 함수의 집합으로 변환한 후, 그 흐름을 제어하는 것이다.
4. 기본 개념과 동작 원리 이해의 중요성
프로그래머가 해야 할 일은 문제를 해결하기 위한 방안을 고안하고 이것을 문법에 맞게 코드로 구현하는 것이다. 구현된 코드는 의도한 대로 정확히 동작하여 문제를 해결해야 한다. 이때 자신이 구현한 코드가 컴퓨터 내부에서 어떻게 동작할 것인지 그리고 무엇을 돌려 줄 것인지 예측 가능해야 하며 이것을 동료에게 명확히 설명할 수 있어야 한다.
이를 위해서는 프로그래밍 언어의 기본 개념과 동작 원리를 정확히 이해하는 것이 중요하다. 기본 개념과 동작 원리를 이해하지 못한 상태에서 Copy & Paste로 단순히 동작만 하는 코드를 만들고 그것에 만족한다면 여러분이 구현한 코드는 언제 무너져도 이상할 것이 없는 사상누각일 뿐이다. 신뢰할 수 없고 유지하고 보수하기 까다로운 코드가 될 것이다. 그리고 문제 해결 능력은 어느 선에서 성장을 멈추고 말 것이다.
기본 개념은 문맥에 맞는 정확한 용어를 사용할 수 있게 돕는다. 문맥에 맞는 정확한 용어의 사용은 오해를 불러 일으키지 않는 명확한 의사 소통을 가능케 한다. 이는 협업의 기본이며 필수 요소이다.
동작 원리의 이해는 코드의 동작을 예측할 수 있게 돕는다. 요구 사항을 코드로 구현하려면 당연히 자신이 작성하는 코드의 동작을 예측할 수 있어야 한다. 동작이 예측되지 않는 코드를 작성한다는 것은 말이 되지 않는다. 또한 에러를 발생시키는 코드를 만나면 에러가 발생하는 원인을 이해해야 디버깅이 가능하다. 이를 위해 코드의 동작을 예측할 수 있는 능력은 필수 불가결적 요소이다.
즉, 기본 개념과 동작 원리의 이해는 어렵고 생소한 용어들로 이루어진 기술적 의사소통을 가능케하고, 자신의 머리 속에서 코드를 실행시켜 볼 수 있는 능력을 갖게 한다. 이를 통해 다른 사람이 작성한 코드를 이해하는 것은 물론 의도 또한 파악할 수 있게 되어 보다 효과적인 코드를 생산할 수 있는 기본기를 쌓을 수 있다. 기본기는 아무리 강조해도 지나침이 없다.
빨리 가는 유일한 방법은 제대로 가는 것이다.
- 로버트 C. 마틴(Robert C. Martin), “클린 코드”의 저자