hoon's bLog

Javascript 자바스크립트 코딩의 기술 7장 유연한 함수를 만들어라 (1) 본문

IT/Javascript

Javascript 자바스크립트 코딩의 기술 7장 유연한 함수를 만들어라 (1)

개발한기발자 2024. 2. 21. 11:20
반응형


7장 유연한 함수를 만들어라

tip32. 테스트하기 쉬운 함수를 작성하라

  • 코드를 쉽게 리팩토링 가능
  • 오래된 코드를 쉽게 이해 가능
  • 마지막으로 명확하고 버그가 적은 코드를 작성 가능
  • 너무 당연한 말이 아닐까 싶지만, 현실은 테스트 코드를 작성하는 것 조차 쉽지 않다.
    (테스트 작성의 어려움, 코드가 외부 의존성이 강하게 결합, 개발속도가 느림 등...)
import { getTaxInformation } from './taxService';

function formatPrice(user, { price, location }) {
  const rate = getTaxInformation(location); // <label id="test.external" />
  const taxes = rate ? `추가 세금 $${price * rate}` : '추가 세금';

  return `${user}님의 합계 금액: $${price} 및 ${taxes}`;
}
  • 위의 코드는 주어진 사용자 정보와 제품 금액 및 위치에 따라서 추가 세금 정보를 계산하여 포맷팅하는 함수
  • formatPrice 함수는 user, { price, location }을 매개변수로 받고, { price, location }는 객체 구조 분해를 사용하여 price와 location 속성을 각각의 변수로 분리하여 받는다
  • getTaxInformation(location) : taxService.js 파일에서 가져온 getTaxInformation 함수를 호출하여 해당 위치에 대한 세금 정보를 가져옴. (getTaxInformation() 외부 함수를 호출 할때 시작, 이 함수는 제공된 위치에 대한 세금 비율을 반환)
  • 우리는 단지 return되는 문자열만 필요할 뿐인데, 이 함수가 외부 서비스, 설정 파일, 네트워크 통신 등에 의존하게 될 경우 복잡해진다.
  • 이 문제를 해결하려면 모의 객체(mock)을 생성해서 함수를 가로채고 명시적인 반환값을 설정!!!
    (가짜 함수를 생성해서 가짜 값을 반환한다 라고 이해하면 편할 듯)
import expect from 'expect';
import sinon from 'sinon';
import * as taxService from './taxService';
import { formatPrice } from './problem';

describe('금액 표시', () => {
  let taxStub;

  beforeEach(() => {
    taxStub = sinon.stub(taxService, 'getTaxInformation'); // <label id="test.stub" />
  });

  afterEach(() => {
    taxStub.restore(); // <label id="test.restore" />
  });

  it('세금 정보가 없으면 세금 추가를 안내해야 한다', () => {
    taxStub.returns(null); // <label id="test.stub2" />
    const item = { price: 30, location: 'Oklahoma' };
    const user = 'Aaron Cometbus';
    const message = formatPrice(user, item);
    const expectedMessage = 'Aaron Cometbus님의 합계 금액: $30 및 추가 세금';
    expect(message).toEqual(expectedMessage);
  });

  it('세금 정보가 있으면 세금 금액을 알려줘야 한다', () => {
    taxStub.returns(0.1);

    const item = { price: 30, location: 'Oklahoma' };
    const user = 'Aaron Cometbus';
    const message = formatPrice(user, item);
    const expectedMessage = 'Aaron Cometbus님의 합계 금액: $30 및 추가 세금 $3';
    expect(message).toEqual(expectedMessage);
  });
});
  • 예제에서는 mocha 테스트 프레임워크를 사용했다.
    (mocha에 대한 자세한 설명은 나중에 다른 포스팅으로 소개!)
  • 10 line : taxStub 생성, getTaxInformation 함수를 덮어 써서 간단한 반환값을 반환(taxStub.returns(반환값);)
  • 14 line : 테스트 꾸러미가 종료되면 원래의 메서드를 사용하도록 코드를 복구한다.
  • 만약 예제 코드보다 더 많은 외부함수를 호출하고 밀접하게 결합된 코드는 의존성 주입(Dependency Injection)을 해야한다.
    (의존성을 인수로 전달하는 것이다.)
function formatPrice(user, { price, location }, getTaxInformation) {
  const rate = getTaxInformation(location);
  const taxes = rate ? `추가 세금 $${price * rate}` : '추가 세금';
  return `${user}님의 합계 금액: $${price} 및 ${taxes}`;
}
  • 위와 같이 getTaxInformation()을 인수로 전달한다.
  • 이제 stub이 필요하지 않게 되었고, 테스트를 작성할 때 불러오기를 생략할 필요 없다.
  • 그 대신 필요한 값을 반환하는 간단한 함수를 작성하면 된다.
import expect from 'expect';

import { formatPrice } from './test';

