새소식

프론트엔드 공부/자바스크립트

koans 문제풀이로 알게된 점

  • -

지금껏 배워왔던 것들을 바탕으로 기본 개념에 대해 50여가지의 문제로 나와있는 코드스테이츠의 koans 문제를 풀며

배우면서 명확히 이해하지 못했던 것들을 접하며 개념을 다시 익히는 시간이 되었다.


expect, matcher

 

첫번째로 코플릿을 풀때 어떻게 정답을 검사하는지 expect와 matcher에 대해 알게되었다.

expect는 작성한 함수가 주어진 입력값에 대해서 리턴하는 값이 기대하는 값과 같은지를 비교하는 데 사용한다. 즉, 테스트하는 값과 기대값을 비교하기 위해 expect 함수를 사용한다. expect의 기본적인 사용법은 다음과 같다.

expect(테스트하는값).기대조건

테스트하는 값으로는 1) 표현식 또는 2) 함수실행이 들어갈 수 있다. true, 1+1 등 값을 반환하는 식 또는 코드가 표현식에 해당하고, isEven(3), sum(1,2) 등 특정 함수가 실행되는 것이 함수실행이다.

expect(isEven(3)).to.be.true  // isEven(3)의 결과가 참인지 판단
expect(1 + 2).to.equal(3)     // 1+2가 3과 같은지 판단(equal)

 

'기대하는조건'에 해당하는 함수를 matcher라고 한다.

to.be.true  //'참인 것이어야 한다'
to.equal(3) //'3과 같아야 한다'

// matcher 기대하는 조건

expect에서 '기대조건'에 해당하는 함수를 matcher라고 부른다. 위의 코드에서 to.be.true와 to.equal이 바로 matcher이다. 
mocha, chai framework에는 다양한 matcher들이 있고 상황에 따라 사용하면 될 것 같다.

'기대하는 값'은 true, false 뿐만 아니라 어떤 구체적인 값일 경우가 있다. 이 경우 '기대하는 값'이 '특정 값'과 같은지를 비교해서 테스트를 진행하면 된다.  가장 간단한 방법은 비교 연산자 === 을 사용하는 방법이다.

it("'테스트하는 값'을 '기대하는 값'과 비교한 결과가 참 인지 확인합니다.", function () {
    // '테스트하는 값'은 우리가 작성한 어떤 코드의 실제 실행 결과 값이므로 '실제 값'이라고 불러도 됩니다.
    let actualValue = 1 + 1;
    let expectedValue = actualValue; // TODO: 'FILL_ME_IN'을 변경하여 테스트 케이스를 완성합니다.
    expect(actualValue === expectedValue).to.be.true;
  });

 

두 값 A와 B를 '비교한 결과'가 참인지를 확인하는 대신에 직접 A가 B와 같은지 확인하는 matcher인지 확인 하는 방법으로는 .equal이 확인하는 역할을 한다. 아래 테스트 코드는 '테스트하는값'이 '기대하는값'과 같은지 직접 확인 가능하다.   

expect('테스트하는값').to.equal('기대하는값');
 it('Matcher .equal 의 사용법을 학습합니다.', function () {
    let expectedValue = 1 + 1; // TODO
    // .equal은 두 값이 타입까지 엄격하게 같은지 검사(strict equality, ===)합니다.
    expect(1 + 1).to.equal(expectedValue);
  });
  
  it('Matcher .equal의 사용법을 학습합니다.', function () {
    let actualValue = (1 + 1).toString();
    expect(actualValue).to.equal('2'); 
  });

동치연산자(느슨한 동치 연산 ==  엄격한 동치 연산 ===)

 

느슨한 동치연산과 엄격한 동치연산은 예시를 들어보면 이해하기 쉬운것 같다.

it("비교연산자 '=='는 두 값의 일치 여부를 느슨하게 검사(loose equality)합니다.", function () {
    let actualValue = 1 + 1;
    let expectedValue = 2;
    expect(actualValue == expectedValue).to.be.true;
    
    
    //느슨한 동치연산의 예시 
    expect(0 == false).to.be.true;
    expect('' == false).to.be.true;
    expect([] == false).to.be.true;
    expect(![] == false).to.be.true;
    expect([] == ![]).to.be.true;
    expect([] == '').to.be.true;
    expect([] == 0).to.be.true;
    expect([''] == '').to.be.true;
    expect([''] == 0).to.be.true;
    expect([0] == 0).to.be.true;
  });

위 코드 expectedValue = 2; 에서 숫자타입 2를 expectedValue ='2'; (문자열 '2')로 변경해도 테스트가 통과된다

'=='의 실행 중 타입 변환(type coercion)이 일어나기 때문이며 느슨한 동치 연산(loose equality)는 프로그램의 작동을 예측하기 다소 어렵게 만든다. 매우 비효율적이므로 사용을 지양하고 엄격한 동치 연산(strict equality) '==='를 사용하는걸 지향한다.

let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue === expectedValue).to.be.true;

위 코드에서 expectedValue = 2; 는 '2' 문자열으로 바꾸면 통과하지 못한다.

 

// 자바스크립트의 별난(quirky) 부분

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(1 + '1').to.equal('11');
  });

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(123 - '1').to.equal(122);
  });

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(1 + true).to.equal(2);
  });

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect('1' + true).to.equal('1true');
  });
  

// 이러한 것들을 모아놓은 https://github.com/denysdovhan/wtfjs 사이트도 존재
// 외울 생각 하지말고 애초에 올바른 코딩습관을..
최대한 같은 타입끼리 연산을 하고, 즉 엄격한 동치 연산('===')을 사용하고, 조건문에 비교 연산을 명시하는 것이 훨씬 좋다.
 

