쏙쏙 들어오는 함수형 코딩 - Chapter 03, 04, 05

Haz·2023년 7월 2일
0
post-thumbnail

본격적으로 심화 과정이 시작된 3장과 4장, 그리고 대망의 5장이다. 가장 헷갈리는 부분인 액션과 계산을 분리하는 것에 대해서 연습도 해보고 개념도 되짚어본다.



액션, 계산, 데이터 구분하기


개념 복습

액션계산데이터
실행 시점과 횟수에 의존입력으로 출력 계산이벤트에 대한 사실
부수효과, 순수하지 않은 함수순수함수, 수학함수X

문제에 대해 생각할 때, 실질적으로 코딩할 때, 코드를 읽을 때 모든 개발 단계에서 이런 구별 작업을 할 수 있다.

중요한 건 "액션 안에 계산과 데이터, 또다른 액션이 포함되어있을 수 있다"는 것이다. 이를 무조건 나누기보단 왜?를 고민해서 구분해야한다. 계산 또한 더 작은 계산과 데이터로 나눌 수 있지만 꼭 필요하지 않은 경우에는 나눌 필요까지는 없는 것 같다. 데이터는 다른 영향을 주지 않기 때문에 가장 먼저 데이터를 찾는 작업을 해야하고, 그 결과를 활용해서 동작(계산, 액션)을 어떻게 만들지 고민해야한다.


데이터

앞으로 데이터에 대해서는 많이 다룰 것 같지 않다. 데이터는 3개 요소 중에 가장 간단하게 구별되기 때문이다.

JavaScript에서 데이터 타입은 숫자, 문자, 배열, 객체 등이 있다. 데이터에 의미를 담기 위해서는 자료구조를 활용하면 되며 8가지(배열, 연결 리스트, 스택, 큐, 해시 테이블, 그래프, 트리, 힙)가 대표격이다.

함수형 프로그래밍에서는 자료구조를 만들 때 두 가지 원칙이 있다.

  1. Copy-on-Write: 변경 시에는 복사본 만들기
  2. 방어적 복사: 보관하려고 하는 데이터도 복사본 만들기

데이터의 장점은 다음과 같다.

  • 직렬화: 함수보다 디스크에 저장해 전송하고 읽기 쉬움
  • 동일성 비교: 비교가 쉬움
  • 자유로운 해석: 다양한 문제에 데이터를 활용해 해석할 수 있음

계산

계산은 입력값으로 출력값을 만드는 단순 연산이다. 실행 시점과 횟수에 관계없이 항상 같은 입력값에 대해 같은 출력값을 돌려준다.

계산은 함수로 만들 수 있고, 연산을 담을 수 있기 때문에 의미를 담을 수 있다. 계산을 언제 사용할지, 어떻게 사용할지 때에 따라 달라지기 때문에 그것에 따라 담는 함의가 달라진다.

계산은 부수효과를 발생하지 않기 때문에 순수함수, 수학 함수 라고 부른다.

계산의 장점

  1. 테스트가 쉬움
  2. 기계적인 분석이 쉬움
  3. 여러 개의 계산을 하나의 큰 계산으로 조합하기 좋음

계산 활용 시 고민하지 않아도 되는 것들

  1. 동시에 실행되는 것
  2. 과거에 실행되었던 것이나 미래에 실행할 것
  3. 실행 횟수

계산의 단점

  1. 실행 전에는 결과를 장담할 수 없음

액션

액션은 부수효과를 발생하는 함수로 특정 함수가 액션이 아니라 하더라도, 그 함수 내 지역함수에 액션이 포함되어있다면 그 함수도 액션이 된다. 액션은 쓰는 순간 코드 전체로 퍼져나가기 때문에 조심해야한다.

액션의 형태

  1. 함수 호출: alert();
  2. 메소드 호출: console.log();
  3. 생성자: new Date()
  4. 표현식: 변수 참조, 속성 참조 user.first_name, 배열 참조 stack[0]
  5. 상태: 값 할당, 속성 삭제 등

이렇듯 액션은 매우 다양하고, 모두 회피하여 쓰기란 불가능하다. 그렇기 때문에 액션을 찾기 위해 모든 액션 코드를 찾을 필요는 없고, 해당 코드가 호출 시점(순서)이나 횟수(반복)에 의존하는지를 판단해 수정하면 된다.

액션 또한 함수로 구현하기 때문에 계산과 구별하기는 쉽지 않다. 다만, 액션은 외부 세상에 영향을 주기 떄문에 그것으로 구별할 수도 있다.



액션 & 계산 커플 깨트리기

책에서는 절차적 방식으로 짠 예시 코드를 함수형으로 수정하며 액션과 계산을 어떻게 분리하는지 설명한다.

액션과 계산을 분리하는 함수형 원칙을 적용하면 액션은 줄어들고, 계산은 늘어나서 언뜻 보면 코드량이 늘어나 오히려 나쁜 방향으로 리팩토링했다고 느껴지기도 한다. 그러나 꼭 코드가 길어졌다고 해서 나쁜 코드인 건 아니며, 함수형 원칙을 적용해 수정한 코드는 테스트 하기 쉽고 재사용성이 좋아지므로 단점보다는 장점이 더 크다.

이 둘을 분리하기 위한 몇 가지 규칙들이 있는데 이를 정리해보고자 한다.

