Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ 4주차 기본과제 ] 👨🏻‍💻 깃헙 프로필 검색하기 #8

Closed
wants to merge 16 commits into from

Conversation

iamphj3
Copy link
Collaborator

@iamphj3 iamphj3 commented Nov 11, 2022

✨ 구현 기능 명세

  • 기본 과제

    • 1. react-router-dom 을 이용해 url 에 따른 컴포넌트를 나타낸다

      route를 이용하여 url에 따라 컴포넌트를 보여주였습니다!

      function Router() {
        return (
          <BrowserRouter>
            <Routes>
              <Route path="/search" element={<SearchPage />}>
                <Route path=":username" element={<UserPage />} />
              </Route>
            </Routes>
          </BrowserRouter>
        );
      }
      
      • 1. /search 에서, 검색창 및 검색 history 를 포함한 내용을 나타낸다

      SearchPage 컴포넌트에서 form 태그를 사용한 검색창 및 검색 history를 보여주는 드롭다운을 구현해 주었습니다!

      • 2. /search/:userId 에서, userId 에 맞는 유저 정보를 받아온다 (GET 통신)

      useParams 훅을 통해 검색값 파라미터를 가져오고, 해당하는 api를 받아옵니다.
      api를 불러오기 전에 다른 컴포넌트들이 렌더링되지 않도록 async와 await을 사용하여 비동기 처리를 해주었습니다.

      const { username } = useParams();
      const [loading, setLoading] = useState(true);
      const [users, setUsers] = useState([]);
      
      // 검색 input값 받아와서 해당 유저 정보 불러오기
      const getUser = async (username) => {
        const response = await axios.get(
          `https://api.github.com/users/${username}`,
          {
            headers: {
              Authorization: `Bearer ${process.env.REACT_APP_GITHUB_PERSONAL_ACCESS_TOKEN}`,
            },
          }
        );
        setUsers(response.data);
        setLoading(false);
      };
      
    • 2. 검색 history를 구현한다
      • 1. 검색창에 입력을 하고 검색을 할 때에 history 배열에 추가한다

      handleSubmit 함수를 통해, 검색 시 history 배열에 추가하고 로컬스토리지를 업데이트합니다.

      // history 배열에 넣기 (중복 아닐 때)
      if (!history.includes(username)) {
        history.push(username);
      }
      // 로컬스토리지 업데이트
      setLocalStorage();
      
      • 2. 입력창에 focus 될 때, history 박스가 나타나고, 검색하거나 다른 곳을 클릭할 때 박스를 없앱니다

      focus 상태를 저장하는 state를 만들어서, focus가 true일 때만 드롭다운이 보여지게 하였습니다.
      input에 포커스 할 때 setFocus(true), 검색이나 히스토리를 클릭할 때 setFocus(false)로 변경합니다.

       const [focus, setFocus] = useState(false); // input 포커싱 상태
      
       // return 구문 
        {focus && (
        <SearchHistories>
          {history.map((_history) => (
            <DropdownList>
              <SearchHistory onClick={() => onClickHistory(_history)}>
                {_history}
              </SearchHistory>
              <DeleteButton onClick={() => onClickDelete(_history)}>
                X
              </DeleteButton>
            </DropdownList>
          ))}
        </SearchHistories>
      )}
      
    • 3. history에 저장된 해당 유저를 누르면, 검색창에서 검색한 것과 같이 /search/:userId 로 이동한다

      히스토리를 누르면 navigate를 통해 해당 유저의 프로필을 불러온 페이지로 이동합니다.

      // 드롭다운 히스토리 클릭 시 해당 유저 프로필 페이지로 이동, 드롭다운 사라짐
      const onClickHistory = (_history) => {
        navigate(`/search/${_history}`);
        setFocus(false);
      };
      
      • 1. 해당 리스트를 delete 하는 버튼을 추가한다

      Link를 통해 삭제 버튼 클릭 시 search 페이지로 이동하게 하였습니다.

        <CloseBtn>
          <Link to="/search"> X </Link>
        </CloseBtn>
      
    • 3. (선택) Loading 을 나타내는 state 혹은 Suspense 를 추가하여 로딩 중을 나타내는 UI를 구성한다

      loading 상태를 관리하는 state를 만들고, 옵셔널 체이닝을 통해 loading이 true일 때 로딩중 이미지를 보여줍니다.

       const [loading, setLoading] = useState(true);
      
      // retun 구문
            {loading ? (
              <LoadingImg src="./image/loading.gif" alt="로딩중" />
            ) : (
                  // 프로필 UI 보여주기
            )
            }
      
      
    • 4. 유저 정보 박스 내에 X(닫기) 버튼을 구현하여, 검색 전 상태로 되돌리도록 구현한다

      Link를 통해 X 버튼 클릭 시 search 페이지로 이동하게 하였습니다.

         <CloseBtn>
          <Link to="/search"> X </Link>
        </CloseBtn>
      

🎁 PR Point

  • 로딩이 너무 빨리 돼서 로딩중 화면을 볼 수가 없어요...ㅜ.ㅜ
  • 드롭다운 위치를 그냥 absolute로 박아버렸는데.. 이게 최선의 방법일까요.. ? 위 검색창을 기준으로 위치시키는 방법도 있는지 궁금해요
  • 히스토리 저장하는 건 저번 벨로그 해시태그 저장 기능과 동일하게 배열에 저장 -> 로컬스토리지 저장 방식으로 구현했는데 좋은 방법이 맞는지 피드백 부탁드립니다!

😭 소요 시간, 어려웠던 점

  • 3일
  • 50번 이상 api를 요청하면 막힌다는것(?) 을 알게 되었습니다.. 그래서 깃허브에서 token을 받아오면 5000번으로 늘어난다는 사실을 알려준 재욱싀 너무 감사했습니당
  • 포커스 할 때만 드롭다운이 뜨게 하는 부분이 어려웠는데 onFocus라는 이벤트를 이용하면 된다는 것을 알게되었습니다!! 재욱싀 고마워...
  • 구글링하다가 PropType 과 isRequired라는 것을 알게되었는데, 이 설정은 컴포넌트 필수사항은 아니지만 대형 프로젝트에서 다른 개발자들과 협업을 할 때 사용하면 유용하다고 해서 한번 추가해보고 싶습니다!

😎 구현 결과물

video1439188108

@iamphj3 iamphj3 self-assigned this Nov 11, 2022
Copy link

@YOOJS1205 YOOJS1205 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떻게든 해내는 현지 넘 멋있다 ~~!!

export default function SearchPage() {
const [username, setUsername] = useState(""); // input 값
const [focus, setFocus] = useState(false); // input 포커싱 상태
let [history, setHistory] = useState([]); // 검색 히스토리를 저장할 배열
Copy link

@YOOJS1205 YOOJS1205 Nov 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const [history, setHistory] = useState(JSON.parse(localStorage.getItem('history') || '[]'));

이렇게 한 번에 상태 관리하는 것은 어떨까?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 이렇게 하면 따로 initHistory()로 초기화를 안해줘도 되겠구나!!! 넘 좋은거 배워가용!!!

const handleChange = (e) => setUsername(e.target.value);

// 검색 클릭 시 history 배열에 넣고 로컬스토리지에 저장
const handleSubmit = async (e) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await 어디갔어 !!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아앗 ,,, 부끄러운 내코드,,,,,,,,

// 히스토리 삭제
const onClickDelete = (_history) => {
const num = history.indexOf(_history);
setHistory(history.splice(num, 1));
Copy link

@YOOJS1205 YOOJS1205 Nov 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React에서 불변성을 지켜주는 것이 핵심인데, splice 메서드를 사용하면 지켜지지 않아!
https://devbksheen.tistory.com/258

setHistory((prev) => prev.filter((_, index) => index !== num))

이렇게 forEach메서드를 활용하면 좋을 것 같아!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@YOOJS1205 리액트 불변성도 생각하지 않고 막 갈긴 내 코드........ 한없이부끄럽다........................... 필터를 사용해서 수정 완료!!!!

);
}

// -------------------------- style --------------------------

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정확한 기준이 있는 것은 아닌데, 코드가 100줄 이상 넘어가면
스타일 코드를 다른 파일로 빼는 방법도 좋을 것 같아

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 그렇구나!!! 관련해서 한번 더 찾아봐야게따!!! 고마워 준상!!!~~~

);
setUsers(response.data);
setLoading(false);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try catch 문을 활용해서 예외처리를 해주면 에러가 떴을 때 디버깅하기 훨씬 용이해져서 좋을 것 같아.
https://ko.javascript.info/try-catch

const getUser = async (username) => {
  try {
    // 시도할 코드들
  } catch (error) {
    // 오류가 발생했을 때 발생시킬 코드들
  }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try catch 문으로 예외처리도 추가해줘따!!! 관련 자료까지 ㅠㅠㅠ 준상 넘 고마워 ㅠㅠㅠㅠㅠㅠㅠ @YOOJS1205

@iamphj3 iamphj3 closed this Feb 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants