2주차(월) - 프리 온보딩 코스 프론트엔드 - 기업 과제 회고

minbr0ther·2022년 2월 9일
0

pre-onboarding-fe

목록 보기
7/15
post-thumbnail

이번 과제는 '필터링을 통해 원하는 카드 컴포넌트만 노출하는 기능이 핵심인 웹사이트'를 만드는 것이였다.

기업 A의 실제 기획서를 받았고 현업에서 실제 사용한 것을 보이는 Figma주소를 받았다.

지난번의 과제보다는 분량이 확실히 적어서 상대적으로 마음이 편하게 시작할 수 있었다.

이번 과제는 추가적으로 수업시간에 배운 'TypeScript'를 필수로 적용해야 했다

👨🏻‍💻 전체적인 개발 순서

1. 프로젝트 세팅

  • CRA + TS template
  • ESLint, Prettier (+ TypeScript)
  • README.md 초안 작성
  • components 나누기
  • common/constants
    • type, interface 정의
  • API mock data 불러오기

2. 컴포넌트 기능 구현

  • 컴포넌트간 구별을 위한 inline style (border) 등만 하기
  • 개발후 pull request 날리기

3. CSS 스타일링

4. 리팩토링


⚙️ 구현한 기능 설명

1. API 서버 구현

과제 내용 중 json 파일을 제공하고 mock rest-api server를 띄우셔서 과제를 진행해주세요.

라고 되어있었는데 불편함을 느낀 선명님이 json서버를 heroku로 뚝딱 만들어서 배포해주셨다.

선명님 덕분에 편하게 시작할 수 있었다 👍🏻

// 위코드 COO 예리님이 수업시간마다 상수화를 강조하신다 👀
const URL = 'https://sixted-mock-server.herokuapp.com/';

  useEffect(() => {
    async function GetApi() {
      const data = await getApi(URL);
      setOrders(data); // 원본 데이터 보존용
      setFilteredOrders(data); // 필터링 데이터 전용
    }
    GetApi();
  }, []);

export default function getApi(address: string) {
  const data = fetch(address, { mode: 'cors' })
    .then(function (response) {
      return response.json();
    })
    .then(function (myJson: { requests: [] }) {
      return myJson.requests;
    });

  return data;
}

2. 필터링 기능


1. 필터링은 '가공방식', '재료'로 나뉘어 진다.

  • 각각 '가공방식', '재료'안에서 조건을 동시에 여러개 선택하면 합집합으로 노출한다.

  • '가공방식', '재료'이 각각 하나 이상 조건이 선택되면 두 조건의 교집합으로 노출한다.

  1. 필터가 선택되어 있으면 '필터링 리셋'을 노출하고, 클릭하면 모든 필터가 해제된다.

  2. '상담 중인 요청만 보기'는 toggle button (on/off) 방식으로 작동한다.

2.1 Checkbox States

// 입력받은 '가공방식', '재료'의 개수만큼 false로 채운 배열을 반환한다.
const makeFalseArr = (target: string[]) => new Array(target.length).fill(false);

// 초기값으로 false배열을 저장한다
const [materialChecked, setMaterialChecked] = useState<boolean[]>(
    makeFalseArr(MATERIAL),
  );
  const [processingMethodChecked, setProcessingMethodChecked] = useState<
    boolean[]
  >(makeFalseArr(PROCESSING_METHOD));

2.2 Checkbox OnChange Handler

  const handleOnChange = (
    position: number, // 클릭한 체크박스의 index
    e: React.ChangeEvent<HTMLInputElement>,
  ): void => {
    const target = e.target.name;

    // '가공방식', '재료' 분기처리
    if (MATERIAL.includes(target)) {
      const updatedChecked = materialChecked.map(
        // toggle 느낌으로 false -> true, true -> false 반환
        (item: boolean, index: number) => (index === position ? !item : item),
      );
      
      // 업데이트된 배열 리스트 setState
      setMaterialChecked(updatedChecked);
    } else {
      const updatedChecked = processingMethodChecked.map(
        (item: boolean, index: number) => (index === position ? !item : item),
      );
      
      setProcessingMethodChecked(updatedChecked);
    }
  };

2.3 Checkbox + toggle useEffect

이 useEffect에서는 materialChecked(재료), processingMethodChecked(가공방식), toggle(상담중인 요청 보기)의 상태가 변할때마다 작동하게 됩니다.

큰 순서는 다음과 같습니다.

  1. '가공방식'이 체크되어있는 항목 필터링

  2. '재료'가 체크되어있는 항목 필터링

  3. '상담중인 요청' 토글 필터링

(+ useEffect 안에서 이렇게 처리과정이 길어져도 되나 고민이 되었으나 마땅하게 해결할 수 있는 방법을 찾지 못하였습니다 🤔)

  useEffect(() => {
    // '가공방식'과 '재료'로 각각 필터링된 항목 저장
    let filterCondition: { material: string[]; method: string[] } = {
      // makeCondition => 체크된 항목을 name[]로 반환 ex) ['밀링']
      method: makeCondition(processingMethodChecked, PROCESSING_METHOD),
      material: makeCondition(materialChecked, MATERIAL),
    };

    // orderFilter은 아래 2.4 에서 자세하기 설명하겠습니다.
    // 1. '가공방식'이 체크되어있는 항목 필터링
    const methodFiltered: OrderInfo[] = orderFilter(
      filterCondition,
      CategoryName.가공방식,
      orders,
    );

    // 전 단계에서 필터링된 methodFiltered을 사용해서 새로 필터링
    // 2. '재료'가 체크되어있는 항목 필터링
    const materialFiltered: OrderInfo[] = orderFilter(
      filterCondition,
      CategoryName.재료,
      methodFiltered,
    );

    let statusFiltered: OrderInfo[] = [];

    // 전 단계에서 필터링된 materialFiltered 사용해서 새로 필터링
    // 3. '상담중인 요청' 토글 필터링
    if (toggle) {
      materialFiltered.forEach((order: OrderInfo) => {
        const status = order.status;

        if (status === Status.상담중) {
          statusFiltered.push(order);
        }
      });
    } else {
      statusFiltered = materialFiltered;
    }

    // 4. 최종 결과를 렌더링에 쓰이는 filteredOrders에 업데이트 해줍니다.
    setFilteredOrders(statusFiltered);
  }, [materialChecked, processingMethodChecked, toggle]);

2.4 useEffect > orderFilter

궁극적으로 필터링을 담당해주는 함수입니다.

매개변수는 다음과 같습니다.

  1. FilterCondition: { material: string[]; method: string[] },
    ➡️ ex) {method: ["밀링"], material: ["구리"]
  1. category: Category, ➡️ 'material' 혹은 'method'
  1. beforeFilter: OrderInfo[], ➡️ 이전에 필터링된 항목
export const orderFilter = (
  FilterCondition: { material: string[]; method: string[] },
  category: Category,
  beforeFilter: OrderInfo[],
) => {
  // 선택된 category를 통해 배열을 받습니다 ex) ["밀링"]
  const optional: string[] = FilterCondition[category];
  let afterFilter: OrderInfo[] = [];

  if (optional.length === 0) {
    // 선택된 필터가 없으면 그대로 반환합니다.
    afterFilter = beforeFilter;
  } else {
    // 선택된 필터가 있으면 주문들을 순회한다
    beforeFilter.forEach((order: OrderInfo) => {
      
      // 선택한 필터를 order가 가지고 있으면
      const found = order[category].some((r) => optional.includes(r));

      if (found) {
        // 결과 값에 push 한다
        afterFilter.push(order);
      }
    });
  }
  return afterFilter;
};

🤔 후기

이번에 처음으로 프로젝트에 '타입스크립트'를 적용하면서, 여러 난관이 있었다.

팀원 모두가 처음이기 때문에 프로젝트 타입 지정, API Fetch 과정을 모두 같이 진행했다.
(enum 타입을 한글로 지정했는데 개인적 의견으로는 확실히 눈에 잘 띄고 가독성이 높았다)

특히 함수 구현을 할때 타입 지정이 어려웠던 것 같다.

확실하게 경험 부족으로 판단되어서 앞으로 모든 프로젝트들을 타입스크립트로 진행하고 추가적으로 꾸준히 공부를 해야할 것 같다.

이전의 과제들은 분량, CORS 배포문제 등 제대로 완성을 못했었는데, 이번 과제는 그나마 완성도 높게 제출한 것 같아서 기분좋게 회고를 작성할 수 있었다.

앞으로도 과제가 7개 남았다 화이팅 🔥

profile
느리지만 꾸준하게 💪🏻

0개의 댓글