English Version

이 글은 “Embracing the Four Python Programming Styles”1이라는 블로그 글에 영감받아 작성되었습니다.

프로그래밍 패러다임은 프로그램 언어의 특성에 따라 절차적(procedural), 객체지향형(object oriented), 함수형(functional), 논리형(logic-based) 등으로 나누는 개념이며 어느 패러다임이 어느 패러다임보다 좋은지에 대한 논의는 항상 많은 사람적의 관심과 함께 밤새 토론해도 끝나지 않을 내용입니다2. 참고로 파이썬은 매우 다양한 프로그래밍 패러다임을 지원하는 언어입니다. 기본적인 절차지향형 및 객체지향형만이 아니라 함수형까지도 언어에서 기본적으로 지원합니다3. 그리고 이러한 자유도 때문에, 파이썬을 사용하다 보면 자연스래 “어떤 프로그래밍 패러다임을 이용해야 맞는것인가?”라는 질문에 맞닥드리게 됩니다. 짦게 결과만 놓고 본다면 “어떤 패러다임으로 구현해도 모두 괜찮다”입니다. 하지만, 각 패러다임의 단점만 잘 모아서 개발하는 불상사를 피하기 위해 각 패러다임의 장단점에 대해 한번 집고 넘어갈 필요가 있습니다.

절차적 (procedural)

절차적 형식은 명령형(imperative) 프로그래밍의 한 종류로써 연속적인 계산 절차로 이루어진 프로시저(procedure)를 기반으로하고 있습니다4. 기본적으로 개발자는 프로시저의 조합을 통해 컴퓨터에게 특정 결과를 얻기 위해 어떠한 절차를 진행해야 하는지 명령을 하게 됩니다. 프로시저는 보통 함수(function) 정의문을 통해 모듈화 되어있으며 함수는 다른 개발자들이 손쉽게 사용할 수 있는 인터페이스를 갖고 있습니다 (함수의 이름과 인수(argument)만을 통해 사용가능). 덕분에 개발자는 여러군데에 흩어져있는 각 모듈을 조합해서 프로그램을 만들어낼 수 있습니다. 하지만 각각의 함수는 다른 함수들과 연관성에 대한 정보가 아예 없어 아무렇게나 방치할 수 있기 때문에 개발자들이 알맞은 프로시저를 찾기 위해 던전을 발굴해야 하는 상황을 만들어냅니다 (덕분에 같은 프로시져가 여기저기에 만들어져 있는 광경도 목격할 수 있습니다). 이러한 문제를 해결하기 위해 각 프로그래밍 언어들은 관련있는 여러 함수 (및 변수)들을 하나로 모을 수 있는 일종의 컨테이너들을 지원합니다. 파이썬의 경우에는 각 파일이 하나의 컨테이너로 작동해서 (파이썬에서 모듈이라고 불리움) 개발자들이 손쉽게 관련된 내용들을 정리할 수 있게 도와줍니다.

절차적 방식으로 리스트 값들의 총합을 구하는 로직은 아래와 같이 만들 수 있습니다:

new_lst = [1, 2, 3, 4]
def sum_list(lst):
    result = 0
    for value in lst:
        result += value
        return result
print(sum_list(new_lst))

객체지향형 (objected oriented)

객체지향형 형식 역시 명령형 프로그래밍의 한 종류이지만, 절차적과는 다르게 객체(object)라는 개념이 존재합니다. 객체지향형이라는 이름에서는 유추하기 힘들지만 영문이름인 objected oriented라는 이름이 나타내듯 객체가 모든 것의 중심이 됩니다. 이 개념은 캡슐화(encapsulation)이라고 불리우며, 이는 각 데이터와 행동(메세드 - method)들이 각각의 인스턴스(객체)에 포함되게 합니다. 캡슐화를 통해 객체지향형 방식은 자연스럽게 관련있는 데이터와 메서드들을 한곳으로 뭉치게 만들어주어 절차적 언어에서 (어느정도) 어려웠던 모듈화를 손쉽게 할 수 있도록 도와주었습니다. 또한 상속(inheritance)이라는 개념을 통해 여러 클래스(객체를 생성하는데 쓰이는 틀)에서 공통적으로 사용하는 데이터나 메서드들을 한번만 정의할 수 있는 특징도 갖고 있습니다. 객체지향형의 큰 단점 중 하나는 프로그램이 매우 쉽게 복잡해질 수 있다는 점입니다. 객체지향형은 우리 주변에서 쉽게 볼 수 있는 생물 분류 개념(포유류, 양서류 등)을 따라 만든것이지만, 개발을 하다 보면 생각보다 모든 것을 특정 분류로 구분하는 것이 쉽지 않을 뿐더러 프로그램을 손쉽게 복잡하게 만듭니다.

