함수형 프로그래밍? 순수 함수? 일급 함수?

박요셉·2024년 5월 14일
0

React

목록 보기
1/15

함수형 프로그래밍

함수형 프로그래밍(FP)는 계산을 수학 함수의 평가로 취급하고 상태 변경 및 변경 가능한 데이터를 피하는 프로그래밍 패러다임이다.
React와 결합할 때, FP는 예측 가능하고, 모듈식이며, 유지 보수가 가능한 코드로 이어진다.
여기 React 컨텍스트에서의 함수형 프로그래밍 개요가 있다.

함수형 프로그래밍이란 무엇인가?

함수형 프로그래밍은 프로그래밍 패러다임의 한 종류로, 계산을 수학적 함수의 평가로 취급하고 상태 변경과 가변 데이터를 피하는 것을 중점으로 둡니다. 이 패러다임은 순수 함수를 사용하여 부작용(side effects)을 최소화하며, 데이터 처리를 위해 함수의 조합과 함수 간의 관계를 강조합니다. 결과적으로, 함수형 프로그래밍은 예측 가능하고, 재사용 가능하며, 테스트하기 쉬운 코드를 작성하도록 돕습니다.

계산을 수학적 함수의 평가로 취급한다.
**"계산을 수학적 함수의 평가로 취급한다"**는 표현은 함수형 프로그래밍의 핵심 원리 중 하나로, 이것이 의미하는 바는 프로그램의 작동 방식이 수학적 함수처럼 이해될 수 있고, 이러한 함수의 사용 방식이 프로그램의 구조와 동작을 정의하는데 중점을 두고 있다는 것이다.

수학에서, 함수는 하나 이상의 입력을 받아 결과를 생성한다. 이 과정은 외부 상태에 영향을 받지 않으며, 동일한 입력에 대해서는 항상 동일한 출력을 제공한다. 이러한 특징은 **'순수성'**으로 알려져 있으며, 순수 함수는 부작용이 없는 함수를 말한다.

함수형 프로그래밍에서 **"계산을 수학적 함수의 평가로 취급한다"**는 것은 다음과 같은 이점을 가지고 있다.

1. 예측 가능성: 함수가 외부 상태에 의존하지 않고 동일한 입력에 대해 항상 동일한 출력을 제공하기 때문에, 프로그램의 동작이 예측 가능하다.
2. 모듈성 : 각 함수는 독립적인 단위로 작동하므로, 코드의 각 부분을 별도로 이해하고 테스트하며 재사용할 수 있다.
3. 버그 감소: 부작용이 없으므로, 시스템에서 버그가 발생할 가능성이 줄어들며, 디버깅이 더 간편해진다.
4. 병행성 처리: 순수 함수는 스레드에 안전하므로 병행 및 병렬 프로그래밍이 용이해진다.

요약하자면, 함수형 프로그래밍은 수학적 함수의 특성을 활용하여 코드의 안정성, 클린함, 그리고 관리 용이성을 증진시키는 프로그래밍 패러다임이다.

핵심 개념

  • 불변 데이터(Immutable Data): 데이터가 한 번 생성되면 변경할 수 없다.
  • 일급 및 고차 함수(First-Class and Higher-Order Functions) : 함수는 인수로 전달되거나, 값으로 반환되거나, 변수에 저장될 수 있다.
  • 순수 함수(Pure Functions) : 함수의 출력은 입력 값에만 의해 결정되며 부작용이 없다.

함수형 프로그래밍의 역사

