> For the complete documentation index, see [llms.txt](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_note/fp/undefined-11.md).

# 일급 함수

## 일급 값(first-class value)

> 변수에 할당할 수 있고 함수의 인자로 넘기거나, 반환값으로 사용할 수 있다.
>
> 즉, **코드로 다룰 수 있는 값**을 의미

### 자바스크립트에서 일급 값

> 자바스크립트에서는 일급 값과 일급이 아닌 값이 섞여있다.

#### 일급 값이 아닌 것

* 조건 문
* 반복 문
* 수식 연산자
* try/catch 블록

#### 일급 값으로 할 수 있는 것

* 변수에 할당
* 배열이나 객체에 담기
* 함수의 입력 값, 출력 값으로 넘기기

{% hint style="info" %}
일급 값이 아닌 것들을 찾고 일급 값으로 바꾸는 기술은 함수형 프로그래밍에서 중요한 기술로 문제를 해결하는 새로운 능력을 얻게된다.
{% endhint %}

### 어떤 문법이든 일급 함수로 바꿀 수 있다.

> 자바스크립트에서 함수는 일급 값이므로 일급 값이 아닌 것들을 함수로 만들어 일급 값으로 바꿀 수 있다.

```typescript
// +,- 일급 값이 아닌 수식연산자를 함수로 만들어 일급 값으로 변환
const plus = (a: number, b:number) => a + b;
```

## 일급 함수

> 일급 함수란 함수를 일급 값처럼 쓰이는 함수를 의미

### 고차 함수(High-Order Function)

> **일급 함수를 함수의 인자로 받거나 반환하는 함수**로 일급 함수를 활용한 함수를 의미한다.

* 고차함수로 패턴이나 원칙을 코드로 만들 수 있음
* 고차함수를 사용하지 않으면 일일히 수작업으로 해줘야하 하는 부분이 생김
* 고차함수는 한번 정의하고 필요한 곳에서 여러번 재사용 가능
* 고차함수로 함수를 반환하는 함수를 만들 수 있다. 반환 받은 함수는 변수에 할당해서 이름이 있는 일반적인 함수처럼 사용이 가능하다.
* 고차함수는 많은 중복 코드를 제거할 수 있지만, 가독성을 해칠 수 있다.
* **코드에 반복되는 부분을 줄이기 위해 고차 함수를 사용하는 것이 중요**

{% hint style="info" %}
함수를 인자로 받는 **고차함수**는 **반복적이지만 조금씩 다른 작업을 하는 코드**에서 **공통적인 부분을 함수로 하나의 함수로 추상화**시키고 **다른 작업을 하는 부분을 함수의 인자로 분리**할 수 있기 때문에 **재사용성이 높아짐**
{% endhint %}

{% hint style="info" %}
만약 고차 함수로 만든 좋은 방법을 찾았다면 **직관적인 방법과 항상 비교해보자**\
&#x20;**코드가 더 읽기 쉬운지**, **이해하기 쉬운지**, **중복을 얼마나 제거했는지**를 판단하여 적용하자
{% endhint %}

## 일급 함수를 활용한 리팩토링

### 리팩토링을 통해 얻는 것

1. 표준화된 원칙
2. 새로운 동작에 원칙을 적용할 수 있음
3. 여러 개를 변경할 때 최적화

### 코드의 냄새를 맡는 방법

1. 함수 구현부가 거의 똑같음
2. 함수의 이름이구현의 차이를 만듦 →  함수의 이름에서 서로 다른 부분이 암묵적 인자이다.

{% hint style="info" %}
**코드의 냄새(Code Smell)**&#xB294; 더 큰 문제를 가져 올 수 있는 나쁜 코드를 의미한다.
{% endhint %}

### 코드 냄새: 함수 이름의 암묵적 인자

> **함수 본문에서 사용하는 어떤 값이 함수 이름에 나타난다면** 함수 이름에 있는 암묵적 인자는 **코드의 냄새**가 된다.

* 거의 똑같이 구현된 함수에서 함수의 이름이 구현부에 있는 다른 부분을 가리킨다.
* 함수 이름의 암묵적 인자를 일급 값으로 바꾸면 표현력이 좋아짐

### 리팩토링: 암묵적 인자 들어내기

> 암묵적 인자가 일급 값이 되도록 함수 인자를 추가하여 잠재적인 중복을 없앨 수 있고, 코드의 목적을 더 잘 표현할 수 있게 됨

1. 함수 이름에 있는 암묵적인 인자를 확인
2. 명시적인 인자를 추가
3. 함수 본문에 하드코딩된 값을 새로운 인자로 바꾼다.
4. 함수 호출부를 리팩토링된 코드에 맞춰 수정한다.

#### Before

> 원래 가격만 설정하는 setPriceByName() 함수

