S4 Unit 10. [deploy] CI/CD

나현·2022년 12월 7일
0

학습일지

목록 보기
49/53
post-thumbnail

💡 이번에 배운 내용

  • Section4. 사람과 기계가 모두 쉽고 빠르게 접근 가능한 Web App을 만들 수 있다.
  • Unit10. [deploy] CI/CD: 배포 자동화 개념에 대해 학습하고, 실습해본다.

느낀점

CI/CD의 개념에 대해 배우면서 깃헙 액션을 직접 실습해봤는데, 빌드-테스트-배포까지 git push와 동시에 이뤄지는 점이 참 신기했다. 이틀에 걸쳐 학습하면서 블로깅까지 하려니 쉽지 않았지만 이번에도 이렇게 해내고야 말았다. 그리고 proxy 실습도 해보았는데, webpack dev server보다는 http-proxy-middleware 라이브러리를 쓰는게 훨씬 편하다. 쉽지는 않지만 그래도 재미가 있으니 이렇게 공부할 수 있는 것 같다. 좀만 더 힘내자!


키워드

개발 프로세스, 소프트웨어 개발 생명주기 SDLC, 워터폴 waterfall 방식, 애자일 agile 방식, DevOps, CI/CD, Continuous Integration, Continuous Delivery, Continuous Deployment, CI/CD 파이프라인, Github Actions, YAML, yml, proxy, CORS, webpack dev server proxy, http proxy middleware, http-proxy-middleware


학습내용

Ch1. 개발 프로세스

개발 프로세스, 즉 소프트웨어 개발 프로세스 모델은 소프트웨어 개발 생명주기(SDLC, Software Develpment Life Cycle)을 기반으로 만들어졌습니다.

소프트웨어 개발 생명주기(SDLC, Software Develpment Life Cycle)

  • 요구분석:
    문제분석를 분석하고, 소프트웨어의 기능, 제약조건, 목표 등을 사용자와 함께 정의하는 단계.
    이를 토대로 개발 방법, 필요한 자원, 예산 예측 후 요구명세를 작성한다.

  • 설계 :
    정의한 기능을 실제로 수행하기 위해 설계하는 단계.
    시스템, 프로그램, UI(User Interface) 설계

    • 시스템 구조 설계: 시스템을 구성하는 내부 프로그램, 모듈 간의 관계와 구조를 설계
    • 프로그램 설계: 프로그램 내의 각 모듈에서의 처리 절차나 알고리즘을 설계
    • UI(User Interface) 설계: 사용자 인터페이스 설계
  • 구현 :
    설계 내용을 프로그래밍 언어를 사용해 실제 프로그램을 작성하는 단계. 즉, 개발 단계.

    • 구조화 프로그래밍 :
      순차구조, 선택구조, 반복구조의 세 가지 제어구조로 표현(반복문, 제어문 등 사용)
      구조가 명확하여 정확성 검증, 테스트, 유지보수가 쉽다.
    • 모듈러 프로그래밍 :
      프로그램을 여러 개의 작은 모듈로 나누어 계층 관계로 구성
      모듈별 개발,테스트,유지보수가 가능하며, 모듈의 재사용이 가능하다.
  • 테스트 :
    개발한 시스템이 요구사항을 만족하는지, 실행 결과가 예상한 결과와 정확하게 맞는지를 검사, 평가하는 단계

  • 배포 및 유지보수 :
    시스템이 인수되고 설치된 후(배포) 일어나는 모든 활동을 지칭한다.
    이후 일어나는 커스터마이징, 구현, 테스트 등 모두 이 단계에 포함된다.
    소프트웨어 생명주기에서 가장 긴 기간을 차지하는 단계.
    유지보수의 유형은 아래와 같다.

    • 수정형 유지보수 : 사용 중에 발견한 프로그램의 오류 수정 작업
    • 적응형 유지보수 : 시스템의 환경적 변화에 적응하기 위한 재조정 작업
    • 완전형 유지보수 : 시스템의 성능을 향상하기 위한 개선 작업
    • 예방형 유지보수 : 발생할 수 있는 변경 사항을 수용하기 위한 대비 작업

