728x90

React에서 테스트 코드를 짤 때 API 핸들러 함수와 React 컴포넌트를 테스트하는 두 가지 주요 범주로 나눌 수 있다.

 

Next.js를 사용한다면 npm install node-mocks-http로 설치해서 사용하는 node.js 환경에서 http 요청 및 응답 객체를 모킹하는 라이브러리를 사용해도 좋다. 이 라이브러리를 사용하면, 테스트 시 실제 서버를 띄우지 않고도 http 요청과 응답을 시뮬레이션할 수 있다. 

 

Node.js와 Jest는 CommonJS 모듈을 사용하지만 ES 모듈을 사용하는 프로젝트에는 import { jest } from '@jest/globals';를 사용해보자 jest 객체를 전역으로 가져올 수 있고, require을 사용하는 CommonJS 환경에 맞춰진 Jest를 import를 사용하는 ES모듈에 사용할 수 있도록 도와준다.

 

루트폴더에서 __tests__라는 폴더를 만들고, 테스트대상인 파일명.test.js나 ts 로 이름을 짓고 테스트 코드를 작성해보자 대충 이런 느낌이다.

import { createMocks } from 'node-mocks-http';
import handler from './handler';
import { jest } from '@jest/globals';
import axios from 'axios';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('API Handler', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  it('should return 200 with success message', async () => {
    const { req, res } = createMocks({
      method: 'GET',
    });

    await handler(req, res);

    expect(res._getStatusCode()).toBe(200);
    expect(res._getJSONData()).toEqual({ message: 'success' });
  });

  // 추가 테스트 케이스 작성
});

 

그리고 git commit이 될 때마다 test가 되어야하기 때문에 루트폴더/.github/workflows/ci.yml 파일을 만들어야한다.

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x]

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}

    - name: Install dependencies
      run: npm install

    - name: Run tests
      run: npm test
      env:
        NEXT_PUBLIC_RIOT_API_KEY: ${{ secrets.NEXT_PUBLIC_RIOT_API_KEY }}

 

이렇게 코드를 짜면 env쪽에서 경고문구가 나올 수 있다. 이는 그냥 무시해도 좋은데 꼭 경고를 보기 싫으면 error lens와 같이 vscode extenstion을 고려할 수 있고 이유를 모르겠다 싶으면 settings.json에

"yaml.customTags": [
  "!<tag:yaml.org,2002:js/undefined> [0,1]",
  "!!binary sequence"
],
"yaml.schemas": {
  "https://json.schemastore.org/github-workflow.json": "file:///YOUR_WORKFLOW_FILE_PATH"
}

 이걸 추가해보자

 

민감정보가 담겨있는 환경변수들을 github에 몰래 올려야 하므로, github 저장소에서 Settings > Secrets and variables > Actions로 이동하여 New respository secret 버튼을 누른 뒤, 위 코드 기준 NEXT_PUBLIC_RIOT_API_KEY를 name에 입력하고 value에 api 키를 입력하는 식으로 등록을 해야한다.

 

그리고 프로젝트 루트에 jest.config.js를 생성하여 아래와 같이 코드를 쳐 Jest 설정을 해보자

module.exports = {
  testEnvironment: 'node',
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
  testMatch: ['**/*.test.ts', '**/*.test.tsx'],
};

 

이걸 github에 푸쉬하게 되면 

 

이렇게 초록색 체크를 볼 수있다. 만약 테스트에 실패했다면 위 사진에서 Details를 누르거나 Actions에 들어가서 테스트 실패한 이유도 확인할 수 있다.

취미로 만드는 서비스나 XP같이 빠르고 클라이언트의 요구사항 기능만 잘 됐으면 됐지 뭐 이런 마인드로 개발을 하고자하면 테스트 코드를 안짜는 것을 추천한다. commit 된 후에 배포된 도메인에서 확인하는 시간이 좀 더 길어질 뿐더러 테스트 코드 작성을 하며 시간적 리소스낭비로 개발 생산성도 떨어질 수 있기 때문이다.

 

저사양 노트북일때 github 테스트때메 chrome에서도 렉이 걸릴수도 있다. 그리고 test실패할 때마다 메일이 날아오므로 설정도 필요하다는 단점도 있다.

728x90

'웹 개발 > 기타' 카테고리의 다른 글

XSS, CORS, Code Injection  (0) 2024.05.26
host domain, origin, cookie, local, session  (0) 2024.05.05
HTTP, HTTPS, Mixed Content  (0) 2024.05.05
Web Workers와 Concurrency  (0) 2024.04.13
Synchronous I/O, Asynchronous I/O  (1) 2024.04.06
728x90

XSS(Cross-Site Scripting)는 웹 애플리케이션에서 발견되는 보안 취약점 중 하나다. XSS는 공격자가 웹 페이지에 악의적인 스크립트를 주입하고, 이 스크립트가 다른 사용자의 브라우저에서 실행되도록 함으로써 사용자의 정보를 탈취하거나 사기 행위를 저지를 수 있게 만드는 공격이다.

 

Stored XSS는 악의적인 스크립트가 웹 애플리케이션의 서버에 저장된다. 예를 들어, 게시판이나 댓글 섹션에 악의적인 스크립트가 포함된 내용을 작성할 수 있다. 사용자가 해당 페이지를 방문할 때마다 악의적인 스크립트가 실행된다. 이 유형은 다수의 사용자에게 영향을 미칠 수 있어 매우 위험하다.

 

Reflected XSS 사용자로부터 받은 입력이 즉시 실행된다. 예를 들어, 검색 결과 페이지에서 검색어를 그대로 브라우저에 출력할 때 악의적인 스크립트가 포함된 검색어를 사용자가 입력하면 스크립트가 실행된다. 이 스크립트는 주로 피싱 공격에 사용되며, 사용자가 링크를 클릭해야만 공격이 발동된다.

 

DOM-based XSS DOM(Document Object Model)의 취약점을 이용하여 공격이 수행된다. 웹 페이지의 클라이언트 사이드 스크립트가 DOM을 잘못 처리하여 발생한다. URL의 일부로 전달된 데이터가 안전하지 않게 처리되어 스크립트가 실행될 수 있다.

 

돔 기반 XSS 예시:

<!DOCTYPE html>
<html>
<head>
    <title>DOM-based XSS Example</title>
</head>
<body>
    <h1>Welcome</h1>
    <div id="message"></div>
    <script>
        var message = document.getElementById('message');
        var query = window.location.search.substring(1);
        message.innerHTML = decodeURIComponent(query);
    </script>
</body>
</html>

 

XSS는 사용자의 세션 토큰이나 쿠키를 탈취하여 사용자의 계정을 도용할 수 있다. 악의적인 스크립트를 통해 사용자의 브라우저에서 웹 사이트의 내용이나 기능을 변경할 수 있다. 가짜 로그인 폼을 생성하여 사용자의 로그인 정보를 탈취할 수 있다. 한 사용자의 브라우저에서 실행된 스크립트가 다른 사용자들에게 퍼질 수 있다.

 

입력 검증 및 새니타이징으로 사용자 입력을 검증하고, 스크립트 태그나 이벤트 핸들러 등을 포함할 수 있는 문자를 적절히 필터링하거나 이스케이프 처리를 해야한다.

 

콘텐츠 보안 정책(CSP)은 콘텐츠 보안 정책을 설정하여 외부 스크립트의 실행을 제한한다.

 

HTTPOnly 쿠키는 HTTPOnly 플래그를 쿠키에 설정하여 자바스크립트를 통한 쿠키 접근을 차단한다.

 

CORS(Cross-Origin Resource Sharing)는 웹 페이지가 다른 도메인의 리소스를 안전하게 요청할 수 있도록 하는 보안 메커니즘이다. 기본적으로 브라우저는 같은 출처 정책(Same-Origin Policy)을 적용하여, 스크립트가 실행 중인 원본(도메인, 프로토콜, 포트)과 다른 원본의 리소스에 대한 접근을 제한한다. CORS는 이 정책을 확장하여, 특정 조건 하에 다른 출처의 리소스 접근을 허용한다.

 

CORS는 HTTP 헤더를 사용하여 브라우저와 서버 간에 정보를 교환한다. 서버는 'Access-Control-Allow-Origin'과 같은 CORS 관련 헤더를 설정하여, 특정 출처의 브라우저가 해당 서버의 리소스에 접근할 수 있도록 허용한다.

 

메소드가 GET, HEAD, POST 중 하나이고, HTTP 헤더가 일반적으로 허용되는 헤더(CORS 사양에서 정의된 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type' 등)로만 구성되어 있을 때 단순 요청으로 간주된다.

 

이 경우 브라우저는 특별한 CORS 헤더 없이 요청을 보낸다. 서버는 응답에 'Access-Control-Allow-Origin' 헤더를 포함하여 요청을 허용할 출처를 지정한다.

 

PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH 또는 Content-Type이 application/json인 POST 등, 단순 요청에 해당하지 않는 메소드를 사용하는 요청은 사전 요청이 필요하다.

 

브라우저는 실제 요청을 보내기 전에 'OPTIONS' 메소드를 사용하여 사전 요청을 보낸다. 이 요청은 서버에게 실제 요청을 안전하게 처리할 수 있는지 확인하기 위한 것이다.

 

서버는 'Access-Control-Allow-Methods', 'Access-Control-Allow-Headers' 등의 헤더로 응답하여, 어떤 HTTP 메소드와 헤더, 출처가 허용되는지 명시한다.

 

쿠키나 HTTP 인증 정보와 같은 사용자 자격 증명을 포함하는 요청도 CORS 정책의 일부다. 이러한 요청을 허용하려면 서버는 'Access-Control-Allow-Origin' 헤더에 구체적인 출처를 명시하고, 'Access-Control-Allow-Credentials' 헤더를 'true'로 설정해야 한다.

 

CORS는 안전하지 않은 교차 출처 요청을 방지하면서도, 필요한 경우에는 이를 허용하여 웹 애플리케이션의 유연성을 증가시킨다.

 

API 제공자는 CORS를 통해 자신의 리소스에 대한 접근을 세밀하게 제어할 수 있다. 개발자는 서로 다른 도메인에서 호스팅되는 리소스를 안전하게 요청하고 활용할 수 있어, 서비스 구성의 유연성이 향상된다.

 

Code Injection은 공격자가 애플리케이션에 악의적인 코드를 주입하고 실행하게 만드는 보안 취약점을 이용한 공격이다. 이러한 공격은 일반적으로 애플리케이션의 입력 검증 또는 적절한 새니타이징이 부족할 때 발생한다. 공격자는 이 취약점을 통해 시스템을 제어하거나 데이터를 탈취하고, 추가적인 악의적 행위를 수행할 수 있다.

 

SQL Injection은 애플리케이션의 데이터베이스 쿼리에 악의적인 SQL 코드를 삽입하는 방식이다. 공격자는 데이터베이스에서 데이터를 읽어오거나 수정, 삭제하는 등의 작업을 수행할 수 있다.

 

서비스 거부(DoS)는 애플리케이션의 리소스를 고갈시켜 정상적인 서비스 제공을 방해할 수 있다.

728x90
728x90

Host는 네트워크에 연결된 장치를 지칭할 수도 있고, URL에서 웹 서버의 주소를 가리키는 용어이다. 웹 주소에서의 Host는 도메인 이름과 포트 번호를 포함할 수 있다. 예를 들어, URL http://www.example.com:80/page.html에서 Host는www.example.com:80이다. 여기서 www.example.com은 도메인 이름이고, 80은 포트 번호이다.

 

Domain은 인터넷에서 사이트를 식별하는 주소이다. 쿠키와 관련하여, "Cookie Domain"은 해당 쿠키가 유효한 도메인을 명시한다. 쿠키 도메인이 설정되면, 이 도메인과 서브 도메인에서만 쿠키가 접근 가능하다. 예를 들어, 쿠키 도메인이 .example.com으로 설정되면, www.example.com, subdomain.example.com 등에서 해당 쿠키를 사용할 수 있다. 쿠키 도메인이 명시적으로 설정되지 않으면, 쿠키는 그 쿠키를 생성한 서버의 도메인에 대해서만 접근 가능하다.Origin은 웹 보안에서 매우 중요한 개념으로, 프로토콜, 호스트(도메인과 포트 번호를 포함) 그리고 포트를 포함하여 정의된다.

 

Origin은 콘텐츠의 출처를 식별하고, 다양한 보안 정책(예: SOP, CORS 등)에서 중요한 역할을 한다. 예를 들어, https://www.example.com:443에서 프로토콜은 https, 호스트는 www.example.com, 포트는 443이다. 이 모든 요소가 같아야 동일 출처로 간주된다.Host vs. Domain: Host는 특정 웹 서버를 가리키는 데 사용되며 포트 번호를 포함할 수 있다. 반면, Domain은 더 넓은 범위로, 같은 도메인 내에서 쿠키 등의 공유 자원을 관리하는 데 사용된다.

 

Domain vs. Origin: Domain은 보통 웹 주소의 일부로 쓰이며 쿠키와 같은 웹 리소스의 범위를 정의하는 데 사용된다. Origin은 프로토콜, 호스트, 포트 모두를 명시적으로 포함하여 웹 콘텐츠의 출처를 보다 엄격하게 정의한다. Origin이 같아야만 웹 페이지간 데이터를 자유롭게 공유할 수 있다.Host vs. Origin: Host는 도메인 이름과 선택적으로 포트 번호를 포함하지만, Origin은 프로토콜, 도메인, 포트 모두를 명시적으로 포함한다. 보안 정책 적용 시 Origin이 더 엄격하게 사용된다.

 

SameSite 쿠키는 웹사이트가 사용자의 브라우저와 상호 작용하는 방식을 보다 세밀하게 제어할 수 있게 하는 쿠키의 속성 중 하나이다. 이 속성은 크로스 사이트 요청 보안(Cross-Site Request Forgery, CSRF) 공격을 방지하는 데 효과적으로 사용될 수 있다. SameSite 속성은 쿠키가 한 웹사이트에서 생성되어 다른 웹사이트와의 상호 작용에서 사용되는 것을 제한한다.

 

SameSite=Strict로 설정된 쿠키는 쿠키를 생성한 동일한 사이트에서만 보내진다. 예를 들어, 사용자가 www.example.com에서 생성된 쿠키는 오직 www.example.com에서만 읽을 수 있다.이 설정은 사용자가 링크를 클릭하여 다른 사이트로 이동했을 때, 쿠키가 전송되지 않는다는 것을 의미한다. 이로 인해 사용자 경험이 제한될 수 있지만, 보안은 매우 강화된다.

 

SameSite=Lax 설정은 좀 더 유연하다. 이 설정은 사용자가 외부 사이트에서 현재 사이트로 자연스럽게 넘어갈 때 (예: 링크 클릭을 통해) 쿠키를 전송한다.그러나 외부 사이트에서 발생하는 POST 요청에서는 쿠키를 전송하지 않는다. 이는 대부분의 CSRF 공격 시나리오에서 보호할 수 있다. 이 설정은 사용자 경험을 크게 훼손하지 않으면서도 필수적인 보안을 제공한다.

 

SameSite=None 설정은 쿠키가 모든 출처의 요청에 대해 전송되도록 허용한다. 이 설정을 사용하려면 쿠키는 반드시 Secure 플래그도 설정되어야 하며, 쿠키는 오직 보안 연결(HTTPS)을 통해서만 전송된다.이 옵션은 다양한 도메인 간에 세션을 유지해야 하는 애플리케이션에서 유용하다.

 

SameSite 속성은 웹 애플리케이션을 CSRF 공격과 일부 크로스 사이트 스크립팅(XSS) 공격으로부터 보호하는 데 도움을 준다.개발자는 이 속성을 사용하여 애플리케이션의 쿠키 전송 정책을 더 세밀하게 제어할 수 있다.특히 Lax 설정은 대부분의 사용 사례에 적합한 보안과 사용성의 균형을 제공한다. 브라우저들은 SameSite 속성을 지원하고 있으며, 웹 개발자들은 이 옵션을 적절히 사용하여 애플리케이션의 보안을 강화할 수 있다.

 

웹 개발에서 사용자 데이터를 저장하는 방법으로 Cookies, localStorage, sessionStorage 세 가지가 주로 사용된다. 이 저장 방법들은 각기 다른 특성과 용도를 가지며, 사용 사례에 따라 적절한 저장소를 선택하는 것이 중요하다.

 

Cookies는일반적으로 4KB로 제한된다. 쿠키는 만료 날짜를 가질 수 있으며, 설정된 날짜가 지나면 자동으로 삭제된다. Secure, HttpOnly, SameSite 등의 플래그를 설정하여 보안을 강화할 수 있다. 모든 HTTP 요청에 쿠키 정보가 포함되어 서버로 전송되므로, 네트워크 트래픽에 영향을 줄 수 있다.사용자 인증, 사용자 설정 등을 관리하며, 서버가 사용자를 식별하도록 돕는다.

 

localStorage는 대부분의 브라우저에서 5MB~10MB 사이의 데이터를 저장할 수 있다. 브라우저가 닫혀도 데이터가 지속적으로 저장되며, 수동으로 삭제하지 않는 한 유지된다. 데이터가 서버로 전송되지 않아 네트워크 부하가 없다. 같은 도메인 및 프로토콜 내에서만 접근할 수 있다. 사용자 설정, 사이트 기본 설정, 대량의 데이터 저장 등에 사용된다.

 

sessionStorage는 localStorage와 비슷하게 5MB~10MB 사이다. 브라우저 탭이 열려 있는 동안에만 데이터가 유지되며, 탭을 닫으면 데이터가 삭제된다. 데이터가 서버로 전송되지 않아 네트워크 부하가 없다. localStorage와 같이 같은 도메인 및 프로토콜 내에서만 접근할 수 있다. 각 브라우저 탭에 독립적인 세션 데이터를 유지하는 데 사용되며, 예를 들어 온라인 폼 작성 중 페이지를 새로 고침해도 데이터가 유지되도록 한다.

728x90
728x90

HTTP는 웹에서 데이터를 주고받기 위한 프로토콜로, 클라이언트와 서버 간의 통신을 가능하게 한다. 클라이언트(대개 웹 브라우저)는 서버에 페이지나 데이터를 요청하고, 서버는 그 요청에 응답하여 필요한 정보를 제공한다. 서버는 클라이언트의 이전 상태를 기억하지 않으며, 각 요청은 독립적이다.

 

이러한 특성 때문에 HTTP는 "무상태(stateless)" 프로토콜로 분류된다. HTTP 통신은 일반적으로 신뢰성 있는 TCP/IP 프로토콜을 사용하여 이루어지며, 이 프로토콜은 다양한 데이터 형식을 지원하고 새로운 기능을 쉽게 추가할 수 있는 유연성을 제공한다.

 

HTTP는 기본적으로 암호화되지 않기 때문에 데이터가 네트워크를 통해 전송될 때 외부로부터 쉽게 엿보거나 변조될 수 있다는 단점이 있다. 이러한 보안 문제를 해결하기 위해 HTTPS가 도입되었다. HTTPS는 HTTP의 보안 버전으로, SSL(Secure Sockets Layer) 또는 TLS(Transport Layer Security) 프로토콜을 사용하여 클라이언트와 서버 간의 통신을 암호화한다. 이는 데이터의 기밀성과 무결성을 보장하며, 서버나 클라이언트가 자신이 주장하는 대로 신뢰할 수 있는지 확인하는데 중요한 역할을 한다.

 

데이터 보안과 관련하여, HTTPS는 중간자 공격(Man-In-The-Middle Attack)을 방지하고, 크로스 사이트 스크립팅(XSS), 세션 하이재킹, 크로스 사이트 리퀘스트 포저리(CSRF)와 같은 다양한 웹 기반 공격으로부터 사용자를 보호한다.

 

또한, HTTPS는 데이터를 암호화하여 통신 내용이 외부에 노출되는 것을 방지한다. 이는 데이터가 전송 중에 손상되거나 변경되지 않았음을 보장하고, 웹사이트의 신뢰성을 사용자에게 보장하는 데 도움을 준다. SSL/TLS 인증서는 웹사이트의 신원을 인증받을 수 있으며, 사용자가 실제로 의도한 서버와 통신하고 있음을 확인시켜 준다.

 

Mixed Content 발생은 HTTPS로 보호된 웹 페이지에서 HTTP를 통해 로드된 리소스가 포함될 때 일어난다. 이는 웹 보안을 약화시킬 수 있다.

 

능동적인 Mixed Content는 웹 페이지와 상호 작용하며 보안 위험을 증가시키고, 수동적인 Mixed Content는 페이지의 기밀성을 손상시키지는 않지만 보안 상태에 대한 잘못된 인식을 유발할 수 있다. 현대의 브라우저는 대부분 Mixed Content를 차단하고, 개발자들은 모든 리소스를 HTTPS를 통해 제공하도록 권장받는다.

 

SSH, TLS, GPG, 그리고 HTTP/2는 모두 네트워크와 데이터 전송을 안전하게 보호하는 데 필수적인 기술들이다. SSH는 네트워크 서비스를 안전하게 운영하며 원격 시스템 관리와 파일 전송에 사용된다. TLS는 데이터의 암호화를 통해 네트워크 통신의 보안을 강화하고, GPG는 데이터의 암호화와 디지털 서명을 제공한다.

 

HTTP/2는 웹의 성능을 향상시키기 위해 설계되었으며, 다중화와 헤더 압축을 통해 더 빠른 웹 경험을 제공한다. 이 기술들은 통신의 보안을 강화하며, 사용자 데이터를 보호하는 데 중요한 역할을 한다. 각 기술은 특정 목적에 맞게 설계되었으며, 안전한 파일 전송, 네트워크 통신의 보안 강화, 개인 데이터의 보호 등에 사용된다.

728x90
728x90

웹 워커(Web Workers)는 자바스크립트에서 멀티스레딩을 구현할 수 있는 기능이다. 자바스크립트는 기본적으로 싱글 스레드로 동작하기 때문에, 복잡하고 시간이 많이 걸리는 작업을 수행할 때 UI가 멈추지 않도록 별도의 백그라운드 스레드에서 코드를 실행할 필요가 있다.

 

웹 워커를 사용하면 메인 스레드가 아닌 별도의 스레드에서 스크립트를 실행할 수 있어, 메인 스레드(주로 UI를 담당)는 사용자 인터랙션과 화면 업데이트에 집중할 수 있고, 워커 스레드에서는 계산이 많이 필요한 작업을 동시에 처리할 수 있다. 이로 인해 애플리케이션이 더욱 빠르고 반응적으로 동작할 수 있다.

 

웹 워커는 데이터를 메인 스레드와 메시지 형태로 교환하며, 이 메시지는 복사되어 전달되므로 메인 스레드와 워커 스레드 사이에 데이터를 공유하지 않는다. 워커 내에서는 DOM에 직접 접근할 수 없으며, 메인 스레드에서 전달받은 데이터를 기반으로 작업을 수행하고 결과를 메인 스레드로 다시 보낸다.

 

예를 들어, 이미지 처리나 파일 다운로드와 같은 무거운 작업을 워커에서 처리하면 메인 스레드는 이러한 처리가 완료될 때까지 멈추지 않고 계속하여 사용자와의 상호작용을 유지할 수 있다.

 

자바스크립트는 싱글 스레드 언어로, 기본적으로 한 번에 하나의 작업만 수행할 수 있다. 그러나 비동기 프로그래밍 모델을 사용하여 동시성(concurrency)을 구현하고, 여러 작업을 효율적으로 처리할 수 있다. 이를 위한 주요 메커니즘으로 이벤트 루프, 콜백 함수, 프로미스(Promises), async/await 등이 있다.

 

자바스크립트의 실행 환경인 브라우저나 Node.js는 이벤트 루프를 사용하여 연속적인 작업을 처리한다. 이벤트 루프는 콜 스택(Call Stack), 이벤트 큐(Event Queue), 백그라운드 작업을 관리한다.

 

콜 스택이 비어있을 때, 이벤트 루프는 이벤트 큐에서 콜백 함수를 하나씩 콜 스택으로 옮겨 실행한다. 이러한 구조 덕분에 자바스크립트는 비동기 콜백을 활용하여 여러 작업을 동시에 처리할 수 있다.

 

프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체이다. 콜백 지옥을 피하고, 비동기 코드의 가독성과 작성 편의성을 높이는 데 도움을 준다. 프로미스는 세 가지 상태를 가진다. (대기(pending), 이행(fulfilled), 거부(rejected))

const promise = new Promise((resolve, reject) => {
  if (/* 조건 */) {
    resolve(value); // 작업 성공
  } else {
    reject(error); // 작업 실패
  }
});

promise.then(result => {
  console.log(result); // 성공 결과 처리
}).catch(error => {
  console.error(error); // 에러 처리
});

 

 

async/await는 프로미스 기반의 비동기 코드를 동기 코드처럼 쉽게 작성할 수 있게 해주는 ES2017(ES8)의 문법이다. async로 함수를 정의하면 그 함수는 자동으로 프로미스를 반환한다. await 키워드는 프로미스가 처리될 때까지 함수 실행을 일시 중지하고, 결과 값 또는 에러가 반환될 때까지 기다린다.

async function fetchData() {
  try {
    const data = await fetch('url'); // 프로미스가 이행될 때까지 기다림
    const json = await data.json();
    console.log(json);
  } catch (error) {
    console.error(error);
  }
}

 

이러한 메커니즘을 통해 자바스크립트는 싱글 스레드임에도 불구하고 다수의 비동기 작업을 효과적으로 관리하고, 동시에 여러 작업을 수행할 수 있는 동시성을 제공한다.

728x90
728x90

동기 I/O(Synchronous I/O)는 입력 또는 출력 작업이 실행될 때 프로그램의 실행이 그 작업이 완료될 때까지 차단(Block)되는 방식을 의미한다. 즉, 동기 I/O 작업을 요청하면 해당 작업이 완전히 끝나야만 다음 코드 라인이 실행된다. 그리고 프로그램의 실행 흐름이 소스 코드의 순서와 일치하기 때문에, 코드를 읽고 이해하기 쉽다.

 

Blocking I/O에서는 I/O 작업(예: 파일 읽기, 네트워크를 통한 데이터 수신 등)을 요청할 때, 해당 작업이 완료될 때까지 해당 요청을 한 프로세스 또는 스레드가 대기 상태에 들어간다. 즉, 작업이 완료될 때까지 아무것도 하지 못하고 기다려야 한다. 이 방식은 프로그래밍 모델이 단순하고 직관적이지만, I/O 작업의 대기 시간 동안에 CPU가 다른 유용한 작업을 수행하지 못하게 하여 자원을 낭비할 수 있다. (동기 I/O는 I/O 작업 중에 CPU가 유휴 상태가 될 수 있으므로 리소스 활용 측면에서 비효율적일 수 있다.)

 

동기 I/O 방식을 썼을 때 일어나는 일이 뭐가 있을까?

  1. 프로그램이 파일 시스템에서 데이터를 읽거나 쓸 때, 해당 작업이 완료될 때까지 다음 작업으로 넘어가지 않는다.
  2. 데이터베이스에 쿼리를 요청하고, 그 결과를 받기 전까지 다른 작업을 진행하지 않는 경우가 생긴다.
  3. 웹 요청을 보내고 응답을 받을 때까지 기다리는 작업 등이 동기 I/O 방식의 예이다.

비동기 I/O와 비교할 때, 동기 I/O는 구현의 단순함과 코드의 직관적인 흐름 때문에 작은 규모의 애플리케이션 또는 I/O 작업의 복잡성이 낮은 경우에 적합하다. 반면, 비동기 I/O는 I/O 작업이 백그라운드에서 수행되어 프로그램의 응답성과 성능을 향상시킬 수 있는 환경에서 유리하다.

 

비동기 I/O(Asynchronous I/O)는 I/O 작업이 시작된 후, 그 작업이 완료될 때까지 프로그램의 실행이 대기하지 않고 바로 다음 작업으로 넘어가는 방식이다. 이는 I/O 작업이 백그라운드에서 처리되며, 작업의 완료와 데이터의 반환을 대기하는 동안 프로그램이 다른 작업을 수행할 수 있게 해준다.

 

비동기 I/O는 비차단(Non-blocking) 방식을 활용하여 I/O 작업이 프로그램의 실행 흐름을 차단하지 않는다. 프로그램은 I/O 작업의 완료를 기다리지 않고 즉시 다음 코드를 실행할 수 있다.

 

Non-blocking I/O에서는 I/O 작업을 요청하더라도 해당 작업이 즉시 완료될 수 없을 때 대기하지 않고 바로 제어권이 호출자에게 반환된다. 이는 I/O 작업이 완료되지 않았다는 신호와 함께, 프로세스 또는 스레드가 다른 작업을 계속 진행할 수 있게 해준다. Non-blocking I/O는 I/O 작업의 완료를 폴링(polling)하거나, 이벤트 기반 모델을 사용하여 알아차리게 된다. 이 방식은 리소스를 더 효율적으로 활용할 수 있지만, 작업 완료를 확인하고 관리하기 위한 추가적인 로직이 필요하다.

 

Non-blocking I/O를 효과적으로 활용하기 위해서는 비동기 프로그래밍 모델, 이벤트 루프, 콜백 함수, 프로미스, async/await 등과 같은 개념과 기술을 이해하고 적용할 필요가 있다. 이러한 방식은 Node.js와 같은 플랫폼에서 널리 사용되며, 고성능 네트워크 서버와 애플리케이션을 구축하는 데 중요한 역할을 한다.

 

콜백 함수는 다른 함수에 인자로 전달되는 함수로, 어떤 이벤트가 발생했거나 특정 작업이 완료된 후에 실행된다. 이를 통해 비동기 작업을 처리할 수 있다. 그러나 콜백 함수를 사용하여 여러 비동기 작업을 연속으로 처리하려고 할 때, 코드는 점점 중첩되고 가독성이 떨어지는 문제가 발생한다. 이러한 상황을 콜백 지옥(Callback Hell) 또는 콜백 피라미드(Pyramid of Doom)라고 한다.

getData(function(a){
    getMoreData(a, function(b){
        getMoreData(b, function(c){ 
            console.log('콜백 지옥');
        });
    });
});

 

프로미스는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체다. 콜백 지옥의 문제를 해결하기 위해 ES6에서 도입되었다. 프로미스는 .then() 메서드를 통해 연속된 비동기 작업을 체이닝할 수 있게 해준다. 또한, .catch() 메서드를 통해 에러를 쉽게 처리할 수 있다.

getData()
  .then(result => getMoreData(result))
  .then(result => getMoreData(result))
  .then(finalResult => console.log(finalResult))
  .catch(error => console.error(error));

 

async/await는 ES2017(ES8)에서 소개된, 비동기 작업을 동기적으로 보이는 코드 스타일로 작성할 수 있게 해주는 문법이다. async 함수 내부에서 await 키워드를 사용하면, 프로미스가 처리될 때까지 함수의 실행을 일시 중단하고, 프로미스의 결과가 반환되면 실행을 재개한다. 이를 통해 비동기 코드를 마치 동기 코드처럼 쉽게 작성하고 이해할 수 있다.

async function asyncCall() {
  try {
    const result = await getData();
    const moreData = await getMoreData(result);
    const finalResult = await getMoreData(moreData);
    console.log(finalResult);
  } catch (error) {
    console.error(error);
  }
}

 

 

비동기 프로그래밍 모델의 장접은 애플리케이션의 응답성을 향상시켜주고, I/O 작업 중에도 CPU는 다른 작업을 처리할 수 있으므로, 자원을 보다 효율적으로 활용할 수 있다. 또한, 여러 I/O 작업을 동시에 처리할 수 있어, 애플리케이션의 처리 능력을 향상시킬 수 있다.

 

하지만 비동기 프로그래밍 모델은 동기 모델에 비해 이해하고 사용하기가 더 복잡할 수 있고 콜백 지옥과 같은 문제를 관리해야 하는 단점이 있다.

728x90

+ Recent posts