Frontend

비동기와 동기 방식의 차이점

  • 동기적 방식은 웹 사이트에서 어떤 요청을 하면 그 요청을 수행하는 동안 사용자는 아무것도 못하는 것을 말한다.
    • 데이터 요청
    • 화면유지
  • 이렇게 로직이 있을 떄, 동기적 방식으로 1번을 수행하게 된다면 그 1번을 수행하는 동안 웹 사이트는 아무것도 못하게 된다. 그래서 흰 하면이 뜰 수 밖에 없다.
  • 비동기적 방식은 데이터 요청을 수행하면서 화면을 유지하는 것을 말한다. 따라서 사용자가 웹 사이트를 화면유지된 상태에서 사용할 수 있으므로 좀 더 상호작용적이다.

Ajax

  • Ajax는 Asynchronous Javascript And XML로 넓은 의미로는 웹 클라이언트 측에서 리로드 없이 비동기적으로 컨텐츠를 변경하기 위해 사용하는 모든 기술을 지칭하고, 좁은 의미에서는 서버측과 비동기적으로 통신하는 기술을 말한다.

jQuery

  • 오픈소스로 구현된 브라우저용 클라이언트 자바스크립트 라이브러리이다. 이를 통해 문서 객체 모델과 관련된 처리를 쉽게 할 수 있고, 일관된 이벤트 연결을 쉽게 구현해주며, 시각적 효과를 쉽게 구현할 수 있다. 또한 ajax 애플리케이션을 쉽게 개발할 수 있다.

node.js

  • 이론
    • Node.js는 자바스크립트를 브라우저에서만 쓰는 것이 아닌 브라우저 밖, 즉 내 컴퓨터에서 다양한 용도로 확장하기 위해 만들어진 자바스크립트 런타임이다.
    • 이를 통해 파일 시스템을 이용할 수도 있고 서버를 만들수도 있으며 크롤링도 할 수 있다.
    • npm을 통해 다양한 패키지를 설치해서 사용할 수 있다.
  • 배경
    • 기존, 웹 사이트를 만들기 위해 HTML문서를 일일히 전부 만들기가 너무 귀찮더라.
    • 따라서 웹 개발자들에게 익숙한 JavaScript로 웹 페이지를 자동으로 생성해주는 Server Side Application을 만들 수 있게 해 주고 싶더라.
    • 기존, JavaScript로 인해 웹은 사용자와 상호작용하는 애플리케이션의 면모를 띌 수 있었다.(문서 -> 애플리케이션)
    • 하지만 JavaScript는, 좋게 말하면 웹브라우저에서 유일하게 사용할 수 있는 언어이지만, 나쁘게 말하면 웹 브라우저에 갖혀있는 편파적인 언어였다.
    • 그런데 2008년, Google이 Chrome의 성능 향상을 위해 JavaScript Engine, V8을 개발하였고 이를 오픈소스로 공개하여 수 많은 개발자들을 이끌었다.
    • 그리고 2009년 Ryan Dal이 JavaScript 언어로 구현된 Server Side 언어, Node.js를 내보였다.
    • Node.js를 통해 JS를 이용하여 웹 브라우저가 아닌 컴퓨터 자체를 제어할 수 있게 되었다.

Express.js

  • 이론
    • 익스프레스는 노드 상에서 동작하는 웹 개발 프레임워크이다.
    • 익스프레스는 클라이언트와 서버 사이의 미들웨어 구조이기에 개발자가 필요한 것만 선택해서 익스프레스와 결합하여 사용할 수 있다.
  • 배경
    • 기존 Node.js만으로 웹 앱을 개발하는 것이 세련되지 못하고 불편하더라.
    • 그래서 웹 개발 프레임워크를 개발.
    • 프레임워크는 반복적으로 어디에서나 등장하는 일들을 처리할 때, 더 적은 코드로 더 많은 일을 안전하게 처리할 수 있도록 도와주는 도구이다.
    • 공통적인 일은 프레임워크가 미리 구현해놓은 것을 사용하고 웹사이트의 개성에 집중할 수 있다.

화살표 함수와 일반 함수

화살표 함수와 일반 함수는 서로 가리키고 있는 this값이 다르다.

function BlackDog() {
    this.name = 'white dog';
    return {
        name: 'black dog',
        bark: function() {
            console.log(this.name + ' : 멍멍');
        }
    }
}

const blackDog = new BlackDog();
blackDog.bark(); // black dog : 멍멍

function WhiteDog() {
    this.name = 'white dog';
    return {
        name: 'black dog',
        bark: () => {
            console.log(this.name + ' : 멍멍');
        }
    }
}

const whiteDog = new WhiteDog();
whiteDog.bark(); // white dog : 멍멍

