JS STUDY - 1주차( 매개변수 )

👊0👊·2020년 1월 13일
0

es6 스터디

목록 보기
3/8

자바스크립트 코딩의 기술을 정리한 것이니 좀 더 궁금하면 책을 찾아보자.

JS의 함수는 매우 유연하다. 인자 타입을 정의할 수도 없고, 인자의 수를 정하기도 힘들다.
그래서 함수에 예외처리에 매우 까다롭다.

그래서 함수 인수를 변경하는 계획을 세워보고, 간결하면서 유연한 매개변수를 생성하는 방법을 알아보도록 하자. 완벽하진 않지만, 전보다 나은 코드가 작성될 것이라 생각한다.

자세한 타입에 대한 이야기는 아래글을 참고해보면 좋을 것 같다.
[번역] 두려움, 믿음, 그리고 자바스크립트 - 언제 타입 시스템과 함수형 프로그래밍이 먹히지 않는가

매개변수 기본값 생성하기

기본값 문제

파운드를 킬로그램으로 바꾸는 함수를 생각해보자.

1 파운드 = 16온스
2.2 파운드 = 약 1킬로그램

단순하게 파운드를 입력받아 2.2로 나누면 된다.

function coverWeight(pound) {
  return pound / 2.2;
}

근데 변경이 생겨서 온스도 변환해야 한다는 요청을 받았다. 자바스크립트는 매개변수가 생략될 수 있다. 그래서 기존에 coverWeight를 사용하고 있는 코드변경 없이, coverWeight를 수정하면 된다.

하지만 추가한 매개변수를 누락하면 값이 undefined가 되기 때문에 기본값이 필요하다.

function coverWeight(pound, ounces) {
  const oz = ounces ? ounces / 16 : 0;
  const total = pound + oz;
  return total / 2.2;
}

괜찮은 코드일 수 있지만, 부동소수점 연산 때문에, 예상치 못한 결과가 나온다.
convertWeight(6, 6)은 3이지만, 2.9999...가 나온다.
그래서 반올림을 처리하기 위한 헬퍼 함수를 만들어 보안하자.

두 번째 소수점 자리까지 기본적으로 나오게 만들어봅시다.

function roundToDecimalPlace(number, decimalPlaces) {
  const round = 10 ** decimalPlaces;
  return Math.round(number * round) / round;
}

function convertWeight(weight, ounces, roundTo) {
  const oz = ounces / 16 || 0;
  const total = weight + oz;
  const conversion = total / 2.2;
  
  // const round = roundTo || 2;
  const round = roundTo === undefined ? 2 : roundTo;

  return roundToDecimalPlace(conversion, round);
}

const round = roundTo || 2는 좀 더 간결하지만 적절한 방법이 아니다.
왜냐하면 소수점 자리르 0으로 넘겼을 때 rountTo가 거짓 값으로 처리되어 소수점 두 번째 자리까지 반환하기 때문이다.

기본 매개변수

이제 기본 매개변수로 코드를 바꿔보자.

function convertWeight(weight, ounces = 0, roundTo = 2) {
  const total = weight + (ounces / 16);
  const conversion = total / 2.2;

  return roundToDecimalPlace(conversion, roundTo);
}

매개변수 기본값은 값을 전달하지 않았을 때, 미리 정해둔 값을 기본값으로 사용한다.
게다가 특정한 자료형이 필요하다는 단서를 다른 개발자에게 알려줄 수도 있다.
예를 들어 온스에는 정수를 넘겨줘야 한다는 것을 눈치챌 수도 있다.

물론 완벽한 해결책은 아니다.
매개변수의 순서가 중요하기 때문이다. 온스를 추가하지 않는 경우에도 소수점 자릿수를 지정하려면 온스 자리에 0을 입력해야 한다.

coverWeight(4, 0, 2);

해체 할당으로 객체 속성에 접근하기

매개변수 기본값은 항상 순서를 지켜야 한다는 문제를 해결하지 못했다. 매개변수를 건너뛰고 싶은 경우에는 매개변수 기본값이 별 소용이 없다.

아래코드는 사진 정보를 HTML 문자열로 바꾸는 코드입니다.
사진에 따라서 정보의 형식이 달라질 수 있습니다.

이런 정보를 개별 매개변수로 전달하는 것은 적절하지 않다. 그러다가 10여 개의 매개변수를 작성하게 될지도 모른다. 게다가 구조화된 정보를 다시 변경하는 것은 바람직하지 않다.

문제

const landscape = {
  title: 'Landscape',
  photographer: 'Nathan',
  equipment: 'Canon',
  format: 'digital',
  src: '/landscape-nm.jpg',
  location: [32.7122222, -103.1405556],
};

function displayPhoto(photo) {
  // 미리 알고 있는 정보들
  const title = photo.title;
  const photographer = photo.photographer || 'Anonymous';
  const location = photo.location;
  const url = photo.src;
  // 복제본을 만들고 다시 삭제한다.
  const copy = { ...photo };
  delete copy.title;
  delete copy.photographer;
  delete copy.location;
  delete copy.src;
  // 나머지 정보들을 추출한다.
  const additional = Object.keys(copy).map(key => `${key}: ${copy[key]}`);

  return (`
    <img alt="${title} 사진 ${photographer} 촬영" src="${url}" />
    <div>${title}</div>
    <div>${photographer}</div>
    <div>위도: ${location[0]} </div>
    <div>경도: ${location[1]} </div>
    <div>${additional.join(' <br/> ')}</div>
  `);
}

