들어가며
오늘은 함수형 프로그래밍과 HTML/CSS, DOM에 대해 공부했다. 오랜만에 자료구조/알고리즘 문제가 없는 하루였다.
함수형 프로그래밍이 중요하다는 얘기는 구글링 할 때도 보고, 유튜브에서도 알고리즘에 떠서 많이 들었었는데 직접 공부를 시작한건 처음이다. 이미 먼저 공부를 해봤던 분들이 어렵다고 해서 걱정은 되지만 새로운 걸 배운다는 건 항상 두근두근하다.
함수형 프로그래밍
함수형 프로그래밍이란 자료 처리를 수학적인 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임이다. 명령형 프로그래밍에서 상태를 바꾸는 것을 강조하는 것과 다르게, 함수형 프로그래밍은 함수의 응용을 강조한다.
함수형 프로그래밍 특징
함수형 프로그래밍은 다음과 같은 특징을 가지고 있다.
순수함수이다.
함수가 동일한 입력에 대해 동일한 결과값을 반환하고, 함수의 실행이 프로그램의 실행에 영향을 미치지 않아야하며, 함수 내부에서 인자의 값을 변경하거나 프로그램의 상태를 변경하는 Side Effect 가 없어야 한다.1 let num = 0;2 function add(n) {3 return n + num;4 }위 코드는 add 함수 안에서 전역변수 num에 접근했기 때문에 순수함수가 아니다.
1 function add(a, b) {2 return a + b;3 }4 console.log(add(2, 3)); // 5아래의 add 함수는 인자 2개를 입력 받아 그 값들을 더한 값을 반환하기 때문에 이는 순수함수이다.
비상태, 불변성 함수형 프로그래밍에서 데이터는 불변성을 만족해야 하고 데이터의 변경이 필요한 경우 데이터의 복사본을 만들어 그 값을 변경하고 이를 사용해 작업을 진행해야 한다.
1 // 불변성 만족 X2 const person = { name: "jeongki", age: 26 };3 function changeAge() {4 person.age = person.age + 1;5 }1 // 불변성 만족 O2 const person = { name:'jeongki', age:26 };3 function changeAge(){4 return {...person, age: person.age+1};선언형 함수 명령형 프로그램은 어떻게 할 것인지에 집중하고 선언형 프로그램은 무엇을 할 것인지에 집중한다. 평소에 반복을 위해 자주 사용했던 for 문이 명령형의 예시이다.
1 // 명령형 프로그래밍2 const nums = [1, 2, 3, 4, 5];3 for (let i = 0; i < nums.length; i++) {4 console.log(nums[i]);5 }1 // 선언형 프로그래밍2 const nums = [1, 2, 3, 4, 5];3 console.log(...nums.map((item) => item));일급 객체와 고차 함수 일급이란 값으로 다룰 수 있고, 변수에 담을 수 있고, 함수의 인자로 사용될 수 있고, 함수의 결과로 사용될 수 있는 것을 의미한다. 자바스크립트에서 함수는 일급이다.
1 const add5 = (a) => a + 5;위의 add5 변수의 값으로 함수가 저장되었다.
고차 함수란 함수를 값으로 다루는 함수, 함수를 인자로 받아서 실행하는 함수, 함수를 만들어 리턴하는 함수(클로저를 만들어 리턴하는 함수)를 의미한다. 자바스크립트 내장 함수 중에서는 map, filter, reduce 등이 고차 함수이다.
1 const nums = [100, 500, 300, 600, 450];2 console.log(nums.filter((num) => num > 300)); // [500, 600, 450]위의 코드에서 filter 함수는 num => num > 300 이라는 함수를 인자로 받았다.
함수형 프로그래밍에서 리스트 순회는 매우 중요하다. 선언적으로 코드를 작성하기 때문이라고 생각하는데 순회의 방법이 ES6부터 달라졌다. ES6에서는 순회를 위해 for...of 를 사용한다.
자바스크립트의 내장 객체 Array, Map, Set은 모두 for...of 를 사용해 순회가 가능하다. 그렇다면 어떻게 형태가 다른 객체를 같은 방법을 사용해 순회할 수 있는걸까? 답은 Symbol.iterator 에 있다.
이터러블/이터레이터 프로토콜
이름부터 멋있다. 이터러블은 이터레이터를 리턴하는 [Symbol.iterator]() 를 가진 값을 의미한다. 앞에서 말했던 Array, Map, Set은 이터러블이다. 이터레이터는 {value, done} 객체를 리턴하는 next()를 가진 값이다. 이터러블/이터레이터 프로토콜은 이터러블 객체를 for of, 전개 연산자 등과 함께 동작하도록 한 규약이다.
사용자가 직접 이터러블 객체를 정의할 수 있다. 정의를 만족하는 객체를 만들면 된다.
1 const customIterable = {2 [Symbol.iterator]() {3 let i = 0;4 return {5 next() {6 return i === 5 ? { done: true } : { value: i++, done: false };7 },8 };9 },10 };11 for (const a of customIterable) {12 console.log(a);13 } // 0 1 2 3 4
기본적인 이터러블 객체를 만들어보았다. 이때 이터레이터가 자기자신을 반환하는 [Symbol.iterator]()를 가지고 있을 때 이를 Well-Formed라고 한다.
제너레이터는 이터레이터이자 이터러블을 생성하는 함수이다. 일반 함수 이름 앞에 *를 붙여 만든다.
1 function* gen() {2 yield 1;3 yield 2;4 yield 3;5 }6 for (const a of gen()) {7 console.log(a);8 } // 1 2 3
제너레이터는 이터러블을 생성하기 때문에 gen()은 이터러블이므로 for of 를 사용해 순회할 수 있다.
정리
함수형 프로그래밍을 비롯해 이터러블/이터레이터, 제너레이터 등 새로운 개념을 많이 배웠다. 아무 생각없이 사용했던 for...of, 전개 연산자 가 내부적으로 어떻게 동작하는지 알 수 있어서 좋았다. 머리로는 이해했는데 글로 정리하는게 어렵다. 계속 정리하면서 글 쓰는 능력도 길러야겠다.
참고자료
함수형 프로그래밍 https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98%ED%98%95_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D
함수형 프로그래밍이란 https://jongminfire.dev/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80
고차 함수 https://velog.io/@jakeseo_me/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-33%EA%B0%80%EC%A7%80-%EA%B0%9C%EB%85%90-22-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B3%A0%EC%B0%A8-%ED%95%A8%EC%88%98Higher-Order-Function-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0