<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>박스여우 - BoxFox</title>
    <link>https://boxfoxs.tistory.com/</link>
    <description>박스여우의 프로그래밍을 주제로한 블로그 입니다.
Programming, Java, C, C#, Algorithm, Android, IOS, JavaScript</description>
    <language>ko</language>
    <pubDate>Tue, 18 Aug 2026 14:34:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>박스여우</managingEditor>
    <image>
      <title>박스여우 - BoxFox</title>
      <url>https://t1.daumcdn.net/cfile/tistory/21661E4F57941D5C2A</url>
      <link>https://boxfoxs.tistory.com</link>
    </image>
    <item>
      <title>Typescript 3.8 변경사항</title>
      <link>https://boxfoxs.tistory.com/435</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Type-Only Imports and Exports&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 기능은 대부분의 경우 필요하지 않은 기능이지만, 만약 &lt;span style=&quot;color: #ee2323;&quot;&gt;--isolatedModules&lt;/span&gt; 옵션이나 타입스크립트, &lt;span style=&quot;color: #000000;&quot;&gt;babel&lt;/span&gt;의 &lt;span style=&quot;color: #ee2323;&quot;&gt;transpileModule&lt;/span&gt; API를 사용해 컴파일 할 때 이슈가 발생한다면, 이 기능이 필요할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Typescript는 타입을 참조하기 위해 JavaScript의 import 문법을 재사용 사용합니다. 예를 들어, 아래의 예제에서 우리는 순수 TypeScript 타입인 &lt;span style=&quot;color: #ee2323;&quot;&gt;Options&lt;/span&gt;과 함께&amp;nbsp;JavaScript 변수인 &lt;span style=&quot;color: #ee2323;&quot;&gt;doThing&lt;/span&gt;을 import 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592103853930&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./foo.ts
interface Options {
    // ...
}

export function doThing(options: Options) {
    // ...
}

// ./bar.ts
import { doThing, Options } from &quot;./foo.js&quot;;

