본문 바로가기
프론트엔드

펫링크 마이그레이션 후기

by ISA(류) 2021. 7. 8.

펫링크

마이퍼피 펫링크 관련 설명.
인수인계 및 리팩토링시 참고해서 진행바랍니다.

기술 구성

해당 프로젝트는 기존의 PHP 코드 이그나이트, 부트스트랩, 제이쿼리 기반, 멀티페이지 어플리케이션을 리액트로 마이그레이션 진행한 프로젝트입니다.
프로젝트 환경 설정 과정에서 개인적으로 작성한 웹팩 템플릿을 사용했습니다.

적용된 기술스택: React, TypeScript, Axios, Redux, Redux-saga, Bootstrap(react), C3, React-hook-form, React-quill, React-select, React-tooltip, Signature_pad, Styled-components, React-circular-progressbar, React-daumpost-hook, Webpack, Storybook, Popper, React-router-dom,
React-helmet, Jest, @testring-library

  1. React: CSR방식으로 적용되었다. 관리자 페이지라 SEO를 고려할 필요가 없었기에 SSR은 적용하지 않았음. React 가상돔을 고려해서 기존 제이쿼리 부분을 걷어냈다.
  2. TypeScript: 자바스크립트의 약점인 약한타입 문제를 보안해서 코드 견고함을 얻고, 혹시나 모를 구버전 브라우저 지원을 위한 트랜스파일링을 위해서 적용했다.
  3. Axios: 클라이언트-벡엔드 아키텍쳐에 맞게 클라이언트 서버와 벡엔드 API와의 AJAX 통신을 위해서 적용했다.
  4. Redux: state management로는 리덕스를 선택했다. + 사가 미들웨어를 통해서 비동기 AJAX을 처리.
  5. 부트스트랩: 기존에 적용된 스타일을 리액트방식으로 포맷팅 할바에는 다시 만드는게 나았기에 기존 부트스트랩 스타일을 그대로 적용했다. 예전 방식 그대로
  6. C3: 차트 라이브러리 프론트단에 보여주는 차트는 모두 해당 라이브러리가 적용했다.
  7. React-hook-form: 폼 데이터 관리 및 양식 관리를 위해서 선택해서 적용했다.
  8. React-quill: 위지윅 에디터.
  9. React-select: 폼셀렉옵션 모듈.
  10. React-Tooltip: 툴팁 모듈.
  11. Signature_pad: 서명 관련 보드.
  12. Styled-components: CSS의존성 관리를 위해서 사용해서 적용 단순히 기존 스타일을 분리하는 용도라 큰 의미는 없다.
  13. Webpack: 프론트 개발 번들링.
  14. React-Helmet: 페이지마다 타이틀이 변경되는 기능을 위해서 & SNS공유시 관련될 오픈그래프(옵션).
  15. Storybook: 스토리북을 통한 컴포넌트 개발, UI테스트 및 크로매틱을 통한 시각적 회귀 테스트을 적용했다.
  16. Jest: 기능 테스트를 위해서 기본적인 스냅샷 테스트 + 기타 로직들에 대한 테스트용도.
  17. etc: 잡다한 것들은 생략한다.

아키텍쳐 구성

프로젝트 설계

루트

  1. 루트: 기본적인 설정 파일들
  2. build: 웹팩설정
  3. types: 타입스크립트 글로벌타입
  4. public: 스태틱
  5. src: 개발소스
  6. .storybook: 스토리북 config
  7. config: jest config

Src

  1. assets: 정적 리소스 - 기존 리소스들 그대로 가지고 있음, 정리 안된상태.
  2. components: 컴포넌트.
  3. lib: 라이브러리.
  4. store: 스토어.
  5. view: 뷰(PAGE) ROUTE.
  6. app: 어플리케이션 라우팅 및 기본적인 리소스.

Components

  1. account: account VIEW에 해당 하는 컴포넌트 (템플릿) - 계정정보 관련된 컴포넌트.
  2. auth: auth VIEW에 해당 하는 컴포넌트 (템플릿) - 로그인, 비밀번호 찾기, 회원가입등에 관한 컴포넌트.
  3. contract: contract VIEW에 해당 하는 컴포넌트 (템플릿) - 분양계약목록, 분양계약하기 등에 관한 컴포넌트.
  4. customer: customer VIEW에 해당 하는 컴포넌트 (템플릿) - 고객목록, 고객문의, 기존고객목록 등에 관한 컴포넌트.
  5. forms: forms VIEW에 해당 하는 컴포넌트 (템플릿) - 분양계약서목록, 분양계약서 변경, 서명목록 등에 관한 컴포넌트.
  6. main: main VIEW에 해당 하는 컴포넌트 (템플릿) - 메인 페이지에 UI에 관한 컴포넌트.
  7. pet: pet VIEW에 해당 하는 컴포넌트 (템플릿) - 동물관리 등에 관한 컴포넌트.
  8. shop: shop VIEW에 해당 하는 컴포넌트 (템플릿) - 매장관리, 매니저 관리, 연락처 관리, KPI 등에 관한 컴포넌트.
  9. support: support VIEW에 해당 하는 컴포넌트 (템플릿) - 고객지원에 관한 컴포넌트.
  10. layout: layout 에 해당하는 컴포넌트들
  11. ui: 아토믹 패턴에 따른 프레젠테이션 컴포넌트들 - atoms, molecules, organisms으로 구성 되어있다.

Lib

  1. api: AJAX에 관한 소스들이 존재한다. 말 그대로 API 서버와 직접적으로 AJAX하는 코드 AJAX에 관한 모든 코드들이 존재한다.
  2. custom: React 커스텀 훅들이 위치한다. STORE에 관한 useSelector, useDispatch나, API요청에 대한 전반적인 처리로직들이 존재한다.
  3. util: 제네릭한 util들이 존재한다. date나 url파싱 관련 로직들 - 커스텀 훅과의 차이점은 리덕스 라이프 사이클이나 HOOK을 포함하냐 차이다.

Store

  1. actions: 리덕스 액션과 타입이 존재한다.
  2. reducers: 리덕스 리듀서가 존재한다.
  3. sagas: 리덕스 사가가 존재한다.

프로그램 플로우

