Skip to content

[3주차 과제] 숫자 야구 게임 & 깃허브 검색 #5

Open
yeonilil wants to merge 12 commits intomainfrom
week3-homework
Open

[3주차 과제] 숫자 야구 게임 & 깃허브 검색 #5
yeonilil wants to merge 12 commits intomainfrom
week3-homework

Conversation

@yeonilil
Copy link
Copy Markdown
Collaborator

@yeonilil yeonilil commented May 2, 2025

🤙🏻 구현 기능 명세

💡 기본 과제

  • Context API, 전역상태 라이브러리 사용 X (ThemeProvider 제외)
  • CSS라이브러리 사용하기
  • 외부 UI 라이브러리 없이 직접 컴포넌트를 구현합니다.
  1. 헤더

    • 헤더에는 제목과 2개의 탭(깃허브 검색, 숫쟈야구 게임)이 위치한다.
    • 탭을 클릭하면 각 탭에 맞는 페이지를 렌더링 한다. (라우팅 X, URL은 안바뀜)
  2. 숫자야구 게임

    • 게임이 시작되면 무작위 3자리 정답 숫자를 생성한다.
    • 정답을 맞춘경우 3초후 게임을 초기화 한다.

    2.1 Input

    • '최대 3자리'의 '숫자'만 입력 가능하다.
    • 중복된 숫자는 입력이 불가능하다.

    2.2 Message(사용자의 입력값에 대한 정보를 출력)

    • '최대 3자리'의 '숫자'가 아니라면, 경고 문구를 출력한다.
    • 정답을 맞춘경우 게임 승리 문구를 출력한다.
    • 오답인 경우 게임 결과를 출력한다 (ex. 1스트라이크 1볼)

    2.3 List(게임 진행 상황을 출력)

    • 이전 시도 결과를 출력한다. (ex. 123 - 1스트라이크 1볼)
    • 게임이 초기화되면 리스트도 초기화된다.
  3. 깃허브 검색 기능

    • 깃허브 공공 API를 이용한다. (세부 설명에 제공되는 코드 활용)

    3.1 Input

    • 제출 시 GitHub API를 통해 해당 아이디의 프로필 정보를 요청한다.

    3.2 Card

    • 검색 성공시 사용자 정보를 출력한다.
    • 출력 항목 -> (아바타 이미지 (avatar_url), 이름 (name), 깃허브 아이디 (login), 자기소개 (bio), 깃허브 주소 (html_url), 팔로워 수 (followers), 팔로잉 수 (following)
    • x 버튼을 누르면 사용자 정보가 사라진다.
    • 사용자 이름 or 아바타 이미지를 클릭하면 해당 깃허브 Url로 이동한다. (새로운 탭이 생겨야함)

    3.3 최근 검색어

    • 검색한 아이디는 최근 검색어로 저장된다.
    • 브라우저를 새로고침해도 검색 기록은 유지된다. (localStorage 활용)
    • x 버튼을 누르면 해당 최근 검색어는 삭제된다. (localStorage에서도 삭제)

🔥 심화 과제

  1. 숫자야구 게임

    • 시도 횟수는 최대 10회로 제한한다.
    • 시도 횟수가 10번이 넘어가면 5초후 게임을 초기화 한다.
    • 시도 횟수가 10번이 넘어가면, 게임패배 문구를 출력한다.
  2. 깃허브 검색 기능

    • 최근 검색어를 클릭하면 해당 아이디로 재검색이 실행된다.
    • 최근 검색어는 최대 3개까지만 유지되며, 최신 검색어가 가장 오른쪽에 위치한다.
    • 중복된 검색어는 최근 검색어에 저장되지않는다.
  3. 깃허브 검색 userInfo status에 따른 분기처리

    • idle, pending, resolved, rejected에 대한 분기 처리를 진행한다.
    • rejected인 경우(검색 결과가 없는 경우) 결과를 찾을 수 없다는 메세지를 출력한다.
    • pending인 경우(로딩 중인 경우) 스피너를 출력한다.

공유과제

제목:

링크 첨부 :


🚀 내가 새로 알게 된 점

  • emotion 라이브러리를 사용해 css를 구현했습니다. 기존에는 Tailwind CSS나 일반 CSS Modules을 주로 사용했는데, CSS-in-JS는 어떤 장점이 있는지 알아보고 싶어 도입하게 되었습니다! 반복되는 스타일을 별도 파일로 분리하고, 여러 컴포넌트에서 import하여 재사용할 수 있어 코드의 재사용성과 유지보수성을 높일 수 있다는 점이 좋았다고 생각합니다.

🤔 구현 과정에서의 어려웠던/고민했던 부분

  • input, button 등 여러 컴포넌트에서 중복되는 스타일을 Emotion css로 어떻게 깔끔하게 분리하고 재사용할지 고민이 들었습니다. 스타일이 각 컴포넌트 내부에 흩어져 있어서 유지보수가 어렵고 코드 길이가 길어졌습니다. 공통 스타일은 inputStyles.js로 분리해 export하고 필요한 컴포넌트에서 import하여 재사용하는 방식을 사용해 개선했습니다!

⏳ 소요 시간

  • 6h

🤳🏻 구현 결과물

숫자 야구 게임

숫자 야구

깃허브 검색

깃헙 검색

@yeonilil yeonilil self-assigned this May 2, 2025
@yeonilil yeonilil requested review from maehwasoo, mmaybei and yeeeww May 2, 2025 06:25
Copy link
Copy Markdown
Member

@maehwasoo maehwasoo left a comment

Choose a reason for hiding this comment

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

이번 주차 과제도 정말 고생 많으셨습니다~!!

import { css } from "@emotion/react";
import closeIcon from "../../assets/ic-close.svg";

const cardStyle = css`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

별도의 styles.js 파일로 분리하는 것도 좋아보입니다~!

}
}, []);

const updateRecentSearches = (username) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

최근 검색어 관리 로직이 컴포넌트 내에 직접 구현되어 있습니다. 해당 로직을 재사용 가능한 커스텀 훅(예를 들어 useRecentSearches)으로 분리하면, 코드 중복도 줄이고 관심사를 분리하여 단일 책임 원칙도 지키고 더욱 추상화가 가능하여 추후에 더 복잡한 시스템 구현에 도움이 많이 될 것 같습니다!

setIsGameOver(false);
};

const handleSubmit = () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

handleSubmit 함수가 너무 길어 보이며 여러 책임(입력 검증, 게임 로직 처리, 상태 업데이트)을 가지고 있습니다.
이를 validateInput, checkGameResult, updateGameState 등의 더 작은 함수로 분리하면 코드 가독성과 유지보수성이 향상될 것 같습니다!

function Input({ input, setInput, onSubmit }) {
const [warning, setWarning] = useState("");

const handleChange = (e) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

현재 입력 유효성 검사 로직이 구현되어 있는데 BaseballContainer.jsx의 handleSubmit 함수(line: 61-64)에서도 유사한 검증을 수행하고 있어 보입니다.
유효성 검사 로직을 한 곳으로 통합하거나, 별도의 유틸리티 함수로 분리하여 재사용하는 방식으로 리팩토링하면 유지보수와 관리에 더욱 좋을 것 같습니다!

@@ -0,0 +1,7 @@
import React from "react";

function Message({ text }) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Message 컴포넌트가 비록 매우 간단하지만, 별도의 파일로 분리되어 있어 관심사 분리가 잘 구현되어 있다고 생각됩니다. 이 부분은 개인적으로 매우 좋다고 생각됩니다~!

import GithubProfileCard from "./GithubProfileCard";
import RecentSearchList from "./RecentSearchList";

function GithubSearchContainer() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

GithubSearchContainer 컴포넌트가 조금 크다고 생각됩니다. 현재 검색 입력 처리, API 호출, 최근 검색어 관리 등의 여러 책임을 가지고 있는데, 이를 SearchForm, SearchResults 등 더 작은 컴포넌트로 분리하고, 상태 관리 로직은 커스텀 훅으로 분리하여 (하나의 컴포넌트는 하나의 책임만 가질 수 있도록 한다면) 코드 가독성과 유지보수성이 매우 향상될 것 같습니다!

`;

function App() {
const [activeTab, setActiveTab] = useState("github");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

activeTab의 초기값으로 "github" 문자열이 하드코딩되어 있는데 이 값은 이후에 조건부 렌더링에서도 또 사용되고 있습니다. 상수로 분리하여 활용한다면 관리 측면에서 더욱 좋을 것 같습니다

<button
css={buttonStyle(activeTab === "github")}
onClick={() => onTabChange("github")}
disabled={activeTab === "github"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

마찬가지로 여기도 탭 이름이 "github", "baseball"으로 하드코딩되어 있는데 해당 값들은 App.jsx에서도 사용되므로 일관성을 유지하기 위해 상수로 분리하거나 props로 전달받는 것도 좋을 것 같습니다!

localStorage.setItem("recentSearches", JSON.stringify(limitedList));
};

const getUserInfo = async (username) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

API 호출 로직이 컴포넌트 내에 직접 구현되어 있는데 별도의 API 서비스 모듈이나 커스텀 훅으로 분리하면 재사용성과 관리 측면에서 더욱 좋을 것 같습니다!

Comment on lines +56 to +59
const handleSearch = () => {
getUserInfo(searchInput.trim());
setSearchInput("");
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

username.trim()은 getUserInfo 안에서 처리하는 게 다른 곳에서 getUserInfo를 호출할 때 유효성 측면에서도 안전할 것 같습니다!

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.

3 participants