발행일

[ZBF] 자바스크립트 동기/ 비동기 TAIL ASK

[ZBF] 자바스크립트 동기/ 비동기 TAIL ASK

이 글은 네이버 블로그에 2024년 5월 21일에 올렸던 것을 그대로 옮겨온 것입니다.

함수를 호출 하면 함수 코드가 평가되어 함수 실행 컨텍스트가 생성된다. 이 때 생성된 함수 실행 컨텍스트는 실행 컨텍스트 스택, 콜스택에 푸시 되고 함수 코드가 실행 된다.

실행 컨텍스트 (Execution Context)

실행 컨텍스트는 자바스크립트 코드가 실행되는 환경을 의미합니다. 자바스크립트 엔진은 이 컨텍스트를 이용해 코드를 평가하고 실행합니다. 실행 컨텍스트는 함수가 호출될 때마다, 전역 코드가 실행될 때마다, 그리고 eval 함수가 호출될 때마다 생성됩니다.

실행 컨텍스트의 구성 요소

  1. 변수 객체 (Variable Object, VO):
  • 변수, 함수 선언 및 함수의 매개변수를 저장합니다.

  • 전역 컨텍스트에서는 전역 객체가 변수 객체가 됩니다. 함수 컨텍스트에서는 활성 객체(Activation Object, AO)가 변수 객체가 됩니다.

  1. 스코프 체인 (Scope Chain):
  • 현재 컨텍스트와 외부 렉시컬 환경을 참조합니다.

  • 스코프 체인은 식별자(변수 이름 등)를 해결하는 데 사용됩니다.

  1. this 바인딩:
  • 실행 컨텍스트 내에서 this 키워드가 가리키는 객체를 의미합니다.

실행 컨텍스트의 생성 과정

  1. 전역 컨텍스트:
  • 스크립트가 처음 실행될 때 전역 컨텍스트가 생성됩니다.

  • 전역 객체는 브라우저 환경에서는 window 객체이고, Node.js 환경에서는 global 객체입니다.

  1. 함수 컨텍스트:
  • 함수가 호출될 때마다 새로운 함수 컨텍스트가 생성됩니다.

  • 함수 컨텍스트는 해당 함수의 지역 변수, 매개변수, 내부 함수 선언 등을 포함합니다.

실행 컨텍스트의 동작 과정

  1. 생성 단계 (Creation Phase):
  • 변수 객체 생성: 함수의 매개변수와 변수 선언이 VO에 저장됩니다.

  • 스코프 체인 설정: 현재 컨텍스트의 스코프 체인이 생성됩니다.

  • this 바인딩: this 키워드가 설정됩니다.

  1. 실행 단계 (Execution Phase):
  • 코드가 한 줄씩 실행되면서 변수 값이 할당되고, 함수 호출이 이루어집니다.

실행 컨텍스트 스택

실행 컨텍스트 스택은 코드의 실행 순서를 관리합니다. 전역 컨텍스트가 스택의 bottom에 위치하며, 함수 호출 시마다 새로운 실행 컨텍스트가 스택의 top에 쌓입니다. 함수 실행이 완료되면 해당 컨텍스트가 스택에서 제거됩니다.

자바스크립트 엔진은 단하나의 실행 컨텍스트 스택을 갖는다.

스코프 (Scope)

스코프는 변수와 함수가 참조될 수 있는 유효 범위를 의미합니다. 자바스크립트에서는 스코프를 통해 변수와 함수의 가시성을 제어합니다. 스코프는 크게 전역 스코프와 지역 스코프로 나눌 수 있으며, ES6 이후에는 블록 스코프가 추가되었습니다.

스코프의 종류

  1. 전역 스코프 (Global Scope)

  2. 지역 스코프 (Local Scope)

  3. 블록 스코프 (Block Scope)

전역 스코프 (Global Scope)

전역 스코프는 코드의 어느 곳에서든 접근할 수 있는 범위를 의미합니다. 전역 스코프에 선언된 변수는 전체 코드에서 참조될 수 있습니다.

