캡틴판교님 블로그에서 좋은 게시글을 발견해서 이렇게 제 블로그에 끄적여 봅니다.
아직 갈 길이 멀고도 멀었지만, 저는 아직 외우지 않고 술술 대답할 능력이 되지 않기 때문에 이전에 썼던 내용이라도 다시 한 번 찾아보면서 정리를 해보려 합니다. 질문들은 모두 캡틴판교님 블로그에서 발췌하였습니다.
# 단골 질문 : 기본적으로 다 알고 대답할 수 있어야 함
1. 브라우저 저장소에 대해서 설명해 보세요.
1) 로컬스토리지
유효기간 없이 데이터를 저장.
JavaScript를 사용하거나, 브라우저 캐시 또는 로컬 저장 데이터를 지워야만 사라짐.
저장 공간이 셋 중 제일 크다.
브라우저를 닫았다가 열어도 데이터가 남아있다.
2) 세션스토리지
각각의 출처에 대해 독립적인 저장 공간을 브라우저가 열려있는 동안 제공한다.
데이터를 절대 서버로 전송하지 않는다.
저장공간이 쿠키보다 크다. (5MB)
브라우저 또는 탭이 닫힐 때까지만 데이터를 저장한다.
3) 쿠키 (HTTP 쿠키 or 웹 쿠키 또는 브라우저 쿠키)
사용자의 브라우저에 전송하는 작은 데이터 조각.
브라우저는 조각들을 저장해 놓았다가, 동일한 서버에서 재 요청 시 저장된 데이터를 함께 전송한다.
쿠키는 두 요청이 동일한 브라우저에서 들어왔는지 아닌지 판단할 때 주로 사용한다. 이를 이용하면 사용자 로그인 상태를 유지 할 수 있다. stateless한 HTTP 프로토콜에서 상태 정보를 기억시켜주기 때문에.
쿠키의 사용 목적
1. 세션 관리: 서버에 저장해야 할 로그인, 장바구니, 게임 스코어 등의 정보 관리
2. 개인화: 사용자 선호, 테마 등의 세팅
3. 트래킹: 사용자 행동을 기록하고 분석하는 용도
#과거에는 클라이언트 측에 정보 저장할 때 쿠키를 주로 사용했다. 이것이 유일한 방법이었기 때문에. 지금은 modern storage APIs를 사용해 정보를 저장하는 것을 권장한다. 모든 요청마다 쿠키가 함께 전송되면 성능이 떨어지는 원인이 될 수 있기 때문에. 정보를 클라이언트 측에 저장하려면 Modern APIs의 종류인 웹스토리지 API(localStorage와 sessionStorage)와 IndexedDB를 사용하면 된다.
Reference: MDN HTTP쿠키, Web Storage API
2. 자바스크립트 this란?
자바스크립트에서 this는 일반적으로 함수를 호출하는 객체에 대한 참조이다. 또한 엄격모드와 비엄격 모드에서 일부 차이가 있음.
ES5는 함수를 어떻게 호출했는지 상관하지 않고 this를 설정할 수 있는 bind 메서드를 도입했음
ES2015는 스스로의 this바인딩을 제공하지 않는 화살표 함수를 추가했다. (렉시컬 컨텍스트안의 this 값을 유지)
# binding이란 무엇인가?
값들이 확정되어 더 이상 변경할 수 없는 구속 상태가 되는 것을 말한다.
# 전역 객체(Global Object)
전역 객체는 모든 객체의 유일한 최상위 객체를 말한다. 브라우저 환경은 window, Node.js 환경에서는 global 객체를 의미.
# 함수가 호출되는 방식에 따라 this는 달라지게 된다.
1) 전역에서 호출되는 this : 엄격 모드 여부에 관계없이 window 객체를 참조한다.
console.log(this) // window {...}
2) 일반 함수를 호출 했을 때 this : window인 전역 객체 참조, 엄격모드이면 undefined.
function whatIsThis(){
console.log(this);
}
whatIsThis(); // window
this의 값이 호출에 의해 설정되지 않으므로, 기본값으로 브라우저에서는 window인 전역 개체를 참조한다.
반면 엄격모드에서 this 값은 실행 문맥에 진입하며 설정되는 값을 유지하므로 다음 예시에서 보여지는 것 처럼 this는 undefined로 남음.
function whatIsThis() {
'use strict'
console.log(this);
}
whatIsThis(); // undefined
3) 객체 메서드(method)의 this : 객체의 method로 호출시 this는 해당 객체를 가리킨다.
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: eatAppleFn
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다`);
}
// (1) 객체 method 호출
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
// (2) 함수 직접 호출
eatAppleFn(); // 백설공주가 독이 든 사과를 먹습니다.
(1) home의 메서드로 호출: 이경우 호출한 객체를 가리킨다.
(2) 직접 호출: 어디에도 bind 되어있지 않고 전역에서 독립적으로 실행. 이경우 this는 window이므로 독이든 사과가 된다.
같은 함수를 호출하더라도 어떻게 호출 했느냐에 따라 this가 참조하는 객체가 다르다.
(3) 프로토타입의 this도 호출한 객체를 가리킨다.
var apple = '독이 든 사과';
function Home() {
this.apple = '맛있는 사과';
}
Home.prototype.eatApple = function() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`)
}
var home = new Home();
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
(4) 내부 함수의 this는 전역 객체 (window)를 바인딩 한다.
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
eatAppleFn();
}
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
home.eatApple(); // 백설공주가 독이 든 사과를 먹습니다.
바인딩 없이 일반 함수의 형태로 호출된 eatAppleFn() 함수는 window를 바인딩한다.
4) call, apply, bind를 이용한 호출 : 이들을 이용하면 this를 원하는 객체에 연결할 수 있다.
내부 함수의 경우에는 this가 window를 나타내는 것을 확인했다. 내부 함수를 사용하면서도 this가 객체를 가리키도록 연결하는 방법은 뭘까?
Call, bind를 이용한 예제
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
eatAppleFn();
},
eatAppleCall: function() {
// 여기서의 this는 home
eatAppleFn.call(this)
},
eatAppleBind: function() {
// 여기서의 this는 home
(eatAppleFn.bind(this))();
}
}
function eatAppleFn() {
console.log(`백설공주가 ${this.apple}를 먹습니다.`);
}
home.eatApple(); // 백설공주가 독이 든 사과를 먹습니다.
home.eatAppleCall(); // 백설공주가 맛있는 사과를 먹습니다.
home.eatAppleBind(); // 백설공주가 맛있는 사과를 먹습니다.
위에서 eatAppleCall과 eatAppleBind 메서드의 this는 home이다.
이 'this'를 call 또는 bind의 인자로 제공해서 eatAppleFn 함수 내에서의 this로 사용할 수 있다. 이제 백설공주는 home 안의 '맛있는 사과'를 먹을 수 있다.
이와 같이 call, apply, bind를 사용하면 내부함수의 this를 원하는 객체로 바인딩 할 수 있다.
var that = this
var apple = '독이 든 사과';
var home = {
apple: '맛있는 사과',
eatApple: function() {
var that = this; // this == home
(function() {
console.log(`백설공주가 ${that.apple}를 먹습니다.`);
})();
}
}
home.eatApple(); // 백설공주가 맛있는 사과를 먹습니다.
that은 결국 외부의 'this'를 내부 함수로 전달하기 위해서 사용하는 변수 선언이다. bind, call로 대체 할 수 있다.
5) 생성자 함수: 생성자 함수의 this는 새로 생성된 객체를 가리킨다.
var sejelye = '백설공주';
function mirrorReply() {
console.log(`세상에서 제일 예쁜 사람은 ${this.sejelye}입니다.`)
}
// 생성자 함수
function MagicMirror(name) {
this.sejelye = name;
this.reply = mirrorReply;
}
// 새로운 거울 생성
var newMirror = new MagicMirror('왕비님');
newMirror.reply(); // 세상에서 제일 예쁜 사람은 왕비님입니다.
new keyword를 사용하는 경우 생성자 함수 내의 this를 통해 프로퍼티와 메서드를 추가할 수 있습니다. 생성자 함수 내의 this는 새로 생성된 객체를 가리키며 생성자 함수는 암묵적으로 this를 return합니다. 따라서 newMirror는 다음과 같은 객체가 된다.
mirrorReply() 함수는 newMirror의 메서드로 호출되므로 3번에서 처럼 함수 내부의 this는 newMirror를 가리킨다. 즉 newMirror안의 'sejelye'를 찾아서 대답함.
번외 : ES6 클래서에서 생성자 내 this는 생성한 인스턴스를 가리킨다.
var sejelye = '백설공주';
class MaginMirror {
constructor(name) {
this.sejelye = name;
}
mirrorReply() {
console.log(`세상에서 제일 예쁜 사람은 ${this.sejelye}임니다.`);
}
}
var newMirror = new MaginMirror('왕비님');
newMirror.mirrorReply(); // 세상에서 제일 예쁜 사람은 왕비님입니다.
6) DOM event handler에서의 this
함수를 event handler로 사용하는 경우, this는 이벤트를 발생시킨 요소로 설정된다. EventHandler에서의 this는 handler 함수를 등록한 element를 가리킨다.
EventHandler에서의 this는 handler 함수를 등록한 element를 가리킨다. 만약 외부의 this를 참조하려면 앞에서 살펴본 bind 등을 이용해야 한다.
<button id="btn1">eventHandler</button>
<button id="btn2">eventHandler_bind</button>
<script>
function alertThis() {
alert('this: ' + this);
}
document.getElementById("btn1").addEventListener("click", alertThis); // this: HTMLButtonElement
document.getElementById("btn2").addEventListener("click", alertThis.bind(this)); // this: window
</script>
btn1 : EventListener를 등록하면서 bind 사용 안함. => this = 함수 등록한 버튼 element
btn2 : Event handler에 bind(this)를 사용 => this = Window
7) 화살표 함수에서 this : 상위 스코프의 this를 의미한다.
var sejelye = '백설공즈';
var maginMirror = {
sejelye: '여왕님',
reply_arrow: function() {
setTimeout(()=>{
console.log(`화살표 거울: 세상에서 제일 예쁜 사람은 ${this.sejelye}입니다.`)
},1000)
}
}
magicMirror.reply_arrow();
중요한 점은 화살표 함수의 this는 함수가 호출될 때가 아닌 함수가 정의될 때 이미 결정되어 버린다는 것.
var arrow_outer = () => {console.log(this);};
var obj = {
inner: function() { (()=>{console.log(this)})()},
outer: function() {arrow_outer()}
}
obj.inner(); // obj
obj.outer(); // window
# 화살표 함수를 사용하면 안되는 경우는?
1) 생성자 함수로는 사용할 수 없다.
var arrow = () => {console.log(this);};
var getFromArrow = new arrow(); //Uncaught TypeError : arrow is not a constructor
2) 객체 또는 프로토타입 메서드에서 객체를 참조해야 하는 경우
var maginMirror = {
sejelye: '여왕님',
reply: () => {
console.log(`세상에서 제일 예쁜 사람은 ${this.sejelye}입니다.`);
}
}
magicMirror.reply(); // 세상에서 제일 예쁜 사람은 undefined입니다.
3) DOM EventHandler에서 Element에 접근해야 하는 경우
<button id="btn1">button_regular</button>
<button id="btn2">button_arrow</button>
<script>
document.getElementById("btn1").addEventListener("click", function(){alert()this.id}); // btn1
document.getElementById("btn2").addEventListener("click", ()=>{alert(this.id)}) // undefined
</script>
# 정리
1) 기본적으로 this는 전역 객체를 참조한다. (브라우저에서 Window, 엄격모드에서는 undefined)
2) 일반 함수, 내부 함수의 this는 전역 객체 참조
3) 객체 메서드에서의 this는 해당 메서드를 호출한 객체를 참조한다.
4) 생성자 함수 내부의 this는 생성된 객체를 참조한다.
5) DOM event handler로 사용되는 함수 내부의 this는 이벤트 발생한 element로 설정된다.
6) 화살표 함수의 this는 상위 스코프 this를 의미한다.
3. 자바스크립트 이벤트 관리 방법? 보통 어떤 식으로 이벤트를 설계해야 하는지?
# 이벤트의 전파
1) 이벤트 버블링 : Event Bubbling
하위 엘리먼트에서 이벤트가 일어났을때 그 이벤트가 상위 엘리먼트까지 전달되는 특성
2) 이벤트 캡쳐링: Event Capturing
상위 엘리먼트에서 일어난 이벤트가 하위 엘리먼트로 전달되는 것
event.stopPropagation()을 하면 이벤트의 버블링, 캡쳐링을 막을 수 있다. 버블링에서는 타겟 엘리먼트에만 이벤트가 발생하도록 하고, 캡쳐링에서는 타겟 엘리먼트 기준 최상단 엘리먼트에만 이벤트가 발생 하도록 해준다.
# 이벤트 위임 패턴(delegation)을 사용해야 하는 이유?
이벤트 위임이란 공통된 부모 요소에 이벤트 리스너를 추가하여 하위 요소에서 일어나는 이벤트들을 부모에 등록된 핸들러에서 관리하는 것
엘리먼트 갯수가 적을 땐 괜찮지만 많아지면 일일이 이벤트리스너를 달아주기는 너무 번거롭기 때문에 사용하면 좋음.
메모리 사용량이 줄어들고 메모리 누수 가능성이 줄어든다. addEventListener의 수가 적어지니 메모리 부담이 적다.
상위 엘리먼트에서 이벤트리스너를 관리해서 하위 엘리먼트는 자유롭게 추가 삭제가 가능하다.
Reference: 왜 이벤트 위임을 해야 하는가?
4. 자바스크립트 비동기 처리에 대한 설명
비동기 : 요청과 결과가 동시에 일어나지 않을 거란 약속
동기 : 요청을 보낸 후 응답이 오기 전까지는 다른 동작을 시행할 수 없음. 순차적으로 실행됨
# 자바스크립트 비동기 처리
1) Callback 함수
어떤 함수의 인자로 전달되는 또 다른 함수. setTimeout 방식으로 사용되며, 콜백 내부에서만 값을 가진다. 처리 순서를 보장하기 위해 함수를 중첩하게 사용해 Callback hell이 발생한다. 에러 핸들링도 각 콜백함수마다 해주어야 해서 가독성이 떨어진다.
2) Promise
callback hell 개선을 위해 도입. 3가지 상태인 pending, fulfilled, reject를 가지고 어떤 시점에 결과값을 반환하겠다는 약속이다. resolve와 reject라는 자바스크립트에서 자체 제공하는 콜백들 중 하나가 호출된다. resolve 호출시 이행상태로 됨. then메서드로 결과 값을 받고, catch()로 에러 핸들링을 한다. then 내부에서 then이 나오는 중첩된 promise 때문에 가독성이 떨어질 수 있음.
3) Async & Await
프로미스 객체를 좀 더 편하게 사용할 수 있는 문법. await를 사용할 곳을 함수에 async를 쓰고, 프로미스 객체 앞에 await를 쓰면 프로미스 객체 result를 추출 할 수 있다. try-catch 문을 사용해 에러를 핸들링 해 주어야한다. then에 비해 코드가 간결하다.
5. 프런트엔드 개발자로서 어떻게 공부하고 계신가요? - 타입스크립트가 무엇인가요?
타입스크립트는 자바스크립트를 기반으로 정적 타입 문법을 추가한 프로그래밍 언어입니다.
자바스크립트는 동적 타입의 인터프리터 언어로 런타임에서 오류를 발견할 수 있다.
타입스크립트는 정적 타입의 컨파일 언어기에 타입스크립트는 컴파일 환경에서 에러가 발생한다. 런타임 오류는 최악의 경우 프로그램이 멈춰버리기 때문에 최악의 사용자 경험을 하게 될 수도 있다.
타입스크립트는 코드 작성 단계에서 타입을 체크해 오류를 확인할 수 있고 미리 타입을 결정해서 실행 속도는 빠르지만,
코드 작성시 매번 타입을 결정해야해서 번거롭고 코드량이 증가하며 컴파일 시간이 오래걸린다는 단점이있습니다.
# 그럼에도 불구하고 타입스크립트 사용을 고려해야 하는 이유
타입스크립트는 코드에 목적을 명시하고 목적에 맞지 않는 변수들이 나 함수들에 에러를 발생시켜 버그를 사전에 제거할 수 있다.
코드 자동완성이나 실행 전 피드백을 제공해 작업과 동시에 디버깅이 가능하다.
자바스크립트와 100% 호환이 가능하다.
'공부 정리' 카테고리의 다른 글
22.11.22 : CRA없이 프로젝트 생성하기-1 (왜?) (0) | 2022.11.22 |
---|---|
22.11.19 : 잘 안 되었던것, 톺아보기 (0) | 2022.11.19 |
22.11.10 : Atomic design pattern (0) | 2022.11.10 |
22.11.09 : SSR, CSR (TTV, TTI) (0) | 2022.11.09 |
22.11.09 : webpack (0) | 2022.11.09 |