describe('금액 표시', () => {
  it('세금 정보가 없으면 세금 추가를 안내해야 한다', () => {
    const item = { price: 30, location: 'Oklahoma' };
    const user = 'Aaron Cometbus';
    const message = formatPrice(user, item, () => null); // 스텁 대신 () => null 로 간단한 함수 반환!!
    expect(message).toEqual('Aaron Cometbus님의 합계 금액: $30 및 추가 세금');
  });

  it('세금 정보가 있으면 세금 금액을 알려줘야 한다', () => {
    const item = { price: 30, location: 'Oklahoma' };
    const user = 'Aaron Cometbus';
    const message = formatPrice(user, item, () => 0.1);
    expect(message).toEqual('Aaron Cometbus님의 합계 금액: $30 및 추가 세금 $3');
  });
});
  • 테스트 코드를 좀 더 작성하기 쉬워졌고, 코드가 단일 책임을 갖도록 책임을 줄이는 면에서도 효율적이다.
  • 일어날 수 있는 Test Case를 고려하며, 테스트를 작성하기 쉽게 코드를 변경해야 한다.

tip33. 화살표 함수로 복잡도를 낮춰라

mozila에서 설명하는 화살표 함수는 다음과 같다.

  • 화살표 함수(=>, Arrow Function) : 함수 표현식에 대한 간결한 대안으로, 약간의 의미 차이와 의도적인 사용상의 제한을 가진다.
    • 화살표 함수에는 자체 바인딩이 this에 없으며, 인수 또는 super로 사용해야 하며, 메서드로 사용 불가
    • 화살표 함수는 생성자로 사용할 수 없다.(new로 호출하면 TypeError가 반환)
    • 화살표 함수는 함수 내부에서 yield를 사용할 수 없으며 Generator 함수로 생성불가.
  • 화살표 함수에서 인수를 해체 할당하는 방법, 객체를 반환하는 방법, 고차함수를 만드는 방법을 알아보자.
  • 해체 할당하는 방법과 객체를 반환하는 방법은 아래 코드로 살펴보자.
const name = {
  first: 'Kevin',
  last: 'De Bruyne',
};

function getName({ first, last }) {
  return `${first} ${last}`;
}

const getName = ({ first, last }) => `${first} ${last}`

getName(name) // "Kevin De Bruyne"
  • 화살표 함수를 사용하고 매개변수의 객체을 () 감싼다.
  • return 값이 객체를 반환할 경우는 꼭 화살표 우측 반환될 객체를 () 감싼다.
    • const getName = ({ first, last }) => ({ fullName:${first} ${last}});
  • 다른 함수를 반환하는 고차함수 만들기는 다음 팁에서 자세히 알아볼 예정
const discounter = discount => {
  return price => {
    return price * (1 - discount);
  };
};
const tenPercentOff = discounter(0.1); // discount 매개변수에 할당
tenPercentOff(100); // 90 // price 매개변수에 할당

tenPercentOff = (price) => {
  return price * (1 - 0.1)
}

const discounter = discount => price => price * (1 - discount);

discounter(0.1)(100);
  • line 1~5를 우리가 알던 function 방식으로 바꿔본다면 아래 코드와 같다.(아래코드 참조)
  • line 13 : return, 중괄호 생략한 고차 함수 discounter 선언
  • line 15 : 고차함수 discounter의 매개변수 discount에 0.1을 받고, 바로 뒤에 괄호로 연결해서 매개변수 price에 100을 전달하면 이어서서 호출한다.
function discounter(discount) {
  return function(price) {
    return price * (1 - discount);
  };
}

 

엇?! 이게 그래도 보기 편한데...라고 생각할 수 있지만,

그럼에도 불구하고 화살표 함수로 바꾸는 이유는

  • 간결한 문법 : 함수 선언을, 특히 중첩된 함수의 경우, 화살표 함수를 사용하면 코드가 더 간결해진다.
  • 자동 바인딩 : 화살표 함수는 자신의 this를 바인딩하지 않고, 외부 스코프의 this를 그대로 사용한다.
    (이는 일반 함수에서 발생하는 this 오류를 예방할 수 있다.)
  • 암묵적 반환 : 화살표 함수는 중괄호 {}를 사용하지 않고 한 줄로 표현 가능하여, 암묵적으로 반환한다.
  • 메서드에서 사용 : 화살표 함수는 메서드로 사용될 때 객체의 바인딩을 유지하므로, 객체 내부에서 화살표 함수를 사용할 때 유용하다.

사실 레거시 코드나, 실무에서 직관적이고 기존의 방식을 고수하기 때문에,

화살표 함수 사용하기를 꺼려하는 경우도 종종 있다.

필자도 화살표 함수뿐만 아니라 다른 자바스크립트의 기술들 또한

모르고 익숙치 않다는 핑계로 잘 사용하지 않았다.

 

개발자는 내가 모른다고 해서, 익숙하지 않다고 해서,

익히지 않고 공부하지 않는 것은 옳지 않다.

 

계속해서 계발하고, 개발하자!

끝.

Reference

  • 자바스크립트 코딩의 기술 요약 / 정리
  • JavaScript - MDN Web Docs

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions

 

화살표 함수 표현식 - JavaScript | MDN

화살표 함수 표현식(화살표 함수 expression)은 함수 표현식에 대한 간결한 대안으로, 약간의 의미적 차이와 의도적인 사용상의 제한을 가지고 있습니다.

developer.mozilla.org

  • lexical environment와  closure

https://velog.io/@paulkim/e

 

Lexical Environment 와 Closure에 대한 이해

본 글은 다음의 블로그 글로 저랑 해당 내용에 대해서 이야기를 하셨던 분이 조금 더 잘 이해할 수 있도록 몇 가지 내용을 생략하거나 의미를 간소화해서 번역한 내용입니다. 보다 정확한 출처

velog.io

 

728x90
반응형