function doThingBetter(options: Options) {
    // do something twice as good
    doThing(options);
    doThing(options);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript는 &lt;span style=&quot;color: #000000;&quot;&gt;import elision 기능을 제공하여&lt;/span&gt; JavaScript로 컴파일 될 때 타입으로 사용되는 것들을 모두 지워버립니다. JavaScript에서는 type이 없기 때문이죠. 위의 예제의 경우 Options는 타입으로써 사용되는 것을 확인하고, 자동적으로 import를 지워버립니다. &lt;span style=&quot;color: #000000;&quot;&gt;따라서 위 예제의 컴파일 결과는 아래와 같습니다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592104174362&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./foo.js
export function doThing(options: Options) {
    // ...
}

// ./bar.js
import { doThing } from &quot;./foo.js&quot;;

function doThingBetter(options: Options) {
    // do something twice as good
    doThing(options);
    doThing(options);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 기능은 대부분의 경우 훌륭하게 사용되지만 몇몇의 경우 문제가 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 먼저, export 되는 것이 변수인지 타입인지 모호한 경우가 몇가지 있습니다. 아래 예시의 경우 &lt;span style=&quot;color: #ee2323;&quot;&gt;MyThing&lt;/span&gt;이 변수인지 타입인지 알 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592104414534&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { MyThing } from &quot;./some-module.js&quot;;

export { MyThing };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우리가 위 예제에서 export 된&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;MyThing&lt;/span&gt;이 변수로 사용되는지, 타입으로 사용되는지 알 수 있는 방법이 없습니다. Babel과 TypeScript의&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;transpileModule&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;API 도 마찬가지로 &lt;span style=&quot;color: #ee2323;&quot;&gt;MyThing&lt;/span&gt;이 타입일 경우 제대로 동작하지 않는 코드를 만들어 낼 것 입니다. 그리고 TypeScript의 &lt;span style=&quot;color: #ee2323;&quot;&gt;isolatedModules&lt;/span&gt; 플래그는 이것이 문제가 될 것이라고 알려줄 것 입니다. 진짜 문제는 &quot;아니야, 이건 타입으로만 사용될 거니까 지워져야해&quot; 라고 알려줄 방법이 없다는 것 입니다. 따라서 import elision 기능만으로는 부족한 부분이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또 다른 이슈는 TypeScript의 import elision 기능은 타입을 import 하는 구문이라면 모두 제거해버립니다. 이는 부수적인 효과를 가지고 있는 모듈을 import 할 때 문제가 발생할 수 있습니다. 따라서 유저들은 원하는 동작을 위해 두 번째 import 선언을 추가해야 합니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592105526049&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// This statement will get erased because of import elision.
import { SomeTypeFoo, SomeOtherTypeBar } from &quot;./module-with-side-effects&quot;;

// This statement always sticks around.
import &quot;./module-with-side-effects&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구체적인 예시로는 Angular.js 같은 프레임워크에서 찾아볼 수 있습니다. Angular.js에서 &lt;a href=&quot;https://docs.angularjs.org/guide/services&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;services&lt;/a&gt; 는 전역적으로 등록되고, 사용하는 쪽에서 등록된 서비스를 주입받게 됩니다. 사용하는 쪽에서 이 서비스들은 타입으로 사용되기 위해 import 합니다. 결과적으로 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;./service.js 는 절대로 실행되지 않고 런타임에 문제가 발생합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592106850063&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./service.ts
export class Service {
    // ...
}
register(&quot;globalServiceId&quot;, Service);

// ./consumer.ts
import { Service } from &quot;./service.js&quot;;

inject(&quot;globalServiceId&quot;, function (service: Service) {
    // do stuff with Service
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 이슈가 발생하는 것들을 해결하기 위해 &lt;span style=&quot;color: #000000;&quot;&gt;어떤 것들이 import 되고 지워져야 하는지 &lt;/span&gt;유저들이 세밀한 조작을 할 수 있는 문법이 추가되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript 3.8에서는 이에 대한&amp;nbsp;&lt;/span&gt;해결책으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;type-only imports and exports&lt;/span&gt; 문법이 추가되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1590303283165&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { SomeThing } from &quot;./some-module.js&quot;;

export type { SomeThing };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;import type&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;은 타입 type annoations과 declarations 을 위해 사용되는 정의만 import 하게 됩니다. 이는 항상 지워지고, 런타임에 남은 잔재물이 없습니다.&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;export type&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;도 비슷하게 타입을 추론할 때에만 사용되는 export만 제공합니다. 그리고 이 또한 컴파일할 때 지워집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이는 기억해야할 중요한 점입니다. 클래스는 런타임에는 변수를 가지고 design-time에는 타입을 가집니다. 그리고 이를 상황에 맞게 사용합니다.&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;만약 클래스를 import 할 때&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;import type&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;을 사용한다면, 상속과 같은 것들을 할 수 없습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591292678111&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { Component } from &quot;react&quot;;

interface ButtonProps {
    // ...
}

class Button extends Component&amp;lt;ButtonProps&amp;gt; {
    //               ~~~~~~~~~
    // error! 'Component' only refers to a type, but is being used as a value here.

    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 Flow를 사용해본 경험이 있다면, 이 문법은 상당히 비슷하게 느껴질 것입니다. 한 가지 다른 차이점은 모호하게 만들 수 있는 코드를 피하기 위해 몇가지 제한 사항을 추가했다는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591292861322&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Is only 'Foo' a type? Or every declaration in the import?
// We just give an error because it's not clear.

import type Foo, { Bar, Baz } from &quot;some-module&quot;;
//     ~~~~~~~~~~~~~~~~~~~~~~
// error! A type-only import can specify a default import or named bindings, but not both.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;런타임에 사용되지 않는 imports 를 제어하기 위한 &lt;span style=&quot;color: #000000;&quot;&gt;새로운 컴파일러 &lt;span style=&quot;color: #ee2323;&quot;&gt;importsNotUsedAsValues &lt;/span&gt;flag도 추가되었습니다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 플래그는 3개의 설정 값 중 하나를&amp;nbsp;사용할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;remove&lt;/span&gt; : 기존 컴파일러의 동작과 동일하게 런타임에 사용되지 않는 imports 들을 제거합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;preserve&lt;/span&gt; : 타입 정보는 제거 되지만, 해당 모듈의 imports 는 유지합니다. 따라서 위의 imports/side-effects 문제상황을 해결할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;error&lt;/span&gt; : 이는 preserve 옵션과 동일하게 동작하지만 타입으로써만 import를 사용했을 때에 오류를 발생시킵니다. 만약 변수가 아닌 것들이 우연하게 import 되는 것을 보호하고 싶을 때 유용할 것 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 기능에 대한 더 많은 정보를 위해서 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/35200&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pull request&lt;/a&gt; 와 연관된 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/36092/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;변경사항&lt;/a&gt; 등을 살펴볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ECMAScript Private Fields&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript 3.8은 ECMAScript의 private fields 를 지원합니다. (&lt;a href=&quot;https://github.com/tc39/proposal-class-fields/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;state-3 class fields proposal&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592110807986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
    #name: string

    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person(&quot;Jeremy Bearimy&quot;);

jeremy.#name
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;private fields 는 몇가지 규칙이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Private field의 이름은 &lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt; 으로 시작해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모든 private field name은 해당 private field를 가지고 있는 class에 고유하게 범위가 지정됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;public, private 같은 TypeScript의 접근 제한자는 private fields에 사용할 수 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;private fields 해당 클래스의 외부에서 접근되거나, 감지조차 될 수 없습니다. -- JS 유저들이라 하더라도! 이를 hard privacy 라고 부릅니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;private field가 주는 이점은 &lt;span style=&quot;color: #000000;&quot;&gt;hard privacy&lt;span&gt; 외에도, 해당 private field 를 가지고 있는 class에 고유하게 범위가 지정된다는 점 입니다.&lt;/span&gt;&lt;/span&gt;&amp;nbsp;예시로 일반적인 프로퍼티 선언은 자식 클래스에서 덮어씌워지기 쉽습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592111333523&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    foo = 10;

    cHelper() {
        return this.foo;
    }
}

class D extends C {
    foo = 20;

    dHelper() {
        return this.foo;
    }
}

let instance = new D();
// 'this.foo' refers to the same property on each instance.
console.log(instance.cHelper()); // prints '20'
console.log(instance.dHelper()); // prints '20'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 private fields를 사용한다면 이에 대해 걱정할 필요가 없어집니다. 각 private field의 이름은 해당하는 클래스에서 고유하게 유지됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592111390076&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    #foo = 10;

    cHelper() {
        return this.#foo;
    }
}

class D extends C {
    #foo = 20;

    dHelper() {
        return this.#foo;
    }
}

let instance = new D();
// 'this.#foo' refers to a different field within each class.
console.log(instance.cHelper()); // prints '10'
console.log(instance.dHelper()); // prints '20'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주의 깊게 봐야할 또 다른 점은 다른 타입의 private field에 접근한다면 &lt;span style=&quot;color: #ee2323;&quot;&gt;TypeError&lt;/span&gt;가 발생합니다. (private field 가 있는 타입끼리는 덕타이핑이 안된다)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592111536965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Square {
    #sideLength: number;

    constructor(sideLength: number) {
        this.#sideLength = sideLength;
    }

    equals(other: any) {
        return this.#sideLength === other.#sideLength;
    }
}

const a = new Square(100);
const b = { sideLength: 100 };

// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막으로, 순수 .js 파일에서 사용할 경우 private fields는 항상 할당 되기 전에 선언되어야 합니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592111668972&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    // No declaration for '#foo'
    // :(

    constructor(foo: number) {
        // SyntaxError!
        // '#foo' needs to be declared before writing to it.
        this.#foo = foo;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JavaScript는 항상 선언되지 않은 프로퍼티에 접근할 수 있도록 해주었고, TypeScript는 항상 속성을 정의해야 했습니다. 하지만 private fields를 사용한다면 JavaScript, TypeScript 상관 없이 항상 선언이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592111825237&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    /** @type {number} */
    #foo;

    constructor(foo: number) {
        // This works.
        this.#foo = foo;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;더 많은 정보를 위해 &lt;a href=&quot;https://github.com/Microsoft/TypeScript/pull/30829&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pull request&lt;/a&gt; 를 살펴볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그럼 private 키워드와 hash(#)중 무엇을 사용해야 하나?&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상황에 따라 적절하게 골라서 사용해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript의 &lt;span style=&quot;color: #ee2323;&quot;&gt;private&lt;/span&gt; 접근자는 항상 지워집니다. 따라서 런타임에는 일반적인 property 과 동일하게 행동하며, 이 프로퍼티가 private 접근자로 선언되었다고 알려줄 방법이 없습니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;private&lt;/span&gt; 키워드를 사용한다면 privacy는 compile-time/design-time에서만 적용됩니다. 컴파일 이후에는 JavaScript 사용자의 의도에 맡기게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592112189896&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    private foo = 10;
}

// This is an error at compile time,
// but when TypeScript outputs .js files,
// it'll run fine and print '10'.
console.log(new C().foo);    // prints '10'
//                  ~~~
// error! Property 'foo' is private and only accessible within class 'C'.

// TypeScript allows this at compile-time
// as a &quot;work-around&quot; to avoid the error.
console.log(new C()[&quot;foo&quot;]); // prints '10'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 &quot;soft privacy&quot; 는 일시적으로 특정 API에 접근 권한이 없는 유저들에게 기능을 제공하거나, 모든 런타임에서 동작할 수 있도록 할 때 유용합니다.&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반면에, ECMAScript의 hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;) 는 완벽하게 클래스 외부에서 해당 프로퍼티에 접근할 수 없도록 만들어줍니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592112429156&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C {
    #foo = 10;
}

console.log(new C().#foo); // SyntaxError
//                  ~~~~
// TypeScript reports an error *and*
// this won't work at runtime!

console.log(new C()[&quot;#foo&quot;]); // prints undefined
//          ~~~~~~~~~~~~~~~
// TypeScript reports an error under 'noImplicitAny',
// and this prints 'undefined'.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 Hard privacy는 아무도 내부에 어떠한 것도 가져갈 수 없다는 것을 강력하게 보장할 때 유용합니다. 만약 당신이 라이브러리의 개발자라면 private field를 지우거나 이름을 바꾸는 것이 사용자들에게는 어떠한 충돌도 발생시키지 않는 다는 것 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위에서 언급했듯이, 또 다른 이점은 hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;)를 사용함으로써 서브클래싱이 더 쉬워질 수 있습니다. hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;)를 사용한 private fields들은 하위 클래스에서 이름이 충돌할 걱정을 하지 않아도 됩니다. TypeScript의 &lt;span style=&quot;color: #ee2323;&quot;&gt;private&lt;/span&gt; 제한자로 선언되었다면, 하위 클래스를 선언할 때에 여전히 상위 클래스의 프로퍼티를 재정의하진 않았는지 주의해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한가지 더 생각해봐야 할 점은 어디에서 당신의 코드가 실행되는지 입니다. TypeScript는 ECMAScript 2015(ES6) 또는 그 이상을 타겟으로 하지 않으면 hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;) private 기능을 지원할 수 없습니다. 이는 hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;)을 사용할 때 ECMAScript 내부적으로 ES6이상 부터 추가된&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;WeakMap&lt;/span&gt;을 &lt;span style=&quot;color: #000000;&quot;&gt;사용하여&lt;/span&gt; private field를 보호하기 때문입니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;WeakMap&lt;/span&gt;은 &lt;span style=&quot;color: #000000;&quot;&gt;하위 버전(ES6 이하)에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;메모리 릭을 발생시키지 않으면서 대체(polyfilled)할 방법이 없습니다. 반면에 TypeScript의 private 키워드 선언 프로퍼티는 모든 타겟에서 동작할 수 있습니다. ECMAScript 3일지라도!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막 고려사항은 속도에 관한 문제입니다: TypeScript의 &lt;span style=&quot;color: #ee2323;&quot;&gt;private&lt;/span&gt; 제한자로 정의된 프로퍼티는 다른 일반적인 프로퍼티와 다르지 않습니다. 따라서 이에 접근할 때 다른 프로퍼티에 접근 할 때와 같이 빠릅니다. 대조적으로 hash(&lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt;) private field는 내부적으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;WeakMap&lt;/span&gt;을 사용하기 때문에 사용할 때 비교적 느립니다. 몇몇 런타임에서는 &lt;span style=&quot;color: #ee2323;&quot;&gt;#&lt;/span&gt; private field의 구현을 최적화 시켰고, &lt;span style=&quot;color: #ee2323;&quot;&gt;WeekMap&lt;/span&gt; 구현체보다는 빠를 수 있지만 모든 런타임에 해당하는 이야기는 아닙니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;export * as ns&lt;/span&gt; 문법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 모듈의 모든 멤버를 하나의 멤버처럼 만들고 export 할 때 종종 아래와 같은 코드를 작성합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592114507425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import * as utilities from &quot;./utilities.js&quot;;
export { utilities };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 패턴을 지원하기 위해 최근 ECMAScript 2020 에 새로운 문법이 추가되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592114586490&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export * as utilities from &quot;./utilities.js&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript 3.8에서도 해당 문법이 추가되었습니다. 만약 당신의 모듈의 컴파일 target이 &lt;span style=&quot;color: #ee2323;&quot;&gt;es2020&lt;/span&gt;보다 하위 버전이라면, 타입스크립트 컴파일 결과로는 첫 번째 예시와 같이 출력됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가적인 정보는 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/34903&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pull request&lt;/a&gt; 에서 확인해볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Top-Level &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대부분의 현대 &lt;span style=&quot;color: #000000;&quot;&gt;JavaScript&lt;/span&gt;환경은 I/O와 같은 작업을 비동기방식으로 제공합니다. 이러한 API들은 이러한 비동기 작업들을 &lt;span style=&quot;color: #ee2323;&quot;&gt;Promise&lt;/span&gt;로 반환합니다. 작업들을 non-blocking으로 만듬으로써 많은 장점을 얻을 수 있기 때문입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592114989968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch(&quot;...&quot;)
    .then(response =&amp;gt; response.text())
    .then(greeting =&amp;gt; { console.log(greeting) });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JavaScript 개발자들은 &lt;span style=&quot;color: #ee2323;&quot;&gt;Promise&lt;/span&gt;s 를 사용할 때 &lt;span style=&quot;color: #ee2323;&quot;&gt;.then&lt;/span&gt; 체인을 기피하기 위해 &lt;span style=&quot;color: #ee2323;&quot;&gt;async&lt;/span&gt; 함수 안에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt; 을 사용하도록 정의합니다. 그리고 그 함수를 즉시 호출합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592115181649&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function main() {
    const response = await fetch(&quot;...&quot;);
    const greeting = await response.text();
    console.log(greeting);
}

main()
    .catch(e =&amp;gt; console.error(e))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 TypeScript 3.8 부터는&amp;nbsp;&lt;/span&gt;async&lt;/span&gt; 함수로 감싸는 것도 필요하지 않습니다. ECMAScript에 새롭게 추가되는 편리한 기능인 &quot;top-level &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt;&quot; 를 사용할 수 있기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이전의 JavaScript에서는 (비슷한 기능을 가진 대부분의 다른 언어들도 마찬가지로), &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt;은 오직 &lt;span style=&quot;color: #ee2323;&quot;&gt;async&lt;/span&gt; 함수 내부에서만 사용될 수 있었습니다. 하지만 top-level &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt;은 모듈의 top level 에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;await&lt;/span&gt;를 사용 할 수 있게 해 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592115396637&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const response = await fetch(&quot;...&quot;);
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;top-level await 은 오직 모듈의 top level에서만 동작합니다. 그리고 파일들은 오직 import, export 를 통해 발견되었을 때에만 모듈로 간주됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Top level await 는 모든 환경에서 동작하지 않습니다. 현재로써는 compile &lt;span style=&quot;color: #ee2323;&quot;&gt;target&lt;/span&gt;을 &lt;span style=&quot;color: #ee2323;&quot;&gt;es2017&lt;/span&gt; 혹은 그 이상으로, &lt;span style=&quot;color: #ee2323;&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;esnext&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;혹은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;으로 &lt;/span&gt;지정 해야 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;원본 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/35813&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pull request&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JSDoc Property Modifiers&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript 3.8은 &lt;span style=&quot;color: #ee2323;&quot;&gt;allowJs&lt;/span&gt; flag를 사용함으로써 JavaScript 파일을 지원합니다. 또한 &lt;span style=&quot;color: #ee2323;&quot;&gt;checkJs&lt;/span&gt; 옵션 이나 &lt;span style=&quot;color: #ee2323;&quot;&gt;//@ts-check&lt;/span&gt; 주석을 통해 JavaScript 파일의 타입 체킹을 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JavaScript 파일은 타입 체킹을 위한 전용 문법을 가지고 있지 않기 때문에 &lt;span style=&quot;color: #000000;&quot;&gt;TypeScript는 JSDoc을 활용합니다. TypeScript 3.8은 프로퍼티를 위해 사용되는 몇가지 JSDoc의 태그를 이해합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째는 접근 제한자 입니다: &lt;span style=&quot;color: #ee2323;&quot;&gt;@public&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;@private&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;@protected&lt;/span&gt; 이 태그들은 각각 TypeScript에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;public&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;private&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;protected&lt;/span&gt; 접근 제한자와 동일하게 동작합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592117274663&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @ts-check

class Foo {
    constructor() {
        /** @private */
        this.stuff = 100;
    }

    printStuff() {
        console.log(this.stuff);
    }
}

new Foo().stuff;
//        ~~~~~
// error! Property 'stuff' is private and only accessible within class 'Foo'.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다음으로, @readonly 제한자도 추가되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592117543533&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @ts-check

class Foo {
    constructor() {
        /** @readonly */
        this.stuff = 100;
    }

    writeToStuff() {
        this.stuff = 200;
        //   ~~~~~
        // Cannot assign to 'stuff' because it is a read-only property.
    }
}

new Foo().stuff++;
//        ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 외에 추가적인 변경사항은 아래에 원문을 참조하시면 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;원문&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1582456659743&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing TypeScript 3.8 | TypeScript&quot; data-og-description=&quot;Today we&amp;rsquo;re proud to release TypeScript 3.8! For those unfamiliar with TypeScript, it&amp;rsquo;s a language that adds syntax for types on top of JavaScript which can be analyzed through a process called static type-checking. This type-checking can tell us about err&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/&quot; data-og-url=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BpIWu/hyE2CrKpcH/FAsx20GjjKc9I8UB6CYZnk/img.png?width=562&amp;amp;height=350&amp;amp;face=0_0_562_350,https://scrap.kakaocdn.net/dn/utVl5/hyE2CrKpbD/rxYrGbgaF5ZLCPA2eRcyQK/img.png?width=562&amp;amp;height=350&amp;amp;face=0_0_562_350,https://scrap.kakaocdn.net/dn/z1XV0/hyE2H0Vazw/e0n0mb5eyYCkhju2RIGW31/img.gif?width=1244&amp;amp;height=996&amp;amp;face=0_0_1244_996&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BpIWu/hyE2CrKpcH/FAsx20GjjKc9I8UB6CYZnk/img.png?width=562&amp;amp;height=350&amp;amp;face=0_0_562_350,https://scrap.kakaocdn.net/dn/utVl5/hyE2CrKpbD/rxYrGbgaF5ZLCPA2eRcyQK/img.png?width=562&amp;amp;height=350&amp;amp;face=0_0_562_350,https://scrap.kakaocdn.net/dn/z1XV0/hyE2H0Vazw/e0n0mb5eyYCkhju2RIGW31/img.gif?width=1244&amp;amp;height=996&amp;amp;face=0_0_1244_996');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Announcing TypeScript 3.8 | TypeScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Today we&amp;rsquo;re proud to release TypeScript 3.8! For those unfamiliar with TypeScript, it&amp;rsquo;s a language that adds syntax for types on top of JavaScript which can be analyzed through a process called static type-checking. This type-checking can tell us about err&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/435</guid>
      <comments>https://boxfoxs.tistory.com/435#entry435comment</comments>
      <pubDate>Sun, 14 Jun 2020 16:35:42 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript - Functor, 이미 당신은 사용하고 있다</title>
      <link>https://boxfoxs.tistory.com/434</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHgHE/btqBvtlCdHd/dSCONDp85aGkC73mZTmP30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHgHE/btqBvtlCdHd/dSCONDp85aGkC73mZTmP30/img.png&quot; data-alt=&quot;해당 포스트는 Functors in JavaScript 를 번역하고 각색하여 작성되었습니다. 원문 링크는 본문 하단에 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHgHE/btqBvtlCdHd/dSCONDp85aGkC73mZTmP30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHgHE%2FbtqBvtlCdHd%2FdSCONDp85aGkC73mZTmP30%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당 포스트는 Functors in JavaScript 를 번역하고 각색하여 작성되었습니다. 원문 링크는 본문 하단에 있습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;프로그래밍에서는 본질적인 데이터의 복잡성 때문에 논리적 정확성이 불가능할 때가 있습니다. 데이터를 추상화 시키는 것은 데이터의 단순한 표현을 만드는 데 도움이 되는 매우 유용한 도구입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 위해 '&lt;span style=&quot;color: #333333;&quot;&gt;Container&lt;/span&gt;' 를 만드는 방법이 있습니다. '&lt;span style=&quot;color: #333333;&quot;&gt;Container&lt;/span&gt;'는 &lt;span style=&quot;color: #333333;&quot;&gt;오직 우리의 데이터만 가지고 있으며, 그 외에 다른 책임을 가지지 않습니다.&lt;/span&gt; 즉, &lt;span style=&quot;color: #333333;&quot;&gt;OOP 처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;'&lt;span style=&quot;color: #333333;&quot;&gt;Container&lt;/span&gt;' 에 프로퍼티나 메소드를 제공하지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;변수를 가져오고 Container 안으로 넣습니다. 그리고 Container 는 함수형 로직을 통과시키는 동안 변수를 안전하게 지키며 필요할 때 즉시적으로 변수를 가져올 수 있습니다. 따라서 Container 는 아래의 두 가지 책임이 있다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스스로 내부에 변수를 저장하고 있다.&lt;/li&gt;
&lt;li&gt;오직 우리가 필요로 할 때 변수를 되돌려 준다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;또한 절대 컨테이너 내부의 값은 변경되지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;함수형 프로그래밍을 할 때 컨테이너는 함수형 구조의 기초에 기여하고 순수 함수형 에러 핸들링과 비동기 액션등 일반적인 기술들을 지원하므로 매우 강력합니다. 하지만&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;이 컨테이너라는 개념은 전혀 새로운 개념이 아닙니다 이미 JavaScript 를 사용하면서 많이 접해 봤을 개념입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbTgTb/btqBuiSEA26/wO42ilsuQp2kgCriba5JPK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbTgTb/btqBuiSEA26/wO42ilsuQp2kgCriba5JPK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbTgTb/btqBuiSEA26/wO42ilsuQp2kgCriba5JPK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bbTgTb/btqBuiSEA26/wO42ilsuQp2kgCriba5JPK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컨테이너에 대해 자세하게 살펴보기 전에, &lt;b&gt;Functor - 펑터라 불리는 특별한 타입의 컨테이너에 대해 간단히 설명하자면 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;'map' 함수와 함께 사용되는 컨테이너&lt;/b&gt; 입니다. 물론 이 말고도 다른 특징이 있지만 아래에서 자세하게 다뤄보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Arrays&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;배열은 프로그래밍에서 항상 사용될 정도로 가장 일반적인 컨테이너입니다. 배열은 모든 데이터 추상화 한것 중 가장 단순하지만 강력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580098208204&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [ 8, 10, 23, 35, 54 ];
const b = a[1]; // 변수를 이렇게 꺼내옵니다.&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1580100221109&quot; class=&quot;javascript&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arr.push(45) ❌
arr[1] = 45 ❌
// 원본 배열을 수정하는 것 대신 아래와 같이 새로운 배열을 만들어 사용하세요.
const arr2 = [ ...arr, 38, 52 ]
const even = filter(x =&amp;gt; x%2 === 0, arr)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;함수형 프로그래밍에서 배열의 값을 변경할 수 있는 메소드를 사용해서는 안됩니다. 값을 가져오기만 하거나 &lt;span style=&quot;color: #333333;&quot;&gt;배열의 수정이 필요하다면 새로운 배열로 만들어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위에서 펑터는 'map' 함수를 사용할 수 있는 컨테이너라고 이야기 했습니다. 하지만 더 자세하게 이야기 하자면 펑터는 단항 함수로 '매핑'할 수 있는 컨테이너입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 '매핑'이란 컨테이너가 담고 있는 모든 데이터에 단항 함수를 적용하고 그 결과 값으로 다른 컨테이너를 반환하는 함수 (ex: fmap, map)로 처리할 수 있음을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BCO7Y/btqBuiSG35k/MKmkh896FuqKdxCNjpeKKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BCO7Y/btqBuiSG35k/MKmkh896FuqKdxCNjpeKKk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BCO7Y/btqBuiSG35k/MKmkh896FuqKdxCNjpeKKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBCO7Y%2FbtqBuiSG35k%2FMKmkh896FuqKdxCNjpeKKk%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;배열의 경우 이 특별한 함수를 'map' 함수라고 부릅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;The Map Function&lt;/h4&gt;
&lt;p&gt;배열의 Map 함수는 배열을 가져오고 특정한 함수를 모든 요소 하나하나에 적용하고 그 결과값으로 새로운 배열을 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580105392861&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1,2,3,4].map(multiplyBy2) 
//=&amp;gt; [2,4,6,8] 또는 map(multiplyBy2, [1,2,3,4]) 
//=&amp;gt; [2,4,6,8] where multiplyBy2 = x =&amp;gt; x * 2 and map = (fn, arr) =&amp;gt; arr.map(fn)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;항상 map 으로부터 또 다른 배열을 가져오기 때문에 map 을 다시 연결 하여 여러 단항 함수를 매핑하는 과정을 수행하는 체인을 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580108713624&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[1,2,3].map(x =&amp;gt; x * 3).map(x =&amp;gt; x * 2).map(x =&amp;gt; x / 6)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Map 함수는 이터레이터 함수 이상의 기능을 제공합니다. 값이 컨테이너 안에 있을 때 함수를 직접 적용 할 수 없으며 값이 변경 될 것으로 예상 할 수 있습니다. 예를 들어&lt;/p&gt;
&lt;pre id=&quot;code_1580108945930&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = [1, 2, 3]
String(a) = &amp;lsquo;[1 ,2, 3]&amp;rsquo;
String(a) != [&amp;lsquo;1&amp;rsquo;, &amp;lsquo;2&amp;rsquo;, &amp;lsquo;3&amp;rsquo;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;map 함수는 함수가 컨테이너 안의 값들에 접근할 수 있도록 해줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1580109052697&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;map(String, [1, 2, 3]) = [&amp;lsquo;1&amp;rsquo;, &amp;lsquo;2&amp;rsquo;, &amp;lsquo;3&amp;rsquo;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한, map 함수는 절대 기존 컨테이너를 변경하지 않고 내부의 값에 따라 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9uKHd/btqBvs79xqv/lAjYJB5NOrdRNCScSnVDxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9uKHd/btqBvs79xqv/lAjYJB5NOrdRNCScSnVDxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9uKHd/btqBvs79xqv/lAjYJB5NOrdRNCScSnVDxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9uKHd%2FbtqBvs79xqv%2FlAjYJB5NOrdRNCScSnVDxK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;map 은 컨테이너의 타입을 변경시키지 않지만 컨테이너가 담고 있는 내용의 타입은 변경할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;컨테이너가 담고 있는 데이터의 타입은 바뀔 수 있습니다. 그리고 그 타입 변경을 map 함수의 정의를 통해 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580109257879&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;map :: (a -&amp;gt; b) -&amp;gt; [a] -&amp;gt; [b] or fmap :: (a -&amp;gt; b) -&amp;gt; F a -&amp;gt; F b &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 a, b 는 같은 타입일 수도 있고 다른 타입일 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 간단하게 살펴보면 map 함수가 a -&amp;gt; b 에서 함수를 가져오고 Fa -&amp;gt; Fb 에서 함수를 반환 하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;여기서 a -&amp;gt; b 는 a를 가져와 b를 반환하는 단항 함수를 나타냅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580109480685&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;multiplyBy2(3) = 6 // is a -&amp;gt; b as 3 -&amp;gt; 6&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 Fa -&amp;gt; Fb 는 a 가 들어있는 컨테이너를 가져오고 b 가 들어있는 컨테이너를 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580109590651&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;multiplyArrBy2([1]) = [2] // is Fa -&amp;gt; Fb as [1] -&amp;gt; [2], F is []&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이에 대한 자세한 내용은 아래 글을 참조해 주세요&lt;/p&gt;
&lt;figure id=&quot;og_1580109634930&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Function Type Signatures in Javascript&quot; data-og-description=&quot;When a Javascript Developer starts to explore the deepest secrets of the Functional Programming, he often finds these weird arrow notations with type written above the functions and feels &amp;lsquo;What the hell is this?&amp;rsquo;. After all, he&amp;rsquo;s a master of dynamic typed &quot; data-og-host=&quot;hackernoon.com&quot; data-og-source-url=&quot;https://hackernoon.com/function-type-signatures-in-javascript-5c698c1e9801&quot; data-og-url=&quot;https://hackernoon.com/function-type-signatures-in-javascript-5c698c1e9801&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjL48z/hyEIZthBWx/z5jGw1Wm1gScDyYjeLQpTK/img.png?width=932&amp;amp;height=646&amp;amp;face=0_0_932_646,https://scrap.kakaocdn.net/dn/caAxeO/hyEIUlda2m/0Fbk32FKGPvkR1cTnxR06K/img.png?width=1150&amp;amp;height=810&amp;amp;face=0_0_1150_810&quot;&gt;&lt;a href=&quot;https://hackernoon.com/function-type-signatures-in-javascript-5c698c1e9801&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hackernoon.com/function-type-signatures-in-javascript-5c698c1e9801&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjL48z/hyEIZthBWx/z5jGw1Wm1gScDyYjeLQpTK/img.png?width=932&amp;amp;height=646&amp;amp;face=0_0_932_646,https://scrap.kakaocdn.net/dn/caAxeO/hyEIUlda2m/0Fbk32FKGPvkR1cTnxR06K/img.png?width=1150&amp;amp;height=810&amp;amp;face=0_0_1150_810');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Function Type Signatures in Javascript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;When a Javascript Developer starts to explore the deepest secrets of the Functional Programming, he often finds these weird arrow notations with type written above the functions and feels &amp;lsquo;What the hell is this?&amp;rsquo;. After all, he&amp;rsquo;s a master of dynamic typed&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;hackernoon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Functions&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;모든 함수는 Functor 이기 때문에 컨테이너 이기도 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;컨테이너는 데이터를 가지고 있지만 함수들은 순수한 로직만 포함하고 있기 때문에 어떻게 함수가 컨테이너가 될 수 있는지 의문이 들 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;잘 생각해보면 함수는 호출되었을 때 변수를 반환합니다. 그래서 어느 방법으로든 변수를 가지고 있어야 합니다. 오직 다른 컨테이너와 다른 차이점은 그 변수들이 동적으로 계산된다는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580111634271&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aFunction(45) // =&amp;gt; 90 So aFunction gives the value 90, when it is passed 45&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수를 무한한 숫자의 배열처럼 생각해봅시다. 그리고 이 무한한 숫자 중 특정한 변수를 원한다면, 특정한 인자와 함께 함수를 호출해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xNDiW/btqBxz0hbT6/SgRLzRds2nw244psJRGZEK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xNDiW/btqBxz0hbT6/SgRLzRds2nw244psJRGZEK/img.jpg&quot; data-alt=&quot;Functions are arrays with Infinite&amp;amp;amp;nbsp;Values&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xNDiW/btqBxz0hbT6/SgRLzRds2nw244psJRGZEK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxNDiW%2FbtqBxz0hbT6%2FSgRLzRds2nw244psJRGZEK%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Functions are arrays with Infinite&amp;nbsp;Values&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;배열이 인덱스가 전달되면 변수를 주는 것처럼 함수도 인자가 전달되었을 때 결과를 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580111858348&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = [ 8, 10, 23, 35, 54 ]
const f = z =&amp;gt; z * 2 a[1] = 10
f(2) = 4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열은 그저 정해진 정수형 인덱스에 대한 결과값만 주지만, 함수는 어떠한 타입의 인자든 제한 없이 받을 수 있습니다. 또한 다른 함수를 인자를 받을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;함수는 무한한 값을 가진 컨테이너 입니다.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;그럼 함수가 Functor 라면 map 함수도 가지고 있나요?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;맞습니다. 함수에 대한 map 을 다음과 같이 정의할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1580112074699&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fnMap = (f, mappingFn) =&amp;gt; (x =&amp;gt; f(mappingFn(x)))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;map 함수가 배열을 가져와 새로운 배열을 만드는 것과 마찬가지로 &lt;span&gt;fnMap은 함수를 가져와서 그 함수의 결과에 다른 함수를 적용하고 두 함수를 결합하는 방식으로 새로운 함수를 반환합니다. 따라서&lt;/span&gt;&lt;span&gt;&amp;nbsp;첫 번째 함수의 결과는 두 번째 함수의 인자가 됩니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이를 사용해보면 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1580116609884&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const multiplyBy6 = fnMap(multiplyBy2, multiplyBy3)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 함수에도 map 함수가 있으며 한 함수를 다른 함수에 매핑하는 경우 두 함수를 융합하는 것입니다. 이를 Compose Function 이라고 부릅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;tl;dr&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;multiplyBy2 함수는 인자로 주어진 값에 2를 곱하여 반환합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;multiplyBy2 로부터 값을 꺼내거나 호출하기 전에 multiplyBy3 으로 매핑했습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;따라서 multiplyBy2 의 결과 값들은 3이 곱해집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이제 multiplyBy2 를 호출 할 때마다. x 가 원본 값이면 x * 3 * 2 를 얻게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1580116991317&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fnMap = (f, mappingFn) =&amp;gt; (x =&amp;gt; f(mappingFn(x)))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수는 데이터 추상화 타입인 배열과 같습니다. 그리고 함수는 데이터를 요구사항에 따라 연산하기만 합니다.&lt;/p&gt;
&lt;p&gt;반대로 배열은 '[]' 를 사용하면 바로 결과를 알려주는 함수와 같다고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 글을 통해 기억해야 할 중요한 점은 함수형 프로그래밍에서 절대로 데이터를 그대로 사용하지 말고 항상 컨테이너로 감싸서 사용해야 한다는 것 입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마치며&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;함수형 프로그래밍의 &lt;b&gt;Functor 는 컨테이너의 값을 매핑하여 또 다른 컨테이너를 만들어 낼 수 있는 특별한 컨테이너&lt;/b&gt;라는 사실을 알게 되었습니다. 사실 기존까지 매우 어렵거나 복잡한 개념이라고 막연한 두려움을 가지고 있었지만, 알고보니 이미 많이 사용되고 있던 개념이여서 싱겁게 느껴지기 까지 하는 것 같습니다.&lt;br /&gt;요즘 영어로 된 문서를 번역하는 재미에 빠져 있어서 자꾸 번역 포스트만 작성하게 되는 것 같습니다. (영여공부도 하고 프로그래밍 공부도 하고 1석 2조..) 조만간 개인적인 이야기도 들고 오겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;원문&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1580119621202&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Functors in JavaScript&quot; data-og-description=&quot;where F is a&amp;nbsp;Functor &amp;ldquo;A mathematician, like a painter or a poet, is a maker of patterns. If his patterns are more permanent than theirs, it is because they are made with ideas.&amp;rdquo; &amp;mdash; G.H. Hardy, A Mathematician&amp;rsquo;s Apology Sometimes, logical accuracy is not pos&quot; data-og-host=&quot;hackernoon.com&quot; data-og-source-url=&quot;https://hackernoon.com/functors-in-javascript-20a647b8f39f&quot; data-og-url=&quot;https://hackernoon.com/functors-in-javascript-20a647b8f39f&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b0TzKB/hyEIXoNPwM/dWnAXIRBSKaenwXnPthfrK/img.png?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768,https://scrap.kakaocdn.net/dn/bvAg9S/hyEI8RqbC6/pKjAEkSUKNHR3MYiFYhdSk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=75_55_226_220&quot;&gt;&lt;a href=&quot;https://hackernoon.com/functors-in-javascript-20a647b8f39f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hackernoon.com/functors-in-javascript-20a647b8f39f&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b0TzKB/hyEIXoNPwM/dWnAXIRBSKaenwXnPthfrK/img.png?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768,https://scrap.kakaocdn.net/dn/bvAg9S/hyEI8RqbC6/pKjAEkSUKNHR3MYiFYhdSk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=75_55_226_220');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Functors in JavaScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;where F is a&amp;nbsp;Functor &amp;ldquo;A mathematician, like a painter or a poet, is a maker of patterns. If his patterns are more permanent than theirs, it is because they are made with ideas.&amp;rdquo; &amp;mdash; G.H. Hardy, A Mathematician&amp;rsquo;s Apology Sometimes, logical accuracy is not pos&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;hackernoon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/434</guid>
      <comments>https://boxfoxs.tistory.com/434#entry434comment</comments>
      <pubDate>Mon, 27 Jan 2020 19:17:00 +0900</pubDate>
    </item>
    <item>
      <title>명령형 프로그래밍 VS 선언형 프로그래밍</title>
      <link>https://boxfoxs.tistory.com/430</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VFObp/btqBkp4wu4X/54aJwbglAkSDkkAwmtkjR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VFObp/btqBkp4wu4X/54aJwbglAkSDkkAwmtkjR0/img.jpg&quot; data-alt=&quot;해당 포스팅은 Imperative vs Declarative Programming 원문을 번역하고 각색하여 작성된 포스팅입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VFObp/btqBkp4wu4X/54aJwbglAkSDkkAwmtkjR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVFObp%2FbtqBkp4wu4X%2F54aJwbglAkSDkkAwmtkjR0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당 포스팅은 Imperative vs Declarative Programming 원문을 번역하고 각색하여 작성된 포스팅입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;명령형 프로그래밍과 선언형 프로그래밍에 대한 비교를 어디선가 한 번쯤은 접해봤을 거라 생각합니다. 그리고 그 둘이 실제로 무엇을 의미하는지 검색을 해보셨다면 아마 아래와 같은 정의를 마주했을 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center; font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;명령형 프로그래밍은 무엇을 &lt;u&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;어떻게&lt;/span&gt;&lt;/u&gt; 할 것인가에 가깝고, 선언형 프로그래밍은 &lt;u&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;무엇을&lt;/span&gt;&lt;/u&gt; 할 것인가와 가깝다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;이 정의를 처음 보면 무슨 말을 하는지 이해하기 어렵게 느껴집니다. 하지만 이 정의에는 명령형 프로그래밍과 선언형 프로그래밍의 핵심이 담겨 있습니다. 먼저, 이 정의를 쉽게 이해하기 위해 프로그래밍의 맥락에서 벗어나서 실세계의 예제를 살펴보겠습니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Red Lobster&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;나는 회사에서 열심히 일하고 퇴근을 한 뒤 Red Lobster에서 가족과 즐거운 외식을 하기로 결정했습니다. 그리고 Red Lobster에 도착한 뒤 안내 데스크에 다가가서 말합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;명령형 방식 (HOW) : &quot;12번 테이블 자리가 비어있습니다. 나와 우리 가족은 저 자리로 걸어가 앉을 것입니다.&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;선언형 방식 (WHAT) : &quot;네 명 앉을자리를 부탁해요&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;위의 예시를 보면 알겠지만 명령형 방식은 내가 실제로 자리에 &quot;&lt;b&gt;어떻게&lt;/b&gt;&quot; 자리에 앉을지 에 관심이 있습니다. 따라서 &quot;&lt;b&gt;어떻게&lt;/b&gt;&quot; 자리에 앉을지에 대한 단계를 하나하나 나열해야 합니다. 그와 반대로 선언형 방식은 내가 &quot;&lt;b&gt;무엇을&lt;/b&gt;&quot; 원하는지에 더 집중되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. 집에 돌아가는 법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;또 다른 예시를 들어보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;아래 질문을 보고 명령형 프로그래밍 방식과 선언형 프로그래밍 방식으로 해결했을 때에 대한 답을 모두 생각해 보았으면 합니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&quot;나는 지금 e마트 바로 옆에 있습니다. 그럼 여기서부터 집까지 어떻게 가야 할까요?&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;명령형 방식 (HOW) &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;: 주차장 북쪽 출구를 나와 왼쪽으로 가세요. 12번가 출구에 도착할 때까지 15번 북쪽 도로를 타세요. 이케아를 끼고 우회전하세요. 직진하여 첫 번째 신호등에서 우회전 하세요. 다음 신호등을 지나 좌회전을 하세요. 우리 집은 #298입니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;선언형 방식 (WHAT)&amp;nbsp;&amp;nbsp;: 내 주소는 98 West Immutable Alley, Eden, Utah 84310입니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;3. 수동 스틱과 오토 스틱 자동차&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른 간단한 예시로는 자동차로 설명할 수 있을 것 같습니다.&lt;br /&gt;수동 스틱(1종) 자동차는 명령형 방식이라고 볼 수 있습니다. 그와 반대로 오토 스틱(2종) 자동차는 선언형 방식이라고 볼 수 있습니다. 만약 내가 차를 운전해야 한다면 수동 스틱(명령형) 자동차와 오토 스틱(선언형) 자동차 중 무엇을 운전할 것인가요?&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;위 예시들을 통해 &lt;span style=&quot;color: #000000;&quot;&gt;&quot;그럼 자리에 앉는 방법은 누가 알고 있지?&quot;, &quot;주소만 알고 있다고 해서 집에 어떻게 돌아가지?&quot;라는 질문이 생길 수 있을 것 같습니다. 사실 &lt;b&gt;선언형 방식이 제대로 동작하기 위해서는 명령형으로 &quot;어떻게&quot;가 구현된 것들이 추상화되어 있어야 합니다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;위에서 살펴본 선언형 예시들에서도 명령형 방식이 추상화된 부분을 찾아볼 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;Red Lobster : 선언형 방식으로 자리에 앉을 때(&quot;네 명 자리를 부탁해요&quot;) Red Lobster 직원은 우리 가족을 식탁에 데려가는 모든 단계를 알고 있다고 가정하고 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;집에 돌아가는 법 : 나는 집으로 가는 명령형 단계를 모두 알고 있는 GPS를 가지고 있다고 가정하고 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;오토 스틱(2종) 자동차는 변속 기어 위에 일종의 추상화 레이어가 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;그럼 이제 실생활에서 돌아와서 다시 코드를 통해 명령형 프로그래밍과 선언형 프로그래밍의 차이를 살펴보도록 하겠습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;아래 코드는 선언형 프로그래밍 방식으로 대표적인 SQL과 HTML의 간단한 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579196134404&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM Users WHERE Country=&amp;rsquo;Mexico&amp;rsquo;;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1579196148552&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;article&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h1&amp;gt;Declarative Programming&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Sprinkle Declarative in your verbiage to sound smart&amp;lt;/p&amp;gt;
  &amp;lt;/header&amp;gt;
&amp;lt;/article&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;두 가지 예제 모두 무엇이 일어나고 있는지 정확하게 이해할 수 있습니다. 두 예제 모두 선언형 프로그래밍이며 원하는 것을 &quot;&lt;b&gt;어떻게&lt;/b&gt;&quot; 수행하는 것보다는 &quot;&lt;b&gt;무엇을&lt;/b&gt;&quot; 수행하는지에 대해 관심이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;어떻게 그것을 달성해야 하는지에 대한 설명 없이 무엇을 달성하려 하는지에 대해서만 설명하고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;Mexico에 거주하는 모든 사용자를 선택하는 방법에 대한 구현은 추상화되어 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;웹 브라우저가 어떻게 html 문서를 파싱 하고 보여주는지에 대한 방법은 관심이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;그럼 이제 조금 더 와 닿는 Javascript 예제를 통해 명령형 프로그래밍 방식과 선언형 프로그래밍 방식에 대해 살펴보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579196991738&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 배열을 파라미터로 받고 각 요소들의 값에 2를 곱하는 함수
function double (arr) {
  let results = []
  for (let i = 0; i &amp;lt; arr.length; i++){
    results.push(arr[i] * 2)
  }
  return results
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1579197001457&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 배열을 파라미터로 받고 각 요소들을 합한 값을 반환하는 함수
function add (arr) {
  let result = 0
  for (let i = 0; i &amp;lt; arr.length; i++){
    result += arr[i]
  }
  return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1579197010135&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 버튼이 클릭 되었을 때 highlight 클래스를 토글하는 jquery listener
$(&quot;#btn&quot;).click(function() {
  $(this).toggleClass(&quot;highlight&quot;)
  $(this).text() === 'Add Highlight'
    ? $(this).text('Remove Highlight')
    : $(this).text('Add Highlight')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;이 3가지 예시의 공통점이 무엇인지 살펴보면 실제로 필수 요소가 무엇인지 더 잘 식별할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;가장 명백한 공통점은 세 예제 모두 &quot;어떻게&quot;를 설명하고 있다는 점입니다. 명시적으로 배열을 반복하거나 원하는 기능을 수행하는 방법에 대한 단계를 설명하고 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;각 예시 모두 '상태'의 일부를 변경하고 있습니다 ('상태' : 기본적으로 메모리에 저장된 것에 대한 정보라고 볼 수 있습니다 - 변수와 비슷). 처음 두 예제에서는 results라는 변수를 만든 다음 계속 수정하고 있습니다. 세 번째 예제에서는 변수가 없지만 DOM의 상태를 가지고 있습니다. 그리고 DOM 의 상태를 수정합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;약간 주관적일 수 있지만 위의 코드들은 읽기 쉽지 않습니다. 코드를 보고 무슨 일이 일어나고 있는지 한 번에 이해할 수 없습니다. 코드를 정확하게 이해하려면 발생 가능한 상황(변동 가능한 데이터)을 고려하면서 컴파일러처럼 코드를 단계별로 살펴봐야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;그럼 선언형 프로그래밍 방식으로 위의 3가지의 문제점들을 고쳐보도록 하겠습니다. 따라서 각각의 예시들은 &quot;어떻게&quot;가 아닌 &quot;무엇&quot; 이 일어나는지를 설명해야 합니다. 그리고 상태를 변경할 수 없으며, 한눈에 읽을 수 있어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579197684521&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function double (arr) {
  return arr.map((item) =&amp;gt; item * 2)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1579197695119&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add (arr) {
  return arr.reduce((prev, current) =&amp;gt; prev + current, 0)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1579197710858&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Btn
  onToggleHighlight={this.handleToggleHighlight}
  highlight={this.state.highlight}&amp;gt;
    {this.state.buttonText}
&amp;lt;/Btn&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;처음 두 예제에서는 JavaScript에 기본으로 내장된 map과 reduce 메서드를 활용했습니다. 이는 반복해서 이야기했던 내용과 마찬가지로 명령형 방식이 추상화된 것입니다. 선언형 프로그래밍의 중요한 솔루션은 명령형으로 작성된 구현에 대한 추상화입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;모든 예시들은 &quot;어떻게&quot; 보다 &quot;무엇&quot; 이 이루어지기를 원하는지를 설명합니다. (나는 map과 reduce 가 어떻게 이루어져 있는지 알지 못하고 신경 쓸 필요조차 없습니다) 그리고 상태를 변경하는 모든 지점들은 map과 reduce 메서드 안쪽으로 추상화되어 나는 직접 상태를 변경하지 않습니다. 또한 위 예제들은 훨씬 가독성이 높습니다. (map과 reduce 함수에 익숙하다면)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;그럼 세 번째 예제는 어떨까요? 사실 세 번째 예제에 React 문법이 약간 포함되어 있습니다. 하지만 이전 예제에서 언급된 세 가지 문제는 수정되었습니다. React의 진정한 장점은 위의 예제처럼 선언적인 방식으로 UI를 만들 수 있다는 것입니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;선언형 프로그래밍의 또 다른 이점은 프로그램이 상황에 독립적일 수 있다는 것 입니다. &lt;span style=&quot;color: #000000;&quot;&gt;종종 명령형 코드는 현재 상태의 컨텍스트에 의존하기 때문에 재사용하기 어려운 경우가 많습니다. &lt;/span&gt;하지만 선언형 코드는 해당 코드가 달성하고자 하는 것이 무엇인지 만을 나열하기 때문에 동일한 코드를 다른 프로그램에서 재사용하기 쉽습니다.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000;&quot;&gt;마치며&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선언형 프로그래밍은 rx를 접하게 되면서 관심을 가지게 되었는데요, rx의 특징 중 하나가 선언적 스타일로 코드를 작성하게 된다는 것이었습니다. &lt;span style=&quot;color: #000000;&quot;&gt;rx를 제품에 도입하기에 앞서 rx를 왜 도입해야 하는지, rx를 도입함으로써 얻는 이점이 무엇인지를 탐구하고 설득할 수 있는 근거를 만들기 위해 알아보는 시리즈 중 첫 단계로써 해당 포스팅을 작성하게 되었습니다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;그래서 알아본 선언형 프로그래밍은 코드의 가독성과 재사용성을 높일 수 있다는 매력이 있는 것 같습니다. &lt;span style=&quot;color: #000000;&quot;&gt;하지만 결국 스스로 다양한 상황과 소프트웨어에서 직접 적용해보고 경험해 봐야 무엇이 진짜 어떻게 좋은지 판단할 수 있는 힘이 생기는 것 같습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&quot;어떻게&quot;가 아닌 &quot;무엇을&quot; 달성할지를 나열하는 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;그 &quot;무엇을&quot; &quot;어떻게&quot; 달성할지는 추상화되어있는 것을 사용하거나, 추상화시킨다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;선언형 방식으로 코드를 작성하면 여러 곳에서 재사용하기 비교적 쉽다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원문&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;해당 포스팅은 &lt;a href=&quot;https://tylermcginnis.com/imperative-vs-declarative-programming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Imperative vs Declarative Programming&lt;/a&gt; 원문을 번역하고 각색하여 작성된 포스팅입니다. 오역, 오류가 있다면 댓글로 피드백 주시면 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1579199072632&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Imperative vs Declarative Programming&quot; data-og-description=&quot;A guide to understanding the difference between Imperative and Declarative programming.&quot; data-og-host=&quot;tylermcginnis.com&quot; data-og-source-url=&quot;https://tylermcginnis.com/imperative-vs-declarative-programming/&quot; data-og-url=&quot;https://tylermcginnis.com/imperative-vs-declarative-programming/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NFd0v/hyEAYaHh4R/q8fGQg2E5MU2SgUzidts8k/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/fO4nq/hyEBaoF8LA/4MjEON6vdSko1usJCNUko0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://tylermcginnis.com/imperative-vs-declarative-programming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tylermcginnis.com/imperative-vs-declarative-programming/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NFd0v/hyEAYaHh4R/q8fGQg2E5MU2SgUzidts8k/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/fO4nq/hyEBaoF8LA/4MjEON6vdSko1usJCNUko0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Imperative vs Declarative Programming&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;A guide to understanding the difference between Imperative and Declarative programming.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;tylermcginnis.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/430</guid>
      <comments>https://boxfoxs.tistory.com/430#entry430comment</comments>
      <pubDate>Sat, 18 Jan 2020 16:15:59 +0900</pubDate>
    </item>
    <item>
      <title>typescript 3.7 소개</title>
      <link>https://boxfoxs.tistory.com/427</link>
      <description>&lt;p&gt;최근 이직을 한 후 개인적으로 '공부' 라는 것과 거리가 멀어져서 포스팅을 쓰면서 다시 시작해보고자 합니다. 마침 최근에 typescript 3.7이 새롭게 공개돼서 해당 문서를 번역해서 간단히 소개해보도록 하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Optional Chaining&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Kotlin을 접해봤다면 가장 반가운 문법이지 않을까 싶습니다. Kotlin의 가장 편리한 문법중 하나가 바로 이 Optional Chaining 이라고 생각합니다. Typescript 에도 3.7 부터 &lt;span style=&quot;color: #333333;&quot;&gt;Optional Chaining 이 &lt;/span&gt;추가 된다고 하니 반가웠습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Optional Chaining 을 간단하게 설명하면 기존의 복잡한 if문 을 줄여주는 문법이라 할 수 있을 것 같습니다. 기존에 null 또는 undefined 타입을 처리하기 위해서는 아래와 같이 복잡한 조건문을 사용해야 했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1573570851462&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x;
if(foo &amp;amp;&amp;amp; foo.bar &amp;amp;&amp;amp; foo.bar.baz) {
	x = foo.bar.baz()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하지만 Optional Chaning 을 사용하면 아래와 같이 간단하게 처리할 수 있습니다. &lt;span style=&quot;color: #333333;&quot;&gt;null 또는 undefined 를 가질 수 있는 변수 뒤에 물음표를 붙이면 됩니다. 만약 물음표가 붙은 변수가 null 또는 undefined 값을 가진다면 코드의 실행을 중지하고 바로 undefined를 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1573571185365&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = foo?.bar?.baz();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1573572432307&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function print(doPrint: boolean): any {
    if (doPrint) {
        return;
    }
    console.log('print');
    return { print: print };
}
print(false)?.print(false)?.print(true).print(false);

// console : print
// console : print&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Nullish Coalescing&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Nullish Coalescing Operator(NCO) 는 새로운&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;ECMAScript 의 기능입니다. 기존의 (||) 연산자와 비슷한 기능을 하는데요, 물음표 두 개(??) 를 통해 왼쪽의 겂이 null 혹은 undefined 인지 확인합니다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약 왼쪽의 값이 null 혹은 undefined 라면 NCO 의 오른쪽에 위치한 값이 반환됩니다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574092503508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const foo = undefined;
const bar = 'test';
const res = foo ?? bar;
// res === 'test'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기존의 || 연산자를 두고도 NCO가 새롭게 추가된 이유가 있습니다. 기존의 || 연산자는 null 또는 undefined 외에도 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/Falsy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;falsy&lt;/a&gt;(false 와 같은 값) 들을 처리하기 때문입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1574092777644&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const zero = 0;
const ten = 10;
const itShouldBeZero = zero || ten; // 10
const itShouldBeTen = zero ?? ten; // 0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;javascript 에서 0이나 NaN은 false 와 동일 시 합니다. 따라서 원하는 || 연산자를 이용해 개발자가 의도한 동작을 하기 위해서는 비교적 복잡한 방법이 필요한 경우가 있었습니다. 하지만 NCO 는 정확히 null 또는 undefined 만 검사해주기 때문에 상황에 따라 적절히 두 연산자를 사용하면 좋을 것 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Assertion Functions&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;타입스크립트는 Type Guard 를 통해 미지의 타입을 검사 하여 타입을 추론하고 안전한 로직을 작성 할 수 있습니다. 또한 Type Guard 를 통해 추론된 타입으로 IDE에서 제공되는 자동 완성과 인텔리센스 기능을 사용할 수 있습니다. 하지만 Type Guard 는 아래 예제와 같이 번거롭게 사용해야 하는 번거로움이 있었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1574093186359&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function yell(str: any) {
    if (typeof str !== &quot;string&quot;) {
        throw new TypeError(&quot;str should have been a string.&quot;)
    }
    return str.toUppercase();
    //         ~~~~~~~~~~~
    // error: Property 'toUppercase' does not exist on type 'string'.
    //        Did you mean 'toUpperCase'?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 TypeScript 3.7 에서는 &quot;assertion signatures&quot; 가 추가되었습니다. assertion signatures 를 통해 assert 함수를 만들고, Type Guard 를 한 줄로 간단하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1574093460883&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function yell(str) {
    assert(typeof str === &quot;string&quot;);

    return str.toUppercase();
    //         ~~~~~~~~~~~
    // error: Property 'toUppercase' does not exist on type 'string'.
    //        Did you mean 'toUpperCase'?
}

function assert(condition: any, msg?: string): asserts condition {
    if (!condition) {
        throw new AssertionError(msg)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;str 변수가 string 타입이 맞는지 검사하는 조건과 함께 assert 함수를 호출하면, 해당 assert는 type guard 로써 동작하기 때문에 그 뒤의 로직에서 str 은 string 타입으로 추론&lt;span style=&quot;color: #333333;&quot;&gt;되어 수행됩니다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;또 다른 방식으로는 아래와 같이 타입을 검사하는 로직을 assert 함수 내부에 구현 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1574093680750&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function yell(str: any) {
    assertIsString(str);

    // Now TypeScript knows that 'str' is a 'string'.

    return str.toUppercase();
    //         ~~~~~~~~~~~
    // error: Property 'toUppercase' does not exist on type 'string'.
    //        Did you mean 'toUpperCase'?
}

function assertIsString(val: any): asserts val is string {
    if (typeof val !== &quot;string&quot;) {
        throw new AssertionError(&quot;Not a string!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Recursive Type Aliases&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;기존 TypeScript에서 Type 별칭을 &quot;재귀적으로&quot; 참조하는 것은 불가능 했습니다. 타입 별칭은 컴파일 되며 스스로를 다른 타입으로 대체할 수 있어야 하기 때문입니다. 따라서 컴파일러는 아래와 같은 상황을 에러로 처리했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1575103709196&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Foo = Foo;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이는 합리적인 제약사항 이였습니다. 왜냐하면 Foo를 사용하는 곳에는 다시 Foo 로 대치 되고, 대체된 Foo 는 다시 Foo 로 대체 되어야 하고... 이렇게 무한하게 재귀적으로 타입이 대체되기 때문입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 TypeScript 3.6과 이전에는 아래와 같이 타입을 재귀적으로 선언 할 경우 컴파일 오류가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1575104086707&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ValueOrArray&amp;lt;T&amp;gt; = T | Array&amp;lt;ValueOrArray&amp;lt;T&amp;gt;&amp;gt;;
//   ~~~~~~~~~~~~
// error: Type alias 'ValueOrArray' circularly references itself.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하지만 위와 같은 문제는 인터페이스로 우회하면 아무런 문제가 없도록 작성 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1575104441957&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ValueOrArray&amp;lt;T&amp;gt; = T | ArrayOfValueOrArray&amp;lt;T&amp;gt;;

interface ArrayOfValueOrArray&amp;lt;T&amp;gt; extends Array&amp;lt;ValueOrArray&amp;lt;T&amp;gt;&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;인터페이스(및 기타 객체 유형) 는 간접적인 레벨에서 타입을 정의하기 때문에 컴파일 결과로 나오는 게 없습니다. 결국 인터페이스를 거쳐 재귀적으로 선언된 타입 별칭은 컴파일되며 결국 사라지기 때문에 문제가 없습니다.&lt;br /&gt;&lt;br /&gt;하지만 결국 재귀적으로 타입 별칭을 참조하는 것과 동일한 역할을 하지만, 위에서 말한 제약사항으로 인해 굳이 필요하지 않은 인터페이스를 하나 더 정의해서 우회해야 하는 불편함이 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 TypeScript 3.7 에서는 이러한 제약사항을 없에고 재귀적으로 타입을 선언할 수 있게 되었습니다. 따라서 아래와 같이 정의해야 했던 타입 선언을&lt;/p&gt;
&lt;pre id=&quot;code_1575105604724&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Json =
    | string
    | number
    | boolean
    | null
    | JsonObject
    | JsonArray;

interface JsonObject {
    [property: string]: Json;
}

interface JsonArray extends Array&amp;lt;Json&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같이 다시 정의할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1575105642654&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Json =
    | string
    | number
    | boolean
    | null
    | { [property: string]: Json }
    | Json[];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러한 기능을 통해 튜플 타입 별칭을 재귀적으로 참조 할 수도 있습니다. 기존에 오류로 판정 했던 아래 코드는 이제 유효한 코드가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1575105730571&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type VirtualNode =
    | string
    | [string, { [key: string]: any }, ...VirtualNode[]];

const myNode: VirtualNode =
    [&quot;div&quot;, { id: &quot;parent&quot; },
        [&quot;div&quot;, { id: &quot;first-child&quot; }, &quot;I'm the first child&quot;],
        [&quot;div&quot;, { id: &quot;second-child&quot; }, &quot;I'm the second child&quot;]
    ];&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그 외의 주요 변경사항&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#--declaration-and---allowjs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;--declaration flag and --allowJs flag&lt;/a&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;--declaration flag와 --allowJs flag를 함께 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;.js 에서 JsDOC을 정의하고 --declaration flag 와 함께 컴파일 하면 .js 파일으로부터 difinition(.d.ts) 파일을 만들 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The&amp;nbsp;useDefineForClassFields&amp;nbsp;Flag&amp;nbsp;and&amp;nbsp;The&amp;nbsp;declare&amp;nbsp;Property&amp;nbsp;Modifier&lt;/a&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useDefineForClassFields flag 와 함께 ts 를 컴파일 하면 Object.defineProperty 로 Class Field 들이 정의됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 TypeScript 가 동작하는 방식과 매우 달라지기 때문에 점진적으로 적용될 계획입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TypeScript 3.7에서는 useDefineForClassFields flag 를 사용해야만 적용됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 TypeScript 코드가 정상적으로 돌아가지 않을 수 있는 중요한 변경사항이라 자세하게 살펴볼 필요가 있을 것 같습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아직 TypeScript 컴파일러(3.7.2) 에 문제가 있어서 사용하지 않는 것 이 좋습니다. (&lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/35445&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 github 이슈 - 3.7.3 에서 수정 예정&lt;/a&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#dom-changes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;DOM Changes&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/microsoft/TypeScript/pull/33627&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lib.dom.ts&lt;/a&gt;&lt;/span&gt; 가 수정되었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#local-and-imported-type-declarations-now-conflict&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Local&amp;nbsp;and&amp;nbsp;Imported&amp;nbsp;Type&amp;nbsp;Declarations&amp;nbsp;Now&amp;nbsp;Conflict&lt;/a&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존에 interface를 동일한 이름으로 여러번 정의 할 수 있었지만 TypeScript 3.7 부터는 에러로 처리됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 자료&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1573570603789&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;TypeScript 3.7 &amp;middot; TypeScript&quot; data-og-description=&quot;Optional Chaining # Playground Optional chaining is issue #16 on our issue tracker. For context, there have been over 23,000 issues on the TypeScript issue tracker since then. At its core, optional chaining lets us write code where TypeScript can immediate&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html&quot; data-og-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wQM3u/hyDGbPghLL/zEqmwK44elaAmDK7x2ULqk/img.png?width=1666&amp;amp;height=1242&amp;amp;face=0_0_1666_1242&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wQM3u/hyDGbPghLL/zEqmwK44elaAmDK7x2ULqk/img.png?width=1666&amp;amp;height=1242&amp;amp;face=0_0_1666_1242');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;TypeScript 3.7 &amp;middot; TypeScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Optional Chaining # Playground Optional chaining is issue #16 on our issue tracker. For context, there have been over 23,000 issues on the TypeScript issue tracker since then. At its core, optional chaining lets us write code where TypeScript can immediate&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/427</guid>
      <comments>https://boxfoxs.tistory.com/427#entry427comment</comments>
      <pubDate>Wed, 4 Dec 2019 02:11:05 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 좋은 테스트 작성법 1-4</title>
      <link>https://boxfoxs.tistory.com/424</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 글은 github&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #1b9ae6;&quot;&gt;&lt;a style=&quot;color: #1b9ae6;&quot; href=&quot;https://github.com/goldbergyoni&quot;&gt;goldbergyoni&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;님의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #1b9ae6;&quot;&gt;&lt;a style=&quot;color: #1b9ae6;&quot; href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices#section-0%EF%B8%8F%E2%83%A3-the-golden-rule&quot;&gt;javascript-testing-best-practices&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;를 번역하여 작성한 글 입니다. 번역 과정중 오류가 있을 수 있으니 댓글을 통해 피드백 주시면 감사하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Error를 catch 하지 말고 검증하라&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일부 입력이 에러를 발생시키는 것을 검증할 때 try-catch-finally 문을 사용하여 catch 스코프에 들어왔을 때 assertion 을 수행하는 방법을 사용할 것입니다. 하지만 이는 테스트의 의도와 기대 결과를 쉽게 파악할 수 없게 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좀 더 우아한 대안은 테스트 프레임워크에서 제공하는 expect(method).to.thorw or expect(method).toThrow() 과 같은&amp;nbsp;one-line throw assertion 을 사용하는 것 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;또한 필수적으로 예외에 오류 유형을 알려주는 속성이 포함되어 있는지 검증해야 합니다. 그렇지 않으면 오류가 발생했을 때 응용 프로그램은 사용자에게 에러 메세지를 출력하는 것 외에 추가로 할 수 있는 작업이 제한적이게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하지 않는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;try-catch 로 에러 발생을 테스트하는 긴 테스트 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568354341064&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;When no product name, it throws error 400&quot;, async() =&amp;gt; {
  let errorWeExceptFor = null;
  try {
    const result = await addNewProduct({name:'nest'});}
  catch (error) {
    expect(error.code).to.equal('InvalidInput');
    errorWeExceptFor = error;
  }
  expect(errorWeExceptFor).not.to.be.null;
  //if this assertion fails, the tests results/reports will only show
  //that some value is null, there won't be a word about a missing Exception
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;권장하는 예제&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;QA, PM 조차도 쉽게 요구사항을 이해할 수 있는 테스트 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568354422881&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it.only(&quot;When no product name, it throws error 400&quot;, async() =&amp;gt; {
  expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', &quot;InvalidInput&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트에 태그를 달아라&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각각의 테스트는 서로 다른 시나리오에 따라 수행되어야 합니다. quick smoke, IO 가 필요 없는 테스트, 개발자가 커밋을 하거나 파일을 저장했을 때 수행되는 테스트, PR 이 생성되었을 때 수행되는 end-to-end 테스트 등.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 테스트들을 다음과 같은 #cold #api #sanity 키워드로 태깅하면 언제든지 필요한 카테고리의 테스트들만 선택하여 수행할 수 있습니다. 그렇지 않으면 개발자가 작은 부분을 수정함에도 수십개의 모든 테스트를 수행하게 되어 개발 생산성이 매우 떨어질 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568356346092&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//this test is fast (no DB) and we're tagging it correspondigly
//now the user/CI can run it frequently
describe('Order service', function() {
  describe('Add new order #cold-test #sanity', function() {
    test('Scenario - no currency was supplied. Expectation - Use the default currency #sanity', function() {
      //code logic here
    });
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;다른 일반적인 좋은 테스팅 팁&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/&quot;&gt;TDD 원칙&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;을 배우&lt;/span&gt;고 연습하는 것을 권장합니다.&amp;nbsp;&lt;a href=&quot;https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html&quot;&gt;red-green-refactor style&lt;/a&gt;&amp;nbsp;에서는 코드를 작성하기 전에 테스트를 작성하는 것이 중요한 원칙입니다. 각 테스트마다 발견할 수 있는 버그를 한 가지만 검사하도록 하고 이 버그가 발견되었을 때 테스트가 실패하도록 구성합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 먼저 우선적으로 테스트를 만족시키는 간단한 코드를 작성하여 모듈 개발을 시작한 뒤 점진적으로 리팩터링 하고 프로덕션 수준으로 발전시키며 개발합니다.&lt;/p&gt;
&lt;h4&gt;원문 자료&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1568379937834&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/banyQM/hyCP7GstwK/66O5PEM0jGLAaVNpDGnU5k/img.jpg?width=400&amp;amp;height=400&amp;amp;face=178_40_242_110,https://scrap.kakaocdn.net/dn/H3Fzk/hyCN873MDl/LR3kayTYkKfXPLJDM9NPv0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;    Comprehensive and exhaustive JavaScript &amp;amp; Node.js testing best practices (August 2019) - goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/424</guid>
      <comments>https://boxfoxs.tistory.com/424#entry424comment</comments>
      <pubDate>Fri, 13 Sep 2019 22:06:14 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 좋은 테스트 작성법 1-3</title>
      <link>https://boxfoxs.tistory.com/421</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 글은 github&amp;nbsp;&lt;span style=&quot;color: #1b9ae6;&quot;&gt;&lt;a style=&quot;color: #1b9ae6;&quot; href=&quot;https://github.com/goldbergyoni&quot;&gt;goldbergyoni&lt;/a&gt;&lt;/span&gt;&amp;nbsp;님의&amp;nbsp;&lt;span style=&quot;color: #1b9ae6;&quot;&gt;&lt;a style=&quot;color: #1b9ae6;&quot; href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices#section-0%EF%B8%8F%E2%83%A3-the-golden-rule&quot;&gt;javascript-testing-best-practices&lt;/a&gt;&lt;/span&gt;&amp;nbsp;를 번역하여 작성한 글 입니다. 번역 과정중 오류가 있을 수 있으니 댓글을 통해 피드백 주시면 감사하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스냅샷 테스트를 할 때는 짧은 인라인 스냅샵만 사용하라&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://jestjs.io/docs/en/snapshot-testing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;snapshot testing&lt;/a&gt;이 필요하다면 핵심 검증 사항에만 집중된 (3~7 라인) 짧은 snapshots 을 외부 파일이 아닌 테스트 코드 내에서 사용하는 것을 권장합니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 가이드라인만 지킨다면 테스트가 쉽게 깨지지 않고 좋은 가독성을 유지할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반면에, &lt;span style=&quot;color: #000000;&quot;&gt;'고전적인 스냅샷'의&amp;nbsp;&lt;/span&gt;튜토리얼이나 도구들은 스냅샷을 매우 크게 만들고 외부 파일로 저장하도록 유도하고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 테스트가 실행될 때 마다 외부에 저장된 파일과 비교하여 문제가 없는지 알려주는 방식으로 검증 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 큰 파일로, 많은 부분을 커버하는 스냅샷으로 테스트를 작성하면 약간의 코드를 변경하더라도 스냅샷이 유효하지 않게 될 가능성이 커집니다. 따라서 테스트가 쉽게 깨질 수 있습니다. 또한 하나의 테스트케이스가 수십 라인의 코드를 커버하기 때문에 테스트가 실패하더라도 실패한 원인을 쉽게 파악하기 어렵습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 문제들은 잘못 작성된 스냅샷 테스트 뿐만 아니라 지나치게 많은 것을 목표로 하는 모호한 테스트에서 많이 발생하는 문제 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 스키마 이거나 데이터가 아닌 것들을 검증할 때 또는 테스트 하고자 하는 docuemnt 가 거의 변경될 일이 없을때와 같은 경우는 예외적으로 해당 가이드라인을 지킬 필요는 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하지 않는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래 테스트 코드는 보이지 않는 수 많은 코드 라인과 커플링 되어 깨지기가 매우 쉬운 상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568290676278&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it('TestJavaScript.com is renderd correctly', ()  =&amp;gt; {

//Arrange
const element = (&amp;lt;DisplayPage page  =  &quot;http://www.testjavascript.com&quot;  &amp;gt; Test JavaScript &amp;lt; /DisplayPage&amp;gt;);


//Act
const receivedPage = renderer.create(element).toJSON();

//Assert
expect(receivedPage).toMatchSnapshot();
//We now implicitly maintain a 2000 lines long document
//every additional line break or comment - will break this test

});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요구사항이 정확하게 드러나있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568290833636&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it('When visiting TestJavaScript.com home page, a menu is displayed', () =&amp;gt; {
  //Arrange
  const element = &amp;lt;DisplayPage page  =  &quot;http://www.testjavascript.com&quot;  &amp;gt; Test JavaScript &amp;lt; /DisplayPage&amp;gt;;
  const snapshot = `
      &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;Home&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt; About &amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt; Contact &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    `;

  //Act
  receivedPage tree = renderer.create(element).toJSON();

  //Assert
  const menu = receivedPage.content.menu;
  expect(menu).toMatchInlineSnapshot(snapshot);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;전역 fixtures, seeds 를 피하고 테스트 마다 데이터를 추가하라&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트를 위해 필요한 사전 작업 또는 데이터를 사용할 때는 각각의 테스트케이스별로 분리해서 작업하면&amp;nbsp;커플링이 방지되고 테스트 흐름을 쉽게 파악할 수 있습니다.&amp;nbsp;하지만 현실은 종종 성능 개선을 이유로 테스트 수행전 한번에 DB에 데이터를 추가하는 등의 경우가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실제로 성능은 중요한 문제이지만,&amp;nbsp;이는 얼마든지 다른 방법으로도 개선 가능한 경우가 많습니다. 하지만 대부분의 경우 테스트의 복잡성은 다른 최우선으로 고려해야 할 만큼 엄격하게 관리해야 합니다.&amp;nbsp;따라서 각 테스트가 명시적으로&amp;nbsp;필요한&amp;nbsp;레코드, 데이터를&amp;nbsp;DB 에 추가하고 해당 레코드만 사용하여 작업하도록 만드는게 좋습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 성능이 중요한 관심사가 되었을 때&amp;nbsp;현명한 타협점은&amp;nbsp;데이터를 변경하지 않는 테스트 집합으로 만들고 테스트 수행 전 한번에 데이터를 셋팅하는 형태로 만들 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하지 않는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 테스트들이 독립적이지 않고 &lt;span style=&quot;color: #000000;&quot;&gt;글로벌&amp;nbsp;&lt;/span&gt;DB 에 데이터를 추가하는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;before hook에 의존합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568294167173&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;before(() =&amp;gt; {
  //adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework
  await DB.AddSeedDataFromJson('seed.json');
});
it(&quot;When updating site name, get successful confirmation&quot;, async () =&amp;gt; {
  //I know that site name &quot;portal&quot; exists - I saw it in the seed files
  const siteToUpdate = await SiteService.getSiteByName(&quot;Portal&quot;);
  const updateNameResult = await SiteService.changeName(siteToUpdate, &quot;newName&quot;);
  expect(updateNameResult).to.be(true);
});
it(&quot;When querying by site name, get the right site&quot;, async () =&amp;gt; {
  //I know that site name &quot;portal&quot; exists - I saw it in the seed files
  const siteToCheck = await SiteService.getSiteByName(&quot;Portal&quot;);
  expect(siteToCheck.name).to.be.equal(&quot;Portal&quot;); //Failure! The previous test change the name :[
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;권장하는 예제&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 테스트는 자체 데이터 세트를 활용하여 작업합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568294784979&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;When updating site name, get successful confirmation&quot;, async () =&amp;gt; {
  //test is adding a fresh new records and acting on the records only
  const siteUnderTest = await SiteService.addSite({
    name: &quot;siteForUpdateTest&quot;
  });
  
  const updateNameResult = await SiteService.changeName(siteUnderTest, &quot;newName&quot;);
  
  expect(updateNameResult).to.be(true);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;원문 자료&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1568301193197&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gkoFE/hyCOjBrUH4/rqz2itUfmpAzMt7l4pCykk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=178_40_242_110,https://scrap.kakaocdn.net/dn/bZD5Xo/hyCQdM77TF/SPWOGzdvDqtpttkdFyI6bk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;    Comprehensive and exhaustive JavaScript &amp;amp; Node.js testing best practices (August 2019) - goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/421</guid>
      <comments>https://boxfoxs.tistory.com/421#entry421comment</comments>
      <pubDate>Fri, 13 Sep 2019 00:14:17 +0900</pubDate>
    </item>
    <item>
      <title>2019 Northeast Asia GDG Summit 후기</title>
      <link>https://boxfoxs.tistory.com/422</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;올해는 이직준비와 토이프로젝트, GDG 활동 그외에 몇몇가지 일들로 인해 너무 바쁘게 지내는 바람에 해외여행을 하리라고 생각도 못하고 있었습니다. 그런데 어쩌다보니 저번주 주말 베이징에 다녀왔습니다. 1박 2일로..&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Northeast Aisa GDG Summit&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한국, 일본, 중국, 대만 등 아시아권 GDG 운영진들은 1년에 한번씩 중국에 모여서 여러 세션과 라이트닝 토크, 토론등을 통해 이야기를 나누고 경험을 공유할 수 있는 Northeast Asia GDG Summit 에 참여할 수 있습니다. 저도 이번 Northeast Asia GDG Summit 2019 에 참가하기 위해&amp;nbsp;베이징에 다녀오게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;vbox7225_SX9A1125_173644_small.JPG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSg3KR/btqybY9YTEr/iMq2TGfU6fqdaeaJG5qZwK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSg3KR/btqybY9YTEr/iMq2TGfU6fqdaeaJG5qZwK/img.jpg&quot; data-alt=&quot;GDG Asia Organizers &amp;amp;amp;amp; Cummunity Managers&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSg3KR/btqybY9YTEr/iMq2TGfU6fqdaeaJG5qZwK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSg3KR%2FbtqybY9YTEr%2FiMq2TGfU6fqdaeaJG5qZwK%2Fimg.jpg&quot; data-filename=&quot;vbox7225_SX9A1125_173644_small.JPG&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GDG Asia Organizers &amp;amp; Cummunity Managers&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원래 행사 일정은 금요일부터 일요일까지였고 월요일에 휴가를 쓰고 관광을 더 하다 오려고 했지만, 예상에 없던 갑작스러운 이직을 하게되는 바람에 마무리를 하느라 휴가를 취소하고 토요일, 일요일 일정만 참가하기로 하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qSkHE/btqx82Z2oXc/N5UTUMwCLFjgid10TFiauK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qSkHE/btqx82Z2oXc/N5UTUMwCLFjgid10TFiauK/img.png&quot; width=&quot;607&quot; height=&quot;455&quot; data-filename=&quot;IMG_7157.png&quot; style=&quot;width: 56.617%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSkHE/btqx82Z2oXc/N5UTUMwCLFjgid10TFiauK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSkHE%2Fbtqx82Z2oXc%2FN5UTUMwCLFjgid10TFiauK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HZXUc/btqyaptvffO/SdhmkBnoILJImF8NUr5020/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HZXUc/btqyaptvffO/SdhmkBnoILJImF8NUr5020/img.png&quot; data-filename=&quot;blob&quot; style=&quot;width: 41.0574%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HZXUc/btqyaptvffO/SdhmkBnoILJImF8NUr5020/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHZXUc%2FbtqyaptvffO%2FSdhmkBnoILJImF8NUr5020%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;토요일 새벽에 공항에 도착해서 노숙을 하고 아침일찍 태풍이 들이닥치기 직전에 비행기를 타고 베이징으로 출발했습니다. 이미 폭풍의 영향을 받기 시작했는지 비행기가 많이 흔들려서 걱정이 되기는 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWNa4/btqyapG3u1G/e1PKSOBN9rTqlOtMfPrCEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWNa4/btqyapG3u1G/e1PKSOBN9rTqlOtMfPrCEK/img.png&quot; height=&quot;590&quot; data-filename=&quot;IMG_7170.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWNa4/btqyapG3u1G/e1PKSOBN9rTqlOtMfPrCEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWNa4%2FbtqyapG3u1G%2Fe1PKSOBN9rTqlOtMfPrCEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGAdZ2/btqyaqMGwl2/ve0kfqsJbNtlwnHL5F4YXK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGAdZ2/btqyaqMGwl2/ve0kfqsJbNtlwnHL5F4YXK/img.jpg&quot; data-filename=&quot;IMG_7172.JPG&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGAdZ2/btqyaqMGwl2/ve0kfqsJbNtlwnHL5F4YXK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGAdZ2%2FbtqyaqMGwl2%2Fve0kfqsJbNtlwnHL5F4YXK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iiqa7/btqycO6UtY3/mV4KuhUzElqQOwYoGHY2tK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iiqa7/btqycO6UtY3/mV4KuhUzElqQOwYoGHY2tK/img.jpg&quot; data-filename=&quot;IMG_7180.JPG&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iiqa7/btqycO6UtY3/mV4KuhUzElqQOwYoGHY2tK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIiqa7%2FbtqycO6UtY3%2FmV4KuhUzElqQOwYoGHY2tK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;공항 / 택시 / 베이징 거리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무사히 베이징에 잘 도착하여 입국 절차를 거쳐&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;택시를 타고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Google Office 까지 이동했습니다. 중국말을 하나도 모르고 구글 번역기를 기사님께 보여드려도 잘 못알아 들으셔서 고비를 겪었습니다. 다행이 영어를 할 수 있는 안내원분이 구글 지도를 보고 도와주셔서 목적지에 잘 도착할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nVpPC/btqx8UBbmn1/9ESPMNlqpTV4C6hD20HGKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nVpPC/btqx8UBbmn1/9ESPMNlqpTV4C6hD20HGKK/img.jpg&quot; data-filename=&quot;IMG_7186.jpg&quot; width=&quot;486&quot; height=&quot;648&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nVpPC/btqx8UBbmn1/9ESPMNlqpTV4C6hD20HGKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnVpPC%2Fbtqx8UBbmn1%2F9ESPMNlqpTV4C6hD20HGKK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7qBiY/btqx8Th19Co/QrMVr21UIGvOleNtbIHRrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7qBiY/btqx8Th19Co/QrMVr21UIGvOleNtbIHRrK/img.png&quot; data-filename=&quot;IMG_7187.png&quot; width=&quot;423&quot; height=&quot;564&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7qBiY/btqx8Th19Co/QrMVr21UIGvOleNtbIHRrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7qBiY%2Fbtqx8Th19Co%2FQrMVr21UIGvOleNtbIHRrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qqn82/btqycO6U0U2/EVU6FJKM2iWfNJHjkbRmnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qqn82/btqycO6U0U2/EVU6FJKM2iWfNJHjkbRmnK/img.png&quot; data-filename=&quot;IMG_7193.png&quot; width=&quot;364&quot; height=&quot;364&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qqn82/btqycO6U0U2/EVU6FJKM2iWfNJHjkbRmnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqqn82%2FbtqycO6U0U2%2FEVU6FJKM2iWfNJHjkbRmnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Il1No/btqx9yj8M34/HKl2Q7IylCp1kNPoUjXPNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Il1No/btqx9yj8M34/HKl2Q7IylCp1kNPoUjXPNk/img.png&quot; data-filename=&quot;IMG_7198.png&quot; width=&quot;281&quot; height=&quot;281&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Il1No/btqx9yj8M34/HKl2Q7IylCp1kNPoUjXPNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIl1No%2Fbtqx9yj8M34%2FHKl2Q7IylCp1kNPoUjXPNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Google Office Beijing 외관 / 팻말 / Google Office 식당 / 음식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여차저차해서 베이징의 Google Office 에 도착하자 마자 점심시간이 되어 바로 점심을 먹었습니다. Google Office 내 식당에서 뷔페식으로 원하는 음식을 담아먹을 수 있었습니다. 중국에 와서 첫 현지 음식(?) 이라고 볼 수 있는데, 처음보는 음식도 많았고 대체적으로 향이 특이했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Office Tour&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;밥먹고 잠깐 쉬면서 깨알 인증샷을 찍고 Office Tour 를 하며 베이징 Google Office 에서 일하시는 분들은 어떤 환경에서 어떻게 일하는지를 엿볼 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btWDq7/btqx9yYIvmP/DzimN1WReJYhfF3gwganCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btWDq7/btqx9yYIvmP/DzimN1WReJYhfF3gwganCK/img.png&quot; data-filename=&quot;IMG_7205.png&quot; width=&quot;434&quot; height=&quot;434&quot; style=&quot;width: 35.5321%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btWDq7/btqx9yYIvmP/DzimN1WReJYhfF3gwganCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtWDq7%2Fbtqx9yYIvmP%2FDzimN1WReJYhfF3gwganCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK5v20/btqyceEQvgo/rPiXKSbrIk7dsCMloAVTS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK5v20/btqyceEQvgo/rPiXKSbrIk7dsCMloAVTS1/img.png&quot; data-filename=&quot;blob&quot; width=&quot;602&quot; height=&quot;602&quot; style=&quot;width: 62.1423%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK5v20/btqyceEQvgo/rPiXKSbrIk7dsCMloAVTS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK5v20%2FbtqyceEQvgo%2FrPiXKSbrIk7dsCMloAVTS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH0yA2/btqx8UVzMhO/uCz7rVPDhSzv42BrRghT2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH0yA2/btqx8UVzMhO/uCz7rVPDhSzv42BrRghT2K/img.png&quot; data-filename=&quot;blob&quot; width=&quot;613&quot; height=&quot;613&quot; style=&quot;width: 54.301%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH0yA2/btqx8UVzMhO/uCz7rVPDhSzv42BrRghT2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH0yA2%2Fbtqx8UVzMhO%2FuCz7rVPDhSzv42BrRghT2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BdMce/btqycOTsP2W/X6uiwR28O3eodNc4kytXn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BdMce/btqycOTsP2W/X6uiwR28O3eodNc4kytXn0/img.png&quot; data-filename=&quot;blob&quot; width=&quot;392&quot; height=&quot;392&quot; style=&quot;width: 43.3734%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BdMce/btqycOTsP2W/X6uiwR28O3eodNc4kytXn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdMce%2FbtqycOTsP2W%2FX6uiwR28O3eodNc4kytXn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHLUHu/btqyaoOTxb1/X5tjPfvtCy913TtWeW2WkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHLUHu/btqyaoOTxb1/X5tjPfvtCy913TtWeW2WkK/img.png&quot; data-filename=&quot;IMG_7207.png&quot; width=&quot;496&quot; height=&quot;496&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHLUHu/btqyaoOTxb1/X5tjPfvtCy913TtWeW2WkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHLUHu%2FbtqyaoOTxb1%2FX5tjPfvtCy913TtWeW2WkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1EZjb/btqycOTnGdS/FnvCIaQZAylfINeSjFgYGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1EZjb/btqycOTnGdS/FnvCIaQZAylfINeSjFgYGK/img.png&quot; data-filename=&quot;IMG_7210.png&quot; width=&quot;381&quot; height=&quot;381&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1EZjb/btqycOTnGdS/FnvCIaQZAylfINeSjFgYGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1EZjb%2FbtqycOTnGdS%2FFnvCIaQZAylfINeSjFgYGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBrHlc/btqybgJNYXA/kOGGBa9k83dFFTaKbTafLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBrHlc/btqybgJNYXA/kOGGBa9k83dFFTaKbTafLk/img.png&quot; data-filename=&quot;IMG_7241.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBrHlc/btqybgJNYXA/kOGGBa9k83dFFTaKbTafLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBrHlc%2FbtqybgJNYXA%2FkOGGBa9k83dFFTaKbTafLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사무실이 넓어서 그런지 쇼핑몰에만 있을 법한 표지판도 있던게 신기했고,&amp;nbsp;책상위에 안드로이드 피규어를 종류별로 모아놓은 분도 계셨습니다. 아기자기하고 창의적으로 꾸며놓은게 많아서 역시 Google Office 라는 생각을 가지게 만들었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8qlsn/btqx8KlgI43/Cqnq7hq5gNK9KcadiC8mfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8qlsn/btqx8KlgI43/Cqnq7hq5gNK9KcadiC8mfk/img.png&quot; data-filename=&quot;IMG_7229.png&quot; width=&quot;455&quot; height=&quot;455&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8qlsn/btqx8KlgI43/Cqnq7hq5gNK9KcadiC8mfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8qlsn%2Fbtqx8KlgI43%2FCqnq7hq5gNK9KcadiC8mfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yiOmA/btqybfqCg7u/9qH9UkxD9cZLSWI1KKY7o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yiOmA/btqybfqCg7u/9qH9UkxD9cZLSWI1KKY7o0/img.png&quot; data-filename=&quot;IMG_7235.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yiOmA/btqybfqCg7u/9qH9UkxD9cZLSWI1KKY7o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyiOmA%2FbtqybfqCg7u%2F9qH9UkxD9cZLSWI1KKY7o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAWnj/btqyao9cfu6/xVxeYchnn14XhNGOZwkBn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAWnj/btqyao9cfu6/xVxeYchnn14XhNGOZwkBn1/img.png&quot; data-filename=&quot;IMG_7236.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAWnj/btqyao9cfu6/xVxeYchnn14XhNGOZwkBn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAWnj%2Fbtqyao9cfu6%2FxVxeYchnn14XhNGOZwkBn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;입사자 이름을 세겨놓은 벽 / Google&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;투어중에 가장 기억에 남았던 점은 Google 에 입사하신 분들의 이름을 위 사진처럼 벽에 적어서 전시한다는 점 입니다. 저도 저런 곳에 이름을 한번 올려볼 수 있었으면 좋겠네요.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Liglightning&amp;nbsp;talk&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Office Tour를 마치고 Liglightning Talk 시간이 이어졌습니다. 여러 GDG Organizer 분들이 GDG 활동을 하면서 겪은 이야기나 생각등에 대해서 자유롭게 발표했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEBguN/btqx8LEy3ol/ykorMVkjNoSHHRk9SOSNCk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEBguN/btqx8LEy3ol/ykorMVkjNoSHHRk9SOSNCk/img.jpg&quot; data-filename=&quot;vbox7225_SX9A0328_141155_small_v1.JPG&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEBguN/btqx8LEy3ol/ykorMVkjNoSHHRk9SOSNCk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEBguN%2Fbtqx8LEy3ol%2FykorMVkjNoSHHRk9SOSNCk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRCfb/btqyapmPdMg/C95RkL0YUWWGjpALtMXt01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRCfb/btqyapmPdMg/C95RkL0YUWWGjpALtMXt01/img.jpg&quot; data-filename=&quot;vbox7225_SX9A0474_142732_small_v1.JPG&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRCfb/btqyapmPdMg/C95RkL0YUWWGjpALtMXt01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRCfb%2FbtqyapmPdMg%2FC95RkL0YUWWGjpALtMXt01%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epGLVA/btqx8KeB6u3/JrV2Q5PxUXvUasGKDK2poK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epGLVA/btqx8KeB6u3/JrV2Q5PxUXvUasGKDK2poK/img.jpg&quot; data-filename=&quot;vbox7225_SX9A0264_140214_small_v1.JPG&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epGLVA/btqx8KeB6u3/JrV2Q5PxUXvUasGKDK2poK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepGLVA%2Fbtqx8KeB6u3%2FJrV2Q5PxUXvUasGKDK2poK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일본, 중국, 태국 등의 GDG Organizer 분들이 각각 스스로 발표자가 되는 방법, 커뮤니티를 만들고 운영하는 방법, 성공적인 해커톤을 만드는 방법 등을 주제로 자신들의 이야기를 공유해 주셨습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해외 운영진들 역시 저희와 비슷한 고민들을 많이 하고 있다는 것을 알 수 있었고 앞으로 GDG 활동을 하며, 행사를 만들면서 참고할만한 팁, 솔루션들을 얻을 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;vbox7225_SX9A9704_105847_small.JPG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C5sq6/btqyapmPfhm/aUAM6ZoLzuDJLrWOpFvKJK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C5sq6/btqyapmPfhm/aUAM6ZoLzuDJLrWOpFvKJK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C5sq6/btqyapmPfhm/aUAM6ZoLzuDJLrWOpFvKJK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC5sq6%2FbtqyapmPfhm%2FaUAM6ZoLzuDJLrWOpFvKJK%2Fimg.jpg&quot; data-filename=&quot;vbox7225_SX9A9704_105847_small.JPG&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한국에서는 GDG Campus Korea의 진겸님께서 &lt;a href=&quot;https://festa.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Festa&lt;/a&gt;를 주제로 발표를 하셨는데 영어를 원어민처럼 잘하셔서 놀랐습니다. 너무 재밌고 멋있게 발표하셔서 저도 영어를 제대로 공부해보고자 하는 자극을 받게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Group discussion&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라이트닝 토크가 끝나고 난 뒤 GDG 활동을 하며 생기는 다양한 고민들 중 같은 주제를 고른 사람들끼리 이야기를 나누는&amp;nbsp;Group discussion 시간이 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;제가 선택한 주제는 행사 참가자의 노쇼 비율과 행사 중간 이탈자를 줄일 수 있는 방법이였습니다. 같은 고민을 하는 9명의 운영진들과 함께 대화를 나눴는데 흥미로운 부분들이 몇가지 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DjMcu/btqyceESwAo/UQLzooCzOJtaqWNdFvay5K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DjMcu/btqyceESwAo/UQLzooCzOJtaqWNdFvay5K/img.jpg&quot; data-filename=&quot;vbox7225_SX9A0664_152022_small.JPG&quot; style=&quot;width: 58.6047%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DjMcu/btqyceESwAo/UQLzooCzOJtaqWNdFvay5K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDjMcu%2FbtqyceESwAo%2FUQLzooCzOJtaqWNdFvay5K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lFrBr/btqx9XYhHKd/IPcKIfViNxYPhl1kncF6VK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lFrBr/btqx9XYhHKd/IPcKIfViNxYPhl1kncF6VK/img.jpg&quot; data-filename=&quot;IMG_7274.JPG&quot; width=&quot;437&quot; height=&quot;437&quot; style=&quot;width: 39.0698%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lFrBr/btqx9XYhHKd/IPcKIfViNxYPhl1kncF6VK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlFrBr%2Fbtqx9XYhHKd%2FIPcKIfViNxYPhl1kncF6VK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해외 GDG 행사의 경우 유료 티켓을 받는 행사는 거의 없다고 합니다. 대부분의 한국 GDG 행사는 노쇼율을 줄이기 위해 작은 금액이라도 받는 전략을 쓰고 있는데, 해외 GDG 분들은 이 이야기를 듣고 신기해 하셨던것 같습니다. 다른 해외 챕터에서는 식사나 먹을거리가 없다면 참석자 수가 줄어든다는 웃픈 이야기도 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; height=&quot;437&quot; data-filename=&quot;blob&quot; width=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btrEAP/btqx9zw5BbB/fQ4PAuVbkx6qY23Sapapkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btrEAP/btqx9zw5BbB/fQ4PAuVbkx6qY23Sapapkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btrEAP/btqx9zw5BbB/fQ4PAuVbkx6qY23Sapapkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtrEAP%2Fbtqx9zw5BbB%2FfQ4PAuVbkx6qY23Sapapkk%2Fimg.png&quot; height=&quot;437&quot; data-filename=&quot;blob&quot; width=&quot;482&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;열심히 이야기를 나누다보니 남은 시간이 얼마 없어서 GDG Hong Kong 챕터의 Kinni Mew님이 열심히 행사 전 참석률을 높이기 위한 체크리스트를 만들어주었고, 결국 발표까지 하시게 되었습니다 ㅋㅋ&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;After Day 2&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Google Office 에서의 본 일정이 끝난 뒤에 저녁을 먹으러 식당으로 이동하였습니다. 식당은 중국 전통 음식점인 것 같았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1Nox/btqx9zYc5gQ/fzWBbBJunA7N8poKcwrxs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1Nox/btqx9zYc5gQ/fzWBbBJunA7N8poKcwrxs1/img.png&quot; data-filename=&quot;IMG_7325.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1Nox/btqx9zYc5gQ/fzWBbBJunA7N8poKcwrxs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1Nox%2Fbtqx9zYc5gQ%2FfzWBbBJunA7N8poKcwrxs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crRJhP/btqydQ4Oz9l/3n3EbVdhrsMP8dgXy4Ev31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crRJhP/btqydQ4Oz9l/3n3EbVdhrsMP8dgXy4Ev31/img.png&quot; data-filename=&quot;IMG_7318.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crRJhP/btqydQ4Oz9l/3n3EbVdhrsMP8dgXy4Ev31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrRJhP%2FbtqydQ4Oz9l%2F3n3EbVdhrsMP8dgXy4Ev31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0zV9W/btqx9XkhUza/VK4gv1PHusvECn5qZhWYv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0zV9W/btqx9XkhUza/VK4gv1PHusvECn5qZhWYv0/img.png&quot; data-filename=&quot;IMG_7340.png&quot; style=&quot;width: 31.7829%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0zV9W/btqx9XkhUza/VK4gv1PHusvECn5qZhWYv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0zV9W%2Fbtqx9XkhUza%2FVK4gv1PHusvECn5qZhWYv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회전식 테이블에 여러가지 메뉴가 있었고, 언제 끝나는지가 궁금해 질만큼 끊임없이 음식 메뉴들이 나왔습니다. 너무 배불러서 결국 중간에 포기했는데 지금와서 생각해 보니 중국의 문화는 손님이 다 먹지 못하고 남길만큼 음식을 많이 준비하는거라는 게 생각났습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웬지 다른나라의 Organizer 분들과 이야기를 나눠보고 싶어서 톈진 챕터 분들과 함께 앉아서 식사를 했습니다. 여러 중국 문화에 대한 이야기를 들을 수 있었고, 중국에서 기념으로 사갈만한 간식이 뭐가 있을까요 여쭤보니 바이두로 직접 주문해주시기 까지 하셨습니다. 너무 친절해서 죄송스러우면서도 잊지 못할 감동을 받은 순간이였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q7qEQ/btqyap1RJWg/GuCrAbqiV05Byk4ZyFJGv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7qEQ/btqyap1RJWg/GuCrAbqiV05Byk4ZyFJGv0/img.png&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7qEQ/btqyap1RJWg/GuCrAbqiV05Byk4ZyFJGv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7qEQ%2Fbtqyap1RJWg%2FGuCrAbqiV05Byk4ZyFJGv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKPxVl/btqycfqH1GF/OYvJP9ZOjqEqOtrLhT3Tpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKPxVl/btqycfqH1GF/OYvJP9ZOjqEqOtrLhT3Tpk/img.png&quot; data-filename=&quot;IMG_7391.png&quot; style=&quot;width: 48.8372%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKPxVl/btqycfqH1GF/OYvJP9ZOjqEqOtrLhT3Tpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKPxVl%2FbtqycfqH1GF%2FOYvJP9ZOjqEqOtrLhT3Tpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKxzSN/btqx81ObFyV/YWWl73eLk72VsY57c7RQr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKxzSN/btqx81ObFyV/YWWl73eLk72VsY57c7RQr1/img.png&quot; data-filename=&quot;IMG_7370.png&quot; style=&quot;width: 38.1395%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKxzSN/btqx81ObFyV/YWWl73eLk72VsY57c7RQr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKxzSN%2Fbtqx81ObFyV%2FYWWl73eLk72VsY57c7RQr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duB2vO/btqycfqHjS1/NxrOiRCWrkVS5mMixBjNMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duB2vO/btqycfqHjS1/NxrOiRCWrkVS5mMixBjNMk/img.png&quot; style=&quot;width: 28.6047%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duB2vO/btqycfqHjS1/NxrOiRCWrkVS5mMixBjNMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduB2vO%2FbtqycfqHjS1%2FNxrOiRCWrkVS5mMixBjNMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XkFIN/btqycONdeRE/vuDLHGcZovBKShgF5XsTM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XkFIN/btqycONdeRE/vuDLHGcZovBKShgF5XsTM1/img.png&quot; style=&quot;width: 28.6047%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XkFIN/btqycONdeRE/vuDLHGcZovBKShgF5XsTM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXkFIN%2FbtqycONdeRE%2FvuDLHGcZovBKShgF5XsTM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;&quot; height=&quot;&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저녁을 먹고 호텔로 이동하여 드디어 짐을 풀고 (&lt;s&gt;어차피 1박 2일이라 가방하나..&lt;/s&gt;) 밖에 나가서 현지 훠궈(?) 식당과 거리를 구경했습니다. 훠거(?)는 맵지만 맛있었고 편의점에는 역시나 한국어가 있는 혹은 한국에서 넘어온 음식들이 많이 진열되어 있어서 반가웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마치며&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사실 그동&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;안 자의든 타의든 다른 GDG Organizer 분들과는 교류할 기회가 적어서 많이 아쉬웠는데 이번 기회를 통해 이야기와 고민들을 나눌 수 있지 않을까? 라는 막연한 기대를 가지고 가게 되었습니다. 물론 아쉽게 다 해소하지 못한 부분들도 있지만 짧은 기간동안 많은 것들을 배우고 경험하고 왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무엇보다 앞으로 GDG 활동을 더 열심히, 더 잘해봐야겠다 라는 생각과 여러 가지 시각에서 자극을 많이 받은 시간이였습니다. 앞으로 진행할 이벤트에 도입해 보고 싶은 팁과 아이디어도 몇가지 얻어와서 바로 다음 행사부터 써먹어 보려고 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 갑작스럽게 서울로 이직하게 되어 앞으로 GDG Daejeon 활동을 유지할 수 있을지가 고민되기도 합니다. 가능하다면 대전 챕터에&amp;nbsp;새로운 운영진 분들을 모시고&amp;nbsp;제가 빠져도 안정적으로 유지 될 수 있게 만들어서 다른 챕터로 옮길 수 있다면 가장 좋지 않을까 생각합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>잡담</category>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/422</guid>
      <comments>https://boxfoxs.tistory.com/422#entry422comment</comments>
      <pubDate>Tue, 10 Sep 2019 22:05:17 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 좋은 테스트 작성법 1-2</title>
      <link>https://boxfoxs.tistory.com/420</link>
      <description>&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 글은 github&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/goldbergyoni&quot;&gt;goldbergyoni&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;님의&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices#section-0%EF%B8%8F%E2%83%A3-the-golden-rule&quot;&gt;javascript-testing-best-practices&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;를 번역하여 작성한 글 입니다. 번역 과정중 오류가 있을 수 있으니 댓글을 통해 피드백 주시면 감사하겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;적절하게 테스트 더블을 사용하라&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.nhn?blogId=dragmove&amp;amp;logNo=220199498169&amp;amp;proxyReferer=https%3A%2F%2Fwww.google.com%2F&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;테스트 더블&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;테스트 코드를 편하게 작성할 할 수 있도록 해주는 도구 입니다. 하지만&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;어플리케이션 내부 구현 코드와 커플링 된다는 문제가 있기 때문에&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;남용하는 것은 권장하지 않습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 더블을 적절하게 사용하려면&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;요구사항 기능을 테스트하는대에 사용하는지&lt;/b&gt;를 고려하면 됩니다.&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 요구사항 기능을 테스트하는대에 사용하는게 아니라면 해당 테스트는 화이트박스 테스트일 가능성이 높습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어, 결제 서비스가 중단되었을 때 앱이 합리적으로 동작하는지를 테스트하기 위해 결제 서비스를 stub 하고 '응답 없음'을 반환하게 하거나.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;spy 를 사용하여 해당 서비스가 중단되었을 때 메일 전송을 시도하는지를 테스트하는 것은&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요구사항 기능을 테스트하는 것이기 때문에 올바르게 테스트 더블을 사용한 경우 입니다. (&quot;결제를 저장할 수 없는 경우 메일 발송&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 요구사항과 관련 없이 특정 메소드가 정확한 파라미터로 어떻게 호출되었는지를 검사하는 경우 바람직하지 못한 사용예 입니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 더블을 남용하는 경우 프로덕션 코드가 변경될 때 마다 테스트 코드도 변경해 주어야 할 가능성이 높기 때문에 테스트 코드가 오히려 짐이되는 상황이 발생하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;권장하지 않는 예제&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유효한 product 가 제거되었을 때 DAL의 메소드 올바른 파라미터로 호출되었는지를 검증하는 테스트 코드입니다. 이는 요구사항과는 전혀 상관 없이 테스트 더블을 사용하였기 때문에 권장하지 않는 예제 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567430753906&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config&quot;, async () =&amp;gt; {
    //Assume we already added a product
    const dataAccessMock = sinon.mock(DAL);
    //hmmm BAD: testing the internals is actually our main goal here, not just a side-effect
    dataAccessMock.expects(&quot;deleteProduct&quot;).once().withArgs(DBConfig, theProductWeJustAdded, true, false);
    new ProductService().deletePrice(theProductWeJustAdded);
    dataAccessMock.verify();
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;권장하는 예제&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유효한 product 가 제거되었을 때 이메일을 전송하는지를 검증하는 테스트 코드 입니다. 요구사항 기능을 테스트 하는 것에 집중한 코드이지만, 불가피하게 Emailer의 sendEmail 메소드 인터페이스에 의존을 가지고 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567430844227&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;When a valid product is about to be deleted, ensure an email is sent&quot;, async () =&amp;gt; {
    //Assume we already added here a product
    const spy = sinon.spy(Emailer.prototype, &quot;sendEmail&quot;);
    new ProductService().deletePrice(theProductWeJustAdded);
    //hmmm OK: we deal with internals? Yes, but as a side effect of testing the requirements (sending an email)
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;현실적인 입력 데이터를 사용하라&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로덕션 코드에서는 특별하거나 특이한 입력 데이터로 인해 버그가 들어나는 경우가 종종 발생합니다. 테스트 코드를 작성할 때 일반적인, 이상적인 데이터만을 이용하여 테스트하면 테스트는 쉽게 통과 합니다. 하지만 실제 제품에서 사용자의 실수나 해커에 의해 비정상적인 값이 입력되면 버그가 발생할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 테스트할 때 사용하는 입력 데이터를 현실적인 값으로 사용할 수록 버그나 취약점을 조기에 발견할 가능성이 높아집니다.&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://www.npmjs.com/package/faker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Facker&lt;/a&gt; &lt;span style=&quot;color: #000000;&quot;&gt;와 같은 라이브러리를 사용해서 실제 입력 데이터와 유사한 다양한 데이터를 생성할 수 있습니다. 간단한 예로 Facker 는 전화번호, 사용자 이름, 카드 번호, 회사 이름,&lt;/span&gt; '&lt;a href=&quot;https://brunch.co.kr/@forchoon/23&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lorem ipsum&lt;/a&gt;' &lt;span style=&quot;color: #000000;&quot;&gt;텍스트 등 현실적인 데이터를 생성할 수 있습니다. 또한 입력 데이터를 랜덤화 하여 테스트하는 unit을 늘릴 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하지 않는 예제&lt;/b&gt; : 비현실적인 데이터로 인해 쉽게 통과하는 테스트 케이스&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567520879921&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const addProduct = (name, price) =&amp;gt;{
  const productNameRegexNoSpace = /^\S*$/;//no white-space allowd

  if(!productNameRegexNoSpace.test(name))
    return false;//this path never reached due to dull input

    //some logic here
    return true;
};

test(&quot;Wrong: When adding new product with valid properties, get successful confirmation&quot;, async () =&amp;gt; {
    //The string &quot;Foo&quot; which is used in all tests never triggers a false result
    const addProductResult = addProduct(&quot;Foo&quot;, 5);
    expect(addProductResult).toBe(true);
    //Positive-false: the operation succeeded because we never tried with long
    //product name including spaces
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하는 예제&lt;/b&gt; : 현실적인 랜덤 입력 데이터&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567520909572&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;Better: When adding new valid product, get successful confirmation&quot;, async () =&amp;gt; {
    const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
    //Generated random input: {'Sleek Cotton Computer',  85481}
    expect(addProductResult).to.be.true;
    //Test failed, the random input triggered some path we never planned for.
    //We discovered a bug early!
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Property 기반 테스트로 많은 입력 조합을 테스트&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반적으로 테스트 코드를 작성할 때 입력하는 데이터 샘플은&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;매우 적은 양으로 구성합니다&lt;/span&gt;. 이렇게 적은 양의 입력자료 샘플은 입력자료를 겨우 몇가지 조합만을 테스트 하게 됩니다.&amp;nbsp;(method(&amp;lsquo;&amp;rsquo;,&amp;nbsp;true,&amp;nbsp;1),&amp;nbsp;method(&amp;ldquo;string&amp;rdquo;&amp;nbsp;,&amp;nbsp;false&amp;rdquo;&amp;nbsp;,&amp;nbsp;0))&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 실제 소프트웨어 제품에서는 API가 5개의 파라미터만 입력받더라도 수천 가지의 조합으로 코드가 수행될 수 있습니다. 또한 이러한 수천 가지의 조합중 어떤 조건이 버그를 발생시키는지 알 수 없습니다.&lt;/span&gt; (&lt;a href=&quot;https://en.wikipedia.org/wiki/Fuzzing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Fuzz Testing&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;property-based testing 는&amp;nbsp;&lt;/span&gt;하나의 테스트 코드만 작성하고도 수천 가지 조합의 파라미터를 자동으로 입력한 뒤 올바른 결과를 받는지를 검증할 수 있습니다. 또한 이를 통해&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;발견 가능한 버그의 범위를 넓힐 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;대표적인 property-based testing 라이브러리는 &lt;a href=&quot;https://github.com/jsverify/jsverify&quot;&gt;js-verify&lt;/a&gt;, &lt;a href=&quot;https://github.com/leebyron/testcheck-js&quot;&gt;testcheck-js&lt;/a&gt;, &lt;a href=&quot;https://github.com/dubzzz/fast-check#readme&quot;&gt;fast-check&lt;/a&gt;&amp;nbsp;등이 있으며 jest. mocha 등 테스트 프레임워크에 상관 없이 사용할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;b&gt;권장하는 예제&lt;/b&gt; :&amp;nbsp;mocha-testcheck 를 사용한 테스트&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567522556956&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;require('mocha-testcheck').install();
const {expect} = require('chai');

describe('Product service', () =&amp;gt; {
  describe('Adding new', () =&amp;gt; {
    //this will run 100 times with different random properties
    check.it('Add new product with random yet valid properties, always successful',
      gen.int, gen.string, (id, name) =&amp;gt; {
        expect(addNewProduct(id, name).status).to.equal('approved');
      });
  })
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참고자료&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1567430939867&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://blog.naver.com/dragmove/220199498169&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://m.blog.naver.com/PostView.nhn?blogId=dragmove&amp;amp;logNo=220199498169&amp;amp;proxyReferer=https%3A%2F%2Fwww.google.com%2F&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b1Zs0Q/hyCG5P8ir9/OVR1iDhyDZmfQrOkfUhk20/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;테스트 더블(test double)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;# 테스트 더블(test double) + 의미테스트 작성시, 테스트 대상 코드와 상호작용하는 객체 + 역할테스트 대...&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1567599890463&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Ia3Kg/hyCIQyxAPI/QU5drwSM3zO7nkkOKKz7sk/img.jpg?width=400&amp;amp;height=400&amp;amp;face=178_40_242_110,https://scrap.kakaocdn.net/dn/34wdd/hyCIFjtAaa/KRNgnMvwEjzfM49KBOQntK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;    Comprehensive and exhaustive JavaScript &amp;amp; Node.js testing best practices (August 2019) - goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/420</guid>
      <comments>https://boxfoxs.tistory.com/420#entry420comment</comments>
      <pubDate>Wed, 4 Sep 2019 21:27:30 +0900</pubDate>
    </item>
    <item>
      <title>Javascript 좋은 테스트 작성법 1-1</title>
      <link>https://boxfoxs.tistory.com/417</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 글은 github&lt;/span&gt; &lt;a href=&quot;https://github.com/goldbergyoni&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;goldbergyoni&lt;/a&gt; &lt;span style=&quot;color: #000000;&quot;&gt;님의&lt;/span&gt; &lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices#section-0%EF%B8%8F%E2%83%A3-the-golden-rule&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;javascript-testing-best-practices&lt;/a&gt;&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;를 번역하여 작성한 글 입니다. 번역 과정중 오류가 있을 수 있으니 댓글을 통해 피드백 주시면 감사하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;들어가며&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우리는 평소에 소프트웨어 구현만을 위한 코드(Production code)만을 신경쓰고 있습니다. 따라서 기존에 익숙하지 않은 테스트 코드를 작성하는 대에 어려움과 거부감을 느끼게 되고 많은 분들이 포기하게 됩니다. 하지만 익숙하지 않음에도 불구하고 테스트 코드에 대한 작은 투자는 큰 가치를 얻을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 과한 테스트보다는 필요한 만큼만 테스트하고, 빠른 테스트를 유지하기 위해 노력해야 합니다. 때로는 민첩성과 단순성을 위해 테스트를 포기하고&amp;nbsp;신뢰성을 낮추는게 더 가치 있을 때도 있습니다. 해당 포스팅에서는 테스트를 잘 작성하는 방법과 잘 작성된 테스트 코드들을 소개합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트의 3 요건&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 수행 결과는 코드가 요구사항을 만족하는지 알려주어야 합니다. 이는 테스트를 3가지 구성요소로 작성한다면 가장 잘 표현할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 무엇을 테스트 하는가?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 어떤 상황과 어떤 시나리오에서?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3.예상되는 결과는?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566828756939&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//1. unit under test
describe('Products Service', function() {
  describe('Add new product', function() {
    //2. scenario and 3. expectation
    it('When no price is specified, then the product status is pending approval', ()=&amp;gt; {
      const newProduct = new ProductService().add(...);
      expect(newProduct.status).to.equal('pendingApproval');
    });
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 구조 디자인 : AAA 패턴&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트를 작성할 때 Arrange, Act, Assert (AAA) 3가지 섹션으로 나누어 디자인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 234px;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 78px;&quot;&gt;
&lt;td style=&quot;width: 9.53482%; height: 78px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Arrange&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.4652%; height: 78px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;테스트를 위해 필요한 객체들을 생성, 설정 하는 등 테스트를 위한 조건을 예열하는 코드를 말합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;여기에는 테스트 대상의 인스턴스 생성, DB 레코드 추가, mocking / stubbing 등의 준비 코드가 포함됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 78px;&quot;&gt;
&lt;td style=&quot;width: 9.53482%; height: 78px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Act&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.4652%; height: 78px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;테스트중인 대상을 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;일반적으로 한 줄의 코드로 이루어집니다.&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 78px;&quot;&gt;
&lt;td style=&quot;width: 9.53482%; height: 78px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Assert&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.4652%; height: 78px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;Act 를 통해 수행하여 반환 받은 값이 기대치를 충족하는지 확인하는 섹션입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;이 역시&amp;nbsp;일반적으로 한 줄의 코드로 이루어집니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1566916263419&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('Customer classifier', () =&amp;gt; {
    test('When customer spent more than 500$, should be classified as premium', () =&amp;gt; {
        //Arrange
        const customerToClassify = {spent:505, joined: new Date(), id:1}
        const DBStub = sinon.stub(dataAccess, &quot;getCustomer&quot;)
            .reply({id:1, classification: 'regular'});

        //Act
        const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);

        //Assert
        expect(receivedClassification).toMatch('premium');
    });
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BDD style assertions&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드를 선언적 스타일로 코딩하면 해당 코드를 읽는 사람이 의도를 파악하기 매우 쉽습니다. 테스트 코드의 요구사항을&amp;nbsp;조건문으로 분기하여&amp;nbsp;작성하면 이를 이해하기 위해 비교적 더 많은 시간이 필요합니다. 따라서&amp;nbsp;요구사항을&amp;nbsp;인간의 언어에 친화적인 선언형 BDD style 로 작성하는것이 좋습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Javascript Test Framework인 Jest의 경우 BDD style로 작성할 수 있도록 expect API 를 제공하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;권장하지 않는 예제&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567341819203&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;When asking for an admin, ensure only ordered admins in results&quot; , () =&amp;gt; {
    //assuming we've added here two admins &quot;admin1&quot;, &quot;admin2&quot; and &quot;user1&quot;
    const allAdmins = getUsers({adminOnly:true});

    const admin1Found, adming2Found = false;

    allAdmins.forEach(aSingleUser =&amp;gt; {
        if(aSingleUser === &quot;user1&quot;){
            assert.notEqual(aSingleUser, &quot;user1&quot;, &quot;A user was found and not admin&quot;);
        }
        if(aSingleUser===&quot;admin1&quot;){
            admin1Found = true;
        }
        if(aSingleUser===&quot;admin2&quot;){
            admin2Found = true;
        }
    });

    if(!admin1Found || !admin2Found ){
        throw new Error(&quot;Not all admins were returned&quot;);
    }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;권장하는 예제&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567341831548&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;it(&quot;When asking for an admin, ensure only ordered admins in results&quot; , () =&amp;gt; {
    //assuming we've added here two admins
    const allAdmins = getUsers({adminOnly:true});

    expect(allAdmins).to.include.ordered.members([&quot;admin1&quot; , &quot;admin2&quot;])
  .but.not.include.ordered.members([&quot;user1&quot;]);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;블랙박스 테스트 : public 메소드만 테스트 하라&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드의 내부를 테스트하는 것은 큰 오버헤드를 가져옵니다. 내부 동작까지 테스트하는 코드는 깨지기 쉽고&amp;nbsp;올바르게 동작하고 올바른 결과를 반환함에도 불구하고 내부적으로 어떻게 동작하는지를 테스트해야 합니다. 따라서 테스트를 작성하고 유지하는 대에 많은 시간을 소비하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 외부에 공개되는 public 메소드들만 테스트 하는 것을 권장합니다. public 메소드를 테스트할 때 마다 암묵적으로 private 구현체들까지 테스트되기 때문에 크리티컬한 문제가 있을 때에만 테스트가 실패하게 됩니다. 이러한 접근법은 행위, 행동 테스트라고도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반대로 white box 접근법(테스트)의 경우에는&amp;nbsp;반드시 내부를 테스트해야 합니다.&amp;nbsp;이는 컴포넌트의 결과를 계획하는 것이 아닌 세부적인 동작 과정에 초점을 맞춰 테스트코드를 작성하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;권장하지 않는 예제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래 코드는 이유없이 내부 함수를 테스트하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1567425650947&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ProductService{
  //this method is only used internally
  //Change this name will make the tests fail
  calculateVAT(priceWithoutVAT){
    return {finalPrice: priceWithoutVAT * 1.2};
    //Change the result format or key name above will make the tests fail
  }
  //public method
  getPrice(productId){
    const desiredProduct= DB.getProduct(productId);
    finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
  }
}


it(&quot;White-box test: When the internal methods get 0 vat, it return 0 response&quot;, async () =&amp;gt; {
    //There's no requirement to allow users to calculate the VAT, only show the final price. Nevertheless we falsely insist here to test the class internals
    expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원문 출처&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1567426312970&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://github.com/goldbergyoni/javascript-testing-best-practices#section-0%EF%B8%8F%E2%83%A3-the-golden-rule&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/doEnuh/hyCHfrApVr/t2JLkyyJgWfS4G6GZVD2v0/img.jpg?width=400&amp;amp;height=400&amp;amp;face=178_40_242_110,https://scrap.kakaocdn.net/dn/bFfcCT/hyCG779yiL/TrTVzjyYDQkT9wO0kqKMy0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;    Comprehensive and exhaustive JavaScript &amp;amp; Node.js testing best practices (August 2019) - goldbergyoni/javascript-testing-best-practices&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>프로그래밍</category>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/417</guid>
      <comments>https://boxfoxs.tistory.com/417#entry417comment</comments>
      <pubDate>Mon, 2 Sep 2019 21:13:07 +0900</pubDate>
    </item>
    <item>
      <title>웹사이트 스크래핑 라이브러리 Puppeteer 소개</title>
      <link>https://boxfoxs.tistory.com/418</link>
      <description>&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;해당 포스트는 Puppeteer 에 대해 학습하기 위해 puppeteer 의 영어 문서를 토대로 부가적인 설명과 간단한 puppeteer의 사용 방법을 소개합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000;&quot;&gt;Puppeteer&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Puppeteer는 &lt;a href=&quot;https://chromedevtools.github.io/devtools-protocol/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DevTools Protocol&lt;/a&gt;을 통해 Chrome 또는 Chromium을 제어하기 위한 API를 제공하는 node.js 라이브러리 입니다. 다양한 기능이 있지만 가장 큰 특징은 Headless로 실행될 수 있다는 점 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;puppeteer 문서에서 소개되는 대표적인 기능들은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;1. SPA(Single-Page Application)를 크롤링하고 미리 렌더링된 컨텐츠 (SSR - Server Side Rendering)를 생성 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;2. 최신 자동 테스트 환경을 만듭니다. JavaScript 테스트를 최신 버전의 Chrome에서 직접 수행할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;3. 사이트의 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;timeline trace&lt;/a&gt; 를 기록하여 성능 문제를 진단할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4.&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;Form Submit, UI 테스트, 키보드 입력 등을 자동화 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 웹 페이지의 스크린샷, PDF를 생성할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;6. Chrome Extensions을 테스트 할 수 있습니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;636&quot; height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE7oBc/btqxtVULIeG/Pa3tKuQCzkuO2yuknemrS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE7oBc/btqxtVULIeG/Pa3tKuQCzkuO2yuknemrS1/img.png&quot; data-alt=&quot;puppeteer 구조도 :&amp;amp;amp;nbsp;http://bit.ly/3024OZC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE7oBc/btqxtVULIeG/Pa3tKuQCzkuO2yuknemrS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE7oBc%2FbtqxtVULIeG%2FPa3tKuQCzkuO2yuknemrS1%2Fimg.png&quot; width=&quot;636&quot; height=&quot;622&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;puppeteer 구조도 :&amp;nbsp;http://bit.ly/3024OZC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Puppeteer는 Google Chrome 개발팀에서 직접 개발하였습니다. 때문에 Chrome 브라우저 환경만을 지원한다는 단점이 있습니다. 하지만 그만큼 Chrome 브라우저에 대한 제어 기능이 많고 조합이 좋으며 간단하게 설정하고 사용할 수 있다는 장점이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000;&quot;&gt;Headless란?&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;그럼 puppeteer에서 강조하는 headless란 무엇일까요? 쉽게 말하면 브라우저가 사용자의 눈에 보여지지 않고 백그라운드 환경에서 작동하는 것을 의미합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;실제 브라우저 환경에서 Javascript 코드를 테스트 하고 싶을 때 실제 브라우저 창을 띄워야 한다면 테스트 운영체제에 대한 제약이 많아집니다. 예를들어 우분투 서버와 같은 GUI가 없는 OS나 GPU가 없는 데스크탑의 경우 해당 테스트를 수행해볼 수 없습니다. 때문에 headless 모드가 이를 해결해 줄 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;실제 창을 띄우지 않고도 실제 브라우저에서 수행되는 렌더링 과정을&amp;nbsp;가상으로&amp;nbsp;수행하기 때문에 실제 브라우저와 동일하게 동작하지만 '창'이 띄워지지 않는 방식으로 동작 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000;&quot;&gt;Puppeteer VS Selenium&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;웹 스크래핑 라이브러리를 찾으면 가장 대표적으로 puppeteer와 selenium 이 추천되고 있습니다. 이 두 가지 중 어떤 라이브러리를 사용해야 하는지 결정하기 위해 장단점을 간단하게 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 48px;&quot; border=&quot;1&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 11.3953%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;라이브러리&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.1396%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Puppeteer&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.4651%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Selenium&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 11.3953%; height: 10px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;장점&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.1396%; height: 10px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 크롬 브라우저에 대한 강력한 기능 제공&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 스크린샷 테스트&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 웹 페이지 성능 측정 및 프로파일링&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Selenium 에 비해 빠름&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 45.4651%; height: 10px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- JavaScript 외 다양한 언어 지원&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 크롬 외에 테스트 환경에 설치된 다양한 브라우저 지원&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Android, IOS 등 모바일 환경 테스트&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- puppeteer 보다 많은 사용자 커뮤니티&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 11.3953%; height: 19px; text-align: center;&quot;&gt;단점&lt;/td&gt;
&lt;td style=&quot;width: 43.1396%; height: 19px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- JavaScript 만 지원&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- Chrome, Chromium 만 지원&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 45.4651%; height: 19px;&quot;&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- puppeteer에 비해 느린 속도&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- puppeteer에 비해 브라우저 제어 기능이 적음&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- puppeteer에 비해 복잡한 설정&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단한 기준을 제시하자면, JavaScript에서 Chrome 환경만을 테스트 할때 Puppeteer를, 다양한 언어에서 여러 플랫폼 환경을 테스트하고 싶다면 Selenium을 선택하면 될것 같습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Puppeteer 사용해보기&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;npm 또는 yarn을 사용하여 쉽게 모듈을 설치할 수 있습니다. puppeteer를 설치하게 되면 puppeteer와 호환되는 가장 최신 버전의 Chromium도 함께 설치됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565793254157&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i puppeteer
 # 또는 &quot;yarn add puppeteer&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;만약 Chromium을 설치하고 싶지 않다면 기존에 설치된 브라우저를 사용하는 방법도 있습니다. puppeteer-core를 설치하면 되는데요, 이 때 주의해야 할 점은 기존에 설치했던 Chrome 버전이 설치한 puppeteer-core와 호환되는지를 확인해야 합니다. (&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteer-vs-puppeteer-core&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;puppeteer vs puppeteer-core&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565793389375&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i puppeteer-core
 # 또는 &quot;yarn add puppeteer-core&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이렇게 모듈을 설치했다면 본격적으로 puppeteer를 사용해 보겠습니다. 아래 예제는 puppeteer를 사용하여 google에서 '박스여우' 키워드를 검색해서 나오는 링크 텍스트들을 가져오는 코드 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565843373649&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('https://google.com'); // 구글 검색창 이동
  await page.type('input[class=&quot;gLFyf gsfi&quot;]', '박스여우'); // 박스여우 검색어 입력
  await page.type('input[class=&quot;gLFyf gsfi&quot;]', String.fromCharCode(13));  // 엔터키를 입력하여 검색 수행
  await page.waitForSelector('a h3'); // a 태그 밑, h3태그 가 존재하는 컨텐츠가 로드될 때 까지 대기
  const links = await page.evaluate(() =&amp;gt; {
    return Array.from(document.querySelectorAll('a h3')).map(h3 =&amp;gt; (h3.textContent)); // h3태그 의 text 가져옴
  });
  console.log(links.join('\n'));  //콘솔에 출력
  browser.close();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 예제에서 browser와 page 객체를 만들고 goto api를 통해 google.com 사이트에 접속합니다. 그리고 검색어 입력창에 '박스여우'와 검색을 수행하기 위한 엔터 키를 입력합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그러면 검색이 진행되고 페이지가 다시 로드될 것 입니다. 이 때 검색이 완료될 때 까지 대기하기 위해 waitForSelector API를 사용해 Selector에 해당되는 컨텐츠가 로드될 때 까지 기다립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;result.png&quot; width=&quot;506&quot; height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9Zxie/btqxvMoFVMS/0zJkdTZHbk4kRLO4UgVUF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9Zxie/btqxvMoFVMS/0zJkdTZHbk4kRLO4UgVUF1/img.png&quot; data-alt=&quot;예제를 통해 캡처된 &amp;amp;#39;박스여우&amp;amp;#39; 검색 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9Zxie/btqxvMoFVMS/0zJkdTZHbk4kRLO4UgVUF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9Zxie%2FbtqxvMoFVMS%2F0zJkdTZHbk4kRLO4UgVUF1%2Fimg.png&quot; data-filename=&quot;result.png&quot; width=&quot;506&quot; height=&quot;380&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제를 통해 캡처된 '박스여우' 검색 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;그 이후 스크린샷을 찍어 저장한 후 a 태그 하위에 존재하는 h3 태그를 탐색하여 그 안의 텍스트 컨텐츠들을 가져옵니다. 최종적으로 콘솔에 출력되는 결과는 아래와 같습니다. 검색 결과는&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;검색 엔진에의해 달라질 수 있기 때문에 출력 결과가 다르게 나올 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565843991360&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예제 수행 결과
박스여우 - BoxFox
BoxFox - 박스여우 - YouTube
[닫음]여우박스  (@FoX___B0X) | Twitter
#여우박스 hashtag on Twitter
여우의 전화 박스 - YES24
골골박스 여우 - 모모제리
여우의 전화박스 : 네이버 블로그
[여우의 전화박스]를 읽고
동화와 어린이
finish&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고 자료&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;hr&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;figure id=&quot;og_1565844168117&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-original-url=&quot;https://github.com/GoogleChrome/puppeteer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMKtVG/hyCrnJ3qeX/zEiLhabmUfKqnpUhBKKmd0/img.png?width=192&amp;amp;height=192&amp;amp;face=0_0_192_192');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;GoogleChrome/puppeteer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Headless Chrome Node API. Contribute to GoogleChrome/puppeteer development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>프로그래밍</category>
      <author>박스여우</author>
      <guid isPermaLink="true">https://boxfoxs.tistory.com/418</guid>
      <comments>https://boxfoxs.tistory.com/418#entry418comment</comments>
      <pubDate>Thu, 15 Aug 2019 13:56:14 +0900</pubDate>
    </item>
  </channel>
</rss>