Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SP-668 페이지별 성능 측정 tool(LightHouse) 자동화 환경 구축 #349

Merged
merged 73 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
b4be238
feat: UT 배너 제거 (SP-668)
SungHyun627 Jul 31, 2024
61a965b
feat: lighthouse를 활용하여 성능 측정을 할 페이지 이름 및 라우트 정의 (SP-668)
SungHyun627 Jul 31, 2024
13cfa49
chore: lighthouse ci 설정 파일 작성 (SP-668)
SungHyun627 Jul 31, 2024
8442b03
chore: lighthouse 성능 측정 workflow 작성 (SP-668)
SungHyun627 Jul 31, 2024
e502476
fix: lighthouse workflow step key 수정 (SP-668)
SungHyun627 Jul 31, 2024
cd3633c
fix: 불필요한 따옴표 제거 (SP-668)
SungHyun627 Jul 31, 2024
8cccefd
chore: lighthouse 설정 extension 및 module 가져오는 방식 수정 (SP-668)
SungHyun627 Jul 31, 2024
963bd5d
fix: 모듈 경로 수정 (SP-668)
SungHyun627 Jul 31, 2024
5842222
fix: 모듈 Extension 수정 (SP-668)
SungHyun627 Jul 31, 2024
c4cfd12
fix: CJS import 에러 해결을 위해 안에서 페이지 라우트 선언언 (SP-668)
SungHyun627 Jul 31, 2024
8d5f3e0
fix: internal 컴파일러에서 인식하지 못하는 indentifier 제거 (SP-668)
SungHyun627 Jul 31, 2024
70b1322
fix: as const 제거 (SP-668)
SungHyun627 Jul 31, 2024
940e2b0
feat: config에 필요한 정보를 lighthouserc.js 내부에 선언 (SP-668)
SungHyun627 Jul 31, 2024
1f2bacd
chore: lighthouse config 파일 extension 수정 (SP-668)
SungHyun627 Jul 31, 2024
2eb2e59
feat: 페이지 url을 얻는 로직 수정 (SP-668)
SungHyun627 Jul 31, 2024
008c0bc
chore: mkcert를 사용하여 HTTPS를 적용하는 step 추가 (SP-668)
SungHyun627 Aug 1, 2024
873604c
chore: github host에 호스트명 추가 (SP-668)
SungHyun627 Aug 1, 2024
4165007
fix: 생성된 SSL 인증서 및 key를 cert 폴더로 이동 (SP-668)
SungHyun627 Aug 8, 2024
ff47287
fix: dev server start step 제거 (SP-668)
SungHyun627 Aug 8, 2024
0f15592
chore: mkcert를 통해 생성된 env 변수 export (SP-668)
SungHyun627 Aug 8, 2024
9dc8fa0
fix: Lighthouse 결과를 수집하는 횟수 수정 (SP-668)
SungHyun627 Aug 8, 2024
1971ac9
fix: 폴더를 생성하는 command 수정 (SP-668)
SungHyun627 Aug 8, 2024
7033090
chore: certificate errors를 ignore하는 command 추가 (SP-668)
SungHyun627 Aug 8, 2024
f0f8a03
chore: CA 확인 step 추가 (SP-668)
SungHyun627 Aug 11, 2024
e40dab3
chore: 개발 서버 응답 확인 (SP-668)
SungHyun627 Aug 11, 2024
14ac04d
chore: CA 인증서 생성 확인 (SP-668)
SungHyun627 Aug 11, 2024
cb27f07
chore: 파일 경로 수정 (SP-668)
SungHyun627 Aug 11, 2024
7096fa4
chore: 불필요한 코드 제거 (SP-668)
SungHyun627 Aug 11, 2024
b3101c3
chore: 환경변수 및 lighthouse CI 출력 flag 설정 (SP-668)
SungHyun627 Aug 16, 2024
54aea10
chore: certificate error ignore flag 설정 (SP-668)
SungHyun627 Aug 16, 2024
29b9800
chore: dev server를 https로 start하는 코드 추가 (SP-668)
SungHyun627 Aug 16, 2024
02b6fe9
chore: 인증서 및 key 경로 재설정 (SP-668)
SungHyun627 Aug 16, 2024
4ead725
chore: 인증서 및 key 경로 재설정 (SP-668)
SungHyun627 Aug 16, 2024
abd0909
chore: 서버 로그 분석 및 서버 접근 테스트 step 추가 (SP-668)
SungHyun627 Aug 18, 2024
d31deca
chore: 서버 접근 테스트 제거 및 ignore certificate error flag 추가 (SP-668)
SungHyun627 Aug 18, 2024
33e5dd0
chore: 테스트를 위해 lighthouse ci 설정 branch를 workflow 실행 branch에 추가 (SP-668)
SungHyun627 Aug 18, 2024
ca9a31c
chore: 테스트할 branch 재설정 (SP-668)
SungHyun627 Aug 18, 2024
f76c241
fix: lighthouse 결과를 담을 outputDir 수정 (SP-668)
SungHyun627 Aug 18, 2024
4a91969
chore: feat/SP-668-setup-lighthouse-ci branch에 push 시, lighthouse rep…
SungHyun627 Aug 18, 2024
d3aca9e
chore: 성능 측정할 페이지 route 정리 (SP-688)
SungHyun627 Aug 19, 2024
eb894a5
chore: SSL Cert 파일과 key를 생성하는 step 제거 (SP-668)
SungHyun627 Aug 19, 2024
79ae828
refactor: Lighthouse 관련 상수 분리 (SP-668)
SungHyun627 Aug 19, 2024
648ddf4
chore: lighthouse 설정을 desktop과 mobile로 분리 (SP-668)
SungHyun627 Aug 19, 2024
355631d
chore: lighthouse 실행 workflow 수정 (SP-668)
SungHyun627 Aug 19, 2024
5635564
chore: lighthouse formatting 하는 step 추가 (SP-668)
SungHyun627 Aug 19, 2024
d7ee340
chore: desktop 기준으로 측정된 lighthouse 결과를 pr comment에 반영하는 step 추가 (SP-668)
SungHyun627 Aug 19, 2024
d70f6fe
chore: workflow 띄어쓰기 공백 제거 (SP-668)
SungHyun627 Aug 19, 2024
d9be260
fix : 읽은 파일의 내용을 JSON 객체로 변환 (SP-668)
SungHyun627 Aug 19, 2024
e0d3f92
fix: 파일을 읽는 메서드 오타 수정 (SP-668)
SungHyun627 Aug 19, 2024
5a7c615
chore: lighthouse 점수 색상 기준 수정정 (SP-668)
SungHyun627 Aug 19, 2024
215d63f
chore: lighthouse results 경로 재설정 (SP-668)
SungHyun627 Aug 19, 2024
30e4d1c
chore: github-token 항목 추가 및 경로 수정 (SP-668)
SungHyun627 Aug 19, 2024
0eeebf0
chore: env working-directory 추가 (SP-668)
SungHyun627 Aug 19, 2024
9d6bb73
chore: working-directory 출력 코드 추가 (SP-668)
SungHyun627 Aug 20, 2024
6bc69b6
chore: lighthouse Desktop 결과 출력 (SP-668)
SungHyun627 Aug 20, 2024
dfc8f79
fix: workflow script 내의 주석 제거 (SP-668)
SungHyun627 Aug 20, 2024
31ea82d
chore: 사용하지 않는 파일 제거 (SP-668)
SungHyun627 Aug 20, 2024
f702f6c
feat: lighthouse 점수 기준 상수화 (SP-668)
SungHyun627 Aug 20, 2024
2782d54
chore: lighthouse score 포맷팅 (SP-668)
SungHyun627 Aug 20, 2024
acf051c
chore: lighthouse 결과를 comment에 반영하는 step 추가 (SP-668)
SungHyun627 Aug 20, 2024
8b17fba
fix: 이모지를 문자열로 인식하도록 수정 (SP-668)
SungHyun627 Aug 20, 2024
1598c39
fix: workflow 오타 수정 (SP-668)
SungHyun627 Aug 20, 2024
ae6db8e
fix: 누락된 괄호 추가 (SP-668)
SungHyun627 Aug 20, 2024
4baab51
fix: octokit 오타 수정 (SP-668)
SungHyun627 Aug 20, 2024
3b3d649
fix: issue_number로 넘겨주는 pull request number 프로퍼티 수정 (SP-668)
SungHyun627 Aug 20, 2024
c820ff6
fix: 이전 report comment를 나타내는 변수명 수정 (SP-668)
SungHyun627 Aug 20, 2024
7cc5888
chore: comment List 데이터의 변수명을 prevComments로 설정 (SP-668)
SungHyun627 Aug 20, 2024
24be4e5
chore: 이전 comment가 없을 때 새로운 comment를 생성하도록 메서드 수정 (SP-668)
SungHyun627 Aug 20, 2024
e90d910
fix: comment 포맷팅 수정 (SP-668)
SungHyun627 Aug 20, 2024
06f0b54
chore: PR의 본문이 edit되거나 reopen 시에는 workflow가 실행되지 않도록 수정 (SP-668)
SungHyun627 Aug 20, 2024
bf7c694
chore: 이모지 사이 공백 추가 (SP-668)
SungHyun627 Aug 20, 2024
60637b4
fix: workflow name 수정 (SP-668)
SungHyun627 Aug 20, 2024
43e629f
chore: node-version을 node-version-file로 수정 (SP-668)
SungHyun627 Aug 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'prettier', 'plugin:storybook/recommended'],
env: { browser: true, es2020: true, node: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier',
'plugin:storybook/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
Expand All @@ -14,5 +20,6 @@ module.exports = {
'react/jsx-key': 'off',
// 디버그 허용
'no-debugger': 'off',
'@typescript-eslint/no-var-requires': 'off',
},
};
166 changes: 166 additions & 0 deletions .github/workflows/lighthouse.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Run lighthouse CI When Push on PR to Dev Branch
on:
pull_request:
branches:
- dev
types: [synchronize, opened]

