nodejs

객체지향 프로그래밍(OOP)와 함수형 프로그래밍(FP)

"객체지향(OOP)과 함수형(FP), 둘 중 어떤 것을 써야 할까요?" 개발을 시작하면서 가장 자주 듣는 질문 중 하나입니다. 두 개념 모두 오래된 역사와 풍부한 사례를 가진 프로그래밍 패러다임이며, 각각의 장점과 단점을 갖고 있습니다. 그리고 최근 개발 환경에서는 이 둘을 서로 대립되는 개념으로 보기보다는 상호 보완적인 사고 방식으로 받아들이는 추세입니다.


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

객체지향 프로그래밍(Object-Oriented Programming, OOP)은 프로그램을 서로 독립적인 객체들로 나누어 구성하는 방식입니다. 이 객체들은 각각 **데이터(상태)**를 가지고 있으며, 그 데이터에 작용하는 **기능(메서드)**도 함께 포함합니다. 즉, 현실 세계에서 사물이 고유한 성질과 행동을 갖듯, 객체도 스스로 상태를 유지하고 행동하는 구조로 만들어져, 현실을 코드로 표현하기 쉽게 도와주는 방식입니다.


OOP의 핵심 개념은 캡슐화, 상속, 다형성으로 요약됩니다. 캡슐화는 내부 데이터를 보호하고 외부에는 필요한 인터페이스만 제공함으로써, 코드의 안정성과 유연성을 높입니다. 상속은 공통된 기능을 상위 클래스에서 정의하고 하위 클래스가 이를 재사용하도록 하며, 다형성은 동일한 인터페이스로 다양한 형태의 동작을 구현할 수 있게 합니다.


이러한 특성은 특히 사용자 인터페이스(UI) 설계, 도메인 중심 설계(Domain-Driven Design), 그리고 복잡한 비즈니스 로직을 가진 시스템에서 강력한 표현력과 구조적 이점을 제공합니다. 현실 세계의 사물을 객체로 표현하고 그들의 관계를 모델링하는 데 용이하여, 추상화된 설계와 유지보수에 매우 효과적인 접근 방식입니다.


함수형 프로그래밍(FP)이란?

함수형 프로그래밍(Functional Programming, FP)은 함수를 중심으로 로직을 구성하는 프로그래밍 방식입니다. 이때의 함수는 수학적 의미의 **순수 함수(Pure Function)**를 의미하며, 동일한 입력에 대해 항상 같은 출력을 반환하고, 외부 상태를 변경하지 않는 것이 특징입니다. 즉, 코드가 실행되는 과정에서 예측 불가능한 부작용(Side Effect)을 최대한 줄이는 것을 중요하게 생각합니다.


FP에서는 데이터를 직접 변경하지 않고, 기존 데이터를 기반으로 새로운 데이터를 만들어 반환합니다. 이러한 특성 덕분에 함수형 프로그래밍은 **불변성(immutability)**을 기본 원칙으로 삼고 있으며, 반복문보다는 map, filter, reduce 같은 고차 함수나 **재귀(recursion)**를 통해 데이터를 가공합니다. 이로 인해 로직이 더 선언적으로 표현되며, 코드의 가독성과 테스트 용이성이 크게 향상됩니다.


또한 상태 변경이 없기 때문에 여러 연산을 병렬로 수행할 때 충돌이 발생할 위험이 적어, 병렬 처리나 멀티스레드 환경에서도 안정적인 동작이 가능합니다. 최근에는 React, Redux, RxJS와 같은 프론트엔드 라이브러리나 스트림 기반 아키텍처에서 함수형 사고가 널리 활용되고 있으며, 특히 복잡한 상태 관리나 비동기 흐름 제어에 유리하다는 평가를 받고 있습니다. 이러한 특성은 코드의 예측 가능성과 유지보수성을 높이는 데 큰 도움이 됩니다.


항목객체지향(OOP)함수형(FP)
기본 단위객체 (상태 + 메서드)함수 (입력 → 출력)
상태 관리변경 가능한 상태 중심불변 데이터 중심
코드 스타일절차적, 명령형 흐름선언형, 조합 중심
중심 개념캡슐화, 상속, 다형성순수 함수, 고차 함수, 불변성
사이드 이펙트허용 (필요시)최소화 또는 제거
병렬 처리상대적으로 어려움 (공유 상태 존재)상대적으로 쉬움 (불변 상태 기반)
테스트 용이성상태와 흐름 추적 필요함수 단위 테스트 용이

예제

oop & fp
// OOP 예시 (JavaScript)
// 객체가 자신의 상태를 직접 변경합니다.
class Counter {
  constructor() {
    this.value = 0
  }
  increment() {
    this.value += 1
  }
}
const counter = new Counter()
counter.increment()
console.log(counter.value) // 1
 
// FP 예시 (JavaScript)
// 상태를 변경하지 않고, 새로운 값을 반환합니다.
function increment(value) {
  return value + 1
}
const result = increment(0)
console.log(result) // 1

위 예시는 객체지향(OOP)과 함수형(FP) 프로그래밍의 가장 근본적인 차이점인 상태 관리 방식을 잘 보여줍니다. 첫 번째 OOP 예시에서는 Counter라는 객체가 내부 상태인 value를 가지고 있으며, increment() 메서드를 통해 직접 그 값을 변경합니다. 즉, 객체는 자신의 상태를 내부적으로 유지하고, 스스로 변경할 수 있는 주체입니다. 이처럼 상태와 동작이 한곳에 모여 있는 것이 OOP의 특징입니다.


반면 두 번째 FP 예시에서는 상태를 객체로 보관하거나 변경하지 않고, increment 함수는 단순히 값을 받아 새로운 값을 리턴할 뿐입니다. 기존 값은 유지되고 새로운 결과가 생성되므로, 불변성을 유지하며 사이드 이펙트 없이 동작합니다. 이는 테스트나 병렬 처리 환경에서 예측 가능성과 안정성을 높여주는 함수형 프로그래밍의 대표적인 방식입니다.


아래는 섞은 결합형 예시입니다:

oop & fp
class Counter {
  constructor(initialValue = 0) {
    this.value = initialValue
  }
 
  increment() {
    this.value = Counter.incrementValue(this.value)
  }
 
  static incrementValue(val) {
    return val + 1
  }
}
 
const counter = new Counter()
counter.increment()
console.log(counter.value) // 1

결합형 프로그래밍 방식은 객체지향의 구조를 유지하면서도, 함수형의 장점을 내부 로직에 적용하는 전략입니다. 먼저 Counter 클래스는 OOP 스타일로 정의되어 있으며, 내부에 value라는 상태를 갖고 있습니다. 생성자에서는 초기값을 설정하고, 이 상태는 외부에서 직접 접근하지 않고 클래스의 메서드를 통해서만 변경됩니다. 이는 객체지향의 핵심인 **캡슐화(encapsulation)**를 유지하는 구조입니다.


하지만 상태를 변경하는 방식은 함수형 프로그래밍의 특징을 따릅니다. increment() 메서드는 내부 상태를 직접 연산하지 않고, 정적(static) 메서드인 incrementValue()를 호출해 새로운 값을 반환받아 할당합니다. 이 정적 메서드는 외부 상태에 의존하지 않으며, 주어진 입력을 받아 동일한 출력을 반환하는 **순수 함수(pure function)**입니다. 이를 통해 상태 변경 로직을 순수하게 분리하고, 테스트 및 재사용이 용이해집니다.


결과적으로 이 구조는 객체는 상태를 안전하게 관리하고, 로직은 사이드 이펙트 없는 함수로 위임하는 방식으로 구성됩니다. 이는 OOP의 구조적 안정성과 FP의 예측 가능성, 가독성, 테스트 용이성을 동시에 얻을 수 있는 실용적인 접근입니다. 실무에서는 이러한 패턴을 통해 유지보수성과 유연성을 높이고 있으며, 특히 복잡한 상태 관리가 필요한 도메인 모델이나 UI 컴포넌트 로직에서 자주 활용됩니다.


선택 기준

실무에서 객체지향(OOP)과 함수형(FP) 중 어떤 프로그래밍 패러다임을 선택할지는 업무의 성격과 개발 환경에 따라 달라집니다. 예를 들어 UI를 구성하거나 비즈니스 로직을 구조화해야 하는 경우에는 객체지향이 더 적합한 선택이 될 수 있습니다. 특히 Java 기반의 Spring, TypeScript 기반의 NestJS처럼 프레임워크 중심으로 개발되는 프로젝트에서는, 클래스와 의존성 주입을 기반으로 한 객체지향적 추상화가 코드를 이해하고 확장하는 데 유리하게 작용합니다.

반대로, 복잡한 연산을 다루거나 대용량 데이터를 처리하는 등 데이터 중심의 로직이 핵심인 경우에는 함수형 프로그래밍이 더 적합할 수 있습니다. 함수형 스타일은 상태를 변경하지 않고, 불변성을 유지하며 동작하므로, 복잡한 상태 변화가 얽히지 않도록 코드를 깔끔하게 유지할 수 있습니다. 특히 멀티스레드 환경이나 병렬 처리 시스템에서는 상태 공유로 인한 충돌을 피할 수 있어 안정성과 예측 가능성 측면에서 큰 장점을 가집니다.

최근에는 React, Redux, RxJS 같은 프론트엔드 프레임워크가 함수형과 선언형 스타일을 적극 채택하면서, OOP와 FP를 결합한 하이브리드 형태의 개발 방식이 널리 사용되고 있습니다. 객체지향 구조 속에서 불변성과 순수 함수를 적극 활용하거나, 상태 관리를 함수형 스타일로 위임하는 방식이 대표적입니다. 실무에서는 특정 패러다임에만 의존하기보다는, 두 가지 접근법의 강점을 상황에 맞게 조합하여 적용하는 유연한 전략이 점점 더 중요해지고 있습니다.


마무리

객체지향 프로그래밍(OOP)과 함수형 프로그래밍(FP)은 겉보기에는 상반된 접근처럼 보이지만, 결국 지향하는 바는 같습니다. 바로 변화에 유연하고, 안정적으로 동작하며, 유지보수가 쉬운 소프트웨어를 만드는 것입니다. OOP는 현실 세계의 개념을 추상화하여 상태와 책임을 구조화하는 데 강점을 가지며, FP는 데이터 흐름을 예측 가능하게 설계하고 부작용을 최소화함으로써 코드의 안정성을 확보합니다.


현대의 개발 환경에서는 어느 한 쪽을 고집하기보다, 문제의 성격에 따라 두 패러다임을 유연하게 조합하는 실용적인 사고방식이 더욱 중요해졌습니다. UI 중심의 구조화가 필요할 땐 객체지향적으로, 복잡한 데이터 처리나 상태 제어에는 함수형 접근을 병행하는 식의 선택이 일반적입니다.


참고