워터폴(Waterfall) 방식

위의 개발 프로세스를 폭포처럼 한 방향으로만 진행하는 개발 과정.
유지보수까지 끝나면 다시 처음의 단계로 돌아가 시작하는 것이 가장 기본적인 모델이다.

워터폴 방식의 특징은 다음과 같다.

  • 실제로 배포되어 유저에게 전달되는 시간이 오래 걸린다.
  • 어떤 버그나 수정 사항이 생기면 다시 처음으로 돌아가 수정한다.(일정과 비용 증가)
  • 출시 시점에 소프트웨어의 신뢰성 및 안정성을 보장 받기가 힘들며, 배포 직후에도 수많은 버그를 마주하게 될 가능성이 있다. 이를 보완하기 위해 테스트 단계에 다양한 테스트를 도입한다.
    아래는 대표적인 테스트들이다.
    • 시스템 테스트 :
      모든 모듈을 통합한 후 최종적으로 완성된 시스템이 요구사항을 만족하는지 확인한다.
      요구사항을 만족하지 않는다면 다시 요구분석 단계로 돌아가 새로 개발을 하기도 한다.
    • 알파 테스트 :
      완전히 개발된 시스템을 개발 현장에서 비공개로 테스트한다.
      주문형 제품의 경우 개발진과 클라이언트 사이에서 동의가 이루어질 때까지 수행한다.
      대기업의 경우 이 업무를 주로 하는 전문 QA팀이 존재한다.
    • 베타 테스트 :
      미리 선별된 유저들이 실제 사용하며 테스트한다.
      이 과정에서 에러나 버그가 발견되면 수정하는 식으로 진행된다.
  • 버그가 수정(patch)된 버전이 유저에게 즉각적으로 전달되기가 어려울 수 있다.
    다양한 테스트 방식을 도입했어도 모바일 어플의 경우 사용자가 항상 최신 상태로 업데이트를 해야 한다.

애자일(Agile) 방식

애자일(Agile) 방식은 위의 개발 프로세스 주기를 작은 단위로 반복하는 방식이다.
모던 개발 프로세스 중 하나로 각 주기를 ‘스프린트(sprint)' 라고 한다.

애자일 방식의 특징은 다음과 같다.

  • 요구사항이 변하는 것을 당연한 전제로 두고 있다.
  • 워터폴 방식보다 훨씬 효율적으로 개발에 착수할 수 있다.
    문제를 해결하고 다음 스프린트에 또 문제를 해결하기 때문이다.
  • 빠르게 문제를 해결해 하루에도 여러 번의 배포가 가능해진다.
  • SaaS(Software as a Service, 서비스형 소프트웨어)를 개발하는 데에 적합하다.

    SaaS란?
    클라우드 서비스의 한 방식으로, 브라우저에 접속하기만 해도 새 버전을 즉시 사용할 수 있는 서비스 방식이다. 애플리케이션부터 서버, 가상화, 스토리지, 네트워킹까지 전부 공급자 쪽에서 관리하기 때문에 고객이 제어하거나 관리할 부분이 거의 없다.
    따라서 사용자 업데이트에 거의 필요없어 하루에 여러 번의 배포도 가능하다.

워터폴 VS 애자일

