프론트엔드 면접 질문 문답

남정호·2023년 4월 10일
6
post-thumbnail

많은 부분을 아래 링크들에서 퍼왔다.
퍼온 답변도 많아서 중간에 존댓말 섞여있다.

✅ 기술 스택 단위로 쪼갤라 했는데 그냥 한 번에 보는게 내가 편해서 안 쪼갬
✅ 내용은 계속 추가될듯

댓글 알림이 안 떠서 급한 건은 rainy_waltz@naver.com 으로

🚩 자바스크립트 - 기본

✨ 자바스크립트란?

자바스크립트는 개발자가 별도의 컴파일 작업을 수행하지 않는 인터프리터 언어(Interpreter language)이다.

자바스크립트는 명령형(imperative), 함수형(functional), 프로토타입 기반(prototype-based) 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어다.

컴파일러(compiler) (C, C++, C#, JAVA)
고급 언어로 작성 된 소스 코드를 저급 언어로 번역하는 프로그램을 가리킨다.

인터프리터(interpreter) (Python, Javascript, Ruby)
프로그래밍 언어의 소스 코드를 바로 실행하는 컴퓨터 프로그램을 말한다.

Ref

✨ 브라우저 엔진 vs nodejs

브라우저 엔진: ECMAScript + WebAPI(DOM, Canvas, XMLHttpRequest, Fetch, ...)
nodejs: ECMAScript + Nodejs 고유 API

Ref

✨ JavaScript와 ECMAScript

ECMAScript는 자바스크립트의 표준 명세인 ECMA-262를 말하며 프로그래밍 언어의 타입, 값, 객체와 프로퍼티, 함수, 빌트인 객체 등 핵심 문법(core syntax)을 규정한다. 각 브라우저 제조사는 ECMAScript를 준수하여 브라우저에 내장되는 자바스크립트 엔진을 구현한다.

자바스크립트는 일반적으로 프로그래밍 언어로서 기본 뼈대를 이루는 ECMAScript와 브라우저가 별도 지원하는 클라이언트 사이드 Web API, 즉 DOM, BOM, Canvas, XMLHttpRequest, Fetch, requestAnimationFrame, SVG, Web Storage, Web Component, Web worker 등을 아우르는 개념이다.

클라이언트 사이드 Web API는 ECMAScript와는 별도로 World Wide Web Consortium (W3C)에서 별도의 명세로 관리하고 있다. 클라이언트 사이드 Web API의 자세한 내용은 MDN web docs: Web API를 참고하기 바란다.

Ref

🚩 자바스크립트 - ECMAScript

✨ 실행 컨텍스트

자바스크립트 면접 질문은 사실상 실행 컨텍스트에서 끝난다. 맨날 나오는 그 놈의 클로저랑 호이스팅도 결국 실행 컨텍스트와 관련이 있다. 반드시 이해하고 면접장에 들어가자.

실행 컨텍스트의 생성 기준

ECMAScript 사양은 소스코드를 4가지 타입으로 구분한다. 이 4가지 타입의 소스코드는 실행 컨텍스트를 생성한다.

이 중 실제로 마주하는 코드는 대부분 전역코드함수 코드이다.

소스코드 평과와 실행

자바스크립트 엔진은 소스코드를 2개의 과정,"소스코드 평가""소스코드의 실행"과정으로 나누어서 처리한다.

소스코드 평가 과정에서는 실행 컨텍스트를 생성한다.
소스코드 실행 단계에서는 실행에 필요한 정보를 실행 컨텍스트에서 얻으며 소스 코드의 실행 결과는 다시 실행 컨텍스트가 관리한다. (소스코드 실행 단계를 런타임이라고 한다.)

실행 컨텍스트의 구성

실행 컨텍스트가 생성이 되면 LexicalEnvironmentVariableEnvironment 를 생성한다.
VariableEnvironment 는 변하지 않지만, LexicalEnvironment 는 코드가 실행됨에 따라 변하기도 한다.

Lexical & Variable Env 까지 설명하면 날 샐 수 있으니 그냥 실행 컨텍스트로 퉁치는게 좋다. 지금부터는 LexicalEnvironment 를 그냥 실행 컨텍스트라고 부를 것이다.

실행 컨텍스트는 변수들을 저장하는 EnvironmentRecord 와 상위 스코프의 레퍼런스인 OuterLexicalEnvironmentReference 로 구성되어있다.

변수 저장 (호이스팅)

혼란을 막기위해 const, let 을 제외하고 var 변수함수 선언문에 대해서만 먼저 설명한다.
함수 평가단계에서 자바스크립트 엔진은 스코프 내에 선언된 var 변수와 함수 선언문으로 정의한 함수를 EnvironmentRecord 에 저장한다.

이 때, var 변수는 변수의 생성 3단계인 선언 - 초기화 - 할당 중 초기화 단계까지 처리되며 함수 선언문은 할당까지 전부 처리된다.

이러한 특징 때문에 자바스크립트 런타임에서 해당 선언부까지 가지 않아도 var 변수와 함수에 접근할 수 있는 것이다. 다만, var 변수는 할당되기 전까지는 undefined로 존재하며 함수는 바로 실행이 가능하다.

이러한 특징을 자바스크립트의 호이스팅이라고 부른다.

const, let 변수의 저장 (const, let 의 호이스팅)

앞서 설명한 바와 같이 실행 컨텍스트는 특별한 경우를 제외하고는 전역 코드, 함수 코드에서 생성된다. 이 말인즉 블록 스코프에서는 실행 컨텍스트가 생성되지 않는다는 뜻이다.

사실 Environment Recordvar 변수함수 선언문을 저장하는 Object Enviroment Recordconst, let 변수를 저장하는 Declarative Environment Recored 로 나누어져있다.

이 중 Declarative Environment Recored 는 스택의 형태로 관리 되며, 자바스크립트 런타임에서 블록을 만나면 새로운 레코드가 생성되어 스택에 쌓이게 된다.

Declarative Environment Recored 가 생성되면 블록 스코프 내의 const 와 let 변수가 레코드에 저장된다. 이 때 선언 단계까지만 처리가 되는데, 이 때문에 const, let 변수의 선언문에 도달하기 전에 해당 변수를 호출하면 초기화가 되지 않았다는 에러가 발생한다.

참고로 블록 스코프 시작부터 변수 선언문 까지를 TDZ(temporal dead zone) 라고 부른다.

스코프 체이닝
자바스크립트 런타임중 변수를 사용하는 코드를 만나면 실행 컨텍스트의 Environment Recored 에서 해당 변수를 검색한다. 이 때, 현재 실행 컨텍스트의 레코드에서 변수가 발견되지 않으면 상위 스코프의 실행 컨텍스트를 검색하는데 이 것을 스코프 체이닝이라고 한다.

실행 컨텍스트는 상위 스코프에 대한 레퍼런스를 가지고 있기 때문에 이와 같은 동작이 가능하다. 이러한 특징은 클로저와 아주 밀접하게 관련이 되어있다.

Ref

✨ 클로저

클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수이다.

자바스크립트는 런타임에서 변수를 사용할 때에 위에서 설명한 스코프 체이닝을 통해 변수를 검색한다. 예를들어 outer 함수에서 정의한 변수를 inner 함수에서 사용할 경우, inner 함수의 Lexical Environment에 저장된 OuterLexicalEnvironmentReference 를 통해서 outer 함수의 Lexical Environment 에 접근해서 값을 가져온다.

비록 outer 함수는 종료되어서 실행 컨텍스트가 제거되었지만, outer 함수의 Lexical Environment 는 inner 함수의 Lexical Environment 에 의해 참조가 되고 있으므로 가비지 컬렉터에 의해 제거 되지 않고 있게 된다.

이러한 원리로 outer 함수가 종료되어 실행 컨텍스트가 제거된 이후에도 outer 함수의 Lexical Environment 에 접근하여 outer 함수에서 정의한 변수를 사용할 수 있는 것이다.

이러한 원리를 클로저라고 부른다.

Ref

✨ 호이스팅

실행 컨텍스트에서 설명했다.

Ref

✨ this

  1. 함수 호출
  2. 메소드 호출
  3. 생성자 함수 호출
  4. apply/call/bind 호출

자바스크립트의 경우 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정된다. 다시 말해, 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

화살표 함수의 this

화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다.

Ref

✨ 진짜 상수

js 의 참조값들은 언제든지 property 에 접근하여 값을 변경할 수 있다.

Object.freeze() 메소드를 사용하면 진짜 상수를 만들수 있다.

Ref

✨ 원시 값의 메서드

자바스크립트는 원시 값의 메서드와 프로퍼티에 접근할 수 있도록 언어 차원에서 허용합니다. 이를 가능하게 하기 위해, 원시값이 메서드나 프로퍼티에 접근하려 하면 추가 기능을 제공해주는 특수한 객체, "원시 래퍼 객체(object wrapper)"를 만들어 줍니다. 이 객체는 메서드를 실행하거나 프로퍼티를 반환한 후 삭제됩니다.

let str = "Hello";

alert( str.toUpperCase() ); // HELLO

위의 코드는 다음과 같이 동작합니다.
1. 문자열 str은 원시값이므로 원시값의 프로퍼티(toUpperCase)에 접근하는 순간 특별한 객체가 만들어집니다. 이 객체는 문자열의 값을 알고 있고, toUpperCase()와 같은 유용한 메서드를 가지고 있습니다.
2. 메서드가 실행되고, 새로운 문자열이 반환됩니다(alert 창에 이 문자열이 출력됩니다).
3. 특별한 객체는 파괴되고, 원시값 str만 남습니다.

Ref

✨ 모듈 시스템

ESM
ES6 에 추가된 ECMA Script공식 모듈 시스템이다. ES6 이전에는 js에 모듈 시스템이 없어서 commonjs, amd 등의 방식이 나왔으나, 현재는 노드에서 채택한 commonjs 방식 정도만 사용한다.

Common JS
이 방식은 노드에서 채택한 방식으로 매우 유명합니다. 노드 강좌에서도 다뤘습니다. 보통 서버사이드에서는 CommonJS를 더 자주 씁니다.

AMD
AMD는 Asynchronous Module Definition으로 비동기적 모듈 선언이란 뜻입니다.
AMD가 목표로 하는 것은 필요한 모듈을 네트워크를 이용해 내려받아야 하는 브라우저 환경에서도 모듈을 사용할 수 있도록 표준을 만드는 일이다.

UMD
모듈 시스템을 개발한 것 까지는 좋은데 문제는 다양한... 이라는 것이죠. AMD와 CommonJS를 쓰는 두 그룹으로 나누어지다보니 서로 호환이 안 되게 되었습니다. 그래서 나온 것이 UMD입니다. 어떤 모듈을 쓰든지 동작되게 하기 위한 것이죠.

필요한 파일이 모두 로컬 디스크에 있어 바로 불러 쓸 수 있는 상황, 즉 서버사이드에서는 CommonJS 명세가 AMD 방식보다 간결하다. 반면 필요한 파일을 네트워크를 통해 내려받아야 하는 브라우저와 같은 환경에서는 AMD가 CommonJS보다 더 유연한 방법을 제공한다.

Ref

✨ Promise vs async await

Ref

✨ 1급 객체

  1. 함수의 실질적인 매개변수가 될 수 있다.
  2. 함수의 반환값이 될 수 있다.
  3. 할당의 대상이 될 수 있다.
  4. 비교 연산(==, equal)을 적용할 수 있다.

Ref

✨ ES2022

1. Top-Level await

2. .at() method

console.log([1, 2, 3].at(-1)) // 3

3. Object.hasOwn

4. Error Cause

try{
  throw new Error("Failed", {cause:"some reason"})
} catch (e){
  console.log(e.cause)
}

5. Private class fields

class Circle {
  // private field
  #radius;
  constructor(value) {
    // You can access private field from constructor
    this.#radius = value;
  }
  get area() {
    return Math.PI * Math.pow(this.#radius, 2);
  }
}

Ref

✨ ES2015(ES6)

  1. 기본 매개 변수 (Default Parameters)
  2. 템플릿 리터럴 (Template Literals)
  3. 멀티 라인 문자열 (Multi-line Strings)
  4. 비구조화 할당 (Destructuring Assignment)
  5. 향상된 객체 리터럴 (Enhanced Object Literals)
  6. 화살표 함수 (Arrow Functions)
  7. Promises
  8. 블록 범위 생성자 Let 및 Const (Block-Scoped Constructs Let and Const)
  9. 클래스 (Classes)
  10. 모듈 (Modules)

Ref

🚩 자바스크립트 - Web

✨ 이벤트 루프

Ref

✨ Ajax

Ajax는 JavaScript의 라이브러리중 하나이며 Asynchronous Javascript And Xml(비동기식 자바스크립트와 xml)의 약자이다. 브라우저가 가지고있는 XMLHttpRequest 객체를 이용해서 전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 로드하는 기법이다. Ajax를 한마디로 정의하자면 JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 XML 데이터를 주고받는 기술이라고 할 수 있다.

ajax는 html 페이지 전체가아닌 일부분만 갱신할수 있도록 XML HttpRequest객체를 통해 서버에 request한다.

Ref

✨ Event Delegation

🚩 Typescript

✨ typescript vs javascript

  1. 타입스크립트는 컴파일언어다. 때문에 컴파일 단계에서 타입을 검사하여 사전에 오류 발견이 가능하다. 자바스크립트의 경우 인터프리터 언어이며 런타임 단계에서 오류가 발견된다.
  2. 타입스크립트는 정적 타입 언어로, 변수의 타입을 미리 지정해주어야 한다. 반면 자바스크립트는 동적 타입 언어로, 변수의 타입이 런타임 시에 결정된다.

✨ Generic

제네릭은 타입을 일반화하여 재사용 가능한 코드를 작성하는 기술이다. 함수나 클래스에서 사용될 타입을 미리 지정하지 않고, 호출할 때 지정할 수 있게 해준다. 제네릭을 사용하면 코드의 유연성과 재사용성이 높아진다.

function identity<T>(arg: T): T {
  return arg;
}

const result = identity<string>("hello");

✨ Extend of interface

인터페이스의 상속은 이미 정의된 인터페이스를 확장하여 새로운 인터페이스를 만드는 것이다. 상속받은 인터페이스의 속성과 메서드를 그대로 사용할 수 있으며, 추가적인 속성과 메서드를 정의할 수도 있다.

✨ Advanced types

교차 타입 (Intersection Types)

interface Shape {
  color: string;
}

interface Size {
  width: number;
  height: number;
}

type ShapeAndSize = Shape & Size;

const square: ShapeAndSize = { color: "red", width: 10, height: 10 };

유니온 타입 (Union Types)

function padLeft(value: string, padding: string | number) {
  // ...
}

타입 가드와 차별 타입 (Type Guards and Differentiating Types)

function isFish(pet: Fish | Bird): pet is Fish {
    return (pet as Fish).swim !== undefined;
}

pet is Fish는 이 예제에서의 타입 서술어이다. 서술어는 parameterName is Type 형태이고, parameterName는 반드시 현재 함수 시그니처의 매개변수 이름이어야 한다.

조건부 타입 (Conditional Types)

Ref

✨ Object vs object vs {}

Object
Contains stuff (like toString(), hasOwnProperty()) that is present in all JavaScript objects. Any value (primitive, non-primitive) can be assigned to Object type.

{}
{}는 빈 오브젝트이다. Object 와 기본적으로 비슷하지만 컴파일 타임에서 다르게 동작한다. {} 는 Object 의 멤버들을 가지고 있지 않다.

object
object 는 Typescript 2.2 에 추가되었다. object는 모든 참조값 타입들에 적용이 가능하다. bool, number, string, symbol 과 같은 원시 값들에는 사용할 수 없다.

Ref

✨ interface vs type

1. Function을 설정하는 방법이 조금 다르다.

interface

interface SetPoint {
  (x: number, y: number): void;
}

type

type SetPoint = (x: number, y: number) => void;

2. type은 interface와 달리 원시값, union, tuples 을 사용할 수 있다.

// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

3. syntax는 다르지만 둘 다 Extend 기능을 사용할 수 있으며, 상호 extend 가 가능하다.

Interface extends interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }

Type alias extends type alias

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

Interface extends type alias

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

Type alias extends interface

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

4. class 는 interface, type 모두 implements 할 수 있지만, union 타입은 불가하다.

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// FIXME: can not implement a union type
class SomePartialPoint implements PartialPoint {
  x = 1;
  y = 2;
}

5. Declaration merging - interface 는 여러번 선언이 가능하다.

// These two declarations become:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };

Ref

✨ any vs unknown

unknown 은 any의 일부분이라고 볼 수 있다. 아무 값이나 할당이 가능하다.
다만, any로 선언된 변수는 아무 타입에나 할당 가능하지만, unknown 으로 선언된 변수는 unknown 타입에만 할당이 가능하다.

let vAny: any = 10;          // We can assign anything to any
let vUnknown: unknown =  10; // We can assign anything to unknown just like any 


let s1: string = vAny;     // Any is assignable to anything 
let s2: string = vUnknown; // Invalid; we can't assign vUnknown to any other type (without an explicit assertion)

Ref
https://stackoverflow.com/questions/51439843/unknown-vs-any

🚩 리액트

✨ React의 주요 특징

  • UI 컴포넌트를 만들기 위한 라이브러리이며 React의 컴포넌트는 트리형태로 구성된다.
  • Virtual DOM을 사용하여 변경된 부분에 대한 최소한의 DOM 처리로 UI를 업데이트하여 애플리케이션의 성능을 향상한다.
  • 부모 컴포넌트에서 하위 컴포넌트로 전달하는 단방향의 단순한 데이터 흐름을 갖고 있어 데이터 추적과 디버깅을 쉽게 해준다.

Ref

✨ 왜 리액트는 라이브러리인가?

  • 리액트는 템플릿이나 디자인 패턴을 가지고 있지 않다. 어플리케이션의 구조에 전혀 관여하지 않는다.
  • 제어의 역전의 여부는 프레임워크를 정의하는 좋은 기준이다. 리액트의 사이클이 제어의 역전을 나타내기 때문에 프레임워크라고 볼 수도 있다. 하지만 라이프 사이클은 구조적 문제를 해결하거나 아키텍쳐를 정의해주지 않는다.
  • 본질적인 프레임 워크는 코드 레벨에서 구조적 문제를 해결하거나 및 아키텍쳐적를 정의한다. 하지만 리액트는 app level에서 구조적, 아키텍쳐적 문제를 해결하지 못한다. 리액트는 단지 fe 를 핸들링하는데 도움이 되는 method를 제공해줄 뿐이다.
  • Angular 나 vue 의 경우에는 프로젝트를 set up 하면 앱을 만들기 위한 거의 모든 요소들을 세팅해주며 큰 규모의 앱에 필요한 리소스들을 포함시켜준다. 하지만 리액트는 그렇지 못하다.

Ref

✨ Virtual DOM

Virtual DOM은 실제 DOM의 구조와 비슷한, React 객체의 트리다. 개발자는 직접 DOM을 제어하지 않고 Virtual DOM을 제어하고, React에서 적절하게 Virtual DOM을 DOM에 반영하는 작업을 한다.

state에 변경사항이 발생하면, 새로운 Virtual DOM이 생성된다. 새롭게 생성된 Virtual DOM tree는 real DOM tree 와 "diffing" 과정을 거쳐 real DOM에 변경 사항을 반영할 수 있는 최적의 방법을 계산한다. 계산이 끝나면 한 번에 real DOM을 갱신한다.

모든 변경사항은 묶어서 한 번에 real DOM을 갱신되기 때문에 레이아웃 계산과 리렌더링의 규모는 커지지만, 딱 한 번만 일어나기 때문에 전체 연산의 수를 줄일 수 있다.

Virtual DOM을 이용하여 real DOM 을 갱신하는 방법의 가장 큰 장점은 DOM 갱신의 추상화이다. 개발자는 atrribute 조작, 이벤트 처리 또는 수동 DOM 업데이트가 백그라운드에서 어떻게 수행되는지 알 필요가 없다. 또한 기존 값 중 어떤게 바뀌었고 어떤게 바뀌지 않았는지 계속 파악하고 있지 않아도 된다.

Ref

✨ Diffing Algorithm

트리를 새로 만들게 되면 최첨단의 알고리즘을 이용하더라도 O(n3)의 복잡도를 가지게 된다.
이는 너무 비싼 연산이기 때문에, React는 대신, 두 가지 가정을 기반하여 O(n) 복잡도의 휴리스틱 알고리즘을 구현한다.

  1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.
  2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다.

리액트에서 DOM이 업데이트 되는 과정

  1. 처음 실행하면 virtual DOM 과 real DOM이 생성된다.
  2. 리액트는 옵져버 패턴을 통해서 동작하며, state에 변경사항이 생기면 virtual DOM 을 업데이트 한다.
  3. 리액트는 virtual DOM과 real DOM 을 비교하고 real DOM 을 갱신한다. 이 과정을 재조정(reconciliation) 이라고 한다.

Diffing Algorithm

  • 엘리먼트의 타입이 서로 다르면, 이전 트리를 완전히 버리고 새로운 트리를 구축한다.
  • 엘리먼트의 타입이 같으면 두 엘리먼트의 속성을 확인하여, 동일한 내역은 유지하고 변경된 속성들만 갱신한다.

DOM 노도의 처리가 끝나면, React는 이어서 해당 노드의 자식들을 재귀적으로 처리한다.

자식들이 key를 가지고 있다면, React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인한다. 이 때 key 값이 일치하면 이전 element 를 그대로 사용하고, 새롭게 추가되었거나 제거된 element 들의 변경사항을 반영한다. 만약, index 를 키 값으로 사용한다면, state가 엉망이 되거나 의도치 않은 방식으로 변경될 수 있다.

Ref

✨ re-rendering 과정

1. Marking the component dirty
DOM 이벤트가 발생하면 이를 감싸고 있는 React event listeners가 this.setState를 실행시킨다. setState 를 이용하여 state 를 변경시킨다. setState 는 해당 컴포넌트(this)를 변경 대상 컴포넌트(dirty component)로 마킹한다.

2. 새 virtual DOM element 생성(dirty한 component 업데이트)
React의 flushBatchedUpdates 함수가 수행한다. dirty로 마킹된 컴포넌트들과 하위 노드들을 업데이트한다. 그리고 shouldComponentUpdate를 확인하여 해당 컴포넌트가 re-rendering 될지 말지를 체크한다.

3. use the diffing algorithm to do the reconciliation
Virtaul DOM과 real DOM을 비교(diffing)하여 어떤 노드를 어떻게 업데이트할지 판단한다. 이 과정은 render 단계에서 진행된다.

4. update the actual DOM
재조정 후 수정 대상이 되는 값들을 한 번에 batch 적용

Ref

✨ 함수형 컴포넌트 vs 클래스형 컴포넌트

Props는 리액트에서 불변(immutable) 값이다. 하지만, this는 변경 가능하며(mutable), 조작할 수 있다. 이것이 this가 클래스에서 존재하는 목적이다. 리액트가 시간이 지남에 따라 이를 변경하기 때문에 render나 라이프사이클 메서드를 호출할 때 업데이트된 값들을 읽어 올 수 있는 것이다.

반면 함수형 컴포넌트의 경우 props는 보존된다. 클래스의 this와는 다르게, 함수가 받는 인자는 리액트가 변경할 수 없다.

클래스형 컴포넌트의 props 는 this 에 바인딩 되어있기 때문에, this.props 자체가 변경되면 props 또한 변경될 수 밖에 없다.

다음은 비슷한 예시이다. 실제 예시는 ref 의 링크를 참고하자.

class

class Messenger{
    constructor(message){
        this.message = message;
    }
    showMessage = ()=>{
        console.log(this.message);
    };
    delayedMessage = ()=>{
        setTimeout(this.showMessage, 1000);
    };
}

var m = new Messenger('hi');
m.showMessage();
// hi

m.delayedMessage();
m.message = 'bye'
// bye

function

function Messenger(message){
    this.showMessage = ()=>{
        console.log(message);
    };
    this.delayedMessage = ()=>{
        setTimeout(this.showMessage, 1000);
    };
}

var m = new Messenger('hi');
m.showMessage();
// hi

// m 의 message를 바꿀 방법이 없음

ref

✨ React Hook

훅은 리액트 컴포넌트에서 상태와 이펙트를 도입하고 관리하는 효과적인 매커니즘입니다.

react hook 을 도입한 계기

  1. 컴포넌트 사이에서 상태 로직을 재사용하기 어렵습니다.
    React는 컴포넌트간에 재사용 가능한 로직을 붙이는 방법을 제공하지 않습니다. (예를 들어, 스토어에 연결하는 것) 이전부터 React를 사용해왔다면, render props이나 고차 컴포넌트와 같은 패턴을 통해 이러한 문제를 해결하는 방법에 익숙할 것입니다. 그러나 이런 패턴의 사용은 컴포넌트의 재구성을 강요하며, 코드의 추적을 어렵게 만듭니다.
    Hook을 사용하면 컴포넌트로부터 상태 관련 로직을 추상화할 수 있습니다. 이를 이용해 독립적인 테스트와 재사용이 가능합니다. Hook은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와줍니다. 이것은 많은 컴포넌트 혹은 커뮤니티 사이에서 Hook을 공유하기 쉽게 만들어줍니다.

  2. 복잡한 컴포넌트들은 이해하기 어렵습니다.
    상태 관련 로직들과 사이드 이펙트가 있는 컴포넌트들을 유지보수해야 합니다. 각 생명주기 메서드에는 자주 관련 없는 로직이 섞여들어가고는 합니다. 예시로 componentDidMount 와 componentDidUpdate는 컴포넌트안에서 데이터를 가져오는 작업을 수행할 때 사용 되어야 하지만, 같은 componentDidMount에서 이벤트 리스너를 설정하는 것과 같은 관계없는 로직이 포함되기도 하며, componentWillUnmount에서 cleanup 로직을 수행하기도 합니다. 함께 변경되는 상호 관련 코드는 분리되지만 이와 연관 없는 코드들은 단일 메서드로 결합합니다. 이로 인해 버그가 쉽게 발생하고 무결성을 너무나 쉽게 해칩니다.

  3. Class은 사람과 기계를 혼동시킵니다.
    JavaScript의 this키워드는 대부분의 다른 언어에서와는 다르게 작동함으로 사용자에게 큰 혼란을 주었으며, 코드의 재사용성과 구성을 매우 어렵게 만들고는 했습니다.

기본 훅

  • useState: 컴포넌트에 지역 상태를 추가합니다.
  • useEffect: 컴포넌트 안에서 사이드 이펙트를 발생시키는 부분을 실행합니다.
  • useContext: 컴포넌트에 리액트 컨텍스트 값을 제공합니다.

훅 규칙

  1. 가장 상위(at the top level)에서만 훅을 호출해야 합니다.
  2. 리액트 함수에서만 훅을 호출할 수 있습니다.

custom hook

커스텀 훅을 사용하면 기존 컴포넌트 로직을 재사용할 수 있도록 별도의 함수로 추출할 수 있습니다.

ref

✨ Controlled vs Uncontrolled

controlled compoennt

  • <input>, <select>, <textarea> 의 value(checked)를 state로 직접 관리하는 방법이다.
  • value 와 onChange 를 input 태그의 props 으로 넘겨줘야 한다.
  • 타자를 칠 때마다 rerendering이 일어난다.

uncontrolled component

  • react state 를 사용하여 input 의 value를 제어하지 않고, html input의 value property를 그대로 사용하는 방법이다.
  • defaultValue, defaultChecked 를 사용하며 value, checked 는 사용하면 안 된다.

Ref

✨ React18

New Feature

  • Automatic Batching
  • Transitions
  • Suspense

Ref

🚩 HTML

✨ DOCTYPE

"문서 형식 선언"(Document Type Declaration), 또는 doctype이란 문서의 유형을 정의하기 위해 사용하는 선언문이다.

문서 타입 정의는 HTML5, XHTML, HTML의 세가지 문서 유형이 존재하며, 기술한 유형에 따라 마크업 문서의 요소와 속성들ㅇ르 처리하는 기준이 되며 유효성 검사에 이용된다.

웹 문서의 시작을 알려주며 태그보다 먼저 선언한다. DOCTYPE은 웹 브라우저에서 처리할 문서가 HTML이며 어떠한 버전으로 사용했으니, 해당 방식대로 해석하라는 의미를 갖는다.

Ref

✨ 시맨틱 태그

의미를 가진 태그. HTML5에서는 처음 등장하였으며 <header><footer> 같은 태그들을 말한다.

장점
1. HTML 문서의 가독성과 유지보수가 쉬워지기 때문입니다
2. 웹 브라우저가 HTML만 보고도 어느 영역인지 쉽게 알 수 있다 이는 웹 접근성 시각에서 볼 때도 중요하게 사용된다. (시각 장애인용 스크린 리더를 사용할 때 등)
3. 검색엔진이 검색을 수행할 때 HTML내의 태그를 분석할 수 있다.

웹 사이트 구성 예시

시맨틱 태그 종류

Ref

🚩 CSS

✨ Box Sizing

🚩 브라우저

✨ DOM

Ref

✨ 브라우저 렌더링 과정

  1. DOM, CSSOM생성: 가장 첫번째 단계로 서버로부터 받은 HTML, CSS를 다운받는다 → 단순한 텍스트인 HTML, CSS파일을 Object Model로 만든다. HTML은 DOM으로, CSS는 CSSOM으로 만들어진다. (html이 여기서 파싱된다)
    DOM Tree와 CSSOM Tree가 만들어진다
  2. Render Tree생성: DOM Tree와 CSSOM Tree가 만들어졌으면 그 다음으로는 이 둘을 이용하여 Render Tree를 생성한다. 렌더트리에는 스타일 정보가 설정되어있고, 실제 화면에 표현되는 노드들로 구성된다.
  3. Layout 단계: 브라우저의 뷰포트(Viewport) 내에서 각 노드들의 정확한 위치와 크기를 계산한다. 생성된 Render Tree 노드들이 가지고 있는 스타일과 속성에 따라서 브라우저 화면의 어느위치에 어느크기로 출력될지 계산하는 단계이다.(reflow 단계) 레이아웃 단계에서 %, vh, vw와 같이 상대적인 위치, 크기 속성은 실제 화면에 그려지는 픽셀 단위로 변환된다.
  4. Paint: Layout 계산이 완료되면 이제 요소들을 실제 화면을 그리게 된다.(repaint 단계) 처리해야하는 스타일이 복잡할수록 paint 단계에 소요되는 시간이 길다. (가령 그라데이션, 그림자 효과 > 단색 배경

Ref

script tag async vs defer

일반적인 실행

<script src="script.js">

async
HTML 구문 분석과 병행하여 스크립트를 가져온 후 스크립트가 준비 될 때마다 즉시 실행이 가능하다.
실행의 순서가 다운로드 완료 시점의 결정되므로 실행 순서가 중요한 스크립트들에 async를 사용할 때는 유의해야 한다

<script async src="script.js">

defer
defer 속성은 HTML 구문 분석이 완전히 완료되면 스크립트 파일을 실행하도록 브라우저에 지시한다.
async와는 다르게 호출된 순서대로 실행된다.

<script defer src="script.js">

Ref

✨ 쿠키, 로컬스토리지, 세션스토리지 차이

사용 예시

  • 쿠키: 다시 보지 않음창
  • 로컬 스토리지: 자동 로그인, 다시 보지 않기
  • 세션 스토리지: 입력 폼 정보 저장, 장바구니

✨ 주소창에 naver.com을 입력하면 생기는 일

  1. 사용자가 입력한 url 주소 중에서 도메인 네임을 DNS 서버에서 검색한다.
  2. DNS 서버에서 해당 도메인 네임에 해당하는 IP주소를 찾아 사용자가 입력한 URL 정보와 함께 전달한다.
  3. 웹 페이지 URL + IP 주소는 HTTP 프로토콜을 사용하여 HTTP 요청 메세지를 생성한다.
  4. HTTP 요청 메세지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 해당 IP 주소의 컴퓨터로 전송된다
  5. 이렇게 도착한 HTTP 요청 메세지는 HTTP 프로토콜을 사용하여 웹 페이지 URL 정보로 변환된다.
  6. 웹 서버는 도착한 웹 페이지 URL 정보에 해당하는 데이터를 검색한다.
  7. 검색된 웹 페이지 데이터는 또다시 HTTP 프로토콜을 사용하여 HTTP 응답 메세지를 생성한다.
  8. 이렇게 생성된 HTTP 응답 메세지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 원래 컴퓨터로 전달된다.
  9. 도착한 HTTP 응답 메세지는 HTTP 프로토콜을 이용하여 웹 페이지 데이터로 변환되고, 웹 브라우저에 의해 출력되어 사용자가 볼 수 있게 된다.

✨ 크로스 브라우징

✨ Reflow & Repaint

  1. 변경이 잦은 요소는 Absolute에 배치합니다.
  2. 스타일 변경은 한번에 묶어서 처리합니다.
  3. 테이블은 점진적 배치에서 제외되기 때문에 사용을 지양합니다.
  4. CSS에서의 JS표현식을 피합니다. (ex. 3항 연산자 등)
  5. 스타일을 최적화 합니다. (ex. css 최소화)

Ref

쿠키 세션

쿠키 세션 스토리지 로컬 스토리지

쿠키 세션 로그인

🚩 HTTP

✨ CORS

CORS란 Cross Origin Resource Sharing의 줄임말로, Cross-Site Http Request를 가능하게 하는 표준 규약이다.

CORS는 도메인 혹은 포트가 다른 서버의 자원을 요청할 때 생긴다. 그러나 동일 출처 정책으로 인해 CORS같은 상황이 발생하면 외부서버에 요청한 데이터를 브라우저에서 보안목적으로 차단을 한다.

문제를 해결하는 방법은 두 가지가 있다.

  1. 서버에서 "Access-Control-Allow-Origin" 설정을 열어준다.
  2. 클라이언트와 서버 사이에 미들웨어 서버를 생성하고 이 서버에서 요청을 프록시 처리한다.

보통은 2번 방법을 사용하는데 리액트 개발 환경에서는 package.json 의 proxy 필드를 추가하거나, 'http-proxy-middleware' 라이브러리를 사용함으로써 해결이 가능하다.
실제 배포 환경에서는 배포 장비의 nginx에서 proxy 처리를 해주면 된다.

Ref

기타

프레임 vs 라이브러리 + 제어의 역전

의존성 주입

객체지향 프로그래밍이 뭔가요?

객체 지향 프로그래밍은 컴퓨터 프로그래밍 패러다임중 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.

출처: https://jeong-pro.tistory.com/95 [기본기를 쌓는 정아마추어 코딩블로그]

객체 지향 5대 원칙 SOLID

객체 지향 4대 특성

추상화: 객체들의 공통적인 특징을 뽑아내는 것이다. 즉, 우리가 구현하는 객체들이 가진 공통적인 데이터와 기능을 도출해 내는 것을 의미한다. 즉, "공통의 속성이나 기능을 묶어 이름을 붙이는 것"

캡슐화: 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉한다.

상속성: 상속이란 상위개념의 특징을 하위 개념이 물려받는 특징을 말한다. 하나의 클래스가 가지고 있는 특징들을 그대로 다른 클래스가 돌려주고자 할 때 상속성의 특징을 사용한다.

다형성: 약간 다른 방법으로 일을 하는 함수의 동일한 이름으로 호출해 주는 것을 말한다. 예를 들어 홍길동과 김철수가 있다고 하자. 그런데 선생님이 길동이를 바라 보면서 칠판을 지우라고 했다. 그럼 길동 나름의 방법대로 칠판을 지울것이다. 그리고 선생님은 다시 철수에게 칠판을 지우라고 명령을 했다. 철수도 철수의 방식대로 칠판을 지울것이다. 이처럼 표현은 같지만 칠판을 지우는 행위는 다르게 나타난다. 이것이 다형성이다. 같은 하나의 명령이 다른 결과로 나타나는 것을 말한다. 오버로딩이 다형성에 해당된다.

비즈니스 로직

DAO DTO

primitive type과 reference type

컴파일과 인터프리팅

Stack Heap Data

응집도와 결합도

결합도가 높은 예시: 바리스타와 매니저가 전역 변수를 공유한다. 매니저가 바리스타의 특정 값을 직접 참조한다.

인터넷 구조

image

성능 최적화

0개의 댓글