면접 스터디

kler_HJ·2020년 6월 30일

INTERVIEW

목록 보기
3/5

1. Promise

콜백 헬을 개선하기위해 등장.
자바스크립트 비동기 처리에 사용되는 객체이다.

  • pending: 비동기처리가 아직 수행되지 않은 상태
  • fullfiled: 비동기처리가 성공적으로 수행
  • rejected: 비동기처리 결과 실패


작업이 성공적으로 수행되면 resolve 호출, 에러가 발생할 경우 reject 호출

const time = new Promise((resolve, reject) => {resolve(response)})

time.then((num)=>console.log(num), ()=>console.log('error'))

// 두번째 인자(reject)보다 catch()를 사용하는 것이
// resolve 내 발생하는 에러도 처리하기위해 더 효율적!! 
time.then(function(result) {
  console.log(result); // hi
  throw new Error("Error in then()");
}).catch(function(err) {
  console.log('then error : ', err); // then error :  Error: Error in then()
});

2. fetch vs axios

basic

라이브러리인 axios는 axios.post(), axios.get( )처럼 바로 request 요청 방식을 메소드의 이름으로 구분해 fetch보다 조금 더 직관적이다.
또한, axios는 body 키 값이 생략 가능하고, body에 포함할 데이터를 JSON.stringify()로 직접 변환해주지 않아도된다.


3. function vs arrow function

  • 일반 함수: 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

- 함수를 호출 할 때 this는 전역객체(window)에 바인딩된다.

var name = "fee";

var sayName = function () {
	var name = "foo";
  	console.log(this.name); // fee 
};

sayName(); // undefined

=⇒ sayName()은 window.sayName()이 생략된 것이므로 this는 전역 객체인 window다.

=⇒ 따라서, function() 속 지역 변수 var name = "foo" 가 아니라,
함수 밖, window.name 인 "fee" 가 출력된다.

- 객체에서의 this는 해당 메서드를 호출한 객체에 바인딩된다.

// myObj
var myObj = {
  name: "foo",
  sayName: function () {
    console.log(this.name)
  },
};
myObj.sayName(); // foo

// otherObj
var otherObj = {
  name: "other",
};

otherObj.sayName = myObj.sayName;

otherObj.sayName(); // other

=⇒ myObj.sayName()은 myObj에 의해 호출되어 this는 myObj이다.

=⇒ otherObj.sayName()은 otherObj에 의해 호출되었으므로 this는 otherObj이다.

