무스마 백엔드 살펴보기 - 개발환경 세팅부터 배포까지 (1/2)
민경환 사원
무스마 백엔드 살펴보기 - 개발환경 세팅부터 배포까지 (1/2)
글을 쓴 이유
저는 대학생 때부터 무스마에 입사하기 전까지 Java를 이용하여 주로 개발을 하였고 당연히 Java/Spring을 이용하는 회사에 입사를 할 줄 알았습니다. 감사하게도 언어는 도구일 뿐이라며 무스마 SW 개발팀에서 입사 제안을 해주셨고 작년 12월부터 Node.js 백엔드 개발자로 입사하여 재미있는 회사 생활을 하고 있습니다.
입사 이틀째에 설레는 마음으로 프로젝트를 VSCode에 불러왔을 때 정말 당황했었는데요. 자바스크립트와 Node.js 관련 지식이 전무했던 저는 수많은 파일 및 옵션들이 무엇을 의미하는지 정말 하~나도 몰랐고 개발부터 배포까지 어떻게 진행되는지 이해가 잘 가지 않았기 때문입니다.
그래서
- Typescript, Node.js에 익숙하지 않고
- 수많은 파일이 뭔지 하나도 모르겠지만
- 대충이라도 알고 싶고 실습도 좀 해보고 싶어!
하는 분들 및 앞으로 합류하실 무스마 크루의 개발자분들을 위하여 작성하게 되었습니다.
모든 코드는 GitHub에서 확인하실 수 있습니다.
개발환경 세팅
패키지 설치
NVM 설치
NVM을 이용하면 Node.js 설치하면 다양한 버전을 설치 및 변경이 가능해집니다.
brew install nvm
mkdir ~/.nvm
vi ~/.zhrc # 아래 내용을 붙여넣는다.
export NVM_DIR="$HOME/.nvm"
[ -s "/usr/local/opt/nvm/nvm.sh" ] && . "/usr/local/opt/nvm/nvm.sh" # This loads nvm
[ -s "/usr/local/opt/nvm/etc/bash_completion.d/nvm" ] && . "/usr/local/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion
zsh compinit: insecure directories, run compaudit for list. 에러가 발생할 경우,
cd /usr/local/share/zsh
sudo chmod -R 755 ./site-functions
Node.js(LTS) 설치
nvm install 14 # LTS 버전 확인 후 입력
Yarn 설치
무스마에서는 Node Package Manager로 yarn을 사용하고 있습니다.
그 이유는
- 패키지 다운로드 속도가 더 빠르고
- 기능이 더 많기 때문에 (workspaces 등)
npm install -g yarn
Nest CLI 설치
Nest는 타입스크립트로 빌드되는 Node.js 서버측 애플리케이션을 구축하기 위한 프레임워크입니다.
CLI를 이용하여 빠르고 간편하게 프로젝트를 생성할 수 있으므로 설치합니다.
npm install -g @nestjs/cli
패키지 매니저로
yarn을 쓰면서 왜npm으로 다운로드 하냐? 라는 의문을 가지실 수 있는데요.
yarn을 이용하여@nestjs/cli를 글로벌로 다운로드 하면 생기는 이슈가 있습니다.
VS Code 설치
Jetbrains사의 IntelliJ, Webstrom 등을 사용하셔도 상관 없으나 무스마에서는 VS Code를 적극적으로 사용하고 있습니다.
brew install --cask visual-studio-code
Docker 설치
brew install --cask docker
Kubectl 설치
brew install kubectl
minikube 설치
무스마 SW 개발팀은 워크로드를 Amazon EKS에 배포합니다.
듀토리얼을 EKS로 진행하기에는 비용적으로, 시간적으로 무리가 있으므로 쿠버네티스를 로컬에서 실행할 수 있도록 minikube를 설치합니다.
brew install minikube
Lens 설치
쿠버네티스 클러스터를 제어하는 데 필요한 유일한 IDE입니다.
minikube가 기본적으로 제공해주는 대시보드가 있으나 더 아름답고 사용하기 편하기에 사용하고있습니다.
brew install --cask lens
GitHub Personal access tokens 발급받기
여기를 참고하여 wrtie:packages 스코프를 선택한 뒤 Personal access tokens을 발급받습니다.
토큰은 도커 이미지 푸시, 풀을 위해 사용하는 GitHub Container Registry와 무스마에서 생성한 eslint를 다운받을 때 사용됩니다.
발급받은 토큰을 ~/.npmrc에 저장합니다.
vi ~/.npmrc
//npm.pkg.github.com/:_authToken=[GITHUB_PERSONAL_ACCESS_TOKENS]
VSCode Extension 설치
Extensions: Marketplace 에서 Extension을 설치합니다.
ESLint