지역 스코프 (Local Scope)

지역 스코프는 함수 내에서 선언된 변수와 함수만 접근할 수 있는 범위를 의미합니다. 함수 내부에서 선언된 변수는 함수 외부에서 참조할 수 없습니다.

블록 스코프 (Block Scope)

블록 스코프는 letconst 키워드를 사용하여 선언된 변수에 적용됩니다. 블록 스코프는 코드 블록 내에서만 유효합니다. 코드 블록은 \{\}로 감싸진 영역을 의미합니다.

스코프 체인 (Scope Chain)

스코프 체인은 현재 실행 컨텍스트의 변수 객체와 외부 환경의 변수 객체를 연결하는 목록입니다. 변수나 함수를 참조할 때, 자바스크립트 엔진은 스코프 체인을 따라 상위 스코프로 이동하면서 식별자를 찾습니다.

렉시컬 스코핑 (Lexical Scoping)

자바스크립트는 렉시컬 스코핑을 따릅니다. 이는 함수의 스코프가 함수가 정의된 위치에 의해 결정된다는 의미입니다. 함수가 호출된 위치가 아니라 정의된 위치에 따라 상위 스코프가 결정됩니다.

자바스크립트 엔진은 한 번에 하나의 태스크만 실행하는 싱글스레드 방식으로 동작

싱글스레드 방식의 동작

자바스크립트는 싱글스레드 방식으로 동작하며, 다음과 같은 주요 요소들을 통해 작업을 관리합니다:

  1. 호출 스택 (Call Stack)

  2. 이벤트 루프 (Event Loop)

  3. 태스크 큐 (Task Queue)

  4. 백그라운드 (Web APIs)

  5. 호출 스택 (Call Stack)

호출 스택은 자바스크립트 코드가 실행되는 동안 함수 호출을 관리하는 LIFO(Last In, First Out) 구조입니다. 함수가 호출되면 호출 스택의 맨 위에 쌓이고, 함수가 완료되면 스택에서 제거됩니다.

  1. 이벤트 루프 (Event Loop)

이벤트 루프는 호출 스택과 태스크 큐를 모니터링하여 비동기 작업을 관리하는 역할을 합니다. 호출 스택이 비어 있을 때 태스크 큐에서 작업을 가져와 호출 스택에 추가합니다.

  1. 태스크 큐 (Task Queue)

태스크 큐는 비동기 작업의 콜백 함수를 대기시키는 큐입니다. 비동기 작업이 완료되면 해당 콜백 함수가 태스크 큐에 추가됩니다. 이벤트 루프는 호출 스택이 비어 있을 때 태스크 큐에서 콜백 함수를 가져와 실행합니다.

  1. 백그라운드 (Web APIs)

브라우저 환경에서는 타이머, 네트워크 요청 등의 비동기 작업이 백그라운드에서 실행됩니다. 백그라운드 작업이 완료되면 해당 콜백 함수가 태스크 큐에 추가됩니다.

비동기 작업의 처리

console.log('Start');

setTimeout(() => {
  console.log('This message is displayed after 2 seconds');
}, 2000);

console.log('End');

자바스크립트는 싱글스레드 방식으로 동작하지만, 비동기 작업을 통해 동시성을 제공합니다. 비동기 작업은 호출 스택을 블로킹하지 않고 백그라운드에서 실행되며, 완료되면 태스크 큐를 통해 처리됩니다.

싱글 스레드 방식은 하나의 태스크만 실행 할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행 하게 되면 블로킹이 발생한다.

블로킹 (Blocking)

블로킹(Blocking)이란 프로그램 실행 중 하나의 작업이 완료될 때까지 다른 작업이 대기하는 상태를 의미합니다. 블로킹이 발생하면 시스템의 자원이 해당 작업에 의해 점유되어 다른 작업을 수행할 수 없습니다. 블로킹은 주로 I/O 작업, 네트워크 요청, 파일 읽기/쓰기 등의 작업에서 발생할 수 있습니다.

  1. 동기(Synchronous) 블로킹

동기적 코드는 블로킹 방식으로 동작합니다. 다음 작업이 이전 작업이 완료될 때까지 기다립니다.

  1. 비동기(Asynchronous) 비블로킹

비동기적 코드는 블로킹 없이 동작합니다. 작업이 완료될 때까지 기다리지 않고 다음 작업을 계속 수행합니다.

블로킹의 문제점

블로킹이 발생하면 시스템의 효율성이 떨어질 수 있습니다. 특히, 사용자 인터페이스(UI)에서는 블로킹이 사용자 경험에 악영향을 미칠 수 있습니다. 예를 들어, 웹 페이지가 서버로부터 데이터를 가져올 때 블로킹이 발생하면, 그 동안 사용자는 페이지와 상호작용할 수 없습니다.

자바스크립트에서 블로킹을 피하는 방법

자바스크립트는 기본적으로 싱글스레드로 동작하지만, 비동기 프로그래밍을 통해 블로킹을 피할 수 있습니다. 비동기 프로그래밍은 주로 콜백 함수, 프라미스(Promise), async/await를 사용하여 구현됩니다.

콜백 함수 (Callback Function)

콜백 함수는 다른 함수에 인자로 전달되어 실행되는 함수입니다. 자바스크립트는 비동기 프로그래밍과 함수형 프로그래밍의 특징을 가지며, 이러한 특성으로 인해 콜백 함수는 매우 자주 사용됩니다.

콜백 함수의 기본 개념

  1. 정의: 콜백 함수는 함수의 인자로 전달되어, 특정 작업이 완료된 후 호출되는 함수입니다.

  2. 사용 목적: 주로 비동기 작업(예: 타이머, 네트워크 요청, 이벤트 처리)이나 함수형 프로그래밍에서 사용됩니다.

콜백 함수의 장단점

장점

  1. 비동기 작업 처리: 콜백 함수는 비동기 작업을 처리하는 데 매우 유용합니다. 작업이 완료된 후 특정 작업을 수행할 수 있게 해줍니다.

  2. 함수 재사용성: 콜백 함수는 여러 함수에 재사용될 수 있으며, 코드의 재사용성을 높여줍니다.

  3. 유연성: 콜백 함수를 통해 동적으로 함수를 실행할 수 있어 코드의 유연성이 높아집니다.

단점

  1. 콜백 지옥 (Callback Hell): 콜백 함수가 중첩되어 사용될 경우, 코드의 가독성이 떨어지고 유지보수가 어려워질 수 있습니다.

  2. 에러 처리의 복잡성: 콜백 함수 내에서 에러 처리가 복잡해질 수 있습니다.

콜백 지옥 (Callback Hell) 해결 방안

콜백 지옥을 해결하기 위해 다음과 같은 기법을 사용할 수 있습니다:

  1. 이름 있는 함수 사용

  2. 프라미스 (Promise) 사용

  3. async/await 사용

프라미스 (Promise)

프라미스(Promise)는 자바스크립트에서 비동기 작업을 처리하기 위해 사용되는 객체입니다. 프라미스는 비동기 작업의 완료 또는 실패를 나타내며, 이를 통해 비동기 코드를 더 깔끔하고 읽기 쉽게 작성할 수 있습니다.

프라미스의 기본 개념

  1. 정의: 프라미스는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다.

  2. 상태:

  • 대기 (Pending): 초기 상태, 비동기 작업이 아직 완료되지 않음.

  • 이행 (Fulfilled): 비동기 작업이 성공적으로 완료됨.

  • 거부 (Rejected): 비동기 작업이 실패함.

  1. 결과 값:
  • 이행 값 (Value): 비동기 작업이 성공적으로 완료된 경우 반환되는 값.

  • 거부 이유 (Reason): 비동기 작업이 실패한 경우 반환되는 이유.

프라미스의 사용 방법

프라미스는 new Promise 생성자를 사용하여 생성됩니다. 이 생성자는 두 개의 콜백 함수, resolvereject를 인자로 받습니다. 비동기 작업이 성공적으로 완료되면 resolve를 호출하고, 실패하면 reject를 호출합니다.

프라미스의 메서드

  1. then 메서드: 프라미스가 이행되었을 때 호출되는 콜백 함수를 등록합니다.

  2. catch 메서드: 프라미스가 거부되었을 때 호출되는 콜백 함수를 등록합니다.

  3. finally 메서드: 프라미스가 이행되거나 거부된 후에 호출되는 콜백 함수를 등록합니다.

then 메서드

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 2000);
});

myPromise.then((value) => {
  console.log(value); // 'Success!' (2초 후에 출력)
});
  • 역할: 프라미스가 이행(Fulfilled) 상태가 되었을 때 호출됩니다.

  • 인자: 두 개의 콜백 함수를 인자로 받을 수 있습니다. 첫 번째 콜백은 프라미스가 성공했을 때 호출되며, 두 번째 콜백은 선택적이고 프라미스가 실패했을 때 호출됩니다.

  • 반환값: 새로운 프라미스를 반환하여 체이닝이 가능합니다.

catch 메서드

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Error occurred!');
  }, 2000);
});

myPromise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.error(error); // 'Error occurred!' (2초 후에 출력)
  });
  • 역할: 프라미스가 거부(Rejected) 상태가 되었을 때 호출됩니다.

  • 인자: 프라미스가 실패했을 때 호출되는 하나의 콜백 함수를 인자로 받습니다.

  • 반환값: 새로운 프라미스를 반환하여 체이닝이 가능합니다.

finally 메서드

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 2000);
});

myPromise
  .then((value) => {
    console.log(value); // 'Success!' (2초 후에 출력)
  })
  .catch((error) => {
    console.error(error);
  })
  .finally(() => {
    console.log('Promise processing completed'); // 항상 실행 (2초 후에 출력)
  });
  • 역할: 프라미스가 이행(Fulfilled) 상태이든 거부(Rejected) 상태이든 상관없이 항상 호출됩니다.

  • 인자: 아무 인자도 받지 않으며, 항상 실행되는 하나의 콜백 함수를 인자로 받습니다.

  • 반환값: 새로운 프라미스를 반환하여 체이닝이 가능합니다.

const promise1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('First promise');
  }, 1000);
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('Second promise');
  }, 2000);
});

Promise.all([promise1, promise2])
  .then((values) => {
    console.log(values); // ['First promise', 'Second promise'] (2초 후에 출력)
  })
  .catch((error) => {
    console.error(error);
  });

프라미스의 활용 예

프라미스는 주로 비동기 작업, 예를 들어, 서버로부터 데이터 가져오기, 파일 읽기/쓰기, 타이머 설정 등에 사용됩니다.

프라미스의 장단점

장점

  1. 가독성 향상: 비동기 작업을 순차적으로 처리하는 코드를 작성할 수 있어 가독성이 향상됩니다.

  2. 에러 처리: catch 메서드를 통해 비동기 작업의 에러를 쉽게 처리할 수 있습니다.

  3. 체이닝: 여러 비동기 작업을 체이닝하여 순차적으로 처리할 수 있습니다.

단점

  1. 콜백 헬 해결 한계: 프라미스는 콜백 지옥을 어느 정도 해결하지만, 여전히 복잡한 비동기 로직에서는 한계가 있을 수 있습니다.

  2. 상대적으로 복잡한 문법: 프라미스의 문법이 초보자에게는 다소 복잡할 수 있습니다.

async/await

프라미스를 더 간결하고 직관적으로 사용할 수 있게 해주는 ES8(ECMAScript 2017) 문법입니다. 이 문법을 사용하면 비동기 코드를 마치 동기 코드처럼 작성할 수 있습니다.

async 키워드

  • 역할: 함수 앞에 붙여서 해당 함수를 비동기 함수로 만듭니다.

  • 반환값: 항상 프라미스를 반환합니다. 함수 내에서 반환된 값은 자동으로 resolve됩니다.

await 키워드

  • 역할: async 함수 내부에서만 사용 가능하며, 프라미스가 이행될 때까지 기다립니다.

  • 반환값: 프라미스가 이행되면 그 결과 값을 반환하고, 거부되면 오류를 던집니다.

콜스택과 힙으로 구성 되어 있는 자바스크립트 엔진

콜 스택 (Call Stack)과 힙 (Heap)

콜 스택 (Call Stack)

콜 스택은 자바스크립트 엔진이 함수 호출을 관리하는 LIFO(Last In, First Out) 구조의 데이터 스택입니다. 함수가 호출되면 콜 스택에 쌓이고, 함수가 완료되면 콜 스택에서 제거됩니다. 콜 스택은 현재 실행 중인 코드의 위치를 추적하고, 함수 호출 간의 순서를 관리합니다.

콜 스택의 동작 원리

  1. 함수 호출: 함수가 호출되면 콜 스택의 맨 위에 해당 함수가 추가됩니다.

  2. 함수 실행: 함수가 실행되면서 콜 스택에 쌓여 있는 상태를 유지합니다.

  3. 함수 종료: 함수 실행이 완료되면 콜 스택에서 해당 함수가 제거됩니다.

힙 (Heap)

힙은 동적으로 생성된 객체가 저장되는 메모리 공간입니다. 힙은 구조화되지 않은 메모리 풀로, 대규모 객체를 저장하고 관리합니다. 객체는 힙에 할당되며, 가비지 컬렉터(Garbage Collector)에 의해 필요 없는 객체는 자동으로 정리됩니다.

힙의 특징

  • 동적 메모리 할당: 객체, 배열, 함수 등 동적으로 생성된 데이터는 힙에 저장됩니다.

  • 비구조적: 힙은 콜 스택과 달리 구조화되지 않은 메모리 영역입니다.

  • 가비지 컬렉션: 사용되지 않는 객체를 자동으로 정리하여 메모리 누수를 방지합니다.

콜 스택과 힙의 차이점

특징콜 스택 (Call Stack)힙 (Heap)
용도함수 호출 및 실행 컨텍스트 관리동적으로 생성된 객체 및 데이터 저장
구조LIFO(Last In, First Out)비구조적 메모리 영역
메모리 할당함수 호출 시마다 스택 프레임이 추가되고, 함수 종료 시 제거됨객체, 배열, 함수 등이 동적으로 할당됨
자동 관리함수 호출 순서에 따라 자동 관리가비지 컬렉터에 의해 자동 관리
사용 예함수 호출, 함수 실행 상태 추적객체, 배열, 함수 등 동적 데이터 저장

현재 실행 중인 태스크가 종료되지 않은 상태라고 해도 다음 태스크를 곧바로 실행한다면 비동기 처리라 한다.

HTML 요소가 애니매이션 효과를 통해 움직이면서 이벤트를 처리 하기도 하고, HTTP 요청을 통해 서버로부터 데이터를 가지고 오면서 렌더링을 하기도 한다. 이 처럼 자바스크립트의 동시성을 지원하는 것이 이벤트 루프이다.

AJAX

AJAX(Asynchronous JavaScript and XML)는 웹 페이지에서 동적으로 데이터를 가져와서 페이지를 새로 고침하지 않고도 업데이트할 수 있는 기술입니다. AJAX는 주로 XMLHttpRequest 객체나 Fetch API를 사용하여 비동기적으로 서버와 통신합니다.

방식

  1. XMLHttpRequest

  2. Fetch API

장점

  • 페이지를 새로 고침하지 않고도 데이터를 동적으로 업데이트 가능

  • 비동기적으로 데이터를 처리하여 사용자 경험 향상

  • 서버와 클라이언트 간의 데이터 전송을 효율적으로 수행

단점

  • 코드가 복잡해질 수 있음

  • 비동기 처리로 인해 콜백 지옥 발생 가능

  • CORS(Cross-Origin Resource Sharing) 이슈 발생 가능

HTTP와 HTTPS

HTTP (Hypertext Transfer Protocol)

  • 정의: 웹에서 데이터를 주고받기 위한 프로토콜

  • 포트: 80번 포트를 사용

  • 특징: 암호화되지 않은 평문 데이터를 전송, 보안에 취약

HTTPS (Hypertext Transfer Protocol Secure)

  • 정의: HTTP에 SSL/TLS를 추가하여 보안을 강화한 프로토콜

  • 포트: 443번 포트를 사용

  • 특징: 데이터를 암호화하여 전송, 데이터 무결성과 기밀성 보장

차이점

  • 보안: HTTP는 암호화되지 않은 데이터를 전송하지만, HTTPS는 데이터를 암호화하여 전송

  • 신뢰성: HTTPS는 SSL/TLS 인증서를 사용하여 데이터의 신뢰성을 보장

웹소켓 통신

웹소켓(WebSocket)은 클라이언트와 서버 간의 양방향 통신을 가능하게 하는 프로토콜입니다. 웹소켓은 단일 TCP 연결을 통해 실시간으로 데이터를 교환할 수 있습니다.

특징

  • 양방향 통신: 클라이언트와 서버가 자유롭게 데이터를 주고받을 수 있음

  • 실시간 데이터 전송: 지연이 거의 없는 실시간 통신 가능

  • 연결 유지: 초기 연결 이후에 계속해서 연결을 유지하여 효율적인 데이터 전송 가능

JWT 토큰

JWT(JSON Web Token)는 JSON 객체를 사용하여 정보를 안전하게 전송하기 위한 토큰입니다. 주로 사용자 인증 및 권한 부여에 사용됩니다.

구성 요소

  1. 헤더(Header): 토큰 유형과 해싱 알고리즘 정보

  2. 페이로드(Payload): 사용자 정보와 같은 클레임(Claims)을 포함

  3. 서명(Signature): 헤더와 페이로드를 암호화한 서명

장점

  • 보안: 정보는 서명되어 있어 변조가 어려움

  • 자체 포함: 모든 정보가 토큰에 포함되어 있어 추가 서버 요청이 필요 없음

  • 유연성: 다양한 플랫폼과 언어에서 사용 가능

단점

  • 토큰 크기: 자체 포함된 정보로 인해 토큰 크기가 커질 수 있음

  • 기밀성 부족: 기본적으로 암호화되지 않기 때문에, 민감한 정보는 포함하지 말아야 함

RESTful API

REST(Representational State Transfer)는 웹 서비스를 설계하기 위한 아키텍처 스타일입니다. RESTful API는 REST 원칙을 따르는 API입니다.

특징

  • 무상태성(Stateless): 각 요청은 독립적이며, 서버는 이전 요청의 상태를 저장하지 않음

  • 클라이언트-서버 구조: 클라이언트와 서버는 독립적으로 개발되고 운영될 수 있음

  • 캐시 가능: 응답은 캐시될 수 있어 네트워크 성능이 향상됨

  • 계층화: 시스템은 계층화될 수 있어 각 계층은 독립적으로 관리됨

RESTful API 설계 원칙

  1. 자원 기반 설계: 모든 리소스는 고유한 URI를 통해 식별됩니다.
  2. 표현 방식: 리소스는 JSON, XML 등 다양한 표현 방식으로 전송될 수 있습니다.
  3. HTTP 메서드 사용: 각 HTTP 메서드는 특정 작업을 수행하기 위해 사용됩니다.
  4. 무상태성: 서버는 클라이언트의 상태를 저장하지 않으며, 각 요청은 독립적으로 처리됩니다.
  5. 캐싱 가능: 응답은 캐시될 수 있어 네트워크 성능을 향상시킬 수 있습니다.

HTTP 메서드와 RESTful API

  1. API 엔드포인트 (Endpoint)

프론트엔드가 요청을 보낼 백엔드의 URL입니다. 이 URL은 서버의 특정 리소스나 기능을 가리킵니다.

  1. HTTP 메서드 (Method)

어떤 작업을 수행할지 명시하는 HTTP 메서드입니다. 주요 HTTP 메서드와 그 사용 목적은 다음과 같습니다:

  • GET: 데이터를 요청합니다.
  • POST: 새로운 데이터를 생성합니다.
  • PUT: 기존 데이터를 업데이트합니다.
  • DELETE: 데이터를 삭제합니다.
  1. 요청 헤더 (Request Headers)

요청에 대한 추가 정보를 제공하는 헤더입니다. 중요한 헤더는 다음과 같습니다:

  • Content-Type: 전송하는 데이터의 형식을 명시합니다. 일반적으로 application/json을 사용합니다.
  • Authorization: 인증이 필요한 경우 토큰 등을 포함하여 전송합니다.
  • Accept: 클라이언트가 이해할 수 있는 미디어 타입을 명시합니다.
  1. 요청 본문 (Request Body)

주로 POST와 PUT 요청에서 사용되며, 서버에 전송할 데이터를 포함합니다. JSON 형식이 일반적입니다.

  1. 쿼리 파라미터 (Query Parameters)

특정 데이터를 필터링하거나 세부 사항을 지정할 때 사용됩니다. URL의 끝에 ?key=value 형식으로 추가합니다.

  1. 인증 및 보안

보안이 필요한 경우, 토큰 기반 인증(JWT), API 키 또는 세션을 통해 인증 정보를 전송합니다. 이를 통해 백엔드는 요청이 적법한지 확인할 수 있습니다.

const url = 'https://api.example.com/users';
const data = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  password: 'securepassword'
};

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <JWT_TOKEN>'
  },
  body: JSON.stringify(data)
})
  .then(response => response.json())
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error));

- GET: 리소스 조회

- POST: 리소스 생성

- PUT: 리소스 업데이트

- DELETE: 리소스 삭제

메서드설명URL 예시요청 페이로드응답 예시
GET리소스 조회/users/123없음json<br>{ "id": 123, "name": "John Doe", "email": "john.doe@example.com" }<br>
POST리소스 생성/usersjson<br>{ "name": "John Doe", "email": "john.doe@example.com" }<br>json<br>{ "id": 124, "name": "John Doe", "email": "john.doe@example.com" }<br>
PUT리소스 업데이트/users/123json<br>{ "name": "Jane Doe", "email": "jane.doe@example.com" }<br>json<br>{ "id": 123, "name": "Jane Doe", "email": "jane.doe@example.com" }<br>
DELETE리소스 삭제/users/123없음json<br>{ "message": "User deleted successfully" }<br>

AXIOS와 FETCH

  • AXIOS와 FETCH는 JavaScript에서 HTTP 요청을 보내기 위해 사용되는 두 가지 주요 방법입니다.
  • FETCH는 자바스크립트 내장 함수로, 별도의 라이브러리 설치 없이 사용할 수 있습니다. Promise 기반으로 동작하며, 간단한 구문으로 서버와 통신할 수 있습니다.
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Callback

Callback 함수는 다른 함수에 인자로 전달된 함수입니다. 비동기 처리에서 특정 코드의 실행이 끝난 후 실행되어야 할 때 사용됩니다.

function fetchData(callback) {
  // 비동기 작업
  setTimeout(() => {
    callback('done');
  }, 1000);
}

fetchData(result => console.log(result)); // 'done'

Promise

Promise는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체입니다. Resolve (성공), Reject (실패) 두 가지 상태를 가집니다.

let promise = new Promise(function(resolve, reject) {
  // 비동기 작업
  setTimeout(() => resolve("done"), 1000);
});

