본문 바로가기
러너스하이

토스 러너스하이 (4) - 피드백

by 아마도개발자 2026. 1. 14.
반응형

 

지금까지 개발했던 것들을 실무에 적용하기 전에, 팀원들에게 배포자동화를 개발하게 된 개요 설명과 시연을 해보았다. 

 

2026.01.13 - [분류 전체보기] - 토스 러너스하이 (3) - 구현

 

토스 러너스하이 (3) - 구현

배포 자동화를 위해 Gitea 서버를 호스팅하고, act_runner를 설정한 뒤 개발 PC 환경에서부터 IIS 배포까지의 과정을 검증할 수 있는 프로토타입을 구현했었다. 2026.01.06 - [분류 전체보기] - 토스 러너

maybe-developer.tistory.com

 

1. 개요 설명 및 시연

우리 서비스의 운영 서버에 접근할 수 있는 권한은 나 밖에 없었기 때문에 배포와 서버 운영에 대한 부분이 암묵적으로 내게 일임되어 있었다. 그래서 간단한 테스트를 할 때도 나는 VDI와 서버를 넘나들며 시간을 낭비했어야 했다는 점을 설명했다. 이후, 말로는 설명이 부족할 것 같아 간단한 기능을 추가하고, 배포하는 전체 액티비티를 보여주었다.

 

그 다음은 파이프라인을 활성화하여 코드 push를 하고 잠깐 아키텍처를 설명하는 사이에, 배포가 완료되는 것을 보여주었다.

 

반응은 생각보다 더 긍정적이었다. 사실 그동안 내가 배포를 그 동안 도맡아 해주었기 때문에 팀원들은 크게 관심이 없을 것이라 생각했는데, 간단한 수정을 했을 때 나를 거치지 않아도 되고 이에 필요한 액티비티가 굉장히 작았기 때문에 편리성이 높다는 평가를 받았다.

 

우리 회사처럼 강성 제조업의 개발 문화에서는 DevOps나 백오피스 관련 개발에 별도로 시수를 투자하는 문화가 없고, 그 개념이나 필요성에 대한 이해도 낮은 편이기에 걱정이 많았는데, 첫 시도를 했다는 점에 뿌듯함이 있었다.

 

2. 피드백

설명을 마치고 몇 가지 피드백과 아이디어를 받을 수 있었다. 

 

첫 번째 피드백은 실무에 바로 적용했을 때 문제가 발생하거나 사이드 이펙트가 발생할 여지가 없는지 였다. 이 부분에 대해서는 기본적으로 배포되는 기능단위에 영향을 미치는 시스템이 아니고, 브랜치 마다 책임이 나뉘어져 있기 때문에 내부 컨벤션을 정의하여 지키기만 하면 문제가 없다고 설명했다. 하지만, 파이프라인 자체가 에러가 발생할 수도 있다는 지적은 수용할 수 밖에 없었다.

 

이외에도 보안규정 준수 체크, 파이프라인의 모듈화 등의 아이디어를 받을 수 있었고, 그 중 가장 필요한 아이템을 2가지를 선정했다.

 

1. 파이프라인 과정 중 실패 혹은 에러가 발생했을 때 알림 기능

2. 빌드 전 테스트에 대한 결과를 GitLab에 접속하지 않고 바로 확인할 수 있는 기능

 

3. 수용

우선 아키텍처에 위 추가기능들을 넣어 다시 그림을 그려보았다.

 

 

사내에서 Slack, Discord 등은 사용할 수가 없었고 유일하게 Notification으로 사용할 수 있는 것이 사내 메일이었다.

Test가 실패하거나 파이프라인에서 에러가 발생하는 경우 사내 메일 서비스로 메일을 보내줄 수 있도록 설계했다.

 

Test결과 파악은 원래 GitLab CI/CD 웹에서 제공하는 기능을 사용하려고 했다. GitLab에서는 테스트 결과에대한 리포트를 Test탭에서 별도로 확인할 수 있기 때문에 리포트 파일만 산출하면 쉽게 결과 조회가 가능할 것이라 생각했다.

하지만 우리 GitLab사내망은 11.6 버전으로 심각한 구형버전이었고, Test탭은 사용할 수가 없었다.

 

그래서 .NET에서 제공하는(프로젝트가 .NET9 사용중) ReportGenerator라는 테스트 결과 시각화 패키지를 사용하기로 했다.

테스트 결과를 xml로 산출하여 ReportGenerator를 통해 html로 변환한 뒤, IIS에 자동배포하도록 구성을 할 예정이다.

Artifacts에 넣어 Gitlab 에서 다운받아 확인할 수도 있지만, 웹으로 바로 배포하는 것이 접근성이 훨씬 뛰어날 것이라 생각했다.

 

// ci.yml   *변수 및 주소는 모두 가명처리 한 뒤, LLM에서 다시 더미로 변환. 최종 개발 후 남은 변수도 모두 Variable로 변경 예정

stages:
  - test
  - build
  - deploy
  - notify

variables:
  DOTNET_CLI_TELEMETRY_OUTPUT: "1"
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1"
  BUILD_CONFIGURATION: "Release"

test_project:
  stage: test
  tags:
    - windows
  allow_failure: false
  script:
    - echo "=== [TEST] 단위 테스트 시작 ==="
    - dotnet restore
    - dotnet build --configuration $BUILD_CONFIGURATION
    - echo "=== [테스트 실행 + 커버리지 수집] ==="
    - dotnet test --configuration $BUILD_CONFIGURATION --logger "trx;LogFileName=test_results.trx" --collect:"XPlat Code Coverage"
    - echo "=== [ReportGenerator 설치 및 커버리지 리포트 생성] ==="
    - dotnet tool install -g dotnet-reportgenerator-globaltool
    - $env:PATH += ";$env:USERPROFILE\.dotnet\tools"
    - New-Item -ItemType Directory -Force -Path "$env:CI_PROJECT_DIR\TestReport" | Out-Null
    - reportgenerator "-reports:tests/Application.UnitTests/TestResults/**/*.xml" "-targetdir:$env:CI_PROJECT_DIR\TestReport"
    - echo "=== [HTML 리포트 생성 완료] ==="
    - |
      if (Test-Path Z:\) {
          Write-Host "Z: drive is already mapped. Skipping connection."
      } else {
          Write-Host "Z: drive not mapped. Attempting to connect to $env:NAS_PATH..."
          & net use Z: "$env:NAS_PATH" /user:"$env:NAS_USER" "$env:NAS_PASS"
      }

      New-Item -ItemType Directory -Force -Path "Z:\TestReport" | Out-Null
      Copy-Item -Recurse -Force "$env:CI_PROJECT_DIR\TestReport\*" "Z:\TestReport"
  artifacts:
    when: always
    paths:
      - tests/Application.UnitTests/TestResults/
      - TestReport/
    expire_in: 7 days
  after_script:
    - echo "✅ 테스트 및 커버리지 리포트 완료"

build_project:
  stage: build
  tags:
    - windows
  script:
    - echo "=== [BUILD] 프로젝트 빌드 시작 ==="
    - dotnet restore
    - dotnet build ./src/API/API.csproj --configuration $BUILD_CONFIGURATION    
    - echo "=== [NAS 연결 시도] ==="
    - |
      if (Test-Path Z:\) {
          Write-Host "Z: drive is already mapped. Skipping connection."
      } else {
          Write-Host "Z: drive not mapped. Attempting to connect to $env:NAS_PATH..."
          & net use Z: "$env:NAS_PATH" /user:"$env:NAS_USER" "$env:NAS_PASS"
      }
    - echo "=== [PUBLISH → NAS 업로드] ==="
    - dotnet publish ./src/API/API.csproj -c $BUILD_CONFIGURATION -o "$env:NAS_PATH\publish"
    - net use Z: /delete /y
  dependencies:
    - test_project