Prettier - Code formatter

프로젝트 생성
개발환경 세팅이 끝났으니 본격적으로 시작해보겠습니다.
musma 프로젝트 생성
Nest CLI를 이용하여 musma 프로젝트를 생성합니다.
package manager로
yarn을 선택해주세요.
nest new musma
musma-eslint 적용
.npmrc 생성
프로젝트 루트에 .npmrc 파일을 생성합니다.
echo @musma:registry=https://npm.pkg.github.com/ >> .npmrc
.npmrc 파일을 생성함으로써 pakcage.json 파일의 dependency에 @musma/eslint-config와 같이 @musma가 붙는 패키지가 있다면,
공식 npm 저장소 http://registry.npmjs.org/ 대신에 GitHub Package Registry https://npm.pkg.github.com/에서 찾아서 다운로드 하게 됩니다.
읽어보세요
[무스마 기술블로그] GitHub 패키지 저장소 호스팅: GitHub Package Registry 시작하기
.eslintrc.js 변경 및 .prettierrc 제거
Nest CLI를 이용하면 ESLint를 구성할 수 있는 설정파일인 .eslintrc.js와 코드를 정렬해주는 Code Formatter인 Prettier를 적용할 수 있는 .prettierrc 파일을 생성해주는데요.
.prettierrc 설정은 다운로드 할 @musma/eslint-config에 들어있기 때문에 제거합니다.
rm .prettierrc
.eslintrc.js는 아래와 같이 변경합니다.
module.exports = {
extends: [
'@musma',
],
}
@musma/eslint-config에 적용된 세부적인 룰이 궁금하시다면 여기에서 확인하실 수 있습니다.
.eslintignore 생성
.eslintignore 파일을 생성하여 규칙 적용이 필요없는 node_modules, *.js, *d.ts 파일을 제외합니다.
vi .eslintignore
**/node_modules
**/*.js
**/*.d.ts
@musma/eslint-config 적용
@musma/eslint-config를 설치합니다.
yarn add @musma/eslint-config --dev --update-checksums
필요없는 의존성 제거
아래의 의존성들은 musma/eslint-config에 설치되어 있으므로 제거합니다.
- @typescript-eslint/eslint-plugin
- @typescript-eslint/parser
- eslint
- eslint-config-prettier
- eslint-plugin-prettier
- prettier
yarn remove @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier eslint-plugin-prettier prettier
settings.json 생성
save 버튼을 누를 때 마다 prettier가 적용되도록 settings.json을 생성합니다.
mkdir .vscode
vi .vscode/settings.json
{
"eslint.codeAction.showDocumentation": {
"enable": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.formatOnSave": true,
"eslint.alwaysShowStatus": true
}
ESLint, Prettier 테스트
접근제한자가 없으므로 app.controller.ts 파일이 오류가 나고 있을텐데요.
getHello 함수에 public을 붙여주고 저장을 눌러서 아래와 같이 변경되면 성공입니다.
import { Controller, Get } from '@nestjs/common'
import { AppService } from './app.service'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
public getHello(): string {
return this.appService.getHello()
}
}
Hello World! 테스트
서버를 실행시켜 Hello World!를 정상적으로 응답하는지 테스트해보겠습니다.
yarn start
curl -XGET 'localhost:3000'
Hello World!
기능 구현
app.controller.ts에 간단한 GET, POST 요청을 구현해보겠습니다.
타입 생성
파라미터 및 반환값으로 사용할 타입 두 가지를 생성합니다.
type ResponseType = {
type: string
data: string
}
type PostBodyType = {
data: string
}
GET
GET 요청에 대한 getRequest 함수를 구현합니다.
@Get('/get')
public getRequest(): ResponseType {
return { type: 'GET', data: 'getResponse' }
}
POST
POST 요청에 대한 postRequest 함수를 구현합니다.
@Post('/post')
public postRequest(@Body() body: PostBodyType): ResponseType {
return { type: 'POST', data: body.data }
}
테스트
생성한 GET, POST 요청을 테스트해보겠습니다.
yarn start
GET
curl -XGET 'localhost:3000/get'
{"type":"GET","data":"getResponse"}
POST
curl -XPOST -H "Content-type: application/json" -d '{ "data": "postResponse" }' 'localhost:3000/post'
{"type":"POST","data":"postResponse"}
바이너리 생성
무스마 개발팀에서는 Node.js 프로젝트를 도커 이미지로 생성하여 배포하고있습니다.
Node.js 프로젝트를 도커 이미지로 생성할 때는 크게 두 가지가 있는데요.
node이미지를 사용하는 방법- 실행 파일을 만들어 리눅스 이미지에서 실행하는 방법
두 가지 방법 모두 장단점이 있지만 1번의 방법보다 2번의 방법이 배포할 때 쉽고 간편하므로 2번을 이용하고 있습니다.
이번에는 pkg를 이용하여 Node.js 프로젝트를 하나의 바이너리로 생성하는 방법을 알아보겠습니다.
pkg 설치
pkg를 devDependencies에 설치합니다.
yarn add pkg --dev
엔트리 포인트 등록
pkg를 이용하여 하나의 바이너리를 만들기 위해서는 엔트리 포인트 지정이 필요한데요.
이는 pakcage.json의 bin 프로퍼티를 이용하여 지정이 가능합니다.
해당 내용은 pkg에서 확인하실 수 있습니다.
package.json에 아래와 같이 bin 프로퍼티와 엔트리 포인트를 추가합니다.
{
...
"bin": "dist/main.js",
...
}
스크립트 등록
pkg 명령어를 이용하기 위한 스크립트를 생성합니다.
현재 macOS-Bigsur 환경에서 실습을 진행하고 있으므로 이에 맞춰서 패키징을 해보겠습니다.
target 옵션에 대한 자세한 내용은 여기에서 확인하실 수 있습니다.
{
"scripts": {
"build:pkg": "pkg . --target node14-macos-x64 --output dist/server",
}
바이너리 생성
등록한 스크립트를 이용하여 바이너리를 생성합니다.
yarn build:pkg
$ pkg . --target node14-macos-x64 --output dist/server
> pkg@5.3.3
> Fetching base Node.js binaries to PKG_CACHE_PATH
fetched-v14.17.6-macos-x64 [====================] 100%
✨ Done in 17.26s.
결과는 output 옵션에 적힌대로 dist 폴더에 server라는 이름으로 생성됩니다.
ls dist
app.controller.d.ts app.service.js
app.controller.js app.service.js.map
app.controller.js.map main.d.ts
app.module.d.ts main.js
app.module.js main.js.map
app.module.js.map server
app.service.d.ts tsconfig.build.tsbuildinfo
바이너리 실행 및 테스트
생성된 바이너를 실행해보겠습니다.
dist/server
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [NestFactory] Starting Nest application...
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [InstanceLoader] AppModule dependencies initialized +14ms
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [RoutesResolver] AppController {/}: +1ms
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [RouterExplorer] Mapped {/get, GET} route +1ms
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [RouterExplorer] Mapped {/post, POST} route +0ms
[Nest] 11971 - 2021-10-07 2:40:37 PM LOG [NestApplication] Nest application successfully started +1ms
GET 요청을 테스트해보겠습니다.
curl -XGET 'localhost:3000/get'
{"type":"GET","data":"getResponse"}
정상적으로 작동하는 것을 확인할 수 있습니다.
정리
프로젝트를 배포하기 전 까지의 과정을 알아보았습니다.
- 패키지 설치
- 프로젝트 생성
- 기능 구현
- 바이너리 생성
이 다음 시간에는 도커 이미지 생성 ~ 프로젝트 배포까지 다루어보겠습니다.
무스마 백엔드 살펴보기 - 개발환경 세팅부터 배포까지 (2/2) 편에서 계속됩니다.