xction-dev / xction.co.kr

Xction!의 홈페이지를 만들고 있습니다
0 stars 0 forks source link

Architecture - Packaging and Deploy #35

Open designDefined opened 7 months ago

designDefined commented 7 months ago

목차

JS

버저닝

자바스크립트는 기본적으로 ECMAScript(ES)라는 버전을 사용한다. 그 중에서 제일 중요한 건 ES5 / ES6(ES2015)의 변경이다. ES6은 import와 export를 도입하여 자바스크립트의 모듈화 방식을 크게 변경시켰고, let과 const를 추가하여 이른바 '모던 자바스크립트 환경'을 구축했다. 다만 모든 브라우저에서 이 버전을 지원하지 않는다는 게 단점.

개발에 사용할 버전

개발에 사용할 버전은 우리가 로컬에 설치한 Node.js 버전에 의존한다. 14 이상의 Node.js가 깔려 있다면 최신(ES2020) 버전 문법까지 문제 없이 지원할 것이다.

배포에 사용할 버전

배포에 사용하는 버전은 최대한 낮게 고정시키는 게 일반적이다. 그래야 브라우저 호환성도 좋고, 어차피 추가되는 기능은 개발자의 경험을 개선하는 문법이 대부분이기 때문이다. 그렇다면 어떻게 ES2020을 낮은 버전으로 변환시킬까? 타입스크립트 환경에서는 크게 두 가지 방법이 있는데, 하나는 타입스크립트를 자바스크립트로 컴파일할 때 변환하는 것이고, 다른 하나는 자바스크립트 코드를 번들링할 때 변환하는 것이다. Next.js는 전자를 사용하는데, tsconfig.json을 보면 compilerOptions.target을 지정하지 않은 것을 볼 수 있다(기본값은 es3). 서버에서는 타입스크립트를 컴파일할 때 버전을 다운시키지 않는다. 어차피 ec2에 최신버전 노드를 설치하고 구동시킬 것이라 그렇다. 따라서 compilerOptions.target을 "ES2020"으로 지정해두었다.

{
  "compilerOptions": {
    "target": "ES2020",
    ...후략...
}

번들링

번들링은 여러 파일에 나뉘어 있는 자바스크립트 코드를 하나의 파일로 뭉치는 과정이다. 번들링을 하는 이유는 아래와 같다.

번들러

번들링을 해주는 번들러 라이브러리는 대표적으로 Webpack이 있다. 직접 번들링을 하

우리 프로젝트는

프론트엔드 프레임워크인 Next.js는 자체 번들러를 사용하고, 서버는 현재 번들러를 달지 않은 상태이다. 즉 typescript 코드를 변환만 해서 사용한다. 서버는 난독화를 크게 신경 쓸 필요가 없긴 하지만, 코드 용량을 위해서는 번들링을 할 필요가 있을 듯.

TS

타입스크립트는 자바스크립트로 컴파일되는 정적 타입 언어다. 버전에 따라 다르긴 하지만 자바스크립트 문법을 완벽하게 지원하고, 자체적으로 (tsc 명령어) 자바스크립트 컴파일이 가능하다. 따라서 하나의 언어라 보기보다는 자바스크립트의 스타일을 바꿔주는 라이브러리에 가깝다고 볼 수 있다.

타입스크립트의 용도

타입스크립트는 독립적인 언어가 아니다 보니 그 용도를 제한할 필요가 있다. 아래와 같은 사실을 주목하자.

tsconfig.json

타입스크립트의 설정을 관리하는 파일로, 주로 컴파일이나 린팅 관련 옵션을 정의한다. 서버 워크스페이스의 tsconfig.json 파일을 보면 대충 어떤 용도인지 알 수 있다.

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "CommonJS",
    "skipLibCheck": true,

    /* Build settings */
    "outDir": "./dist",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "paths": {
      "@/*": ["./src/*"],
      "@core/*": ["../core/*"]
    }
  },
  "include": ["src", "../core/**/*.ts"]
}

대충 include에 명시된 파일들을 es2020 버전으로 dist 폴더에 컴파일하겠다 라고 생각하면 된다. 직접 tsc 명령어를 돌려보면 동일하게 동작할 것이다. 자세히 뜯어 보면 린팅이나 import alias 관련된 규칙도 정의하고 있는 것을 볼 수 있다. 이런 규칙들은 eslint의 typescript parser가 알아서 이해하여 적절히 린트를 해준다.

배포 시의 타입스크립트

ec2를 이용해 동적 배포를 하게 되면 타입스크립트를 크게 두 방식으로 처리할 수 있다. 하나는 타입스크립트 코드까지 싹다 업로드 후에 ec2 인스턴스 내에서 ts-node로 구동 또는 tsc로 컴파일 후 구동하는 것이고, 다른 하나는 로컬 내지는 github actions에서 타입스크립트를 컴파일한 후에 컴파일된 코드만 ec2에 올리고 구동시키는 것이다. 전자가 구현이 간단하고, 후자는 용량과 서버 비용 상의 이점이 있다.

AWS

EC2

서버 컴퓨터를 빌리는 간단한 서비스이다. 빌린 컴퓨터는 인스턴스라고 하고, 다양한 비용과 사양의 인스턴스를 구매할 수 있다. 대부분 우분투가 깔린 linux 컴퓨터를 빌려 사용한다.

접속 및 배포 방법

접속에는 여러가지 방법이 있는데, ssh를 이용하여 접속하는 것이 가장 간단하다. 대충 ec2 인스턴스의 쉘에 원격으로 접속하여 커맨드라인으로 인스턴스를 조작한다고 생각하면 된다. 수동으로 무언가를 배포하고 싶으면, 가장 쉬운 방법은 아래와 같다.

