프로그래밍 패러다임🎯 : "선택의 문제, 최고의 패러다임은 없다?"

나나's Brain·2024년 12월 24일
0

CS 개념 모음

목록 보기
8/21
post-thumbnail

프로그래밍을 하다 보면 다양한 방식으로 문제를 해결할 수 있다는 것을 느낀다. 그 중에서 가장 많이 접하게 되는 패러다임들이 바로 선언형, 함수형, 객체지향형, 절차형 프로그래밍이다. 각각의 패러다임은 고유의 특징을 가지고 있으며, 각기 다른 상황에서 장점이 있을 수 있다. 그럼, 각 패러다임이 어떻게 다르고, 어떤 상황에서 유용한지 한번 살펴보자.


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

선언형 프로그래밍은 "어떻게" 보다는 "무엇을" 해결할 것인지에 집중하는 방식이다. 코드를 작성할 때, 우리가 원하는 결과를 선언적으로 표현하고, 그 결과를 도출하기 위한 구체적인 방법은 컴퓨터가 알아서 처리한다. 즉, 우리는 결과를 선언하고, 컴퓨터는 그걸 어떻게 할지 찾아낸다.

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

함수형 프로그래밍은 선언형 프로그래밍의 일종으로, 함수가 일급 객체(First-Class Citizens)로 취급된다. 이는 함수를 값처럼 다루어 고차 함수(Higher-Order Function)와 같이, 다른 함수를 인자로 받아서 새로운 함수를 반환하거나 조작하는 방식이다.

함수형 프로그래밍의 주요 특징은 순수 함수(Pure Functions)를 사용한다는 점이다. 순수 함수는 입력값만으로 출력을 결정하며, 부수 효과(side effects)가 없다.

🔍 예시 - 배열에서 최댓값을 찾기:

const numbers = [1, 2, 3, 4, 5, 11, 12];
const max = numbers.reduce((max, num) => (num > max ? num : max), 0);
console.log(max); // 12

이 코드는 배열의 각 값을 순차적으로 비교하여 최댓값을 구한다. 여기서 reduce는 고차 함수로, 배열을 처리하는 방식에 대한 구체적인 내용은 내포되어 있다.

🔑 핵심: 함수형 프로그래밍은 "무엇을" 해결할지를 명확하게 선언하고, 그 해결 방법은 컴퓨터가 알아서 찾아간다. 즉, 코드의 가독성재사용성을 높여준다.


2️⃣ 객체지향 프로그래밍 (OOP)

객체지향 프로그래밍(OOP)은 프로그램을 객체의 집합으로 바라보고, 객체 간의 상호작용을 통해 문제를 해결한다. 객체는 데이터와 그 데이터를 다루는 메서드를 포함하고 있으며, 객체끼리 메시지를 전달하여 상호작용한다. OOP의 주요 특징은 추상화, 캡슐화, 상속, 다형성이다.

📚 주요 특징

  • 추상화 (Abstraction): 복잡한 시스템을 간소화하고 핵심 기능만 노출
  • 캡슐화 (Encapsulation): 객체 내부 데이터를 보호하고 외부에서 접근을 제한
  • 상속 (Inheritance): 상위 클래스의 기능을 하위 클래스가 이어받아 재사용
  • 다형성 (Polymorphism): 같은 메서드가 다른 방식으로 동작하도록 함

🔍 예시 - 배열에서 최댓값을 찾는 객체지향 방식:

class List {
    constructor(list) {
        this.list = list;
        this.max = list.reduce((max, num) => (num > max ? num : max), 0);
    }

    getMax() {
        return this.max;
    }
}

const list = new List([1, 2, 3, 4, 5, 11, 12]);
console.log(list.getMax()); // 12

🔑 핵심: OOP는 데이터와 기능을 객체로 묶어, 재사용성유지보수성을 향상시킨다. 복잡한 시스템을 효율적으로 관리할 수 있게 도와준다.

✨ 객체지향 설계의 SOLID 원칙

원칙영문 약자설명예시
단일 책임 원칙SRP (Single Responsibility Principle)모든 클래스는 하나의 책임만 가져야 하며, 그 책임에 대한 수정만 일어나야 하는 원칙.한 클래스에서 UI 로직과 비즈니스 로직을 분리하여 각각의 클래스가 별도의 책임을 갖도록 설계.
개방-폐쇄 원칙OCP (Open Closed Principle)기존 코드는 수정하지 않으면서도 확장할 수 있도록 설계해야 하는 원칙.새 기능 추가 시 기존 코드를 수정하지 않고 새로운 클래스를 만들어 확장.
리스코프 치환 원칙LSP (Liskov Substitution Principle)부모 객체를 사용하는 프로그램에 자식 객체를 대신 넣어도 시스템이 문제없이 동작해야 한다는 원칙.Animal 클래스를 상속한 Dog 클래스가 Animal 대신 사용되어도 문제없이 동작해야 함.
인터페이스 분리 원칙ISP (Interface Segregation Principle)하나의 일반적인 인터페이스보다 구체적이고 작은 여러 개의 인터페이스로 나누어야 하는 원칙.Printer 인터페이스를 ScanPrinterPrintOnlyPrinter로 나누어 각각 필요한 기능만 구현.
의존 역전 원칙DIP (Dependency Inversion Principle)구체적인 구현보다 추상화된 인터페이스나 상위 클래스에 의존하도록 설계하여, 변화에 유연한 구조를 만드는 원칙.Car 클래스가 Tire 인터페이스를 사용해 다양한 타이어를 교체할 수 있게 설계.

SOLID 원칙은 객체지향 설계의 핵심 가이드라인으로, 이를 준수하면 코드의 유지보수성, 확장성, 재사용성이 향상된다. 프로젝트 개발 시 이러한 원칙을 고려하여 설계하면 복잡한 문제도 체계적으로 해결할 수 있다.

💡 오버로딩 (Overloading) vs 오버라이딩 (Overriding)

구분오버로딩 (Overloading)오버라이딩 (Overriding)
정의같은 이름의 메서드를 매개변수로 구분하여 여러 개 정의상위 클래스의 메서드를 하위 클래스에서 재정의
발생 시점컴파일 시 (정적 다형성)런타임 시 (동적 다형성)
목적매개변수에 따라 다른 동작을 하게 함하위 클래스에서 상위 클래스 메서드를 재정의하여 새로운 동작을 구현
특징매개변수의 개수, 타입, 순서가 달라야 함메서드 시그니처가 동일해야 함
예시void print(int num)
void print(String str)
class Dog extends Animal {
@Override void makeSound()

오버로딩: 메서드 이름은 같지만 매개변수에 따라 구분되며, 컴파일 시점에 메서드 호출이 결정됨.

오버라이딩: 상속받은 메서드를 하위 클래스에서 재정의하여, 런타임 시점에 메서드 호출이 결정됨.


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

절차형 프로그래밍은 "순차적"으로 명령을 수행하는 방식이다. 즉, 코드를 실행하는 순서대로 처리가 이루어진다. 로직이 수행되는 순서대로 계산 과정이 이루어지며, 변수에 값을 할당하고, 조건문과 반복문을 사용하여 문제를 해결한다.

🔍 예시 - 배열에서 최댓값을 찾는 절차형 방식:

let numbers = [1, 2, 3, 4, 5, 11, 12];
let max = 0;
for (let i = 0; i < numbers.length; i++) {
    max = Math.max(numbers[i], max);
}
console.log(max); // 12

🔑 핵심: 절차형 프로그래밍은 단순하고 효율적인 로직 구현이 가능하다. 계산량이 많은 작업에 유리하다. 하지만 복잡한 시스템에서는 코드의 유지보수성에 어려움이 있을 수 있다.


🔄 패러다임의 혼합: "가장 좋은 것은 없다!"

모든 프로그래밍 패러다임에는 장단점이 존재하며, 비즈니스 로직이나 프로젝트의 특성에 따라 적합한 패러다임을 선택하는 것이 중요하다. 상황에 따라 다양한 패러다임을 조합하는 것이 더 효율적일 수 있다.

🔄 혼합 예시:

  • 머신 러닝 파이프라인은 절차형으로 구현
  • 거래 관련 로직은 함수형으로 구현
  • 웹 서버의 라우팅 시스템은 객체지향으로 설계

🔑 핵심: 프로그래밍 패러다임은 상황에 맞춰 유연하게 선택하고 혼합하는 것이 가장 효율적인 접근법이다.

예를 들어, 간단한 알고리즘을 구현할 때는 함수형 프로그래밍이 적합하고, 복잡한 비즈니스 로직을 다룰 때는 객체지향 프로그래밍이 유리할 수 있다. 중요한 것은 문제 해결에 최적화된 방식을 선택하는 것이다.


💡 마무리

프로그래밍 패러다임을 선택할 때, 하나의 패러다임만 고수할 필요는 없다.
각 패러다임이 가지는 특성과 장점은 문제를 해결하는 방법에 따라 다르게 적용될 수 있기 때문이다. "최고의 패러다임은 없다"는 생각으로 상황에 맞는 패러다임을 조합하여 사용하는 것이 중요하다. 어떤 방식으로 프로그래밍할지 고민하며, 각 패러다임의 특징을 이해하고 유연하게 접근하는 것이 더 좋은 코드를 만들 수 있다. ✨

[ 참고 서적 ] : 주홍철, 면접을 위한 CS 전공지식 노트, 길벗, 2022년 04월 28일.

profile
"로컬에선 문제없었는데…?"

0개의 댓글