OOP 디자인 패턴을 FP로 재해석한다는 것의 의미
Java 개발자라면 GoF 디자인 패턴에 익숙할 것이다. Strategy, Observer, Decorator 같은 패턴들은 오랫동안 객체지향 설계의 정석으로 자리잡아 왔다. 그런데 Java 8 이후 람다와 함수형 인터페이스가 도입되면서, 이 패턴들을 반드시 클래스와 인터페이스 계층 구조로 구현해야 하는가에 대한 의문이 생기기 시작했다. 함수형 프로그래밍(FP) 관점에서 보면, 기존 OOP 패턴 중 상당수는 언어 수준에서 일급 함수(first-class function)가 지원되지 않았기 때문에 생겨난 우회책에 가깝다.
FP 진영에서는 "OOP와 FP를 어떻게 통합할 것인가"라는 질문에 Turtles all the way down, 즉 "끝없이 이어지는 거북이들"이라는 경구로 답하곤 한다. 물리학자 리처드 파인만의 일화에서 비롯된 이 표현은, 우주가 거북이 위에 있고 그 거북이는 또 다른 거북이 위에 있다는 무한 퇴행 논리를 담고 있다. FP 맥락에서 이 말이 시사하는 바는 명확하다. 함수형 사고를 일부만 적용하려 하면 결국 그 경계를 어디서 그을지 끝없이 고민하게 된다는 것이다. 진정한 통합은 패러다임을 선택적으로 섞는 것이 아니라, 함수형 사고 자체를 설계의 기반으로 삼는 데서 시작된다.
실무에서 패턴이 어떻게 달라지는가
대표적인 예가 Strategy 패턴이다. OOP에서는 인터페이스를 정의하고 구현 클래스를 별도로 만들어야 했지만, FP 스타일에서는 함수 자체를 전달하는 것으로 대체된다.
// OOP 방식
public interface SortStrategy {
List<Integer> sort(List<Integer> list);
}
// FP 방식: 함수를 직접 전달
public List<Integer> process(List<Integer> list,
UnaryOperator<List<Integer>> strategy) {
return strategy.apply(list);
}
Decorator 패턴 역시 함수 합성(function composition)으로 자연스럽게 표현된다. Function.andThen()이나 Function.compose()를 활용하면 클래스 계층 없이도 동일한 확장성을 확보할 수 있다. 실무에서 이 차이는 단순히 코드량의 문제가 아니다. FP 방식은 부수 효과(side effect)를 줄이고 테스트 가능성을 높이며, 각 변환 단계를 독립적으로 이해하고 교체할 수 있게 만든다.
4년차 이상 개발자가 주목해야 할 지점
단순히 람다 문법을 쓴다고 해서 FP 설계를 한다고 볼 수 없다. 핵심은 불변성(immutability), 순수 함수(pure function), 선언적 표현을 설계 전반에 일관되게 적용하는 것이다. 예를 들어 서비스 레이어에서 상태를 직접 변경하는 대신 변환된 새 객체를 반환하거나, 조건 분기를 Optional과 함수 체이닝으로 표현하는 방식이 여기에 해당한다.
특히 레거시 코드베이스를 유지보수하는 상황에서는 OOP 구조를 유지하면서도 내부 구현을 점진적으로 FP 스타일로 전환하는 전략이 현실적이다. 이때 중요한 것은 경계를 명확히 하는 것으로, 도메인 로직의 핵심부터 순수 함수 형태로 정리하고, I/O나 상태 변경이 필요한 부분은 바깥 레이어로 밀어내는 구조가 효과적이다.
정리
- GoF 디자인 패턴 중 상당수는 일급 함수가 없던 시절의 우회책으로, Java 8 이후에는 FP 스타일로 더 간결하게 표현 가능하다.
- FP와 OOP의 통합은 절충이 아니라 함수형 사고를 설계 기반으로 삼는 것에서 출발해야 한다.
- 실무에서는 도메인 핵심 로직을 순수 함수로 정리하고 부수 효과를 외부로 분리하는 점진적 전환이 현실적인 접근이다.