기본적으로 리액트 TS, 함수 컴포넌트 방식을 채택하고 있다. 그 이유는 리액트 Hook공식문서나 리액트에 대해서 공부하면 알 수 있으니 생략한다.

  1. 라우트는 APP ENTRY에서 시작한다. 각 ROUTE에 맞는 VIEW는 VIEW DIR에 존재한다. VIEW에서 서브 라우트는 Params로 받는다.
  2. 라우트에 맞는 컴포넌트들은 components 폴더의 view dir안에 존재한다. 해당 dir들의 목적은 각 라우트에 적합한 컴포넌트 템플릿들을 분리해서 관리하기 위함이다. 도메인 단위 구분이라고 볼 수 있다.
  3. 컴포넌트는 프레젠테이션과 컨테이너로 나뉜다. 프레젠테이션을 편의를 위해서 UI컴포넌트로 지칭한다. 컨테이너를 편의상 컨트롤러라고 지칭한다.
  4. 프레젠테이션 컴포넌트는 기본적으로 아토믹 패턴을 통해서 재사용성을 높인다. 일반적으로 View dir 안의 템플릿들은 UI 컴포넌트들(제네릭)의 조합 및 해당 UI에 적합한 부분들로 구성 되어있고, 코드가 너무 긴 것을 방지하기 위해서 스텝 단위로 쪼개어져있다.
  5. 각 컴포넌트들은 그 컴포넌트에 해당하는 스토리북 코드가 같은 위치에 존재한다. 원래는 테스트코드 존재해야하지만 작업 과정에서 중단 되었다.
  6. 시각테스트는 스토리북을 통해서 진행하면 되며, 기능 테스트는 Jest를 통해서 진행하면된다.
  7. UI컴포넌트의 컨트롤러 부분은 임의로 해당 폴더 내 controller폴더에 추출해뒀다.(안된 것들도 존재한다.) 해당 코드는 사실상 리액트 Hook을 통한 커스텀 훅에 해당한다.
  8. 컨트롤러와 lib의 custom의 차이는 해당 로직들이 얼마나 노말하고 제네릭 한가의 여부이다. 즉 제네릭한 커스텀훅을 (상속)받아서 해당 페이지에 필요한 로직들을 짜면 그게 컨트롤러이다.
  9. 컨트롤러의 경우 최상단 컴포넌트인 VIEW의 라이프사이클 (컨텍스트 !context Hook)에 의존하고 있다. API에 대한 데이터를 AJAX 방식으로 리덕스에서 관리하는 것이 불편하기에 자연히 해당 Put Redux Ajax data를 Saga 미들웨어에 위임했고 그로 인해서 데이터를 불러와 렌더링 하는 과정에서 딜레이와 리플로우가 발생한다.(UX)
  10. 그런 이유로 서브라우팅의 경우 해당 카테고리 진입시 해당 라우트에 해당하는 서브 라우팅들에 도달할 확률이 높다고 판단하여서 VIEW의 라이프사이클 (컨텍스트 !context Hook)에 컨트롤러 로직을 연결했다. 그래서 해당 카테고리 진입시 카테고리 내부 모든 라우팅이 기본적으로 필요한 정보들을 받아서 스토어에 넣는다.
  11. 리액트의 경우 본디 MVVM 패턴을 따르고 있지만 해당 프로젝트의 경우 아키텍쳐 설계 구상시 프레젠테이션 - 컨테이너, MVVM 디자인 패턴, 아토믹 패턴을 자체적으로 응용해서 설계 되어 있다.
  12. VIEW 즉 UI는 독립되어서 아토믹패턴을 따르고 있다.(재사용성) 다만 리팩토링 여지가 아직 많이 남아있으니 확인바란다.
  13. MVVM 패턴에서 Model의 경우 Store와 벡엔드 API가 담당한다. View Model은 커스텀훅이 담당한다. 즉 리액트 커스텀훅이 ViewModel이다. lib/custom
  14. 커스텀훅에 비즈니스 로직들이 들어간다. 컨테이너(컨트롤러)는 컨트롤러의 역할을 담당한다. ViewModel과 View를 이어주는 역할이다.
  15. 아토믹 패턴만으로는 제네릭하지 않는 요소를 관리하기 어렵다. 그렇기에 레이아웃, 도메인(view)부분의 템플릿을 따로 나누었다.
  16. Ui의 경우 당연한 말이지만 도메인에 종속적이지 않고 제네릭해야한다. UI 즉 아토믹 부분에서 도메인(이 경우는 VIEW) 종속적인 부분이 있다면 리팩토링 해야하는 요소이다.

JWT

토큰 인증 방식으로 JWT를 사용한다. OAUTH2.0을 베이스 삼아서 벡엔드에서 자체적으로 설계한 방식으로 진행된다. 엑세스 토큰과 리프레쉬 토큰을 기반으로 한 토큰 인증체제로 리프레쉬 토큰의 경우 HTTP SECURE COOKIE에 전달된다. 엑세스 토큰의 경우 api의 response body에 담겨서 전달 되니 해당 부분을 받아서 store에 저장하는 로직으로 구성 되어있다. 특이점으로 AUTH 서버와 API서버가 나뉘어져 있지 않기에 일반적은 API응답시 엑세스토큰이 만료되었을시 리프레쉬 토큰을 확인하고 엑세스 토큰을 다시 발급하는 구조라서 lib/api 부분을 살펴보면 엑세스토큰을 받아서 header의 AUTH에 넣는 로직이 모든 API에 포함 되어 있다. 해당 부분의 경우 벡엔드 AUTH 방식에 따라서 변경 할때 참고하길 바란다.
토큰 관련된 모든 AJAX 요청은 Redux-saga에서 위임받아서 처리한다.

### gist 링크
https://gist.github.com/yoonjonglyu/ce496c889322ad55b382878974892327

펫링크 프론트엔드 아키텍처 구성 및 인수인계사항.

펫링크 프론트엔드 아키텍처 구성 및 인수인계사항. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

### 스토리북 링크
https://608a0b86c9dfa5002145eda0-lxvntfpevo.chromatic.com/

Storybook

608a0b86c9dfa5002145eda0-lxvntfpevo.chromatic.com

반응형