[꿀팁] Lighthouse CI와 GitHub Actions를 통한 웹 성능 모니터링과 Slack 알림 연동하기 (1)

in-ch·2024년 3월 4일
1

꿀팁

목록 보기
10/14
post-thumbnail
post-custom-banner

서론

현대 웹 개발에서는 사용자 경험과 성능 최적화가 매우 중요합니다.

개발을 자주 진행하다보면 Lighthouse 측정을 놓치는 경우가 있을 수 있는데 웹 사이트 성능을 지속적으로 모니터링하고 개선하는 것은 사용자들에게 더 나은 경험을 제공하고 검색 엔진 최적화(SEO)에도 도움이 됩니다.

이번에 프로젝트에서 Lighthouse CIGitHub Actions를 사용하여 웹 사이트의 성능을 지속적으로 모니터링하고, 성능 문제를 식별하고 해결하기 위한 프로세스를 구현하였는데, 어떻게 진행했는지 같이 살펴봅시다!

이론

본격적으로 도입하기 전 간단하게 Lighthouse측정을 도와두는 도구, CWVGit Actions, Slack에 대해서 간단하게 정리하겠습니다.

Lighthouse

Lighthouse는 Google에서 개발한 오픈 소스 도구로, 웹 앱이나 웹 페이지의 성능, 접근성, SEO 등 다양한 측면을 평가하는 도구입니다.

  • Lighthouse의 주요 기능
    1 . 성능 평가: 웹 페이지의 로딩 속도, 렌더링 시간, 자원 사용 등을 평가하여 성능을 개선하는 데 도움
    2 . 접근성 평가: 웹 페이지의 접근성을 평가하여 장애인이나 저시력자를 포함한 모든 사용자가 쉽게 이용할 수 있는지 확인
    3 . SEO 평가: 웹 페이지의 검색 엔진 최적화(SEO) 상태를 평가하여 검색 결과에서 노출되는 정도를 개선하는 데 도움
    4 . 최적화 제안: 평가 결과를 기반으로 성능을 개선할 수 있는 다양한 제안 사항을 제공

  • 예시

Lighthouse CI

Lighthouse을 측정하기 위해서는 크롬 브라우저의 개발자 도구에서 [Lighthouse] -> Analyze page load 버튼을 클릭함으로써 측정이 가능합니다.

그러나 이러한 방식은 수동으로 진행되며, 코드 변경이나 배포와 관련된 성능 변화를 감지하거나, 성능을 지속적으로 모니터링하기에는 적합하지 않습니다.

이를 해결하기 위해 등장한 것이 바로 Lighthouse CI입니다. Lighthouse CI는 GitHub Actions, GitLab CI 등의 CI/CD 도구와 통합하여 웹 페이지의 성능을 자동화된 방식으로 평가할 수 있게 해줍니다.

CWV (Core Web Virtal)

Core Web Vitals(CWV)는 Google에서 제공하는 세 가지 핵심 웹 퍼포먼스 지표로, 웹 페이지의 사용자 경험을 측정하는 데 사용됩니다.

  • Largest Contentful Paint (LCP): LCP는 웹 페이지의 주요 콘텐츠가 화면에 표시되는 데 걸리는 시간
  • First Input Delay (FID): FID는 사용자가 페이지와 상호 작용할 때까지의 시간을 측정, 즉 사용자가 페이지를 클릭하거나 탭할 때부터 브라우저가 실제로 반응하는 시점까지의 지연 시간을 의미
  • Cumulative Layout Shift (CLS): CLS는 페이지 로딩 중에 발생하는 레이아웃 변화의 누적 정도를 측정

GitHub Actions

대표적인 CI/CD 툴입니다. Github에서 제공하며, 소프트웨어 개발 및 배포 과정을 자동화할 수 있습니다.

사용법은 너무 길어지니 여기서는 생략하도록 하겠습니다.

Slack

Slack은 업무 협업 및 커뮤니케이션 도구로 널리 사용되는 플랫폼입니다.

여기서는 Slack Web hooks을 활용하여 PR(Pull Request) 혹은 push 마다 Lighthouse 측정 결과를 슬랙 메시지로 알림을 받아볼 수 있게 하겠습니다.

프로세스 구현하기

본격적으로 프로세스를 구현해보도록 해봅시다. 🔥

1. Lighthouse CI 설치 및 설정

  • 설치하기
    먼저 로컬에서 로컬에 설치를 진행합니다.
npm i -E -g @lhci/cli
  • 설정 파일 생성
    로컬에 .lighthouserc.js 파일을 생성하고 다음과 같이 입력합니다.
  const {paths} = require("./etc/paths.js");

  // https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/configuration.md#target
  module.exports = {
    ci: {
      collect: {
        settings: {
          emulatedFormFactor: 'desktop',
        },
        startServerCommand: 'yarn start',
        url: paths, // 성능을 측정할 url
        numberOfRuns: 1, // Lighthouse가 실행되는 횟수, 점수가 동일한 코드라고 하더라도 네트워크 및 웹 기술 등의 변수에 의해 결과가 다르게 나올 수 있으니 여러번 실행하여 테스트한다.
      },
      assert: {
        assertions: {
          'categories:performance': ['warning', { minScore: 0.9 }],
          'categories:accessibility': ['warning', { minScore: 0.9 }],
          'categories:best-practices': ['warning', { minScore: 0.9 }],
          'categories:seo': ['warning', { minScore: 0.9 }],
          'categories:pwa': ['warning', { minScore: 0.9 }],
        },
      },
      upload: {
        target: 'filesystem',
        outputDir: './lhci_reports',
        reportFilenamePattern: 'example-%%DATETIME%%-CWV-report.%%EXTENSION%%',
      },
      server: {},
      wizard: {},
    },
  };
  • path.js
    여기서 pathsdevServer는 입맛에 맞게 수정하면 됩니다.