permissions:
contents: read
pull-requests: write

jobs:
lhci:
name: Lighthouse CI
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js 20.10.0
uses: actions/setup-node@v4
with:
node-version-file: .node-version

- name: Install dependencies
run: |
yarn install --immutable --immutable-cache --check-cache

- name: Add host "local.ludo.study"
run: sudo echo "127.0.0.1 local.ludo.study" | sudo tee -a /etc/hosts

- name: Build the project
run: |
yarn build

- name: Run Lighthouse CI - Desktop
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
run: |
yarn global add @lhci/cli
lhci collect --config=lighthouserc-desktop.cjs || echo "Fail to Run Lighthouse CI!"
lhci upload --config=lighthouserc-desktop.cjs || echo "Fail to Run Lighthouse CI!"

- name: Run Lighthouse CI - Mobile
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
run: |
lhci collect --config=lighthouserc-mobile.cjs || echo "Fail to Run Lighthouse CI!"
lhci upload --config=lighthouserc-mobile.cjs || echo "Fail to Run Lighthouse CI!"


- name: Format Lighthouse Score
id: format_lighthouse_score
uses: actions/github-script@v7
env:
working-directory: ${{ github.workspace }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');

const {
LH_MONITORING_PAGE_NAMES,
DEV_ORIGIN_URL,
LH_MONITORING_PAGE_ROUTES,
LH_MIN_SCORES,
} = require('./src/Constants/lighthouse.ts');

const desktopLightHouseResults = JSON.parse(fs.readFileSync('lhci_reports/desktop/manifest.json'));
const mobileLightHouseResults = JSON.parse(fs.readFileSync('lhci_reports/mobile/manifest.json'));

let comments = `### 💡 LightHouse Reports\n\n`;
comments += `#### 🟢 90 ~ 100    🟠 50 ~ 89    🔴 0 ~ 49 \n\n`;

const getFormattingScore = (res) => Math.round(res * 100);

const getScoreColor = (score) => (score >= LH_MIN_SCORES.GREEN ? '🟢' : score >= LH_MIN_SCORES.ORANGE ? '🟠' : '🔴');

const getMonitoringPageName = (url) => {
const route = url.replace(DEV_ORIGIN_URL, '');

for (let pageName of LH_MONITORING_PAGE_NAMES) {
if (route === LH_MONITORING_PAGE_ROUTES[pageName]) return pageName;
}
};

const getFormattingResultByPage = (result) => {
const { url, summary, jsonPath } = result;
const { audits } = JSON.parse(fs.readFileSync(jsonPath));

const { performance, accessibility, 'best-practices': bestPractices, seo } = summary;

const {
'first-contentful-paint': firstContentfulPaint,
'largest-contentful-paint': largestContentfulPaint,
'speed-index': speedIndex,
'total-blocking-time': totalBlockingTime,
'cumulative-layout-shift': cumulativeLayoutShift,
} = audits;

const formattingTable = [
`| Category | Score |`,
`| --- | --- |`,
`| ${getScoreColor(getFormattingScore(performance))} Performance | ${getFormattingScore(performance)} |`,
`| ${getScoreColor(getFormattingScore(accessibility))} Accessibility | ${getFormattingScore(accessibility)} |`,
`| ${getScoreColor(getFormattingScore(bestPractices))} Best practices | ${getFormattingScore(bestPractices)} |`,
`| ${getScoreColor(getFormattingScore(seo))} SEO | ${getFormattingScore(seo)} |`,
`| ${getScoreColor(getFormattingScore(firstContentfulPaint.score))} First Contentful Paint | ${firstContentfulPaint.displayValue} |`,
`| ${getScoreColor(getFormattingScore(largestContentfulPaint.score))} Largest Contentful Paint | ${largestContentfulPaint.displayValue} |`,
`| ${getScoreColor(getFormattingScore(speedIndex.score))} Speed Index | ${speedIndex.displayValue} |`,
`| ${getScoreColor(getFormattingScore(totalBlockingTime.score))} Total Blocking Time | ${totalBlockingTime.displayValue} |`,
`| ${getScoreColor(getFormattingScore(cumulativeLayoutShift.score))} Cumulative Layout Shift | ${cumulativeLayoutShift.displayValue} |`,
`\n`,
].join('\n');

return `<details>\n<summary>${`📄 ${getMonitoringPageName(url)}\n`}</summary>\n\n${formattingTable}\n</details>\n\n`;
};

const getLightHouseFormattingResult = (results, type) => {
let comment = type === 'mobile' ? `#### 📱 Mobile\n` : `#### 🖥 Desktop\n`;
results.forEach((result) => (comment += getFormattingResultByPage(result)));

return comment + '\n';
};

comments += getLightHouseFormattingResult(desktopLightHouseResults, 'desktop');
comments += getLightHouseFormattingResult(mobileLightHouseResults, 'mobile');

core.setOutput('comments', comments)

- name: Comment PR
id: add_pr_comment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { Octokit } = require('@octokit/rest');
const octokit = new Octokit({ auth: `${{ secrets.GITHUB_TOKEN }}` });

const { payload, repo } = context

const newComment = `${{ steps.format_lighthouse_score.outputs.comments }}`

const { data: prevComments } = await octokit.rest.issues.listComments({
owner: repo.owner,
repo: repo.repo,
issue_number : payload.pull_request.number,
})

const prevReportComment = prevComments.find(comment => comment.body.includes(`### 💡 LightHouse Reports\n\n`));

if (prevReportComment) {
await octokit.rest.issues.updateComment({
owner: repo.owner,
repo: repo.repo,
comment_id: prevReportComment.id,
body: newComment,
});
} else {
await octokit.rest.issues.createComment({
owner: repo.owner,
repo: repo.repo,
issue_number: payload.pull_request.number,
body: newComment,
});
}
26 changes: 26 additions & 0 deletions lighthouserc-desktop.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const {
LH_MONITORING_PAGE_NAMES,
DEV_ORIGIN_URL,
LH_MONITORING_PAGE_ROUTES,
} = require('./src/Constants/lighthouse.ts');

const urls = LH_MONITORING_PAGE_NAMES.map((pageName) => `${DEV_ORIGIN_URL}${LH_MONITORING_PAGE_ROUTES[pageName]}`);

module.exports = {
ci: {
collect: {
startServerCommand: 'yarn start:mac',
url: urls,
numberOfRuns: 1,
settings: {
chromeFlags: '--ignore-certificate-errors',
preset: 'desktop',
},
},
upload: {
target: 'filesystem',
outputDir: './lhci_reports/desktop',
reportFilenamePattern: '%%PATHNAME%%-%%DATETIME%%-desktop-report.%%EXTENSION%%',
},
},
};
25 changes: 25 additions & 0 deletions lighthouserc-mobile.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const {
LH_MONITORING_PAGE_NAMES,
DEV_ORIGIN_URL,
LH_MONITORING_PAGE_ROUTES,
} = require('./src/Constants/lighthouse.ts');

const urls = LH_MONITORING_PAGE_NAMES.map((pageName) => `${DEV_ORIGIN_URL}${LH_MONITORING_PAGE_ROUTES[pageName]}`);

module.exports = {
ci: {
collect: {
startServerCommand: 'yarn start:mac',
url: urls,
numberOfRuns: 1,
settings: {
chromeFlags: '--ignore-certificate-errors',
},
},
upload: {
target: 'filesystem',
outputDir: './lhci_reports/mobile',
reportFilenamePattern: '%%PATHNAME%%-%%DATETIME%%-mobile-report.%%EXTENSION%%',
},
},
};
26 changes: 26 additions & 0 deletions src/Constants/lighthouse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const TEST_RECRUITMENT_ID = 70;
const LH_GREEN_MIN_SCORE = 90;
const LH_ORANGE_MIN_SCORE = 50;

const PAGE_ROUTES = {
MAIN: '/',
AUTH: {
LOGIN: '/login',
},
RECRUITMENT: {
RECRUITMENTS: '/studies',
DETAIL: `/studies/${TEST_RECRUITMENT_ID}/recruitment`,
},
};

module.exports = {
DEV_ORIGIN_URL: `https://local.ludo.study:3000`,
LH_MONITORING_PAGE_NAMES: ['메인', '로그인', '모집공고 모아보기', '모집공고 상세'],
LH_MIN_SCORES: { GREEN: LH_GREEN_MIN_SCORE, ORANGE: LH_ORANGE_MIN_SCORE },
LH_MONITORING_PAGE_ROUTES: {
메인: PAGE_ROUTES.MAIN,
로그인: PAGE_ROUTES.AUTH.LOGIN,
'모집공고 모아보기': PAGE_ROUTES.RECRUITMENT.RECRUITMENTS,
'모집공고 상세': PAGE_ROUTES.RECRUITMENT.DETAIL,
},
};
2 changes: 0 additions & 2 deletions src/Router/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ import { ReviewPage } from '@/Pages/Review';
import { Notifications } from '@/Pages/Notifications';
import { MyPageReviews } from '@/Pages/MyPageReviews';
import { ProfileLayout } from '@/Layout/ProfileLayout';
import UTbanner from '@/Components/UTbanner';

export const RouterPath = createBrowserRouter([
{
element: (
<>
<UTbanner />
<Header />
<Outlet />
<Footer />
Expand Down