const

이미 다른 포스팅에서 let, const, var에 대해 다루며 익혔지만 새삼 또 새롭게 느꼇다.

const는 변수 선언 후 재할당이 안되며 새로운 변수에 할당하거나 .push(), .pop() 같은 매소드는 사용가능하다.

describe("'const'에 대해서 학습합니다.", function () {
  it("'const'로 선언된 변수에는 재할당(reassignment)이 금지됩니다.", function () {
    // 아래 코드에서 문제가 되는 부분을 삭제합니다.
    const constNum = 0;
    expect(constNum).to.equal(0);
	// constNum = 0; 삭제되어야 하는 부분. const변수는 재할당이 금지된다.
    const constString = 'I am a const';
    // constString = "which means I'm a constant variable, delete me."; 삭제되어야 하는 부분. const변수는 재할당이 금지된다.
    expect(constString).to.equal('I am a const');
  });

  it("'const'로 선언된 배열의 경우 새로운 요소를 추가하거나 삭제할 수 있습니다.", function () {
    const arr = [];
    const toBePushed = 42;
    arr.push(toBePushed); // .push() 사용가능
    expect(arr[0]).to.equal(42);

    // 여전히 재할당은 금지됩니다.
    // arr = [1, 2, 3];
  });

  it("'const'로 선언된 객체의 경우, 속성을 추가하거나 삭제할 수 있습니다.", function () {
    const obj = { x: 1 };
    expect(obj.x).to.equal(1);

    delete obj.x; // delete 사용가능
    expect(obj.x).to.equal(obj.x);

    // 여전히 재할당은 금지됩니다.
    // obj = { x: 123 };

    obj.occupation = 'SW Engineer';
    expect(obj['occupation']).to.equal('SW Engineer');
  });

 

구글 스타일 가이드는 선언 키워드에서 변수 선언에 대해 아래와 같이 언급한다.
5.1.1 사용 const 및 let
const 또는 let 를 사용하여 모든 지역 변수를 선언합니다.  변수를 재할당해야 하는 경우가 아니면 기본적으로 const를 사용합니다.
var 키워드를 사용하면 안 됩니다.
5.1.2 선언당 하나의 변수
모든 지역 변수 선언은 하나의 변수만 선언합니다. 와 같은 선언 let a = 1, b = 2;은 사용되지 않습니다.

hoisting

JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.  
즉, 함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위 최상단에 선언하는 것.

var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화하는 반면 let const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않는다.

JavaScript는 함수의 코드를 실행하기 전에 함수 선언에 대한 메모리부터 할당합니다. 덕분에 함수를 호출하는 코드를 함수 선언보다 앞서 배치할 수 있습니다. 예를 들어,

function catName(name) {
  console.log("제 고양이의 이름은 " + name + "입니다");
}

catName("호랑이");

/*
결과: "제 고양이의 이름은 호랑이입니다"
*/

 

위의 코드 조각이 일반적으로 코드를 작성하는 순서라면, 함수를 선언하기 전에 먼저 호출했을 때의 예제

catName("클로이");

function catName(name) {
  console.log("제 고양이의 이름은 " + name + "입니다");
}

/*
결과: "제 고양이의 이름은 클로이입니다"
*/

 

함수 호출이 함수 자체보다 앞서 존재하지만, 그럼에도 불구하고 이 코드 역시 동작한다. 이것이 JavaScript에서 실행 맥락이 동작하는 방식.

호이스팅은 다른 자료형과 변수에도 잘 작동한다. 변수를 선언하기 전에 먼저 초기화하고 사용할 수 있는 것이다.


=>  화살표함수

화살표 함수 표현(arrow function expression)은 전통적인 함수표현(function)의 간편한 방법이다. 하지만, 화살표 함수는 몇 가지 제한점이 있고 모든 상황에 사용할 수는 없다.

it('함수 표현식 사용법을 복습합니다', function () {
    const add = function (x, y) {
      return x + y
    }
    expect(add(5, 8)).to.eql(13)
  })

 

위와 같은 식의 코드를 화살표 함수로 바꾼다면

it('화살표 함수 사용법을 익힙니다', function () {
    // function 키워드를 생략하고 화살표 => 를 붙입니다
    const add = (x, y) => {
      return x + y
    }
    expect(add(10, 20)).to.eql(30)

    // 리턴을 생략할 수 있습니다
    const subtract = (x, y) => x - y
    expect(subtract(10, 20)).to.eql(-10)

    // 필요에 따라 소괄호를 붙일 수도 있습니다
    const multiply = (x, y) => (x * y)
    expect(multiply(10, 20)).to.eql(200)

    // 파라미터가 하나일 경우 소괄호 생략이 가능합니다
    const divideBy10 = x => x / 10
    expect(divideBy10(100)).to.eql(10)
  })
it('화살표 함수를 이용해 클로저를 표현합니다', function () {
    const adder = x => {
      return y => {
        return x + y
      }
    }

    expect(adder(50)(10)).to.eql(60)

    const subtractor = x => y => {
      return x - y
    }

    expect(subtractor(50)(10)).to.eql(40)

    const htmlMaker = tag => textContent => `<${tag}>${textContent}</${tag}>` 
    expect(htmlMaker('div')('code states')).to.eql('<div>code states</div>') // 태그 라는거에는 < > 가 포함되는건지?

    const liMaker = htmlMaker('li')
    expect(liMaker('1st item')).to.eql('<li>1st item</li>')
    expect(liMaker('2nd item')).to.eql('<li>2nd item</li>')
  })
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.