/react_mission

Primary LanguageTypeScript

React Mission Repo - seo0h

  • 담당자 : @Seo0H

기간

  • 24.02.16 ~ 03.02

목표 / 요구사항

  • 설문조사 폼 제작

  • UI는 상단, 중앙, 하단의 레이아웃을 가지고 있습니다.

  • 상단에는 로고가 위치합니다.

  • 하단은 CTA 버튼이 위치합니다.

  • 중앙에는 설문 조사 form이 위치하고 input에 자동 focus 됩니다.

  • 중앙 가장 위에 현재 설문조사 progress 상태를 볼 수 있습니다. (현재 진행률)

  • 유저가 입장하면 /api/question/common으로 질문지를 받는다.

  • 유저가 대상이 아닌 경우 /no_target으로 redirect 시킨다.

  • 유저 답변을 /api/answers/common으로 post 요청하고, 다음 질문지 typeID와 userId를 받는다.

  • 공통 질문을 통해 얻은 typeID를 이용해 /api/question/:typeID에 요청해 질문지를 받는다.

  • 질문지는 data.forms에 제시된 순서로 user에게 답변을 받는다.

  • 답변을 다 받으면/api/answers/:typeID에 post로 작성한 답변과 userId을 업로드한다.

  • nextTypeId가 없으면 /thanks 페이지로 이동 후 설문조사를 종료한다.

  • 한/영 지원 : 모든 api는 ?lang=en을 통해 질문을 영어로 받을 수 있다.

사용 라이브러리

  • Dependency
    • React
    • React router dom
  • Dev dependency
    • Typescript
    • Jest, react testing lib
    • Css module
    • Json server (server 코드)
      • mock api를 이용해 실제 http get, post request를 구현하기 위해 사용했습니다.
      • ⚠️ json server 문법 상 get 요청 시 question id 와 lang 을 동시에 요청하기 위해서는 /api?id=:id&lang=:lang 과 같이 변경헤야 하기에 일단 해당 방식으로 구현했습니다. 추후 프로덕션 서버가 존재힌다면 /api/:id?lang=:lang 과 같은 형식으로 요청을 보내도록 수정이 필요합니다.
    • Eslint, prettier
    • Babel
    • Webpack

로컬 실행 방법

  1. 의존성을 다운로드합니다.
npm i
  1. json-server를 실행합니다. localhost:3000 에서 실행됩니다.
npm run server
  1. 프로그램을 실행합니다. localhost:3300 에서 실행됩니다.
npm run start

프로젝트 / 로직 구조

로직 구조 다이어그램

이미지를 클릭하시면 크게 보실 수 있습니다.

image

Form Inputs 로직 - ConditionalInput 컴포넌트

"text"| "number" | "checkbox" | "radio" | "radioNumber" | "radioWithInput"

  • common/form 컴포넌트는 제어, 비제어 모드 둘 다 사용이 가능하도록 설계했습니다.
  • RadioCheckboxRadioGroupCheckboxGroup와 같이 사용될 경우 내부 상태를 Context를 이용해 공유하도록 구현했습니다.

Form State 관리, 핸들링 - useForm 커스텀 훅 / FormProvider

  • 비제어 모드로 폼을 컨트롤 할 수 있는 커스텀 훅입니다.

  • option으로 autofocus 를 넘길 수 있습니다. (기본값 false)

  • react hook form의 구조를 참고해 제작했습니다.

  • 사용법 (onSubmit 으로 전체를 검증하는 경우의 예시입니다. 개별 질문에 대한 검증은 validateSingleValue 함수를 이용해 가능합니다.)

    const App = () => {
      const uniqId = useId();
      const { register, handleSubmit, formState } = useForm<Form>({ autoFocus: true });
      const { isValid, errors } = formState;
    
      return (
        <form onSubmit={handleSubmit((data) => console.log(data))}>
          <label htmlFor='userName'> 이름 : </label>
          <input
            {...register('userName', {
              required: true,
              requiredMessage: '필수 질문입니다.',
              validates: [{ type: 'not', target: '', validateText: '공백은 허용되지 않습니다.' }],
            })}
          />
          {isValid &&
            errors.userName?.message?.map((el, idx) => <span key={`${idx}-username-error-${uniqId}`}>{el}</span>)}
          <label htmlFor='age'>나이 :</label>
          <input
            {...register('age', {
              required: true,
              requiredMessage: '필수 질문입니다.',
              validates: [{ type: 'minMax', target: [20, '-'], validateText: '20살 이상이여야 합니다.' }],
            })}
          />
          {isValid && errors.age?.message?.map((el, idx) => <span key={`${idx}-age-error-${uniqId}`}>{el}</span>)}
          <button type='submit'>제출하기</button>
        </form>
      );
    };

question ID - URL query parameter

  /question/:questionId?lang=ko
  • API factory class 를 이용해 통일된 fetch 로직 관리하도록 했습니다.
  • form get 요청은 react router dom의 loader를 이용해 받아오도록 했습니다. - ui랜더링 전 prefetching 을 위함

Error 처리

  • Route 관련 에러 : react router dom의 error element를 사용했습니다.
  • ferch 관련 에러 : Error tost 를 이용해 에러 상태가 사용자에게 보여지도록 했습니다.

다국어 지원 - useLanguage 커스텀 훅 / LanguageProvider

  • useLanguage : React Router Dom에서 제공하는 useUrlParams를 이용해 url의 lang 키 값을 관리하고 현재 상태를 반환합니다.
  • LanguageProvider : 전역에서 useLanguage의 리턴 값을 전달하는 제공자입니다.

추가 구현 사항 : Enter를 이용한 폼 컨트롤 - PressEnter 컴포넌트

  • PressEnter : 전역에서 enter 이벤트를 감지하며 해당 이밴트 후 실행할 콜백 함수를 인자로 받습니다.
  • 주어진 예시 디자인에서 Enter를 이용한 폼 컨트롤이 가능했기에 추가 구현했습니다.

최적화

테스팅 - jest, react-testing-library


추후 개선되어야 할 요소

  • 질문 별 유저 접근권한 추가 : id를 url에서 관리하기에 url을 이용해 다른 유저의 질문에도 접근 가능한 상태입니다. 유저 별 허용된 질문을 세션에 담아 유저 권한을 체크하는 로직이 추가되면 더 좋았을 것 같습니다.

일정 관리


코드 규칙 (convention)

  • 커밋 컨벤션

gitmessage.txt

# chore: 빌드 프로세스나 도구 관련 변경
# docs: documentation 변경
# feat: 새로운 기능
# fix: 버그 수정
# perf: 성능 개선
# delete: 미사용 코드, 파일 삭제
# refactor: 버그를 수정하거나 기능을 추가하지 않는 코드 변경, 리팩토링
# style: 코드 의미에 영향을 주지 않는 변경사항 ( white space, formatting, colons )
# test: 누락된 테스트 추가 또는 기존 테스트 수정
# revert: 작업 되돌리기
# ui: css 관련 작업사항