deploy_build:
  stage: deploy
  tags:
    - windows
  dependencies:
    - build_project
  script: |
    $ErrorActionPreference = "Stop"

    # --- 1. WinRM 자격 증명 생성 ---
    $SecureString = $env:REMOTE_SERVER_PASS | ConvertTo-SecureString -AsPlainText -Force
    $WinRMCredential = New-Object System.Management.Automation.PSCredential($env:REMOTE_SERVER_USER, $SecureString)

    Write-Host "=== [DEPLOY] 원격 IIS 배포 시작 ==="

    # --- 2. 원격 IIS 서버 명령 실행 ---
    Invoke-Command -ComputerName $env:REMOTE_IIS_SERVER_IP -Credential $WinRMCredential -Authentication CredSSP -ScriptBlock {
      param(
        [string]$iisAppPoolName,
        [string]$iisPhysicalPath,
        [string]$nasPath,
        [string]$nasUser,
        [string]$nasPass,
        [string]$iisReportPath
      )

      $ErrorActionPreference = "Stop"
      Import-Module WebAdministration

      Write-Host "Connecting to NAS..."
      net use X: "$nasPath" /user:"$nasUser" "$nasPass" /persistent:no

      Write-Host "Stopping AppPool..."
      Stop-WebAppPool -Name $iisAppPoolName

      if (Test-Path $iisPhysicalPath) {
        Write-Host "Cleaning existing site files..."
        Remove-Item -Recurse -Force "$iisPhysicalPath\*"
      } else {
        Write-Host "Creating IIS folder..."
        New-Item -ItemType Directory -Path $iisPhysicalPath | Out-Null
      }

      Write-Host "Copying WebApp from NAS → IIS..."
      Copy-Item -Recurse -Force "X:\publish\*" $iisPhysicalPath

      Write-Host "Restarting AppPool..."
      Start-WebAppPool -Name $iisAppPoolName

      Write-Host "=== [REPORT] 테스트 리포트 복사 시작 ==="
      if (!(Test-Path $iisReportPath)) {
        Write-Host "Creating report directory: $iisReportPath"
        New-Item -ItemType Directory -Path $iisReportPath -Force | Out-Null
      }
      Copy-Item -Recurse -Force "X:\TestReport\*" $iisReportPath

      net use X: /delete /y
      Write-Host "✅ 웹앱 + 테스트 리포트 배포 완료!"
    } -ArgumentList `
      $env:IIS_APP_POOL_NAME, `
      $env:IIS_PHYSICAL_PATH, `
      $env:NAS_PATH, `
      $env:NAS_USER, `
      $env:NAS_PASS, `
      $env:IIS_REPORT_PATH

notify_mail:
  stage: notify
  tags:
    - windows
  when: always
  script:
    - echo "=== [NOTIFY] 파이프라인 상태 확인 ==="
    - |
      $status = "$env:CI_JOB_STATUS"
      $branch = "$env:CI_COMMIT_BRANCH"
      $project = "$env:CI_PROJECT_NAME"
      $pipelineUrl = "$env:CI_PIPELINE_URL"
      $commitMsg = "$env:CI_COMMIT_MESSAGE"
      $author = "$env:GITLAB_USER_NAME"

      Write-Host "현재 파이프라인 상태: $status"

      if ($status -eq "success") {
          $subject = "✅ [$project] 파이프라인 성공 알림"
          $message = @"
🚀 프로젝트: $project
브랜치: $branch
결과: ✅ 성공
커밋: $commitMsg
작성자: $author
상세 보기: $pipelineUrl
"@
      } else {
          $subject = "❌ [$project] 파이프라인 실패 알림"
          $message = @"
⚠️ 프로젝트: $project
브랜치: $branch
결과: ❌ 실패 ($status)
커밋: $commitMsg
작성자: $author
상세 보기: $pipelineUrl
"@
      }

      $body = @{
        subject = $subject
        message = $message
      } | ConvertTo-Json

      try {
          Invoke-RestMethod `
            -Uri "https://mail-api.example.com/send" `
            -Method POST `
            -Body $body `
            -ContentType "application/json"

          Write-Host "📨 메일 발송 성공: $subject"
      }
      catch {
          Write-Host "❗ 메일 발송 실패: $($_.Exception.Message)"
      }

 

이렇게 완성할 수 있었다.

 

테스트 리포트는 아래 이미지 처럼 작성되어 커버리지 추세까지 확인이 가능하다.(보안상의 이유로 예시 이미지 첨부)

출처: https://marketplace.visualstudio.com/items?itemName=Palmmedia.reportgenerator

4. 마무리

이제 1차 개발이 완료되었다고 개인적으로 생각한다. 앞으로는 당장 실무에 적용해보고 다시 개선할 부분들을 찾아나가면 될 것 같다.

사실 배포자동화는 다른 곳에서는 당연히 갖춰진 시스템이고, IT회사에서는 비교도 안될 만큼 대규모의 CI/CD 시스템이 갖춰진 곳이 많을 것 같아서 이게 현업에 IMPACT를 줄 수 있을지에 대한 고민은 있었다.

 

테스트를 진행하며 배포 과정에서의 리소스가 크게 줄어드는 걸 보면서, 이번 개발이 팀에 꼭 필요한 일이라는 확신이 들었다. 피드백 과정에서 팀원들의 관심도 높아졌고, 앞으로 백오피스 영역으로까지 확장할 수 있을 것 같아 만족스럽다.

반응형