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

[3주차 심화 과제] 닮은 동물 찾기 #8

Open
wants to merge 22 commits into
base: week3
Choose a base branch
from

Conversation

moondda
Copy link
Contributor

@moondda moondda commented Dec 11, 2023

✨ 구현 기능 명세

🌠 심화 과제

  1. theme + Globalstyle 적용

    • 전역으로 스타일을 사용할 수 있도록 적용해보세요
  2. 애니메이션

    • 랜덤 추천 - 카운트다운에 효과를 넣어서 더 다채롭게 만들어주세요!
  3. 헤더

    • 처음으로 버튼

      → 추천 종류 선택 화면일시 해당 버튼이 보이지 않습니다.

      → 처음 추천 종류 선택 화면으로 돌아갑니다.

      → 모든 선택 기록은 리셋됩니다.

[ 취향대로 추천 ]

  1. 단계 노출

    • 3단계의 진행 단계를 보여줍니다.
  2. 이전으로 버튼

    • 이전으로 돌아가도 선택했던 항목이 선택되어 있습니다.
  • 6. useReducer , useMemo , useCallback 을 사용하여 로직 및 성능을 최적화합니다.

💎 PR Point

기본과제까지 했을 때 금잔디원 + 서현이가 해준 코리 바탕으로 리팩토링 및 심화과제를 했습니다~

☘️ 전역 스타일링을 위해 theme + Globalstyle 적용. 또 많이 쓰는 styled 컴포넌트는 commonStyle에서 export해서 사용했습니당.

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      {RenderPage()}
    </ThemeProvider>
  );
}

☘️ 1초를 기준으로 카운트다운하는 숫자에 애니메이션을 적용하였습니다

const CountContainer = styled.div`
  display: inline-block;
  transform-origin: center;
  padding: 0 0.5rem;
  color: #2aff29;

  @keyframes animate {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.5);
    }
    100% {
      transform: scale(1);
    }
  }

  animation: animate 1s infinite;
`;

☘️ 진행바를 만들어서 단계를 시각화하였습니다. 총 페이지와 현재 페이지 숫자가 기준입니다.

const ProgressBar = ({ currentPage, totalPages }) => {
  const progress = (currentPage / (totalPages - 1)) * 100;

  return <St.Bar progress={progress} />;
};

☘️ 이전으로/다음으로 버튼 + useCallBack

  const navigateProps = {
    goBack: () => {
      setSelectedOptions((prevOptions) => prevOptions.slice(0, -1));
      setShowPage(showPage - 1);
    },
    goForward: (option) => {
      handleOptionSelect(option);
      setShowPage(showPage + 1);
    },
  };
  const handleOptionSelect = useCallback(
    (option) => {
      setSelectedOptions((prevOptions) => [...prevOptions, option]);
    },
    [setSelectedOptions]
  );

이전으로 갈때는 마지막으로 배열에 추가된 option을 slice합니다.
다음으로 갈때는 배열에 option을 추가해줍니다.
handleOptionSelect에 useCallBack을 적용해봤습니당

☘️ 중복되는 코드 함수화

//before
  const [showPage1, setShowPage1] = useState(false);
  const [showPage2, setShowPage2] = useState(false);
  const [showPage3, setShowPage3] = useState(false);

  const goPage1 = (option) => goToPage(option, 1);
  const goPage2 = (option) => goToPage(option, 2);
  const goPage3 = (option) => goToPage(option, 3);

 {showPage1 && <Page1 goPage0={goPage0} goPage2={goPage2} />}
 {showPage2 && <Page2 goPage1={goPage1} goPage3={goPage3} />}
 {showPage3 && <Page3 goPage2={goPage2} goPage4={goPage4} />}
//after
  const [showPage, setShowPage] = useState(0);
  const allPage = [Page0, Page1, Page2, Page3, Page4, PageRandom];

  const showCurrentPage = (props) => {
    const PageComponent = allPage[showPage];
    return <PageComponent {...props} />;
  };

  {(showPage === 1 || showPage === 2 || showPage === 3) && showCurrentPage(navigateProps)}