워터폴애자일
장점- 프로세스가 길고 순서가 잡혀 있어 팀원들이 따르기 쉽다.
- 새로운 프로젝트를 안정적으로 시작할 수 있다.
- 요구 사항이 확정되어 있으므로 프로젝트를 실행하기 수월하다.
- 개발 목표를 자주 변경하지 않아도 된다.
- 예상 결과와 리스크를 통제하기 훨씬 쉽다.
- 빠르면서 유연한 개발 과정
- 빠르게 결함을 인지하고 수정 가능하여 품질 향상에 유리하다.
- 개발 과정 중에 신속히 제품 변경이 가능하다.
단점- 앞 단계가 완성되지 않으면 다음 단계로 넘어갈 수 없다.
- 개발 속도가 느리고 유연성이 떨어진다.
- 테스팅 단계에 이르러 이슈가 발견되는 경우가 많다.
- 개발 요구 사항의 범위 변경이 자유롭지 못하다.
- 스프린트에 대해 익숙한 팀원이 있어야 한다.(스크럼 마스터 등)
- 고객이 수많은 변경 사항을 검토해야 한다.
- 팀원의 성향에 따라 애자일 방식으로 진행할 시 문제가 생길 수 있다.

각 방식마다 장단점이 명확하므로 적합한 상황을 잘 판단하여 채택해야 한다.

워터폴 방식에 적합한 경우

  • 높은 예측 가능성과, 순차적인 프로젝트 타임라인, 사전 확정 예산이 필요한 경우
  • 프로젝트 팀의 경험이 적은 경우
  • 요구사항이 간단하거나, 타임라인이 긴 프로젝트를 수행하는 경우
  • 개발상의 변경이나 리스크에 덜 민감한 기업 및 팀
  • 제한적인 시간과 자원 탓에 협업이 자유롭지 못한 고객을 둔 기업 및 팀

애자일 방식에 적합한 경우

  • 고성능 소프트웨어 개발을 진행할 경우
  • 고품질의 결과물과 지속적인 개선에 초점을 맞출 경우
  • 프로젝트가 완벽히 수행될 때까지 결과물을 기다리는 것보다 결과물에 대해 빠른 피드백이 필요한 경우
  • 크고 복잡한 회사들이 프로세스를 간소화해 변화에 더욱 신속하게 대응하고자 할 때
  • 고객 및 외부 관계자와 정기적으로 긴밀한 협업을 수행하는 프로젝트 팀

Ch2. DevOps

DevOps란?

DevOps는 개발(Development)과 운영(Operations)의 합성어로 기존 소프트웨어 개발 업무와 IT운영 업무를 통합한 일종의 포지션이다. 소프트웨어를 자주, 빨리 그리고 안전하게 배포하는 것을 목표로 하며, 애자일 개발 프로세스를 기반으로 한다.

주 특징은 다음과 같다.

  • 개발에서 운영까지 하나의 통합된 프로세스로 묶어 사용하는 툴과 시스템을 표준화한다.
  • 코드 통합, 테스트, 배포 과정을 자동화하고 지속한다.(CI/CD)

DevOps 문화

DevOps는 일종의 개발 문화로 만약 서비스가 중단될 경우 '누구든지 문제점을 진단하고 시스템을 복구하여 운영할 수 있는 절차를 알고 있어야 한다'가 주 핵심이다. 이를 위한 기술과 지식이 제공되기 위해서 훈련과 효과적인 협업체계를 마련하는 것이 매우 중요하나 실제 실무에서는 업무의 분리를 위해 DevOps팀이 따로 있을 가능성이 크다.

Ch3. CI/CD

CI/CD란

CI(Continuous Integration)는 지속적인 통합이란 뜻으로 개발 자동화 프로세스를 의미한다.
앱의 코드가 변경되었을 때 정기적으로 빌드 및 테스트되어 공유 리포지토리에 통합되는 것을 자동으로 처리하게 되면, 개발자들이 동시에 작업할 때 서로 충돌할 수 있는 문제를 해결할 수 있다.
CD는 지속적인 서비스 제공(Continuous Delivery)과 지속적인 배포(Continuous Deployment)를 의미한다. 두 가지 의미 모두 자동화를 의미하나 이 자동화가 얼마나 지속적으로 진행되는지에 초점이 맞추어져 있다.
정리하자면 CI/CD는 빌드, 테스트를 자동화하여 지속적으로 배포하는 것을 의미한다.

CI: 지속적 통합