"함수형 프로그래밍의 역사"는 컴퓨터 과학과 프로그래밍 언어의 발전과 밀접하게 연결되어 있으며, 수학적 이론과 실제 컴퓨팅의 필요 사이의 간극을 메우는 데 중요한 역할을 해왔다.

  1. 람다 대수 : 함수형 프로그래밍의 근간은 1930년대 알론조 처치에 의해 개발된 람다 대수에 있다.
    람다 대수는 모든 계살을 수학적 함수의 평가로 분석하는 방법을 제공하며, 이는 후에 함수형 프로그래밍 언어의 핵심 개념이 되었다.

  2. LISP : 1958년, 존 매카시는 LISP를 개발했는데, 이것은 함수형 프로그래밍 언어의 선구자 중 하나로 널리 알려져 있다.
    LISP는 계산을 함수와 그 평가로 간주하는 람다 대수의 개념을 구체화한 최초의 프로그래밍 언어 중 하나였다.

  3. ML과 Haskell: 1970년대와 80년대에, 더 진보한 함수형 언어들이 등장했다.
    ML은 정적 타입 시스템을 도입했고, Haskell은 1990년에 발표되어 순수 함수형 프로그래밍 언어로 널리 인정받았다.

  4. Erlang : 1980년대 말, Ericsson에서 Erlang이 개발되었으며, 이것은 분산, 내결함성, 실시간 시스템에 적합한 함수형 언어로 자리잡았다.

  5. 현대의 발전 : 21세기에 들어서며, 함수형 프로그래밍은 스칼라, 클로저, F# 등의 새로운 언어들과 함께 주류로 자리 잡기 시작했다.
    또한, 기존의 객체 지향 언어들도, 예를 들어, 자바와 C#에서 람다 함수의 도입과 같은 함수형 기능을 통합하기 시작했다.


    함수형 프로그래밍은 그 이론적 기초와 람다 대수의 원리 덕분에 병렬 처리와 동시성, 그리고 소프트웨어 설계의 다양한 측면에서 이점을 제공하는 견고한 프로그래밍 패러다임으로 자리 잡았다.
    이러한 이유로, 현대의 복잡한 컴퓨팅 환경, 특히 클라우드 컴퓨팅, 분산 시스템, 대용량 데이터 처리 등의 분야에서 함수형 프로그래밍의 중요성이 더욱 부각되고 있다.

객체지향(OOP)에서 함수형 프로그래밍으로..왜?

객체지향의 문제점과 함수형 프로그래밍의 장점

함수형 프로그래밍과 객체 지향 프로그래밍은 각기 다른 문제 해결 방식과 프로그래밍 접근 방식을 제공한다.
객체 지향 프로그래밍이 널리 사용되기 시작한 것은 1970년대부터이며, 데이터와 데이터를 조작하는 방법을 하나의 '객체'에 캡슐화하는 것에 중점을 두었다.
이 접근 방식은 소프트웨어 엔지니어링, 특히 대규모 시스템에서 코드의 재사용성, 확장성 및 관리 용이성을 향상시키는 데 큰 도움이 되었다.

그러나 객체 지향 프로그래밍이 일부 문제를 해결하는 데 탁월했음에도 불구하고, 다음과 같은 여러 문제를 완전히 해결하지 못했다

  1. 부작용: OOP에서 메서드는 그들의 상태를 변경시킬 수 있는데, 이는 예측 불가능한 부작용을 초래할 수 있다.
  2. 동시성 제어 : 멀티 스레드 환경에서 여러 객체의 상태를 관리하고 동기화하는 것은 매우 복잡하다.
  3. 불변성 : 객체의 상태가 시간이 지남에 따라 변경될 수 있으므로, 코드의 복잡성이 증가하고 오류가 발생할 가능성이 높아진다.

이러한 문제를 해결하고자, 함수형 프로그래밍이 주목받기 시작했다.
함수형 프로그래밍은 다음과 같은 이점을 제공한다.

  1. 불변성 : 데이터는 생성 후 변경되지 않으므로, 여러 스레드에서 안전하게 접근할 수 있다.

  2. 함수의 일급 시민 : 함수는 다른 함수에 인수로 전달되거나 결과로 반환될 수 있으므로, 더 유연하고 재사용 가능한 코드를 작성할 수 있습니다.

  3. 부작용의 최소화 : 순수 함수는 동일한 입력에 대해 항상 동일한 출력을 생성하므로, 시스템의 전반적인 예측 가능성이 향상된다.

또한, 오늘날의 애플리케이션은 높은 수준의 병행성과 비동기 처리를 요구하는데, 함수형 프로그래밍은 이러한 복잡성을 추상화하고 코드의 안정성을 향상시키는 데 도움을 준다.