일반 함수는 자신이 종속된 객체를 this로 가리키고, 화살표 함수는 자신이 종속된 인스턴스를 가리킨다는 것을 알 수 있다.

클로저

  • 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있다. 이러한 매커니즘을 ‘클로저’라고 한다.

    function getClosure() {
        var text = 'variable1';
        return function() {
            return text;
        };
    }
    var closure = getClosure();
    console.log(closure());  // variable1
    
  • 위에서 getClousre()는 함수를 반환하고 반환한 함수는 해당 함수 내부에서 선언된 변수를 참조하고 있다. 또한 이렇게 참조된 변수는 함수 실행이 끝났다고 해서 사라지지 않고 여전히 제대로 된 값을 반환하고 있다. 즉 이렇게 반환된 함수가 클로저이고 환경을 기억하고 있다.

  • 클로저 예제

    var arr = []
    for(var i = 0; i < 5; ++i) {
       arr[i] = function() {
           return i;
       } 
    }
    for(var index in arr) {
        console.log(arr[index]());
    }
    // 위 코드를 실행 시키면 5 5 5 5 5가 출력된다.
      
    // 이를 클로저를 통해 해결
    var arr = []
    for(var i = 0; i < 5; ++i) {
        arr[i] = function(id) {
            return function() {
                return id;
            }
        }(i);
    }
    for(var index in arr) {
        console.log(arr[index]());
    }
    // 코드 실행 결과 : 0 1 2 3 4
    
  • 클로저 활용

    function hero_movie(title) {
        return {
            get_title: function() {
                return title;
            },
            set_title: function(_title) {
                title = _title;
            }
        }
    }
      
    superman = hero_movie('Superman');
    batman = hero_movie('Batman');
      
    alert(superman.get_title());
    alert(batman.get_title());
      
    batman.set_title('배트맨');
      
    alert(superman.get_title());
    alert(batman.get_title());
    
    • 외부함수의 변수를 통해 private한 변수를 만들 수 있다!
    • 함수를 통해서만 해당 변수에 접근이 가능하다.
    • get, set 프로퍼티와 같은 개념

익명함수

  • 익명함수는 말 그대로 이름이 없는 함수이다. 보통 클로저, 또는 콜백이라고도 부른다.
  • 함수의 인자로 함수를 전달한다.

이벤트 캡처링 버블링 (이벤트 전파)

  • 브라우저에서 이벤트의 전달 단계는 크게 3단계로 이루어진다.
    • 캡쳐링 단계는 운영체제에서 캐치한 이벤트가 브라우저로 전달되는 단계이다. (부모 -> 자식)
    • 이벤트가 해당 엘리먼트로 도착한 단계가 타겟 단계이다. (HTML -> BODY -> 엘리먼트)
    • 엘리먼트에서 이벤트가 처리되고 다시 상위 엘리먼트를 통해 브라우저를 빠져나가는 단계가 버블링 단계이다. (자식 -> 부모)

호이스팅

  • 호이스팅이란 변수의 정의가 그 소코프에 따라 선언과 할당으로 분리되는 것을 의미한다. 선언 부분이 해당 스코프의 최상위로 변경된다.

    function hoisting() {
        hoistingText ="hoisting";
        var hoistingText;
        console.log(hoistingText);
    }
    hoisting();  // hoisting
    
  • 위의 예는 호이스팅에 의해 정상적으로 출력된다. 다음과 같이 재해석된다.

    function hoisting() {
        var hoistingText;
        hoistingText ="hoisting";
        console.log(hoistingText);
    }
    hoisting();
    

this 바인딩

  • 기본적인 this : 글로벌 스코프에 쓰인 this는 window(글로벌)를 가리킨다
  • 함수 실행에서의 this : 일반적인 함수 안에 있는 this는 window를 가리킨다.
  • 메소드 실행에서의 this : 메소드에서 쓰인 함수의 this는 그 부모 객체를 가리킨다.
  • 생성자 실행에서의 this : new 연산자로 생성된 함수 영역의 this는 새로 생성된 객체를 가리킨다
  • call, apply에서의 this : call 혹은 apply를 통해 호출된 함수의 this는 call과 apply의 첫번째 인자를 가리킨다.

이벤트 바인딩

  • 이벤트 바인딩은 이벤트가 처리되는 이벤트 핸들링 과정에서 이루어진다.

  • HTML 이벤트 핸들러

    • HTML 요소의 onclick 속성에 함수를 호출하여 바인딩하는 방법 (inline)

      <button class="btn" onclick="myFunction()">
      
  • DOM 이벤트 핸들러

    • 프로퍼티 리스너 - 이벤트 대상에 해당하는 객체의 프로퍼티로 이벤트를 등록

    • DOM 요소에 이벤트를 바인딩하고 이벤트가 발생하면 실행될 코드를 함수로 설정하는 방법

      element.on이벤트이름 = myFunction(){...}
      
  • DOM 이벤트 리스너

    • DOM 요소에 addEventListener메소드를 호출하여 이벤트를 바인딩하고 수행할 함수를 작성

      element.addEventListener("이벤트이름", myfunction(){ ... }, [, boolean]);
      

