Search
🎖️

10. 일급 함수 - 1

POINT - 일급 값변수, 인자, 리턴 값으로 활용할 수 있음 - 일급이 아닌 기능은 함수로 감싸 일급으로 만들 수 있음 - 코드의 냄새 중 하나인 ‘함수 이름에 있는 암묵적 인자’를 일급 값인 ‘명시적 인자’로 만들 수 있음

 추상화를 잘 할 수 있는 리팩터링 두 가지

타겟: 코드의 냄새 (함수 이름에 있는 암묵적 인자)

특징
1.
거의 똑같이 구현 된 함수가 있음
2.
함수 이름이 구현의 차이점을 가리킴

리팩터링 ① : 암묵적 인자 드러내기

단계
1.
함수 이름에 있는 암묵적 인자 확인
2.
명시적 인자 추가
3.
함수 내 하드 코딩 된 값 인자로 변경
4.
함수 호출부 수정

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

단계
1.
함수 본문에서 바꿀 부분앞/뒷부분 확인
2.
리팩터링 할 코드를 함수로 분리
3.
분리 된 함수의 인자 부분을 다른 함수로 분리

 상황: 마케팅 팀은 여전히 개발팀과 협의해야 합니다

// 함수의 이름이 구현의 차이점을 가리킴 function setPriceByName(cart, name, price) { var item = cart[name]; // 거의 똑같이 구현 된 함수가 있음 var newItem = objectSet(item, 'price', price); var newCart = objectSet(cart, name, newItem); return newCart; } function setShippingByName(cart, name, ship) { var item = cart[name]; var newItem = objectSet(item, 'shipping', ship); var newCart = objectSet(cart, name, newItem); return newCart; } function setQuantityByName(cart, name, quant) { var item = cart[name]; var newItem = objectSet(item, 'quantity', quant); var newCart = objectSet(cart, name, newItem); return newCart; } function setTaxByName(cart, name, tax) { var item = cart[name]; var newItem = objectSet(item, 'tax', tax); var newCart = objectSet(cart, name, newItem); return newCart; }
JavaScript
복사
코드의 냄새: 함수 이름에 있는 암묵적 인자
1.
거의 똑같이 구현 된 함수가 있음
2.
함수 이름이 구현의 차이점을 가리킴
*코드의 냄새: 더 큰 문제를 가져올 수 있는 코드

 리팩터링: 암묵적 인자를 드러내기

1. 함수 이름에 있는 암묵적 인자를 확인

// 함수의 이름이 구현의 차이점을 가리킴 function setPriceByName(cart, name, price) { var item = cart[name]; var newItem = objectSet(item, 'price', price); var newCart = objectSet(cart, name, newItem); return newCart; }
JavaScript
복사

2. 명시적 인자를 추가

  BAD
// 함수의 이름이 구현의 차이점을 가리킴 function setPriceByName(cart, name, price) { (...) }
JavaScript
복사
  GOOD
// 암묵적 인자 대신 명시적인 인자를 추가 function setFieldByName(cart, name, field, value) { (...) }
JavaScript
복사

3. 함수 본문에 하드 코딩된 값을 새로운 인자로 바꿈

  BAD
// 함수의 이름이 구현의 차이점을 가리킴 function setPriceByName(cart, name, price) { var item = cart[name]; var newItem = objectSet(item, 'price', price); var newCart = objectSet(cart, name, newItem); return newCart; }
JavaScript
복사
  GOOD
// 암묵적 인자 대신 명시적인 인자를 추가 function setFieldByName(cart, name, field, value) { var item = cart[name]; var newItem = objectSet(item, field, value); var newCart = objectSet(cart, name, newItem); return newCart; }
JavaScript
복사

4. 함수를 부르는 곳을 고침

  BAD
// 함수 이름에 있는 암묵적인 인자를 사용하던 호출부 cart = setPriceByName(cart, "shoe", 13) cart = setShippingByName(cart, "shoe", 3) cart = setQuantityByName(cart, "shoe", 0) cart = setTaxByName(cart, "shoe", 2.34)
JavaScript
복사
  GOOD
// 명시적인 인자를 사용하는 방식으로 호출부를 수정 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)
JavaScript
복사

결과

