함수형 프로그래밍(FP)과 객체지향 프로그래밍(OOP)의 핵심 차이는 무엇을 중심으로 코드를 조직하느냐입니다. 이 글은 두 패러다임의 철학적 차이, 유지보수성과 확장성 관점의 장단점, 그리고 TypeScript/React 실전 예제를 통해 프로젝트에 맞는 선택을 빠르게 내릴 수 있게 돕습니다.
December 2, 2025 (4mo ago) — last updated January 3, 2026 (3mo ago)
함수형 프로그래밍 vs 객체지향: 실전 가이드
함수형과 객체지향의 차이, TypeScript·React 실전 예제, 적용 기준과 체크리스트로 프로젝트에 맞는 설계를 빠르게 결정하세요.
← Back to blog
함수형 프로그래밍 vs 객체지향: 실전 가이드
함수형 프로그래밍 vs 객체지향: 패러다임을 비교하고 실용적인 코드 예제를 확인하며 프로젝트에 가장 적합한 접근법을 선택하는 방법을 배우세요.

소개
함수형 프로그래밍(FP)과 객체지향 프로그래밍(OOP)의 핵심 차이는 무엇을 중심으로 코드를 조직하느냐입니다. FP는 연산을 중심으로, 즉 무엇을 하는지에 집중해 상태 변경을 피하는 순수 함수와 불변성을 강조합니다. OOP는 객체, 즉 누가 행동을 수행하는지를 중심으로 상태와 동작을 캡슐화합니다. 이 글은 두 패러다임을 비교하고 TypeScript와 React 예제로 실무에서의 적용 차이를 보여주며 프로젝트에 맞는 선택을 돕습니다.
FP vs OOP 빠른 개관
패러다임 선택은 단순한 문법의 문제가 아니라 시스템 설계, 데이터 흐름, 그리고 팀의 작업 방식에 대한 약속입니다. 많은 팀이 두 접근법의 장점을 혼합해 사용합니다. 주요 차이를 빠르게 요약하면 다음과 같습니다.
핵심 철학적 차이
OOP는 데이터와 동작을 객체로 묶어 캡슐화합니다. 예를 들어 사용자 데이터를 보관하고 이메일 업데이트 같은 메서드를 제공하는 User 객체를 생각해보세요. 객체는 자신의 상태를 관리합니다.
FP는 데이터와 동작을 분리합니다. 데이터는 대개 불변이며, 순수 함수는 입력에 따라 항상 같은 출력을 반환하고 부작용을 피합니다. 이 접근은 공유 가변 상태로 인한 버그를 줄여 동시성 처리에 유리합니다.
“나중에 코드를 이해하는 것이 진짜 과제입니다. FP는 이동하는 부품을 최소화하고, OOP는 그 부품들을 이해 가능한 구성 요소로 조직합니다.”
함수형과 객체지향의 핵심 차이
| 개념 | 함수형 프로그래밍 (FP) | 객체지향 프로그래밍 (OOP) |
|---|---|---|
| 주요 단위 | 함수 | 객체 |
| 상태 관리 | 불변 상태 | 가변 상태 |
| 데이터 & 연산 | 분리 | 캡슐화 |
| 동시성 | 처리 용이 (무상태) | 동기화 필요할 수 있음 |
| 흐름 제어 | 함수 합성 | 메서드 호출, 루프 |
| 핵심 원칙 | 순수 함수, 불변성 | 캡슐화, 상속, 다형성 |
핵심 철학 이해하기
패러다임은 코드 작성 방식뿐 아니라 시스템을 어떻게 사고하느냐의 차이입니다. 어떤 패러다임이 적합한지는 변화와 복잡성을 어떻게 관리할지에 달려 있습니다.

