icon안동민 개발노트

객체 지향 프로그래밍 vs 함수형 프로그래밍


 객체 지향 프로그래밍(OOP)과 함수형 프로그래밍(FP)은 프로그래밍의 두 가지 주요 패러다임으로, 각각 고유한 개념과 원칙을 가지고 있습니다.

 이 절에서는 두 패러다임을 비교하고, 자바스크립트에서의 구현 방법을 살펴보겠습니다.

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

 OOP는 데이터와 그 데이터를 조작하는 메서드를 하나의 객체로 묶는 프로그래밍 패러다임입니다.

 핵심 원칙

  1. 캡슐화 : 데이터와 메서드를 하나의 단위로 묶음
  2. 상속 : 기존 클래스의 속성과 메서드를 새 클래스가 재사용
  3. 다형성 : 같은 인터페이스로 다양한 객체 타입을 다룸
  4. 추상화 : 복잡한 시스템을 간단한 인터페이스로 표현

 자바스크립트에서의 OOP 구현

  1. 생성자 함수와 프로토타입
function Animal(name) {
  this.name = name;
}
 
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound.');
};
 
function Dog(name) {
  Animal.call(this, name);
}
 
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
 
Dog.prototype.bark = function() {
  console.log(this.name + ' barks!');
};
 
const dog = new Dog('Buddy');
dog.speak(); // Buddy makes a sound.
dog.bark();  // Buddy barks!
  1. ES6 클래스
class Animal {
  constructor(name) {
    this.name = name;
  }
 
  speak() {
    console.log(this.name + ' makes a sound.');
  }
}
 
class Dog extends Animal {
  bark() {
    console.log(this.name + ' barks!');
  }
}
 
const dog = new Dog('Buddy');
dog.speak(); // Buddy makes a sound.
dog.bark();  // Buddy barks!

함수형 프로그래밍 (FP)

 FP는 순수 함수를 조합하여 소프트웨어를 만드는 프로그래밍 패러다임입니다.

 주요 개념

  1. 순수 함수 : 동일한 입력에 항상 동일한 출력을 반환하며 부작용이 없는 함수
  2. 불변성 : 데이터 변경 대신 새로운 데이터 생성
  3. 고차 함수 : 함수를 인자로 받거나 함수를 반환하는 함수
  4. 합성 : 여러 함수를 조합하여 새로운 함수 생성

 자바스크립트에서의 FP 예시

// 순수 함수
const add = (a, b) => a + b;
 
// 불변성
const addToList = (list, item) => [...list, item];
 
// 고차 함수
const multiplyBy = (factor) => (number) => number * factor;
const double = multiplyBy(2);
console.log(double(5)); // 10
 
// 합성
const compose = (f, g) => (x) => f(g(x));
const addThenMultiply = compose(double, add(5));
console.log(addThenMultiply(10)); // 30

OOP vs FP : 장단점 비교

 OOP

  • 장점 : 직관적인 모델링, 코드 재사용성, 확장성
  • 단점 : 복잡한 의존 관계, 상태 변경으로 인한 부작용

 FP

  • 장점 : 예측 가능성, 테스트 용이성, 병렬 처리 적합
  • 단점 : 초기 학습 곡선, 일부 문제에 대한 모델링 어려움

패러다임 혼합 사용

 자바스크립트는 멀티 패러다임 언어로, OOP와 FP를 혼합하여 사용할 수 있습니다.

class Calculator {
  constructor(initialValue = 0) {
    this.value = initialValue;
  }
 
  add(n) {
    return new Calculator(this.value + n);
  }
 
  multiply(n) {
    return new Calculator(this.value * n);
  }
 
  getValue() {
    return this.value;
  }
}
 
const result = new Calculator(5)
  .add(3)
  .multiply(2)
  .getValue();
 
console.log(result); // 16

 이 예제는 OOP의 캡슐화와 FP의 불변성을 결합합니다.

현대적 프레임워크에서의 활용

 React에서는 두 패러다임이 모두 활용됩니다.

  • 함수형 컴포넌트와 훅 : FP 원칙 적용
  • 클래스 컴포넌트 : OOP 원칙 적용

 예시

// 함수형 컴포넌트 (FP)
function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}
 
// 클래스 컴포넌트 (OOP)
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.initialCount };
  }
 
  increment = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }
 
  render() {
    return (
      <>
        Count: {this.state.count}
        <button onClick={this.increment}>+</button>
      </>
    );
  }
}

테스트와 유지보수

 OOP

  • 단위 테스트 : 객체의 상태와 행동을 테스트
  • 유지보수 : 캡슐화로 인한 지역적 변경 용이, but 복잡한 상속 구조는 유지보수 어려움

 FP

  • 단위 테스트 : 순수 함수의 입출력만 테스트하면 됨
  • 유지보수 : 부작용 없는 함수로 인해 예측 가능성 높음, 변경의 영향 범위 제한적

 객체 지향 프로그래밍과 함수형 프로그래밍은 각각 고유한 강점을 가지고 있으며, 상황에 따라 적절한 패러다임을 선택하거나 혼합하여 사용하는 것이 중요합니다.

 OOP는 실세계의 객체를 모델링하는 데 적합하며, 큰 규모의 애플리케이션에서 코드의 구조화와 재사용성을 높이는 데 도움이 됩니다. 특히 복잡한 시스템을 설계할 때, 객체 간의 관계를 통해 직관적인 구조를 만들 수 있습니다.

 반면 FP는 데이터 변환과 흐름에 중점을 둡니다. 불변성과 순수 함수를 강조하여 예측 가능하고 테스트하기 쉬운 코드를 작성할 수 있게 해줍니다. 특히 병렬 처리가 필요한 경우나 복잡한 상태 관리가 필요한 경우에 유용합니다.

 자바스크립트의 유연성 덕분에 두 패러다임을 혼합하여 사용할 수 있습니다. 이는 각 패러다임의 장점을 살리면서 단점을 보완할 수 있는 기회를 제공합니다. 예를 들어, 데이터 모델은 OOP로 구현하고 비즈니스 로직은 FP 원칙을 따라 구현할 수 있습니다.

 현대적인 프레임워크와 라이브러리들은 이러한 혼합 접근법을 잘 보여줍니다. React의 경우, 최근 함수형 컴포넌트와 훅을 강조하는 추세이지만, 여전히 클래스 컴포넌트도 지원합니다. 이는 개발자가 상황에 따라 적절한 패러다임을 선택할 수 있게 해줍니다.

 테스트와 유지보수 측면에서도 각 패러다임은 장단점이 있습니다. OOP는 객체의 캡슐화를 통해 관련 로직을 하나의 단위로 관리할 수 있게 해주지만, 복잡한 상속 관계는 유지보수를 어렵게 만들 수 있습니다. FP는 순수 함수와 불변성을 통해 예측 가능한 코드를 만들어 테스트와 디버깅을 용이하게 합니다.

 결론적으로, OOP와 FP는 각각의 장단점을 가지고 있으며, 프로젝트의 요구사항, 팀의 경험, 성능 고려사항 등을 바탕으로 적절한 패러다임을 선택해야 합니다. 많은 경우 두 패러다임의 장점을 결합하여 사용하는 것이 최선의 접근 방식일 수 있습니다. 중요한 것은 각 패러다임의 핵심 원칙을 이해하고, 이를 효과적으로 활용하여 깨끗하고 유지보수가 용이한 코드를 작성하는 것입니다.