CI는 보통 Code - Build - Test 단계를 자동화하는 것을 의미한다.

  • Code : 개발자가 코드를 원격 코드 저장소 (Ex. github repository)에 push
  • Build : 원격 코드 저장소로부터 코드를 가져와 유닛 테스트 후 빌드하
  • Test : 코드 빌드의 결과물이 다른 컴포넌트와 잘 통합되는 지 확인

이러한 CI 과정을 통해 버그를 일찍 발견할 수 있고, 테스트가 완료된 코드를 빠르게 전달할 수 있다. 또한 지속적인 배포가 가능해진다.
위의 CI를 실현하기 위해 다음과 같은 사항을 신경써야 한다.

  • 모든 코드 변화를 하나의 리포지토리에서 관리한다.
    모든 개발팀이 코드의 변화를 확인할 수 있기 때문에, 투명하게 문제점을 파악할 수 있다.
  • 잦은 풀 리퀘스트(pull request)와 머지(merge)로 코드를 자주 통합한다.
    이 때, 기본적인 테스트도 작동시킨다.

이렇게 지속적 통합을 통해 보안 이슈, 에러를 빠르게 확인하고 배포할 수 있다.

CD: 지속적 배포

CD는 보통 Release - Deploy - Operate 단계를 의미한다.

  • Release : 배포 가능한 소프트웨어 패키지를 작성
  • Deploy : 프로비저닝을 실행하고 서비스를 사용자에게 노출(실질적 배포)
  • Operate : 서비스 현황을 파악하고 생길 수 있는 문제를 감지

CD에는 테스트 자동화와 코드 배포 자동화도 포함된다.
이 프로세스를 완료하면 준비가 완료된 빌드를 자동으로 배포할 수 있다.

CI/CD의 영역

최근에는 클라우드 기술 발전과 맞물려 지속적 통합과 지속적 배포가 빠른 속도로 진행되면서 언급했던 CI/CD를 하나로 묶어서 진행하는 경우도 증가하고 있다.
즉 CI/CD는 지속적 통합, 지속적 제공, 지속적 배포 모두를 의미할 수도 있다.
요즘은 고객의 피드백을 빨리 받고 서비스를 중단하지 않기 위해 릴리즈만 잘 기록해두고 바로바로 배포하는 사례가 증가하고 있다.

그리고 또한 이 CI/CD를 하나로 묶어서 자동화하는 것을 CI/CD 파이프라인이라고 한다.

CI/CD 파이프라인 구성과 단계

배포에서 파이프라인(Pipeline)이란 소스 코드의 관리부터 실제 서비스로의 배포 과정을 뜻한다.

CI/CD 파이프라인의 구성 요소는 다음과 같다.

  • 빌드 : 소프트웨어 컴파일
  • 테스트 : 호환성 및 오류 검사
  • 릴리스 : 버전 제어 저장소의 애플리케이션 업데이트
  • 배포 : 개발에서 프로덕션 환경으로의 변환
  • 규정 준수 및 유효성 검사

이 파이프라인은 전체 배포 과정을 여러 단계(Stages)로 분리하고, 각 단계는 파이프라인 안에서 순차적으로 실행된다. 각 단계는 주어진 작업(Actions)들을 수행한다.

대표적으로 세 가지 단계가 있다.

  • Source:
    원격 저장소에 관리되고 있는 소스 코드에 변경 사항이 일어날 경우 이를 다음 단계로 전달한다.
  • Build :
    Source 단계에서 전달받은 코드를 컴파일, 빌드, 테스트하여 가공한다.
    그리고 이 결과물을 다음 단계로 전달한다.
  • Deploy :
    Build 단계로부터 전달받은 결과물을 실제 서비스에 반영한다.

파이프라인의 단계는 필요에 따라 더 세분화되거나 간소화될 수 있으며 업체에 따라 용어를 미묘하게 다르게 사용하기도 한다.


Ch4. Github Actions

Github Actions이란?

GitHub Actions는 Github가 빌드, 테스트 및 배포 파이프라인을 자동화할 수 있도록 제공하는 CI/CD 플랫폼이다.
GitHub Actions는 레포지토리에서 Pull Request 나 push 를 할 때 자동화 프로세스가 이뤄지도록 GitHub 작업 워크플로(Workflow)를 구성할 수 있다.
워크플로는 하나 이상의 작업이 실행되는 자동화 프로세스로 .yml (혹은 .yaml) 파일에 의해 구성된다.
테스트, 배포 등 기능에 따라 여러개의 워크플로도 만들 수 있으며 생성된 워크플로는 .github/workflows 디렉토리 안에 위치하게 된다.

private 레포지토리는 용량과 시간이 제한되어있으며, public 레포지토리는 무료로 사용 가능하다.

Github Actions 공식문서 :
🔗 https://docs.github.com/en/actions

YAML 이란?

YAML(Yet Another Markup Language 혹은 YAML ain’t markup language)의 약자로, 사람이 읽을 수 있는 데이터 직렬화 언어를 의미한다.
파일로 작성시 .yaml 혹은 .yml 확장자를 가진다.
YAML은 다른 프로그래밍 언어와 함께 사용할 수도 있으며 유연성과 접근성이 좋아 자동화 프로세스를 생성하는 데에도 사용된다.

YAML 문법

  • 주석, 문서의 시작과 끝
    • # : 주석
    • --- : 문서의 시작 (선택사항)
    • ... : 문서의 끝 (선택사항)
--- #문서 시작
#내용
... #문서 끝
  • 들여쓰기:
    기본적으로 2칸 또는 4칸으로 보통 2칸씩 들여쓴다. 탭 키가 아닌 스페이스 키를 사용해야 한다.

  • 기본 표현:
    key: value: 다음에는 무조건 공백이 와야 한다.

  • 자료형:
    int, string, boolean, 리스트, 매핑을 지원한다.
    여기서 int와 string 타입은 스칼라(Scalar), 배열 혹은 리스트는 시퀀스(Sequence)라고 한다.
    key-value 쌍 및 hash, dictionary는 매핑이다.

#int(숫자)
int_type: 1

#string(문자열)
string_type: "1"

#blooean(참/거짓)
boolean_true_type: true
boolean_false_type: false

#리스트(배열 형태)
# JSON으로 치면 "fruits" : [apple, banana]처럼 나타낼 수 있다.
bucket:
  name: kim's fruits bucket
  fruits: 
    - docker
    - kubernetes
  
  • 객체:
    key 작성 후 두 칸을 들여쓰고 key-value 형태로 작성을 해주거나,
    key를 작성 후 중괄호({})로 한 번 묶어서 안에 key-value 형태로 작성한다.
#방법1
key: 
  key: value
  key: value

#방법2
key: {
  key: value,
  key: value
}
  • 줄바꿈 표현(|), 줄바꿈 무시 표현(>)
# 줄바꿈 표현 |
contents: |
  The first line.
  The second line.
  
# JSON으로는 "contents": "The first line.\nThe second line.\n"로 표시할 수 있다.

# 줄바꿈 무시 표현 >
contents: >
  The first line
  The second line.
# JSON으로는 "contents": "The first line The second line."로 표시할 수 있다.
  • 문자열 따옴표
    key-value 쌍에서 value에 :가 들어간 경우 따옴표로 감싸야 한다.
# 올바른 사용
windows_drive: "d:"
windows_drive: 'd:'

# error
windows_drive: d:

Ch5. Proxy

CORS 정책

CORS는 교차 출처 리소스 공유로 출처가 다른 자원에 접근할수 있도록 하는 정책이다.
지난 시간에 배웠기 때문에 링크로만 첨부하고 자세한 설명은 생략한다.👍

🔗 S2 Unit 10. Web Server 기초

이러한 CORS 정책으로 인해 개발단계에서 다른 출처로 요청할 때 모든 도메인이 아니라 특정 도메인을 허용하도록 구현해야 한다.
이 경우 보통 프론트엔드 개발자가 백엔드 개발자에게 프론트엔드 개발 서버 도메인을 허용해달라고 요청하며, 백엔드 개발자는 응답 헤더에 필요한 값들을 담아서 전달을 한다.

이 때 CORS 정책을 우회할 수 있는데 이렇게 우회할 때 사용하는 것이 Proxy이다.
한 마디로 우회하여 브라우저를 같은 출처인 것처럼 속이는 것이다.

예를 들어, React 앱에서 브라우저를 통해 API를 요청하는 상황에서 proxy를 사용하면 다음과 같은 과정을 거친다.

  • proxy를 사용하면 백엔드 서버로 요청을 우회하여 보낸다.
  • 백엔드 서버는 응답을 React 앱으로 보낸다.
  • React 앱은 받은 응답을 백엔드 서버 대신 브라우저에게 전달한다.
  • 이렇게 되면 출처가 같아져서 브라우저는 이를 허용한다.

Proxy 사용

Proxy를 사용하기 위해 Webpack Dev Server에서 제공하는 proxy 기능을 사용하거나,
http-proxy-middleware 라이브러리를 사용할 수 있다.

1) webpack dev server proxy 사용
웹팩 개발서버의 proxy 설정은 원래 웹팩 설정을 통해서 적용하지만, CRA(create-react-app 명령어)를 통해 만든 리액트 프로젝트는 package.json 에서 "proxy" 값을 전역으로 설정할 수 있다. 보통 맨 밑에 작성한다.
(다만 여러 도메인을 사용할 경우, CRA로 만든 프로젝트는 "proxy": {}를 사용할 수 없으니 주의한다.)

...
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
	"proxy" : "우회할 API 서버 주소" 
}

그리고 기존의 요청 부분(fetch, 혹은 axios)에서 도메인 부분을 제거한다.

//변경 전
export async function getAllfetch() {
    const response = await fetch('우회할 API 서버 주소/필요한 params');
    .then(() => {
			...
		})
}

//변경 후
export async function getAllfetch() {
    const response = await fetch('/필요한 params');
    .then(() => {
			...
		})
}

2) http-proxy-middleware 라이브러리 사용
http-proxy-middleware 라이브러리를 사용하기 위해 먼저
npm install http-proxy-middleware --save
로 설치한다.

설치하면 라이브러리가 자동으로 src파일의 setupProxy.js파일을 찾아 proxy를 실행한다.
그러므로 반드시 React App의 src 파일에 setupProxy.js 라는 이름으로 파일을 생성하도록 한다.
setupProxy.js는 다음과 같이 작성한다.

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  //도메인이 2개 이상인 경우 app.use를 도메인 갯수만큼 다음과 같이 작성한다.
  app.use(
    '/api1', //proxy가 필요한 path prameter
    createProxyMiddleware({
      target: 'http://localhost:3001', //우회할 API 서버 주소
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정
    })
  );
  app.use(
    '/api2', //proxy가 필요한 path prameter
    createProxyMiddleware({
      target: 'http://localhost:3002', //우회할 API 서버 주소
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정
    })
  );  
};

도메인이 여러 개라면 위 코드를 다음처럼 하나로 합칠 수도 있다.

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    ['/api1','/api2','/api3'], //순서를 잘 맞춰야 한다.
    createProxyMiddleware({
      target: 'http://localhost:3001', //우회할 'api1' API 서버 주소
      changeOrigin: true,
      router:{ //나머지 경로를 배열 순서대로 작성한다.
        '/api2': "http://localhost:3002",
        '/api3': "http://localhost:3003"
      }
    })
  );
};

그리고 webpack dev server proxy에서 했던 것처럼 기존의 요청 부분(fetch, 혹은 axios)의 도메인을 제거한다.


profile
프론트엔드 개발자 NH입니다. 시리즈로 보시면 더 쉽게 여러 글들을 볼 수 있습니다!

0개의 댓글