🥺 소요 시간, 어려웠던 점

6-7h

🌈 구현 결과물

Screen.Recording.2023-12-12.at.3.06.49.AM.mov

Comment on lines +24 to +32
const restartPage0 = () => {
setShowPage(0);
setSelectedOptions([]);
};

const restartPage1 = () => {
setShowPage(1);
setSelectedOptions([]);
};
Copy link
Member

Choose a reason for hiding this comment

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

요거 하나로 묶어보는 방법은 어떨까용??

Comment on lines +34 to +39
const handleOptionSelect = useCallback(
(option) => {
setSelectedOptions((prevOptions) => [...prevOptions, option]);
},
[setSelectedOptions]
);
Copy link
Member

Choose a reason for hiding this comment

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

👍👍👍👍👍👍👍

Copy link
Member

Choose a reason for hiding this comment

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

❤❤❤

Comment on lines +4 to +23
export const ANIMAL_DATA = [
{ answer: [YES, YES, YES], animal: "강아지", imgUrl: "/img/1.png" },
{ answer: [YES, YES, NO], animal: "고양이", imgUrl: "/img/2.png" },
{ answer: [YES, NO, YES], animal: "오랑우탄", imgUrl: "/img/3.png" },
{ answer: [YES, NO, NO], animal: "쿼카", imgUrl: "./img/4.png" },
{ answer: [YES, HALF, YES], animal: "토끼", imgUrl: "./img/5.png" },
{ answer: [YES, HALF, NO], animal: "붕어", imgUrl: "./img/6.png" },
{ answer: [NO, YES, YES], animal: "팬더", imgUrl: "./img/7.png" },
{ answer: [NO, YES, NO], animal: "비버", imgUrl: "./img/8.png" },
{ answer: [NO, NO, YES], animal: "얼룩말", imgUrl: "./img/9.png" },
{ answer: [NO, NO, NO], animal: "돌고래", imgUrl: "./img/10.png" },
{ answer: [NO, HALF, YES], animal: "쥐", imgUrl: "./img/11.png" },
{ answer: [NO, HALF, NO], animal: "미어캣", imgUrl: "./img/12.png" },
{ answer: [HALF, YES, YES], animal: "용", imgUrl: "./img/13.png" },
{ answer: [HALF, YES, NO], animal: "기린", imgUrl: "./img/14.png" },
{ answer: [HALF, NO, YES], animal: "곰", imgUrl: "./img/15.png" },
{ answer: [HALF, NO, NO], animal: "펭귄", imgUrl: "./img/16.png" },
{ answer: [HALF, HALF, YES], animal: "사자", imgUrl: "./img/17.png" },
{ answer: [HALF, HALF, NO], animal: "오리", imgUrl: "./img/18.png" },
];
Copy link
Member

Choose a reason for hiding this comment

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

아 나도 이렇게 배치하는거 참고해서 리팩토링 해야겠다 굳굳!!

@ExceptAnyone
Copy link
Member

전반적으로 엄청 깔끔한 듯???? 나도 리팩토링 미루고 있었는데 너꺼 보니까 얼렁 시작하고 싶어졌다 ㅎㅎ 고생했어 앱잼 얼마 안남았는데 끝까지 화이팅 해보자!!

Copy link
Member

@seobbang seobbang left a comment

Choose a reason for hiding this comment

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

언니 그새 넘 성장한 것 같은데,,?! 고생 많았어 끝까지 해내다니 멋있디 ❤

</div>
<ThemeProvider theme={theme}>
<GlobalStyle />
{RenderPage()}
Copy link
Member

Choose a reason for hiding this comment

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

컴포넌트니까

Suggested change
{RenderPage()}
<RenderPage/>

얘도 JSX 문법으로 이렇게 가져오면 되겠다!

Copy link
Member

Choose a reason for hiding this comment

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

구조 완전 고쳤네 🥺 고생했어 !!!

const [selectedOptions, setSelectedOptions] = useState([]);

const startGame = (page) => {
if (page === "page1") {
Copy link
Member

Choose a reason for hiding this comment

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

이런 "page1" 같은 특정 string값은 상수처리해주면 좋아! 미리 const PAGE1 = "page1" 이런식으로 constants에서 따로 선언해놓고 export 해서 쓰는게 더 좋은 코드 습관이랍니당!

page가 여러개니까 더 구조화해보면 constants 에서

export const PAGE = [ ,"page1", "page2", "page3"];

요렇게 export 해서 PAGE[1] 이런식으로 쓸 수 있겠당
인덱스와 페이지 번호가 헷갈리지 않게 0번째 인덱스는 비워놓았어!

Comment on lines +34 to +39
const handleOptionSelect = useCallback(
(option) => {
setSelectedOptions((prevOptions) => [...prevOptions, option]);
},
[setSelectedOptions]
);
Copy link
Member

Choose a reason for hiding this comment

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

❤❤❤

[setSelectedOptions]
);

const totalPages = 5;
Copy link
Member

Choose a reason for hiding this comment

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

상수는 전체 대문자 스네이크 케이스로 쓰는게 관행!

Suggested change
const totalPages = 5;
const TOTAL_PAGES = 5;

Comment on lines +27 to +36
<QuestionBox>
<AnswerButton
answer="응!!"
onClick={() => handleOptionChange("웅!")}
/>
<AnswerButton
answer="아니."
onClick={() => handleOptionChange("아니ㅠ")}
/>
</QuestionBox>
Copy link
Member

Choose a reason for hiding this comment

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

각 페이지에 보면 QuestionBox랑 AnswerButton처럼 반복되는 구조들이 있는데 요런 애들을 컴포넌트로 만들어서 재사용하면 너무 좋은 구조가 되겠다!

Copy link
Member

Choose a reason for hiding this comment

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

예를 들면 QuestionBox라는 컴포넌트를 components 디렉토리 아래에 만들어서

Suggested change
<QuestionBox>
<AnswerButton
answer="응!!"
onClick={() => handleOptionChange("웅!")}
/>
<AnswerButton
answer="아니."
onClick={() => handleOptionChange("아니ㅠ")}
/>
</QuestionBox>
<QuestionBox
answer={["웅!", "아니ㅠ"]}
handlOptionChange={handlOptionChange}
/>

이렇게 넘겨주고,

const QuestionBox = ({answer, handleOptionChange}) => {
// 여기서 answer을 map 돌려서 AnswerButton을 만드는 방식
}

이런식으로!

const matchingAnimalPic = getMatchingAnimal(selectedOptions).imgUrl;

return (
<div>
Copy link
Member

Choose a reason for hiding this comment

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

스타일링도 없이 단지 최상위 태그가 하나 필요해서 div로 감싸주는 경우 React.Fragment를 사용할 수 있어! 즉, <> 이렇게 빈 태그를 사용해줘도 된답니당

Comment on lines +8 to +16
const handleButton1Click = () => {
setIsButton1Active(true);
setIsButton2Active(false);
};

const handleButton2Click = () => {
setIsButton1Active(false);
setIsButton2Active(true);
};
Copy link
Member

Choose a reason for hiding this comment

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

이것도 하나의 함수로 만들어보면 좋겠다!


return (
<Group>
<div className="question-box">
Copy link
Member

Choose a reason for hiding this comment

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

리액트에서 className은 카멜케이스로 작성하는 것이 관행! questionBox

Comment on lines +8 to +25
return (
(progress === 25 || progress === 50 || progress === 75) && (
<St.Bar progress={progress} />
)
);
};

export default ProgressBar;

const St = {
Bar: styled.hr`
background-color: ${theme.colors.darkBlue};
margin: 2rem 0;
border-radius: 0.5rem;
width: ${(props) => props.progress}%;
height: 1.5rem;
`,
};
Copy link
Member

Choose a reason for hiding this comment

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

와우 progress Bar를 만들어버렸다니 너무 좋다 ,,

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.

3 participants