AS-IS
TO-BE
개발 방식
함수명에 암묵적 인자가 포함 됨 (코드의 냄새)
필요한 내용이 명시적 인자로 분리 됨 (일급 값)
개발 공수
비슷한 요청에 대해 매번 다른 함수추가
필드명 추가 → 일반적인 함수 setFieldByName으로 여러가지 일을 할 수 있게 됨
사용 방식
각각의 요청 대해 각기 필요한 함수를 찾아서 사용
일반적인 함수 하나와 그에 대한 API 문서필드명 리스트를 참고하여 사용

 일급인 것과 일급이 아닌 것 구별하기

일급 값 (first-class value)이란?

언어에 있는 다른 값처럼 쓸 수 있는 것 → 언어 전체 어디에서나 쓸 수 있음

일급 값의 예시

문자열, 숫자, 불리언, 배열, 객체

일급 값으로 할 수 있는 일

변수에 할당 하기
함수의 인자로 넘기기
함수의 리턴 값으로 받기
배열이나 객체의 값으로 담기

일급 값이 아닌 것

수식연산자 (+, -, *, /)
반복문 (for, while)
조건문 (if, switch)
try/catch 블록

중요한 것은 일급 값이 아닌 것을 일급 값으로 바꾸는 방법 을 아는 것!

ex) 함수명의 일부(일급값 X) → 명시적인 인자(일급값 O)로 바꾸는 것

 필드명을 문자열로 사용하면 버그가 생기지 않을까요?

문제점
문자열 형태의 필드명 사용 시의 버그 가능성 (오타 등)
해결책 ①
컴파일 타임에 검사
설명
- 타입스크립트 등 정적 타입시스템을 활용 - 문자열이 사용할 수 있는 필드인지 코드 실행 전 확인 가능
해결책 ②
런타임에 검사
설명
- 함수 실행 시 사용가능 한 필드명 확인 - 문자열이 사용할 수 있는 필드인지 런타임이 되서야 확인 가능
const validItemFields = ['price', 'quantity', 'shipping', 'tax']; function setFieldByName(cart, name, field, value) { // 런타임 검사: 정해진 필드명 외의 값은 에러 던짐! if(!validItemFields.includes(field)) throw "Not a valid item field: " + "'" + field + "'."; // (...) }
JavaScript
복사

 일급 필드를 사용하면 API를 바꾸기 더 어렵나요?

상황
함수명에 있던 엔티티 → 명시적인 필드명으로 바꿀 시
걱정되는 점
① 추상화 벽 외부로 구현이 노출되는 것인지? ② API 문서에 필드명을 명시하면 추후 변경이 불가한 지?
설명
① 명시적인 인자로 만들었다 해도 구현이 노출된 것은 아님 ② API 명시된 필드명은 그대로 두고 내부 구현만 변경 가능
const translations = { 'quantity': 'number' }; function setFieldByName(cart, name, field, value) { // (...) // 외부에 노출 된 필드명을 내부에서 필요한 필드명으로 치환 가능! if(translations.hasOwnProperty(field)) field = translations[field]; // (...) }
JavaScript
복사

 객체와 배열을 너무 많이 쓰게 됩니다

데이터 지향
이벤트엔티티 사실 표현 위해 일반 데이터 구조를 사용하는 프로그래밍 형식
활용
장바구니와 제품처럼 일반적인 엔티티 → 객체와 배열처럼 일반적인 데이터 구조를 활용해야 함
장점
데이터가 미래에 해석이 필요할 시 유용함 (여러가지 방법 중 알맞은 방법으로 해석이 가능)

 정적 타입 vs 동적 타입

정적 타입 언어와 동적 타입언어는 각각 장단점이 있다.

 모두 문자열로 통신합니다

※ 도식화 된 통신과정
웹 브라우저
- 서버로 문자열을 보냄 (JSON)
웹 서버
- 데이터베이스로 문자열을 보냄
API
- 클라이언트에게 받은 데이터를 런타임에 체크함 - 정적 타입 언어 사용 시 시스템 내 코드의 타입 관리만 가능

데이터의 단점: 항상 해석이 필요!

 어떤 문법이든 일급 함수로 바꿀 수 있습니다

  연산자
const plus = (a, b) => a + b
JavaScript
복사
  연산자
const minus = (a,b) => a - b
JavaScript
복사
  연산자
const times = (a, b) => a * b
JavaScript
복사
  연산자
const dividedBy = (a, b) => a / b
JavaScript
복사

자바스크립트에 있는 많은 값을 일급으로 바꿀 수 있음

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

고차 함수: 함수인자로 받거나 리턴값으로 내보낼 수 있는 함수

→ 함수 본문을 콜백으로 바꾸기 (To be continued…)