Logspot

FE skill interview Q&A

Front-end 기술면접을 준비하면서 예상 질문과 답을 적어둔 페이지입니다. 틀린 내용이 있거나 추가하고 싶은 내용이 있으면 메일을 보내주세요 📬

Logspot 템플릿을 사용해서 만들었습니다. Nuxt, Vue, and Tailwindcss로 만들어진 오픈 소스 로그 템플릿으로 여기를 참고하세요.

JS

자바스크립트에서 비동기 처리를 위한 방식은 무엇이 있는가?

callback, Promise, async/await 3가지 방식

// callback
function fetchData(callback) {
  setTimeout(() => {
    console.log("Data fetching completed");
    callback("Data from server");
  }, 2000);
}

fetchData((data) => {
  console.log("Received data:", data);
});

// 실행 결과
Data fetching completed
Received data: Data from server
// Promise
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true;
      if (success) {
        console.log("Data fetching completed");
        resolve("Data from server");
      } else {
        reject(new Error("Failed to fetch data"));
      }
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log("Received data:", data);
  })
  .catch((error) => {
    console.error("Error:", error.message);
  });

// 실행 결과
Data fetching completed
Received data: Data from server
// async/await
function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("Data fetching completed");
      resolve("Data from server");
    }, 2000);
  });
}

async function displayData() {
  try {
    const data = await fetchData();
    console.log("Received data:", data);
  } catch (error) {
    console.error("Error:", error.message);
  }
}

displayData();

// 실행 결과
Data fetching completed
Received data: Data from server
  • callback과 Promise: 콜백은 함수를 다른 함수의 인자로 전달하는것, 콜백은 주로 비동기 작업이 완료 되었을 때 실행 / 프로미스는 비동기 작업의 최종 완료나 실패를 나타내는 객체, then, catch, finally 메서드 사용하여 성공, 실패, 완료 시 실행할 로직을 정의할 수 있음.
  • async/await: 프로미스 기반의 비동기 코드를 더 읽기 쉽고 동기식으로 작성할 수 있게 함 - 비동기 처리를 위한 문법적 설탕임, 해당 키워드를 사용하면 프로미스 기반의 비동기 코드를 동기식으로 작성할 수 있음, async 함수를 선언해서 해당 함수가 비동기 임을 나타내고, await은 프로미스를 기다리는 표현식 앞에 사용, 프로미스가 완료될 때까지 해당 비동기 작업의 실행을 일시 중단하고 프로미스가 완료되면 결과를 반환. await은 async 함수 내에서만 사용 가능
  • async/await 의 동작방식은?
    • async 함수가 호출되면 항상 Promise를 반환함. 이 Promise는 async 함수 내에서 return 키워드로 반환되는 값으로 해결(resolve).
    • await 키워드가 사용된 표현식은 Promise.resolve()로 감싸져 비동기 작업을 시작.
    • await 키워드 뒤의 Promise가 완료될 때까지 async 함수의 실행이 일시 중단되고, 이후 Promise가 완료되면 그 결과가 반환.
    • try/catch 블록을 사용하여 await 표현식 내에서 발생하는 에러를 캐치할 수 있음.
    • async/await를 사용하면 비동기 코드를 동기식 코드처럼 작성할 수 있어 가독성이 향상되며, 에러 처리도 try/catch 블록을 통해 쉽게 할 수 있음. 그러나 await 키워드는 비동기 작업의 완료를 기다리는 동안 현재 함수의 실행이 일시 중단되므로, 여러 비동기 작업을 병렬로 실행하려면 다른 방법(예: Promise.all())과 함께 사용해야 할 수도 있음.

  • 바벨 트랜스파일링: JS 컴파일러, ES6 문법을 이전의 JS로 변환
  • polyfill 브라우저가 지원하지 않는 JS기능을 제공해서 기능을 지원 (Promise나 Object.assign, Array.includes)

  • 현재 실행중인 코드가 완료된 후, 이벤트 큐에 있는 우선순위 높은 것들이 실행되고 난 후 timer phase에 실행될것, JS는 싱글쓰레드 언어임 setTimeout은 이벤트 큐에 추가
JS

이벤트 루프, 마이크로 태스크 큐

이벤트 루프는 JavaScript 코드 실행, 브라우저 렌더링, 비동기 작업 처리 등을 조율하는 핵심 메커니즘. JavaScript는 단일 스레드로 작동하기 때문에 여러 작업을 동시에 처리할 수 없기 때문에 비동기 작업이 필요한 경우 이벤트 루프가 중요한 역할을 한다.

이벤트 루프는 콜 스택(Call Stack), 태스크 큐(Task Queue), 마이크로태스크 큐(Microtask Queue)**로 이루어져 있음

  • 콜 스택 (Call Stack): 함수 호출을 기록하는 스택. JavaScript 코드가 실행되면 해당 함수들이 콜 스택에 쌓이고 함수가 완료되면 스택에서 제거됨. 동기적 코드가 실행되는 곳.
  • 태스크 큐 (Task Queue): 비동기 작업의 콜백 함수나 이벤트 핸들러가 대기하는 큐. 스택이 비워졌을 때 이벤트 루프가 이 큐에서 작업을 가져와 실행.
  • 마이크로태스크 큐(Microtask Queue): 프로미스와 같이 우선적으로 처리되어야 하는 작업들이 대기하는 큐. 태스크 큐보다 우선순위 높음.
  • 이벤트 루프 (Event Loop) 동작방식
    1. 콜 스택이 비어있는지 확인: 이벤트 루프는 콜 스택에 작업이 남아있는지 확인
    2. 마이크로태스크 큐 우선 처리: 콜 스택이 비었으면, 먼저 마이크로태스크 큐의 모든 작업을 실행. 마이크로태스크 큐는 항상 우선적으로 처리되며, 새 마이크로태스크가 추가될 때까지 실행을 반복.
    3. 태스크 큐에서 작업 실행: 마이크로태스크 큐가 비어있으면, 태스크 큐에서 작업을 하나 가져와 콜 스택에 추가하고 실행.
    4. 렌더링 업데이트: 브라우저는 필요할 경우 DOM 업데이트와 렌더링 작업을 수행. 이 작업은 일반적으로 모든 태스크가 완료된 후에 실행.
console.log("Start");

setTimeout(() => {
  console.log("setTimeout");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise Microtask");
});

console.log("End");

// 실행 결과
Start
End
Promise Microtask
setTimeout

위 코드에서는 setTimeout과 Promise가 비동기 작업. 이러한 비동기 작업은 콜 스택에 즉시 실행되지 않고 태스크 큐에 대기하게 된다. Promise.resolve()는 프로미스의 마이크로태스크 큐에 들어가게 된다. setTimeout은 0초 후에 실행되도록 설정되어 있지만, 실제로는 콜 스택이 비어있을 때 setTimeout과 Promise가 콜 스택으로 이동되어 실행된다.