```typescript
// 함수 이름에 있는 price가 암묵적 인자가 된다.
const setPriceByName = (cart, name, price) => {  
  const item = cart[name];
  const newItem = objectSet(item, 'price', price);
  const newCart = objectSet(cart, name, newItem);
  
  return newCart;
}

// 사용하는 코드
cart = setPriceByName(cart, 'shoe', 13);
cart = setQuantityByName(cart, 'shoe', 3);
cart = setShippingName(cart, 'shoe', 0);
cart = setTaxByName(cart, 'shoe', 2.34);
```

#### Refactoring

> 어떤 필드값이든 설정할 수 있는 setFieldByName() 함수로 리팩토링 하자
>
> 리팩토링을 통해 비슷한 함수를 모두 일반적인 함수 하나로 변경

```typescript
// 명시적인 인자를 추가하고 원래 인자는 더 일반적인 이름으로 변경
const setFieldByName = (cart, name, field, value) => {
  const item = cart[name];
  const newItem = objectSet(item, field, value);
  const newCart = objectSet(cart, name, newItem);
  
  return newCart;
}

cart = setFieldByName(cart, 'shoe', 'price', 13);
cart = setFieldByName(cart, 'shoe', 'quantity', 3);
cart = setFieldByName(cart, 'shoe', 'shipping', 0);
cart = setFieldByName(cart, 'shoe', 'tax', 2.34);
```

### 리팩토링: 함수 본문을 콜백으로 바꾸기

> 함수 본문에 비슷하지만 서로 다른 부분을 함수의 콜백 인자로 바꾼다.

* 일급 함수의 특징을 이용하여 어떤 함수에 동작(함수)을 전달할 수 있게됨
* 원래 코드를 **고차함수로 만드는 강력한 방법**

1. 함수 구현부의 앞뒤로 바뀌지 않는 부분을 찾고, 리팩토링할 본문을 찾는다.
2. 리팩토링할 코드를 함수로 추출한다.
3. 추출한 함수를 인자로 전달받고 빼낸 함수 본문에 적용한다.
4. **문법의 중복 및 코드 원칙 같은 다른 형태의 중복을 없앨때 탁월하다.**

{% hint style="info" %}
인자로 전달하는 함수를 **콜백(Callback)함수**라 부르며, 콜백으로 전달된 함수는 **나중에 호출 될 것을 기대**한다.  핸들러 함수라고도 불린다.
{% endhint %}

#### Before

```javascript
// 모든 곳에서 같은 형태로 catch 구문을 사용한다.
try { // 앞부분
  saveUserData(user); // 본문 (달라지는 부분)
} catch (error) {  // 뒷부분
  logToSnapErrors(error);
}

// #2
try {
  fetchProduct(productId); // 달라지는 부분
} catch (error) {
  logToSnapErrors(error);
}
```

#### Refactoring

```typescript
const withLogging = (f) => {
  try {
    f();
  } catch (error) {
    logToSnapErrors(error);
  }
}
```

{% hint style="info" %}
**함수의 인자로 일반 데이터를 전달하지 않고 함수를 전달하는 이유**

함수로 전달하는 이유는 **전달하는 함수 내부 코드가 특정 문맥 안에서 실행되어야 하기 때문**이다.

고차함수를 활용하면 다른 곳에 정의된 문맥에서 코드를 실행시킬 수 있다.  그리고 문맥은 함수이기 때문에 재사용이 가능하다.
{% endhint %}

### 리팩토링: 카피온 라이트 패턴 적용

1. 함수 본문의 앞부분과 뒷부분 확인하기
2. 함수 빼내기
3. 콜백 빼내기

#### Before

> 본문과 앞부분, 뒷부분을 확인해보면 카피-온-라이트가 적용된 두 함수는 비슷하다.

```typescript

const arraySet = <T>(array: T[], idx: number, value: T) => {
  const copy = [...array];
  copy[idx] = value;
  return copy;
}

const push = <T>(array: T[], elem: T) => {
  const copy = [...array];
  copy.push(elem);
  return copy;
}
```

#### After

```typescript
// #1 함수 빼내기 (리팩토링 할 코드를 함수로 빼는 단계)
const arraySet = <T>(array: T[], idx: number, value: T) => {
  return withArrayCopy(array);  
}

const withArrayCopy = <T>(array: T[]) => {
  const copy = [...array];
  copy[idx] = value;
  return copy;
}

// #2 콜백 빼내기 (본문을 콜백함수로 빼는 단계)
const arraySet = <T>(array: T[], idx: number, value: T) => {
  return withArrayCopy(array, (copy) => copy[idx] = value);  
}
// 카피-온-라이트 원칙을 따르고 재사용할 수 있는 함수
const withArrayCopy = <T>(array: T[], modfiy: (x: T[]) => void) => {
  const copy = [...array];
  modify(copy);
  return copy;
}
```
