[Programming Paradigm] - 선언형, 함수형, 객체 지향, 절차형

김민경·2025년 6월 30일

Design Pattern

목록 보기
9/9
post-thumbnail

프로그래밍 패러다임 (Programming Paradigm)

"프로그래밍을 어떻게 할 것인가?"에 대한 사고 방식 또는 코딩 스타일
즉, 문제를 어떻게 구조화하고, 어떻게 해결할지를 결정하는 프로그래밍의 큰 틀

코드를 작성하는 방법론, 관점, 패턴 정의
같은 문제를 어떤 관점에서 접근하느냐에 따라 코드의 작성 방식이 달라지며 패러다임에 따라 코드의 구조, 가독성, 유지보수, 확장성이 크게 달라짐

종류와 예제

1부터 5까지 더하는 코드

  • 선언형 (무엇을 할지만 설명)
print(sum([1,2,3,4,5]))
  • 함수형 (순수 함수 사용)
from functools import reduce
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))
  • 객체 지향 (객체 중심으로)
class SumCalculator {
    int sumUpTo(int n){
    	int sum = 0;
        for (int i=1; i<=n; i++) sum += i;
        return sum;
    }
}
  • 절차형 (순서 중심)
sum = 0
for i in range(1,6):
    sum += i
print(sum)

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

"어떻게"가 아닌 "무엇"을 할 것인지를 설명하는 프로그래밍 방식

  • 결과를 설명하고 어떻게 수행할지는 신경 쓰지 않음.
  • 주로 상태 변화, 순서를 크게 고려하지 않음.

예제

SQL, HTML, React의 JSX, 함수형 언어가 대표적

SELECT name FROM users WHERE age > 20;
<h1>Hello, World!</h1>

특징

  • 코드가 간결하면서 가독성이 좋음
  • 동시성 처리에 유리
  • 하지만 성능 제어가 어려울 수 있음

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

함수를 수학적 함수처럼 순수하게 작성하고, 상태를 변경하지 않는 패러다임

  • 부수 효과(Side Effect)를 최소화하며, 상태, 데이터 변경 불가능 (Immutable)
  • 순수 함수(Pure Function)을 사용하고, 고차 함수(Higher-Order Function)를 통해 재사용성을 높인 패러다임

부수 효과

함수가 자신의 외부 상태를 변경하거나, 외부에 영향을 주는 모든 행동

순수 함수

  • 같은 입력 -> 같은 출력
  • 외부에 절대 영향을 주지 않음
  • 따라서 안정성, 예측 가능성, 재사용성, 병렬 처리에 매우 유리
//순수 함수
function add(a, b) {
    return a + b;
}


//순수 하지 않은 함수
let count = 0;

function increment() {
    count += 1;  // 외부 상태 변경 (부수 효과 발생)
}

고차 함수

  • 함수를 매개변수처럼 사용하고, 함수를 반환
  • 코드 재사용성, 추상화에 유리
  • 함수 조합 가능 -> 유연한 코드 작성
//함수형 인터페이스를 인자로 받는 고차 함수
public static int applyFunction(int x, Function<Integer, Integer> func) {
	return func.apply(x);
}

public static void main(String[] args) {
	// 람다식을 함수로 전달
	int result = applyFunction(5, num -> num * 2);
	System.out.println(result);  // 결과: 10
}

//Stream API에서 고차 함수 사용
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// map, filter, reduce 모두 함수를 인자로 받는 고차 함수
int sumOfSquares = numbers.stream()
	.filter(num -> num % 2 == 0)
	.map(num -> num * num)
	.reduce(0, Integer::sum);
    
System.out.println(sumOfSquares); // 결과: 20

예제

JavaScript, Scala, Haskell에서 활용

const add = (a, b) => a + b;

JDK 1.8

jdk 1.8 이전의 자바는 객체지향 프로그래밍을 지원
jdk 1.8부터
함수형 프로그래밍 패러다임을 지원하기 위해 람다식, 생성자 레퍼런스, 메서드 레퍼런스를 도입했고

//람다식
Comparator<String> compLambda = (o1, o2) -> o1.length() - o2.length());


//생성자 레퍼런스 (클래스명::new)
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
ArrayList<String> list = listSupplier.get();


//메서드 레퍼런스 (객체명::메서드명)
names.forEach(System.out::println);

선언형 프로그래밍을 위해 스트림(Stream) 같은 표준 API 등을 추가

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

int sum = numbers.stream()
        .filter(num -> num % 2 == 0)   // 짝수만 선택
        .map(num -> num * num)         // 제곱
        .reduce(0, Integer::sum);      // 합산

System.out.println(sum);  // 👉 결과: 20
//어떤 작업을 할 것인지만 명시, "어떻게"의 작업은 Java 내부가 처리

