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. 명시적 인자를 추가
// 함수의 이름이 구현의 차이점을 가리킴
function setPriceByName(cart, name, price) {
(...)
}
JavaScript
복사
// 암묵적 인자 대신 명시적인 인자를 추가
function setFieldByName(cart, name, field, value) {
(...)
}
JavaScript
복사
3. 함수 본문에 하드 코딩된 값을 새로운 인자로 바꿈
// 함수의 이름이 구현의 차이점을 가리킴
function setPriceByName(cart, name, price) {
var item = cart[name];
var newItem = objectSet(item, 'price', price);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
JavaScript
복사
// 암묵적 인자 대신 명시적인 인자를 추가
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. 함수를 부르는 곳을 고침
// 함수 이름에 있는 암묵적인 인자를 사용하던 호출부
cart = setPriceByName(cart, "shoe", 13)
cart = setShippingByName(cart, "shoe", 3)
cart = setQuantityByName(cart, "shoe", 0)
cart = setTaxByName(cart, "shoe", 2.34)
JavaScript
복사
// 명시적인 인자를 사용하는 방식으로 호출부를 수정
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
복사
→ 자바스크립트에 있는 많은 값을 일급으로 바꿀 수 있음
리팩터링②: 함수 본문을 콜백으로 바꾸기
고차 함수: 함수를 인자로 받거나 리턴값으로 내보낼 수 있는 함수