Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions 01_Introduction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
describe('expect에 대해서 학습합니다.', function () {
/*
아래 코드는 여러분이 작성한 함수가 주어진 입력값에 대해서 리턴하는 값이 기대하는 값과 같은지를 비교하는 것입니다.

이때 테스트하는 값과 기대값을 비교하기 위해 expect 함수를 사용합니다.
expect의 사용법은 아래와 같습니다.

expect(테스트하는값).기대하는조건
expect(isEven(3)).to.be.true => 'isEven(3)'의 결과값은 참(true)이어야 한다'
expect(1 + 2).to.equal(3) => 'sum(1, 2)의 결과값은 3과 같아야(equal) 한다'

'기대하는조건'에 해당하는 함수를 matcher라고 합니다.
'참인 것이어야 한다' => to.be.true
'3과 같아야 한다' => to.equal(3)

mocha, chai framework에는 다양한 matcher가 있지만, 이번 과제에서는 그 일부가 사용됩니다.
궁금하시면 아래 링크를 통해 확인하시기 바랍니다. (어떤 것들이 있는지만 확인하는 수준이면 충분합니다.)
https://www.chaijs.com/api/bdd/

여러분들은 expect를 활용하여 모든 Koans 과제를 완성하시면 됩니다.
즉, 각 테스트 케이스가 테스트를 통과하도록 테스트 코드를 적절하게 변형하여야 합니다.
단순히 테스트를 통과하는 것을 넘어, '왜 통과하는지'를 고민하는 시간이 되었으면 합니다.
*/

it('테스트하는 값(expect의 전달인자)이 true인지의 여부를 검사합니다.', function () {
/*
첫 문제는 가볍게 풀어봅시다.
expect 함수의 전달인자를 아래와 같이 false 대신 true로 변경하여 테스트를 통과하세요.
expect(true).to.be.true;
*/
// TODO: 테스트가 통과될 수 있도록(테스트하는 값이 true가 되도록) expect의 첫 번째 전달인자를 수정합니다.
expect(true).to.be.true;
});

it('테스트하는 값(expect의 전달인자)이 falsy 여부를 검사합니다.', function () {
// 반대의 경우에는 어떻게 해야할까요?
// TODO: 테스트가 통과될 수 있도록(테스트하는 값이 false가 되도록) expect의 첫 번째 전달인자를 수정합니다.
expect(false).to.be.false;
});

/*
2가지 matcher(.true, .false)를 통해 '기대하는 값'이 true인지 false인지 확인하는 방법을 학습했습니다.
이 때, '기대하는 값'은 표현식(expression)이거나 함수의 실제 실행 결과입니다.
1) 표현식: true || false, 1 + 1, 10 * 3
2) 함수의 실행: isEven(3), sum(1, 2)

보시면 알 수 있듯이, '기대하는 값'은 true, false 뿐만 아니라 어떤 구체적인 값인 경우가 있습니다.
이 경우 '기대하는 값'이 '특정 값'과 같은지를 비교해서 테스트를 진행하면 됩니다.
가장 간단한 방법은 비교 연산자 === 을 사용하는 방법이 있습니다.
'기대하는 값과 특정 값을 비교한 결과'가 true인지 확인하면 됩니다.
expect('기대하는 값과 특정 값을 비교한 결과').to.be.true;
*/
it("'테스트하는 값'을 '기대하는 값'과 비교한 결과가 참 인지 확인합니다.", function () {
// '테스트하는 값'은 우리가 작성한 어떤 코드의 실제 실행 결과 값이므로 '실제 값'이라고 불러도 됩니다.
let actualValue = 1 + 1;
let expectedValue = 2; // TODO: 'FILL_ME_IN'을 변경하여 테스트 케이스를 완성합니다.
expect(actualValue === expectedValue).to.be.true;
});

/*
이처럼 to.be.true, to.be.false 만을 가지고도 많은 테스트 케이스를 작성할 수 있습니다.
하지만 이는 직관적이지 않고 다소 불편합니다.
두 값 A와 B를 '비교한 결과'가 참인지를 확인하는 대신에 직접 A가 B와 같은지 확인하는 matcher가 없을까요?
.equal이 바로 그런 역할을 합니다. 아래 테스트 코드는 '테스트하는값'이 '기대하는값'과 같은지 직접 확인합니다.
expect('테스트하는값').to.equal('기대하는값');

이제 'FILL_ME_IN'을 적절한 값으로 변경하여 테스트 케이스를 완성하시면 됩니다.
이후에도 같은 방식으로 'FILL_ME_IN' 변경하면 됩니다.
*/
it('Matcher .equal 의 사용법을 학습합니다.', function () {
let expectedValue = 2; // TODO
// .equal은 두 값이 타입까지 엄격하게 같은지 검사(strict equality, ===)합니다.
expect(1 + 1).to.equal(expectedValue);
});

it('Matcher .equal의 사용법을 학습합니다.', function () {
let actualValue = (1 + 1).toString();
expect(actualValue).to.equal('2'); // TODO
});
});
67 changes: 67 additions & 0 deletions 02_Types-part1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
describe('type에 대해서 학습합니다.', function () {
it("비교연산자 '=='는 두 값의 일치 여부를 느슨하게 검사(loose equality)합니다.", function () {
let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue == expectedValue).to.be.true;

/*
혹시 'FILL_ME_IN'을 '2'(문자열 '2')로 변경해도 테스트가 통과된다는 사실을 알고 계십니까?
'=='의 실행 중 타입 변환(type coercion)이 일어나기 때문입니다.
이는 다소 복잡하고, 명확한 규칙이 존재하지 않습니다.
느슨한 동치 연산자 '=='의 느슨함을 보여주는 예시가 아래에 있습니다.
아래 테스트 코드들의 주석을 제거해도 이 테스트는 통과합니다.
*/

// 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;
});

/*
사실 느슨한 동치 연산(loose equality)는 프로그램의 작동을 예측하기 다소 어렵게 만듭니다.
'=='의 특성을 정확하게 외워서 모든 상황에 대응하겠다는 자세는 접어두시기 바랍니다.
매우 비효율적일뿐더러 일반적인 컴퓨터 프로그래밍 언어의 관습(convention)과도 거리가 먼 자세입니다.
지금부터는 엄격한 동치 연산(strict equality) '==='을 사용하시기 바랍니다.
*/

it("비교연산자 '==='는 두 값의 일치 여부를 엄격하게 검사(strict equality)합니다.", function () {
let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue === expectedValue).to.be.true;
// 이제 'FILL_ME_IN'을 대신할 수 있는 건 number 타입의 2뿐입니다.
// 문자열 '2'는 테스트를 통과하지 못합니다.
});

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');
});