특징

  • 코드 재사용성이 좋으며, 병렬 처리에 유리
  • 데이터 변경이 없어서 안정적

선언형과 함수형

함수형 프로그래밍은 선언형 프로그래밍의 한 종류로,

선언형은 넓은 개념, 함수형은 선언형의 한 종류

  • 선언형 프로그래밍

    • "결과를 설명하되, 방법은 신경 안 씀"
    • 부수 효과가 있어도 괜찮으며, 무엇을 원한다만 선언하면 끝
  • 함수형 프로그래밍

    • "결과를 설명하되, 상태 변경과 부수 효과는 철저히 금지"
    • 순수 함수, 불변성, 고차 함수 사용

선언형은 단순히 "결과만 말하는 코드"지만,

함수형은 "결과 + 상태 변경 금지 + 순수 함수" 까지 포함


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

객체와 객체 간의 상호작용을 통해 문제를 해결하는 프로그래밍 방식

  • 객체는 데이터(속성) + 기능(메서드)를 가진 독립적인 단위
  • 객체 간 메시지(메서드 호출)을 주고받으며 프로그램 동작

예제

Java, C++, Python 등이 대표적

// 부모 클래스
class Animal {
    String name;

    void speak() {
        System.out.println("Some sound");
    }
}

// 자식 클래스
class Dog extends Animal {

    @Override
    void speak() {
        System.out.println("Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        myDog.name = "Buddy";
        myDog.speak();
    }
}

/*
Woof!
*/

특징

  • 추상화 : 복잡한 시스템으로부터 핵심 기능을 간추려내는 것
  • 캡슐화 : 객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것
  • 상속성 : 상위 클래스의 특성을 하위 클래스가 이어받아 재사용하거나 추가, 확장하는 것
  • 다형성 : 하나의 메서드나 클래스가 다양한 방법으로 동작하는 것 (Overloading, Overriding)

유지보수성과 확장성이 뛰어나며, 복잡한 시스템 설계에 적합
하지만 객체 간 결합도가 높아질 위험 존재

SOLID 설계 원칙

  • SRP, Single Responsibility Principle (단일 책임 원칙)

    모든 클래스는 각각 하나의 책임만 가져야 한다. (A는 A에 대한 책임만)

  • OCP, Open Closed Principle (개방-폐쇄 원칙)

    기존 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야 한다.

  • LSP, Liskov Substitution Principle (리스코프 치환 원칙)

    프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
    (부모 객체에 자식 객체를 넣어도 문제가 없어야 함)

  • ISP, Interface Segregation Principle (인터페이스 분리 원칙)

    하나의 일반적인 인터페이스보다 구체적인 여러 개의 인터페이스를 만들어야 한다.

  • DIP, Dependency Inversion Principle (의존 역전 원칙)

    추상화된 인터페이스나 상위 클래스를 두어, 변하기 쉬운 것에 의존해도 변화에 영향받지 않게 해야 한다.


절차형 프로그래밍 (Procedural Programming)

코드를 위에서 아래로 순차적으로 실행하는 전통적인 방식

  • 로직을 절차(순서)대로 나열
  • 데이터를 전역으로 두고, 함수를 순서대로 호출

예제

C 언어가 대표적

#include <stdio.h>

int main() {
    int sum = 0;

    for (int i = 1; i <= 5; i++) {
        sum += i;
    }

    printf("합계: %d\n", sum);

    return 0;
}

변수 선언 → 반복문 → 합산 → 출력
위에서 아래로 순서대로 진행됨.

특징

  • 코드 작성이 단순하고 빠르며, 간단한 프로그램에 적합
  • 대규모 프로젝트에선 구조가 복잡해질 수 있으며, 유지보수가 어렵고 코드 중복 가능성 높음
  • 확장성에 한계가 있음

정리

구분절차형객체 지향함수형선언형
중심 사고순서, 절차 중심객체와 메세지 중심함수와 조합 중심결과(What) 중심
코드 흐름명령어를 순차 실행객체 간 메세지 전달함수 조합무엇을 할 지 선언
데이터 처리전역/공유 변수객체 속성 관리데이터 불변상태 숨김, 최종 결과만 기술
상태 변경자유롭게 상태 변경객체 내에서 변경상태 변경 최소화상태 추상화
부수 효과있음상대적으로 적음최소화 지향없음 또는 숨김

절차형: "어떻게"를 하나씩 직접 지시하는 방식

객체 지향: "누가" (객체) 처리할지를 설계하는 방식

함수형: "무엇을" 함수로 조합할지를 선언하는 방식 (선언형의 하위 개념)

선언형: "결과"만 명시하고, 처리 방법은 시스템이 알아서 하도록 하는 방식

profile
뭐든 기록할 수 있도록

0개의 댓글