안녕하세요. 함수형 프로그래밍을 주제로 발표한 장호윤입니다. 보다 친숙한 객체지향 프로그래밍 외에 다른 프로그래밍 패러다임에 대해서 다뤄보고자 이 주제를 선정하게 되었습니다.
프로그래밍 패러다임
함수형 프로그래밍은 프로그래밍 패러다임 중 하나입니다. 프로그래밍 패러다임은 프로그래머가 어떤 관점으로 코드를 작성할지 결정하는 역할을 하는데, 최근의 프로그래밍 패러다임은 크게 아래와 같이 구분할 수 있습니다.
- 명령형 프로그래밍
- 절차지향 프로그래밍
- 객체지향 프로그래밍
- 선언형 프로그래밍
- 함수형 프로그래밍
명령형 프로그래밍은 무엇(What)을 할 것인지 나타내기보다 어떻게(How) 할 건지를 설명하는 방식의 패러다임이며 하위에 수행되어야 할 순차적인 처리 과정을 포함하는 절차지향 프로그래밍, 그리고 객체들의 집합으로 프로그램의 상호작용을 표현하는 객체지향 프로그래밍이 있습니다.
선언형 프로그래밍은 어떻게 할건지(How)를 나타내기보다 무엇(What)을 할 건지를 설명하는 방식이며, 함수형 프로그래밍이 이에 속합니다.
함수형 프로그래밍
그렇다면 함수형 프로그래밍은 무엇일까요? 정의하자면 함수형 프로그래밍은 수학적 함수의 계산을 통해 자료를 처리하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임입니다.
다르게 표현하자면, 함수형 프로그래밍은 함수들의 조합으로 프로그램을 작성하는 패러다임입니다. 각 함수는 입력을 받아 결과를 반환하며 내부적으로 어떠한 상태도 가지지 않습니다. 이로 인해 함수 호출 시 입력과 결과에만 집중하고 함수 내부에서 발생하는 일에 대해 신경 쓸 필요가 없습니다. 함수형 프로그래밍은 상태 변경을 피하고 부수 효과를 최소화하여 코드의 예측 가능성과 유지 보수성을 높이는 데 중점을 둡니다.
함수형 프로그래밍이 주목받는 이유
함수형 프로그래밍 언어는 객체지향과 같은 언어보다 더 오래된 역사를 가지고 있지만, 객체지향이나 절차지향 언어가 사람의 사고 흐름과 유사하여 더 널리 사용되었습니다. 그럼에도 불구하고, 최근 몇 년간 함수형 프로그래밍이 주목받는 이유는 여러 측면에서 이점을 제공하기 때문입니다.
첫째로, 함수형 프로그래밍은 가독성과 코드 재사용성을 높여주는 특성을 갖고 있습니다. 함수는 입력에 대한 출력만을 반환하고 내부적으로 상태를 유지하지 않기 때문에 코드의 동작을 이해하기 쉽고 예측하기 쉽습니다.
둘째로, 함수형 프로그래밍은 데이터 처리와 계산의 병렬화에 특히 유용합니다. 최근에는 AI, IoT, 빅데이터와 같은 분야에서 방대한 데이터를 빠르게 처리하고 병렬적으로 안정적으로 처리하는 것이 중요해졌는데, 함수형 프로그래밍은 이러한 요구사항을 충족시키는 데 효과적입니다.
특히, 함수형 프로그래밍에서는 데이터가 변경 불가능하고 부수 효과가 없으므로 여러 스레드가 동시에 공유 데이터에 접근해도 데이터가 변경되는 것을 막아줍니다. 이로 인해 동시성과 관련된 문제를 해결하는 데 유리합니다.
또한, 함수형 프로그래밍은 코드를 테스트하기 쉽고 함수의 역할이 명확하여 개발자가 코드를 이해하고 유지보수하기 쉽도록 돕습니다.
함수형 프로그래밍의 특징
함수형 프로그래밍의 특징을 한 줄로 요약하면 아래와 같습니다.
부수 효과가 없는 순수 함수를 1급 객체로 간주하여 파라미터나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있다.
1. 부수효과가 없는 순수함수
부수효과(Side Effect)란 다음과 같은 변화 또는 작업을 의미합니다.
- 변수값 변경
- 함수나 코드 블록 내에서 자료 구조(예: 배열, 리스트)의 내용이 수정
- 객체의 필드값을 설정
- 예외나 오류가 발생하며 실행 중단
- 콘솔 또는 파일 I/O 발생
이러한 부수 효과(Side Effect)를 제거한 함수를 순수 함수(Pure Function)라고 합니다. 함수형 프로그래밍에서는 주로 이러한 순수 함수를 사용합니다. 부수효과는 프로그램의 예측성과 안정성을 저해할 수 있으며, 함수형 프로그래밍에서는 이러한 부수효과를 제거한 순수함수를 사용함으로써 더욱 안정적이고 확장 가능한 소프트웨어를 개발하는 데 초점을 맞춥니다.
2. 1급 객체
1급 객체(First-class object)는 다음과 같은 특성을 가진 객체를 말합니다:
- 변수나 데이터 구조 안에 담기 가능
- 파라미터로 전달 가능
- 반환값으로 사용 가능
- 할당에 사용된 이름과 무관하게 고유한 구별 가능
함수형 프로그래밍에서는 함수가 1급 객체로 취급되기 때문에 함수를 변수에 할당하거나 데이터 구조 안에 담을 수 있으며, 함수를 다른 함수의 인자로 전달하거나 함수의 반환값으로 사용할 수 있습니다.
예를 들어, Java 8에서 추가된 함수형 인터페이스를 사용하면 메서드를 객체로 다룰 수 있습니다. Java에서는 객체가 1급 시민이므로, 함수 역할을 하는 객체를 변수에 담거나 파라미터로 전달하고 반환값으로 사용하는 것이 가능합니다. 이를 통해 함수형 프로그래밍의 많은 이점을 활용할 수 있습니다.
3. 참조투명성
참조 투명성(Referential Transparency)은 다음과 같은 특성을 가지는 개념입니다:
-
동일한 인자에 대해 항상 동일한 결과를 반환해야 합니다. 즉, 어떤 함수 f에 동일한 인자 x를 넣고 실행하면 항상 f(x)라는 동일한 결과를 얻어야 합니다.
-
참조 투명성이 있다는 것은 함수를 실행하여도 기존의 값이 변경되지 않고 유지된다는 것을 의미합니다. 이는 불변 데이터(Immutable Data)와 관련이 있습니다.
-
함수를 실행해도 어떠한 상태의 변화 없이 항상 동일한 결과를 반환하여 항상 동일하게 실행 결과를 참조할 수 있다는 것을 의미합니다.
함수형 프로그래밍에서는 값의 대입이 없고, 부작용을 제거하여 프로그램의 동작을 이해하고 예측하기 쉽게 합니다. 따라서 참조 투명성은 함수형 프로그래밍에서 코드를 이해하고 유지보수하는 데 매우 유용한 개념입니다.
자바에서의 함수형 프로그래밍
Java는 기본적으로 객체지향 언어이지만, JDK 8부터는 Stream API와 람다식, 함수형 인터페이스 등을 도입하여 함수형 프로그래밍의 개념과 기능을 지원하게 되었습니다.
그 중 Stream API이 함수형 프로그래밍의 관점에서 가지는 여러가지 장점들에 대해서 소개해보고자 합니다.
이전에는 배열이나 컬렉션을 다룰 때 for문이나 for each 문을 사용하여 요소를 하나씩 처리하는 방식이 일반적이었습니다. 그러나 이러한 방식은 로직이 복잡해지면 코드 양이 늘어나고 가독성이 떨어지며, 여러 로직이 섞이게 되는 등의 문제를 야기할 수 있었습니다.
하지만 Stream API를 이용하면 다음과 같은 측면에서 이점을 얻을 수 있습니다.
-
내부 반복 (Internal Iteration): 스트림은 내부 반복을 통해 작업을 처리합니다. 이는 개발자가 직접 반복문을 작성하지 않고도 데이터를 순회하고 가공할 수 있도록 해줍니다. 내부 반복을 사용하면 코드가 간결해지고 가독성이 향상되며, 개발자가 데이터를 처리하는 방법에 집중할 수 있습니다.
-
코드의 간결성과 가독성: 람다식과 스트림을 함께 사용하면 코드의 양을 줄이고 가독성을 높일 수 있습니다. 스트림은 함수형 프로그래밍 스타일에 부합하는 API를 제공하며, 간결하고 명확한 코드를 작성할 수 있도록 도와줍니다.
-
불변성과 부작용 최소화: 스트림은 데이터를 변환하는 과정에서 불변성(Immutable)을 유지하고 부작용(Side Effect)을 최소화하는 함수형 프로그래밍의 원칙을 따릅니다. 이는 코드의 예측 가능성과 안정성을 높여줍니다.
-
병렬 처리 가능: 스트림은 병렬 처리를 위한 기능을 제공합니다. 데이터를 여러 스레드에 분산하여 동시에 처리함으로써 성능을 향상시킬 수 있습니다. 이는 대용량 데이터를 효율적으로 처리하는 데 매우 유용합니다.
-
코드 재사용성과 모듈화: 스트림을 사용하면 코드를 더 작고 재사용 가능한 단위로 분리할 수 있습니다. 함수형 프로그래밍의 원칙에 따라 각각의 스트림 연산은 독립적으로 동작하므로, 이러한 연산들을 조합하여 복잡한 작업을 수행할 수 있습니다.
결론적으로,이러한 장점들은 스트림을 통해 함수형 프로그래밍 스타일을 채택한 Java 코드를 더 간결하고 유연하게 만들어줍니다. 함수형 프로그래밍의 개념을 활용하면 코드를 더 쉽게 이해하고 유지보수할 수 있으며, 높은 수준의 추상화를 통해 코드의 품질을 향상시킬 수 있습니다.
함수형 프로그래밍 vs 객체지향 프로그래밍?
함수형 프로그래밍과 객체지향 프로그래밍은 서로 상반된 개념이 아니라 함께 어우러져서 사용될 수 있습니다. 이 두 가지 프로그래밍 패러다임은 각각의 장점을 결합함으로써 유연하고 효율적인 소프트웨어 시스템을 구축하는 데 도움이 됩니다.
객체지향 프로그래밍의 다형성은 강하게 비결합된 시스템을 만들어주어 설계된 구조의 경계 사이에서 종속성이 역전될 수 있도록 합니다. 이는 모킹이나 가짜 객체들을 사용하여 테스트가 가능하고, 다른 모듈들에 영향을 주지 않으면서도 수정될 수 있는 유연한 시스템을 만들어줍니다. 반면, 함수형 프로그래밍의 참조 투명성은 시스템을 예측 가능하게 만들어줍니다. 내부 상태를 변경할 수 없다는 성질은 시스템을 이해하기 쉽고, 개발하기 쉽게 만들어주며, 스레드 경쟁 상태와 같은 동시성 관련 문제를 현저히 줄여줍니다.
따라서 한 가지 방법론을 선택하고 다른 쪽을 배제하기 보다 함수형 프로그래밍과 객체지향 프로그래밍을 함께 사용될 때 최적의 결과를 얻을 수 있습니다. 이 두 가지 방법을 조화롭게 활용하면 유연하고, 유지보수 가능하며, 테스트 가능하면서도 단순하고 완성도 높은 소프트웨어 시스템을 구축할 수 있습니다.
참고 자료
https://mangkyu.tistory.com/111 https://mangsby.com/blog/programming/fp-vs-oop/ https://wildeveloperetrain.tistory.com/52#google_vignette