/*
지금까지 본 것처럼 자바스크립트에는 다소 이해하기 힘든 부분들이 존재합니다.
아래처럼 자바스크립트의 별난(quirky) 부분들을 따로 모아둔 저장소도 있을 정도입니다.
https://github.com/denysdovhan/wtfjs

여기서도 동치 연산을 학습하면서 당부한 자세가 요구됩니다.
이런 별난 특성들을 전부 외워서 모든 상황에 대응하려고 하지 말고, 올바른 코딩 습관을 기르시기 바랍니다.
대표적으로 최대한 같은 타입끼리 연산을 하고, 즉 엄격한 동치 연산('===')을 사용하고, 조건문에 비교 연산을 명시하는 것이 훨씬 좋습니다.
*/
});
44 changes: 44 additions & 0 deletions 03_LetConst.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
describe("'const'에 대해서 학습합니다.", function () {
it("'const'로 선언된 변수에는 재할당(reassignment)이 금지됩니다.", function () {
// 아래 코드에서 문제가 되는 부분을 삭제합니다.
const constNum = 0;
expect(constNum).to.equal(0);

const constString = 'I am a const';
expect(constString).to.equal('I am a const');
});

it("'const'로 선언된 배열의 경우 새로운 요소를 추가하거나 삭제할 수 있습니다.", function () {
const arr = [];
const toBePushed = 42;
arr.push(toBePushed);
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;
expect(obj.x).to.equal(undefined);

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

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

/*
재할당도 안되는 'const' 키워드를 굳이 써야하는지 이해가 안 될수도 있습니다.
'let' 키워드는 재할당이 가능하기 때문에 여러모로 편하고, 큰 문제도 없어 보이기 때문입니다.
이에 대해서 잠시 고민하신 후, 'const'가 추천되는 이유에 대해 직접 찾아보시기 바랍니다.

동기 부여를 위해 구글 자바스크립트 코딩 스타일 가이드를 소개해 드립니다.
세계의 탑코더들이 있는 구글 스타일 가이드는 선언 키워드에 대해서 어떻게 안내하고 있을까요?
https://google.github.io/styleguide/jsguide.html#features-use-const-and-let
*/
});
136 changes: 136 additions & 0 deletions 04_Scope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
describe('scope 대해서 학습합니다.', function () {
// scope는 변수의 값(변수에 담긴 값)을 찾을 때 확인하는 곳을 말합니다. 반드시 기억하시기 바랍니다.
it('함수 선언식(declaration)과 함수 표현식(expression)의 차이를 확인합니다.', function () {
let funcExpressed = 'to be a function';

expect(typeof funcDeclared).to.equal("function");
expect(typeof funcExpressed).to.equal("string");

function funcDeclared() {
return 'this is a function declaration';
}

funcExpressed = function () {
return 'this is a function expression';
};

// 자바스크립트 함수 호이스팅(hoisting)에 대해서 검색해 봅니다.

const funcContainer = { func: funcExpressed };
expect(funcContainer.func()).to.equal('this is a function expression');

funcContainer.func = funcDeclared;
expect(funcContainer.func()).to.equal('this is a function declaration');
});

it('lexical scope에 대해서 확인합니다.', function () {
let message = 'Outer';

function getMessage() {
return message;
}

function shadowGlobal() {
let message = 'Inner';
return message;
}

function shadowGlobal2(message) {
return message;
}

function shadowParameter(message) {
message = 'Do not use parameters like this!';
return message;
}

expect(getMessage()).to.equal('Outer');
expect(shadowGlobal()).to.equal('Inner');
expect(shadowGlobal2('Parameter')).to.equal('Parameter');
expect(shadowParameter('Parameter')).to.equal('Do not use parameters like this!');
expect(message).to.equal('Outer');
});

it('default parameter에 대해 확인합니다.', function () {
function defaultParameter(num = 5) {
return num;
}

expect(defaultParameter()).to.equal(5);
expect(defaultParameter(10)).to.equal(10);

function pushNum(num, arr = []) {
arr.push(num);
return arr;
}

expect(pushNum(10)).to.deep.equal([10]);
expect(pushNum(20)).to.deep.equal([20]);
expect(pushNum(4, [1, 2, 3])).to.deep.equal([1,2,3,4]);
});

it('클로저(closure)에 대해 확인합니다.', function () {
function increaseBy(increaseByAmount) {
return function (numberToIncrease) {
return numberToIncrease + increaseByAmount;
};
}

const increaseBy3 = increaseBy(3);
const increaseBy5 = increaseBy(5);

expect(increaseBy3(10)).to.equal(13);
expect(increaseBy5(10)).to.equal(15);
expect(increaseBy(8)(6) + increaseBy(5)(9)).to.equal(28);

/*
mdn에 따르면 클로저의 정의는 다음과 같습니다. 반드시 기억하시기 바랍니다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.

클로저는 함수와 함수가 선언된 어휘적 환경의 조합을 말한다.
이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.

여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"입니다.
특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 합니다.
유어클레스 영상에서 언급되는 "외부함수의 변수에 접근할 수 있는 내부함수"를 클로져 함수로 부르는 이유도 그렇습니다.

클로저는 내부(inner) 함수가 외부(outer) 함수의 지역 변수에 접근할 수 있습니다.
이를 유념하시고 클로저의 유즈 케이스를 검색해 보시기 바랍니다. 아래 검색 키워드를 활용합니다.
function factories
namespacing private variables/functions
*/
});

it('lexical scope와 closure에 대해 다시 확인합니다.', function () {
let age = 27;
let name = 'jin';
let height = 179;

function outerFn() {
let age = 24;
name = 'jimin';
let height = 178;

function innerFn() {
age = 26;
let name = 'suga';
return height;
}

innerFn();

expect(age).to.equal(26);
expect(name).to.equal('jimin');

return innerFn;
}

const innerFn = outerFn();

expect(age).to.equal(27);
expect(name).to.equal('jimin');
expect(innerFn()).to.equal(178);
});
});
52 changes: 52 additions & 0 deletions 05_ArrowFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe('화살표 함수에 관해서', 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')('javascript')).to.eql('<div>javascript</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>')
})
})
Loading