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__ 외에 handle과 wheel 속성이 추가되어 있다.
-
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은 핵심 로직을 서버에 두고 처리.