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주차 기본/심화/생각 과제 ] 점심 메뉴 추천 🍡🍥 #7

Merged
merged 54 commits into from
Nov 17, 2023

Conversation

Arooming
Copy link
Contributor

@Arooming Arooming commented Nov 10, 2023

✨ 구현 기능 명세

🌱 기본 조건

  • 기본조건1

✅ 선택 과정은 총 3단계입니다. ( 3개 → 3개 → 2개)

✅ 아이템은 총 18개 이상입니다. (3 x 3 x 2 = 18)

위는 “최소”기준이며 그 이상의 개수는 가능합니다.

  • 기본조건2

✅ 전역상태관리 라이브러리, context 사용 금지 ❌

✅ Router 사용 금지 ❌


🧩 기본 과제

  1. 추천 종류 선택
    • 취향대로 추천 / 랜덤 추천 중 선택합니다.
    • 선택시 다음화면으로 넘어갑니다.

[취향대로 추천]

  1. 답변 선택
    • 호버시 스타일 변화가 있습니다.
    • 클릭시(선택시) 스타일 변화가 있습니다.
  2. 이전으로, 다음으로(결과보기) 버튼
    • 아무것도 선택되지 않았을 시 버튼을 비활성화 시킵니다.

      → 눌러도 아무 동작 X

      → 비활성화일 때 스타일을 다르게 처리합니다.

    • 이전으로 버튼을 누르면 이전 단계로 이동합니다.

    • 다음으로 / 결과보기 버튼을 누르면 다음 단계로 이동합니다.

    • 버튼 호버시 스타일 변화가 있습니다.

  3. 결과
    • 선택한 정보들에 맞는 결과를 보여줍니다.

[ 랜덤 추천 ]

  1. 숫자 카운트 다운
    • 3 → 2 → 1 숫자 카운트 다운 후 결과를 보여줍니다.
    • 추천 결과는 반드시 랜덤으로 지정합니다.

[ 공통 ]

  1. 결과 화면
    • 다시하기 버튼

      → 랜덤추천이면 랜덤 추천 start 화면으로, 취향대로 추천이면 취향대로 추천 start 화면으로 돌아갑니다.

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


🌠 심화 과제

  1. theme + Globalstyle 적용
    • 전역으로 스타일을 사용할 수 있도록 적용해보세요
  2. 애니메이션
    • 랜덤 추천 - 카운트다운에 효과를 넣어서 더 다채롭게 만들어주세요!
  3. 헤더
    • 처음으로 버튼

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

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

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

[ 취향대로 추천 ]

  1. 단계 노출
    • 3단계의 진행 단계를 보여줍니다.
  2. 이전으로 버튼
    • 이전으로 돌아가도 선택했던 항목이 선택되어 있습니다.
  • 6. useReducer , useMemo , useCallback 을 사용하여 로직 및 성능을 최적화합니다.

생각과제

  • 리액트에 대하여

컴포넌트는 어떤 기준과 방법으로 분리하는 것이 좋을까?좋은 상태 관리란 무엇일까?렌더링을 효과적으로 관리하는 방법은 무엇이 있을까?Props Drilling이란 무엇이고 이를 어떻게 해결할 수 있는가?


💎 PR Point

🍟 theme + Globalstyle 적용

  1. theme과 GlobalStyle 파일을 각각 만들었고, 이를 App에 넣어서 앱 전체에 적용했습니다.
  2. thme에는 프로젝트 전반적으로 사용할 색상코드와 폰트를 정의해놓았고, GlobalStyle에는 프로젝트 내에서 공통으로 구현하고 싶은 부분에 대해 정의해놓았습니다.
// theme.js
const colors = {
  white: "#FFFFF0",
  lightPurple: "#ECD5E3",
  purple: "#CBAACB",
  lightYellow: "#FFFFB5",
  salmon: "#fec086",
  lightGreen: "#CCE2CB",
  green: "#8EB695",
  darkGreen: "#2D4849",
  lightPink: "#ffcad6",
  darkPink: "#d03c5a",
  lightBlue: "#A9E0EB",
};

const font = {
  fontFamily: "UhBeeSe_hyun",
};

const theme = {
  colors,
  font,
};

export default theme;
// GlobalStyle.js
import { createGlobalStyle } from "styled-components";

