약 한 달 동안(23.12.11~ 24.01.12) “클라우드 기반 MSA 이커머스 서비스 새싹농장“ 이라는 주제로 팀 프로젝트를 진행했다. 본문에서는 가볍게 회고를 해보려고 한다.
 

 

 

프로젝트 요약

 

 
프로젝트의 주된 목적은 Mono Repo로 관리하는 폴리글랏 마이크로 서비스를 CI/CD 파이프라인을 구축해 AWS EKS(AWS 완전 관리형 쿠버네티스) 상에 배포하고 운영하는 것이었다. 추가로 MSA 특성인 유지보수 용이성을 경험해보기 위해 처음부터 개발하지 않고 서비스 및 기술스택과 파일구조가 잡힌 레포지토리를 가져와서 코드를 수정하는 작업을 진행했다. 그 결과 위 도표와 같은 구조로 서비스를 개발했다. CI는 Github Action으로, CD는 Argo CD를 사용해서 AWS EKS 클러스터 상에 배포했다. 서비스 기능은 심플하다. 회원가입, 로그인, 상품 정보 노출, 장바구니에 상품 담기가 있다.
 
나의 역할은 서비스 개발과 CI를 맡았고 전체 아키텍처를 설계였다. 처음으로 FastAPI, Spring Boot, Express를 사용해 폴리글랏으로 백엔드를 개발했고 CI/CD 파이프라인 구축과 AWS EKS에 배포까지 Software Life Cycle을 경험했다. 아쉬운 점이 있다면 k8s 환경에 서비스를 배포했음에도 EKS Auto Scaling을 경험해 보지 못했다는 점이다. EKS AutoScailing은 추가적으로 학습하고 구축해 볼 계획이다. 
 

 


 

느낀점

기본 적인 것에 대하여 : 지각하지 말아다오

 

잦은 늦잠.. 안됩니다..

 

나는 규칙 준수를 중요하게 생각하는 편이다. 그러나 이를 중요시하지 않는 사람들이 도처에 암약하고 있다. 우리 팀원도 예외는 아니었다. 지각을 꾸준히 하는 팀원이 둘이나 있었다. 이유 없이 늦지 말아 달라고 당부하는 일이 계속되자 심적으로 너무 괴로웠지만 그래도 늦지 않을 때까지 포기하지 않았다. 심지어 새벽 2~3시가 넘어서 취침하지말고 일찍 주무시라는 부탁까지 했다. 결과적으로 개선이 되었고 지각하는 사례는 줄어들었다. 사소한 행동들이 쌓여서 신뢰에 영향을 준다고 생각한다. 동료에게 신뢰를 쌓기 위해서는 기본적인 약속부터 잘 지켜야 한다는 것을 몸소 느꼈다.
 

 


 

서로의 열정 차이: 극복하기 위해 솔선수범 했던 기억

 

Confluence 사용 및 Jira 사용에 대한 안내

 

 

팀장을 맡았다. 그래서 더 적극적으로 찾아보고 공유했다. 특히 개발자로 일할 때 팀에서 사용했던 애자일 방법론을 적용하기 위해 노력했다. 협업도구로는 칸반보드를 사용하기 위해 Jira를 채택했고 프로젝트 문서 관리를 위해 Confluence라는 제품을 사용했다. 내가 먼저 사용 방법을 학습하고 팀원에게 공유했다. 도구의 필요성을 설명하고 각 사용 방법을 공유했다. Jira를 사용하면서 업무 진행도를 시각화해서 볼 수있게 했다. Confluence를 사용하면서 프로젝트 문서를 아카이빙할 수 있었다. 무료 노션 팀 페이지 사용에서 제한되던 텍스트 블럭 수를 신경쓰지 않고 마음 껏 문서를 작성하는 것은 덤이었다. 스크럼 회의, 코어타임, 일주일 단위 스프린트를 도입했다. 작은 목표를 빠르게 이루어 큰 목표를 달성하기 위함이었다. 작업 상황을 시시각각으로 공유하고 서로 피드백을 주도록 팀원들을 설득했다. 
 
물론 처음부터 모든 것이 뜻대로 되진 않았다. 업계 표준이라 생각했던 협업도구는 팀원들에게 낯설고 사용하기 어려운 걸림돌이었고 작업을 위한 작업으로 여겨졌다. 애자일 방법에 대해 설명하고 실천하기로 이야기를 했으나 받아들이는 방법은 각자 달랐다. 나는 주도적으로 일하고 최대한 의사소통을 많이 하고 변화에 빠르게 대응할 수 있는 마음가짐에 중점을 두었으나 팀원들은 수동적이었고 워터폴 방식으로 작업했다. 한 번은 워터폴방식으로 작업하는 팀원과의 마찰이 있었다. AWS 인프라를 담당하는 조원에게 진행도를 물었으나 개발을 맡은 내가 개발을 마칠 때까지 기다리고 있었다고 했다. 나는 기본적인 crud가 되는 Todo List 서비스라도 배포해보라고 권했다. 이처럼 작업 진행에 간극이 생길 수 있기 때문에 칸반보드를 이용해 작업 상황을 파악하고 짧은 주기로 동료와 진행사항에 대해 이야기하려고 했다. 보다 적극적인 의사소통을 해야겠다!   
 
 

 

 

설득하기: 한 번 해보시고 판단해 주시면 안 될까요?

프로젝트 내내 서비스 개발, 협업 툴 도입, 방법론 등을 IT 프로젝트 경험이 없는 조원들을 설득하는 과정의 연속이었다. 왜 해야하는지 설명하고, 우선 해보고 판단해달라고 부탁했으나 늘 뚜렷한 근거없는 반대에 부딪혔다. 가령, 개발에서는 폴리글랏 마이크로 서비스 개발, Mono Repo 도입에 대해서 개발을 할 줄 모른다며 반대했고, 인프라 구성에서는 AWS 워크샵에서 안내해주는대로 EKS 클러스터를 구성하는게 아닌 차례차례 EKS의 모든 요소를 직접 구성해 EKS 클러스터 구축하자는 의견을 내면 팀원들은 어렵고 복잡하다는 이유에서 반대했던 일이 있었다. 그럼에도 불구하고 십 분이라도 사용하는 기술을 이해하기 위해서는 현업과 비슷하게 서비스를 개발해야한다는 생각이 들었고 계속 제안했다. 나는 꾸준히 말했다. “그래서 말인데, 한 번 해보시고 판단해주시면 안될까요?” 
 
 

 

 

갑작스러운 팀 규모 축소와 프로젝트 규모 조정

 

5명이었는데 2명이 되었다..

 

다섯 명의 팀원이 있었으나 결과적으로 2명이 진행했다. 처음부터 취업으로 빠진 팀원 A, 중간에 하차한 B와 C가 있었다. 그리고 팀원은 D만 남았다. 이 과정에서 너무나 많은 마음 에너지를 소모해버렸다. 하차하는 조원들은 일단 하는 데까지 뭐라도 해보겠다는 태도였다. 나는 이들 각자가 자발적으로 떠나기 전까지 할 수 있는 작업을 제시하고 어떻게 인수인계할 것인지 말해주길 바랐지만 그들은 두루뭉술하게 최대한 돕겠다는 말만 했다. 구체적으로 계획을 말해달라고 했으나 시키는 대로 하겠다는 대답을 받았다. 혹은 그마저도 말하지 않는 사람도 있었다. 이런 태도를 보고 남아서 끝까지 마무리 짓는 사람은 어찌 되든 상관없다는 태도로 대하는 느낌을 받았다. 결자해지. 자신이 저지른 일은 자신이 책임져야 한다. 반면교사 사례가 또 하나 늘었다.

 

모쪼록 팀원의 부재로 중간에 프로젝트 규모와 팀 내 역할을 조정해야 했다. 첫 계획은 Prometheus와 Grapana와 같은 o11y를 서비스메시를 활용해 적용해 보려고 했다. 또한 백엔드 마이크로 서비스 간에 통신 로직을 추가할 예정이었다. 그러나 인력이 갑자기 감축되었다. 가장 먼저 고민했던 것은 작업의 우선순위였다. 그 결과, 먼저 서비스를 개발하고 AWS EKS 환경에서 실행하는 작업을 했다. 그 후 CI/CD 파이프라인을 구축해서 코드 수정부터 배포까지 자동화할 수 있었다.  

 
 

 

 

질문방어: 설명하면서 학습도 하는 방법

 

어둠의 마법 방어술이 아닌 질문 방어술을 연마했다

 


 조원들은 대부분의 IT용어를 알고 있지 못했다. MSA와 Monolithic, 애자일 방법론, 스크럼 미팅, 형상관리, 프로그래밍 언어, 서비스메쉬와 이를 설명하기 위한 사이드카 패턴 등 수없이 많은 개념을 생소하게 받아들였다. 나는 설명하는 일을 좋아한다(마치 해리포터에 나오는 어둠의 마법 방어술처럼 "질문방어"라고 생각했기 때문이었을까?). 어떤 개념을 이해하기 쉽고 정확하게 설명하고 싶은 생각에 더욱 열심히 레퍼런스를 찾아서 설명했다. 기억에 남는 일은 VPC 내부에 있는 백엔드 서비스에서 외부의 MongoDB Atlas와 어떻게 통신하는지 묻는 팀원에게 화이트보드에 그림을 그려가며 설명했던 일이다 ①. 항상 설명 끝엔 백 번 이론에 대해 말하는 것보단 직접 해봐야 이해할 수 있다고 습관처럼 말을 했었는데 스스로도 이 말을 명심하고 지켜나갈 것이다. 동료에게 지식을 설명하기 위해 학습한 뒤 설명하고 궁금증 방어는 잘 잊혀지지 않는 좋은 공부 방법이라고 생각한다. 특히 스터디 후 짧은 발표를 해보는 일은 더욱 좋은 학습법이라고 생각한다.  

 

 

 

 

회고를 마치며 : 솔선수범, 긍정, 행동

 

북극 가까이에 있는 북극성을 향해 나아가듯이 목표를 떠올리며 방향을 잃지 말자

 

프로젝트를 마무리 지었다. 기술 구현에 대한 어려움보단 IT에 대한 배경 지식이 없는 동료를 공감시키는 점이 가장 어려웠다. 그래도 꿋꿋하게 내가 먼저 “클라우드 기반 MSA 이커머스 서비스”라는 주제를 찾아와서 공유했고 애자일 방식과 Jira, Confluence를 사용하게끔 노력했다. Gitops 전략을 사용하기 위해 CD를 담당한 팀원에게 GitOps Repo를 운영할 수 있도록 부탁했고 Git 사용법을 알려주었다. 또 다른 시련은 갑자기 인원이 5명에서 2명으로 줄었던 일이다. 좌절하기보다는 둘이서 할 수 있는 작업 우선순위를 세웠고 프로젝트를 마무리했다.

 

지금까지 살면서 곤란했던 순간이 이렇게 많았던 프로젝트는 처음이었다. 그 이유는 태도 및 마음가짐 때문이라고 생각한다. 어떻게든 해내야겠다는 생각으로 문제에 접근하는 사람과 그렇지 않은 사람 사이에서 마찰이 잦았다. 내가 어떤 사항을 제안할 때 어렵다고 거절하거나 모른다는 이유로 고민하는 모습조차 보이지 않고 멍하니 있는 조원을 볼 때면 깊은 고뇌에 빠지기도 했다. 문득 무력감의 수렁에서 빠져나가야 겠다는 생각이 들었고 솔선수범해보자는 판단을 했다. 내가 먼저 좋은 방법을 찾아 제안하고 적극적으로 피드백을 주고받을 수 있는 분위기를 만들기 위해 노력했고 선순환을 만들 수 있었다. 어떤 상황에서도 부정적으로 생각해서 바뀌는 것은 없다. 긍정적인 자세로 목표를 완수할 수 있는 방법을 생각해보자.     

 

 

 

 

 

 

추가 설명 

①: AWS EKS에서는 Private Subnet에 배치된 node가 외부와 통신할 때 Nat Gateway를 사용하는데, 이 때 AWS에서 Nat Gateway의 Public IP를 할당하고 관리한다. 해당 Public IP를 MongoDB 접근가능 IP로 추가하면 VPC 내부의 백엔드 서비스와 통신할 수 있다. 
 

'프로젝트' 카테고리의 다른 글

이커머스 클론 프로젝트 후기  (0) 2023.06.08

Redux toolkit +TypeScript 클론프로젝트

 

 

***라는 회사 입사 과제 였으나 MVP를 제시간에 구현할 수 없어서 포기했다.

 

우선 오랜만에 React 프로젝트를 진행했기 때문에 예상치 못한 에러가 많이 발생했다. 

 

그리고 TypeScript 기반 프로젝트는 진행해 본 경험이 없었기 때문에 이부분에서 애를 많이 먹었다.

 

문제 1

tsconfig.json 절대 경로 문제

 

baseUrl은 보통 프로젝트 루트 디렉토리를 뜻하는 “.”이다. “./src” 등으로 수정할 수 있다. paths는 baseUrl 부터 시작해서 설정할 수 있다.

  

CRA 내부의 Webpack 설정 때문에 tsconfig를 변경한 내용이 적용되지 않고 초기에 생성된 tsconfig 설정으로 돌아가기 때문에 절대 경로를 인식하지 못한다. craco 라는 라이브러리를 이용해 해결했다. 

 

https://tesseractjh.tistory.com/232

 

위 블로그를 보고 해결했다.  이문제로 거의 하루를 썼다.

 

문제1.1

절대 경로를 사용할 수 있었으나 dir이름/index.ts 와 같은 파일에 접근하려면 절대경로를 “.src/dir” 까지만 써야한다. “src/dir/*”까지 쓰면 dir의 자식 dir 넘어서 참조할 경우 쓸 수 있다. Uikit dir을 만들고 UiKit/index.ts에 접근하려고 했을 때 생겼던 문제다.

 

 

문제 2. 

Prettier 적용 안됨 

default formatter와 format On Save에 관한 이야기만 있었다. 그러나 Ts로 작업 시엔 적용 되지 않았다.

setting.json 파일을 열어서 default formatter을 바꿔줘야한다.

 

https://velog.io/@shelly/tsx-prettier

 

해당 블로그를 보고 해결했다.

 

문제3. 

 

ts for Redux 

 

ts는 인자와 변수, props 등 전부 타입을 정해줘야한다. 그런데 모르는 입장에선 알기가 어렵다.

 

더 공부할 것

  1. TypedUseSelectorHook, RootState, AppDispatch
  2. useSelector<RootState, Product[]> 와 같이 타입이 2개나 들어가는 것이 무엇인지
  3. React.FC와 JSX.Element 등 차이
  4. interface 와 type 차이
  5. 화살표함수, func 함수 어느 때 쓰면 효과적인지
  6. 프로젝트 dir 구조 템플릿, 과연 폴더마다 index.ts 를 만들어야하는가? 손가락이 너무 아프다
  7. useCounter()와 같이 프레젠테이셔널 컴포넌트를 분리해서 hooks/~ 에 넣어야할지
  8. Grid 구현하기 react-virtualized ? 추천 하더라 어떤 미디엄 블로거
  9. 가짜 json 서버 만들기 , 1만자 까지만 쓸 수 있다. 'https://my-json-server.typicode.com/aingface/fake-ecommerce-data/productData'
  10. Lazy 로딩 처리하기 
  11. css 효과적으로 적용하기

 

 

잊어버렸었던 꼭 필요한 기법들

 

  1. interface에 올지 안올지 모르는것은 interface?
  2. width:100% 와 vw100 차이
  3. npm i 라이브러리 설치 + @types/라이브러리명 설치 둘다 필요
  4. box-sizing: border-box ,부모 요소에 box-sizing: border-box 스타일을 추가하여 패딩 값을 포함하여 요소의 전체 크기를 계산하도록 설정

 5. props 전달 시 {props : type }:interfaceProps 과 같이 입력

등등..

 

 

 

 

프로젝트 시작시 실행 순서

 

  1. Redux + TypeScript template :npx create-react-app my-app --template redux-typescript
  2. 프리티어, EsLint 설치, 적용 
  3. 파일 구조  
  4. 리덕스 적용
  5. 필요에 따라 react-router-dom 적용 
  6. 리덕스 미들웨어는 필요에 따라

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고

 

 

 

 

 

더 공부할것 2번 예시

const products = useSelector<RootState, Product[]>((state) => state.products.productList); 

 

이 코드에서 useSelector 훅에 타입이 왜 2개나 들어가?

 

 

ChatGPT

 

 

 

useSelector 훅은 제네릭(generic) 타입을 사용하여 사용자가 상태(state)와 반환하는 값의 타입을 명시할 수 있습니다. 제네릭 타입은 useSelector<타입>(selector) 형식으로 사용됩니다.

첫 번째 제네릭 타입은 RootState입니다. RootState는 전체 Redux 스토어의 상태 타입을 나타내며, 일반적으로 애플리케이션의 루트 리듀서에서 내보내는 상태의 타입을 지칭합니다. RootState는 리덕스 스토어가 생성될 때 combineReducers 함수에 전달한 객체의 각 리듀서의 상태 타입을 포함합니다.

두 번째 제네릭 타입은 반환하는 값의 타입을 나타냅니다. state.products.productList는 products 배열을 나타내는 상태의 경로입니다. 따라서 해당 경로에서 반환되는 값의 타입을 지정해야 합니다. 위 예시 코드에서는 Product[]로 지정하여 products 변수의 타입을 Product 배열로 설정하고 있습니다.

따라서 useSelector<RootState, Product[]>은 RootState를 첫 번째 제네릭 타입으로 지정하고, 반환되는 값의 타입을 Product 배열(Product[])로 지정하는 것을 의미합니다.

 

 

 

 

 

 

 

github action은 소프트웨어 workflow를 자동화할 수 있도록 도와주는 도구 라고한다.

업데이트가 생기면 자동으로 프로젝트를 배포하기 위해 사용했다.

 

Github Secrets 만들기

보안 취약점을 방어하기 위해 사용한다. 나는 노드 버전과 깃허브 Personal Access Token을 Secrets에 추가했다.

 

Github Secrets 경로

해당 Repogitory > setttings > 왼쪽 메뉴 Secrity > Secrets and variables > Actions 

 

GITHUB_TOKEN은 어디서 만들까? 

github actions의 workflow를 업데이트하기위해 personal access token이 필요하다.

깃허브 우측 상단 프로필 사진 > Settings > Developer Settings > Personal access tokens > Tokens > Generate new token을 통해 만들 수 있다. 

 

.github/workflow 폴더에 ph-pages.yml 만들기 

우선 .github/workflow 폴더를 만들자. 그리고 그 안에 ph-pages.yml 파일을 만들고 작업흐름을 정의해야한다.

더 설명하자면 .github/workflows 폴더 안에 있는 YAML 에 정의된 GitHub Actions 작업 흐름대로 자동화된 빌드, 테스트 및 배포 작업을 수행할 수 있기 때문이다. Repository에 push 될 때 마다 workflow가 실행된다.

 

Github Action Core 개념 

  • Github Action을 이해하기 위해서 알아야 하는 개념은 Workflow, Event, Job, Step, Action, Runner 등이 있음
  • 1) Workflow
    • 여러 Job으로 구성되고, Event에 의해 트리거될 수 있는 자동화된 프로세스
    • 최상위 개념
    • Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장됨
  • 2) Event
    • Workflow를 Trigger(실행)하는 특정 활동이나 규칙
    • 예를 들어 다음과 같은 상황을 사용할 수 있음
      • 특정 브랜치로 Push하거나
      • 특정 브랜치로 Pull Request하거나
      • 특정 시간대에 반복(Cron)
      • Webhook을 사용해 외부 이벤트를 통해 실행
    • 자세한 내용은 Events that trigger workflows 참고
  • 3) Job
    • Job은 여러 Step으로 구성되고, 가상 환경의 인스턴스에서 실행됨
    • 다른 Job에 의존 관계를 가질 수 있고, 독립적으로 병렬 실행도 가능함
  • 4) Step
    • Task들의 집합으로, 커맨드를 날리거나 action을 실행할 수 있음
  • 5) Action
    • Workflow의 가장 작은 블럭(smallest portable building block)
    • Job을 만들기 위해 Step들을 연결할 수 있음
    • 재사용이 가능한 컴포넌트
    • 개인적으로 만든 Action을 사용할 수도 있고, Marketplace에 있는 공용 Action을 사용할 수도 있음
  • 6) Runner
    • Gitbub Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
    • Github에서 호스팅해주는 Github-hosted runner와 직접 호스팅하는 Self-hosted runner로 나뉨
    • Github-hosted runner는 Azure의 Standard_DS2_v2로 vCPU 2, 메모리 7GB, 임시 스토리지 14GB
  • 7) with: Action에 값을 전달할 수 있다. ${{ secrets.XXX }}라는 값으로 GitHub의 Secrets 값을 가져올 수 있다.

       

 

 

        

 

나는 아래와 같이 정의 했다. 

 

# workflow의 이름
name: Build and Deploy

# Workflow를 동작시키는 trigger
on:
  push:
    branches:
      - "main"

jobs:
  #실행할 job deploy 뿐만 아니라 test 등과 같이 다양한 job이 있다.
  deploy:
  	# job을 사용하는 환경
    runs-on: ubuntu-latest
    steps:
      # GitHub Actions는 프로젝트를 리눅스 환경에 checkout하고 나서 실행한다.
      # 누군가 만들어 놓은 Action을 사용할 때는 uses라는 키워드를 사용해야 한다.
      - uses: actions/checkout@v2
	  # job의 이름
      - name: Use Node.js
        uses: actions/setup-node@v2
		#GitHub의 Secrets 값인 NODE_VERSION을 가져온다.
        with:
          node-version: ${{ secrets.NODE_VERSION }}

 	  # 해당 프로젝트의 node_modules가 변했는지 안 변했는지를 이용해서
      # 모듈 변화가 있을 때만 패키지를 install 하도록 한다.
      - name: Cache node modules
        uses: actions/cache@v2
        # 해당 step을 대표하는 id
        id: cache
        with:
          # cache의 대상을 정한다. 
          # yarn에서 의존성이 설치되는 dir인 node_modules가 대상이다.
          path: node_modules
          # cache 무효화를 결정하는 기준은
          # 의존성이 변경되면 함께 변경되는 파일인 yarn.lock(npm은 package-lock.json)을 기준으로 한다.
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          # key가 유효하지 않은 경우 runner의 운영체제 값과 node라는 suffix를 key로 복구.
          # 결과적으로 yarn.lock(npm은 package-lock.json)이 변경되지 않았다면 
          # 캐싱된 node_modules를 사용한다.
          # 만약 복구될 캐시가 없다면 아래에서 사용할 cache-hit는 false
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install Dependencies
      	# 이전의 cache가 없다면 dependency를 설치
        if: steps.cache.outputs.cache-hit != 'true'
        run: yarn install

      - run: yarn install --immutable --immutable-cache --check-cache
      - run: yarn build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.TOKEN }}
          # build가 되는 경로
          publish_dir: ./build

 

이제 push를 하면 위에서 정의한대로 workflow가 실행된다.

 

실행된 모습

 

예전에 진행했을 때는 정말 복잡하다고 생각했는데 다행히 여러 참고자료가 있어서 자료조사에 수고를 덜었다. 감사합니다.

 

 

참고

- Github Action 사용법 정리

https://zzsza.github.io/development/2020/06/06/github-action/

 

- 카카오웹툰은 GitHub Actions를 어떻게 사용하고 있을까?

https://fe-developers.kakaoent.com/2022/220106-github-actions/  

 

- npm ci란? 그렇다면 yarn은?

https://duck-blog.vercel.app/blog/web/npm-ci-do-how-about-yarn 

 

- 뱅크샐러드 Web chapter에서 GitHub Action 기반의 CI 속도를 개선한 방법

https://blog.banksalad.com/tech/github-action-npm-cache/

 

- Github Actions를 이용하여 gh-pages 자동 배포하기 

https://davidyang2149.dev/front-end/github-actions%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-gh-pages-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0/ 

Github pages로 배포하기

깃허브 페이지스는 git repogitory의 코드를 호스팅 해주는 기능이다. 

작성한 코드를 빌드해서 올려서 호스팅할 수 있다. 

처음엔 빌드하지 않고 올려서 readme.md만 출력되었는데 생각해보니 어처구니 없는 실수였다..

 

gh-pages 패키지 설치

npm install gh-pages --save-dev

or

yarn add gh-pages --save-dev

 

 

github pages를 사용하기 위해 파일을 빌드하고 배포할 수 있게 해주느 패키지인 gh-pages를 받아야한다.

 

Package.json 수정

아래왜 같이 수정해준다.

 

"homepage": "웹사이트 url"
"predeploy": "npm run build" or "yarn build" 
"deploy": "gh-pages -d build"

{
  "homepage": "https://aingface.github.io/my-resume",
  "name": "resume",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "gh-pages": "^5.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "styled-components": "^5.3.9",
    "typescript": "^5.0.2",
    "web-vitals": "^2.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "predeploy": "yarn build",
    "deploy": "gh-pages -d build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@types/styled-components": "^5.1.26",
    "@typescript-eslint/eslint-plugin": "^5.56.0",
    "@typescript-eslint/parser": "^5.56.0",
    "eslint": "^8.36.0",
    "prettier": "^2.8.6"
  }
}

 

그리고 배포하기 

 

npm run deploy

or 

yarn deploy

 

배포에 성공하면 프로젝트가 있는 Repository에 gh-pages라는 브랜치가 생성됩니다.

 

디렉토리를 gh-pages로 해야한다

 

 

그런데.. 매번 이렇게 배포하기 귀찮고 remote repository에 push 할 때마다 새로 배포되게 할 수는 없을까? 

당연히 방법이 있다. 그것은 Github actions

 

다음 글에서 다뤄보겠다. 

React로 1페이지 자기소개 페이지를 만들었던 과정이다.

패키지 관리자는 Yarn을 사용했다.

 

선행 과정

- Source Tree로 원격/로컬 Repository 만들기  

- VsCode extention으로 Prettier - code formatter, ESLint, Prettier ESLint  설치 및 적용

- Create React App  

프로젝트 코드 

코드 작성

텍스트만 적었다. 반응형 작업만 적용하고 불필요한 시각적 효과는 주지 않았다.

우선 각 파트를 컴포넌트로 나누어 작업했다.

props 전달은 하지 않았다.

 

컴포넌트 분리

컴포넌트를 포함한 item 디렉토리

위 사진에서처럼 컴포넌트로 분리했다.

 

src/item/index.ts

import Greetings from './Greetings';
import ExperienceTitle from './ExperienceTitle';
import OtherExperience from './OtherExperience';
import WorkExperience from './WorkExperience';
import Skills from './Skills';
import Contact from './Contact';
export { Greetings, OtherExperience, ExperienceTitle, WorkExperience, Skills, Contact };

item/index.ts 코드이다. App.tsx에서 위 코드에 명시된 컴포넌트를 import할 때 src/item 에서 불러오면 된다.

 

CSS적용

h1 {
  font-size: 4rem;
  line-height: 1.1;
}

h2 {
  font-size: 3rem;
  margin: 4rem 0 2rem;
}

h3,
h4 {
  font-size: 2rem;
  margin: 0 0 1rem;
}

h5 {
  font-size: 1.3rem;
  margin: 0 0 0.5rem;
}

p {
  margin: 0 0 1.5rem;
  font-size: 1.1rem;
  line-height: 1.4rem;
}

ul {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  font-size: 1.1rem;
}

ul > li:before {
  padding-right: 0.5rem;
  display: inline-block;
  content: '•';
}

a {
  text-decoration: none;
}

.App {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: center;

  background-color: #b5d692;
}

.container {
  padding: 40px 0px;
}

.big-p {
  font-size: 2rem;
  line-height: 1.4;
  font-weight: 500;
}

.time {
  display: block;
  margin-bottom: 1.5rem;
}

.role {
  margin-bottom: 0.3rem;
  display: block;
}

.row {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  padding: 3rem 0;
}

.row-left {
  -ms-flex-preferred-size: 18rem;
  flex-basis: 18rem;
  padding-right: 1rem;
  -ms-flex-negative: 0;
  flex-shrink: 0;
}

.row-right {
  -webkit-box-flex: 1;
  -ms-flex-positive: 1;
  flex-grow: 1;
  flex-direction: column;
}

.project {
  padding: 1.5rem 0;
}

.project:first-child {
  padding-top: 0;
}

.other {
  padding: 3rem 0;
}

@media (max-width: 768px) {
  .container {
    width: 90%;
  }
}

@media (min-width: 769px) and (max-width: 1024px) {
  .container {
    width: 80%;
  }
}

@media (min-width: 1025px) and (max-width: 1527px) {
  .container {
    width: 70%;
  }
}

@media (min-width: 1528px) {
  .container {
    width: 50%;
  }
}

@media (max-width: 960px) {
  .row {
    flex-direction: column;
  }
}

위와 같이 적용했다. 태그 선택자 및 클래스 선택자 별로 스타일을 적용해줬다. media-query를 통해 간단한 반응형 적용을 했다.  

styled-components는 적용하지 않았다.

 

 

 

+ Recent posts