프로그래밍 패러다임의 변화
프로그래밍 패러다임이란, "프로그램을 작성하는 전형적인 방식"을 말한다. 프로그래밍 패러다임은 특정 프로그래밍 언어에 종속적인 것이 아니라, 프로그래머가 추구하는 프로그램 작성 방식에 따라 다르게 나타난다. 그럼에도 프로그래밍 언어가 자연스럽게 지원하는 프로그래밍 패러다임은 정해져 있다.
프로그래밍 패러다임 전환은 컴퓨팅 환경이 변화함에 따라 자연스럽게 진행되었다. 프로그래밍 패러다임은 단순히 프로그램에 대한 요구사항이 바뀐다고 즉각적으로 바뀌지 않는다. 프로그래밍 패러다임의 변화를 주도한 원인들이 있다. 프로그래밍 패러다임의 변화의 배경은 다음과 같다.
- 응용 도메인(Application Domain): 요구사항의 변화와 비슷한 개념으로, 초기 컴퓨터 프로그램의 요구사항이 "계산 분야" 였으나, 컴퓨터 분야가 발전하며 "데이터베이스 관리", "임베디드 시스템" 등 아주 다양한 응용 분야에 컴퓨터가 사용되었고 그에 따라 프로그램에 대한 요구사항도 달라지게 되었다.
- 프로그램 구성 방식(Program Composition Methods): 초기에는 문제 해결을 위한 명령어의 나열을 프로그램으로 생각했으나, 프로그램을 모듈로 인식하는 경향이 강해져서 모듈을 조합하여 전체 응용 프로그램을 구성하는 방식이 주요한 프로그램 구성 방식이 되었다. 이로 인해 프로그래밍 패러다임이 변화하였다.
- 계산 모델(Computational Model): 컴퓨터가 개발된 이래로 폰 노이만 방식의 컴퓨터 상에서 수행되는 "튜링기계 모델"이 주류 계산 모델로 자리잡게 되었고 사람이 이해하기 쉬운 방식으로 프로그램을 작성하고자 하는 욕구가 늘어났다. 따라서 새로운 방식의 계산 모델을 고려하게 되었고 "재귀 함수론을 계산 모델로 삼은 함수형 패러다임", "연역 추론을 계산 모델로 삼은 논리 패러다임" 등이 등장하게 되었다.
프로그래밍 언어 패러다임
프로그래밍 패러다임은 프로그램을 작성하는 방식을 말한다. 반면, 프로그래밍 언어 패러다임은 "어떤 프로그래밍 패러다임을 해당 프로그래밍 언어가 지원하는가? 에 대한 것"이다. 프로그래밍 언어가 특정 패러다임을 주로 지원하여도 일반적으로 프로그래밍 언어는 여러 프로그래밍 패러다임을 지원한다. 예를 들어, Python은 명령형 패러다임과 절차형 패러다임을 지원하였으나, 새로운 버전이 발표되며 함수형 패러다임, 객체지향 패러다임을 포함하는 형태로 발전하였다.
즉, 한 언어가 지원하는 프로그래밍 패러다임은 여러가지일 수 있으며, 여러 프로그래밍 패러다임은 양립할 수 있다.
여러 가지 패러다임
지금부터 다양한 프로그래밍 패러다임에 대해서 구체적으로 알아보도록 하겠다.
명령형 프로그래밍 (Imperative programming)
초기 프로그래밍 환경은 기계어를 사용하는 것이었기에, 초기 언어는 CPU의 명령어 집합을 그대로 차용하는 경우가 많았다. 명령형 프로그래밍 패러다임에서는 프로그램을 "일렬로 나열된 명령어"로 간주한다. 매 단계 컴퓨터는 한 명령어를 수행하고, 필요에 따라서 특정 명령어를 수행하지 않을 수 있고 또한 특정 명령어 나열을 여러 번 수행할 수도 있다. 이러한 명령어 수행 여부, 횟수를 제어하기 위한 "흐름 제어 명령어(Control Command)"가 사용된다.
프로그램을 매우 쉽게 이해할 수 있다는 장점이 있으나, 프로그램이 복잡한 경우 효과적으로 다루기 힘들다. 현재도 여러 프로그래밍 언어의 기초가 되는 패러다임이다.
절차형 프로그래밍 (Procedural Programming)
명령형 프로그래밍에서 사용했던 명령어 나열인 "서브루틴(Subroutine)"이라는 절차를 통해 프로그램을 구성하는 방식이다. 특정 명령어 나열에 이름을 붙여 서브루틴으로 이름 지을 수 있다. 절차형 프로그래밍은 프로그램을 "절차 집합"으로 간주한다. 재귀 호출을 사용하여 프로시저를 간단하게 정의할 수 있다는 장점이 있다. 이때, 프로시저는 값을 반환하지 않는 서브루틴을 의미한다. (값을 반환하면 함수가 된다)
구조화 프로그래밍 (Structured Programming)
구조화 프로그래밍은 goto문 없이 프로그램을 작성하는 방법으로, 블록과 서브루틴을 활용하여 프로그램을 작성하는 방식이다. goto는 프로그램을 읽기 어렵게 만들고 프로그래머의 의도를 파악할 수 없게 난해하게 만들게 된다. 따라서 구조화 프로그래밍 방식에서는 goto문 대신 if문, while문과 같이 다른 문장을 포함하는 중첩된 제어문(제어 구조)을 이용한다.
중첩된 블록을 통해 프로그램을 작성하는 것이 구조화 프로그램의 가장 큰 특징이고, 최근 프로그래밍 언어의 대표적인 특징이다.
객체지향 프로그래밍 (Object-Oriented Programming)
객체지향 프로그래밍은 데이터와 관련 연산을 합쳐 객체로 모형화하고, 객체 사이의 상호작용을 통해 프로그램을 수행하는 프로그래밍 패러다임이다. 이때, 객체란 상태를 유지하고 있으며 외부의 요청에 반응하여 어떤 행위를 수행하는 데이터이다. 상태는 "필드(멤버 변수)"로 나타내고 행위는 "메소드(멤버 함수)"로 나타낸다. 이렇게 객체를 모형화할 경우 객체 사이의 상호작용을 나타내기 매우 유용하다. 또한 같은 부류의 객체는 "클래스"로 나타낼 수 있다. 클래스는 필드를 외부에 숨기고 메소드만 외부에 공개함으로 "정보은닉"과 "캡슐화"를 지원하며, 이는 프로그램의 재사용성을 매우 편리하게 하며, 소프트웨어 생산성에 크게 기여하였다.
함수형 프로그래밍 (Functional Programming)
함수형 프로그래밍에서는 데이터를 값으로 간주하고, 주어진 데이터로부터 새로운 값을 생성하는 함수에 초점을 맞추어 프로그램을 작성하는 방식이다. 명령어를 함수로 취급할 수 있기에 이점들이 있다. 다만, 명령어를 함수로 간주하기 위해서는 명령어가 데이터를 바꿀 수 없어야 한다. 따라서 함수형 언어에서는 "대입문"이 사라지게 되고 "=" 대입 연산자 기호는 어떤 변수의 값을 바꾸는 것이 아니라 어떤 값에 이름을 붙이는 의미로 사용된다. 대입문이 사라지면 "반복문"도 사라지기에 반복문 대신 "재귀호출"을 사용하게 된다. 재귀호출은 호출 오버헤드를 발생시키기에 일반적으로 함수형 언어의 수행 성능은 명령형 언어보다 떨어지게 된다.
함수형 언어에서는 함수 자체를 값으로 취급할 수 있기에 함수를 다루는 함수를 자연스럽게 사용할 수 있다. 함수를 다루는 함수를 "고계함수(고차함수, Higher-Order Function)"이라고 부른다. 고계함수를 통해 매우 간결한 코드를 작성할 수 있다. 그리고 함수형 언어에서는 별도의 함수 정의 없이 함수 값을 나타낼 수 있다. 이때 함수 값을 "함수 리터럴" 또는 "람다함수(Lambda Function)"이라고 한다. 람다함수는 주로 고계함수의 인수로 사용되는데 인수로 사용할 간단한 함수를 람다함수로 작성하면 아주 간편하다.
논리 프로그래밍 (Logic Programming)
논리형 프로그래밍은 "선언적 프로그래밍 패러다임(Declarative Programming)"의 일종으로, 문제의 조건을 논리식으로 표현하고 이 식을 바탕으로 어떤 사실을 입증해 나가는 방식으로 프로그램을 수행하는 방식이다.
즉, 논리 언어는 패턴 매칭과 재귀적 정의를 통하여 계산을 수행하고, 논리 언어 프로그램은 논리식 집합으로 구성된다. 논리식의 열거를 통하여 선언적 프로그래밍을 지원하는 것이다.
프로그래밍 언어와 프로그래밍 패러다임
프로그래밍 패러다임은 프로그래밍 언어와 더불어 발전하였다. 한 프로그래밍 패러다임에서는 자연스럽게 지원이 되지만, 다른 프로그래밍 패러다임에서는 복잡하게 구현될 수 있기에, 다양한 프로그래밍 패러다임은 상호 보완적이다. 특히 최근들어 프로그래밍 패러다임이 결합되는 형태를 보이기도 한다. 따라서 다양한 패러다임을 익혀 자신의 프로그래밍 사고를 넓히는 것이 중요하다.
위의 프로그래밍 패러다임 뿐 아니라 "관점지향 프로그래밍(Aspect-Oriented Programming)", "이벤트 구동 프로그래밍(Event-Driven Programming)", "반영지향 프로그래밍(Reflective Programming)" 등 다양한 프로그래밍 패러다임이 있으며, 이러한 프로그래밍 패러다임은 또 프로그래밍 언어에 영향을 끼칠 것이고 그에 따라 프로그래밍 언어와 패러다임은 발전할 것이다.