export const GlobalStyle = createGlobalStyle`
@font-face {
font-family: 'UhBeeSe_hyun';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_five@.2.0/UhBeeSe_hyun.woff') format('woff');
font-weight: normal;
font-style: normal;
}

*{
    margin: 0;
    padding: 0;
}

body {
    background-color: ${({ theme }) => theme.colors.lightGreen};
    font-family: 'UhBeeSe_hyun';
}

button {
    border: none;
    
    font-family: 'UhBeeSe_hyun';
}
`;

export default GlobalStyle;
function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <Header />
      <OnBoarding />
    </ThemeProvider>
  );
}

🍟 단계 노출

  1. step 상태를 만들어서 단계를 관리해주었고, 각 step에 따라 헤더 또한 동적으로 변하도록 구현했습니다.
const [step, setStep] = useState(0);

<St.SelectCategoryHeader>
    {step === 0 && <h2>원하는 추천 방식을 골라줘 !</h2>}
    {step === 1 && <h2>오늘은 어떤 종류가 먹고 싶어?</h2>}
    {step === 2 && <h2>그럼 이 중에는 뭐가 끌려?</h2>}
    {step === 3 && <h2>마지막으로 골라줘 !</h2>}
    {step === 4 && <h2>오늘의 추천 음식은 바로 !!</h2>}
</St.SelectCategoryHeader>

🍟 이전으로 버튼

  1. 2, 3단계에서는 단순히 step만 1씩 줄어들도록 구현했습니다.
  2. 1단계의 경우, 이전 버튼을 눌렀을 때 “취향대로 추천”이나 “랜덤 추천”이 떠야 하기 때문에, 이를 관리하는 상태 값을 변경하여 원하는 컴포넌트가 렌더될 수 있도록 구현했습니다.
<St.Button onClick={clickedPrevHandler} $isActivated={true}>
    이전으로
</St.Button>
const clickedPrevHandler = () => {
	  // 1단계에서 이전 버튼이 눌린 경우
    if (step === 1) {
      // 상태를 변경해서 이전 단계에 해당하는 컴포넌트가 뜨도록 구현
      props.setIsStartClicked(false);
    }
    // 2, 3단계에서는 단순히 step만 1씩 줄어듦
    props.setStep(step - 1);
    setIsActivated(true);
  };

🍟 로직 및 성능을 최적화

  1. useRef()의 current를 활용하여 해당 값이 변하더라도 리렌더가 되지 않도록 구현했습니다.

  2. useReducer를 활용하여 카운트다운 타이머의 상태를 관리해주었습니다.

    timeState.time의 초기값을 3, payload를 1로 지정하여 3부터 1까지 숫자가 하나씩 감소하도록 구현했습니다.

const INTERVAL = 1000;
  const [timerState, dispatch] = useReducer(reducer, {
    time: 3,
  });

  // useRef(): setState 값이 변하면 렌더링이 발생함. useRef의 current를 사용하면 current 값이 변하더라도 리렌더링되지 않기 때문에 렌더링 최적화를 이룰 수 있음.
  const timer = useRef(0);

  // setTimeout(): 두 번째 인자로 전달받은 시간이 지나면 한 번만 동작, 타이머 만료 후 첫 번째 인자로 전달받은 콜백함수를 호출함.
  // setInterval(): 두 번째 인자로 전달받은 시간을 간격으로 동작, 첫 번째 인자인 콜백함수가 시간 간격을 두고 반복적으로 호출됨.
  useEffect(() => {
    if (timerState.time > 0) {
      timer.current = setInterval(() => {
        dispatch({ type: "SET_TIME", payload: 1 });
      }, INTERVAL);
    }

    return () => {
      clearInterval(timer.current);
    };
  }, [timerState.time]);
const initialState = {
  time: 3,
};

export const reducer = (state = initialState, action) => {
  switch (action.type) {
    // 타이머 시간이 줄어들게 하는 동작
    case "SET_TIME":
      return {
        ...state,
        time: state.time - action.payload,
      };

    default:
      throw new Error("Unhandled action");
  }
};