테스트 하기 쉽게 만들기

  • DOM 업데이트와 비즈니스 규칙은 분리할 것
  • 전역변수를 최대한 배제하여 의존하지 말 것

재사용하기 쉽게 만들기

  • DOM을 사용할 수 있는 곳에서 실행된다고 가정하지 말 것
  • 함수가 결괏값을 리턴할 것

암묵적 입/출력 줄이기

우리 액션이 달라졌어요 섹션에서 더 구체적으로 이와 관련해 서술하니 자세한 내용은 하단에서 확인하자🙂

  • 입력: 함수의 인자
  • 출력: return

Copy-on-Write(카피-온-라이트)

JavaScript에서는 배열 메소드에서 원본을 그대로 사용하는 경우가 많다. 따라서 복사본을 만들어 지역변수에 할당하는 과정을 통해 데이터의 불변성을 보장해야한다.

계산 추출 단계 정리

1. 빼낼 코드를 찾아 새 함수를 만들고, 인자가 필요하다면 추가한다.
2. 새 함수에서 압묵적 입/출력을 찾는다.
3. 압묵적 입력을 인자로, 암묵적 출력을 리턴값으로 바꾼 후 필요한 부분에서 함수를 호출한다.



우리 액션이 달라졌어요

비즈니스 요구사항과 설계

비즈니스 요구사항

액션과 계산을 다 분리했다면, 이제는 액션이 제대로 비즈니스 규칙을 반영하고 있는지 확인할 시간이다.

  • 비즈니스 요구사항에 맞는 함수인가?
  • 인자가 비즈니스 요구사항에 적합한가?
  • 사용하는 데이터가 비즈니스 요구사항에 부합하나?

위와 같은 기준들을 함수가 잘 반영하고 있는지 코드 리뷰를 통해서 확인하고, 필요한 경우에는 적절하게 수정을 하는 과정이 필요하다.

설계

액션과 계산을 갈라버리고, 액션에서도 작은 기능의 함수들로 분리하거나 재조합하는 과정을 통해 우리는 엉킨 실타래를 잘 풀어 하나의 공으로 잘 뭉치는 결과를 얻는다.

설계는 결국 너무 많은 기능을 안고 있는 함수 하나를 잘게 쪼개고 나눠서 재사용성, 유지보수의 유용성, 테스트의 용이성 등을 보장할 수 있도록 하는 단계다.

처음에 설계에 실패했다하더라도, 지속적인 리팩토링을 통해서 설계 품질을 올리면 된다.

압묵적 입/출력

이미 여러 차례 말한 것처럼 전역 변수를 사용하여 이를 수정하거나, 인자 외의 변수를 사용하고 return 값 외에 다른 값이 함수에서 빠져나간다면 이는 암묵적 입/출력 이다.

계산과 액션을 분리하기 위해서도 암묵적 입/출력을 배제하는 원칙을 사용하곤 하지만, 액션에서도 불필요한 부수효과를 줄이기 위해 이 원칙을 사용하기도 한다.

입력과 출력을 명시적으로 하는 이유는 다른 컴포넌트와의 의존성을 줄이기 위함이다. 입/출력을 명시적으로 바꾸면 모듈화된 컴포넌트로 만들 수 있으며 재사용성이 극대화되고 테스트하기도 쉬워진다.

Copy-on-Write(카피-온-라이트) 패턴 빼내기

위에서 카피-온-라이트가 뭔지는 대략 살펴봤다.

액션에서 말하는 카피-온-라이트 패턴을 분리하는 건 Utility 함수를 만드는 걸 의미한다.

즉, 여러 곳에서 공통으로 쓸 수 있는 코드로서 함수 이름이나 인자 등에도 보편적인 단어들을 활용해 명명한 함수로 분리하는 것이다.

// 원본 코드
function 아이템_추가하기(cart, item) {
  let new_cart = [...cart];
  // ...
};

// 보편화된 단어로 바꾼 코드
function elem_추가하기(arr, elem) {
  let new_array = [...arr];
  // ...
}                    

이처럼 특정 기능에만 활용하는 함수가 아니라 여러 가지 기능을 만들 때 활용할 수 있도록 이름만 살짝 수정해도 다른 개발자들이 확인하고 사용하기 쉬워진다.

또한 유틸리티 함수를 분리하는 건 차후 챕터에서 나올 설계 기술과도 관련이 있다고 하니 좀 더 깊은 이유는 나중에 알 수 있을 것 같다.




결론

여기까지가 5장까지의 내용이다. 2장까지의 내용보다 좀 더 구체적으로 액션과 계산을 나누고, 액션을 발전시키는 원칙에 대해서 기술되어있다.

Code smell 같은 낯선 프로그래밍 용어도 얻어가면서 자바스크립트 언어의 불변성에 대해서도 같이 고민할 수 있는 챕터들이었다.

책에서는 배열을 복사하기 위해 slice() 메소드를 썼지만(왠지는 모르겠지만 대부분의 코드가 ES5 문법으로 작성됐더라) spread 문법으로 얕은 복사도 가능하고, map() 메소드로도 복사가 가능하다. 또한 ES2023에서 추가된 toSorted(), toReversed() 같은 함수들은 배열을 복사하여 복사본을 핸들링하기 때문에 점점 자바스크립트의 단점들을 보완하는 방향으로 발전하고 있다는 걸 확인할 수 있었다.

profile
나도 재밌고, 남들도 재밌는 서비스 만들어보고 싶다😎

0개의 댓글