요약하자면, 객체 지향 프로그래밍과 함수형 프로그래밍은 서로 다른 접근 방식과 장단점을 가지고 있다.
많은 현대적인 언어는 이 두 패러다임을 결합하여, 개발자가 상황에 가장 적합한 도구를 선택할 수 있도록 한다.

FP vs OOP 차이점

React에서 함수형 프로그래밍이 각광받게 된 이유

웹 개발, 특히 React에서 함수형 프로그래밍이 각광받게 된 이유는 여러 가지가 있다.
React의 주요 개념과 함수형 프로그래밍의 원칙 사이에는 많은 연관성이 있기 때문에, 이 두 방식이 자연스럽게 어우러지는 경향이 있다.

  • 컴포넌트 기반 접근 : React는 UI를 독립적이고 재사용 가능한 컴포넌트로 구성하는 방식을 채택한다.
    함수형 프로그래밍 또한 작고 독립적인 단위로 분해하여 문제를 해결하는 것을 선호한다.
    이러한 공통점으로 인해, 함수형 프로그래밍은 컴포넌트 기반 아키텍처를 구축하는데 매우 적합하다.

  • 불변성 : 함수형 프로그래밍에서는 데이터의 불변성이 중요한 원칙 중 하나이다.
    React에서도 불변성은 중요한 개념으로, 상태를 직접 변경하는 것이 아니라 새 상태 객체를 생성하여 성능을 최적화하고 예측 가능한 상태 관리를 할 수 있게 한다.

  • 순수 함수 : 함수형 프로그래밍의 순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며 부작용이 없다.
    React의 함수형 컴포넌트도 비슷한 개념을 따르며, 주어진 props에 대해 예측 가능한 방식으로 UI를 렌더링한다.

  • 선언적 프로그래밍 : 함수형 프로그래밍은 '어떻게'가 아닌 '무엇을' 하는지에 중점을 둔다.
    React도 선언적 접근 방식을 취하며, 개발자가 UI가 어떤 식으로 구성되어야 하는지 선언하고, React가 '어떻게' 그것을 달성할지 결정하도록 한다.

  • 효율적인 상태 관리 : React의 Hooks 기능은 함수형 컴포넌트에서 상태와 생명주기 기능을 사용할 수 있게 해준다.
    이는 함수형 프로그래밍의 개념과 잘 어울리며, 상태 관리를 더욱 간결하고 이해하기 쉽게 만들어준다.

  • 테스트와 유지 보수의 용이성 : 순수 함수와 불변 데이터 구조를 사용하면 테스트가 더 쉬워지고, 코드가 더 예측 가능하며, 디버깅이 더 쉬워진다.
    이는 크고 복잡한 웹 애플리케이션의 유지 보수를 훨씬 간편하게 만들어준다.

이러한 이유로, 함수형 프로그래밍 패러다임은 React와 같은 웹 개발 환경에서 빠르게 인기를 얻고 있으며, 효율적이고 유지 보수가 용이한 애플리케이션을 만드는데 도움이 된다.

React에서의 함수형 컴포넌트

훅(Hooks) 도입 전, React의 함수형 컴포넌트는 상태가 없었다.
그들은 오직 props만을 받아들이고 JSX만을 반환했다.
하지만 훅의 도입으로 함수형 컴포넌트는 여전히 함수형 원리를 고수하면서 상태와 사이드 이펙트를 가질 수 있는 능력을 얻었다.

예시 :

import React from "react";

const Greeting = (props) => <h1>안녕하세요, {props.name}!</h1>;

export default Greeting;

useState 훅

생성자와 this.setState를 가진 클래스 컴포넌트를 사용하는 대신에, 함수형 컴포넌트에서 useState훅을 사용할 수 있다.

import React, { useState } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
};

useEffect 훅

componentDidMount, componentDidUpdate, componentWillUnmount 같은 생명주기 메서드를 대체한다.

import React, { useState, useEffect } from "react";