🥺 소요 시간, 어려웠던 점

  • 5 ~ 6h

  • 라우터 없이 단순 state로 모든 컴포넌트를 관리하다보니까 props drilling도 꽤 생기고, 생각보다 코트 및 구조가 복잡해졌던 것 같네요.. 흑

    reducer로 다양한 state를 관리하고자 했지만, 원하는대로 구현하지 못해서 일단은 이 부분은 제외하고 state로 컴포넌트를 관리하는 코드로 올렸습니다! 리팩토링 때 좀 더 최적화해서 올릴 계획입니당 ..!


🌈 구현 결과물

1.mp4
2.mp4
3.mp4

Comment on lines +15 to +22
<St.Container>
<St.SelectCategoryHeader>
{step === 0 && <h2>원하는 추천 방식을 골라줘 !</h2>}
{step === 1 && <h2>오늘은 어떤 종류가 먹고 싶어?</h2>}
{step === 2 && <h2>그럼 이 중에는 뭐가 끌려?</h2>}
{step === 3 && <h2>마지막으로 골라줘 !</h2>}
{step === 4 && <h2>오늘의 추천 음식은 바로 !!</h2>}
</St.SelectCategoryHeader>

Choose a reason for hiding this comment

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

아 너의 코드를 보니 나도 접근 방식 자체는 괜찮았나보다 ㅎ.ㅎ step에 따라 다른거 보여지게 해야겠다는 생각을 내가 했다는게 스스로 기특하네

Comment on lines +2 to +9
{
id: 1,
firstCategory: "중식",
secondCategory: "밥",
thirdCategory: "국물 O",
description: "마라탕",
imgURL: "../src/asset/Chinese/rice/rice1.jpeg",
},
Copy link

Choose a reason for hiding this comment

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

아 음식마다 id를 하나씩 주고 단계를 하나씩 나아갈 때마다의 카테고리도 한꺼번에 넣어놨구나... 나는

