2025-08-09 10:16

Tags:

프로그래밍 패러다임 핸드북

1. 만들어진 이유: 문제 해결을 위한 다양한 접근 방식의 필요성

초기 컴퓨터 프로그래밍은 단순히 기계가 이해할 수 있는 명령어(기계어)를 나열하는 방식. 하지만 프로그램의 규모가 커지고 복잡해지면서 이런 방식은 한계에 봉착. 코드의 재사용이 어렵고, 수정이나 유지보수가 거의 불가능에 가까웠기 때문.

이를 해결하기 위해 사람들은 코드를 더 체계적으로 구성하고, 문제에 더 효과적으로 접근할 수 있는 ‘생각의 틀’을 고안. 이것이 바로 프로그래밍 패러다임의 시작.

  • 비유: 요리에 비유할 수 있음. 똑같은 재료(데이터)를 가지고도 한식 레시피(절차지향), 양식 코스요리 레시피(객체지향), 분자요리 레시피(함수형) 등 다양한 조리법(패러다임)에 따라 과정과 결과물이 달라지는 것과 같음. 각 패러다임은 특정 종류의 문제를 더 우아하고 효율적으로 해결하기 위해 만들어짐.

2. 구조: 주요 프로그래밍 패러다임의 종류와 핵심 개념

프로그래밍 패러다임은 크게 명령형 프로그래밍선언형 프로그래밍 두 가지로 나눌 수 있음.

가. 명령형 프로그래밍 (Imperative Programming)

  • ‘어떻게(How)’ 할 것인지를 명시하는 방식. 컴퓨터에게 수행할 명령을 순서대로 상세하게 지시.

  • 상태(State) 와 상태를 변경시키는 구문(Statement) 이 중심.

1) 절차지향 프로그래밍 (Procedural Programming)

  • 프로그램을 ‘절차(Procedure)’ 또는 ‘함수(Function)’ 의 연속으로 보는 관점. 데이터는 부차적.

  • 코드를 기능 단위로 묶어 재사용성을 높임.

  • 핵심 요소: 프로시저(함수), 전역 변수, 순차적 실행

  • 대표 언어: C, Pascal, Fortran

2) 객체지향 프로그래밍 (Object-Oriented Programming, OOP)

  • 프로그램을 상호작용하는 ‘객체(Object)’ 들의 집합으로 보는 관점. 데이터와 그 데이터를 처리하는 함수(메서드)를 하나로 묶어 객체로 만듦.

  • 핵심 요소:

    • 캡슐화 (Encapsulation): 데이터와 기능을 객체 안에 숨기고, 외부 노출을 제어.

    • 상속 (Inheritance): 부모 객체의 속성과 기능을 자식 객체가 물려받음.

    • 다형성 (Polymorphism): 같은 이름의 메서드가 다른 객체에서 다른 방식으로 동작.

    • 추상화 (Abstraction): 불필요한 세부 정보는 숨기고, 핵심적인 기능만 노출.

  • 대표 언어: Java, C++, Python, C#

나. 선언형 프로그래밍 (Declarative Programming)

  • ‘무엇을(What)’ 할 것인지를 기술하는 방식. ‘어떻게’ 해결할지는 컴퓨터에 위임.

  • 직접적인 제어 흐름이나 상태 변경을 다루지 않음.

1) 함수형 프로그래밍 (Functional Programming)

  • 프로그램을 순수 ‘함수(Function)’ 의 조합으로 보는 관점. 부수 효과(Side Effect)를 최소화하고 상태 변경을 피함.

  • 핵심 요소:

    • 순수 함수 (Pure Function): 동일한 입력에 대해 항상 동일한 출력을 반환.

    • 불변성 (Immutability): 생성된 후에는 상태를 변경할 수 없는 데이터.

    • 1급 객체 (First-class Citizen): 함수를 변수에 할당하거나, 다른 함수의 인자로 전달하거나, 반환 값으로 사용할 수 있음.

  • 대표 언어: Lisp, Haskell, F#, JavaScript(부분적 지원)

2) 논리 프로그래밍 (Logic Programming)

  • 프로그램을 ‘논리적인 추론’ 의 집합으로 보는 관점. 주어진 사실과 규칙에 기반하여 목표를 증명하는 방식으로 동작.

  • 대표 언어: Prolog

3. 사용법: 패러다임별 코드 예시

“1부터 10까지의 합을 구하는” 동일한 문제를 각 패러다임으로 어떻게 다르게 해결하는지 살펴봄.

가. 절차지향 (C 언어)

  • 어떻게: total 변수를 만들고, for 반복문을 이용해 1부터 10까지 순서대로 더해나가는 과정을 명시.
#include <stdio.h>

int main() {
    int total = 0;
    for (int i = 1; i <= 10; i++) {
        total += i; // 상태(total)를 직접 변경
    }
    printf("Total: %d\n", total);
    return 0;
}

나. 객체지향 (Java)

  • 어떻게: 합계를 계산하는 책임을 가진 Calculator 객체를 설계. calculateSum 메서드를 통해 계산을 수행.
class Calculator {
    public int calculateSum(int n) {
        int total = 0;
        for (int i = 1; i <= n; i++) {
            total += i;
        }
        return total;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator(); // 객체 생성
        int sum = calculator.calculateSum(10); // 객체의 메서드 호출
        System.out.println("Total: " + sum);
    }
}

다. 함수형 (JavaScript)

  • 무엇을: 1부터 10까지의 배열을 만들고(Array.from), 그 배열의 모든 요소를 더한다(reduce)는 ‘무엇’을 할 것인지만 선언.
// 1부터 10까지 숫자로 이루어진 배열을 생성
const numbers = Array.from({ length: 10 }, (_, i) => i + 1);

// reduce 함수를 이용해 합계를 구함
// 이전 값(acc)과 현재 값(cur)을 더하는 함수를 전달
const sum = numbers.reduce((acc, cur) => acc + cur, 0);

console.log("Total: " + sum);

4. 심화 내용: 멀티-패러다임 언어와 패러다임의 선택

현대의 많은 프로그래밍 언어는 하나의 패러다임만 지원하지 않음. Python, JavaScript, C++ 등은 여러 패러다임을 지원하는 멀티-패러다임 언어. 개발자는 문제의 성격에 따라 가장 적합한 패러다임을 선택하거나 조합하여 사용할 수 있음.

  • 예시: 웹 프론트엔드 개발에서 UI 컴포넌트는 객체지향적으로 설계하고, 서버와의 데이터 통신이나 상태 관리는 함수형으로 처리하여 코드의 안정성과 예측 가능성을 높이는 식.

패러다임 선택 기준:

  • 문제의 종류: 계산이 많은 과학 분야는 절차지향이나 함수형이, 복잡한 시스템을 모델링해야 하는 경우는 객체지향이 유리할 수 있음.

  • 팀의 숙련도: 팀원들이 익숙한 패러다임을 사용하는 것이 생산성에 도움.

  • 프로젝트의 규모 및 유지보수: 대규모 프로젝트일수록 구조화되고 예측 가능한 객체지향이나 함수형 패러다임이 유지보수에 유리.

각 패러다임은 우열의 관계가 아닌, 문제 해결을 위한 서로 다른 도구 상자와 같음. 다양한 패러다임에 대한 이해는 더 나은 문제 해결 능력으로 이어짐.

References

프로그래밍 패러다임