마이크로 태스크 큐(microtask queue)는 비동기 작업 중에서도 다른 비동기 작업보다 더 우선순위가 높은 작업을 위한 큐. 주로 Promise와 관련된 작업이 이 큐에 들어간다.

이벤트 루프는 콜 스택이 비어있을 때 먼저 마이크로 태스크 큐를 확인하고, 마이크로 태스크 큐에 작업이 있으면 해당 작업을 우선순위에 따라 콜 스택으로 이동시켜 실행. 마이크로 태스크 큐의 작업이 모두 완료되면 태스크 큐를 확인한다.

web

브라우저 렌더링 과정

브라우저 렌더링 과정

  1. HTML 파싱 → DOM 생성
  2. CSS 파싱 → CSSOM 생성
  3. DOM + CSSOM 결합 → 렌더 트리 생성
  4. 렌더 트리 기반 레이아웃 계산 (레이아웃)
  5. 화면에 요소를 그리기 (페인팅)
  6. 레이어 결합하여 최종 화면 표시 (컴포지팅)
  • <script> 태그를 만나면 JS 코드 파싱하고 실행, 실행 순서와 타이밍 중요
  • 이벤트가 있을때 DOM 요소 변경, 브라우저는 리플로우, 리페인팅하여 반영함
  • 리렌더링 조건: 주로 구조나 스타일/ 상호작용 변경이 있어서 레이아웃을 다시 계산해야 할 때

리플로우는 레이아웃 계산이 다시 실행되는 과정 리플로우가 발생하는 경우 - DOM 요소의 크기나 위치가 변경될 때, DOM 구조가 변경될 때, 폰트/창 크기 변경 시

리플로우가 발생되지 않는 경우 (리페인트만 발생) bg color, text color, font family, font size 등

리플로우를 최소화하는게 성능에 좋음


JavaScript의 동작 원리 파싱, 컴파일, 실행 과정을 거치며, 이벤트 루프를 통해 비동기 작업을 처리함. 메모리 관리는 가비지 컬렉션을 통해 이루어짐.

  1. 파싱 (Parsing) - 브라우저는 HTML 문서를 파싱하면서
  2. 컴파일 (Compilation) - 파싱된 JavaScript 코드는 바이트코드나 기계어로 컴파일되는데, 일반적으로 이 과정은 브라우저의 JavaScript 엔진에서 수행.
  3. 실행 (Execution) - 컴파일된 코드는 실행되며, 이때 메모리에 변수, 함수, 객체 등이 할당됨. 실행 과정에서는 스택(stack)과 힙(heap) 메모리를 사용.
  4. 이벤트 루프 (Event Loop) - JavaScript는 싱글 스레드 기반의 언어로 비동기 작업을 위해 이벤트 루프(event loop)를 사용. 이벤트 루프는 큐(queue)에 있는 작업들을 순차적으로 실행하며, 비동기 작업이 완료되면 콜백 함수를 큐에 추가.
  5. Call Stack, Callback Queue, Event Loop
    • Call Stack: 현재 실행 중인 함수와 그 상태를 저장, 함수가 호출되면 스택에 푸시되며, 실행이 완료되면 팝
    • Callback Queue: 비동기 작업의 콜백 함수들이 대기하는 큐
    • Event Loop: Call Stack과 Callback Queue를 주기적으로 확인하여, Call Stack이 비어 있으면 Callback Queue의 콜백 함수를 가져와서 실행
  6. 메모리 관리 JavaScript는 가비지 컬렉션(garbage collection)을 사용하여 더 이상 사용되지 않는 메모리를 자동으로 해제
web

캐시 / 웹 스토리지, 쿠키, 세션 스토리지의 차이에 대해서

캐시는 데이터나 자원을 빠르게 접근할 수 있도록 임시로 저장하는 메모리나 저장소, 웹페이지 이미지 스크립트 스타일 시트 등의 자원을 개시하여 로딩속도 향상, 네트워크 사용량을 줄이는데 활용

  • 브라우저 캐시: 웹 브라우저 내부에 저장되는 캐시로, 사용자가 웹 페이지를 방문할 때 자동으로 다운로드된 자원을 임시로 저장함. 이후에 같은 자원을 요청할 때 서버로부터 다시 받아오는 대신 캐시에서 빠르게 로드.
  • CDN 캐시: 콘텐츠 전송 네트워크(Content Delivery Network)에서 제공하는 캐시 서버에 자원을 저장하여 전 세계의 사용자에게 빠르게 콘텐츠를 제공.
  • 서버 캐시: 웹 서버에서 동적인 콘텐츠를 미리 계산하거나 저장하여 웹 요청에 더 빠르게 응답할 수 있게 한다.

웹 스토리지/쿠키/세션 스토리지는 클라이언트 사이드 저장 메커니즘

  • 웹 스토리지: 클라이언트 사이드에 데이터를 저장하는 로컬 저장소로, 브라우저 세션 또는 영구적으로 데이터를 저장한다.웹 애플리케이션 설정, 사용자 정보, 캐시 데이터 등 웹 스토리지는 주로 로컬 스토리지 (LocalStorage)와 세션 스토리지 (SessionStorage)를 포함하는 개념
  • 쿠키: 작은 데이터를 클라이언트에 저장하며, 서버와 클라이언트 간의 상태를 유지합니다.모든 HTTP 요청에 자동으로 포함되어 서버로 전송, 보안 취약, 로그인 상태 유지 등에 사용
  • 세션: 서버에 저장되는 상태 정보로, 클라이언트와 서버 간의 연결을 유지합니다.브라우저 세션 종료나 세션 타임아웃 시간까지 유지, 클라이언트에 직접 저장 안하니까 보안적으로 더 안전로그인 정보, 장바구니 정보, 세션 상태 관리 등

차이점

  • 목적: 쿠키와 세션은 상태 정보를 유지하고 관리하는 데, 웹 스토리지는 임시 데이터 저장을 위한 데 사용. / 캐시는 자원의 빠른 로딩을 위해 데이터를 저장하는 데 사용.
  • 저장 위치: 쿠키와 웹 스토리지는 클라이언트 측, 세션은 서버 측에 저장. / 캐시는 브라우저의 메모리나 디스크에 저장.

참고로 세션 스토리지와 세션은 다름. 세션은 서버측에서 관리되며 사용자의 상태를 서버에서 유지하는 데 사용. 예를 들어, 사용자가 웹사이트에 로그인 했을 때, 로그인 상태임을 유지하는 정보를 서버가 관리함. 클라이언트는 세션 ID를 쿠키에 저장하고, 해당 ID를 통해 서버에서 자신의 세션에 접근한다. 세션은 일반적으로 특정 시간 동안 유효하고, 로그아웃하거나 세션 만료되면 무효화됨.

세션 스토리지는 클라이언트 측에 저장되고, JS를 통해 데이터를 일시적으로 저장하는 데 사용함. JS를 통해서만 접근 가능. 브라우저 탭이나 창이 열려있는 동안에만 유효하고, 닫으면 사라짐. 새로고침해도 초기화되지 않는다. 닫을 때만 초기화. 탭 간에 공유되지 않음.


로컬 스토리지와 세션 스토리지의 차이점 로컬 스토리지는 명시적으로 삭제되기 전까지 영구적으로 유지, 반면에 세션 스토리지는 브라우저 탭이나 창이 열려 있는 동안에만 유지. 로컬 스토리지는 탭간 공유, 세션 스토리지는 탭 간 격리 둘 다 용량은 5-10MB를 저장할 수 있다.


JWT는 세 부분으로 구성된 문자열이며 각 부분은 점(.)으로 구분된다.

헤더(Header): JWT의 유형과 서명 알고리즘 정보를 담고 있음
페이로드(Payload): 토큰에 담길 실제 데이터. 주로 사용자의 정보나 토큰의 만료 시간 등을 포함하며, 이 부분은 암호화되지 않고 Base64URL로 인코딩된다.
서명(Signature): JWT의 무결성을 검증하기 위한 서명. 헤더와 페이로드를 합쳐서 비밀 키로 서명한다.

일반적으로 사용자가 로그인하면 서버가 해당 사용자의 정보를 기반으로 JWT를 생성하고 클라이언트에 전달, 클라이언트는 토큰을 로컬 스토리지/세션 스토리지/쿠키에 저장한다. 클라이언트는 서버에 요청할 때마다 JWT를 HTTP 헤더의 Authorization 필드에 포함하여 보낸다. 근데 이럴 경우 해당 토큰이 탈취될 수 있어 보안적 문제가 생길 수 있다.

보통 로컬 스토리지나 쿠키에 저장하는데 로컬 스토리지에 저장하면 XSS(교차 사이트 스크립팅) 공격에 취약하다. 쿠키에 저장하면 자동으로 HTTP 요청과 함께 전송되기 떄문에 별도로 헤더에 토큰을 추가할 필요가 없다. HttpOnly 속성을 설정하면 JS에서 쿠키에 접근이 불가하기 떄문에 XSS 공격을 방지할 수 있지만, CSRF(크로스 사이트 요청 위조) 공격에 취약할 수 있다. 추가적 방어 기법이 필요하다.

또는 웹세션에 저장하는 방법도 있다. 웹세션에 저장하면 세션은 서버에서 관리되며 세션 ID가 클라이언트 측 쿠키에 저장된다. 또는 세션 쿠키에 JWT를 저장할 수 있다. (세션 쿠키는 브라우저 세션 동안만 유지되는 특정 유형의 쿠키, 보통 서버 세션을 관리하기 위해 사용) 근데..... 서버의 세션 저장의 한계가 있는데, 메모리/디스크공간/성능저하/확장성 문제 등이 있다. 방식은 적절하게 선택해서 사용해야 한다.

결론적으로는 여러요소를 고려해서 결정해야 하는데, 쿠키의 HttpOlny 속성을 사용하는게 가장 권장되는 방법으로 볼 수 있다. 자동으로 매 HTTP 요청마다 서버로 전송되기 때문에 서버에서 인증을 처리할 수 있고, HttpOnly 속성을 사용할 수 있어서 JS로 접근이 불가하기 때문에 XSS(교차 사이트 스크립팅) 공격에 대해서도 방어할 수 있다. 하위 도메인 간에도 사용 가능한 것도 장점.

그러나 쿠키는 브라우저에서 자동으로 전송되므로, Cross-Site Request Forgery (CSRF - 교차 사이트 요청 위조) 공격에 취약할 수 있어서 이를 방지하려면 SameSite 속성을 Strict 또는 Lax로 설정해야 한다.

// JWT를 HttpOnly 쿠키로 저장
document.cookie = "token=your-jwt-token; path=/; Secure; HttpOnly; SameSite=Strict";
web

CORS

CORS(Cross-Origin Resource Sharing) 웹 브라우저의 보안 기능

다른 도메인/포트에서 리소스에 접근할때 발생하는 보안 문제 해결을 위해 생겨남. 특정 origin에서 로드된 스크립트나 리소스가 다른 출처의 리소스에 접근할때 제한을 적용, 다른 출처에서 AJAX 요청 시

  • 해결방법: 서버 CORS 헤더를 설정해서 요청 허용하도록 서버 구성, 아니면 프록시 서버, JSONP(json with padding) 사용-서버로부터 데이터를 가져오는 방식, access-control-allow-origin 값에 특정 출처나 메서드 허용으로 헤더를 설정한다

preflight 요청은 CORS 정책을 준수하기 위한것. 예비 요청을 보내서 특정 HTTP 헤더와 메서드가 안전하게 요청될 수 있는 확인하는 과정. 실제 요청 처리 전에 클라이언트 요청을 승인하거나 거부할 수 있다.

preflight 요청이 필요한 상황

  • HTTP 메서드: GET, POST, HEAD 외의 다른 메서드를 사용할 때
  • 특정 헤더: Content-Type, Authorization와 같은 특정 HTTP 헤더를 사용할 때
  • 크로스 도메인 요청: 다른 도메인, 포트, 프로토콜에서 리소스를 요청할 때

복잡한 요청일때 preflight 요청을 하고, 복잡한 요청인지는 브라우저가 결정함.

// example
fetch("https://example.com/data", {
  method: "POST", // POST는 단순 요청이지만, Content-Type이 json일 경우 복잡한 요청으로 간주됨
  headers: {
    "Content-Type": "application/json", // 복잡한 요청을 유발하는 Content-Type
    Authorization: "Bearer token", // 복잡한 요청을 유발하는 커스텀 헤더
  },
  body: JSON.stringify({ data: "test" }),
});
JS

클로저

클로저란? 렉시컬 환경에서 외부 변수에 접근할 수 있는 함수

  • 렉시컬 환경: 함수가 선언될때의 환경을 저장하고 이 환경과 함께 클로저를 생성
  • 활용 예시로 캡슐화, 외부 변수를 숨기고 안전하게 접근할 수 있는 범위 제공 - 비동기, 콜백 등에서 외부 변수를 안전하게 참조, 클로저를 적절히 해제하여 메모리 누수를 조심해야함

함수가 자기 자신을 둘러싼 환경을 기억하고 그 환경에 접근할 수 있다는 것이 핵심

// ex1
function outerFunction() {
  let outerVar = 'I am an outer variable';

  function innerFunction() {
    console.log(outerVar);  // outerVar에 접근할 수 있음
  }

  return innerFunction;
}

const closure = outerFunction();  // innerFunction을 반환받음
closure();  // 'I am an outer variable'이 출력됨

// ex2
function makeCounter() {
  let count = 0;

  return function() {
    count++;
    return count;
  };
}

const counter1 = makeCounter();
const counter2 = makeCounter();

console.log(counter1());  // 1
console.log(counter1());  // 2
console.log(counter2());  // 1
console.log(counter2());  // 2
// 사용 예시
// 데이터 은닉
function createAccount(balance) {
  return {
    deposit: function(amount) {
      balance += amount;
      console.log('Deposited: ' + amount);
    },
    withdraw: function(amount) {
      if (balance >= amount) {
        balance -= amount;
        console.log('Withdrew: ' + amount);
      } else {
        console.log('Insufficient funds');
      }
    },
    getBalance: function() {
      return balance;
    }
  };
}

const myAccount = createAccount(1000);
myAccount.deposit(500);    // Deposited: 500
myAccount.withdraw(200);   // Withdrew: 200
console.log(myAccount.getBalance());  // 1300

// 함수형 프로그래밍
function multiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const multiplyBy2 = multiplier(2);
const multiplyBy3 = multiplier(3);

console.log(multiplyBy2(5));  // 10
console.log(multiplyBy3(5));  // 15

클로저에서는 불필요하게 많은 변수들을 계속 기억하게 할 수 있어서 메모리에서 해제되지 않고 남게 될 수 있고, 메모리 누수가 발생할 수 있음 주의!

style

CSS

  • display flex/grid 차이점
    flex, 단방향 레이아웃, 수평 또는 수직으로 배열할때 사용. flex-grow, flex-shirink, flex-basis 공간 동적 분배 grid, 이차원 레이아웃 설정 시, 행과 열을 동시에 사용하여 복잡한 레이아웃을 만들 수 있음. grid-template-rows, grid-template-columns 등 사용
  • width, height를 수정하는 것과 transform: scale을 건드는것이 어떤 차이가 있는가?
    width와 height를 수정하면 요소의 레이아웃이 다시 계산되어 리플로우와 리페인팅이 발생할 수 있고 성능에 영향을 미칠 수 있음 / 원래의 비율이 유지되지 않음 / 부모 요소의 크기에 영향을 받음
    transform: scale() 사용하면 요소의 크기는 변경되지 않고, 레이아웃 계산이 다시 수행되지 않아 성능에 더 유리하고, 원래의 가로세로 비율이 유지되므로 왜곡 없이 크기를 조절할 수 있음. 부모 요소나 다른 레이아웃 속성에 덜 영향을 받음.
  • 가운데 정렬
    • text-align: center; 인라인 요소나 내부 텍스트를 가운데 정렬
    • margin: auto; 블록 레벨 요소의 자동 마진을 사용한 가운데 정렬
    • Flexbox나 CSS Grid: 요소를 가운데 정렬하기 위한 강력한 레이아웃 도구
    • transform: 요소의 위치를 조정하여 가운데 정렬

Flexbox와 Grid의 차이점

특성flex (Flexbox)grid (Grid)
배치 방식1차원 레이아웃(가로 또는 세로)2차원 레이아웃(가로와 세로)
주요 사용 용도요소들을 한 방향으로 나열할 때 사용복잡한 2차원 레이아웃을 만들 때 사용
배치 방향flex-direction으로 설정, 기본값은 rowgrid-template-columnsgrid-template-rows로 설정
정렬justify-content, align-items, align-self 등으로 정렬justify-items, align-items, grid-column, grid-row 등으로 정렬
아이템 배치아이템들이 주축교차축에 따라 배치아이템들이 행과 열에 따라 배치
유연성매우 유연함, 한 방향에서 아이템들의 크기나 배치를 쉽게 조정2차원 레이아웃에서 복잡한 배치를 쉽게 관리할 수 있음
web

이벤트

  • 키보드 타이핑이나 스크롤 등 이벤트가 빈번하게 일어나서 퍼포먼스에 문제가 생기거나 과도한 네트워크 요청이 생기는 경우 어떻게? 모든 키업이 아닌 엔터 버튼을 누르도록 변경하거나, 스크롤이 멈췄을때 이벤트를 발생한다거나 이벤트 조건을 변경
    • 쓰로틀링(Throttling): 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것. 선실행 후대기, 호출 빈도 제한, 불필요한 연산/렌더링 줄임
    • 디바운싱: 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것. 선대기 후실행, 연속적으로 발생하는 이벤트를 일정 시간 동안 무시하고, 마지막 이벤트 발생 후에만 핸들러를 실행합니다.
  • 이벤트 버블링과 캡처링, 웹 브라우저에서 발생하는 이벤트의 전파 방식
    • 버블링 하위에서 상위로 전파
    • 캡처링 상위에서 하위로 전파, 가장 상위 window 객체에서 시작해서 타겟 요소까지
  • 이벤트 위임
    • 여러 자식 요소에 대한 이벤트 리스너를 부모 요소 하나에만 등록하여 처리하는것
    • 동적으로 생성되는 요소나 여러 요소에 동일한 이벤트를 적용할때 유용함 / 상향식으로 전파 (버블링)
network

브라우저 주소창에 URL 검색 시 웹서버에서 html 컨텐츠를 가져오기까지의 과정

  1. DNS 조회: 도메인 이름을 IP 주소로 변환
  2. TCP 연결: 안정적인 연결 설정
  3. HTTP 요청: 웹 서버에 리소스 요청
  4. 웹 서버 처리: 요청 처리 및 리소스 제공
  5. HTTP 응답: 처리 결과 전송
  6. 컨텐츠 렌더링: HTML, CSS, JavaScript 실행으로 웹 페이지 렌더링

상세하게

  • ISP(인터넷 서비스 제공자, Internet Service Provider)의 DNS(Domain Name System) 서버에서 IP 조회
  • 도메인 이름을 IP 주소로 변환
  • DNS 서버는 해당 도메인의 IP 주소를 찾아 응답
  • TCP (Transmission Control Protocol) 연결
  • HTTP(HyperText Transfer Protocol) 요청 메시지(리소스;HTML파일, 이미지, CSS파일 등)를 웹 서버에 전송
  • 웹 서버는 HTTP 요청을 처리하고 리소스를 제공함
  • HTTP 응답 - 응답 메시지에는 상태 코드(200 OK, 404 Not Found 등), 헤더 정보, 본문 데이터 등이 포함
  • 브라우저는 HTML 컨텐츠를 파싱하고 DOM(Document Object Model) 트리를 구성
  • CSS, JavaScript 실행으로 웹 페이지 렌더링

  • 네트워크가 좋지 않은 장소에서 사용될 것이라 가정했을 때, 고려할 점은? 예를 들어 버튼을 클릭할 때, 클라 측에서 즉시 버튼 상태를 변경하고, 동시에 서버에 클릭 시 발생하는 이벤트 요청을 보내는 방식으로 개발. 서버의 응답이 오기 전에도 사용자는 버튼의 상태 변화를 볼 수 있게 한다. 만약 서버 응답이 실패하면, 클라이언트 측에서 변경된 상태를 롤백하여 원래 상태로 되돌린다 - 낙관적 업데이트(Optimistic update)

  • HTTP (HyperText Transfer Protocol)는 인터넷 상에서 데이터를 주고받는 프로토콜 기본적으로 텍스트 기반의 데이터를 암호화하지 않고 전송하기 때문에, 보안이 취약 특히, 비밀번호나 개인정보와 같은 민감한 정보를 전송할 때 위험할 수 있음
  • HTTPS (HyperText Transfer Protocol Secure)는 HTTP의 보안 버전으로, SSL(Secure Socket Layer) 또는 TLS(Transport Layer Security) 프로토콜을 사용하여 데이터를 암호화 암호화된 데이터는 중간자 공격(man-in-the-middle attack)으로부터 안전하게 보호됨 HTTPS는 웹사이트의 보안을 강화하고 사용자의 개인정보를 안전하게 보호하는 데에 중점을 두고 있음 웹 브라우저에서는 HTTPS로 암호화된 웹사이트는 보안이 강화되었다는 표시(예: 잠금 아이콘)를 제공
  • SSL (Secure Sockets Layer)은 인터넷을 통한 데이터 전송 시 보안을 제공하는 프로토콜
    • 암호화: SSL은 데이터를 암호화하여 전송. 암호화된 데이터는 중간에 누군가가 가로채더라도 해독할 수 없음
    • 인증: SSL 인증서를 통해 웹사이트의 신원을 확인하고, 사용자가 접속하는 웹사이트가 실제로 원하는 웹사이트인지 확인. 인증서는 신뢰할 수 있는 인증 기관(Certificate Authority, CA)에 의해 발급되며, 웹사이트의 도메인 정보와 공개키(public key)를 포함함.
    • 무결성 보장: SSL은 데이터 전송 과정에서 데이터의 무결성을 보장함. 데이터가 전송 중에 수정되거나 손상되지 않았는지를 확인함.
    • 보안 프로토콜: SSL은 다양한 보안 프로토콜을 포함하고 있으며, 주로 SSL/TLS(Transport Layer Security) 프로토콜이 사용됨. 최신의 보안 기술과 알고리즘을 사용하여 데이터의 보안을 강화함.
    • HTTPS: SSL을 사용하는 웹사이트는 HTTPS (HyperText Transfer Protocol Secure) 프로토콜 사용.
web

성능 최적화

  • 웹사이트 성능 최적화 방법
    • 이미지 최적화: 이미지 포맷, 크기, 레이지 로딩 적용
    • CSS 및 JavaScript 최적화: 파일 병합, 코드 압축, 비동기 로딩
    • 캐싱 및 CDN: 브라우저/서버 캐싱, CDN 활용
    • 서버 및 데이터베이스 최적화: 캐싱, 리소스 최적화
    • 모바일 최적화: 반응형 웹 디자인, 모바일 특화
    • 성능 모니터링 및 분석: 성능 측정, 문제점 식별
    • 프리로드(preloading): 특정 리소스(이미지, 스크립트, 스타일 시트 등)를 미리 로드
      <link rel="preload" href="image.jpg" as="image">
      <link rel="preload" href="script.js" as="script">
      <link rel="preload" href="styles.css" as="style">
      
      • 프리페치와의 차이: 프리로드(preload)는 리소스를 미리 로드하는 반면, 프리페치(prefetch)는 브라우저가 실제로 필요할 때 리소스를 백그라운드에서 미리 로드함.
      • 오버로딩 주의: 필요 이상으로 많은 리소스를 프리로드하면 오히려 페이지 성능에 악영향을 줄 수 있으므로, 필요한 리소스만 프리로드하는 것이 좋음.
      • 크로스오리진 요청: 다른 도메인의 리소스를 프리로드할 때는 크로스 오리진(CORS) 정책에 주의해야 함.
    • 레이지 로딩(lazy loading): 웹 페이지나 애플리케이션에서 이미지나 프레임, 스크립트와 같은 리소스를 필요한 시점에만 로드. 초기 페이지 로딩 시간을 단축하고, 사용자 경험 향상
      <!-- image lazyload -->
      <img src="image.jpg" alt="Image" loading="lazy" />
      
      <!-- frame lazyload -->
      <iframe src="iframe.html" loading="lazy"></iframe>
      
      // script lazyload: JS를 사용한 동적 로딩
      const lazyScripts = document.querySelectorAll("script[data-lazy-src]");
      
      const lazyLoadScript = (entry) => {
        if (entry.isIntersecting) {
          const script = document.createElement("script");
          script.src = script.dataset.lazySrc;
          document.body.appendChild(script);
          script.removeAttribute("data-lazy-src");
        }
      };
      
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(lazyLoadScript);
      });
      
      lazyScripts.forEach((script) => {
        observer.observe(script);
      });
      
    • 리소스 사이즈 최적화
      • 이미지 최적화: 포맷 선택/크기 조정/압축
      • JS, CSS 최적화: 파일 병합/코드압축/비동기 로딩
      • 리소스 압축: Gzip, Brotli 압축 - 서버에서 리소스를 압축하여 전송하면 대역폭을 절약하고 로딩 시간을 단축
      • 브라우저 캐싱 및 CDN
      • 이미지 스프라이트(Sprite) 사용 - 여러 이미지를 하나의 이미지 스프라이트로 결합하여 서버 요청 수를 줄이고 페이지 로딩 시간을 단축
        /* sprite 사용 */
        .sprite {
          background-image: url("sprite.png");
        }
        .icon1 {
          width: 50px;
          height: 50px;
          background-position: 0 0; /* 첫 번째 이미지 위치 */
        }
        .icon2 {
          width: 50px;
          height: 50px;
          background-position: -50px 0; /* 두 번째 이미지 위치 */
        }
        /* ... */
        
        <div class="sprite icon1"></div>
        <div class="sprite icon2"></div>
        
web

Web Worker

Web Worker는 웹 브라우저에서 실행되는 JS 스크립트 웹 어플리케이션 성능을 향상시키고, UI 반응성을 개선하기 위해 백그라운드에서 병렬로 실행됨 메인 스레드가 블로킹되거나 느려지는 것 방지 가능

주요 특징:

  • 병렬 처리: 메인 스레드와 별도의 스레드에서 실행되므로, 병렬로 CPU 집약적인 작업을 처리할 수 있음
  • 백그라운드 작업: 백그라운드에서 실행되기 때문에, 메인 스레드의 작업을 방해하지 않고 웹 페이지의 반응성을 유지할 수 있음.
  • 메시지 기반 통신: 메인 스레드와 메시지 기반으로 통신하며, postMessage()와 onmessage 이벤트 핸들러를 사용하여 데이터를 주고받을 수 있다.
  • 종류:
    1. Dedicated Worker: 하나의 메인 스크립트와 연결되어 있는 별도의 스레드에서 실행되는 Web Worker로, 메인 스크립트와 독립적인 실행 환경
    2. Shared Worker: 여러 메인 스크립트와 연결되어 있는 공유 스레드에서 실행되는 Web Worker로, 여러 웹 페이지 또는 탭 간에 상태와 데이터를 공유
    3. Service Worker: 네트워크 요청을 중간에서 가로채고 처리하는 Web Worker로, 오프라인 캐싱, 백그라운드 동기화 등의 작업을 수행
  • Web Worker의 사용 예:
    • 데이터 처리와 변환
    • 복잡한 계산과 알고리즘
    • 파일 로딩과 파싱
    • 무한 스크롤, 드래그 앤 드롭, 게임 로직 등의 복잡한 사용자 인터랙션 처리
  • 주의사항:
    • 동일 출처 정책(Same-Origin Policy)에 따라 제한된 환경에서만 실행
    • 메인 스레드와 Web Worker 간의 통신은 메시지 기반으로 이루어지며, 복잡한 데이터 구조나 큰 데이터를 전송할 때는 성능과 메모리 사용에 주의해야함
    • 네트워크 요청, DOM 조작 등의 브라우저 API에 제한적인 접근 권한이 있음
    • 웹 애플리케이션의 성능 최적화와 사용자 경험 향상을 위한 중요한 기술로, 적절하게 활용하면 웹 페이지의 반응성을 개선하고 사용자 인터페이스의 부드러움을 유지
      [main.js];
      
      // Web Worker 생성
      const worker = new Worker("worker.js");
      
      // 메시지 수신
      worker.addEventListener("message", (event) => {
        console.log(`Result: ${event.data}`);
      });
      
      // 계산 작업 요청
      worker.postMessage({ type: "calculate", value1: 10, value2: 20 });
      
      [worker.js];
      
      // 메시지 수신
      self.addEventListener("message", (event) => {
        if (event.data && event.data.type === "calculate") {
          const result = calculate(event.data.value1, event.data.value2);
      
          // 계산 결과 전송
          self.postMessage(result);
        }
      });
      
      // 계산 함수
      function calculate(value1, value2) {
        // 간단한 덧셈 계산
        const result = value1 + value2;
      
        return result;
      }
      

Web Worker와 Service Worker의 차이

  • 둘 다 웹 브라우저에서 비동기 및 백그라운드 작업을 수행하는 기술
  • 사용 목적과 동작 방식에 차이가 있음

웹 워커는 별도의 스레드에서 JS 코드를 실행해서 메인 스레드의 성능을 향상시키는게 주 목적

  • 별도의 메모리 공간 사용 및 실행
  • DOM에 직접 접근불가, 일부 웹 API에도 접근 불가
  • PWA 성능 최적화 역할 - 복잡한 작업을 실행할 때 메인 스레드를 차단하지 않도록 하기 위해 사용

서비스 워커는 웹 어플리케이션의 네트워크 요청을 중간에 가로채고 처리해서 지원, 캐싱, 알림 등을 제공해주는게 목적

  • PWA (Progressive web app)의 핵심 기술 중 하나
  • ServiceWorker API를 사용하여 등록하고 제어함
  • DOM에 직접 접근 불가, 네트워크 관련 작업에 사용.
  • 보안상의 이유로 HTTPS에서만 동작
JS

Map, Set

Map

  • 키-값 쌍: Map은 키와 값의 쌍을 저장하는 데이터 구조. 키는 중복될 수 없음.
  • 순서 보장: 원소가 추가된 순서대로 반복(iteration)
  • 키의 타입: 객체도 키로 사용 가능.
  • 반복: for...of, forEach 등의 반복 메서드를 사용할 수 있음.
  • 메서드: set(key, value), get(key), has(key), delete(key), clear() 등의 메서드

Set

  • 유일한 값: Set은 중복된 값을 허용하지 않는 데이터 구조
  • 순서 보장: 원소가 추가된 순서대로 반복(iteration)
  • 반복: for...of, forEach 등의 반복 메서드를 사용할 수 있음
  • 메서드: add(value), has(value), delete(value), clear() 등의 메서드를 제공합니다.

차이점

  • 키-값 vs 유일한 값: Map은 키-값 쌍을 저장하고, Set은 유일한 값만을 저장
  • 키의 유형: Map에서는 객체와 같은 복잡한 데이터 타입을 키로 사용할 수 있지만, Set은 원시 타입만을 값으로 허용
  • 메서드: 두 컬렉션 모두 추가, 삭제, 확인 등의 기본적인 메서드를 제공하지만, 목적에 따라 사용할 메서드가 다르다.
  • 용도: Map은 키-값 쌍의 데이터를 관리하거나 매핑할 때 주로 사용되며, Set은 중복을 허용하지 않는 데이터를 관리할 때 사용
// Map 예시
const myMap = new Map();
myMap.set("name", "John");
myMap.set("age", 30);

// Set 예시
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(2); // 중복된 값은 무시됩니다.

WeakMap과 WeakSet은 Map과 Set의 약한 참조 버전

특정 상황에서 객체의 메모리 누수를 방지하거나, 추가 정보를 객체에 연결할 때 사용

반복을 위한 메서드 없고, 참조되지 않으면 가비지 컬렉션의 대상이 됨.

TS

Typescript utility type

Pick, Omit, Exclude

Pick<T, K> 타입은 제공된 타입 T에서 특정 키 K만을 선택하여 새로운 타입을 생성

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

interface Person {
  name: string;
  age: number;
  address: string;
}

type PersonNameAndAge = Pick<Person, "name" | "age">;

// 결과: { name: string, age: number }

Omit<T, K> 타입은 제공된 타입 T에서 특정 키 K를 제외하고 새로운 타입을 생성

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

interface Person {
  name: string;
  age: number;
  address: string;
}

type PersonWithoutAddress = Omit<Person, "address">;

// 결과: { name: string, age: number }

Exclude<T, U> 타입은 T 타입에서 U 타입에 할당할 수 있는 모든 것들을 제외하여 새로운 타입을 생성합니다.

type Exclude<T, U> = T extends U ? never : T;

type Numbers = 1 | 2 | 3 | 4 | 5;
type OddNumbers = Exclude<Numbers, 2 | 4>;

// 결과: 1 | 3 | 5

이러한 타입 유틸리티들은 코드의 가독성을 높이고, 재사용 가능한 타입을 정의할 때 유용하게 사용된다. Pick, Omit, Exclude와 같은 타입 유틸리티를 활용하면 타입 안정성을 높일 수 있고, 타입 오류를 미리 방지할 수 있음.

이외에도 다양한 타입의 유틸리티가 있음

  • Partial
  • Required
  • Readonly
  • Record<K, T>
  • Exclude, Extract, NonNullable, ReturnType, Parameters
react

React

SPA(single-page application) 단일 페이지 애플리케이션으로, 현재의 페이지를 동적으로 작성함으로써 사용자와 소통하는 웹 애플리케이션이다. 연속되는 페이지 간의 사용자 경험을 향상시키고, 웹 애플리케이션이 데스크톱 애플리케이션처럼 동작하도록 도와준다.

useStateuseRef의 차이

  • 훅으로 제공되는 함수
  • useState는 상태관리 목적, 상태가 변경되면 컴포넌트 재렌더링, 각 상태변수는 클로저를 통해 독립적인 값과 상태 유지
  • useRef는 참조관리, DOM이나 다른 리액트 요소에 직접 접근하기 위해 사용, 상태 변경에 따른 렌더링 없이 참조값만을 관리

VirtualDom

가상 DOM 리액트 컴포넌트의 상태나 속성 변경이 발생하면 새로운 가상 DOM 트리가 생성되고, 이전 가상 DOM과 새로운 가상 DOM을 비교해서 변경된 부분을 식별, 반영하여 렌더링을 최적화함

<StrictMode>는 애플리케이션 내의 잠재적 문제를 알아내기 위한 도구 렌더링 단계의 메서드에 부작용이 있는지를 검사한다. Strict 모드에서 렌더링 단계의 메서드를 두번씩 호출

next.js

Next.js

SSR 서버 사이드 렌더링을 사용하면 모든 데이터가 매핑된 서비스 페이지를 브라우저에 바로 보여줄 수 있음. CSR보다 페이지를 구성하는 속도는 늦어지지만, 전체적으로 보여주는 콘텐츠 구성은 빨라짐. + SEO(search engine optimization; 검색 엔진 최적화) 또한 쉽게 구성 가능. - 웹크롤러 봇들은 HTML에서 콘텐츠를 수집하고 JS 파일은 읽지 못하기 때문

  • SSR은 서버가 HTML 파일을 만들어 보내주면 브라우저가 받고, 이후 JS 파일을 다운받아서 React를 실행함, 이 후 페이지 작동. 첫페이지 로딩속도 빠르고, 검색엔진 최적화가 가능.
  • But, 초기 로딩 이후 페이지 이동 시 CSR에 비해 느리고, 깜빡임 이슈가 있을 수 있음.
JS

a 태그와 onclick 이벤트로 페이지 이동시 차이

<a> 태그로 페이지를 이동하면 브라우저는 기본 동작을 수행하며, 페이지를 새로고침한다. 서버로부터 새로운 페이지의 내용을 받아오게 되며, 브라우저의 히스토리에 이동한 페이지가 추가되고, 웹 페이지의 URL도 변경된다.

onclick 이벤트로 페이지를 이동하는 경우는 JavaScript를 사용하여 페이지 내에서 동적으로 이동을 처리. 이 경우, 새로운 페이지를 브라우저가 서버로부터 받아오는 것이 아니라, 현재 페이지 내에서 동적으로 DOM 요소들이 변경되며, URL도 변경되지 않는다. 따라서 브라우저의 히스토리에 이동한 내용이 추가되지 않음.

차이점

<a> 태그는 전통적인 방식으로 페이지를 이동하는 데 사용되며, onclick 이벤트는 JavaScript를 사용하여 동적인 페이지 내에서의 이동이나 작업을 처리하는 데 사용된다.

network

OLAP 과 OLTP 차이점

OLAP (Online Analytical Processing)과 OLTP (Online Transaction Processing)는 데이터베이스 관리와 사용에 있어 서로 다른 목적과 특성을 가진 두 가지 시스템.

  • OLAP는 분석과 의사 결정을 지원하며, 복잡한 쿼리와 다차원 데이터를 처리하는데 중점
  • OLTP는 실시간 트랜잭션 처리를 지원하며, 빠른 응답 시간과 데이터 무결성을 유지하는데 중점
  • OLAP는 분석 및 의사 결정 지원, OLTP는 거래 처리 및 데이터 무결성 유지
  • OLAP는 다차원 모델, OLTP는 관계형 모델
  • OLAP는 복잡한 읽기 작업, OLTP는 단순한 읽기/쓰기 작업
  • OLAP는 빈번하지 않음, OLTP는 빈번함
  • OLAP는 길어도 괜찮음, OLTP는 짧아야 함
  • OLAP는 대규모 데이터, OLTP는 상대적으로 적은 데이터
web

웹 성능 최적화

성능 최적화 = 렌더링 최적화 + 로딩 최적화

성능 측정 도구는

  • Lighthouse
  • PageSpeed Insights 등을 이용할 수 있다.

렌더링 최적화

웹 페이지 렌더링 최적화의 목표는 리플로우를 최대한 적게 발생시키면서, 빠르게 화면을 그리는 것

css 최적화

  • 리플로우, 리페인트(Reflow/Repaint)를 고려한 스타일 작성

브라우저의 스타일이 그려지는 순서는 JS > Style > Layout > Paint > Composite이다. 이때 레이아웃의 넓이, 높이, 위치 등에 영향을 주는 css 속성을 변경할 경우 'Layout'부터 다시 그려지게 되는데 이게 리플로우(또는 레이아웃)이다.

반면 레이아웃에 영향을 주지 않는 속성을 변경하면 레이아웃을 건너뛰고 페인트 작업부터 다시 수행하게 되는데 이를 리페인트라고 한다.

리플로우가 일어나면 브라우저가 전체 픽셀을 다시 계산해야 하기때문에 되도록 리페인트 속성을 사용해 스타일을 작성하는 것이 성능면에서 좋다.

리플로우(Reflow)를 발생시키는 속성

position / width / height / margin / padding / display / top / left / right / bottom /
box-sizing / border-color / text-align / border / border-width /
font-family / float / font-size / font-weight / line-height / vertical-align /
white-space / word-wrap / text-overflow / text-shadow ...

리페인트(Repaint)를 발생시키는 속성

color / border-style / visibility / background / background-color /
background-image / background-position / background-repeat / background-size /
text-decoration / outline / outline-style / outline-color / outline-width /
border-radius / box-shadow ...

리플로우와 리페인트를 발생시키지 않는 속성

opacity / transform / cursor / z-index ...

리플로우를 피하면서도 div의 너비를 변경하려면, CSS의 transform 속성을 사용하는 것이 가장 효과적. transform 속성은 요소의 렌더링 위치를 변경하지 않고도 크기와 모양을 변형할 수 있다. 이는 리플로우를 발생시키지 않고, 대신 리페인트(Repaint)만 발생시킴. 예시: transform: scaleX() 사용

어쩔 수 없이 너비나 높이를 계속 변경해야 할 때는, absolute 속성을 이용해서 여러개의 레이어를 띄우는 것도 하나의 방법일듯!

  • 사용하지 않는 css 제거 구글 라이트하우스(Lighthouse)는 2KB 이상 사용되지 않은 css가 있을 시 오류로 표기함
  • 간결한 스타일 작성 복잡한 셀렉터 사용은 지양한다. css가 복잡하고 방대할수록 레이아웃을 그리는 데에 시간이 많이 소요된다. 선택자를 간결하게 사용하여 특이성을 낮게 유지하는 것이 좋다. 자식 요소의 경우, 부모요소를 가지고 있는지 확인하기 위해 DOM을 거슬러 올라가는 시간이 소요된다.
  • 애니메이션 최적화 애니메이션을 구현할 때에는 자바스크립트 api, 라이브러리 보다 css를 통해 구현하는 것이 성능면에서 이득이다. transform은 리플로우와 리페인트 모두 발생시키지 않고 합성만 발생시키는 속성이다. 때문에 애니메이션에서 사용 시 렌더링 속도를 향상시킬 수 있다. position 설정 시 absolute나 fixed로 설정하면 주변 요소에 영향을 주지 않는다.

HTML 최적화

  • 인라인 스타일을 사용하지 않는다 html 요소에 style을 통해 인라인 스타일을 작성하면 불필요한 코드 중복이 발생하기 쉽다. 인라인 스타일은 웹 페이지가 그려지면서 레이아웃에 영향을 미치면서 추가로 리플로우를 발생시킨다. 애초에 스타일은 스타일 시트에 작성하는 것이 표준에도 맞고, 유지보수 측면에서도 좋다.
  • 복잡한 DOM 트리 지양 DOM 트리가 깊고, 자식 요소가 많을수록 DOM 트리는 커진다. DOM 트리가 커지면 DOM 변경 시 계산해야하는 것이 많아진다.

DOM이 작고 깊이가 얕을수록 계산이 빠르다. 불필요하게 감싸는 요소는 제거한다. (ex. 불필요하게 감싼 div wrapper)

JS 최적화

  • 강제 동기 레이아웃 피하기 / 레이아웃 스래싱(thrashing) 피하기
    • 레이아웃 단계가 완료되기 전에 요소의 위치나 크기를 변경 후 바로 가져오려고 하면 강제로 레이아웃이 발생하는데 이것을 강제 동기 레이아웃 이라고한다.
    • 그리고 이 레이아웃을 반복적으로 발생시키는 것이 레이아웃 스레싱이다.
  • js의 공백 줄이기, 파일 크기를 줄이고 로딩 속도를 높이기 위해 / 캐싱 효율성이 증가

로딩 최적화

  • style은 상단, js는 하단에서 불러온다 html, css는 동시에 파싱되고 빠르게 그려질수록 좋다. css는 head 내에서 임포트. 브라우저는 차례대로 로드되는데, HTML 파일을 읽어온 후 css, js 파일을 만나면 해당 파일을 해석하는 동안 웹 페이지 렌더링이 일시적으로 차단되기 때문. (일반적으로 css/js 파일이 렌더링 차단 리소스, 모든 css/js 파일이 렌더링 차단 리소스인 것은 아니다.)
  • media 속성 사용 조건별로 css를 불러올 수 있다. 반응형 제작시 유용함 구글 크롬 라이트하우스는(Lighthouse) media 속성이 없는 < link rel=”stylesheet” >태그를 렌더 블로킹 리소스로 판단한다. media 속성이 없는 스타일시트는 해당 스타일시트를 브라우저가 해석하는 동안 화면에 스타일을 불러오지 않는다.
  • async / defer 사용 async와 defer 속성은 스크립트 파일을 병렬로 다운로드하게 해준다. 즉, 로딩 시 웹페이지 해석을 멈추지 않고 스크립트를 다운로드 하는것이다.

async는 다운로드 후 즉시 실행한다. defer는 웹페이지가 모두 그려지고 DOM이 들어왔을 때 스크립트를 실행한다.

<script> - 반드시 순서대로 실행되어야 할때
<script async> - 빨리 실행되어야 할때
<script defer> - 마지막에 파싱해도 상관없을때

사용하면 된다.
  • 이미지 최적화
    • picture, img 지연로딩 활용하기
    • 스프라이트 이미지 사용

picture 태그의 type 속성을 통해 사용자 환경에 맞는 이미지를 제공할 수 있다. picture 나 source 요소는 화면에 출력되는 요소가 아니다.

<!-- 브라우저가 avif를 지원하면 avif를 사용하고,
            그렇지 않은 경우 webp,
        둘 다 지원하지 않을 경우 jpg 이미지를 사용한다. -->
<picture>
  <source srcset="aaa.avif" type="image/avif" />
  <source srcset="aaa.webp" type="image/webp" />
  <img src="aaa.jpg" alt />
</picture>

loading 속성을 사용해서 이미지를 브라우저 화면에 지연/병렬 로딩할 수 있다. 사용 가능한 값으로 auto, lazy, eager가 있다.

  • auto: 디폴트 값. loading 속성을 쓰지 않은 것과 같다.
  • lazy: 화상에 보이는 부분만 먼저 출력하고 화면 바깥쪽 이미지들은 로딩하지 않는다. 사용자가 화면을 위로 올리면 아래쪽에 있던 이미지가 올라오면서 로딩된다.
  • eager: 화면 위치에 상관없이 페이지가 로딩되자마자 이미지를 로드한다.

스프라이트 이미지 사용 (저는 잘 안쓰지만.....) 여러개 이미지를 하나의 이미지로 만들어서 css의 background-position 속성을 사용해 부분적으로 이미지를 사용하는 방법이다. 이미지 파일 개수 자체를 줄이므로 리소스 요청 개수를 줄일 수 있다.

  • 웹팩(Webpack) 사용 모듈 번들러 웹팩(Webpack)을 사용해서 css와 js 파일을 번들링해(하나의 파일들로 묶어서) 리소스 요청을 줄일 수 있다
  • Gzip 사용 Gzip을 사용해 텍스트 기반의 리소스로 압축한다. 이미지, pdf 등은 이미 압축된 파일일 경우가 많아서 Gzip을 사용하지 않는다.
  • js 압축 UglifyJS 등을 사용해서 js 파일을 압축한다. 불필요한 공백이나 줄바꿈을 제거해서 파일의 용량이 감소하며, 난독화를 하면 민감한 코드를 알아보기 어렵게 만들 수 있다.
  • CDN 사용 CDN(Content Delivery Network)은 유저에게 많은 콘텐츠를 손실없이 빠르게 전달하는 서비스이다. 대용량 콘텐츠 다운 또는 스트리밍 등에 사용 사용한 만큼 비용을 지불한다.
  • 캐싱 사용 html, css, js, image 등을 첫 요청 시에 내려받은 뒤 특정 위치에 복사본을 저장하고, 이후 동일한 URl의 리소스 요청이 왔을 때 이전에 저장해둔 파일을 사용해서 더 빠르게 로딩하는데에 사용된다.