지금 내가 맡고있는 팀의 프론트 환경은 제이쿼리로 구성되어있다.
한 페이지의 모든 JS 코드들이 하나의 파일(...) 로 되어있어서 유지보수하기가 힘든 부분이 많다.
하나의 기능을 수정할려면 기존의 코드를 훑어 보면서 그기능을 구현한 함수가 어디있는지 찾아야하고 또 그 함수에서 쓴 변수, 특히 일종의 전역변수? 처럼 쓰이는 변수들이 많아서 데이터가 어디서 어떤 순간에 변했는지 추적하기도 힘들었다.
그래서 이전에 Vue를 통해 마이그레이션 하려는 시도도 있기는 했지만 우리팀이 맡고 있는 서비스는 런칭된지 얼마 안된 서비스라서 고객의 요구사항과 신기능들을 추가하는 일정이 빡빡하여 물리적인 시간과 조건이 되지 않았다.
그래서 이번에 리액트와 바벨 웹팩과 같은 모던한 프론트엔드 개발환경에 관해 공부도 할겸 토이프로젝트 형식으로 기존에 회사에서 쓰던 코드들을 대상으로 리액트로 마이그레이션을 시도해보았다.
물론 회사에서 쓰는 코드를 내가 마이그레이션한 코드이기 때문에 깃허브에 공개적으로 올리는것은 힘들고 내가 마이그레이션하는 과정의 이해를 돕기위해 내가 구현한 코드의 폴더구조 정도만 블로그 글에 표시했다.
이 프로젝트의 목적은 실무프로젝트에 리액트를 활용할수 있는 방법이 있는지 검토하는 차원에서 진행한것이다.
일단 회사 프로젝트는 기본적으로 스프링 부트로 구성되어있다. 스프링부트의 Tyhmleaf(뷰템플릿) 를 이용하여 html을 보내주고 있고 해당 html에 있어야하는 js파일들이 주루룩 로딩되는 형태이다.
일단 기본적으로 웹팩과 바벨을 사용하기 위해선 노드환경에서 하는것이 좋다고 생각했다.
물론 노드환경에서 하지 않아도 방법은 있지만 실무에서 마이그레이션을 한다고 가정하고 진행했기 때문에 이방법이 실제로 실무 프로젝트에 활용될수 있는지를 검토해야했다. 그런데 프로덕션 환경에서 노드환경이 아닌 html의 인라인 형태로 바벨을 사용하는것은 성능상 좋지 않고 공식문서에서도 권장하지 않았다.
또한 리액트를 활용해보려는 이유는 기존의 한 js파일에 모든 로직이 들어가있는 구조에서 벗어나 컴포넌트 구조로 리팩토링을 해보고 싶었기 때문인데 그러기 위해선 웹팩의 사용이 필수 였다.
물론 노드환경이 아니라도 컴포넌트 구조를 짜본다면 짜볼수도 있긴 하겠지만..
바닐라js로 컴포넌트 구조를 짜느니 내가 페이스북 React팀에 들어가는게 더 빠를것 같아 맘 편하게 노드환경에서 하기로 결정했다.
그래서 내가 생각해본 과정은 이와 같다.
보통은 개발서버를 띄워놓고 작업하는 것이 일반적이지만 지금은 스프링 부트 프로젝트의 일부를 리액트로 전환하는 과정이기 때문에 컴파일된 js파일을 스프링 부트의 폴더로 옮겨야한다.
먼저 webpack에서 watch 모드로 띄우면 코드가 수정될때마다 자동으로 컴파일해서 결과 파일을 저장해준다.
이때 결과 파일의 경로는 webpack.config에서 ouput 필드에서 지정할수 있다.
그리고 결과파일의 경로를 스프링의 js 폴더로 지정을 해주면,
코드가 수정되고 웹팩이 컴파일을하고 자동으로 스프링의 js폴더로 js파일이 새로저장되거나 수정된다.
그다음 스프링프로젝트에서 js파일을 다시 리컴파일 해주면 된다.
이때 인텔리제이의 자동 컴파일 기능을 사용하면 훨씬 더 편하게 이용할수 있는데,
실행 설정 창에서 설정탭의 밑부분에 on frame deactivation 을 update resources로 설정해주면 코드창에서 포커스가 벗어날때 자동으로 js파일이나 다른 리소스 파일을 업데이트 시켜준다.
결과적으로 리액트에서 무언갈 수정하고 코드창을 벗어나면 웹팩에의해 스프링부트 프로젝트내의 폴더로 js파일이 넘어가 수정되고 다시 코드편집창을 클릭한 후 다른 곳을 클릭하거나 창을 벗어나서 새로고침을 하면 자동컴파일 기능에의해 수정된 결과를 확인할수 있다. 포커스기준이 코드편집창이 기준이고 이때 꼭 스프링부트의 프로젝트 창이 아니어도 되기 때문에 리액트 프로젝트내에서 인텔리제이ide창내에서 아무데나 클릭해주거나 창을벗어나면 적용된다.
가장 먼저 해야할 것은 하나의 js파일에 묶여있는 많은 로직들을 컴포넌트로 쪼개는 것이었다. 그래서 결과적으로는 아래처럼 바뀌게 되었다.
기본적인 유저스토리는 체크박스를 통해 여러 필터 조건들을 걸고 조회버튼을 누르면 해당하는 리스트아이템들을 볼수 있는 것이 었고 화면을 기준으로 재사용 할수 있을 만한 것이나 기능상 쪼개는 것이 가독성이 좋을 것 같은 것들로 나누어보았다.
특히 툴팁 같은 컴포넌트는 화면내에서 쓸일이 굉장히 많은데 간단한 텍스트와 타이틀만 props로 넘기면 바로바로 재사용할 수 있어 굉장히 편리했다.
상태관리는 기본적으로 local state와 remote state 로 나누어서 생각을 해보았다. 지역 상태의 경우 리덕스를 활용했고, 원격 상태의 경우 요즘 next.js를 공부하고 있다보니 자연스럽게 SWR로 결정을 하게 되었다.
SWR 같은 경우 커스텀훅(useVisitorActionFlow.js) 을 만들어서 해당 원격상태를 활용하는 컴포넌트들에서 쉽게 재사용 되도록 하였다.
또한 어떤 체크 박스 들이 체크되어있는지는 다른 컴포넌트에서 이값들을 공유하기위해 리덕스 스토어에 저장하는게 좋다고 판단하여 리덕스를 활용해보았는데, 블로그를 쓰면서 다시 생각해보니 useState로도 충분하지 않았을까 생각이 든다.
조회 조건들을 활용하기 위해 많은 코드값들을 사용하는데 이것들 enum으로 관리하면 좋을 것같다는 판단을 했다. 그래서 따로 파일을 빼서 일반 객체 리터럴 형태로 사용을 하였는데, 타입스크립트를 이용하면 좀더 체계적이고 유지보수성을 더 높일 수 있을것 같다.
사실상 지금 실무프로젝트에는 프론트를 테스트하는 부분은 없다. 그때그때 화면에 문제가 생기면 기획팀이나 영업팀에서 확인해서 개발팀에 알려주는 편이다.
그래서 어떤 로직에 문제가 생겼는지 한번에 파악하기가 어려운데 테스트 케이스를 만들어서 좀 더 유지보수하기 쉬운 튼튼한 코드로 만들었다.
한 페이지의 모든 로직이 하나의 자바스크립트 파일 형태로 들어가있던 구조에서 컴포넌트 구조로 변경하니 로직들이 컴포넌트형태로 분리되어서 유지보수하기 쉽고 코드의 가독성도 좋아졌다.
툴팁 같이 많은 페이지에서 범용적으로 쓰이는 컴포넌트들도 쉽게 재사용 할수 있어 시간을 줄일 수 있다.
redux나 swr과 같은 상태관리 라이브러리를 이용할 수 있어 데이터의 흐름을 한눈에 파악하기 쉽다. (물론 redux는 굳이 리액트가 아니어도 가능하긴하다.)
swr같이 원격 상태관리 라이브러리를 사용하면 창이 전환되거나 할때 자동으로 상태를 업데이트 시켜주므로 좀더 페이지의 사용성을 높일 수 있다.
타입스크립트를 적용해보지 않았는데 타입스크립트로 구현을 하면 undefined 오류의 늪에서 벗어 날수 있을 것 같다는 생각을 했다. enum과 같은 기능들도 덤으로 이용해볼수 있다.
코드스플리팅을 생각해볼 수 있을것 같다. 리액트 프로젝트에서 결국은 스프링 프로젝트로 각각 컴파일된 js파일이 나누어져 각각의 위치에 저장되어야하므로 어느 한 페이지에 해당하는 js파일들을 웹팩의 코드스플리팅 기능을 활용해 나누어 볼수 있을것 같다.
yarn berry를 쓰면 제로 인스톨이 가능하고 빌드속도도 빨라지고 무지막지한 node_modules를 안봐도 될것 같다!
만약 프로젝트를 조금씩 리액트로 마이그레이션 한다면 기존 스프링부트 프로젝트에서 리액트로 전환하는 부분의 우선순위를 어떻게 정할것인가? 새롭게 추가되는 신규기능? 아니면 주로 사용하는 기능 부터?
또한 고객사의 요구사항과 기획팀의 새기능 제안 등 마이그레이션에서 벗어난 업무들과의 우선순위를 어떻게 지정할것인가?
생각해봐야할 부분이 많다.
이런 부분들을 고려해봤을때 아직 까지는 실무에 바로 적용하기 어려운 부분들이 많다. 가장 중요한 핵심은 고객과 개발팀이 아닌 다른팀에게 어떻게 어필할지가 중요할것같다.
왜 마이그레이션을 해야하는지? 비즈니스적으로 더 나아지는 부분이 있는지? 등등