프로그래밍 패러다임

  • 프로그래밍의 패러다임은 시대별로 프로그래밍의 방식이 어떻게 변화되었는가?로 이해하면될 것 같다.
  • 프로그램은 순차, 분기, 반복, 참조로 구성되어지며 프로그램 개발을 위해 전략을 수립해야 한다.
  • 위에서 말한 전략은 어떤 언어를 사용할 것인지, 프로그래밍에서 어떤 것을 지양하고, 지향할 것인지 등등 다양한 방법을 수립하는 것을 말한다.
  • 여기서 말한 전략에 해당되는 내용들이 프로그래밍 패러다임이다.
  • 대표적인 프로그래밍 패러다임은 절차적 -> 객체지향 -> 함수형의 순서로 나왔다.


명령형 프로그래밍

  • 문제를 어떻게(how) 해결해야 하는지 컴퓨터에게 명령을 내리는 방법의 프로그래밍이다.
  • 알고리즘을 명시하고, 목표는 명시하지 않는다.
  • 프로그램의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 방식이다.
  • 대표적으로 절차적 프로그래밍객체지향 프로그래밍이 있다.

절차(순차)적 프로그래밍


  • 말 그대로 실행 절차(순차)에 중점을 둔 프로그래밍을 말한다. 순차적인 처리방식으로 프로그램 전체가 유기적인 연결이 있다. 순차적인 처리방식은 컴퓨터의 동작 방식과 같아서 처리 시간이 빠르다고 한다.
  • 하나의 큰 기능을 처리하기 위해 작은 단위의 기능들로 쪼개어 처리하는 Top-Down 방식이다.
  • 비교적 작은 규모의 작업을 수행하는 함수를 생성한다.
  • 인수, 반환 값으로 명령을 전달하고 수행한다.
  • 데이터와 함수를 별개로 취급한다.
  • 특정 기능을 수행하려면 해당 메소드를 직접 호출한다.
const nums = [1,2,3,4,5];
let res = 0;

for (let i = 0; i < nums.length; i++) {
    res += nums[i];
}

console.log(res); // 15

장단점

  • 개인 프로젝트에 적합하다.
  • 객체 지향에 비해 빠르다.
  • 대형 프로젝트에 부적합하다.

객체지향 프로그래밍


  • 최소 단위가 객체이며, 객체를 만들고, 이 객체를 사용하여 데이터와 메소드를 묶고 소통하는 프로그래밍 방식이다.
  • 클래스를 이용해 연관있는 함수와 변수를 하나의 객체(인스턴스)로 묶어 생성하고 사용한다.
  • 절차적 프로그래밍과 반대로 작은 문제를 해결하는 객체를 생성해 큰 문제를 해결하는 Bottom-Up 방식이다.
  • 네 가지 특징(추상화, 캡슐화, 다형성, 상속)을 지켜서 프로그래밍을 해야하며 이 조건을 지키지 않고 클래스 단위로 프래그래밍하는 것은 객체 지향이 아닌 클래스기반(캡슐화)을 사용하는 것이다.
class Sum {
    res =0;
    
    constructor(nums){
        this.nums = nums;
    }

    sum(){
        for(const i of nums){
            res += i;
        }

        return res;
    }
}

const sum2 = new Sum([1,2,3,4,5]);
console.log(sum2.sum()); // 15

장단점

  • 부모 클래스로부터 상속받은 것을 사용할 수 있으므로, 코드의 재사용성이 가능하다.
  • 재사용성만 고려해서 프로그래밍한다면 혼란이 생길 수 있다.
  • 상속은 다형성을 구현하기 위해 사용할 것을 권장한다.
    • 좋은 상속관계는 is-a 관계 (~은 ~이다) : 바나나는 과일이다 / 자동차는 바퀴가 있는 탈 것이다.
    • 혼란을 줄 수 있는 상속관계는 has-a 관계 : 사람-팔 / 자동차-바퀴 / 새-날개


선언형 프로그래밍

명령형 프로그래밍이 어떻게(how)할 것인가에 집중했다면, 선언형 프로그래밍은 무엇(what)을 할 것인가에 집중하는 프로그래밍 방식이다.

함수형 프로그래밍이 왜 중요한가?

잠시 당신이 나무꾼이라고 가정해보자. 당신은 숲에서 가장 좋은 도끼를 가지고 있고, 그래서 가장 일 잘하는 나무꾼이다. 그런데 어느 날 누가 나타나서 나무를 자르는 새로운 패러다임인 전기톱을 알리고 다닌다. 이 사람이 무척 설득력이 있어서 당신은 사용하는 방법도 모르면서 전기톱을 사게 된다. 당신은 여태껏 했던 방식대로 시동을 걸지도 않고 전기톱으로 나무를 마구 두들겨댄다. 곧 당신은 이 새로운 전기톱은 일시적인 유행일 뿐이라고 단정하고 다시 도끼를 쓰기 시작한다. 그때 누군가 나타나서 전기톱의 시동 거는 법을 가르쳐 준다. — “함수형 사고” 에서

