티스토리 뷰

Front

리팩토링기

콜라먹는 펭귄이 2023. 1. 19. 16:19

서론

최근 회사에서 조직 개편이 이루어졌다. 나는 기존에 개발하던 제품이 아닌 다른 제품을 개발하는 조직에 발령을 받았다. 이번에 개발에 참여하게 된 제품은 기존에는 데이터 개발자 분들이 개발하던 웹 서비스였다. 프론트엔드 개발을 해본적 없는 분들이 개발한 프로그램 + 데이터 엔지니어 업무중 짬짬히 개발한 프로그램이다 보니 빌드, 코드, 개발 정책등 손보고 싶은 부분들이 많이 보였다. 이 부분들을 통틀어 어떤 단어로 표현해야할지 모르겠어 리팩토링이라는 가제를 쓰기로 했다. 이 작업을 하는 과정중에 마주한 문제와 고민들을 이 글에 적어보려한다.


첫번째. 빌드

이 프로젝트에 참여하게 되어 가장 먼저 한것은 빌드와 배포의 수정이었다. 기존 개발된 제품을 보기 위해 배포되어 있는 웹사이트에 접속해보았다. 근데 크롬 React 개발 도구에 빨간불이 들어와 있었다. 설마하고 확인해보니 react-scripts의 start 명령어를 통해 개발 환경을 배포중이었다. 그래서 우선순위를 가장 높혀 빌드 과정과 배포과정을 살펴보았다. 빌드 과정은 다음과 같았다. (빌드와 배포에는 github action과  docker, habor를 사용하고 있다.)

 

1. PR이 특정 브랜치에 머지되거나 PR이 올라오면 github action이 실행된다. (build workflow 파일은 main에 push되면 github action이 돌도록 설정해 놓았다.)

2. workflow가 실행된 브랜치를 checkout한다.

3. harbor에 로그인한다.

4. docker 태그를 정의하여 환경변수에 저장한다.

5. 도커 이미지를 빌드한다. 도커 파일에는 src폴더 package.json, yarn.lock같은 빌드할때 필요한 파일들을 복사한다. 또한 환경변수를 설정하고 yarn install, yarn build 과정(react-scripts 사용)을 거친다. CMD 라인은 yarn start로 되어있었다.

6. 빌드한 도커 이미지에 태그를 붙힌다.

7. push로 실행된 action이면 태그에 lateast를 붙혀 push한다.

8. habor에 push한다.

 

다음은 수정한 흐름이다.

 

1. PR이 특정 브랜치에 머지되면 github action이 실행된다. (build workflow 파일은 main에 push되면 github action이 돌도록 설정해 놓았다.)

2. workflow가 실행된 브랜치를 checkout한다.

3. node 환경을 세팅한다.

4. 캐싱한 node_modules를 불러온다.

5. yarn install을 실행한다.

6. react-scripts를 이용해 build한다.

7. 하버에 로그인한다.

8. 도커를 빌드한다. 도커파일에는 build된 결과물만 copy하고 npm에서 serve 라는 package를 설치하여 배포한다. (static 서버를 만들까 생각도 해봤지만 굳이 필요성을 못느꼈다.)

9.  빌드한 도커 이미지에 태그를 붙힌다.

10. push로 실행된 action이면 태그에 lateast를 붙혀 push한다.

11. habor에 push한다.

 

위 흐름에서 바꿔야겠다고 생각한건 총 세가지였다. 하나는 production 환경으로 배포하는것 하나는 ci에서 빌드하는것, 빌드시간을 줄이는 것이었다. 도커환경에서 빌드하니 빌드할때 필요한 파일이 하나씩 추가될 때 마다 도커로 해당 파일을 복사하는 구문을 추가해주어야 했다. 그리고 내가 빌드쪽 수정하고 있다고 하자 팀원분이 빌드시간이 너무 길다고 줄여달라고 말씀하셔서 확인해보니 한번 빌드 시 10분이 걸리던 것을 4 ~ 5분으로 줄였다.

 

docker build에서 dependency install과 react-scripts build를 했을때 6분이 걸렸는데 지금은 추가된 dependency가 없을때 dependency install은 0초 build는 1분 30초정도가 걸린다. 체감상 build속도도 빨라진 것 같지만 기존 docker에서 빌드했을때 각 단계별로 시간을 측정하지 않아서 정확하지 않은게 아쉽다.

 

이제 추가로 pr시에는 docker 빌드와 habor에 push를 하지 않도록 if문으로 분기를 만들어 주어 시간을 더 단축하였다. 지금은 docker 빌드와 habor에 push job마다 if문을 부여했지만 이 글을 참고하여 pr과 build시의 action을 나눌 생각이다.


두번째. 정책

기존에 git flow같은 정책들이 정의되어 있었는데 거기서 수정한 정책은 많지 않다. github에서 pr시 자동으로 reviewer가 할당되도록 CODEOWNER만 적용했다.

CODEOWNER 설명: https://goodgid.github.io/Github-CODEOWNERS/

 

Github에서 PR 생성 시 Reviewer 자동으로 할당하기 (feat. CODEOWNERS)

Index

goodgid.github.io


세번째. 안쓰는 파일, 변수, 함수, 상태변수 제거

리팩토링 전에 사용하지 않는 코드들을 제거해야 리팩토링 할 코드가 줄어 편하겠다고 생각해서 먼저 사용하지 않는 코드들을 삭제하였다. 사용하지 않는 파일을 쉽게 찾기 위해 구글링하다가 다음 글을 발견해서 unimported라는 package를 사용하여 사용하지 않는 파일들을 찾았다. https://stackoverflow.com/questions/54148788/how-to-find-dead-code-in-a-large-react-project

 

How to find dead code in a large react project?

In order to refactor a client-side project, i'm looking for a safe way to find (and delete) unused code. What tools do you use to find unused/dead code in large react projects? Our product has be...

stackoverflow.com

여담으로 사용하진 않지만 추후에 사용할 수 있는 범용 컴포넌트 같은 코드들은 남겨두었다.

 

사용하지 않는 변수, 함수를 제거하기 위해 eslint rule에 @typescript-eslint/no-unused-vars를 추가했다. 그냥 no-unused-vars를 추가하면 함수 타입을 만들때 매개변수를 사용하지 않는다고 에러가 뜬다.

 

상태변수도 위 방법으로 삭제하였다. 상태변수와 set함수 둘다 쓰지 않는 경우, 상태변수만 사용하지 않는 경우, set함수만 사용하지 않는 경우가 있었는데 상태변수만 사용하지 않는 경우 즉 set함수만 쓰는 경우는 상태를 제거하면 버그가 발생했다. 아마 렌더링 관련해서 로직이 얽힌것 같았다. 이런 경우는 나중에 각 컴포넌트에 맞는 상태를 분배할 때 수정하려고 일단 놔두었다. 현재는 그 상태를 알 필요 없는 컴포넌트에 상태가 선언되어 다른 컴포넌트에 전달하는 경우가 있었다.


네번째. 구조 변경

첫번째로 생각한 문제는 폴더 구분의 의미가 없다고 생각했다. 이 파일이면 이 폴더에 있어야 할 것 같은데? 라는 생각이 드는 경우가 있었다. 폴더 구조는 기존 것을 그대로 활용하되 각 폴더에 역할을 부여하고 그 역할에 포함되지 않는 파일이 포함되어 있을 경우 역할에 맞는 폴더로 옮겨주는 작업을 했다.

src 
 └─ app
     ├─ asset // png, svg, font 등 코드 외적으로 필요한 파일들
     ├─ type // 도메인관련 type 등
     ├─ component // 공통 컴포넌트. button, table, dropdown 등
     ├─ hooks // 커스텀 훅
     ├─ lib // 공통적으로 사용하는 따로 뺀 로직
     ├─ modal // 구체적으로 구현 한 각 모달 혹은 특정 모달에 종속 된 컴포넌트
     ├─ page // url을 고유하게 갔는 페이지 혹은 페이지에 종속 된 컴포넌트
     ├─ reducer // redux 보일러 플레이트
     └─ style // 공통 스타일. 변수, minxin 등 (scss 사용중)

component에 특정 페이지에 종속된 컴포넌트가 있으면 page로 옮겨주는 등의 작업을 하였다. 

 

modal을 폴더말고 조금 더 범용성 있게 관리하고 싶은데 마땅히 생각이 나지 않아 고민중이다. 또한 app 폴더도 의미없이 깊이만 추가하는 것 같아 삭제할 생각이다. 바꾼 폴더 구조를 보면 나만 알 수 있게 변경한 건가 싶기도 하다. 일단 기록하고 나중에 같이 일하게 될 동료가 생기면 의논해봐야겠다.

 

두번째로는 상태가 너무 많다는 것이었다. 보통 페이지 컴포넌트에 몰려 있었는데 한 페이지 컴포넌트에는 무려 상태가 40개가량이 있었다. 사용하지 않는 상태를 제거한 후의 개수이다. 상태가 많으면 나중에 상태를 추가할때 혹은 사용할때 어떤 사이트 이펙트가 발생할 지 알기 힘들다고 생각했다. (그렇게 생각한 이유: 링크) 그래서 먼저 컴포넌트를 분리하였다. 컴포넌트 하나의 여러 관심사가 섞여있어서 상태가 많아졌다고 생각했다. 컴포넌트를 분리하니 자연스럽게 상태도 분리되었다. 뿐만 아니라 useEffect도 관심사에 맞는 컴포넌트로 분리되어 페이지 컴포넌트가 좀 더 깔끔해졌다. 하지만 props 드릴링으로 state와 props가 어디에서 생성되어 어디로 가는지 알기 힘들어지고 jsx코드도 알아보기 힘들어 졌다. 이 부분에대해서는 context api로 해결해볼 예정이다.


다섯번째. 비동기처리

예전부터 react에서 비동기를 처리할때 데이터를 담을 state를 만들고 useEffect에서 데이터 요청하고 응답받아 state를 업데이트하는 식으로 구현을 했었다. 이번에 참여하게 된 프로젝트에서도 같은 방식으로 짜여있었고 나는 이 부분이 마음에 들지 않았다. useEffect가 난무하고 응집도도 떨어진다고 생각했기 때문이다. 그래서 따로 hook처럼 빼려고 하였는데 마침 toss slash 유튜브에서 우아한 비동기처리라는 영상을 보았고 react-query와 suspense, errorboundary를 이용한 비동기처리를 보고 관심이 생겨 도입을 진행해 보았다.

 

 


후기

리팩토링이 필요하다고 생각한 코드는 굉장히 많이 있었지만 사업 특성상 그리고 시기상 굉장히 조금밖에 리팩토링을 하지 못했다. 이 사업은 inhouse 서비스였고 이제 막 외부로 오픈하려는 참이다. 그래서 코드 퀄리티, 제품 퀄리티도 중요하지만 속도도 중요했다. 그래서 이정도 까지만 정비를 하고 앞으로는 기능 추가를 하며 조금씩 개선해 나아갈 생각이다.

'Front' 카테고리의 다른 글

Browser 대용량 파일 다운로드 구현기  (0) 2023.03.20
해시뱅  (0) 2019.03.07
DOM 문서 객체 모델  (0) 2019.03.05
SEO 검색 엔진 최적화  (0) 2019.03.04
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함