어드민 모노레포 적용기 - (2)

s2ksh77·2024년 6월 11일
1

monorepo

목록 보기
2/2
post-thumbnail

들어가기에 앞서 적용기 (1)에 이어서 모노레포 구조 변경과 CI 작업을 수행하게 되면서 회사 본부에 공유하게 된 내용을 작성하였습니다.

어드민 모노레포를 수행하면서 변경된 구조에 대한 공유와 더불어 최근 여러 사업건들의 배포들로 인한 CI 환경을 구성한 부분을 공유하고자 가이드 작성합니다.

변경된 구조

업로드중..
  1. packages 하위 tsconfig 패키지 추가
    • 전역적으로 사용할 base.json 위치 이동
    • 빌드 파일을 수행하게 될 dts.json 추가
    • @admin/tsconfig 와 같은 공통 패키지로 변경
  2. scripts 폴더 구성
    • boilerplate 폴더 추가
      • monorepo 패키지가 추가 되었을 경우 명령어를 통해 기존 구성된 환경과 동일한 환경 생성하도록 함
      • pnpm bp create [생성할폴더명][생성할package.json이름]
    • ci 폴더 추가
      • wsClient.js
        • 프론트 파일을 빌드하고 빌드된 결과물을 appStore의 ws을 통해 업로드 하던 부분을 위한 코드
        • 기본적으로 ci 스크립트에서 appId와 version 및 bussiness_name을 받도록 함
          • bussiness_name에 따라 WS_URL 및 UPLOAD_SERVICE_NAME 변경
        • ws.onmessage 를 통해 전달받은 ArrayBuffer를 JSON 객체로 변환하여 header와 body를 통해 전달받은 메세지 확인 가능
    • cli 폴더 추가
      • cli 폴더 추가 필요성
        • 매번 명령어가 생길 때 마다 (build, deploy, tsc 등) package.json에 스크립트를 추가해야 하는 번거로움
      • 파일 구성 ( index.mjs, dev.mjs, build.mjs, ci.mjs )
      • 각 파일들은 해당 명령어 동작을 수행 pnpm —filter [appName][파일명(dev, build,]

gitlab-ci.yml

  • 회사에서 사용하는 platform인 쉘이 아닌 앱이기 때문에 기존에 쉘에서의 ci와는 다르게 harbor나 gitops에 올리는 부분은 제외 되어 있습니다. 참고 부탁드립니다.
  • 앱의 입장에서 아파치 배포 / appStore에 배포 / 쉘의 foodist 브랜치 배포로 구성되어 있습니다.
# 어드민 모노레포 gitlab-ci 파이프라인
# 환경 별 어드민 빌드하고 AppStore의 서비스에 등록한다.
# Author: soohyun

# 시나리오
# Stage 1: build
#   - 배포를 위해 빌드하고 dist 파일을 zip 파일로 저장한다.

# Stage 2: deploy
#   - IS_DEPLOY 일 경우 apache에 해당 zip 파일을 배포한다.
#   - IS_DEPLOY 이면서 BUSINESS_NAME이 foodist인 경우 shell의 fix/admin-module 브랜치로 커밋 & 푸시한다.
#   - IS_DEPLOY 가 없을 경우 앱스토어에 해당 zip 파일 요청하여 실환경에 반영한다.

image: docker:23.0.0-rc.1-cli-alpine3.17

stages:
  - build
  - deploy

build:
  stage: build
  variables:
    VERSION_NAME: ''
  before_script:
    - apk upgrade
    - apk update
    - apk add nodejs npm zip
    - apk add --no-cache bash
  script:
    - npm install -g pnpm@8.15.8
    - pnpm install
    - BUSINESS_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $1}')
    - echo 'BUSINESS_NAME :::' ${BUSINESS_NAME}
    - APP_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $2}')
    - echo 'APP_NAME :::' ${APP_NAME}
    - VERSION_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $3}')
    - echo 'VERSION_NAME :::' ${VERSION_NAME}
    - |
      if [ "${BUSINESS_NAME}" != "ekr" ] && [ "${BUSINESS_NAME}" != "superApp" ] && [ "${BUSINESS_NAME}" != "foodist" ]; then
        echo "BUSINESS_NAME is ekr or superApp or foodist"
        exit 1
      fi
    - |
      if [ -z "${VERSION_NAME}" ]; then
        echo "VERSION_NAME is Required"
        exit 1
      fi
    - |
      case "${APP_NAME}" in
        appmaster)
          APP_NAME="app-master"
          ;;
        admin)
          APP_NAME="${BUSINESS_NAME}-admin"
          ;;
        *)
          echo "Invalid APP_NAME"
          exit 1
          ;;
      esac
      echo 'Modified APP_NAME :::' ${APP_NAME}
    - pnpm ${APP_NAME}_ci ${VERSION_NAME}
    - cd apps/${APP_NAME}/build
    - zip -r ../../../target.zip .
    - cd ../../../
    - ls -la
  artifacts:
    paths:
      - target.zip
    expire_in: 1 hour
  only:
    - tags
  tags:
    - pl2-2