KOREA: {
    RICE: {
      FOOD_WITH_SOUP: "된장찌개",
      FOOD_WITHOUT_SOUP: "비빔밥",
    },
    NOODLE: {
      FOOD_WITH_SOUP: "칼국수",
      FOOD_WITHOUT_SOUP: "잡채",
    },
    MEAT_OR_SEAFOOD: {
      FOOD_WITH_SOUP: "갈비탕",
      FOOD_WITHOUT_SOUP: "불고기",
    },

  
    
    이렇게 하는거 말고 다르게 하는 방법이 있을거라 생각했는데 너가  그렇게 한듯! 
    이렇게 Id를 붙여준 이유는 랜덤추천을 대비해서 써준거지??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

우웅 맞아! 랜덤추천 때 나온 숫자와 id를 비교해서 두 수가 같은 경우를 필터링해주기 위해서 id를 부여했어 !
그리고 id를 넣어주면 나중에 데이터를 추가하거나 변경/ 수정할 때도 훨씬 편리해서 최대한 id를 부여하려고 하는 편이야 !

Comment on lines 5 to 7
const RecommendMenu = (props) => {
const category = props.category;
const step = props.step;

Choose a reason for hiding this comment

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

아 막 하나하나 다 받아오는걸 작성해줄 필요없이 props만 써주면 되네..?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

타스에서는 오빠처럼 하나씩 props로 전달해줘야 하는데, 자스는 그냥 props로만 넘겨줘도 되더라고 ?!
근데 내가 둘 다 써보니까 자스는 props로만 넘겨줘도 돼서 편하긴 한데,
또 props의 수가 많아지면서 어떤걸 전달해주고 전달받았는지, 각 props의 타입이 뭔지 등등 헷갈리는게 많이 생기더라 ..!!
어차피 나중에 타스를 쓰게 될거라면 각각의 props를 넘겨주는 방식도 쓰게 될테니까 두가지 방식 모두 알고 있으면 좋을듯 !

Copy link
Member

Choose a reason for hiding this comment

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

props로 받아온 객체에 구조분해할당으로 접근해서 받아오려고 중괄호로 감싸서 하나씩 가져오는 원리입니당 ~!

Suggested change
const RecommendMenu = (props) => {
const category = props.category;
const step = props.step;
const RecommendMenu = ({category, step}) => {
// 여기서 category와 step에 재할당해줄 필요 없이 그냥 category, step으로 쓰면 됨!

Comment on lines +8 to +9
const randomNum = parseInt(Math.random() * 22 + 1);
const randomSelectedMenu = MENU.filter((it) => it.id === randomNum)[0];

Choose a reason for hiding this comment

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

아 그러네 id가 22까지 있으니까 랜덤추천 로직이 이렇게 되는거구나...
나는 애초에 데이터 구조(?)라고 해야하나? 이거를 잘못 설계해서 더 복잡해진거같은데....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아냐 구래도 이런저런 구조를 생각해본 것만으로도 많이 도움 됐을듯 !!

Comment on lines +78 to +87
<St.BtnContainer>
<St.Button onClick={clickedPrevHandler} $isActivated={true}>
이전으로
</St.Button>
<St.Button onClick={clickedNextHandler} $isActivated={isActivated}>
{step === 3 ? "결과보기" : "다음으로"}
</St.Button>
</St.BtnContainer>
</St.Container>
);

Choose a reason for hiding this comment

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

혹시 이 부분이 이전으로와 다음으로 버튼을 관리하는 부분일까??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

어엉 마자 !! 단계 컴포넌트를 관리하는 요 상위 컴포넌트에서 이전/다음 버튼을 구현해놓고 쭉 사용한다고 생각하면 편할 것 같아 !

Comment on lines 5 to 6
props.setSelectedCategory(e.target.innerHTML);
};

Choose a reason for hiding this comment

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

e.target.innerHTML로 상태를 전달하는건가?? 굉장히 신박하네

Choose a reason for hiding this comment

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

나는 궁금한게 이런 생각을 어떻게 하는거야?? 이런 생각 자체를 할 수 있는 효율적인 학습법이 있나 싶어서...
남들은 뭔가의 기능을 구현할 때, 여러가지 방법을 떠올릴 수 있고 그 방법마다 장단점이 머릿속에 정확하게 있는 상태에서 개발이 진행이 되는건지 모르겠네... 이번 리액트 과제하면서 정말 부족하다는거를 너무 많이 느껴서 살짝 현타옴

Copy link
Contributor Author

Choose a reason for hiding this comment

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

e.target.innerHTML로 상태를 전달하는건가?? 굉장히 신박하네

어엉 이벤트가 발생한 타겟에 적힌 글자를 selectedCategory로 지정해주는 방식이야 !

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 10 to 12
useEffect(() => {
setStep(0);
}, []);

Choose a reason for hiding this comment

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

이 useEffect를 하는 이유는 현재 step을 0으로 설정해주려고 하는건가???

다른 곳에 보면 setStep(1)도 있고 2도 있고 그렇던데, 각 단계마다 step을 변경시켜주려고 작성한거야??

혹시 맞다면 그렇게 useEffect로 작성한 이유가 있으까??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

우웅 맞아 ! 각 컴포넌트마다 단계를 지정해주려고 작성한거고, useEffect의 의존성에 빈 배열을 넣어줘서 컴포넌트가 처음 렌더링 됐을 때만 실행되도록 구현해줬어 ! 하나의 컴포넌트 내에서 단계라는 상태는 계속 유지되는 속성이라고 생각해서 의존성에는 빈 배열을 넣어줬구 ..!!

Copy link
Member

Choose a reason for hiding this comment

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

오옹 나 useEffect 한 번도 안 써봐서 감이 안 잡혔는데 아름이 코드 보니까 어떻게 써야되는 건지 좀 알겠다!!
짱❤

Copy link
Member

Choose a reason for hiding this comment

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

애초에 초기값이 0이니까 setStep으로 굳이 0 만들어주지 않아도 되지 않나용?! 이렇게 한 이유가 궁금해요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

다른 컴포넌트로 접근하면 step 값이 변하게 되어서 이 컴포넌트로 돌아왔을 때는 원래 의도에 맞게 0단계로 초기화해줘야 한다고 생각했어요 !

라고 댓글을 달고 다시 코드를 뜯어보니까 서현이가 말해준대로 원래 초기값이 0이라 저건 없어도 되는 코드였네요 !!!..
당장 지워버리고 오도록 하겠읍니다 .

@ExceptAnyone
Copy link

아름아 3주차 과제까지 수고많았어. 뭔가 이번 과제는 나 스스로 하여금 너무 느끼는게 많았던 과제였던 것 같아. 뭐랄까... 흠... 욕심은 많은데 현실은 그걸 받춰주지 못한다고 해야하나? 그런 생각이 많이 드네. 결국 리액트를 잘해야하는건데, 보통은 머릿속에 '어떤어떤어떤 방식으로 구현하면 되겠다' 라는 생각을 가지고 그걸 코드에 옮기잖아? 근데 아예 그런 감 자체가 잡히지를 않더라고... 아무래도 경험 부족이겠지? 싶으면서도 앱잼이 얼마 안남았고, 담주부터 당장 합세하는데 뭔가 민폐될까봐 두려워지는 감정이 되게 커진 이번 한 주 였달까... 리액트 잘하는 애들의 머리를 끄집어내서 그 생각을 공유해보고 싶네 ㅋㅋㅋㅋ 도통 감이 안잡힌다고 해야할까? 기초가 너무 부족한게 확 느껴졌다 후 ㅋㅋㅋ 뭔가 늦은만큼 빠르게 성장하고 싶은데 그게 잘 안되는 거 같아서 생각이 좀 많아지네

전반적인 코드리뷰를 해야되는데 뭔가 넋두리만 뱉은거같다 ㅋㅋㅋㅋㅋㅋㅋㅋ

아무튼 고생했어. 앞으로도 잘 부탁해

@moondda moondda self-requested a review November 14, 2023 07:31
) : (
<St.Container>
<St.Step>{step}/3</St.Step>
{step === 1 && (
Copy link

Choose a reason for hiding this comment

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

난 각단계별로 step을 각각 만들고 boolean 값으로 줘서 조금 노가다를 하는? 느낌이 들었는데 아름이처럼 애초에 number로 주면 깔끔하겠다 나도 반영해볼겡

)}

<St.BtnContainer>
<St.Button onClick={clickedPrevHandler} $isActivated={true}>
Copy link

Choose a reason for hiding this comment

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

여기 $isActivated 가 위해서 선언한 const [isActivated, setIsActivated] = useState(false); 이거 쓰는건강?? $를 붙어야하는구나

Copy link
Contributor Author

Choose a reason for hiding this comment

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

우웅 맞아 ! 위에서 선언한 상태 값을 활용해서 조건부 스타일링을 하기 위해서 저렇게 구현했어!

사실 $ 표시가 없어도 동작에는 문제가 없는데, 브라우저 상의 콘솔창을 열어보면 에러가 발생하거든 ..!
그래서 조건부 스타일링을 할 때는 props앞에 $표시를 붙여줘서 사용하고 있어!

Step: styled.p`
position: absolute;
top: 0;
right: -3rem;
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 +103 to +111
CategoryContainer: styled.article`
display: flex;
justify-content: center;
align-items: center;

margin-top: 2rem;

gap: 1rem;
`,
Copy link

Choose a reason for hiding this comment

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

비슷한 결끼리 묶고 줄바꿈을 해준건가??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

우웅 마자 !

Comment on lines +1 to +17
const initialState = {
time: 3,
};

export const reducer = (state = initialState, action) => {
switch (action.type) {
// 타이머 시간이 줄어들게 하는 동작
case "SET_TIME":
return {
...state,
time: state.time - action.payload,
};

default:
throw new Error("Unhandled action");
}
};
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 +3 to +6
export const GlobalStyle = createGlobalStyle`
@font-face {
font-family: 'UhBeeSe_hyun';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_five@.2.0/UhBeeSe_hyun.woff') format('woff');
Copy link

Choose a reason for hiding this comment

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

이건 별 얘기아닌데 과제할때마다 폰트 스타일 주는거 넘 섬세하고 귀엽당..ㅋㅋ

Copy link
Member

@aazkgh aazkgh left a comment

Choose a reason for hiding this comment

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

이번에 리액트 처음 쓰면서 OB들 코드 보며 배우려고 노력하는 중인데 역시 도움이 많이 됐습니다❤❤❤ 아름이 폼미폼미~~!!!!!!

useEffect()를 어떻게 쓰는지 잘 몰라서 쓰기 좀 그랬는데 아름이 거 보고 감을 좀 잡아갑니다!~~!!! 이번 주도 파이팅!!❤❤❤

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="ko">
Copy link
Member

Choose a reason for hiding this comment

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

팟짱대왕님이 좋아하시겠다 ㅋㅎㅋ

time: 3,
});

// useRef(): setState 값이 변하면 렌더링이 발생함. useRef의 current를 사용하면 current 값이 변하더라도 리렌더링되지 않기 때문에 렌더링 최적화를 이룰 수 있음.
Copy link
Member

Choose a reason for hiding this comment

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

헉 아름이 친절한 설명 폼미폼미❤❤❤

);
};

