Skip to content

Conversation

@hansoojeongsj
Copy link
Member

@hansoojeongsj hansoojeongsj commented May 4, 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인 경우(로딩 중인 경우) 스피너를 출력한다.

공유과제

제목: react의 props와 state

링크 첨부 : https://sweeb.tistory.com/73


🚀 내가 새로 알게 된 점

App.jsx에 스타일을 주는 게 좋지 않아 보여서, global을 주고 header와 아래 탭들을 묶는 Layout컴포넌트를 뒀습니다.

idle, pending, resolved, rejected에 대한 분기 처리를 하는데, idle 상태가 기본 상태와 같아 따로 써주지 않고 다른 3가지 상태만 조건을 주었습니다.

야구게임과 깃허브 검색에서 동일한 스타일의 input이 사용되고 있어 공통으로 사용할 수 있게 분리했습니다.

githubSearch안에 githubCard 부분이 로직이 복잡하고 길게 느껴져 이 부분만 컴포넌트 분리했습니다.

theme을 넣고 싶었는데 ThemeProvider 이외에, 스타일을 줄때 css={textStyle(theme)} 와 같이 주고 스타일 파일에서도 theme.colors.blue100 과 같이 길게 써주어야 해서 오히려 더 개발에 오래 걸린다 생각이 들어 제외했습니다.

한 파일에 와다다 쓰는 걸 좋아하는데 hook도 분리해보고 util도 분리하는 시도를 해봤습니다.

기존에는 reset.css를 둬 코드로 다 처리했는데, emotion-reset을 사용해보았습니다.

어제 세미나에서 4주차 실습을 머지한다는게, 3주차 과제를 머지해버렸나봅니다 ..! 바로 다시 올렸습니다. 죄송해여...


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

  • 새로 알게 된 점에서 언급한 모든 부분에 대해.. 고민했던 것 같습니다.

⏳ 소요 시간

  • 9h

🤳🏻 구현 결과물

헤더 확인, 숫자야구 유효성 검사 및 틀렸을 때

+.-.Chrome.2025-05-02.14-26-01.mp4

숫자야구 정답일 때

+.-.Chrome.2025-05-02.14-26-44.mp4

깃허브 검색 웹 페이지에서

+.-.Chrome.2025-05-02.14-19-53.mp4

깃허브 검색 개발자 도구에서

+.-.Chrome.2025-05-02.14-28-41.mp4

Copy link

@Dubabbi Dubabbi left a comment

Choose a reason for hiding this comment

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

전반적으로 코드 구조와 컴포넌트 설계가 매우 깔끔하게 이루어져 있어 인상적이었습니다🌸 각 컴포넌트가 명확한 역할을 가지고 분리되어 있어서 가독성이 뛰어나고, hooks와 utils, 스타일 파일도 잘 나뉘어 있어 유지보수에 강한 구조를 가지고 있네요!! 사용자 경험을 고려한 로딩 상태 처리, 에러 메시지 출력, 최근 검색어 기능 등도 적절히 구현되어 있어 실제 사용 흐름에서 불편함 없이 자연스럽게 이어지는 점이 좋았습니다. 상태 관리도 간결하고 명확하게 이루어져 있고 전반적인 네이밍과 스타일 작성도 일관성 있게 잘 유지해 주신 덕분에 순차적으로 원활하게 코드 리뷰를 진행할 수 있었습니다. 전체적인 완성도가 높아서 많이 배워갑니다. 3주차 과제 수고하셨습니다🥰

} from './GithubCard.style';

