객체 지향 방식과 함수형 프로그래밍의 차이점 비교

핵심 요약 객체 지향 프로그래밍(OOP)은 데이터와 행동을 객체로 캡슐화하여 현실 세계를 모델링하는 방식이며, 함수형 프로그래밍(FP)은 순수 함수와 불변성을 통해 부작용을 최소화하는 방식이다. 마이클 피더스(Michael Feathers)의 말처럼 “OOP는 움직이는 부분을 캡슐화하여 코드를 이해하기 쉽게 만들고, FP는 움직이는 부분을 최소화하여 코드를 이해하기 쉽게 만든다12.

1. 패러다임의 근본적 차이점

1.1 접근 방식의 차이

관점객체 지향 프로그래밍함수형 프로그래밍
세계관객체들의 상호작용으로 문제 해결3함수들의 조합으로 문제 해결3
핵심 단위클래스와 객체순수 함수
데이터 처리객체가 데이터와 메서드를 함께 보유데이터와 함수를 분리하여 처리4
상태 관리객체 내부 상태 변경 허용불변성 강조, 상태 변경 최소화5

1.2 코드 구조 비교

객체 지향 방식 (JavaScript)

// OOP: 데이터와 행동을 객체에 캡슐화
class BankAccount {
    constructor(balance) {
        this._balance = balance;
    }
    
    deposit(amount) {
        this._balance += amount;
        return this;
    }
    
    withdraw(amount) {
        if (this._balance >= amount) {
            this._balance -= amount;
        }
        return this;
    }
    
    getBalance() {
        return this._balance;
    }
}
 
const account = new BankAccount(1000);
account.deposit(500).withdraw(200);

함수형 방식 (JavaScript)

// FP: 순수 함수로 불변 데이터 처리
const createAccount = (balance) => ({ balance });
 
const deposit = (account, amount) => ({
    ...account,
    balance: account.balance + amount
});
 
const withdraw = (account, amount) => 
    account.balance >= amount 
        ? { ...account, balance: account.balance - amount }
        : account;
 
// 함수 파이프라인으로 처리
const account = createAccount(1000);
const updatedAccount = withdraw(deposit(account, 500), 200);

2. 핵심 원리 비교

2.1 객체 지향의 4대 원칙

원칙설명장점
캡슐화데이터와 메서드를 객체 내부에 묶어 외부 접근 제한67데이터 무결성 보장, 인터페이스 단순화
상속기존 클래스를 확장해 새로운 클래스 생성7코드 재사용성, 계층적 구조
다형성같은 인터페이스로 다른 구현체 사용7유연성, 확장성
추상화복잡한 구현을 단순한 인터페이스로 제공7복잡도 관리, 사용 편의성

2.2 함수형의 핵심 개념

개념설명장점
순수 함수동일 입력에 항상 동일 출력, 부작용 없음8예측 가능성, 테스트 용이성
불변성데이터 생성 후 변경 불가7동시성 안전, 버그 감소
고계 함수함수를 인자로 받거나 반환하는 함수8함수 조합, 재사용성
참조 투명성식을 그 값으로 대체해도 프로그램 의미 불변8추론 용이성, 최적화 가능

3. 상태 관리 방식의 차이

3.1 OOP의 상태 관리

  • 가변 상태: 객체 내부 상태를 메서드를 통해 변경59
  • 캡슐화된 상태: private 필드로 외부 직접 접근 차단10
  • 상태 기반 행동: 객체의 현재 상태에 따라 메서드 동작 결정
class Counter {
    constructor() {
        this._count = 0;
    }
    
    increment() {
        this._count++; // 상태 변경
        return this._count;
    }
    
    getValue() {
        return this._count;
    }
}

3.2 FP의 상태 관리

  • 불변 상태: 새로운 상태 객체 생성으로 변경 표현5
  • 순수 함수: 외부 상태에 의존하지 않는 독립적 함수9
  • 함수형 파이프라인: 상태 변환을 함수 조합으로 표현
const increment = (count) => count + 1;
const createCounter = (initialValue = 0) => initialValue;
 
// 상태 변경 시 새로운 값 생성
let counter = createCounter();
counter = increment(counter); // 새로운 값으로 대체

4. 장단점 비교 분석

4.1 객체 지향 프로그래밍

장점11127:

  • 직관적 모델링: 현실 세계 객체와 유사한 구조로 이해하기 쉬움
  • 코드 재사용성: 상속과 다형성을 통한 효율적 코드 재사용
  • 모듈화: 관련 데이터와 기능을 하나의 단위로 묶어 관리
  • 유지보수성: 캡슐화를 통한 독립적 모듈 수정 가능
  • 확장성: 새로운 클래스 추가로 기능 확장 용이

단점1113:

  • 복잡성: 상속 계층이 깊어질수록 복잡도 증가
  • 성능 오버헤드: 객체 생성과 메서드 호출에 따른 메모리·처리 비용
  • 결합도: 객체 간 의존성으로 인한 강한 결합 가능성
  • 학습 곡선: 추상화 개념 이해에 시간 필요

4.2 함수형 프로그래밍

장점1113:

  • 예측 가능성: 순수 함수로 인한 안정적이고 일관된 결과
  • 동시성 안전: 불변성으로 인한 레이스 컨디션 방지
  • 테스트 용이성: 격리된 순수 함수로 단위 테스트 간소화
  • 디버깅 편의성: 부작용 없는 함수로 버그 추적 용이
  • 함수 조합: 작은 함수들의 조합으로 복잡한 로직 구성

단점117:

  • 학습 곡선: 수학적 개념과 함수형 사고방식 습득 어려움
  • 메모리 사용량: 불변 데이터 구조로 인한 메모리 오버헤드
  • 성능 이슈: 재귀와 함수 호출 스택으로 인한 성능 저하 가능
  • 가독성: 함수형 구문에 익숙하지 않은 개발자에게 복잡함

5. 사용 시나리오 및 선택 지침

5.1 언제 OOP를 선택할까?

적합한 상황14111:

  • 고정된 연산, 변화하는 데이터 타입: 기존 메서드는 유지하며 새로운 클래스 추가가 주된 진화 패턴
  • 현실 세계 모델링: 사용자, 주문, 상품 등 구체적 엔티티 중심 시스템
  • 대규모 시스템: 복잡한 비즈니스 로직과 상태 관리가 필요한 엔터프라이즈 애플리케이션
  • GUI 개발: 버튼, 윈도우 등 상호작용하는 컴포넌트 구현
  • 팀 협업: 명확한 인터페이스와 책임 분리가 중요한 프로젝트
// GUI 컴포넌트 - OOP가 적합한 예
class Button {
    constructor(text, onClick) {
        this.text = text;
        this.onClick = onClick;
        this.isEnabled = true;
    }
    
    render() {
        return `<button ${!this.isEnabled ? 'disabled' : ''} 
                 onclick="${this.onClick}">${this.text}</button>`;
    }
    
    disable() {
        this.isEnabled = false;
    }
}

5.2 언제 FP를 선택할까?

적합한 상황14111:

  • 고정된 데이터, 변화하는 연산: 기존 데이터 구조는 유지하며 새로운 함수 추가가 주된 진화 패턴
  • 데이터 변환 처리: 입력 데이터를 여러 단계를 거쳐 원하는 형태로 변환
  • 병렬 처리: 동시성이 중요한 대용량 데이터 처리
  • 수학적 계산: 통계, 분석, 머신러닝 등 계산 중심 로직
  • API 데이터 처리: JSON 변환, 필터링, 집계 등
// 데이터 처리 파이프라인 - FP가 적합한 예
const processUserData = (users) =>
    users
        .filter(user => user.active)
        .map(user => ({
            ...user,
            fullName: `${user.firstName} ${user.lastName}`,
            ageGroup: user.age < 30 ? 'young' : 'mature'
        }))
        .reduce((groups, user) => {
            groups[user.ageGroup] = groups[user.ageGroup] || [];
            groups[user.ageGroup].push(user);
            return groups;
        }, {});

6. 혼합 접근법과 실용적 선택

6.1 멀티패러다임 활용

현대 프로그래밍에서는 순수한 OOP나 FP보다는 두 패러다임의 장점을 결합하는 접근이 일반적이다1516:

// OOP + FP 혼합 예제
class UserService {
    constructor(users) {
        this._users = users; // 캡슐화된 데이터
    }
    
    // 함수형 스타일의 메서드
    getActiveUsers() {
        return this._users.filter(user => user.active);
    }
    
    // 순수 함수로 구현된 변환
    transformUsers(transformFn) {
        return this._users.map(transformFn);
    }
    
    // 불변성을 지키는 업데이트
    updateUser(id, updates) {
        const updatedUsers = this._users.map(user =>
            user.id === id ? { ...user, ...updates } : user
        );
        return new UserService(updatedUsers);
    }
}

6.2 실용적 선택 기준

기준OOP 선택FP 선택
프로젝트 규모대규모, 복잡한 시스템중소규모, 계산 중심
팀 구성OOP 경험 개발자 다수함수형 경험 또는 수학적 배경
성능 요구사항메모리 효율성 중시처리 속도, 동시성 중시
유지보수성장기간 운영, 기능 확장안정성, 버그 최소화
도메인 특성비즈니스 로직, 상태 관리데이터 처리, 알고리즘

7. 결론

객체 지향과 함수형 프로그래밍은 상호 배타적이 아닌 상호 보완적인 관계이다117. OOP는 복잡한 도메인 모델링과 상태 관리에 강점을 보이며, FP는 데이터 변환과 안전한 동시성 처리에 우수하다.

현대적 접근법은 다음과 같다:

  • OOP for Strategy, FP for Tactics: 전략적 설계는 OOP로, 세부 구현은 FP로16
  • 상황별 선택: 문제 도메인과 요구사항에 따른 적절한 패러다임 선택
  • 혼합 사용: 단일 프로젝트 내에서도 영역별로 다른 패러다임 적용

결국 **“어떤 패러다임이 더 좋은가?”**보다는 **“주어진 문제에 어떤 패러다임이 더 적합한가?”**를 고민하는 것이 중요하다. 두 패러다임 모두 현대 소프트웨어 개발에서 중요한 도구이며, 개발자는 상황에 맞는 최적의 조합을 찾아 활용해야 한다.

Footnotes

  1. https://stackoverflow.com/questions/2078978/functional-programming-vs-object-oriented-programming 2 3 4

  2. https://dev.to/lovestaco/object-oriented-programming-encapsulation-moving-parts-and-functional-paradigms-30d0

  3. https://kt.academy/article/oop-vs-fp 2

  4. https://www.imaginarycloud.com/blog/functional-programming-vs-oop

  5. https://javanexus.com/blog/state-in-functional-programming 2 3

  6. https://fluxtech.me/blog/object-oriented-programming-vs-functional-programming/

  7. https://proxify.io/articles/object-oriented-vs-functional-programming 2 3 4 5 6 7

  8. https://scand.com/company/blog/functional-programming-vs-oop/ 2 3

  9. https://jaxlondon.com/wp-content/uploads/2020/10/Functional_Programming___State_Management.pdf 2

  10. https://blog.frankel.ch/encapsulation-dont-think-means-think-means/

  11. https://positiwise.com/blog/object-oriented-programming-vs-functional-programming-comparison 2 3 4 5 6

  12. https://www.geeksforgeeks.org/cpp/benefits-advantages-of-oop/

  13. https://www.ileafsolutions.com/functional-vs-object-oriented-programming-pros-and-cons 2

  14. https://www.geeksforgeeks.org/software-engineering/difference-between-functional-programming-and-object-oriented-programming/ 2

  15. https://stackoverflow.com/questions/552336/oop-vs-functional-programming-vs-procedural

  16. https://www.reddit.com/r/SoftwareEngineering/comments/jpsmhf/what_are_the_pros_and_cons_of_using_functional/ 2

  17. https://www.reddit.com/r/ProgrammingLanguages/comments/tv3ltp/functional_programming_vs_object_oriented/