깃허브 API를 활용한 개인프로젝트

Chanho Yoon·2021년 7월 9일
1

기억보단 기록

목록 보기
7/8
post-thumbnail
post-custom-banner

ReduxTypescript를 공부하던 중 직접 간단한 웹 서비스를 만들어보며 적용해보는게 좋을 것 같아 만들게 되었습니다.

사용한 기술

react hook styled-component redux typescript

구현한 기능

  • 유저(name) 검색
  • 유저 즐겨찾기 추가 및 삭제
  • 유저 리스트 첫 단어를 기준으로 첫 아이템에만 초성 출력
  • 즐겨찾기 조회 및 삭제
  • 즐겨찾기 검색

src 폴더 구조

├── App.tsx
├── assets
│   └── styles
├── components
│   ├── List
│   ├── Search
│   ├── Tab
│   └── main.tsx
├── hooks
│   ├── actions
│   ├── favorites
│   ├── localstorage
│   ├── search
│   └── users
├── index.tsx
├── modules
│   ├── favorites.ts
│   ├── index.ts
│   ├── search.ts
│   ├── tabs.ts
│   └── users.ts
├── react-app-env.d.ts
└── reportWebVitals.ts

프레젠테이셔널 컴포넌트 & 컨테이너 컴포넌트?

  • 프레젠테이셔널 컴포넌트 : 오직 View만을 담당하는 컴포넌트 (UI 로직)
  • 컨테이너 컴포넌트 : dispatch 등 (비즈니스 로직)

위와 같이 컴포넌트를 분리하여 작성할려고 하다가 벨로퍼트님의 블로그 글을 읽고 components 폴더에 UI 로직 컴포넌트를 만들어 사용하고 커스텀 Hook(hooks)을 만들어 사용하기로 했다.
결과는 완전 대만족이다!!

간단히 아래와 같은 방식이다.

// components/list/userList 
const UserList = () => {
  const users = useGetUsers();

  return (
    <ListWrapperStyle>
      <ListUlStyle>
        {users?.map((item: User) => (
          <UserListItem key={item.id} user={item} />
        ))}
      </ListUlStyle>
    </ListWrapperStyle>
  );
};
export default UserList;

// hooks/users/useGetUsers.ts (커스텀 훅)
import { useSelector } from 'react-redux';
import { RootState } from '../../modules';

export default function useGetUsers() {
  return useSelector((state: RootState) => state.users);
}

component에선 ui를 담당하는데 화면에 출력시킬 데이터는 useDispatch, useSelect, ..등로 이루어진 커스텀 훅을 작성하여 사용하면 된다.

redux

├── modules
│   ├── favorites.ts
│   ├── index.ts
│   ├── search.ts
│   ├── tabs.ts
│   └── users.ts

패턴은 다양한 패턴이 있지만 ducks 패턴을 이용해 액션/액션함수/리듀서를 하나의 module로 관리했습니다.

const rootReducer = combineReducers({ users, favorites, search, tabs });

위와 같이 리듀서 함수를 리듀서에 해당하는 상태들만 독립적으로 관리할 수 있도록 분리하였습니다.

users 타입과 favorites(즐겨찾기) 타입

usersfavorites 상태는 같기 때문에 type은 둘다 동일하다.

export type User or Favorites = {
  avatar_url: string;
  events_url: string;
  followers_url: string;
  following_url: string;
  gists_url: string;
  gravatar_id: string;
  html_url: string;
  id: number;
  login: string;
  node_id: string;
  organizations_url: string;
  received_events_url: string;
  repos_url: string;
  score: number;
  site_admin: boolean;
  starred_url: string;
  subscriptions_url: string;
  type: string;
  url: string;
  isFirstWord?: string;
  name?: string;
};

isFirstWord : 리스트에서 각 첫 번째 아이템에만 초성이 보일 수 있도록 하기 위한 상태값
name : github api로 name으로 유저를 검색하면 response에 name 데이터는 넘어오지않아 state값에 검색한 value값을 넣어줘야함

탭메뉴 선택 (유저, 즐겨찾기)

검색폼 (유저, 즐겨찾기)

tabIndex를 활용해 유저 검색, 즐겨찾기 검색인지 구별하여 해당 탭 검색할 수 있도록 구현
searchWord는 상태 관리를 통해 어디서든 접근해서 사용할 수 있도록 했는데 이 상태값은 즐겨찾기 검색부분 filter처리할 때 필요했다

리스트 출력 (유저, 즐겨찾기)

유저 리스트 / 즐겨찾기 리스트

고민했던 부분들

  • 부분적으로만 관리했던 state들을 users(유저) 와 favorites(즐겨찾기)로 분리하여 상태 관리를 하니 한결 더 수월하게 구현할 수 있었다. 예를 들어 즐겨찾기에서 삭제하면 실시간 반영으로 즐겨찾기 리스트에서 제거가 되어야하는데 기존엔 로컬스토리지에서만 사라지고 즐겨찾기 리스트에서는 새로고침하지 않는 이상 계속 남아있었다. 이러한 문제는 바닐라자바스크립트에서는 DOM에 직접 접근하여 해당 리스트를 삭제시키면 됐었지만 리액트에선 강제로 리렌더링을 시켜야 했는데 올바른 로직은 아니라고 생각하여 모든 값들을 state로 관리하기로 했다.
    redux를 사용하는 이유에 대해 조금 더 알게되었던 계기가 되었음

  • 처음 즐겨찾기 검색 기능을 구현할 때 favorites(즐겨찾기) state값을 filter처리하여 다시 뿌려주는 형식으로 했었는데 문제는 다시 검색시 전체 즐겨찾기 리스트를 로컬스토리지에서 가져와 다시 state에 등록해줘야하는 문제점이 발생하였다.
    검색 필터처리는 state값을 직접 건드리지 않고 컴포넌트에서 필터 처리된 리스트만 보여주면 되겠다 싶었다. 그렇게 하기 위해 search라는 상태 관리를 추가해 구현하였다.

post-custom-banner

1개의 댓글

comment-user-thumbnail
2021년 8월 16일

자바스크립트로 스트레스 풀던 찬개발자 이제는 타입스트립트까지..!

답글 달기