너굴 개발 일지

Github Actions 관련 본문

DevOps

Github Actions 관련

너굴냥 2024. 9. 8. 22:05

회사에서 CI-CD 구축 업무을 위해 Github Actions에 대해 공부한 글로 React 기반 테스트용 프로젝트에 적용해보며 느낀 점을 작성했습니다. (Docker Image 파일 크기 : 18MB)

캐시 적용

Github Actions 캐시 사용

 

GitHub Actions cache

Use the GitHub Actions cache to manage your build cache in CI

docs.docker.com

Github Actions 캐시 사용 결과

  • 두번째 빌드 과정을 보면 코드 변경이 일어난 layer인 COPY . . 이전까지의 layer 생성에서는 캐시가 적용된 것을 확인할 수 있다.

첫 빌드 (캐시 적용 안된 상태) - 1m 13s

[stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158
#9 resolve docker.io/library/nginx:alpine@sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158 0.0s done
#9 ...
#10 [internal] load build context
#10 transferring context: 735.56kB 0.0s done
#10 DONE 0.0s
#9 [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158
#9 sha256:9da224fdd4124c20879a425f59ee3d7e9aeccf37356692f37cd7736e38c2efd2 1.40kB / 1.40kB 0.0s done
#9 sha256:41c49cbde6a69c2861d4443a90e47a59e906386088b706d32aba1091d0f262b0 1.21kB / 1.21kB 0.0s done
#9 sha256:532b9a30583c1bf82204f3cbc8054882bace1669cc85fdcb45b8f88b4db82833 393B / 393B 0.0s done
#9 sha256:35b039ba2bc54667ad0fdce04367ea93ed097b0506cda323e280e1ed31f29b31 13.19MB / 13.19MB 0.1s done
#9 sha256:62a896bb4a21c26afb24814d77cc345822fd8b03255bb9f940a0707daa9f2ff6 955B / 955B 0.1s done
#9 sha256:45f552c78c312f2b711135f5af71a2eb06e223246d13cae7bf3a15e447136045 629B / 629B 0.1s done
#9 sha256:7f5898476db744b7e3d5f25c7533b4285e21cf2025610f339cb32bf39bebcfe4 1.76MB / 1.76MB 0.1s done
#9 extracting sha256:7f5898476db744b7e3d5f25c7533b4285e21cf2025610f339cb32bf39bebcfe4
#9 extracting sha256:7f5898476db744b7e3d5f25c7533b4285e21cf2025610f339cb32bf39bebcfe4 0.2s done
#9 extracting sha256:45f552c78c312f2b711135f5af71a2eb06e223246d13cae7bf3a15e447136045
#9 extracting sha256:45f552c78c312f2b711135f5af71a2eb06e223246d13cae7bf3a15e447136045 done
#9 extracting sha256:62a896bb4a21c26afb24814d77cc345822fd8b03255bb9f940a0707daa9f2ff6 done
#9 extracting sha256:532b9a30583c1bf82204f3cbc8054882bace1669cc85fdcb45b8f88b4db82833 done
#9 extracting sha256:41c49cbde6a69c2861d4443a90e47a59e906386088b706d32aba1091d0f262b0 done
#9 extracting sha256:9da224fdd4124c20879a425f59ee3d7e9aeccf37356692f37cd7736e38c2efd2 done
#9 extracting sha256:35b039ba2bc54667ad0fdce04367ea93ed097b0506cda323e280e1ed31f29b31
#9 extracting sha256:35b039ba2bc54667ad0fdce04367ea93ed097b0506cda323e280e1ed31f29b31 0.7s done
#9 DONE 1.2s
#11 [react-build 1/6] FROM docker.io/library/node:18-alpine@sha256:17514b20acef0e79691285e7a59f3ae561f7a1702a9adc72a515aef23f326729
#11 resolve docker.io/library/node:18-alpine@sha256:17514b20acef0e79691285e7a59f3ae561f7a1702a9adc72a515aef23f326729 0.0s done
#11 sha256:62c7e5ec2b01a374e0ad0f2299c63d7940a5bdb466c580b243b9977dc12b8ecb 444B / 444B 0.0s done
#11 sha256:bc6e437c6fa911e43ef82fe79ec3cf94625557d8426639ee7758448b8a4fb464 1.38MB / 1.38MB 0.0s done
#11 sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6 3.62MB / 3.62MB 0.1s done
#11 extracting sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6 0.2s done
#11 sha256:1475bb19bdb76a034cba9b274ea9e4298897d6e108dbc81268740d70639f2b69 39.84MB / 39.84MB 0.3s done
#11 extracting sha256:1475bb19bdb76a034cba9b274ea9e4298897d6e108dbc81268740d70639f2b69
#11 ...
#12 [stage-1 2/3] COPY nginx.conf /etc/nginx/conf.d/default.conf
#12 DONE 0.6s
#11 [react-build 1/6] FROM docker.io/library/node:18-alpine@sha256:17514b20acef0e79691285e7a59f3ae561f7a1702a9adc72a515aef23f326729
#11 extracting sha256:1475bb19bdb76a034cba9b274ea9e4298897d6e108dbc81268740d70639f2b69 1.6s done
#11 extracting sha256:bc6e437c6fa911e43ef82fe79ec3cf94625557d8426639ee7758448b8a4fb464 0.0s done
#11 DONE 2.0s
#11 [react-build 1/6] FROM docker.io/library/node:18-alpine@sha256:17514b20acef0e79691285e7a59f3ae561f7a1702a9adc72a515aef23f326729
#11 extracting sha256:62c7e5ec2b01a374e0ad0f2299c63d7940a5bdb466c580b243b9977dc12b8ecb done
#11 DONE 2.0s
#13 [react-build 2/6] WORKDIR /app
#13 DONE 0.0s
#14 [react-build 3/6] COPY package*.json ./
#14 DONE 0.0s
#15 [react-build 4/6] RUN npm install
#15 7.414 npm warn deprecated w3c-hr-time@1.0.2: Use your platform's native performance.now() and performance.timeOrigin.
#15 DONE 21.6s
 

버전만 변경 후 build (캐시 생성된 상태) - 1m 1s

#10 [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158
#10 resolve docker.io/library/nginx:alpine@sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158 done
#10 DONE 0.0s
#11 importing cache manifest from gha:12972855224182164046
#11 DONE 0.6s
#8 [internal] load build context
#8 transferring context: 735.56kB 0.0s done
#8 DONE 0.0s
#12 [react-build 2/6] WORKDIR /app
#12 CACHED
#13 [react-build 3/6] COPY package*.json ./
#13 CACHED
#14 [react-build 4/6] RUN npm install
#14 CACHED
#14 [react-build 4/6] RUN npm install
#14 CACHED
#14 [react-build 4/6] RUN npm install
#14 sha256:16a24cffa87f8d63f3ffae1193a0f9a93015041fd4da993ecc42156747808189 0B / 98.52MB 0.2s
#14 sha256:9ce45f640f7831f89f81f3fc20f87cd8a91b6ad56bcf44a6d0ef6b5bd733b6d1 0B / 156.18kB 0.2s
#14 sha256:ff042a5863a401911d63533d1d29aa3cfc6230af13e43c2522b4a0013ed4e3bb 0B / 93B 0.2s
#14 sha256:62c7e5ec2b01a374e0ad0f2299c63d7940a5bdb466c580b243b9977dc12b8ecb 0B / 444B 0.2s
#14 sha256:ff042a5863a401911d63533d1d29aa3cfc6230af13e43c2522b4a0013ed4e3bb 93B / 93B 0.3s done
#14 sha256:62c7e5ec2b01a374e0ad0f2299c63d7940a5bdb466c580b243b9977dc12b8ecb 444B / 444B 0.3s done
#14 sha256:9ce45f640f7831f89f81f3fc20f87cd8a91b6ad56bcf44a6d0ef6b5bd733b6d1 156.18kB / 156.18kB 0.5s done
// 생략
#14 DONE 15.5s
#15 [react-build 5/6] COPY . .
#15 DONE 2.8s
#16 [react-build 6/6] RUN npm run build
#16 1.139

Github Actions 캐시 정리

  • 테스트 프로젝트라 빌드 시간의 차이가 크지 않았지만 프로젝트 용량이 클수록 Github Actions 캐시 장점이 돋보일 것 같다.
  • Github Actions 캐시를 사용해도 Docker 이미지 빌드 최적화를 진행하지 않으면 무용지물이므로… 최적화는 필수적인 것 같다.
  • 자주 빌드하지 않거나 서비스가 크다면 Self hosted Runne에서 로컬 캐싱하는 방법도 좋을 것 같다.

 

Code Review

ChatGPT를 활용한 코드리뷰

 

ChatGPT-CodeReview/README.ko.md at main · anc95/ChatGPT-CodeReview

🐥 A code review bot powered by ChatGPT. Contribute to anc95/ChatGPT-CodeReview development by creating an account on GitHub.

github.com

  • 만약 429 에러가 발생한다면 Open AI Billing 정보를 등록해야 한다.
  • secrets.GITHUB_TOKEN 은 깃허브가 워크플로우 안에서 사용할 시크릿을 자동으로 생성하기에 별도로 repo secrets에 추가하지 않아도 된다.
  • 입출력에 따른 1M 토큰마다 요금이 부과되는 방식이다 (https://openai.com/api/pricing/)
    • 4bye가 평균 1토큰이다.
    • 아래 결과를 보면 타이머를 구현한 간단한 코드 리뷰 진행시 $0.01 금액이 사용됨

코드리뷰 결과

리뷰 받은 코드 (타이머 구현 예시 코드)

더보기

리뷰 받은 코드 (타이머 구현 예시 코드)

import React, { useEffect, useState } from "react";
import styles from '../Auction/Timer.module.css';


export default function Timer() {
  // 시간을 담을 변수
  const [count, setCount] = useState(30);

  useEffect(() => {
    // 설정된 시간 간격마다 setInterval 콜백이 실행된다. 
    const id = setInterval(() => {
      // 타이머 숫자가 하나씩 줄어들도록
      setCount((count) => count - 1);
    }, 1000);
    
    // 0이 되면 카운트가 멈춤
    if(count === 0) {
        clearInterval(id);
    }
    return () => clearInterval(id);
    // 카운트 변수가 바뀔때마다 useEffecct 실행
  }, [count]);

  return <div className={styles.timer}><span className={styles.count}>{count}</span></div>;
}

타이머 구현 코드에 대한 리뷰

더보기

코드 리뷰:

  1. useEffect 안에서 count 상태를 직접적으로 비교하여 0일 때 clearInterval을 호출하는 코드는 문제가 있습니다. count의 값이 변경될 때 해당 변화가 즉시 반영되지 않고, setCount가 비동기적으로 동작하기 때문입니다. 이로 인해 clearInterval 조건이 바로 반영되지 않을 수 있습니다.

개선 제안:

  • setInterval 함수에서 현재 시간을 감소시킨 후, 결과가 0보다 작은지를 확인하여 clearInterval을 호출하도록 수정합니다.
  • setCount((count) => count - 1) 대신에 setCount(count - 1)로 변경하여 불필요한 함수 호출을 줄입니다.
  1. 코드에 주석이 상대적으로 많이 사용되었으나, 설명보다는 코드 자체로 이해할 수 있도록 변수나 함수명 등을 더 명확한 이름으로 변경하는 것이 가독성을 높일 수 있습니다.
  2. 파일 끝에 \ No newline at end of file 메시지는 깃(Git)에서 생성된 diff 파일에 해당하는 내용이며, 실제 코드에 영향을 주지 않습니다.
  3. 모듈(css 파일)을 import 하는 부분과 컴포넌트 코드 사이에 한 줄 개행이 권장되며, 일관된 스타일에 따라 현재 코드와 다른 부분들을 고려해볼 수 있습니다.

개선 제안:

import React, { useEffect, useState } from "react";
import styles from '../Auction/Timer.module.css';

export default function Timer() {
  const [count, setCount] = useState(30);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count - 1);
    }, 1000);
    
    if (count <= 0) {
        clearInterval(id);
    }

    return () => clearInterval(id);
  }, [count]);

  return (
    <div className={styles.timer}>
      <span className={styles.count}>{count}</span>
    </div>
  );
}