const GithubCard = ({ user, onClose }) => {
if (!user) return null;
Copy link

Choose a reason for hiding this comment

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

조건부 렌더링을 활용해 user가 없을 때 렌더링을 방지한 점이 좋습니다!👍

<button onClick={() => getUserInfo(user)}>{user}</button>
<button
onClick={() => removeRecent(user)}
aria-label="최근 검색어 삭제"
Copy link

Choose a reason for hiding this comment

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

👍👍

Copy link

Choose a reason for hiding this comment

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

각 역할에 맞는 스타일 네이밍과 분리도가 좋아서 가독성이 좋습니다!! 굿굿

<h1 css={titleStyle}>⚾ 숫자야구 || 깃허브 검색 😺</h1>
<nav css={navStyle}>
<div
css={navItemStyle(selectedTab === '깃허브')}
Copy link

Choose a reason for hiding this comment

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

조건에 따라 스타일링을 동적으로 처리하신 부분이 매우 좋네용

Comment on lines +13 to +24
<div
css={navItemStyle(selectedTab === '깃허브')}
onClick={() => onChangeTab('깃허브')}
>
깃허브 검색
</div>
<div
css={navItemStyle(selectedTab === '야구')}
onClick={() => onChangeTab('야구')}
>
숫자 야구
</div>
Copy link

Choose a reason for hiding this comment

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

div 대신 button으로 변경하면 좋을 것 같습니다~!

Copy link

Choose a reason for hiding this comment

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

색상 값 #142755, #6b74aa 등이 반복 사용되고 있는데, 테마에서 가져오면 유지보수와 일관성 면에서 더 좋습니다!!

gap: 2rem;
`;

export const navItemStyle = (active) => css`
Copy link

Choose a reason for hiding this comment

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

동적 스타일을 함수형으로 처리하신 점 이 좋습니다!

Comment on lines +36 to +41
${active &&
css`
background-color: white;
color: #142755;
font-weight: 500;
`}
Copy link

Choose a reason for hiding this comment

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

삼항처럼 한 줄에 복잡한 조건부 CSS가 있는 경우에는

${active ? css`
  background-color: white;
  color: #142755;
  font-weight: 500;
` : ''}

이렇게 정리하면 가독성이 더 좋아질 것 같아요!

Comment on lines +42 to +46
const removeRecent = (user) => {
const updated = recent.filter((r) => r !== user);
setRecent(updated);
saveToLocalStorage('userList', updated);
};
Copy link

Choose a reason for hiding this comment

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

setRecent(updated)saveToLocalStorage('userList', updated)가 여러 곳에서 반복되고 있어서 공통 로직을 함수로 추출하면 중복을 제거하고 가독성을 향상시키는 데 도움이 될 것 같아요!

Comment on lines +23 to +25
if (!/^\d{3}$/.test(inputValue) || new Set(inputValue).size !== 3) {
setMessage('⚠️ 서로 다른 숫자 3자리를 입력해주세요!');
return;
Copy link

Choose a reason for hiding this comment

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

유효성 검사는 별도 함수로 추출하면 재사용성과 가독성 모두 좋아질 것 같아요!

import Layout from './components/Layout/Layout';

function App() {
const [tab, setTab] = useState('깃허브');

Choose a reason for hiding this comment

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

영어로만 상태 지정해봤었는데 한글 문자열로 상태를 지정한 이유가 있을까요?

href={user.html_url}
target="_blank"
rel="noopener noreferrer"
aria-label="깃허브 프로필로 이동"

Choose a reason for hiding this comment

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

aria-label, alt 등 접근성 고려한 부분 좋아요! 저도 의식해서 작성하는 습관을 길러봐야겠어요

Choose a reason for hiding this comment

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

저는 귀찮아서 짧은 코드일 경우 스타일 분리 안하고 한 파일에 작성했는데 반성하고 갑니다.. 다음 과제부턴 분리 열심히해보겠습니다😢

Choose a reason for hiding this comment

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

props로 받아 재사용성 확보하고 컴포넌트가 매우 단순하고 직관적이어서 다른 폼에서도 쉽게 활용 가능하고 코드도 깔끔해요!

Choose a reason for hiding this comment

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

기능 분리가 명확하고 각 기능이 함수로 잘 나뉘어 있어 읽기 쉬웠어요! 고생하셨습니다🍀

@jjangminii
Copy link

기능별로 잘 분리된 컴포넌트 구조와 커스텀 훅 활용이 인상적이며, Emotion 기반의 스타일 시스템도 일관성 있고 재사용성이 뛰어나게 구성되어 있습니다. hooks, components, styles, utils 등 디렉토리 구조가 명확하게 나뉘어 있고 각 책임에 충실해 전체적으로 코드 가독성이 매우 좋았어요 ❤️
각 컴포넌트에서도 재사용성과 고민의 흔적이 잘 드러나며, 완성도 높은 코드입니다. 고생 많으셨습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants