콜백

setTimeout은 스케줄링에 사용되는 가장 대표적인 함수

스크립트나 모듈을 로딩하는 것 또한 비동기 동작

 

콜백 기반(callback-based)’ 비동기 프로그래밍

<예시>

  • 스크립트 읽어오는 함수
function loadScript(src) {
  // <script> 태그를 만들고 페이지에 태그를 추가합니다.
  // 태그가 페이지에 추가되면 src에 있는 스크립트를 로딩하고 실행합니다.
  let script = document.createElement('script');
  script.src = src;
  document.head.append(script);
}
  • 스크립트 로딩이 끝나자마자 이 스크립트를 사용해 무언가를 해야만 한다고 가정
// script.js엔 "function newFunction() {…}"이 있습니다.
loadScript('/my/script.js'); 

newFunction(); // 함수가 존재하지 않는다는 에러가 발생합니다!

     - 에러는 브라우저가 스크립트를 읽어올 수 있는 시간을 충분히 확보하지 못했기 때문에 발생

  • loadScript 의 두 번째 인수로 스크립트 로딩이 끝난 후 실행될 함수인 콜백함수를 추가
function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(script);

  document.head.append(script);
}
  • 새롭게 불러온 스크립트에 있는 함수를 콜백 함수 안에서 호출
loadScript('/my/script.js', function() {
  // 콜백 함수는 스크립트 로드가 끝나면 실행됩니다.
  newFunction(); // 이제 함수 호출이 제대로 동작합니다.
  ...
});

→ 원하는 대로 외부 스크립트 안의 함수를 사용가능

 

무언가를 비동기적으로 수행하는 함수는 함수 내 동작이 모두 처리된 후 실행되어야 하는 함수가 들어갈 콜백을 인수로 반드시 제공해야 합니다. 이렇게 콜백을 사용한 방식은 비동기 프로그래밍의 일반적인 접근법

 

콜백 속 콜백

 

스크립트가 두 개 있는 경우, 어떻게 하면 두 스크립트를 순차적으로 불러올 수 있을까요?→ 방법은 콜백 함수안에 loadScript 를 호출하는 것, 이렇게 중첩 콜백을 만들면 바깥에 위치한 loadScript가 완료된 후, 안쪽 loadScript가 실행

 

!!! 콜백 안에 콜백을 넣는 것은 수행하려는 동작이 많은 경우엔 좋지 않은 방식

loadScript('/my/script.js', function(script) {

  alert(`${script.src}을 로딩했습니다. 이젠, 다음 스크립트를 로딩합시다.`);

  loadScript('/my/script2.js', function(script) {
    alert(`두 번째 스크립트를 성공적으로 로딩했습니다.`);
  });

});

 

에러 핸들링

loadScript에서 로딩 에러를 추적할 수 있게 기능을 개선

이러한 방식으로 에러를 다루는 패턴을 ⇒ 오류 우선 콜백(error-first callback) 이라고 한다

<예시>

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생했습니다.`));

  document.head.append(script);
}

 

<사용방식>

loadScript('/my/script.js', function(error, script) {
  if (error) {
    // 에러 처리
  } else {
    // 스크립트 로딩이 성공적으로 끝남
  }
});

 

오류 우선 콜백 의 관례

  1. callback의 첫 번째 인수는 에러를 위해 남겨둡니다. 에러가 발생하면 이 인수를 이용해 callback(err)이 호출됩니다.
  2. 두 번째 인수(필요하면 인수를 더 추가할 수 있음)는 에러가 발생하지 않았을 때를 위해 남겨둡니다. 원하는 동작이 성공한 경우엔 callback(null, result1, result2...)이 호출됩니다.

⇒ 오류 우선 콜백 스타일을 사용하면, 단일 콜백 함수에서 에러 케이스와 성공 케이스 모두를 처리할 수 있습니다.

 

멸망의 피라미드

  • 꼬리에 꼬리를 무는 비동기 동작이 많아지면 깊은 중첩이 있는 코드가 만들어진다
  • 이렇게 깊은 중첩 코드가 만들어내는 패턴은 소위 ‘콜백 지옥(callback hell)’ 혹은 '멸망의 피라미드(pyramid of doom)'라고 불립니다
loadScript('1.js', function(error, script) {

  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', function(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('3.js', function(error, script) {
          if (error) {
            handleError(error);
          } else {
            // 모든 스크립트가 로딩된 후, 실행 흐름이 이어집니다. (*)
          }
        });

      }
    })
  }
});

 

각 동작을 독립적인 함수로 만들어 위와 같은 문제를 완화하는 방법?

loadScript('1.js', step1);

function step1(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', step2);
  }
}

function step2(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('3.js', step3);
  }
}

function step3(error, script) {
  if (error) {
    handleError(error);
  } else {
    // 모든 스크립트가 로딩되면 다른 동작을 수행합니다. (*)
  }
};
  • 각 동작을 분리해 최상위 레벨의 함수로 만들었기 때문에 깊은 중첩이 없습니다.
  • 코드가 여기저기 흩어져 보이는 문제가 있다.
  • 게다가 step*이라고 명명한 함수들은 '멸망의 피라미드’를 피하려는 용도 만으로 만들었기 때문에 재사용이 불가능

→ 이를 해결할 수 있는 가장 좋은 방법 중 하나는 “프라미스” 를 사용하는 것이다.

 

참고자료

https://ko.javascript.info/async

npm run build

{
...
"scripts": {
    "build": "nest build",
    ...
    }
...
}

 터미널에서 npm run build 를 입력하면 package.json 에 있는 위의 스크립트가 실행됩니다.

실행이 완료되면, 프로젝트의 소스 코드가 dist 디렉토리 아래에 컴파일된 결과물로 저장됩니다..

이렇게 생성된 파일들은 배포에 사용됩니다.

또한,  aws-ec2 프리티어 micro 에 배포할 때  npm install 시 발생할 수 있는 메모리 부족현상을 해결 할때도 용이합니다.

npm run start:prod

"scripts": {
  "start:prod": "node dist/main"
}

     이 스크립트를 실행하면, dist 디렉토리 내의 main.js 파일이 Node.js를 통해 생성된 JavaScript 파일을 사용하여 애플리케이션이 실행됩니다.

 

이때, 빌드할 때 환경변수를 .env 를 통해 입력되도록 했으면  NODE_ENV 를 적용하여야 합니다.

빌드 과정을 마친 후, package.json 에 있는 스크립트를 아래의 예와 같이 수정하여 애플리케이션을 실행합니다.

"scripts": {
  "start:prod": "NODE_ENV=prod node dist/main"
}

 

Build 를 하는 이유 

Nest.js는 주로 TypeScript로 작성됩니다.

TypeScript는 JavaScript로 컴파일되어야 런타임에서 실행될 수 있습니다.

Build 과정에서 TypeScript 코드가 JavaScript 코드로 변환됩니다.

이 Build 과정을 통해 프로덕션 모드를 위한 애플리케이션을 빌드합니다.

이 과정에서 개발을 위한 의존성이 제외되고 배포만을 위한 의존성만 남게된다.

-> 클론 후 npm install 시 설치해야할 패키지들이 줄어든다. 

 

  package.json package-lock.json
역할 - Node.js 프로젝트에서 사용되는 json 파일.
- 프로젝트의 메터데이터(이름, 버전 등)와 의존성 목록을 정의
- 프로젝트에서 설치된 의존성의 정확한 버전과 의존성 트리를 저장
- 패키지 설치시 추가적으로 설치된 패키지들의 버전을 저장
필요성 - 개발자 간 협업 시 환경의 일관성을 유지 - 빌드의 일관성을  보장
- 배포나 빌드시 버전 충돌이나 예기치 않은 버그를 방지
차이 - 범용적인 의존성 정보를 제공 - 이미 설치된 의존성의 정확한 버전과 구조를 저장

 

 

참고자료

  1. https://velog.io/@songyouhyun/Package.json%EA%B3%BC-Package-lock.json%EC%9D%98-%EC%B0%A8%EC%9D%B4
  2. https://www.geeksforgeeks.org/difference-between-package-json-and-package-lock-json-files/

Git Flow

복잡한 프로젝트 관리를 위해 여러브랜치를 사용하는 전략이다.

장점 : 뚜렷한 브랜치 구조는 대규모 팀이나 프로젝트에 적합하고, 버전 관리와 출시가 용이하다.

단점 : 복잡하고 브랜치 관리가 어려울 수 있으며, 작은 프로젝트나 빠른 개발 사이클에는 비효율적일 수 있다.

GItHub Flow

단순화된 방식으로, main 브랜치 하나와 기능 브랜치들로 구성된다. 모든 변경사항은 main 으로 병합되기 전에 리뷰된다.

장점 : 단순하고 이해하기 쉬우며, 지속적인 배포와 통합에 적합하다.

단점 : 매우 크고 복잡한 시스템에서는 충분한 구조를 제공하지 않을 수 있다.

GitLab-flow

위의 두 전략의 장점을 결합하여 환경에 따라 코드를 배포하는 유연한 전략이다.

장점 : 유연하며 다양한 배포 환경에 적응할 수 있다. 코드 관리와 릴리스가 분리되어 있어 안정성을 높일 수 있다.

단점 : 설정과 관리가 복잡할 수 있으며, 전략을 완전히 이해하고 적용하기 위해서는 경험이 필요할 수 있다.

 

요약 및 정리

전략 브랜치 구조 주 사용 사례 장점 단점
Git Flow master, develop, feature, release, hotfix 대규모 프로젝트, 복잡한 릴리스 사이클 체계적인 브랜치 관리 가능 설정 및 관리 복잡성
GitHub Flow main, feature, 지속적인 배포와 통합, 단순 프로젝트 간단하고 빠른 개발사이클에 적합 대규모, 복잡한 시스템 관리에 한계
GitLab master, pre-production, production 다양한 배포 환경, 유연한 워크 플로우 필요 환경별 코드 배포 가능 초기 설정이 복잡하며 유지 관리에 노력이 필요

 

 

참고자료

  1. https://wiki.yowu.dev/ko/dev/Git/about-git-github-gitlab-flow
  2. https://velog.io/@kw2577/Git-branch-%EC%A0%84%EB%9E%B5
  3. https://blog.hwahae.co.kr/all/tech/9507

설치

$ npm install --save joi

 

 

예시

// app.module.ts

import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('dev', 'prod', 'local', 'debug')
          .default('development'),
        PORT: Joi.number().port().default(3000),
      }),
    }),
  ],
})
export class AppModule {}

 

 

npm run start:local  를 했지만

package.json 에 아래와 같이 되었있다면

"start:local": "NODE_ENV=locla nest start --watch",

 

→ Error: Config validation error: "NODE_ENV" must be one of [dev, prod, local, debug]

 

이처럼 환경변수를 인풋으로 받고 이것이 유효한지 검사하는 기능을 하는 메소드

나중에 포트 번호와 

 

출처

https://docs.nestjs.com/techniques/configuration#schema-validation

+ Recent posts