미리 알 수 있는 값을 받아서 처리하는 것은 어렵지 않지만, 미리 알 수 없는 과도한 양의 정보를 다루기는 어렵다. 객체를 복제한 후에는 필요치 않은 키를 삭제해야한다.
결과적으로 간단한 동작에 수많은 객체 할당이 필요하게 된다.
코드의 2/3이 객체 정보를 가져오는데 할애되고 있다.

그럼 이제 해체 할당을 이용해서 다시 한번 작성해보자.

해결

function displayPhoto(photo) {
  const {
    title,
    photographer = 'Anonymous',
    location: [latitude, longitude],
    src: url,
    ...other
  } = photo;
  const additional = Object.keys(other).map(key => `${key}: ${other[key]}`);
  return (`
    <img alt="${title} 사진 ${photographer} 촬영" src="${url}" />
    <div>${title}</div>
    <div>${photographer}</div>
    <div>위도: ${latitude} </div>
    <div>경도: ${longitude} </div>
    <div>${additional.join(' <br/> ')}</div>
  `);
}

훨씬 깔끔해졌다. 해체할당의 장점은 매개변수에도 적용이 가능하는 점이다. 이번엔 매개변수에 적용해보자.

function displayPhoto({
  title,
  photographer = 'Anonymous',
  location: [latitude, longitude],
  src: url,
  ...other
}) {
  const additional = Object.keys(other).map(key => `${key}: ${other[key]}`);
  return (`
    <img alt="${title} 사진 ${photographer} 촬영" src="${url}" />
    <div>${title}</div>
    <div>${photographer}</div>
    <div>위도: ${latitude} </div>
    <div>경도: ${longitude} </div>
    <div>${additional.join(' <br/> ')}</div>
  `);
}

해체 할당을 사용하면 변수 할당에 관한 문제를 해결할 수 있을 뿐만 아니라, 매개변수로 객체를 전달하기 때문에, 키-값의 순서를 염려하지 않아도 된다.

나머지 매개변수로 여러 개의 인수를 변수로 전달하기

매개변수를 객체로 전달하는 기법은 전달하는 매개변수들이 서로 다르다는 것을 미리 알고 있을 경우네만 유용하다. 즉 객체를 다루는 경우에만 쓸모가 있다.

그렇다면 전체 개수를 알 수 없는 비슷한 매개변수들은 어떻게 처리해야 할까?

문제

인스타를 생각해보자, 인스타의 해쉬태그의 길이를 제한해야 한다는 어떻게 해야 할까?
아래 함수는 모든 태그가 조건을 통과하면 true를 반환하는 유효성 검사 함수이다.

every는 모든 항목들이 참 값으로 반한되면 true를 리턴하고 아니면 false를 리턴하는 메서드이다.

function validateCharacterCount(max, items) {
  return items.every(item => item.length < max);
}

validateCharacterCount(10, ['Hobbs', 'Eagles']);
// true
validateCharacterCount(10, 'wvoquine');
// TypeError

이 코드의 단점은 특정한 컬렉션을 강제한다. 만약 한 건만 검사하려면 배열로 전달해야만 한다.

문서화할 수 있지만, 조금 더 나은 방법이 있다.
argumetns 객체를 사용하는 것이다. arguments는 유사배열객체이다. 그래서 배열 메서드를 직접적으로 사용하지 못한다.

function validateCharacterCount(max) {
  const items = Array.prototype.slice.call(arguments, 1);
  // <label id="rest.arguments" />
  return items.every(item => item.length < max);
}

이제 전달받은 인수가 배열로 바꾼다는 점을 알고 있기 때문에 원하는 만큼 인수를 전달할 수 있다.
하지만 전달할 인수가 이미 배열이면 인수 목록을 변환해서 전달해야 한다.

펼침 연산자를 떠올려보자. 펼침 연산자는 인수에도 사용가능하다.

해결

이 방법의 문제는 arguemtns객체를 다루는 문법이 난해다는 점이다. 또한, 함수 매개변수로 인수 목록을 받는다는 사실을 알기도 어렵다. 함수 몸체를 살펴봐야 함수에 전달할 수 있는 것이 무엇인지 알 수 있다.

이 문제를 해결하기 위해서 나머지 매개변수를 사용할 수 있다.
나머지 매개변수는 인수 목록을 전달해 변수에 담을 수 있다.

function validateCharacterCount(max, ...items) {
  return items.every(item => item.length < max);
}

간결할 뿐 아니라, 예측 가능성도 높아졌다. 이제 다른 개발자들도 최소 두 개 이상의 인수를 받을 수 있다는 것을 알 수 있다.

이제 다양한 인수를 다룰 수 있게 됬다.

validateCharacterCount(10, 'wvoquie');
// true

validateCharacterCount(10, ...['wvoquie']);
// true

const tags = ['Hobbs', 'Eagles'];
validateCharacterCount(10, ...tags);
// true

validateCharacterCount(10, 'Hobbs', 'Eagles');
// true

나머지 매개변수를 사용하는 몇 가지 이유는 다음과 가다.
1. 인수를 배열로 다루는 것을 다른 개발자에게 알려야 하는 경우
2. 나머지 매개변수는 코드 디버깅에 좋은 방법이 될 수 있다.

정리

매개변수 기본값을 이용해서 인자의 기본값을 다루는 방법을 알아보고
해체 할당으로 정적인 데이터를 처리하는 방법,
마지막으로 나머지 매개변수로 정해지지 않은 수의 인수를 다루는 방법을 알아봤다.

스스로 기존코드에 대해서 반성이 좀 되는 시간이었다.

참고자료

profile
ㅎㅎ

0개의 댓글