const St = {
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
Contributor Author

@Arooming Arooming Nov 17, 2023

Choose a reason for hiding this comment

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

음 그냥 하나의 취향?이라고 생각해주면 좋을 것 같은데용 !
저도 원래는 스타일드 컴포넌트를 따로 생성해줬었는데, 몇 번의 협업을 거치며 저런 컨벤션을 지키다보니 요런 식으로 구현하는게 훨씬 편해서 St로 묶어주는 방식을 사용하고 있어요!

개인적으로 해당 방식이 좋다고 생각한 이유는

  1. St.를 앞에 붙여줌으로써 기본 컴포넌트와 스타일 컴포넌트를 한 눈에 알아볼 수 있음
  2. St.를 붙인 컴포넌트를 매번 생성해준다고 생각하면 너무너무 귀찮은데 St로 한 번에 묶어줌으로써 좀 더 편하게 구현할 수 있었음 !

요 두가지가 가장 큰 이유가 될 것 같네용 !

Comment on lines 10 to 12
useEffect(() => {
setStep(0);
}, []);
Copy link
Member

Choose a reason for hiding this comment

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

오옹 나 useEffect 한 번도 안 써봐서 감이 안 잡혔는데 아름이 코드 보니까 어떻게 써야되는 건지 좀 알겠다!!
짱❤

Comment on lines 5 to 6
props.setSelectedCategory(e.target.innerHTML);
};
Copy link
Member