deploy:
  stage: deploy
  variables:
    APACHE_IP: 220.90.208.23
    APACHE_ID: ***
    APACHE_PW: ***
  before_script:
    - apk update
    - apk add nodejs npm sshpass unzip git
    - apk add --no-cache bash
    - npm install -g pnpm@8.15.8
    - pnpm install
    - export BUSINESS_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $1}')
    - export APP_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $2}')
    - export VERSION_NAME=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $3}')
    - export DEPLOY_PATH=/usr/local/apache2/htdocs/${BUSINESS_NAME}/wapl-${APP_NAME}-front
    - export IS_DEPLOY=$(echo ${CI_COMMIT_TAG} | awk -F"_" '{print $4}')
    - export USER_NAME=***
    - export PRIVATE_ACCESS_TOKEN=***
  script:
    - echo 'BUSINESS_NAME :::' ${BUSINESS_NAME} 'APACHE_IP :::' ${APACHE_IP}
    - echo 'VERSION_NAME :::' ${VERSION_NAME} 'APACHE_PW :::' ${APACHE_PW}
    - echo 'DEPLOY_PATH :::' ${DEPLOY_PATH} 'IS_DEPLOY :::' ${IS_DEPLOY}
    - |
      if [ "${BUSINESS_NAME}" != "foodist" ] && [ "$IS_DEPLOY" = "deploy" ]; then
        ls -al
        echo "Uploading target.zip to ${DEPLOY_PATH}/${BUSINESS_NAME}-wapl-${APP_NAME}-web-${VERSION_NAME}.zip"
        sshpass -p ${APACHE_PW} ssh -o StrictHostKeyChecking=no ${APACHE_ID}@${APACHE_IP} "cd ${DEPLOY_PATH}; echo 'Current directory:'; pwd"
        sshpass -p ${APACHE_PW} scp -o StrictHostKeyChecking=no target.zip ${APACHE_ID}@${APACHE_IP}:${DEPLOY_PATH}/${BUSINESS_NAME}-wapl-${APP_NAME}-web-${VERSION_NAME}.zip
        echo 'Apache Deploy Finished'
      # 푸디스트와 같은 경우는 쉘git에 새로운 branch만들어서 커밋하고 푸시합니다.
      elif [ "${BUSINESS_NAME}" = "foodist" ] && [ "$IS_DEPLOY" = "deploy" ]; then
        ls -al
        echo "wapl-shell.git clone....."
        git clone http://${USER_NAME}:${PRIVATE_ACCESS_TOKEN}@192.168.158.11/wapl/wapl-shell.git temp_shell
        cd temp_shell
        echo "cd temp_shell"
        git checkout develop_foodist
        rm -rf ./public/apps/admin/*
        ls -al
        unzip ../target.zip -d ./public/apps/admin

				# 각 환경 runner 돌릴 담당자의 username 키와 private_access_token 값 넣어주시면 됩니다.
        git config user.email "***"
        git config user.name "${USER_NAME}"

        git branch -D fix/admin-module || true
        git checkout -b fix/admin-module
        git add .
        git commit -m "fix: admin modules fixed"
        git push http://${USER_NAME}:${PRIVATE_ACCESS_TOKEN}@192.168.158.11/wapl/wapl-shell.git fix/admin-module
        ls -al
        echo 'Foodist Admin Deploy Finished'
      else
        ls -al
        echo "Starting WebSocket operation"
        node scripts/ci/wsClient.js 3 ${VERSION_NAME} ${BUSINESS_NAME}
        echo "Finishing WebSocket AppStore Uploaded"
      fi
  dependencies:
    - build
  only:
    - tags
  tags:
    - pl2-2

파이프라인 개요

이 가이드는 모노레포 환경 별 어드민을 빌드하고 AppStore의 서비스에 등록하거나 실환경에 배포하는 등의 자동화 파이프라인을 포함하고 있습니다.

Stage 1: Build

  • 배포를 위해 빌드하고 각 모노레포 환경의 build 결과 파일을 target.zip 파일로 저장합니다.

Stage 2: Deploy

  • IS_DEPLOYdeploy일 경우 Apache에 해당 zip 파일을 배포합니다.
  • IS_DEPLOYdeploy 이면서 BUSINESS_NAME이 foodist인 경우 shell의 fix/admin-module 브랜치로 커밋 & 푸시합니다.
  • IS_DEPLOY가 없을 경우 WebSocket을 통해 해당 zip 파일을 앱스토어에 업로드 하고 실환경에 반영합니다.

파이프라인 실행 조건

  • 빌드 단계는 태그가 생성될 때마다 실행됩니다.
  • 배포 단계는 빌드가 성공적으로 완료된 후에 실행됩니다.

환경 변수 설정

Apache 서버 정보

  • APACHE_IP: Apache 서버의 IP 주소
  • APACHE_ID: Apache 서버에 접속할 사용자 ID
  • APACHE_PW: Apache 서버에 접속할 사용자 비밀번호

자동 설정 변수

  • BUSINESS_NAME: 태그에서 추출한 비즈니스 이름
  • APP_NAME: 태그에서 추출한 애플리케이션 이름
  • VERSION_NAME: 태그에서 추출한 버전 이름
  • IS_DEPLOY: 태그에서 추출한 배포 여부 변수

태그 네이밍 규칙

태그는 ${BUSINESS_NAME}_${APP_NAME}_${VERSION_NAME}_${IS_DEPLOY} 형식을 따라야 합니다. 각 필드는 다음과 같은 의미를 가집니다:

  • BUSINESS_NAME: 비즈니스 이름 (예: superApp, ekr, foodist)
  • APP_NAME: 애플리케이션 이름 (예: appmaster, ekr, foodist)
  • VERSION_NAME: 버전 이름 (예: 실환경(앱스토어): v1.0.0, 아파치: 1.0.0)
    • v를 붙이는건 앱스토어 안 붙이는건 아파치
  • IS_DEPLOY: 배포 여부
    • IS_DEPLOY 같은 경우 생략 가능합니다. 생략일 경우 실환경에 반영

예시

  • ekr_admin_1.0.0
  • foodist_admin_v1.0.0_deploy
  • appmaster_1.0.0_deploy

빌드 단계

  1. 필요한 패키지 설치 (nodejs, npm, zip, bash)
  2. pnpm 설치 및 의존성 설치
  3. 태그에서 BUSINESS_NAME, APP_NAME, VERSION_NAME 추출
  4. 어플리케이션 이름 수정 (appmaster -> app-master, admin -> ${BUSINESS_NAME}-admin)
  5. pnpm ${APP_NAME}_ci 명령어 실행
  6. 어드민 빌드 스크립트 실행 및 결과물을 target.zip으로 압축

배포 단계

  1. 필요한 패키지 설치 (nodejs, npm, sshpass, unzip, git, bash)
  2. 태그에서 BUSINESS_NAME, APP_NAME, VERSION_NAME, IS_DEPLOY 추출
  3. IS_DEPLOYdeploy일 경우 Apache 서버에 target.zip${BUSINESS_NAME}-wapl-${APP_NAME}-web-${VERSION_NAME}.zip 명으로 업로드
  4. BUSINESS_NAMEfoodist이고 IS_DEPLOYdeploy일 경우 쉘의 git에 fix/admin-module 브랜치 생성 후 변경 사항 커밋 및 푸시
  5. IS_DEPLOY가 없는 경우 앱스토어에 웹소켓을 통해 zip 파일 요청 및 실환경 반영

마치며

2024년 상반기동안 여러 사업 건을 대응하면서 정신없는 와중에 진행해보고 싶었던 모노레포 구조를 적용해보고 팀원들에게 개발성을 향상 시켜주게되면서 뿌듯하기도 하고 조금 더 깊게 알게 된 것 같았습니다. 아직은 미흡하고 좀 더 개선해나가야 할 부분들이 있지만, 추가적으로 변경된 부분들은 작성할 예정입니다. 혹여나 이 글을 보게 될 분들께 조금이나마 도움이 되었으면 합니다. 감사합니다.

profile
오너십을 가지고 끊임없이 더 나은 방향을 고민하는 개발자 입니다. 새로운 기술을 적용하고 배우는 것을 좋아합니다.

0개의 댓글