promise.then(
  result => console.log(result), // 'done'
  error => console.log(error) // 실행되지 않음
);

await / async

async / await는 Promise를 더 쉽게 사용할 수 있도록 하는 문법입니다. async로 함수를 선언하면 해당 함수는 항상 Promise를 반환합니다. await는 async 함수 내에서만 사용되며, Promise가 처리될 때까지 함수의 실행을 일시 중지합니다.

async function fetchData() {
  let response = await fetch('https://api.example.com/data');
  let data = await response.json();
  console.log(data);
}

fetchData();

Resolve, Reject 그리고 에러 핸들링

// Promise
fetch('https://api.example.com/data')
  .then(response => response.json())
  .catch(error => console.error('Error:', error));

// async/await
async function fetchData() {
  try {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

반복문에서 비동기 처리

let urls = ['https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3'];

Promise.all(urls.map(url =>
  fetch(url).then(response => response.json())
)).then(results => {
  console.log(results); // 각 URL로부터의 응답이 배열로 반환됨
}).catch(error => {
  console.error('Error:', error);
});

CORS (Cross-Origin Resource Sharing)

CORS(Cross-Origin Resource Sharing)는 웹 페이지가 다른 도메인에서 리소스를 요청할 때 보안 정책을 정의하는 방법입니다. CORS는 브라우저의 동일 출처 정책(Same-Origin Policy)을 우회하여, 서버가 특정 도메인에서의 요청을 허용할 수 있도록 합니다. 이 정책은 웹 페이지가 악의적인 요청을 보내는 것을 방지하여 보안을 강화합니다.

동일 출처 정책 (Same-Origin Policy)

동일 출처 정책은 웹 브라우저가 다른 출처(origin)에서 요청된 리소스를 제한하는 보안 메커니즘입니다. 여기서 출처는 프로토콜, 도메인, 포트로 구성됩니다. 동일 출처 정책은 스크립트가 자신의 출처와 동일한 출처에서만 리소스를 요청할 수 있도록 제한합니다.

CORS의 동작 원리

CORS는 서버가 특정 출처에서의 요청을 허용할 수 있도록 HTTP 헤더를 통해 정책을 정의합니다. 이를 통해 웹 페이지가 다른 도메인에서 안전하게 리소스를 요청할 수 있습니다.

주요 CORS 헤더

  1. Access-Control-Allow-Origin: 서버가 허용하는 출처를 지정합니다. 와일드카드(*)를 사용하여 모든 출처를 허용할 수 있습니다.

  2. Access-Control-Allow-Methods: 서버가 허용하는 HTTP 메서드를 지정합니다.

  3. Access-Control-Allow-Headers: 서버가 허용하는 HTTP 요청 헤더를 지정합니다.

  4. Access-Control-Allow-Credentials: 자격 증명(쿠키, 인증 헤더 등)을 포함한 요청을 허용할지 여부를 지정합니다.

  5. Access-Control-Expose-Headers: 클라이언트가 접근할 수 있는 응답 헤더를 지정합니다.

  6. Access-Control-Max-Age: 브라우저가 사전 요청(preflight request)을 캐시할 수 있는 시간을 지정합니다.

CORS 요청의 종류

CORS 요청은 단순 요청(Simple Request)과 사전 요청(Preflight Request)으로 나뉩니다.

1. 단순 요청 (Simple Request)

다음 조건을 모두 만족하는 요청은 단순 요청으로 간주됩니다:

  • GET, POST, HEAD 메서드 중 하나를 사용

  • Accept, Accept-Language, Content-Language, Content-Type 등의 헤더만 포함

서버는 Access-Control-Allow-Origin 헤더를 통해 요청을 허용합니다.

2. 사전 요청 (Preflight Request)

단순 요청의 조건을 만족하지 않는 요청은 사전 요청을 통해 서버의 허용 여부를 확인합니다. 브라우저는 OPTIONS 메서드를 사용하여 서버에 사전 요청을 보냅니다.