[React] 이중검색을 통한 학생관리 – 사이드 프로젝트 개발일지

초기화면

완성된 기능 실행해보기

1. Axios를 사용하여 HTTP요청하기

기본DB정보는 axios를 이용해서 OpenAPI에서 받아왔습니다.
axios의 기본 사용방법은 검색을 통해 쉽게 얻을 수 있었으며, 저의 경우, 벨로퍼트님의 레퍼런스를 참조 했습니다.
1. API 연동의 기본

useEffect(() => {
    const fetchUsers = async () => {
      try {
        // initialization of Error , Users
        setError(null);
        setUsers(null);
        // initialization of loading status (상태초기화)
        setLoading(true);
        const response = await axios.get(
          "https://api.hatchways.io/assessment/students" // 데이터를 받아올 OpenAPI
        );
        setUsers(response.data);
        setStudents(response.data); // UserContext에 받아온 데이터 담아주기
      } catch (e) {
        console.log("ERROR: Something wrong. please checking the code.");
        setError(e);
      }
      setLoading(false);
    };

    fetchUsers();
  }, []);

2. Context API 를 사용한 전역 상태 관리

리덕스의 사용과 Context API 사용, 이 두가지를 놓고 고민을 많이 했습니다.
물론, 처음엔 부모/자식간의 데이터 이동을 통해 손쉽게? 만들려고 했으나, 규모가 작은 프로젝트임에도 불구하고, 데이터 이동이 번거롭다는게 보이더라구요. 결국, 튜터님의 조언에 따라 Context API 를 활용해서 구축하였습니다.

Context API를 여러곳에서 손댈 경우, 해당 데이터의 무결성을 보장하기 어렵기 때문에,
학생의 기본 정보는 UserContext에 담고, 중간에 추가되는 Tag 정보는 TagsContext에 새롭게 담아서 관리했습니다.

UserContext

export const UserProvider = (props) => {
  const [students, setStudents] = useState([{}]);

  return (
    <UserContext.Provider value={[students, setStudents]}>
      {props.children}
    </UserContext.Provider>
  );
};

TagsContext

export const TagProvider = (props) => {
  const [tags, setTags] = useState({});

  return (
    <TagContext.Provider value={[tags, setTags]}>
      {props.children}
    </TagContext.Provider>
  );
};

Context API의 참조 내용 역시, 벨로퍼트님의 레퍼런스를 참조했습니다.
22. Context API 를 사용한 전역 값 관리

3. 학생의 이름과 추가된 태그를 참조한 이중 검색

이번 프로젝트를 진행하면서 많이 벽을 만났지만, 그중 가장 애먹었던 부분이 이중 검색 부분이였습니다. 사실, 이름을 통한 검색은 어렵지 않게 구현할 수 있었습니다.

  if (searchField !== "") {
    filteredUsers = props.students.filter((student) => {
      const fullName = student.firstName.concat(student.lastName);
      return fullName.toUpperCase().includes(searchField.toUpperCase());
    });
  }

검색조건: 검색어가 포함된 이름과 성을 가진 학생을 검색해라.
입력창(searchField)이 빈값이 아닐때 실행
> 학생의 성과 이름을 하나의 변수값에 담아준다

const fullName = student.firstName.concat(student.lastName);

대소문자 구분을 없앤후, 이름에 검색어를 포함하고 있으면 해당 학생을 돌려준다.

return fullName.toUpperCase().includes(searchField.toUpperCase());

4. 클릭시 카드를 확장하여 추가정보 표시

리액트 아이콘(react-icons)

우측 상단에 +(플러스) / -(마이너스) 버튼은 html의 버튼에 React Icon을 붙혀서 뷰를 구현하였습니다. 해당 버튼을 클릭하면 해당 학생의 전과목 스코어가 보여지고, 한번 더 클릭하면 닫힙니다.

<ExpendableButton value="button" onClick={toggle}>
// 클릭시 toggle함수 실행

버튼을 클릭하면 toggle함수가 실행되고, 해당 함수에서 상태값을 참조/변경합니다.

  const toggle = () => {
    if (!isOpen) {
      console.log("isOpen");
      setIsOpen(true);         // 현재 카드가 열려있으면
      setIsOpenText(FaMinus);  // setIsOpenText에 마이너스 아이콘을 담아준다. 
    } else {
      console.log("!isOpen");
      setIsOpen(false);        // 현재 카드가 닫혀있으면
      setIsOpenText(FaPlus);   // setIsOpenText에 플러스 아이콘을 담아준다.
    }
  };

여기서 변경된 isOpen의 상태값은 카드가 보여지는 곳에서 참조됩니다.

const DropdownWrapper = styled.div`
  display: ${({ isOpen }) => (isOpen ? "contents" : "none")}; 
  // isOpen상태에 따라 보여주거나 / 감추거나
`;

const GetScores = (score) => {

... (코드중략)

 <DropdownWrapper isOpen={isOpen}>          
     {score.grades.map((grade, idx) => {
        return (
         <TestScore key={idx}>
            Test {idx + 1}: {grade}%
         </TestScore>
        );
     })}
 }

마무리

이렇게 정리해놓고 보면 제가 굉장히 잘해서 손쉽게 구현해둔것 같지만, 절대 아닙니다.
어디서부터 손대야 할지 몰라서 수없이 튜터님과 주변 지인 개발자분들을 귀찮게 했네요.
구글 검색창에서 나온 검색결과를 보며, 울고 웃고를 반복했고, 수 많은 사탕과 아이스크림을 먹으며 스트레스를 풀었습니다.

보통, 클론 코딩을 하면서 html/css/JavaScript와 친해졌는데, 이번 프로젝트는 맨땅에 헤딩하며 시작한거 더 뜻 깊게 남습니다.

제가 만들었지만, 시간이 지난 후 돌려보면 ‘이 코드는 어디서 나온 코드지?’ 라고 되묻는 경우가 많아, 이번 일지를 준비했으며, 마지막으로 제게 처음 리액트라는 분야를 가르쳐 준, 스파르타 코딩 클럽의 임민영 튜터님. 강의 내용과 무관한 질문에도 하나하나 답변해 주셔서 너무너무 감사드리며, 기록을 마무리 하겠습니다.

스타르타!!

증거사진1 – 튜터님을 귀찮게한 채팅창