var let const 차이

  • 스코프

    • var

      function varTest() {
          if (true) {
              var x = 2;
              console.log(x); //2
          }
          console.log(x); //2
      }
      varTest();
      
      • var은 함수 레벨 스코프이다
      • if문 안에 있는 var x는 함수 레벨 스코프를 가지기 때문에 if문이 종료된 후에도 값을 가지고 있어 출력된다.
      for (var i = 0; i <= 5; i++) {
      }
      console.log(i);  //6
      
      • 전역에서 var을 사용하여 for문을 실행할 경우 for문이 종료된 후에도 i값이 남게 되는 문제가 발생한다.
    • let

      function letTest() {
          if (true) {
              let x = 2;
              console.log(x); //2
          }
          console.log(x); //not defined
      }
      letTest();
      
      • let은 블록 레벨 스코프이다
      • if문 안에 선언된 let x는 블록 레벨 스코프를 가지기 때문에 if문이 종레된 후에는 not defined로 에러가 난다.
      for (let i = 0; i <= 5; i++) {
      }
      console.log(i); //error
      
      • let은 블록 레벨 스코프를 가지고 있기 때문에 전역 변수의 오염을 막을 수 있다. for문 이후 전역변수가 오염되지 않는다.
      var a = 1
      let b = 2
          
      if (true) {
        var a = 11
        let b = 22
        console.log('a = ' + a) // 11
        console.log('b = ' + b) // 22
      }
          
      console.log('a = ' + a) // 11
      console.log('b = ' + b) // 2
          
      function func() {
        var a = 111
        let b = 222
        console.log('a = ' + a) // 111
        console.log('b = ' + b) // 222
      }
          
      func()
          
      console.log('a = ' + a) // 11
      console.log('b = ' + b) // 2
      
    • const

      • const 역시 let과 동일하게 블록 레벨 스코프를 갖는다.
      const MY_FOO ="FOO 1";
      if (true) {
          const MY_FOO ="FOO 2";
          console.log(MY_FOO);  //FOO 2
      }
      console.log(MY_FOO);  //FOO 1
      
  • 재할당 및 재선언

    // var의 경우
    var a = 1
    a = 2
    console.log(a) // 2
    var a = 3
    console.log(a) // 3
      
    // let의 경우
    let b = 1
    b = 2
    console.log(b) // 2
    let b = 3 // SyntaxError: Identifier 'b' has already been declared
      
    // const의 경우
    const c = 1
    c = 2 // TypeError: Assignment to constant variable
    
    • var 의 경우 굉장히 유연하다. var a = 1;로 선언한 뒤, 2 를 재할당하고 다시 var = 3;으로 재선언(?) 해도 문제될게 없다.
    • let 의 경우 재할당은 문제 없습니다만, let b = 3;으로 재선언하고 나면 이미 선언된 변수라는 에러를 뱉어냅니다.
    • const 는 상수이기 때문에 재할당, 재선언 모두 불가합니다. 그리고 const는 정의하자마자 바로 초기화를 해 줘야 한다.

prototype 동작 원리

  • 자바스크립트에서 함수를 정의하게 되면 함수뿐만 아니라 Prototype Obejct도 같이 생성된다

    • 그리고 생성된 함수는 위에서 살펴봤듯이 생성자 함수를 통해 생성된 객체이기 때문에 prototype 이라는 속성을 가지게 되는데 이는 Prototype Object를 가리킨다.

    • Prototype Object는 일반적인 객체와 같으며 디폴트 속성으로 constructor__proto__를 가지고 있다.

    • constructor는 생성자 함수 자체를 가리키고 있다.

    • __proto__는 객체가 생성될 때 조상이었던 함수의 Prototype Object를 참조한다.

    • 위를 보면 Prototype Object는 일반적인 객체로서, 속성을 마음대로 추가 할 수 있다는 것을 알 수 있다.

    • Car 함수를 통해 생성된 bmw와 audi 객체는 wheel이라는 속성을 별도로 직접 설정해주지 않아도 Car.prototype을 참조하고 있기에 console.log(bmw.wheel)이 4를 나타내는 것이다.

    • Car.prototype을 살펴보니 함수 정의 시, 기본적으로 생성되던 constructor__proto__ 외에 handlewheel 속성이 추가되어 있다.

    • bmw객체는 Car 생성자 함수로부터 생성되었으니 Car 함수의 Prototype Object를 가리키고 있다.

    • bmw객체가 wheel을 직접 가지고 있지 않기 때문에 wheel 속성을 찾을 때 까지 객체가 생성될 때 조상이었던 함수의 Prototype Object를 탐색한다.

    • 최상위인 Object의 Prototype Object까지 도달했는데도 못찾았을 경우 undefined를 리턴한다.

    • 이렇게 __proto__속성을 통해 상위 프로토타입과 연결되어있는 형태를 프로토타입 체인(Chain)이라고 한다.

    • 즉 프로토타입은 다른 객체들과 공유된 속성을 제공하는 객체이다.

