목차
서문
함수형 프로그래밍이라는 용어를 여러 번 들어봤고, 인터넷에서 함수형 프로그래밍을 활용한 코드 예시를 따라 해 본 적도 있습니다. 그러나 객체지향 프로그래밍에 비해 함수형 프로그래밍이 무엇인지 명확히 이해하고 적용해 본 경험은 부족했습니다. 개발자로서 이러한 부분이 항상 아쉬웠는데, 최근에 ‘쏙쏙 들어오는 함수형 코딩’이라는 책을 접하게 되어 배운 내용을 정리하고 공유하고자 합니다. 이 글에서는 함수형 프로그래밍의 핵심 개념인 액션, 계산, 데이터에 대해 알아보고, 이를 통해 함수형 사고를 기존 코드에 어떻게 적용해서 코드의 품질을 향상시킬 수 있는지 살펴보겠습니다.
액션, 계산, 데이터
함수형 프로그래밍에서는 코드를 세 가지 요소로 나누어 생각합니다: 액션, 계산, 데이터. 각각의 요소는 프로그램의 다른 측면을 나타내며, 이를 명확히 구분함으로써 코드의 품질과 유지보수성을 향상시킬 수 있습니다.
액션
- 특징:
- 조심스럽게 다뤄야 하는 코드: 액션은 부수 효과가 있으므로 신중하게 관리해야 합니다.
- 부수 효과가 있는 코드: 외부 상태를 변경하거나 외부 상태에 의존합니다.
- 외부에 영향을 미치는 코드: 프로그램 외부의 환경에 변화를 일으킵니다.
- 실행 결과가 실행 시점과 순서, 반복 횟수에 영향을 받는 코드: 동일한 코드를 실행해도 실행 시점이나 횟수에 따라 결과가 달라질 수 있습니다.
- 코드 전체로 퍼지는 영향: 함수의 일부만 액션이어도 함수 전체가 액션으로 분류될 수 있습니다.
- 예시:
- 파일 입출력: 파일을 읽거나 쓰는 코드
- 네트워크 통신: HTTP 요청을 보내거나 응답을 받는 코드
- 데이터베이스 작업: 쿼리를 실행하거나 데이터를 갱신하는 코드
- 콘솔 출력: 화면에 메시지를 출력하는 코드
계산
- 특징:
- 외부에 영향을 미치지 않는 코드: 부수 효과가 없으며, 외부 상태를 변경하지 않습니다.
- 실행 결과가 입력에만 의존하는 코드: 동일한 입력에 대해 항상 동일한 결과를 반환합니다.
- 순수 함수: 함수형 프로그래밍에서 핵심적인 역할을 하는 함수
- 예시:
- 산술 연산: 두 수를 더하거나 곱하는 함수
- 데이터 변환: 문자열을 다른 형식으로 변환하는 함수
- 컬렉션 처리: 리스트를 필터링하거나 맵핑하는 함수
- 장점:
- 테스트 용이성: 부수 효과가 없으므로 단위 테스트가 쉽습니다.
- 재사용성: 외부 상태에 의존하지 않으므로 다양한 상황에서 재사용할 수 있습니다.
- 예측 가능성: 입력이 같으면 결과가 항상 같으므로 코드의 신뢰성이 높아집니다.
데이터
- 특징:
- 이벤트에 대해 기록한 정보: 프로그램이 처리하는 모든 입력과 출력
- 불변성: 가능하면 변경되지 않는 불변 데이터로 취급하여 상태 변이를 최소화합니다.
- 예시:
- 사용자 입력: 사용자가 입력한 이메일 주소나 비밀번호
- API 응답 데이터: 서버로부터 받은 JSON 데이터
- 설정 값: 설정 파일에 저장된 구성 값들
- 장점:
- 명확성: 데이터 구조와 형식을 명확히 정의하여 코드의 이해도를 높입니다.
- 안정성: 불변 데이터를 사용하면 예기치 않은 상태 변경을 방지할 수 있습니다.
액션과 계산을 분리하는 방법
액션과 계산을 분리하는 것은 함수형 프로그래밍에서 매우 중요한 원칙입니다. 이를 통해 코드의 테스트 용이성, 가독성, 재사용성을 높일 수 있습니다.
암묵적 입력과 출력 식별
- 암묵적 입력: 함수 내부에서 전역 변수나 외부 상태를 참조하는 경우
- 암묵적 출력: 함수가 반환하지 않고 외부 상태를 변경하는 경우
- 예시:
- 전역 변수에 의존하는 함수
- 파일 시스템이나 네트워크를 직접 조작하는 함수
명시적 입력과 출력으로 변환
- 명시적 입력: 함수의 매개변수로 필요한 모든 데이터를 전달합니다.
- 명시적 출력: 함수의 반환값으로 결과를 전달합니다.
- 방법:
- 전역 변수를 사용하던 코드를 함수 인자로 대체합니다.
- 외부 상태를 변경하던 코드는 반환값을 통해 결과를 전달하도록 수정합니다.
순수 함수로 리팩토링
- 부수 효과 제거: 함수 내부에서 외부 상태를 변경하지 않도록 합니다.
- 입력과 출력 명시화: 함수의 인터페이스를 통해 필요한 모든 데이터를 주고받습니다.
-
예시:
# 부수 효과가 있는 함수 global_counter = 0 def increment_counter(): global global_counter global_counter += 1 # 순수 함수로 변환 def increment(value): return value + 1
계층형 구조와 함수형 프로그래밍
함수형 프로그래밍에서는 코드의 안정성과 변경 용이성을 고려하여 계층형 구조를 적용합니다. 이는 코드의 복잡성을 관리하고 유지보수성을 향상시키는 데 도움을 줍니다.
계층의 구성
- 데이터 계층 (하위 계층)
- 역할: 프로그램에서 사용하는 모든 데이터와 그 구조를 정의합니다.
- 특징:
- 변화가 거의 없는 안정적인 부분입니다.
- 불변 데이터를 사용하여 상태 변이를 최소화합니다.
- 계산 계층 (중간 계층)
- 역할: 데이터 계층의 정보를 처리하는 순수 함수들로 구성됩니다.
- 특징:
- 외부 상태에 의존하지 않으므로 재사용성과 테스트 용이성이 높습니다.
- 비즈니스 로직과 알고리즘을 구현합니다.
- 액션 계층 (상위 계층)
- 역할: 외부와의 상호 작용을 담당합니다.
- 특징:
- 입출력, 네트워크 통신, 파일 입출력 등의 부수 효과를 처리합니다.
- 변화가 잦고 예측이 어려우므로 안정성이 낮습니다.
계층형 구조의 장점
- 모듈화 강화: 각 계층이 명확한 책임을 가지므로 코드의 구조가 명확해집니다.
- 변경 영향 최소화: 상위 계층의 변경이 하위 계층에 영향을 주지 않습니다.
- 유지보수성 향상: 변화가 잦은 액션 계층과 안정적인 계산, 데이터 계층을 분리하여 관리할 수 있습니다.
- 테스트 용이성: 순수 함수로 구성된 계산 계층은 단위 테스트가 쉽습니다.
마치며
함수형 프로그래밍의 핵심 개념인 액션, 계산, 데이터를 이해하고 이를 코드에 적용하면, 프로그램의 가독성, 재사용성, 유지보수성을 크게 향상시킬 수 있습니다. 함수형 프로그래밍은 상태 변경을 최소화하고, 순수 함수를 적극 활용하여 예측 가능한 코드를 작성하는 데 중점을 둡니다. 액션과 계산을 분리하고, 계층형 구조를 통해 코드의 안정성과 확장성을 높이는 방법을 통해 복잡한 시스템에서도 코드의 품질을 유지하며 개발 생산성을 향상시킬 수 있을 것입니다.