const Timer = () => {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);

    return () => clearInterval(interval); // 정리 함수
  }, []); // 의존성 배열이 비어 있으면 초기 렌더링 후 한 번 실행됩니다

  return <div>경과 시간: {seconds}</div>;
};

React와 불변성

React의 재렌더링 과정은 불변 데이터 구조의 사용으로 이점을 얻습니다.
Immutable.js와 같은 라이브러리나 스프레드 연산자(...)와 같은 유틸리티를 활용함으로써 데이터가 불변하게 유지되는 것을 보장할 수 있다.

고차 컴포넌트(HOCs)

이것들은 컴포넌트를 취하고 추가 props 또는 변경된 행동을 가진 새로운 컴포넌트를 반환하는 함수들이다.

function withLogger(WrappedComponent) {
  return function LoggerComponent(props) {
    console.log("Props:", props);
    return <WrappedComponent {...props} />;
  };
}

함수형 라이브러리들

React에서 함수형 프로그래밍 경험을 향상시켜주는 몇 가지 라이브러리가 있다.

  • Lodash/fp : 함수가 자동으로 query되고 데이터가 불변인 Lodash의 버전.
  • Ramda : 불변성과 함수 커링에 중점을 둔 함수형 프로그래밍을 위한 유틸리티 라이브러리.
  • Recompose : 함수 컴포넌트와 고차 컴포넌트를 위한 React 유틸리티 벨트.

React에서의 함수형 프로그래밍의 장점들

  • 예측 가능성 : 출력이 입력에만 의해 결정되기 때문에, 함수가 무엇을 반환할지 예측하기가 더 쉽다.
  • 테스트 가능성 : 순수 함수는 외부 상태에 독립적이기 때문에 테스트하기가 더 쉽다.
  • 동시성 : 불변 데이터 구조로 인해 동시 변경으로부터 데이터 손상의 위험이 줄어든다.

결론

함수형 프로그래밍 원칙을 React 개발에 통합함으로써 더 읽기 쉽고, 유지 보수가 가능하며, 테스트 가능한 코드로 이어진다.
React가 계속 발전함에 따라, 함수형 프로그래밍 개념과의 조화는 웹 어플리케이션을 구축하는데 있어 확장 가능하고 효율적인 방법을 보장한ㄷ.


순수함수는 뭐지?

순수함수

순수 함수란 사이드 이펙트가 없는 함수, 즉 함수의 실행이 외부에 영향을 끼치지 않는 함수를 뜻하고, 입력으로 전달된 값을 수정하지 않는 불변성을 가지고 있습니다.
따라서 순수함수는 어떠한 전달인자가 주어지더라도 항상 똑같은 값이 리턴됨을 보장한다.

사이드 이펙트?

함수의 입력 외에도 함수의 결과에 영향을 미치는 요인, 대표적으로 네트워크 요청, API 호출이 Side Effect이다.

순수함수 개념이 중요한 이유는?

사이드 이펙트를 줄이고, 모듈화 수준을 높이는 함수형 프로그래밍의 특징, 즉 순수함수는 평가 시점이 무관하다는 특징으로 인해 효율적인 로직을 구성할 수 있다.

데이터의 불변성을 유지하는 것이 중요한 이유는?

순수함수를 만들기 위해 데이터의 불변성을 유지하는 것은 중요하다.
함수의 전달인자로 참조 자료형이 전달되면 의도치 않게 해당 객체 자체를 바꾸는 사이드 이펙트를 만들 수 있는데 이는 해당 데이터의 불변성을 손상시킨다.
따라서 배열의 불변성을 보장하는 메소드 map, filter, reduce 등을 사용하기도 한다.


일급 함수?

함수형 프로그래밍과 일급 함수

함수형 프로그래밍의 이점들을 활용해 로직을 구현하고, 컨셉을 이해하기 위해 일급 함수에 대해 먼저 알아보자.

  • 자바스크립트에서 함수는 일급 함수이다.
    • 다시 말해 함수를 값으로 다룰 수 있다는 뜻이다.

함수를 변수에 담기

// ✅ 일급 함수는 변수에 함수를 담을 수 있다.
const foo = function (a) {
  return a * a;
};

console.log(foo);
// ƒ (a) {
//   return a * a;
// }

// ✅ 변수 끝에 괄호 ()를 사용해서 함수를 호출할 수 있다.
const foo = function (a) {
  return a * a;
};

console.log(foo(3));
// 9

위 예제를 보면 변수 foo에 함수를 할당하고 출력해보면 함수가 담긴 것을 확인할 수 있다.
변수를 사용하여 끝에 괄호 ()를 추가하면 함수를 호출할 수 있다.

함수를 인자로 전달하기

// ✅ f1 함수에게 인자로 함수를 전달하였다.
function f1(f) {
  return f(); // 인자로 받은 함수를 평가(수행)
}

console.log(
  f1(function () {
    return 10;
  })
);

// 10

위 예제의 f1 함수는 인자로 함수를 받아서 함수 내부에서 함수를 평가한 다음 그 결과를 리턴하는 함수(일급 함수)이다.
따라서 f1()은 어떤 함수를 인자로 넘겼느냐에 따라 결과값이 결정 될 것이다.(순수 함수)

다른 함수(f1)에 인자로 전달된 함수(익명 함수)를 콜백 함수라고 한다. 위 예제에선 익명 함수(10을 리턴)가 콜백 함수이다.

일급 함수라는 개념과 순수 함수라는 개념을 이용해서 함수의 조합성을 높여 나가는 것이 함수형 프로그래밍이다.

다시 말해 언제 평가해도 상관 없는 순수 함수들을 만들고, 그 순수 함수들을 값(변수)으로 들고 다니면서 적절한 시점(필요한 시점)에 평가를 하는 방식으로 다양한 로직을 만들어 나가는 것이 함수형 프로그래밍인 것이다.

addMaker 함수 만들기 - 함수를 리턴하는 함수

// ✅ 함수를 리턴하는 함수

// a라는 값을 받고 a라는 값을 알고 있는 컨텍스트의 함수를 정의한 후 리턴한다.
function addMaker(a) {
  return function (b) {
    // 클로저
    return a + b;
  };
}
// 결국 addMaker 함수를 호출하면 a라는 값을 기억하는 클로저가 된다.

// 따라서 변수 add10은 10의 값을 기억하는 클로저이다.
const add10 = addMaker(10);
// 10(a) + 20(b) = 30
console.log(add10(20)); // 30

// 15의 값을 기억하는 클로저
const add15 = addMaker(15);
// 15(a) + 20(b) = 35
console.log(add15(20)); // 35

위 예제의 addMaker 함수는 일급 함수의 개념과 클로저의 개념이 함께 사용된 함수이다. 함수를 값처럼 취급하기 때문에 함수 또한 리턴 할 수 있다.

함수를 리턴하는 함수를 고차 함수라고 부른다.

여기서 주의할 점은 위 예제의 클로저는 순수 함수도 된다는 점이다. a라는 매개변수는 클로저 내부에서 참조만 할 뿐 a라는 값을 직접 변경하지 않는다. 그리고 외부 어느 곳에서도 a가 존재하지 않는다.

그래서 위 예제의 클로저는 항상 동일한 값을 가리키고 있는 a라는 값에 b를 더하는 순수 함수이다. 결국 addMaker()를 통해 만든 add10이나 add15는 어느 시점에 평가를 해도 무관하다(항상 동일한 결과값을 낸다).

위의 예제들은 함수형 프로그래밍으로 작성한 다양한 방식의 코드들을 나타낸다. 함수를 값으로 다루면서 순수 함수를 정의하고, 함수의 평가 시점이 무관해서 다양한 방식으로 작성하는 것. 이것을 함수형 프로그래밍이라고 한다.

출처

함수형 프로그래밍 - https://reactnext-central.xyz/blog/react/functional-programming#google_vignette
순수 함수 - https://dev-ellachoi.tistory.com/54
일급 함수 - https://onlydev.tistory.com/97

profile
개발자 지망생

0개의 댓글