const devServer = 'http://localhost:3000';
const paths = {
    	home: '/',
    	a '/a',
    	b: '/b',
};

exports.paths = Object.keys(paths).map((key) => `${devServer}${paths[key]}`);

2. 실행하기

  • 다음과 같은 명령어를 실행시킵니다.
lhci autorun
  • 결과

lhci_reports.lighthouseci에 각각 측정 결과가 생성된 것을 확인해 볼 수 있습니다.

3. git actions 설정하기

  • /.github/workflows/lighthouse.yml 생성한 후 다음과 같이 입력합니다.
name: Run Lighthouse CI & Slack Msg When feature Push

on:
  push:
    branches:
      - 'feature/**'

jobs:
  create-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Use Node.js 20.9.0
        uses: actions/setup-node@v1
        with:
          node-version: 20.9.0

      - name: Set yarn version 1.22.19
        run: |
          yarn set version classic

      - name: Install packages
        run: |
          yarn install

      - name: Build
        run: |
          yarn build

      - name: Run Lighthouse CI
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
        run: |
          npm install -g @lhci/cli
          lhci autorun || echo "Fail to Run Lighthouse CI!"

      - name: Format lighthouse score
        id: format_lighthouse_score
        uses: actions/github-script@v3
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            const fs = require('fs');
            const results = JSON.parse(fs.readFileSync("/home/runner/work/medipot_front/medipot_front/lhci_reports/manifest.json"));
            let comments = "";

            results.forEach((result) => {
              const { summary, jsonPath } = result;
              const details = JSON.parse(fs.readFileSync(jsonPath));
              const { audits } = details; // details를 사용하기 전에 정의
              const formatResult = (res) => Math.round(res * 100);

              Object.keys(summary).forEach(
                (key) => (summary[key] = formatResult(summary[key]))
              );

              const score = (res) => (res >= 90 ? "🟢" : res >= 50 ? "🟠" : "🔴");

              const comment = [
                `⚡️ Lighthouse report!`,
                `| Category | Score |`,
                `| --- | --- |`,
                `| ${score(summary.performance)} Performance | ${summary.performance} |`,
                // Add other categories here
              ].join("\n");

              const detail = [
                `| Category | Score |`,
                `| --- | --- |`,
                `| ${score(
                  audits["first-contentful-paint"].score * 100
                )} First Contentful Paint | ${
                  audits["first-contentful-paint"].displayValue
                } |`,
                // Add other details here
              ].join("\n");
              comments += comment + "\n" + detail + "\n";
            });
            core.setOutput('comments', comments)

      - name: Create Pull Request
        id: cpr
        uses: peter-evans/create-pull-request@v3
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          base: develop
          branch: ${{ github.ref }}
          title: 'Merge feature branch into develop'
          body: |
            This Pull Request is automatically generated to merge the feature branch into the develop branch.

      - name: comment PR
        uses: unsplash/comment-on-pr@v1.3.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          msg: ${{ steps.format_lighthouse_score.outputs.comments}}
  • feature/로 시작하는 브랜치가 push 됐을 때 해당 job이 실행됩니다.
  • build 및 Lighthouse 측정을 시작하고 검사가 통과가 되면 마지막에 pull request를 생성하고 Lighthouse 측정 결과를 해당 pr의 comment로 남깁니다.

3.1 워크플로우용 환경 변수 설정

LHCI_GITHUB_APP_TOKENGITHUB_TOKEN을 얻으려면 다음과 같은 단계를 따릅니다.

LHCI_GITHUB_APP_TOKEN 생성하기

  1. Github setting profile로 이동합니다.
  2. [Settings] -> [Developer Settings] 페이지로 이동 후 Generate new token을 클릭합니다.
  3. 필요한 권한은 다음과 같습니다.
    • repo: 리포지토리의 전체 관리 권한
    • workflow: GitHub 액션 워크플로우의 업데이트 권한
    • write:packages: GitHub 패키지 레지스트리에 패키지를 업로드하는 권한
    • admin:repo_hook: 리포지토리 후크의 전체 제어 권한
    • project:react:project: 프로젝트 읽기 권한

LHCI_GITHUB_APP_TOKEN 등록하기

  1. 먼저 [레포지토리] -> [Settings]-> [Security] -> [Secrets and variables] -> [Actions]페이지로 이동합니다.
  2. New repository secret을 클릭합니다.
  3. Name에 LHCI_GITHUB_APP_TOKEN을 입력하고 생성했던 토큰값을 입력합니다.
  4. Add secret을 클릭해서 생성을 완료합니다.

GITHUB_TOKEN 얻기

GITHUB_TOKENGitHub Actions에서 자동으로 제공됩니다.
따라서 별도의 설정이 필요하지 않습니다.

참고로 GitHub Actions에서 작업을 실행할 때마다 자동으로 생성되며, 해당 작업에서만 사용할 수 있습니다.

4. 푸시 하기

코드를 푸시해봅시다.


[Merge pull request]의 테스트를 통과했습니다!

이제 Slack 연동을 해서 pr 요청이 올 때 마다 메시지를 받고 Lighthouse 점수까지 같이 받을 수 있도록 해봅시다.

2편에서 계속 ...

참고:

Running GitHub Actions on Preview Deploys
Lighthouse CI를 알아보고 Github Actions에 적용하기
웹 퍼포먼스 개선을 위한 Lighthouse CI 도입기

profile
인치
post-custom-banner

0개의 댓글