prototype 상속 구현

  • new 생성자

    function Person() {
        this.name = "something";
        this.whoIam = function() {
            console.log("I'm " + this.name);
        };
    }
      
    function Rena() {
        this.name = "Rena";
    }
      
    Rena.prototype = new Person();
    var rena = new Rena();
      
    rena.whoIam(); // I'am Rena
    console.log(rena instanceof Rena); // true
    console.log(rena instanceof Person); // true
    console.log(rena.constructor); // Person
    
    • 이렇게 Person함수를 통해 객체를 생성한 다음 다른 함수의 prototype으로 설정함을 통해 new생성자를 이용하여 상속을 구현 할 수 있는것 처럼 보인다.
    • 하지만 constructor의 연결이 깨지게 되는 문제가 발생한다. 실제로 rena를 생성한 Rena()가 아닌 Person()이 생성자로 나오는데 이는 Rena.prototype에 new Person()으로 생성된 객체가 덮어져서 내부적인 생성자 연결이 깨지게 된다.
  • Object.create 생성자

    Object.create = function(obj) {
      function Ghost() {}
      Ghost.prototype = obj;
      return new Ghost();
    };
    
    • 위의 내부 구조를 보면 Ghost의 prototype이 인자로 받는 객체를 참조하도록 한 후 new생성자로 새로운 객체를 생성하여 리런한다. 따라서 Object.create() 함수의 인자에는 상속할 객체가 아닌 객체의 prototype을 설정해야 한다.
    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
      whoIam: function() {
        console.log("I'm " + this.name);
      }
    }
      
    var rena = Object.create(Person.prototype);
    rena.name = "Rena";
    rena.whoIam(); // I'am Rena
    
    • Object.create()의 인자를 보면 Person이 아니라, Object.create(Person.prototype)으로 Person.prototype을 새롭게 생성되는 객체가 참조하도록 하고 있는 것을 볼 수 있다. 이렇게 Person를 직접 넘겨주지 않고 prototype을 넘겨주는 것은 자식 프로토타입과 부모 프로토타입의 연결(체이닝)을 위함이다.
  • new와 Object.create의 결합

    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
      whoIam: function() {
        console.log("I'm " + this.name);
      }
    }
    function Rena() {
        this.name = "Rena";
    }
    Rena.prototype = Object.create(Person.prototype);
    Rena.prototype.constructor = Rena;
    var rena = new Rena();
    rena.whoIam();
    
    • prototype으로 설정하는 객체를 Object.create로 설정하면서 constructor를 다시 설정해주면 된다. 이는 내부적인 constructor 깨짐 문제를 바로 잡아주면서, 기존에 사용하던 new 생성자를 사용할 수 있는 방법이다.

ES6의 클래스 문법

ES6 이전에는 자바스크립트에 클래스(class)가 없었다. 개념 자체는 있었지만, 그것을 구현하려면 class 대신에 prototype을 사용해서 다음과 같이 작업해야 했다.

function Rena(name) {
    this.name = name;
}

Rena.prototype.intro = function() {
    console.log(this.name + '입니다. 반갑습니다.');
}
var rena = new Rena('레나');
rena.intro(); 

ES6 문법부터는 이것과 기능이 똑같은 코드를 class를 사용해서 다음과 같이 작성할 수 있다.

class Rena {
    constructor(name) {
        this.name = name;
    }
    intro() {
        console.log(this.name + '입니다. 반갑습니다.');
    }
}

const rena = new Rena('레나');
rena.intro(); 

SPA (Single Page Application)

어플리케이션 생명주기 중에서 단 한 번만 리소스(HTML, CSS, JS)를 로딩하고, 그 후에는 데이터를 받아올 때만 서버와 통신한다. 즉, 첫 요청시 딱 한 페이지만 불러오고 페이지 이동시 기존 페이지의 내부를 수정해서 보여주는 방식이다. 따라서 자연스러운 페이지 이동과 사용자 경험(네이티브 앱과 같은)을 제공할 수 있다.

SSR (Server Side Rendering)

SPA는 핵심로직들이 Front쪽에서 처리가 되기에 보안상 문제가 발생할 수도 있다. SSR은 핵심 로직을 서버에 두고 처리.

태그:

카테고리:

업데이트: