The Ad Fraud Detection Guide

We (Moloco, Inc.) published a guide for detecting bad ad networks with Cross Install. As the online ad industry is getting complicated, the ad fraud is prevailing everywhere (REALLY!!!). This article is a good summary of the contemporary ad fraud, and it would be a good starting point if you want to know about it.

If you want to know more about it, our series of Medium articles will be helpful:
“Bad Ad Networks Part 1: The “Axis of Evil” in Mobile Ad Fraud”
“Bad Ad Networks Part 2: Easy Checks With Your MMP”
“Bad Ad Networks Part 3: Fighting at the Speed of Fraud”
“DoubleCheck for Ad Fraud”
“Mobile Ad Fraud Taxonomy”

life, tech

F8 2018

Weeks ago, I attended F8 2008. F8 is the tech conference of Facebook. In the tech world, it is usual to say a long word with the number of characters: F8 = Facebook. I remember the days F8 was pronounced as [feit], but everyone now calls it just [F-eight].

It is my third time of F8; I have been there 2010, 2011, which was a long time ago. Comparing to other developer conferences of other tech giants (e.g., WWDC, Google I/O and so on), F8 has been a small conference. It has been only one or two days event and space was limited. However, from the last year, it has been held at a proper place of the host’s power: San Jose Convention Center.

No over-night line for F8.

For a long time, the sanctuary of developer conferences has been Moscone Center, San Francisco. My last tech conference at San Jose Convention Center was GDC 2002. However, San Jose Convention center regains its glory days with coming back of WWDC. I’m not sure why they are returning. I prefer Moscone. Everything is convenient to access. Everywhere is within minutes of walks. However, it is true that the area is too expensive in these days for both attendees and the host.

The food was excellent with a lot of hot food selections. It is hard to prepare warm food for thousands of attendees, but the choice was excellent. Not only always-filled quality caffeine (I hate the ‘only decaf available’ time) in tank dispensers, but there were also many experienced baristas, and I can enjoy lovely lattes. The official after party was excellent (as usual for any tech-giant conference) with the Grammy-nominated artist Logic.

demo booths, short sessions, AR scavenger, flooding food
The after party

Several weeks already past and I don’t want to repeat all announcements here. I expected the mood of the event somewhat gloomy due to the Cambridge Analytica issue, but it was a happy event. I want to mention two things.

VR in Facebook is serious

Against other companies, I always feel that there is a kind of ‘grit’ in Facebook. My favorite was Occuls Go. Not because I had my free one, but I was impressed with their milestones and their intention to realize the dream. While many investors get pessimistic in these days, I felt that they have a clear goal to achieve, a detailed plan to get there, and the potential (and budget) to realize it.

The other thing was that they never talked about the money, their earning. Facebook has a robust business model: ad. Although there were some booths about Facebook ads, it was constrained and only for platform developers. They were trying to say just about how they can make more value to their users: the new features. To me, it was their confidence that they can make more money only if they can satisfy their users. And this is also the mission of my company; to let people concentrate on the value of their service without worrying about the monetization.

P.S.: To Apple. Although I couldn’t be selected for WWDC this year, please get back toasters for breakfast. The hard and cold bagel is too miserable and not proper to the name of the Apple conference.


Javascript this

1. What is ‘this’?

마지막으로 JS의 ‘this’에 대해서 이야기 해 보고자 한다. 많은 사람들이 JS에서 this를 어려워하는 것은 기본적으로 다른 class/instance개념 기반 언어들의 개념으로 this를 생각하기 때문이다.