리스트 값들의 총합을 구하는 같은 기능을 객체지향형으로 만들면 아래와 같이 만들 수 있습니다:

class ListEvaluator:
    def __init__(self, lst):
        self.holding_list = lst
    def sum_list(self):
        result = 0
        for value in self.holding_list:
            result += value
        self.list_sum = result
evaluator = ListEvaluator([1, 2, 3, 4])
evaluator.sum_list()
print(evaluator.list_sum)

함수형 (functional)

함수형 패러다임은 위의 두 형식과는 좀 다릅니다. 함수형은 선언형(declarative) 언어라고 분류되며, 이 방식은 명령형이 명령문(statement)로 이루어진 것과 다르게 표현식(expression)과 선언문(declaration)으로만 이루어져 있습니다 (표현식은 처리되면 하나의 값으로 나오는 형태를 갖고 있으며, 명령문은 명령형에서 하나의 라인으로 표현될 수 있는 모든 문을 의미함). 수학적 함수 개념을 따라 만든 형식으로 변하는(mutable) 상태(state)와 값(data)을 쓰지 않습니다 (명령형의 경우 상태를 변경하는 명령으로 이루어져 있음)5. 모든게 불변하기(immutable)하기 때문에 (순수 함수형으로 구현할 경우) 부작용(side-effect)이 존재하지 않습니다. 만약 프로그램에서 한 함수의 결과값이 이 함수의 밖에 존재하는 특정 요인으로 인해 변화한다면 이 프로그램은 부작용이 존재한다고 표현합니다 6. 예를 들어 객체지향형 언어에서 특정 메서드가 인스턴스의 속성(attribute)에 따라 결과값이 달라지고, 이 속성이 다른 메서드에 의해 값이 변하게 된다면, 이 메서드는 부작용이 존재합니다. 반면에 함수형은 부작용이 존재하지 않기 때문에 프로그램을 직관적으로 만들어서 내용을 이해하는 것과 디버깅을 쉽게 할 수 있습니다. 하지만 컴퓨터 자체가 명령형 패러다임처럼 만들어졌기 때문에 (하드웨어 처리 자체가 변화하는 상태와 명령어로 이루어져 있음) 프로그램을 순수한 함수형으로만 구현하는게 불가능 합니다 (적어도 I/O나 메모리 처리는 함수형으로 처리 불가능). 또한 명령형 언어가 몇십년간 주류로 사용되어왔기 때문에 선언형 언어를 쓰는 것이 매우 어렵습니다 (개인적인 경험으로 새로운 패러다임을 쓰려면 뇌를 포맷시킨 후 새로운 OS를 설치해야 하는 것과 같은 느낌을 받았습니다).

함수형 형식으로 리스트 값들의 총합을 구하는 방법입니다:

new_lst = [1, 2, 3, 4]
def sum_list(lst):
    # len 함수가 순수 함수형으로 구현되어있는지는 모르겠으나, 기본적인 컨셉을 설명하기 위해서...
    if len(lst) == 1:
        return lst[0]
    else:
        return lst[0] + sum_list(lst[1:])
print(sum_list(new_lst))

# 고차함수(higher order function)을 이용해 순수 함수형으로 구현하면
import functools
print(functools.reduce(lambda x, y: x + y, new_lst))

마치며

지금까지 알아본 바와 같이 각각의 프로그래밍 패러다임은 장점과 단점이 존재합니다. 또한 개인적으로 특정 패러다임의 단점은 소프트웨어 구조를 잘 설계한다면 충분히 극복이 가능한 영역이라고 생각합니다. 결론적으로, 파이썬에서 어떤 패러다임을 쓰는 것이 더 좋은가에 대한 물음은 큰 의미가 없습니다. (적어도 우리가 쓸 수 있는 대부분의 형식을 파이썬에서 지원해주는 만큼) 여러 패러다임을 적절히 섞어 써도 되고 아예 가장 편한 방식 하나만을 쓰는 것 또한 괜찮습니다. 따라서 더 뛰어난 프로그래머가 되기 위해서는 어떤 패러다임을 써야하는가에 대한 고민보다, 같은 코드를 작업하는 다른 프로그래머들을 위해 1. 문서화 2.가독성 높은 코드, 그리고 3. 예의 바른 커뮤니케이션에 더더욱 신경쓰는게 좋을 것 같습니다.