주의사항

CodeDeploy

aws 내에서 CI/CD에 필요한 연산 작업을 서버리스하게 구동시켜주는 서비스이다. ec2에 파일을 배포할 때는 s3와 cf를 이용하여 정적배포할 때와는 달리, 파일을 최신화하고 끊김 없이 다시 구동시키는 작업을 수동으로 해주어야 한다. CodeDeploy는 이 수동 작업을 일종의 매크로처럼 자동으로 실행시킬 수 있도록 한다.

CodeDeploy 구축 예시

  1. ec2 인스턴스 만들기
  2. ec2 인스턴스가 CodeDeploy에 접근할 수 있도록 IAM 권한 생성하여 붙이기
  3. ec2 내에 CodeDeploy용 agent 설치
  4. 배포할 코드를 업로드할 s3 버킷 생성
  5. CodeDeploy 애플리케이션 생성하고 사용할 ec2와 s3에 대한 권한 연결
  6. CodeDeploy의 동작을 결정하는 appspec 스크립트 작성
  7. 코드를 s3에 업로드하는 github actions 스크립트 작성

이렇게 구현하면, github actions에 의해 s3에 업로드된 코드는 CodeDeploy 애플리케이션에 의해 ec2로 이동하고, 스크립트에 의해 자동으로 재배포될 것이다.

주의사항

Express.js

express는 노드로 서버를 구축할 때 가장 많이 사용하는 서비스로, 구현이 너무나도 단순하고 어떤 제약도 없는 것이 특징이다. 대충 보면, app이라는 객체를 하나 만든 후 특정 포트 번호로 listen 시키면, 해당 포트로 요청이 들어올 때마다 적절한 콜백 함수를 구동시킨다.

const app = express();

app.get("/", (_, res) => {
  res.send("Xction Server!");
});

app.listen(process.env.PORT, () => {
  console.log(
    `[server]: Server is running at http://localhost:${process.env.PORT}`,
  );
});

적절한 콜백 함수는 어떻게 등록하는가? app.METHOD(경로, 콜백함수)를 통해 등록하면 된다. express가 요청이 들어온 경로와 HTTP 메서드를 파악하여 해당하는 콜백이 있으면 작동시킬 것이다. 콜백은 req와 res를 인자로 받으며, 각각 요청에서 전달받은 정보와 응답에 실어보낼 정보를 명시할 때 사용한다.

express의 특징

배포는 어떻게?

express는 자체적으로 지원하는 배포 방식이 딱히 없기 때문에, 임의로 배포를 구성해보았다. package.json을 보자.

{
  ...전략...
  "scripts": {
      "build": "tsc && tsc-alias",
      "dev": "tsc && (concurrently \"tsc -w\" \"tsc-alias -w\" \"nodemon -q dist/server/src/index.js\")"   
  },
  ...후략...
}

두 개의 명령어가 있다. build는 타입스크립트 코드를 컴파일하는 명령어이다. tsconfig.json을 보면, dist라는 폴더에 컴파일되고 있음을 알 수 있다. dev 명령어는 파일을 서버를 dev 모드로 구동시키는 명령어이다. 일단 tsc -w와 tsc-alias -w를 이용하여 배포 파일을 생성한 후, nodemon을 통해 배포 파일 내부의 index.js를 구동시킨다. nodemon을 이용한 이유는 구성 파일이 변경될 때마다 서버를 재시작하여, 변경 사항이 바로 반영되도록 하기 위함이다. 지금 명령어를 이용하여 그대로 ec2 배포를 하는 것도 가능은 하겠다만, 아래와 같은 개선이 필요한 상황이다.

Next.js

Next.js는 알다시피 동적+정적이 섞인 하이브리드 배포 방식을 지원하는 라이브러리이다. 뭐 까보면 동적 배포(SSR)이긴 한데, 좀 더 효율적이다에 가깝다. 원래 우리는 정적배포 방식으로 NextJS를 사용했는데, NextJS를 제대로 사용하려면 따라서 동적 배포를 해야 한다. 개발에 사용하는 next dev 명령어 말고 next buildnext start라는 명령어가 있다. 전자는 .next 폴더에 배포서버 파일을 생성하고 후자는 이 서버를 구동시킨다. 배포 서버는 접속 요청이 올 때마다 알아서 적절한 html 파일을 만들어 반환한다.

주의사항

nuagenic commented 7 months ago

[질문들] (업데이트 예정)

  1. 저희 서버의 package.jsonexpress는 dependencies로 추가가 되어 있지 않은데, 어떤 방식으로 돌아가는 건가요? (+ yarn server installyarn server dev 시도해 봄 -> DB 연결 이슈 있는듯 -> 관련 코드 주석 처리하니 서버 작동 확인)
  2. 서버의 index.ts 살펴보면 process.env.PORT 사용하고 있는데, 로컬에서 테스트 시에는 env 따로 설정해 주나요? 또는 디폴트가 있나요?
  3. ec2 인스턴스에 node나 git 등을 설치하는 건 한 명이 하면 되는건지? 아니면 각 ssh로 접속했을 때마다 개별적으로 해주는 것인지?
  4. yarn server install로 dependencies 다운 받았는데, 왜 dotenv는 모듈로 인식을 못하는지?
  5. 현재 EC2 인스턴스가 한 개인데, 이건 클라이언트 건지? 서버용은 따로 만들어야 하는지?
  6. 현재 EC2 인스턴스가 SSH 키페어 없이 만들어진 거 같은데, 서치해보니 이러면 인스턴스 접속이 불가하다는데... 어떻게 하는지? 일단 SSH 키페어는 만들어 두었습니다
nuagenic commented 6 months ago

0311 질문들

  1. yarn buildnext build 차이