![https://velog.velcdn.com/images%2Fkler%2Fpost%2F5ab421cb-fbfd-4eca-8e7c-8be5e865156c%2F%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%2C%202020-07-02%2012-20-25.png%5D(https%3A%2F%2Fimages.velog.io%2Fimages%2Fkler%2Fpost%2F5ab421cb-fbfd-4eca-8e7c-8be5e865156c%2F%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%2C%202020-07-02%2012-20-25.png)

  • Arrow function: 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this는 언제나 상위 스코프의 this를 가리킨다.
// 화살표 함수의 this는 상위스코프인 window
const myObj = {
  name: "foo",
  sayName: () => console.log(this.name) // undefined
};

myObj.sayName(); // undefined

=⇒ arrow function의 this는 언제나 상위스코프의 this이므로, sayName의 this는 myObj의 상위 스코프인 window를 가리킨다. (window.name === undfined)


1줄 정리

  • function() {}은 해당 함수를 호출한 객체(. 앞이 객체)가 this로 전달된다.

    sayName()은 window.sayName()이 생략된 것!!

  • () ⇒ {}은 해당 함수가 선언된 상위 스코프의 this가 전달된다.


참고



4. Hoisting

호이스팅은 var 키워드로 생성된 변수, 함수를 javascript 엔진이 스코프의 최상위로 끌어올리는 것을 말한다.

var 키워드전역 스코프를 가지기 때문에 변수의 선언 + 초기화(undefined)가 실행되어 코드의 제일 위로 끌려 올려지게된다.
(출력 시 undefined)

함수는 function showName()과 같이 function키워드로 선언된 함수에만 호이스팅이 동작하고
(함수 선언 위에서 실제 사용 가능),

var func = function(){} 처럼 변수에 할당된 함수는 undefined를 출력한다.
(var 변수이므로 선언 + 초기화가 이루어짐)

변수의 3단계 생성과정

변수는 아래와 같이 3단계의 과정을 통해 생성된다.

  • 선언단계 : 변수를 실행 컨텍스트의 변수객체에 등록한다.
  • 초기화 단계 : 실행 컨텍스트에 등록 된 변수객체에 대한 메모리를 할당한다. 이 단계에서 변수는 undefined로 초기화 된다.
  • 할당단계 : undefined로 초기화 된 변수에 을 할당한다.

- var 키워드는 선언과 함께 초기화(undefined)가 동시에 일어난다.

console.log(name); // undefined (호이스팅 작동)
var name = "shon";

console.log(aa); // undefined; (호이스팅 작동)
var aa;

console.log(afafa); // ReferenceError: afafa is not defined

let, const => TDZ(Temporal Dead Zone 생성)

- 블록스코프 레벨


- let 키워드는 선언단계와 초기화 단계가 분리되어 진행된다.

{
  console.log(name) // ReferenceError: Cannot access 'name' before initialization
  let name = "shon" // (호이스팅 작동)
  
  
  console.log(sir) // ReferenceError: Cannot access 'name' before initialization
  let sir	   // (호이스팅 작동)
  
  
  console.log(afafa) // ReferenceError: afafa is not defined
}

위의 결과를 보면 let 키워드도 블록 내에서 선언은 Hoisting은 되지만,
변수가 선언되기 전(TDZ)에 접근하면 let 키워드는 아직 초기화(undefined)가 되지 않았으므로,
undefined를 리턴하는 var 달리 에러를 발생시킨다.

- const 키워드는 상수이므로 선언과 동시에 값 할당을 해야한다.

const age = 25; // 선언과 동시에 초기화(할당) 필요
//const age;    // const 변수를 선언만 할 경우 에러 발생
//age  = '25';

var 변수의 사용은 전역으로 메모리에 계속 축적됨으로,
블록 스코프의 범위를 가지는 let과 const의 사용이 더 바람직하다.



5. React Lifecycle

  • constructor -> render -> componentDidMount
  • state 변경 시 re-render 수행
  • componentDidUpdate(prevProps, props)



6. 클래스 컴포넌트 vs 함수형 컴포넌트

  1. 클래스는 무겁다. 클래스 자체가 함수보다 무겁기 때문.
  2. 함수형 컴포넌트는 별도의 라이프 사이클 메소드가 없다.
    클래스 컴포넌트의 라이프 사이클 메소드들이 함수형에서는 useEffect() 하나로 사용되기 때문에 이는 더 가볍고 코드상으로도 직관적인 개발을 가능하게 한다.


7. Typescript

  • types

  • any vs unknown

    • unknown은 Typescript 3.0에 추가되었다.

    • 타입 unknown은 any처럼 모든 타입의 값이 할당될 수 있다.

      let variable: unknown
      
      variable = true // OK (boolean)
      variable = 1 // OK (number)
      variable = 'string' // OK (string)
      variable = {} // OK (object)
    • But, unknown은 any를 제외한 다른 타입에는 할당할 수 없다.

      let variable: unknown
      
      let anyType: any = variable
      let booleanType: boolean = variable
      // Error: Type 'unknown' is not assignable to type 'boolean'.(2322)
      let numberType: number = variable
      //  Error: Type 'unknown' is not assignable to type 'number'.(2322)
      let stringType: string = variable
      //  Error: Type 'unknown' is not assignable to type 'string'.(2322)
      let objectType: object = variable
      //  Error: Type 'unknown' is not assignable to type 'object'.(2322)
    • 'unknown' 이라는 단어가 의미하듯이 아직 타입을 모르는 타입이기 때문이다.

    • 따라서 아래와 같이 객체, 배열, 함수로서의 사용도 불가능하다.

      let variable: unknown
      
      variable.foo.bar // Error: Object is of type 'unknown'.(2571)
      variable[0] // Error
      variable.trigger() // Error
      variable() // Error
      new variable() // Error
    • unknown은 어떤 타입도 될 수 있는 다른 타입을 포괄하는 타입이기 때문에,
      유니온(|)과 인터섹션(&)의 결과는 다음과 같다.

      type unknownType = unknown | string // unknown
      type stringType = unknown & string // string
      


8. 프레임워크 vs 라이브러리

  • 프레임워크 : 개발을 편하게 도와주는 틀. 프레임워크는 우리의 코드를 불러온다.

    • 따라야 할 가이드라인이 있음
    • 개발할 수 있는 범위가 정해짐
  • 라이브러리 : 우리의 코드 상에서 라이브러리를 불러와 사용한다.



9. 세션 vs 쿠키

쿠키와 세션은 HTTP의 클라이언트가 reqeuest를 보내고 서버로부터 response를 받으면 통신의 끝으로, 서버에서 클라이언트의 상태 정보를 유지하지 않는 Stateless 특징을 보완하기 위해 사용된다.

쿠키

  • 쿠키는 4KB
  • 인증에 대한 유효 기간을 정할 수 있고, 기간 내에는 브라우저가 종료되어도 인증이 유효하다.
  • 쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참고한다.
  • response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있다.
  • 쿠키는 브라우저가 Request시에 Header에 넣어서 서버에 전송한다.

쿠키의 동작 방식

  1. 클라이언트가 페이지를 요청
  2. 서버에서 쿠키를 생성
  3. HTTP 헤더에 쿠키를 포함 시켜 응답
  4. 브라우저가 종료되어도 쿠키의 유효 기간 내 클라이언트에서 보관하고 있음
  5. 같은 요청을 할 경우 HTTP 헤더에 쿠키를 함께 보냄
  6. 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트 하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답

쿠키 사용 예시

  • 비로그인 시 쇼핑몰의 장바구니
  • 팝업의 '더이상 보지않기'

세션

  • 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 관리한다.
  • 서버에서는 클라이언트를 구분하기 위해 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지한다.
  • 세션 역시 쿠키처럼 유효 기간 설정이 가능하다.
  • 정보를 클라이언트가 아닌 서버에 저장하기 때문에 보안 측면에서 더 낫다고 볼 수 있다.
  • 사용자의 수가 많아질수록 서버에서 사용하는 메모리가 늘어난다.
  • 클라이언트가 request를 보내면 서버에서 클라이언트를 구분할 수 있는 유일한 세션 ID를 부여한다.

세션의 동작 방식

  1. 클라이언트가 서버에 접속 시 세션 ID를 발급받는다.
  2. 클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 보관한다.
  3. 클라리언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 서버에 전달한다.
  4. 서버는 세션 ID를 전달 받아서 세션 ID로 세션에 있는 클라언트 정보를 가져온다.
  5. 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답한다.

세션 사용 예시

  • 자동 로그인

세션 vs 쿠키

  • 세션은 서버에 사용자의 정보를 저장하고, 쿠키는 클라이언트에 저장한다.
  • 쿠키는 클라이언트에 정보를 저장하기 때문에 변질당하거나 request시 snipping 당할 수 있어 보안에 취약하나, 세션은 쿠키로 세션ID 만을 저장하기 때문에 상대적으로 보안이 뛰어나다고 볼 수 있다.
  • 쿠키는 브라우저 종료에도 남아있지만, 세션은 브라우저 종료 시 소멸된다.
  • 세션의 사용은 서버에서 처리하는 시간이 필요하기 때문에 쿠키보다 느릴 수 있다.

보안

  • 세션과 쿠키 모두 자바스크립트 XXS(cross-site-scripting)공격에 취약하다.
  • but, 쿠키는 HttpOnly로 자바스크립트로 값을 조정하는 것을 막을 수 있으나,
    CSRF(cross-site request forgery)라는 것에 취약하다.
    이는 백에서 HTTP요청을 특정 함수를 통해서만 이루어지도록 하는 방법으로 막을 수 있다.

참고


10. JWT

  • JWT(JSON Web Token)는 session을 사용할 시 사용자 개개인의 session을 관리하기 어려워 사용하는 토큰 기반의 인증 방식이다.
  • 엑세스 토큰의 한 구현체로서 사용자의 속성 정보(claim)를 JSON형태로 갖는다.
  • 토큰은 서버와 클라이언트 사이의 통신에서 유저의 인증여부를 판단하는 Authentication,
    클라이언트의 권한을 파악하기 위해 사용하는 Authorization을 위해 사용한다.

JWT의 구조

  • JWT는 BASE64로 인코딩 된 헤더, 내용, 서명의 3구조로 이루어진다.

  • 헤더: 토큰의 무결성 검증에 사용될 알고리즘과 토큰의 타입이 JSON 타입으로 정의되어있다.

  • 내용: 토큰에 담을 정보가 JSON타입으로 정의되어있다.

    • 토큰의 속성을 정의하기 위해 이름이 정해져있는 registerd claim
    • 해당 토큰이 사용되는 엔드포인트 / 웹페이지를 명시하는 public claim
    • 토큰을 통해 전달하기로 협의된 정보, custom claim이 위치하는 private claim

    => 아이디, 회원 정보 등은 private claim에 포함된다.

  • 서명: 헤더와 내용에 해당하는 BASE64 스트링을 헤더에 명시된 해시 알고리즘으로 해시한 HMAC(Hashed Message Authentication Code)으로 이루어진다.

    => 토큰의 위,변조 방지

    JWT의 원리

  1. 유저가 로그인에 성공하면 향후 서비스 사용에 필요한 private claim 을 담아 JWT을 만들어 유저에게 전송한다.

  2. 유저는 해당 토큰을 request 헤더 또는 URL에 담아 서버로 전송한다.

  3. 서버는 이 토큰의 private claim을 유저의 인증, 권한 확인에 사용한다.

    JWT 장점

  • stateless 적합 : 토큰은 클라이언트 사이드에 저장되므로 stateless 기반 서비스에 적합하다.

  • scalability : 서버 기반 인증시스템에 비해서 서버의 scalability(확장성)이 높다.

  • 보안성 : 세션 인증을 위한 쿠키 전달이 필요 없으므로 보안성 향상된다.

  • extensibility : scalability와는 조금 다른 확장성, 서버의 확장성이 아닌
    로그인이 사용되는 분야, 토큰에 포함되는 정보의 종류 등에 대한 확장성이 높다.

  • CORS : 어떤 디바이스와 도메인에서도 토큰이 유효하면 정상적으로 처리된다.

  • 다양한 환경과 언어를 지원한다.

    JWT 단점

  • Header와 Payload가 단순히 BASE64로 인코딩되어있기 때문에
    클라이언트 사이드에 토큰 내부의 정보가 노출된다.

  • 서비스가 커지면 토큰이 필요로하는 데이터가 커져 토큰이 커지고
    이에 따라 매 HTTP요청의 트래픽 양에 영향을 미칠 수 있다

    참고: https://velog.io/@ikswary/JWT



자료구조


1. 링크드 리스트

배열

  • 배열(리스트)은 생성 시 메모리에 연속적으로 할당된다.
  • 따라서, 배열은 index를 알고 있을 때와 순차적으로 접근할 때 동작 속도가 빠르다.
  • 하지만, 중간에 원소를 삭제하거나 추가할 때, 이후의 모든 원소 위치를 바꿔야 하므로 느리다.

링크드 리스트

  • 링크드 리스트는 data와 다음 노드에 대한 pointer를 갖는 Node의 집합이다.
  • 메모리에 연속적으로 위치하지 않고 다음 노드에 포인터로 연결되므로, 중간 원소의 삽입, 삭제가 빠르다.
  • Node에 data와 함께 포인터를 저장해야 되기 때문에 배열에 비해 메모리가 더 소모된다.

링크드 리스트 예시

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedList {
   constructor() {
     this.head = new Node(0);
   }
   find(data) {
     let curNode = this.head;
     while (curNode.data != data) {
       curNode = curNode.next;
     }
     return curNode;
   }
   insert(item, data) {
     let newNode = new Node(data);
     let curNode = this.find(item);
     newNode.next = curNode.next;
     curNode.next = newNode;
   }
   display() {
     let curNode = this.head;
     while (!(curNode.next == null)) {
       console.log(curNode.data);
       curNode = curNode.next;
     }
   }
   findPrevious(data) {
     let curNode = this.head;
     while (!(curNode.next == null) && (curNode.next.data != data)) {
       curNode = curNode.next;
     }
     return curNode;
   }
   remove(data) {
     let prevNode = this.findPrevious(data);
     if (!(prevNode.next == null)) {
       prevNode.next = prevNode.next.next;
     }
   }
}

const ll = new LinkedList();
ll.insert(0, 1); // 0->1
ll.insert(1, 3); // 0->1->3
ll.insert(3, 4); // 0->1->3->4
ll.insert(1, 2); // 0->1->2->3->4
ll.display(); // 0->1->2->3->4
ll.remove(3);
ll.display();// 0->1->2->4

참고: https://dororongju.tistory.com/108

profile
더 나은 제품과 디자인에 대해 고민하기를 즐기는 Front-end 개발자입니다.

0개의 댓글