Choose a reason for hiding this comment

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

헉 왜 현타 왔어,,,ㅜㅜㅜㅠ 아니야 우리 왕초보도 잘할 수 있따~!!!~!!
나도 왕초보지만 많이 하다 보면 요런 아이디어도 잘 생각나지 않을까?? 그니까 그만큼 열심히 하자요👊

time: 3,
};

export const reducer = (state = initialState, action) => {
Copy link
Member

Choose a reason for hiding this comment

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

요 타이머 useReducer로 하는 거 첨 봤어 완전 싱기방기 짱❤

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.

아름언니 고생 많았더!! ㅎㅎ

Comment on lines 10 to 12
useEffect(() => {
setStep(0);
}, []);
Copy link
Member

Choose a reason for hiding this comment

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

애초에 초기값이 0이니까 setStep으로 굳이 0 만들어주지 않아도 되지 않나용?! 이렇게 한 이유가 궁금해요!!

Comment on lines +16 to +22
<St.SelectCategoryHeader>
{step === 0 && <h2>원하는 추천 방식을 골라줘 !</h2>}
{step === 1 && <h2>오늘은 어떤 종류가 먹고 싶어?</h2>}
{step === 2 && <h2>그럼 이 중에는 뭐가 끌려?</h2>}
{step === 3 && <h2>마지막으로 골라줘 !</h2>}
{step === 4 && <h2>오늘의 추천 음식은 바로 !!</h2>}
</St.SelectCategoryHeader>
Copy link
Member

Choose a reason for hiding this comment

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

이것도 컴포넌트 분리해도 나쁘지 않겠당 step만 넘겨줘서 안에서 알아서 분기 처리하도록!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

어어 마자요 이번 과제는 그런 식으로 했는데 훨씬 깔끔하긴 하네용 !

Comment on lines +10 to +21
<St.Category
onClick={(e) => {
clickCategoryHandler(e);
}}
>
취향대로 추천
</St.Category>
<St.Category
onClick={(e) => {
clickCategoryHandler(e);
}}
>
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
<St.Category
onClick={(e) => {
clickCategoryHandler(e);
}}
>
취향대로 추천
</St.Category>
<St.Category
onClick={(e) => {
clickCategoryHandler(e);
}}
>
<St.Category
onClick={clickCategoryHandler}
>
취향대로 추천
</St.Category>
<St.Category
onClick={clickCategoryHandler}
>

인수로 이벤트 객체만을 넘겨주는 경우엔 이렇게만 써도 알아서 넘겨준답니당 !

Comment on lines 13 to 14
props.setSelectedCategory(false);
props.setStep(0);
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
props.setSelectedCategory(false);
props.setStep(0);
const {setSelectedCategory, setStep} = props;
setSelectedCategory(false);
setStep(0);

이렇게 props 에서 구조분해할당 해서 쓰면 props.에 접근하지 않아도 되니까 가독성이 더 좋아지겠죠! - !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네 !!!!!! 구조분해할당. .... 제가 항상 어려워하고 헷갈려 했던 건데 저번 기수에도 서현이는 항상 구조분해할당을 많이 고려하고,
리뷰 남길때도 구조분해할당 할 수 있는 부분에 대해 꼭 리뷰로 남겨주는 것 같더라고용?!
저도 이번 기수에는 구조분해할당을 잘 활용할 수 있도록 코드에 많이 녹여내보겠슴돵 !!!

Comment on lines 17 to 19
useEffect(() => {
props.setStep(0);
}, []);
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
Contributor Author

Choose a reason for hiding this comment

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

위에 답글 달았구 현재는 수정 완 !

category === "랜덤 추천" ? props.setStep(4) : props.setStep(1);
}, []);