함수형 사고라는 책의 내용이다. 일반적인 프로그래밍은 그냥 생각하면 되는 것이고 함수형 프로그래밍은 생각하는 방법을 알려주는 것이다. 개발자스럽게 바꿔 말하면 프로그래밍 언어나 개발 방식을 배우는것이 아닌, 함수로 프로그래밍을 하는 사고를 배우는 것이다. 새로운 계산방식을 배우는 것처럼 사고의 전환을 필요로 한다. 다양한 사고방식으로 프로그래밍을 바라보면 더욱 유연한 문제해결이 가능해진다.

함수형 프로그래밍 필수 개념

1급 객체 (First Object, 또는 1급 시민)

보통 아래의 3가지를 충족하면 일급객체라 한다.

  • 변수나 데이터에 할당할 수 있어야 한다.
  • 객체의 인자로 넘길 수 있어야 한다.
  • 반환값(return value)으로 사용할 수 있어야 한다.

자바스크립트에서 함수(Function)은 객체(Object)이므로 1급 함수로 불린다.

// 함수를 변수에 할당하며, a+10 으로 반환한다.
const add10 = (a) => a+10;
const x = add10(10);

console.log(x) // 20

고차 함수 (High-Order Function)

보통 아래의 2가지 중 하나 이상을 만족하면 고차함수라 한다.

  • 함수를 파라미터로 전달 받는 함수
  • 함수를 리턴하는 함수

고차 함수는 1급 함수의 부분 집합이다.

// 클로저 함수를 생성하여 리턴하는 함수
const addMaker = a => b => a+b;

// a인자 값을 받아 b=>a+b 라는 함수를 리턴한다.
const add10 = addMaker(10);

console.log(add10(5));
console.log(add10(10));

불변성 (Immutablility)

  • 함수형 프로그래밍에서는 데이터가 변할 수 없다. 이를 불변성 데이터라 한다.
  • 자바스크립트는 데이터의 변경이 언제든 가능하지만 안되는 언어들이 존재한다.
  • 데이터 변경이 필요한 경우, 원본 데이터 구조를 변경하지 않고 그 데이터를 복제해 사용한다.

순수 함수 (Pure function)

  • 동일한 입력에는 항상 같은 값을 반환한다.
  • 함수의 실행은 프로그램의 실행에 영향을 미치지 않아야 한다. (Side effect 가 없어야 한다)
  • 함수 내부에서 인자값의 변경이나 프로그램의 상태를 변경하면 안된다.

합성 함수 (Function composition)

  • 합성 함수란 새로운 함수를 만들거나 계산하기 위해 둘 이상의 함수를 조합하는 과정을 말한다.
  • 함수형 프로그래밍은 여러 작은 순수 함수들로 이루어져있기 때문에 이 함수들을 연쇄적으로 또는 병렬적으로 호출하여 더 큰 함수를 만드는 과정으로 구축해야한다.

정리해서

  • 모든 데이터는 변경 불가능해야 한다.
  • 함수는 순수 함수로 만들고, 인자는 적어도 하나 이상 받아야 하며, 데이터나 다른 함수를 반환해야 한다.
  • 모든 것은 객체이다.
  • 루프보다는 재귀를 사용한다.


마무리

사실 함수형 프로그래밍이 정확히 어떻게 동작하는지 어떻게 구현하는지 알지 못했다. 인프런에서 유인동님의 함수형 프로그래밍과 JavaScript ES6+ 강의를 보면서 함수형 프로그래밍에 대해 알게 되었다.

처음 접했을 때 유레카를 외쳤다. 이렇게 프로그래밍을 할 수 있구나…
난 어떠한 개발을 해왔지? 라는 반성까지 하게 되었다. 이제까지 명령형 프로그래밍으로만 개발을 해왔기 때문에 함수형 프로그래밍적 사고 방식에 익숙하지 않은 것은 사실이다.
하지만, 사람은 적응의 동물이라고 했다.

앞으로 함수로 프로그래밍을 하는 사고를 기르기 위해 노력해야 겠다는 동기부여가 확실하게 왔고 노력할 것이다.

하지만 여기서 조심해야할 점은 함수형 프로그래밍이 현 시대적 패러다임이라고 해서 그쪽만 파서는 안된다는 것이다. 서로 우위를 가릴게 아니라 목적과 상황에 맞게 적절하게 사용할 줄 알아야 한다.

아직까지 많은 라이브러리들이 객체지향적으로 설계되어 왔고 현재도 그렇게 사용하고 있기 때문에 객체지향적인 프로그래밍 방식도 같이 채택해야 한다.


참고자료