var MyFunction = function() {
    this.a = 1;
> MyFunction.a

기존 class/instance형 언어의 개념으로 생각해 보면 MyFunction은 ‘선언’만 된 것이고 MyFunction은 생성이 안된 클래스로 생각된다. (아니라는 것은 첫번째 글에서 설명했다. MyFunction은 엄연한 이 상태로 객체이다.)

앞에서 MyFunction은 저 상태로 객체가 생성은 되지만 함수가 호출은 되지 않는다고 했다. 그렇다면 혹시 MyFunction(); 으로 함수를 호출하면 객체가 생성될지도 모르겠다.

var MyFunction = function() {
    this.a = 1;
> MyFunction.a
undefined       // oops.. it is still undefined...

하지만 MyFunction을 호출하더라도 (분명히 호출했다) MyFunction에 a라는 프로퍼티는 존재하지 않는다. 그렇다면 도대체 a는 어디로 간 것일까.

위와 같은 사고의 흐름이 흔히 JS에서 this를 접하는 경우에 겪게되는 사항이다. 일단 사고의 방식을 JS에서는 바꾸어야 한다. 우선 다음의 3가지만 기억하자.

  • JS에서 this는 어떤 객체를 가르키는 것은 맞다.
  • JS에서 this는 함수 안에서만 의미를 가진다.
  • 하지만 this는 어떤 함수에 종속되는 값이 아니며, this가 어떤 값을 가지는지는 그 함수가 ’호출될 때’ 결정된다. (이게 중요한 개념이다!)

2. Call-site

콜스택(call-stack)은 많이 들어보았지만 콜사이트(call-site)는 생소한 사람들도 있을 것이다. call-site는 간단한 개념으로 해당 콜스택에 들어오기 전의 call을 한 곳을 말한다. 다음의 예를 보자

function A() {
    console.log("Hello, I am A");
    B();    // call-site for B() is A()

function B() {
    console.log("Hello, I am B");
    C();    // call-site for C() is B()

function C() {
    console.log("Hello, I am C");

A();    // what is call-site for A(); ?

함수 A는 B를, B는 C를 부른다. B가 호출될 때의 즉, B의 call-stack에 들어와 있을 때의 call-site는 A의 호출지점이 된다. 같은 식으로 C에 들어와 있을 때는 B가 call-site가 될 것이다. 어떤 함수의 this는 다른 조건에 해당하지 않는다면 (하지만 언제나 그렇듯이 이 조건이 매우 복잡해지는게 문제이다) 해당 함수가 호출될 때의 call-site의 값을 가진다. 이것을 기본 바인딩 (Default Binding)이라고 한다. 그렇다면 위에서 A가 호출될 때의 call-site는 무엇일까?

3. Default Binding

위의 질문에 답을 찾기 위해 다음의 코드를 실행해 보자.

function foo() {
foo();      // undefined

var a = 2;
foo();      // 2

처음 foo()함수를 실행시킬 때는 this.a 는 undefined였다. 전역 변수로 a = 2를 선언한 뒤에 다시 foo()를 실행하니 값이 2임을 알 수 있다. foo()는 전역 scope에서 실행시켰기 때문에 call-site는 전역객체가 되는 것이다. 이에 foo()가 실행될 때 this는 전역객체가 된다.

만일 strict mode에서 실행중이라면 this는 전역객체가 되지 않고 TypeError가 발생한다.

이와 같이 다른 조건이 없다면 어떤 함수가 ’호출될 때’ this는 해당 함수 호출의 call-site (함수라면 call-site의 this값이라고도 생각할 수 있다.)의 값으로 셋팅된다. 다시말해 this는 함수에 고정되어 있는 값이 아니라 함수가 호출될 때 마다 그때그때 바뀔 수 있는 값이다.

하지만 this가 결정되는 방법은 이 Default Binding말고도 3가지가 더 있어 다음과 같은 4가지 경우가 있다.

  • Default Binding
  • Implicit Binding
  • Explicit Binding
  • ‘new’ Binding

이 총 4가지의 우선 순위에 따라 최종적으로 this가 어떻게 결정될지가 정해진다.

4. Implicit Binding

그런데 call-site가 어떤 함수의 call 위치가 아니라 객체 자체가 될 수도 있다. 다음의 예를 보자.

function foo() {

var obj = {
    foo: foo
};          // 3

위에서 객체 obj는 함수 foo를 그 자신의 foo라는 이름의 프로퍼티로 가지고 있다. 이 때 호출은 obj라는 객체를 call-site의 context로 가지게 된다. 이 경우 foo의 this는 해당 context가 되며 이를 암묵적 바인딩(Implicit Binding)이라고 한다.

하지만 매우 실수하기 쉬운 함정이 여기에는 하나가 있다. 역시 다음의 코드를 보자.

function foo() {

var obj = {
    foo: foo

var bar =;

bar();          // undefined???

위의 코드에서 bar()를 실행하면 기대와 달리 undefined가 나오는 것을 볼 수 있다. 자세히 보면 obj.foo라고 표시를 했지만 이 값은 그냥 함수 foo를 가리키는 것일 뿐이다. 따라서 다른 변수 bar에 obj.foo를 할당하는 것은 그냥 함수 foo의 또 다른 레퍼런스를 할당하는 것 이상도 이하도 아니다. 다시 말하지만 this가 결정되는 것은 해당 함수가 ’호출되는 순간’이다. obj.foo는 그냥 함수의 레퍼런스,는 obj를 컨텍스트로 하는 ‘함수의 호출’로 분명히 둘은 다르다. 이와 같은 실수는 실제로 복잡한 코드를 작성하다 보면 매우 쉽게 나올 수 있다. 이를 Implicit Lost라고도 한다.

Implicit Lost가 발생하는 대표적인 경우가 함수를 callback함수의 인자로 넘겼을 때다. 위의 obj.foo를 다음과 같이 setTimeout에 인자로 넘긴다고 생각해 보자.

setTimeout(, 100)

콜백으로 넘긴 obj.foo가 실행될 때 this가 obj로 셋팅될거라 기대하기 쉽지만 콜백으로 넘긴 obj.foo는 그냥 foo함수의 레퍼런스일 뿐이다. 해당 콜백이 불릴때 this는 그냥 전역객체가 된다. 심지어 콜백함수를 받는 많은 함수들 중에는 자기가 맘대로 callback의 this를 강제로 셋팅하는 경우도 많기 때문에 항상 레퍼런스를 잘 체크해야 한다.

5. Explicit Binding

this를 사용할 때 생기는 문제의 대부분은 내가 전혀 기대하지 않은 값으로 this가 셋팅되는 경우에 발생한다. 그래서 아예 함수를 호출할 때 this를 명시적으로 정해주는 방법이 있다. 이는 함수 call()과 apply()를 통해 제공된다.

function foo() {

var obj = {
};      // 2

위의 예에서 사용하는 것을 보면 알 수 있듯이 call()과 apply()는 Function.prototype에 정의된 함수이다. 둘 다 인자로 받은 객체를 해당 함수의 호출에서 this로 셋팅한다.

call과 apply는 완전히 같은 함수로 인자를 전달하는 방식만 다르다. 둘 다 첫번째 인자로 this로 셋팅할 객체값을 전달하고, call은 두번째 이후 인자를 원래 함수의 인자로 순서대로 전달하고 apply는 원래 함수의 모든 인자를 하나의 배열에 넣어서 전달한다., arg1, arg2, arg3);
foo.apply(obj, args); // args is an Array

하지만 call과 apply만으로는 해결 못하는 문제가 있다. 앞에서 보았던 setTimeout에 콜백으로 함수를 넘길때가 대표적인 예이다. 콜백으로는 함수의 레퍼런스를 넘길 수 있지만 함수의 호출(call과 apply는 함수의 호출이 필요하다.)을 넘길 수는 없다.

이에 아예 함수 자체에 특정 객체를 this로 bind해 버리는 방법이 필요해졌다. 이는 Function.prototype.bind()함수로 가능하다. 이를 Hard Binding이라고 한다. bind() 함수의 동작을 매우 간략하게 (실제로는 훨씬 복잡하지만) 표현하면 다음의 코드와 같다.

function bind(fn, obj) {
   return function() {
        return fn.apply(obj, arguments);

bind()함수는 인자로 받은 객체를 call이나 apply를 이용하여 강제로 해당 call의 this로 할당한 ’함수를 리턴한다.’ JS에서 어떤 함수가 다른 ‘함수’를 리턴하는 코드를 많이 볼 수 있다. 이는 대부분 해당 함수의 context를 고정하거나 조작하기 위한 경우가 많다.

Hard Binding을 사용하면 앞에서와 같이 Implicit Lost가 발생하는 경우를 막을 수 있다.

function foo() {

var obj = {

var bar = foo.bind(obj);

bar();  // 4

6. new Binding

이는 이전 글에서 말했던 바와 같이 생성자 함수로 new를 통해 새로운 객체를 만들때의 binding을 이야기 한다. 이 경우 해당 생성자의 call에서 this는 언제나 새로 new로 생성되는 객체로 설정된다. 다음의 코드에서 확인할 수 있다.

function foo(a) {
    this.a = a;

var bar = new foo(2);

bar.a;      // 2

7. Binding Order

실제로는 위의 모든 binding의 경우가 뒤섞일 수 있다. 이 경우에는 다음의 순서로 this가 결정된다.

  • 1) 모든 것에 우선해 new Binding이 이긴다. 만일 함수가 new로 새로운 객체를 생성했으면 해당 생성자의 call에서 this는 바로 생성된 객체이다.
  • 2) 그 다음 Explicit Binding이 이긴다. 어떤 함수가 call과 apply를 통해 실행되었으면 인자로 넘어간 객체가 this이다.
  • 3) 그 다음은 Implicit Binding이다. (대신 Implicit Lost가 발생하지는 않는지 주의해야 한다.)
  • 4) 위의 모든 경우가 아닐때 Default Binding이 적용된다.

다음의 예를 보면 확인할 수 있다.

function foo(a) {
    this.a = a;

var obj1 = {a: 2};

var bar = foo.bind(obj1);
var baz = new bar(3);

console.log(obj1.a);    // 2
console.log(baz.a);     //3

8. this in the Arrow Function

한가지 마지막으로 언급하지 않을 수 없는 것이 ES6에서 새로 도입된 Arrow Function이다. Arrow Function은 일종의 anonymous function을 선언하는 방법으로 생각할 수 있다. 인자를 ()안에 넣고 그 뒤에 => 를 쓴 다음 {}로 코드블럭을 쓸 수 있다. Arrow Function은 콜백함수를 선언할 때 편리하게 쓰일 수 있는데 일반적인 JS함수들과 달리 Arrow Function의 this는 Lexical scope, 즉 Arrow Function이 선언될 때의 scope를 따르기 때문이다. 다음의 예를 보자.

function foo() {
    return (a) => {

var obj1 = { a:2 };
var obj2 = { a:3 };

var bar =;;     // 2, not 3!

위에서 보다시피 Explicit Binding 시도(에도 불구하고 bar는 Arrow Function이기 때문에 항상 Lexical scope로 binding됨을 알 수 있다. (foo가 call()함수를 통해 호출될 때의 call-site를 this로 가지고 있다.)

이는 콜백함수에서 매우 편리하다. 다음의 예는 ES6 이전에 callback에서 컨텍스트를 유지하기 위해 쓰던 방법이다.

function foo() {
    var self = this;        // capture this with self
    setTimeout(function() {
    }, 1000);
var obj1 = { a:2 };;         // 2

별도의 변수 self를 선언해 현재의 this를 캡쳐한 후 이를 closure를 이용해 콜백 함수에서 사용하는 것을 볼 수 있다. 이는 Arrow Function을 쓰면 다음과 같이 간단히 쓸 수 있다.

function foo() {
    setTimeout(() => {
        console.log(this.a);        // this is lexically captured
    }, 1000);
var obj1 = { a:2 };;         // 2

여기까지가 JS에서 this에 대한 이야기였다. 지금까지 이야기한 JS의 Object, Prototype, this 에 대해 이해를 확실히 해 두면 많은 부분에서 JS의 구조가 명확해 질 것이라고 생각한다. 간만에 기술적인 글을 쓰려니 힘들긴 하지만 나름 즐거운 경험이었다.


Javascript Prototype

앞의 글에서는 Javascript에서 일반적인 객체 개념에 대해 알아보았다. 다시 정리하자면 JS에는 일반적인 class기반 언어에서 이야기하는 class/instance관계가 존재하지 않는다. 모든 것은 object로서 존재한다. JS에서 함수가 new 연산자에 의해 객체 생성에 쓰이는 경우 그 함수를 생성자(constructor)라고 한다. (생성자라는게 따로 존재하는게 아니다.) 따라서 JS의 함수는 객체를 설계하는 class같은 개념이 ’아니다.’

앞의 글에서 new가 함수에 사용되어 객체가 생성되는 경우 다음의 4단계의 동작이 일어난다고 했다.

  • 1) 새로운 객체가 생성된다.
  • 2) 새로 생성된 객체의 [[prototype]] 이 링크된다.
  • 3) 생성자함수의 this가 새로 생성된 객체로 (bind)되어 생성자가 호출된다.
  • 4) 새로 생성된 객체가 리턴된다 (new의 리턴값으로). 단, 한가지 예외가 있는데 해당 생성자가 다른 별도의 객체를 리턴하는 경우다. 이 경우는 새로 생성된 객체는 그냥 버려지고 생성자가 리턴하는 객체가 new의 리턴값이 된다.

이 글에서는 이 중 많은 사람들을 혼동으로 빠트리는 [[prototype]]에 대해서 이야기 해 보겠다.

1. Prototype

JS에서 prototype은 매우 중요하고 핵심적인 내용이다. 그런데 많은 사람들이 이에 대해 힘들어하는 것은 너무나 서로 다른 개념을 모두 ‘prototype’ 이라고 말하는 경우가 많기 때문이다. JS에서 prototype이라고 보통 불리는 것들은 정확히는 다음과 같이 3가지의 서로 다른 개념이 있다. (용어는 완전히 일치하지는 않는다.)

  • Prototype Object (프로토타입 객체)
  • Prototype Link (프로토타입 링크)
  • Prototype Property (프로토타입 프로퍼티)

위 3가지를 명확하게 구분할 수 있어야 JS에서 prototype을 이야기 할 때 혼동을 피할 수 있다.

모든 JS의 객체는 자신이 생성될 때 그 근본이 되는 객체가 있다. 이것이 Prototype Object이다. 일반적으로 그냥 ‘프로토타입’이라고 말할 때는 바로 이 Prototype Object를 의미한다. (보통 ‘어떤 객체의 프로토타입’이라고 말한다.) 모든 객체의 프로토타입은 궁극적으로는 Object.prototype 이라는 객체이다. (그냥 Object가 아니라 Object.prototype임에 유의해야 한다.)

new가 실행될 때의 4단계에서 언급되는 [[prototype]]은 Prototype Link를 의미한다. 일반적인 객체에서는 각 객체의 __proto__ (’_’이 앞 뒤 각각 2개이다.) 프로퍼티로 접근할 수도 있지만 표준이 아니기 때문에 지원안하는 환경이 있을 수 있다. 경우에 따라 [[prototype]]와 proto 를 혼재해 사용하는 경우도 많다.

2. 함수객체의 생성

다음과 같이 MyFunction의 코드가 있다고 가정하자.

var MyFunction = function() {
    this.a = 1;

다시말하지만 이건 ‘선언(declaration)이 아니다.’ 위의 코드로도 멀쩡하게 MyFunction이라는 함수 객체가 생성된다.

> MyFunction
[Function: MyFunction]

그럼 함수 객체가 생성되었다고 했으니 a값은 1로 셋팅되었나 확인해 보자

> MyFunction.a

엇… 그런데 MyFunction이라는 객체에는 a 라는 프로퍼티는 존재하지 않는 것을 알 수 있다.

그럼 도대체 위의 코드로 생성된 것은 무엇인가? 이는 ‘Function.prototype’이라는 객체를 프로토타입으로 해서 생성된 다른 함수 객체이다. 이제부터 이 함수 객체의 몇가지 중요한 개념을 이야기 해 보겠다.

  • JS에서 생성된 모든 객체는 ‘Prototype Link’를 가진다. 보통 [[prototype]]로 표시되는 녀석이다.
  • JS에서 생성된 모든 객체는 그 객체의 생성과 연관되는 ‘Prototype Object’가 존재한다. 이 Prototype Object를 가리키는 것(일종의 포인터로 생각하면 된다.)이 Prototype Link ( [[prototype]] )이다. 이 [[prototype]]도 그 객체의 프로퍼티이지만 뒤의 함수 객체의 Prototype Property와 혼동을 피하기 위해 명확하게 [[prototype]] 또는 Prototype Link라고 불러주는게 좋다.
  • JS의 객체중 ’함수 객체’는 특이한 프로퍼티를 하나 가지는데 그 이름이 ‘prototype’이다. (이게 많은 혼란의 근본이다. 위의 Function.prototype도 이 일종이다. – ‘Function’ 자체도 하나의 함수이다.) 이 Prototype Property는 해당 함수의 ‘Function Prototype Object’를 가리킨다.

위에서 MyFunction로 함수 객체는 생성이 되었지만 해당 함수는 호출(call)되지 않는다. 함수를 호출하려면 명백하게 함수이름 뒤에 ‘()’를 써서 호출을 해야 한다.

위의 설명을 MyFunction 예제를 바탕으로 무슨 일이 일어났는지 다시 설명하면 다음과 같다.

  • 일반적인 다른 언어의 ‘선언부(declaration)’로 ’보이는’ 코드는 실제로 MyFunction 이라고 하는 ’함수 객체’를 생성한다. 함수 객체는 일반적인 객체와 같지만 몇가지 다른 특징을 가지는 객체이다.
  • MyFunction은 다른 일반 객체와 같이 자신의 생성시 바탕이 되는 Prototype Object가 있다. 이는 역시 다른 객체와 같이 MyFunction의 Property Link, 즉 [[prototype]]이 가리키는 객체이다. MyFunction의 [[prototype]]가 가리키는 값은 (즉, MyFunction의 프로토타입은) Function.prototype이라는 객체이다.
  • 그런데 함수 객체는 일반 객체와 달리 이름이 ’prototype’이라는 프로퍼티가 존재한다. (이게 일반적으로 사람들이 혼동을 일으키게 만든다. 이를 명확하게 하기 위해 Prototype Property라고 구분해서 말하기도 한다.
  • 그럼 이 MyFunction의 prototype이라는 프로퍼티, 즉, MyFunction.prototype 이라는 프로토타입 프로퍼티가 가리키는 값은 무었인가. 이 역시 하나의 객체이다. 다만 이는 MyFunction의 프로토타입과는 다른 별도의 객체이다. MyFunction.prototype은 해당 함수가 (그래서 함수 객체에만 존재한다.) 생성자로 쓰일 때 (new로 호출될 때) 생성 될 객체의 기반이 될, 즉 그 생성되는 객체의 프로토타입이 될 객체이다.

3. new를 통한 객체 생성의 예

다시 다음의 코드가 실행된다고 할 때, (다시 말하지만 해당 코드는 실행되지만 MyFunction은 호출이 되지 않는다.)

var MyFunction = function() {
    this.a = 1;

메모리에는 다음과 같은 상황이 된다.

함수 객체의 Prototype Property(가 가리키는 객체)에는 특별하게 constructor라는 프로퍼티가 있는데 이는 자신을 활용할 생성자 함수를 다시 가리키고 있다.

이제 다음과 같이 MyFunction으로 new를 이용해 새로운 객체를 생성해 보자.

var MyFunction = function() {
    this.a = 1;
var mine = new MyFunction();


이렇게 new를 사용할 때 다음과 같은 과정이 벌어진다.

  • 새로운 객체가 하나 생성된다.
  • 새로운 객체의 프로토타입이 ( = [[prototype]] 의 값이) MyFunction.prototype으로 설정된다. (MyFunction이 아니라 MyFunction.prototype임을 주의해야 한다.)
  • 이 객체의 생성자(MyFunction)의 this가 지금 새로 생성된 객체로 설정되어 실행된다. 이 때 새로운 객체에는 a라는 프로퍼티가 생성된다.
  • 생성자가 다른 객체를 리턴하지 않기 때문에 이 새로 생성된 객체가 new에서 리턴된다. 이 값이 mine에 저장되어 mine은 새로 생성된 객체를 가리킨다.

이후 메모리에는 다음과 같은 상황이 된다.

mine은 MyFunction을 생성자로 해서 (new를 통해) 만들어진 객체이지만 mine의 프로토타입은 MyFunction이 아니라 MyFunction.prototype이라는 함수의 프로토타입 프로퍼티이다.

4. Prototype Chaining과 Shadowing

그러면 이 프로토타입은 어디에 쓰는 것인가? 어떤 객체의 프로퍼티(또는 메소드(프로퍼티가 함수인 경우))가 해당 객체에 없을 경우 JS는 해당 객체의 프로토타입 링크를 따라 ( [[prototype]] 값이 가리키는 값들 ) 거슬러 올라가며 해당 프로퍼티를 찾는다. 이를 프로토타입 체이닝(Prototype Chaining)이라고 한다. 다음의 예제를 보자.

var MyFunction = function() {
    this.a = 1;
var mine = new MyFunction();
MyFunction.b = 2;
MyFunction.prototype.b = 3;


mine에는 b라는 프로퍼티가 정의되어 있지 않다. 그래서 JS는 프로퍼티링크를 거슬러 가면서 b를 찾게 된다. 여기에서는 b의 값이 3인 것으로 보아 mine의 프로토타입은 MyFunction이 아니라 MyFunction.prototype임을 다시 확인할 수 있다.

어떤 프로퍼티의 값을 읽는 경우 ([[Get]] 오퍼레이션의 경우)에는 사실 큰 문제가 없을 수도 있다. 하지만 새로운 프로퍼티를 객체에 동적으로 할당하는 경우 ([[Put]] 오퍼레이션)에는 좀 다른 문제가 발생한다. 다음과 같은 예를 다시 보자.

var MyFunction = function() {
    this.a = 1;
var mine = new MyFunction();
MyFunction.prototype.b = 3;

mine.b = 4

앞에서 mine.b는 프로퍼티 링크를 타고 올라가 MyFunction.prototype.b 의 값을 읽었다. 하지만 mine에 직접 b라는 프로퍼티를 설정하면 b는 직접 mine객체의 프로퍼티로 생성된다. (MyFunction.prototype.b 와는 별개의 프로퍼티가 된다.) 이를 Shadowing이라고 한다.

Shadowing은 어떤 객체에 새로운 프로퍼티를 할당할 때 프로퍼티 링크 상의 상위 객체에 같은 이름의 프로퍼티가 있더라도 그 프로퍼티가 읽기 전용이 아니라면 해당 프로퍼티를 그냥 해당 하위 객체에 바로 생성한다. (상위 객체의 값을 바꾸는 것이 아니라)

상위의 프로퍼티가 읽기 전용이라면 (프로퍼티의 속성을 바꾸는 방법등은 여기서 다루지 않겠다.) 프로퍼티를 할당하려는 시도는 그냥 조용히 실패한다. (strict mode로 동작시에는 에러가 발생한다.) 한가지 특이한 경우는 상위 프로퍼티에 setter가 정의된 경우인데 (다른 언어들과 유사하게 JS의 프로퍼티들도 별도의 getter나 setter를 정의할 수 있다.) 이 경우에는 그 상위 setter가 실행되고 정작 하위의 객체에는 해당 프로퍼티가 생성되지 않는다.

5. Object.create()

생성자 함수를 이용해 new를 통해 객체를 생성하는 것은 위와 같이 매우 복잡한 과정이다. 만일 단순하게 객체를 생성하는 것만이 목적이라면 Object.create() 함수를 이용하면 된다.

var objSource = {
    a: 1,
    b: 2

var objNew = Object.create(objSource);


Object.create 함수는 인자로 받은 객체를 프로토타입으로 하는 새로운 객체를 반환한다. 위의 예제에서 objNew는 Prototype Property가 없이 [[prototype]]이 objSource로 설정되는 객체이다.

6. Inspection


JS에서 객체를 다루다보면 각 객체의 연관관계를 검사해야 하는 경우가 많이 생긴다. instanceof는 이 때 사용할 수 있는 연산자(메소드가 아님)이다. 다음의 예를 보자

mine instanceof MyFunction;     // true

instanceof 연산자는 좌항의 객체가 우항의 함수로 생성된 객체인지를 좌항 객체의 [[prototype]]을 거슬러가며 검사한다. 중요한 것은 우항이 항상 ‘함수’여야 한다는 것이다. 이것은 다시 말해 거슬러가면서 검사하는 것은 그 프로토타입 링크의 값이 우항 함수의 프로토타입 프로퍼티(위의 예에서는 MyFunction.prototype)인지를 확인한다는 것이다.


isPrototypeOf는 연산자가 아니라 Object의 메소드이다. 다시말해 해당 객체가 인자로 받는 객체로부터 생성된 (프로퍼티링크의 체인상에 존재하는) 객체인지를 검사한다. instanceof와 달리 검사하는 대상은 함수가 아니라 객체이다.

objSource.isPrototypeOf(objNew);    // true


Object의 메소드로 인자로 받은 객체의 프로토타입을 구하는 ( [[prototype]] 값을 알아내는) 공식적인 방법이다. 앞에서 이야기 했듯이 proto 프로퍼티를 쓰는 경우도 있지만 표준이 아니다. (Node.js에서는 지원한다.)

Object.getPrototypeOf(objNew) === objSource;    // true
Object.getPrototypeOf(mine) === MyFunction.prototype;    // true
Object.getPrototypeOf(mine) === MyFunction;    // false

여기까지가 JS의 프로토타입에 대한 중요한 내용들이다. 어떤 함수 객체의 프토토타입 프로퍼티 객체 (AFunction.prototype)는 이 함수를 생성자로 사용해 new로 만든 모든 객체에 프로토타입 링크의 체인으로 공유되기 때문에, 흔히 이 프로토타입 프로퍼티 객체에 프로퍼티나 메소드를 정의해 일종의 객체 상속과 같은 개념으로 활용한다. 하지만 엄밀히 말해 이는 상속이 아니라 일종의 위임(delegation)으로 봐야 한다. 이의 동작의 의미를 명확히 이해하기 위해서라도 프로토타입에 대한 정확한 이해가 중요하다.

다음 글에서는 역시 많은 사람들을 괴롭히는 ‘this’에 대해 이야기 해 보겠다.



Javascript Object

1. 들어가며

원래 이 블로그에서는 한국어 포스팅은 안 하려고 했는데 다른 것 보다는 이 블로그는 누가 읽어도 안 읽어도 뭐 크게 상관없다… 라는 스탠스가 기본이었기 때문이다. 하지만 왠지 이런 기술관련 글은 혹시 도움이 될 사람이 있을지도 모른다는 생각에 간만에 한국어 포스팅을 해 보기로 했다. 혹시 시간이 되면 영어나 일본어로 다시 써보게 될지도 모르겠지만.

최근 간만에 프로그래밍 관련 공부를 다시 하고 있는데 이전에 봤던 “You Don’t Know JS” 시리즈를 다시 읽고서 매우 감동을 받고 있다. Javascript를 보통 공부할 때 어느정도 익숙해지고 나면 읽게되는 유명한 책이 Douglas Crockford의 “JavaScript: The Good Parts” 이다. 이 책은 Javascript에 대한 이해를 높여주면서 이를 사용할 때 겪게되는 오류들을 미리 피할 수 있는 바람직한 코딩 가이드를 제공해 준다. 시간이 없는 사람들은 Crokford의 강연 비디오도 다양하게 올라와 있어 꼭 한번 보는 것이 좋다.

하지만 “You Don’t Know JS” 시리즈는 Javascript의 good parts만이 아니라 bad parts에 대해서도 충분한 이해가 필요하다는 생각에서 출발하고 있다. Javascript는 개인적으로 겪어본 프로그래밍 언어중에서 가장 특이하고 ‘현학적’인 언어라고 생각한다. 여전히 Javascript를 매우 ‘쉬운’ 언어라고 생각하는 사람들도 많은데 (사실 쉽게만 쓸 수 있기도 하다.) 이는 태생부터 이상하게 나온 Java + Script 라는 이름이 문제일지도 모르겠다. 혹시 해서 말하지만 Javascript는 Java와는 전혀 관련이 없으며 Script도 일반적으로 인지되는 ‘쉽다’와는 거리가 먼 언어이다. 물론 이는 그만큼 언어가 널리 쓰이게 되면서 보통은 신경쓰지 않아도 될 언어의 내부까지의 이해가 중요해졌기 때문이기도 하다.

보통 프로그래밍 언어를 공부하게 되면 1) 언어의 문법의 이해, 2) 특정 상황에 대한 해결방법 3) 언어 내부 구조의 이해 의 3단계를 거치게 된다고 생각한다. 사실 보통은 1), 2)만으로도 충분한 경우도 많다. 하지만 해당 언어를 정말로 전문가로서 활용하고 싶다면 3)의 과정은 매우 중요한데, Javascript는 이 3)이 매우 어려운편이고 (특히 C/C++ 계열의 언어에 익숙할수록 그러하다. 언어 자체는 마치 한국어와 영어처럼 그 근본이 다른 언어인데 문법적으로는 마치 비슷한 것 같은 모양을 띄고 있어서 더 그럴 수도 있겠다.) 더 잘 알수록 더 다른 사람이 이해하기 힘든 (현학적인) 코드를 만들어 낼 수 있다. 개인적으로는 이런 현학적인 코드를 가능한 만들지 않아야 한다고 생각하지만, Javascript에는 오랜 세월동안 만들어져온 일종의 패턴과 같은 많은 현학적 코드들이 존재한다. 그리고 그 코드들은 단지 자신의 지적 능력의 과시가 아닌, 꽤 현실적인 고민을 해결하기 위해 나온 경우가 많다. 그렇기 때문에 이를 이해하는 것 역시 매우 중요한 일이 된다.

그런 이유로 개인적으로 Javascript에 대한 글을 써보고 싶어졌다. 기본적으로는 내 자신의 공부를 위해서이다. (언제나 공부를 하는데 가장 좋은 방법 중 하나는 다른 사람에게 가르쳐 줄 수 있을만큼 파고들어보는 것이다.) 처음 이 글은 Javascript의 객체(Object) 개념에 대해 쓰고, 그 다음에는 많은 이들을 혼동시키는 ’prototype’에 대해 써 보고자 한다. 그리고 그 다음 글은 역시 많은 사람들을 충격과 공포로 인도하는;; ’this’에 대해 써 보려고 한다. (쓰다보니 너무 길어져 3개의 글로 나누려고 한다.) 책의 많은 부분은 앞에서 언급한 “You Don’t Know JS” 시리즈 중 “You Don’t Know JS: this & Object Prototypes” 편에서 영감을 얻고 있다. 하지만 단순한 번역은 아닌 개인적인 해석과 추가적인 이해에 도움되는 내용을 적고자 한다. 매우 좋은 책이기 때문에 관심있는 분들은 읽어봐도 좋을 것 같다. (참고로 이 글의 코드들은 기본적으로 브라우저 환경이 아닌 Node.js 환경을 가정하고 있다.)

2. Javacript의 모든 것은 객체이다?

흔히 Javascript의 모든 것은 객체이며 Javascript (만이) 진정한 객체지향언어라는 이야기를 한다. 하지만 엄격히 문법적으로 말하자면 사실이 아니다. Javascript에는 다음과 같이 6개의 기본 데이터 타입(primitive data type)이 있다. (You don’t Know JS (YDKJ) 기준으로)

  • string
  • number
  • boolean
  • null
  • undefined
  • object

좀 더 엄격하게 말하면 MDN reference에서는 5개의 primitive type (string, number, boolean, null, undefined)와 object로 구분하여 이야기하기도 한다. ES6부터는 symbol을 primitive type에 추가해 이야기한다. 다른 곳들과 달리 YDKJ에서는 소문자로 모든 기본 type을 쓰는데 (String이 아니라 string) 개인적으로는 이 편을 선호한다. 뒤에 나올 내장객체(Built-in Object)와의 혼동을 피하기 위함이다.

Note: Javascript에서는 모든 것은 객체이다…라고 이야기 되는 몇가지 ‘버그’들이 있는데 대표적인 것이 다음과 같은 것이다. null은 그 자신이 그냥 null인 primitive type이 맞다.

> typeof null
// what the h… No, this is a bug.

’null’과 ‘undefined’: 이 두 개념도 꽤 만악의 근원…인데 기본적으로 공식 정의조차 매우 어긋나 있다. 언어 정의는 undefined는 어떤 변수가 선언되었지만 어떤 값을 할당받지 않은 상태를 말하고 null은 할당값이라고 설명하고 있다. 하지만 undefined와 null은 그 자체로 엄연한 primitive type이고 각각 ‘undefined’와 ‘null’이라는 값을 가진다. (null과 달리 typeof undefined는 ‘undefined’라고 잘 나온다.) 실제로 undefined는 다음과 같은 경우에 쓰인다.

  • 변수에 값이 할당되기 전의 기본 값
  • 함수가 호출될 때 caller로 부터 제공되지 않은 인자의 값
  • 접근한 객체의 프로퍼티가 ‘존재하지 않는’ 경우의 반환 값

이에 반해 null은 개인적인 감각으로는 언어 자체에 의해서 보다는 ‘프로그래머들에게서’ 사용되는 값이다. 원래는 선언은 되었지만 값이 없을 때 쓰이는 것으로 기획되었지만 사실 런타임에서는 이 용도로는 undefined가 더 일반적으로 쓰이기 때문이다. 이에 반해 프로그래머들은 위와 같은 용도로 null을 직접 많이 사용한다. 예를 들어 foo라는 변수를 선언하고 일단 ‘값이 없음’ 이라고 해 두려면 null을 넣어두는게 다른 언어를 사용하던 프로그래머들의 본성 아니겠는가? 이런 이유에서인지 언어 런타임 자체가 아니라 3자(?)가 제작한 프레임워크들의 함수에서는 어떤 객체를 요청했을 때 없는 경우 null이 리턴되는 경우가 많다. (브라우저에서 DOM 객체 접근시 getElementById 같은 함수에서 없으면 null이 넘어오는 게 대표적인 예이다.)

> var foo = null;
// yes, it is usual

3. Built-in Functions (내장 함수들)

위의 primitive type(굳이 기본타입이 아니라 primitive type이라고 하는 것은 한국어로 기본타입이라고 써 버리면 너무 일반적인 의미로 이해되기 때문이다. primitive type은 명확히 다른 내장 객체/함수들이나 다른 객체들과 구분해서 이해할 필요가 있다.)들은 이에 대응하는 내장(built-in) 객체를 가지고 있다. 아니, 매우 엄밀히 말하면 내장 객체가 아니라 그냥 ‘함수’이다. 뭐.. JS에서 함수도 객체 아닌가 하면 맞는 말이기도 하지만 함수는 조금 ‘특이한’ 객체이다. 이는 뒤에 다시 이야기하겠다. Primitive type들 중 undefined나 null을 제외하면 각각 대응하는 내장 함수들이 존재한다. 내장 함수들은 대표적으로 다음과 같은 것들이다. (구분하기 위해서 첫글자를 대문자로 쓴다.)

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

보다시피 바로 대응하는 것도 있고 Date나 RegExp처럼 primitive data type에는 없는 것들도 있다. 예를 들어 primitive type인 ‘string’과 내장 함수 String은 엄연히 다른 존재들이다.

var strPrimitive = "I am a string";
typeof strPrimitive; // 'string'
strPrimitive instanceof String; // false

var strObject = new String("I am a string");
typeof strObject; // 'object'
strObject instanceof String; // true

4. JS에서의 객체 생성

JS에서 객체를 생성하는 방법은 크게 3가지가 있다.

  • 1) 객체 literal로 생성하는 법
  • 2) 생성자 호출, 즉 함수에 new를 써서 생성하는 법
  • 3) Object.create() 를 써서 생성하는 법

3)은 많이 쓰이지 않지만 매우 좋은 방법이다. 왜 그런지는 추후 설명하고자 한다. 1)은 가장 선호되는 방법이다. JS는 간단한 선언 방법으로 객체를 선언할 수 있다.

var myObject = {
    a: 2,
    name: "I'm myObject"

> myObject
{ a: 2, name: 'I\'m myObject' }
> myObject.a
'I\'m myObject'

Literal을 이용한 객체 선언은 간단한 객체의 선언이 필요할 때, JSON을 활용하여 객체를 선언할 때 등에서 매우 편리한 방법이다. 이에 비해 조금 복잡한 객체를 선언하거나 기존의 class 기반 객체지향 언어에 익숙한 사람들이 많이 사용할 수 있는 방법이 2)의 방법일 것이다.

var MyFunction = function() {
    this.a = 1; = "I am MyObject!";

var mine = new MyFunction();

> mine
MyFunction { a: 1, name: 'I am MyObject!' }

이 때의 MyFunction을 생성자/constructor라고 한다. 사실 이를 ‘생성자’라고 부르는 것도 많은 오해를 낳게 하는 악의 근원이다. 기존의 Java나 C++같은 클래스 기반 객체지향 언어를 사용하던 사람들이 볼 때 위의 코드는 매우 평이하게 보일 것이다. 비록 class라고 쓰지는 않았지만 JS에서 함수는 객체와 같다고도 했다. (정확히는 객체 중에는 함수객체라는것도 있는 것이지만) 그래서 함수를 new하면 mine이라는 인스턴스(instance)가 만들어졌다. 라고 생각한다. NO! 하지만 매우 유감이게도 이 말은 틀렸다.

기존의 class기반 객체지향 언어에서는 class는 일종의 청사진(blueprint)이다. 객체의 설계를 class에서 한다음 이를 실제 생성, 즉 인스턴스(instance)를 만드는 것이다. 기존 class기반 언어에서 위의 MyFunction과 mine의 관계는 다음 그림과 같이 이해된다.

하지만 JS에서는 이 class와 instance의 개념이 없다. 굳이 말하자면 모두 instance이지만 인스턴스라고 말하면 헷갈릴 수도 있어 JS에서는 그냥 이를 모두 object라고 한다. 다시말해 위의 MyFunction 부분은 다른 언어처럼 그냥 ‘선언(declaration)’이 아니다. 이 자체로 객체가 생성된다. 그래서 위의 MyFunction, mine은 각각 별도의 object로 존재한다. 그림으로 표현하면 다음과 같다.

5. new가 하는 일

위의 두번째 그림에서 한가지 재미있는 것은 화살표 방향이 첫번째 그림과는 반대라는 것이다. 이 화살표는 무엇인가?의 답을 알기 위해서는 정확하게 new가 어떤 일을 하는지 알아야 할 필요가 있다.

어떤 함수에 new를 사용하면 새로운 객체를 생성할 수 있다. 이 때 이 함수를 생성자(constructor)라고 한다. 여기서 중요한 개념은 생성자에 new를 사용해서 객체를 만드는게 아니라 “new에 사용되는 함수를 생성자라고 하는 것”이다. 다시 말해 모든 함수는 생성자가 될 수 있고 뭔가 따로 있는 것이 아니라는 것이다.

어떤 함수에 new를 사용하면 다음의 4단계의 동작이 발생한다.

  • 1) 새로운 객체가 생성된다.
  • 2) 새로 생성된 객체의 [[prototype]] 이 링크된다.
  • 3) 생성자함수의 this가 새로 생성된 객체로 (bind)되어 생성자가 호출된다.
  • 4) 새로 생성된 객체가 리턴된다 (new의 리턴값으로). 단, 한가지 예외가 있는데 해당 생성자가 다른 별도의 객체를 리턴하는 경우다. 이 경우는 새로 생성된 객체는 그냥 버려지고 생성자가 리턴하는 객체가 new의 리턴값이 된다.

위의 4단계를 잘 살펴보면 매우 중요한 개념을 알 수 있다. 생성자는 새로 생성된 객체와 강하게 연관된(기존 class/instance관계처럼) 그 무엇이 아니라 그냥 객체의 생성에서 호출되어 이를 도와주는 일종의 헬퍼와 같은 역할임을 알 수 있다. JS에서 함수, 즉 여기서의 생성자는 절대 다른 언어의 class와 같은 개념이 아니다. 다음 글에서는 2) 과정의 [[prototype]] 에 대해서 알아보겠다.