return step === 4 && category === "랜덤 추천" ? (
Copy link
Member

Choose a reason for hiding this comment

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

애초에 category === "랜덤추천"이어야 step이 4가 되면

Suggested change
return step === 4 && category === "랜덤 추천" ? (
return step === 4 ? (

요로케만 해도 대지 않오?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

일단 요 컴포넌트는 원하는 추천 방식을 누르고 시작하기 버튼을 눌렀을 때 동작하는 컴포넌트라서
온보딩 화면에서 랜덤추천을 고르고 시작하기를 누른 경우는 step 관련 조건만 있어도 제대로 동작해!

온보딩 화면에서 취향대로 추천을 골랐다면 첫 step은 1에서 시작하지만, 마지막에 메뉴를 추천해줄 때는 step 4로 들어가거든..!
이때 여기서 category 관련 조건이 없다면, step이 4이기 때문에 취향대로 추천을 선택했음에도 카운트다운 컴포넌트가 실행되어버려 !
그래서 카테고리 관련 조건까지 추가해서 step이 4라도 취향대로 추천을 진행하고 있다면 Step 컴포넌트로 들어가서 카운트다운 없이 바로 메뉴 추천 결과 화면을 띄워주도록 구현했어 !

Comment on lines +19 to +23
if (timerState.time > 0) {
timer.current = setInterval(() => {
dispatch({ type: "SET_TIME", payload: 1 });
}, INTERVAL);
}
Copy link
Member

Choose a reason for hiding this comment

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

사실 단순히 1씩 줄여주면 되는 비교적 단순한 로직인데, useReducer를 어떤 이유로 사용했는지 궁금하당 !!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

서현이 말대로 비교적 단순한 로직이긴 하지만, state를 reducer로 대체하면 어떤 액션을 하려고 하는지가 명확히 보이기 때문에 훨씬 가독성과 유지보수 측면에서 좋다고 생각해서, reducer로 대체해주었습니다 ..!

Comment on lines 6 to 7
const Recommendation = (props) => {
const category = props.category;
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 Recommendation = (props) => {
const category = props.category;
const Recommendation = ({category}) => {

마찬가지로 요롷게 받아버리면 좋겠죠 ?-?

Comment on lines 13 to 18
const filteredMenu = MENU.filter(
(it) =>
it.firstCategory === firstCategory &&
it.secondCategory === secondCategory &&
it.thirdCategory === thirdCategory
)[0];
Copy link
Member

Choose a reason for hiding this comment

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

filter 대신 메서드 .find 도 있답니당 ~!

<St.Button onClick={clickedPrevHandler} $isActivated={true}>
이전으로
</St.Button>
<St.Button onClick={clickedNextHandler} $isActivated={isActivated}>
Copy link
Member

Choose a reason for hiding this comment

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

전체적으로 스타일 컴포넌트 props들 넘 잘 사용했다 깔꼼!!

@Arooming Arooming changed the title [ 3주차 기본/심화/생각 과제 ] 점심 메뉴 추천 [ 3주차 기본/심화/생각 과제 ] 점심 메뉴 추천 🍡🍥 Nov 17, 2023
@Arooming Arooming merged commit 467b1f0 into main Nov 17, 2023
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.

None yet

5 participants