함수형 프로그래밍 패러다임
FP는 람다 계산법에서 기원을 찾으며, 같은 입력에 대해 항상 같은 출력을 반환하는 순수 함수와 불변성을 중심으로 합니다. 이 예측 가능성은 테스트와 병렬 처리에서 큰 장점이 됩니다.
객체지향 프로그래밍 패러다임
OOP는 상태와 행동을 함께 묶어 복잡한 도메인 모델을 직관적으로 표현합니다. 캡슐화는 내부 구현을 숨기고 간단한 인터페이스를 제공하도록 도와 설계를 단순화합니다.
유지보수성과 확장성 비교
패러다임 선택은 장기적인 유지보수와 확장성에 직접적인 영향을 줍니다. 아래는 실무 관점의 비교입니다.
유지보수성: 예측성 vs 캡슐화
FP는 순수 함수와 불변성을 통해 예측 가능한 코드를 만듭니다. 이는 함수를 독립적으로 테스트하고 버그를 좁히기 쉽게 합니다. 반면 OOP는 관련 데이터와 동작을 그룹화해 시스템을 모듈화하고 이해하기 쉬운 경계를 제공합니다.
절충점: FP는 데이터-변환 관점에서 명확성을 제공하고, OOP는 객체 경계로 명확성을 제공합니다. 대부분의 대형 코드베이스는 이 둘을 혼합해 사용합니다1.
확장성: 동시성 및 병렬성
FP의 무상태 접근법은 동시성과 병렬성을 단순화합니다. 반면 OOP는 공유 상태를 관리하기 위해 동기화가 필요할 수 있습니다. 현대 애플리케이션에서는 다중 패러다임 언어가 주류이며, JavaScript/TypeScript 같은 언어의 폭넓은 채택은 이 추세를 반영합니다2.
실무 예제: TypeScript와 React
아래는 동일한 사용자 설정 폼을 OOP 스타일의 클래스형 React 컴포넌트와 함수형 Hooks 기반 컴포넌트로 구현한 예입니다. 두 예제는 상태 처리, 로직 재사용성, 구조 차이를 보여줍니다.

OOP 접근: React 클래스 컴포넌트
클래스 컴포넌트는 상태와 메서드를 하나의 객체로 묶어 캡슐화하는 OOP 모델에 적합합니다.
import React, { Component } from 'react';
interface UserSettings {
name: string;
email: string;
}
class UserSettingsForm extends Component<{}, UserSettings> {
state = {
name: 'Jane Doe',
email: 'jane.doe@example.com',
};
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
this.setState({ [name]: value } as Pick<UserSettings, keyof UserSettings>);
};
handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
console.log('Submitting data:', this.state);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input name="name" value={this.state.name} onChange={this.handleChange} />
<input name="email" value={this.state.email} onChange={this.handleChange} />
<button type="submit">Save Settings</button>
</form>
);
}
}
이 패턴은 데이터와 행동을 한 곳에 모아 객체의 책임을 명확히 하지만, 재사용과 테스트를 위해 추가적인 패턴이 필요할 때가 있습니다.
함수형 리팩터: Hooks와 순수 헬퍼 함수
함수형 접근은 상태를 분리하고 순수 함수를 통해 로직을 재사용하기 쉽게 만듭니다.
import React, { useState } from 'react';
const formatUserDataForApi = (name: string, email: string) => ({
userName: name,
userEmail: email,
});
const UserSettingsFormFunctional = () => {
const [name, setName] = useState('Jane Doe');
const [email, setEmail] = useState('jane.doe@example.com');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
if (name === 'name') {
setName(value);
} else {
setEmail(value);
}
};
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
const payload = formatUserDataForApi(name, email);
console.log('Submitting data:', payload);
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={handleChange} />
<input name="email" value={email} onChange={handleChange} />
<button type="submit">Save Settings</button>
</form>
);
};
React의 Hooks는 함수형 컴포넌트에서 상태와 부작용을 관리하는 패턴을 대중화했습니다3. 관련 가이드는 React Hooks 소개에서 확인하세요.
올바른 패러다임 선택 방법
끝장 싸움이 아닙니다. 올바른 선택은 팀, 도메인, 장기 목표에 따라 달라집니다. 아래 포인터를 실무 판단에 활용하세요.
언제 OOP를 선택해야 하나
다음과 같은 경우 OOP를 권장합니다:
- 영속 상태와 행동을 가진 복잡한 도메인 엔터티를 모델링할 때
- 모듈 간 상호연관이 많은 엔터프라이즈 시스템
- UI 컴포넌트의 상태가 객체 모델에 자연스럽게 매핑될 때
- 도메인이 잘 정의되어 있고 안정적일 때
언제 FP를 선택해야 하나
다음과 같은 경우 FP가 더 적합합니다:
- 예측성, 테스트 용이성, 동시성이 중요할 때
- 데이터 처리 파이프라인이나 ETL 작업
- 알고리즘이 함수로 깔끔하게 매핑될 때
실용적 결정 체크리스트
- 데이터의 성격은 무엇인가: 상태 중심인가, 변환 중심인가?
- 동시성이 성능과 정합성에 얼마나 중요한가?
- 팀의 전문성과 학습 여력은 어떤가?
- 혼합 접근이 가능한가? 강제할 필요는 없다.
혼합 접근 수용하기
대부분 성공적인 시스템은 두 패러다임을 결합합니다. TypeScript나 Python 같은 다중 패러다임 언어는 클래스와 불변 데이터를 함께 쓰고, 컬렉션에 map/filter를 적용하며 핵심 비즈니스 로직을 순수 함수로 분리할 수 있게 해줍니다. 관련 스타일과 모범 사례는 TypeScript 모범 사례에서 더 확인하세요.
실무에서 패러다임 결합하기
일반적인 하이브리드 패턴 예:
- 클래스 내부에서 불변 상태를 사용해 메서드는 새 인스턴스를 반환
- 핵심 서비스 로직은 순수 함수로 구현
- 컬렉션 처리에
map,filter,reduce사용
객체는 개념을 모델링하고 순수 함수는 그들 사이의 동작을 오케스트레이션하게 하세요. 이렇게 하면 테스트성과 가독성이 향상됩니다.

자주 묻는 질문
함수형 프로그래밍이 객체지향보다 빠른가요?
성능은 언어와 런타임, 작업 특성에 달려 있습니다. 불변성은 추가 할당을 유발할 수 있지만 동시성 환경에서는 처리량을 개선할 수 있습니다. 단일 스레드에서 제자리 업데이트가 많은 작업은 가변 상태 전략이 더 빠를 수 있습니다.
함수형 코드와 객체지향 코드를 섞어도 되나요?
네. 혼합은 일반적이고 실용적입니다. 핵심 엔터티는 객체로 모델링하고 복잡한 로직은 순수 함수로 분리하세요.
초보자는 어떤 패러다임을 먼저 배워야 하나요?
OOP는 현실 세계 개념과 직관적으로 매핑되어 시작하기 쉽습니다. 다만 순수 함수와 불변성 개념을早期에 익히면 코드 품질이 빨리 향상됩니다.
요약 권장사항
- 문제에 맞는 패러다임을 선택하세요. 예측 가능한 데이터 변환과 동시성에는 FP를, 풍부한 도메인 모델링에는 OOP를 사용하세요.
- 핵심 로직에는 작은 순수 함수를 선호하세요. 컴포넌트와 클래스는 작고 책임이 분명하도록 유지하세요.
- 하이브리드 접근을 활용하세요. 전체 코드베이스에 하나의 패러다임만을 고집할 필요는 없습니다.
실용 Q&A
Q: 기존 코드베이스에서 FP와 OOP 중 무엇을 선택해야 하나요?
A: 가장 큰 문제 지점을 평가하세요. 버그가 공유 가변 상태에서 빈번히 발생하면 불변성과 순수 함수를 도입하세요. 도메인이 본질적으로 객체 기반이면 객체 모델을 유지하되 상태 없는 서비스를 추출하세요.
Q: OOP 프로젝트에 FP 원칙을 안전하게 도입하려면?
A: 작은 서비스부터 시작해 순수 함수로 비즈니스 로직을 분리하세요. 배열 처리에 map/filter를 사용하고, 상태 변경 대신 새 객체를 반환하는 패턴을 점진적으로 적용하세요.
Q: 오늘 유지보수성을 빠르게 개선하려면?
A: 작은 순수 함수로 로직을 분리하고 자동화된 테스트를 추가하세요. 가변 루프를 map/filter로 바꾸고, 상태가 있는 객체의 불변성 규약을 문서화하세요.
AI가 코드를 작성합니다.당신이 그것을 지속시킵니다.
AI 가속 시대에 클린 코드는 단순히 좋은 관행이 아닙니다 — 확장되는 시스템과 자체 무게로 붕괴되는 코드베이스의 차이입니다.