ChatGPT가 제안한 코드로 변경 후 코드 리뷰

더보기

이 코드는 기본적으로 정상적으로 작동할 수 있지만 몇 가지 개선점과 버그 가능성이 있습니다:

  1. 이벤트 처리 시 발생할 수 있는 문제: 현재 코드에서는 음수로 카운트가 내려갈 수 있습니다. 이에 대한 검증 또는 처리 방안이 필요할 수 있습니다.
  2. 시맨틱 요소 추가: <button> 태그에 aria-label을 추가하여 스크린 리더 사용자들에게 좀 더 이해하기 쉬운 정보를 제공할 수 있습니다.
  3. 코드 일관성 유지: 코드 가독성을 높이기 위해 변수나 함수명을 일관된 규칙에 따라 작성할 수 있습니다.
  4. 테스트 추가: 단위 테스트 또는 사용자 테스트를 추가하여 코드 변경에 대한 영향을 빠르게 파악할 수 있습니다.
  5. 상태 업데이트의 안정성 보장: useState에서 상태를 업데이트할 때 이전 상태를 기반으로 하는 경우, 함수형 업데이트를 사용하는 것이 좋습니다.
setCount(prevCount => prevCount + 1);

ChatGPT를 활용한 코드리뷰 정리

  • 종종 ChatGPT를 이용해 코드 리뷰를 받아보곤 하는데 이보단 좀 더 디테일한 코드리뷰를 해주는 것 같다.
  • 사소한 부분들도 짚어주며 버그 위험성과 그에 대한 개선점을 알려주기에 개인 프로젝트에 사용하면 좋을 것 같다.
  • 코드리뷰가 완벽하진 않으며 삭제된 파일, 이름만 변경된 파일 전부 코드 리뷰가 진행되는 불필요한 작업이 발생한다.
  • 무료가 아니기에 필요하다고 생각될때만 코드 리뷰를 받아보는 것도 좋을 것 같다.