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주차 기본/생각 과제] 점메추 #5

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Conversation

SooY2
Copy link
Member

@SooY2 SooY2 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

  1. 기본 구현 방법
    App.jsxHeaderMain으로 나누어 주었고, Main부분의 컴포넌트 바꿔주는 방식으로 구현했습니다!

    <Header>
    ✨ 오늘의 점메추 ✨
    {(type===0)||<button  type="button">처음으로</button>}
    </Header>
    <Main  onType={onType}  gotobase={0}></Main>

    main.jsx 내부의 stage(단계)에 따라 컴포넌트 바꿔주는 코드

    //stage에 따라 컴포넌트 바꾸기
    
    const  renderStage=()=>{
    
    switch (stage) {
    case  0:
    return  <SelectType  changeType={changeType}  changeStage={changeStage}  		type={type}/>;
    case  1:
    return  <StageOne  changeStage={changeStage}  changeStageValue=	{changeStageValue}  />;
    case  2:
    return  <StageTwo  changeStage={changeStage}  changeStageValue=	{changeStageValue}  />;
    case  3:
    return  <StageThree  changeStage={changeStage}  changeStageValue={changeStageValue}  />;
    case  4:
    return  <ResultMenu  changeStage={changeStage}  result={stageValue}  	type={type}/>;
    case  5: //랜덤
    return  <Random  changeStage={changeStage}  changeStageValue=	{changeStageValue}></Random>
    default:
    break;
    }

    stage에 따라 title의 내용이 바뀌는 코드

     //단계에 따라 title 내용 바뀜
      const changeStage = (_stage) => {
        setStage(_stage);
        // 상태 변경 시 타이틀도 업데이트
        const titles = [
          "원하는 추천 방식을 골라줘!",
          "오늘은 어떤 종류가 먹고 싶어?",
          "그럼 이 중에는 뭐가 끌려?",
          "마지막으로 골라줘!",
          "오늘의 추천음식은 바로!!"
        ];
        setTitle(titles[_stage]);
      };
  2. 추천 종류 선택
    SelectType.jsx 파일로 컴포넌트 만듬
    type을 안고른 경우 0, type 이 취향대로인경우 1, type이 랜덤인경우 2
    각 경우에 따라 구현해줬습니당

    const [state,SetState]=useState(props.type);
      let contentContainer;
      let buttonContainer
    
      const change=(_state)=>{
        props.changeType(state);
        props.changeStage(_state);
      }
    
      switch (state) {
        case 0:
          contentContainer=<><button type="button" className="chooseYet" onClick={()=>SetState(1)}>취향대로 추천</button>
          <button type="button" className="chooseYet" onClick={()=>SetState(2)}>랜덤 추천</button></>;
          break;
        case 1:
          contentContainer=
          <button className="choose">취향대로 추천</button>
          ;
          buttonContainer=<Button content="Start!" onClick={()=>change(1)}></Button>
          break;
        case 2:
          contentContainer=<button className="choose">랜덤 추천</button>;
          buttonContainer=<Button content="Start!" onClick={()=>change(5)}></Button>
          break;
        
      }
  3. 이전으로, 다음으로(결과보기) 버튼
    disabled속성을 추가해주소 스타일 입려줬슴니당

    return(
        <Btn type="button" onClick={props.onClick} disabled={props.state === null}>{props.content}</Btn>
      )
    &:disabled{
        background-color: #ccc;
      }	

    state 속성에 이전으로는 전 stage값 넣어주고 다음으로 버튼엔 이제 선택해줄 state값을 넣어줌으로써 선택이 되지 않았을 땐 null값으로 disable하게 해주고 선택했을 때 풀어줬어요!

    <Button content="이전으로" onClick={() => change(2)} state={2}></Button>
    <Button content="결과보기" onClick={() => change(4)} state={state}></Button>
  4. 결과
    assets/recommendationList.js파일에 데이터 정리해두고 import해서 사용해줬습니당

    const recommendationList = {
      0: { //한식
        0: { //밥
          0: { //국물O
            title: '미역국',
            img: 'https://t3.ftcdn.net/jpg/03/70/95/16/240_F_370951619_3An4QoEufQnRkVKyrEhaRxsaPGJOyM6r.jpg'
          },
          1: { //국물X
            title: '비빔밥',
            img: 'https://img.freepik.com/free-photo/fresh-vegetable-bowl-with-healthy-meal-options-generated-by-ai_188544-19554.jpg?size=626&ext=jpg&ga=GA1.1.1317701957.1699594305&semt=sph'
          }
        },
        1: {//면
          0: {
            title: '잔치국수',
            img: 'https://t3.ftcdn.net/jpg/01/37/83/86/240_F_137838654_c48NF2UWPJAo6eHEuywcFak4uRgeW6Py.jpg'
          },
          1: {
            title: '라볶이',
            img: 'https://img.freepik.com/free-photo/korean-instant-noodle-and-tteokbokki-in-korean-spicy-sauce-ancient-food_1150-43004.jpg?size=626&ext=jpg&ga=GA1.1.1317701957.1699594305&semt=ais'
          }
        },
        2: {//고기/해물
        ...

    main에서 관리 해주던 값을 가져와서 각 배열로 무슨 음식인지 추천함

      const stageOne=props.result[1];
      const stageTwo=props.result[2];
      const stageThree=props.result[3];
    
      // console.log(stageOne,stageTwo,stageThree);
      const resultMenuInfo=recommendationList[stageOne][stageTwo][stageThree];
  5. 결과 화면
    지정해뒀던 타입으로 다시하기 눌렀을 때, 타입값에 맞는 화면으로 이동


🥺 소요 시간, 어려웠던 점

  • 6h
  • 오직 컴포넌트만 이용해서 구현하려하니 어떻게 로직을 짜야하나 어려웠어요🥺
  • selectOne,Two,Three 컴포넌트도 하나로 합칠 수 있을거같은데 건드릴 엄두가 안나서.. 제출 후에 심화과제 마저 하면서 해보려구요!!ㅎㅎ

🌈 구현 결과물

https://www.notion.so/dosopt/3-24ed9345cadc4cc29345cc61ed5e3551

Copy link

@Yeonseo-Jo Yeonseo-Jo left a comment

Choose a reason for hiding this comment

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

꼼꼼하게 button에 type도 하나씩 달아주고, 주석으로 설명도 달아주고 세세하게 신경을 많이 쓴게 느껴진다!! 너무 수고해써❤️

(1) 언제 px을 사용하고 rem을 사용하는지 (2) 구조분해할당 활용하기 (3) 폴더 구조 고민해보기!
요 부분들을 전체적으로 고민해 보면 더 더 좋을것 같습니댜

앗 그리고 node_modules까지 pr에 올라와버려서 file changed가 452개..로 굉장히 많이 뜨는데 요 부분 gitignore 잘 되고 있는지 한 번 더 확인해주세욤 !

그럼 다음주도 같이 화이팅 하쟈✨

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="ko">

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.

앗차 놓쳤당ㅎ

Choose a reason for hiding this comment

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

이 파일은 사용하지 않는 파일 같은데 지워주세요!

Comment on lines +7 to +24
function App() {
const [type,setType]=useState(0);

const onType=(_type)=>{
setType(_type);
}

return (
<>
<Header>
✨ 오늘의 점메추 ✨
{(type===0)||<button type="button">처음으로</button>}
</Header>
<Main onType={onType} gotobase={0}></Main>

</>
)
}

Choose a reason for hiding this comment

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

컴포넌트 분리 잘 해줬다!
다만 나는 보통 App.jsx에서는 router, theme, globalStyle 등 프로젝트 전반에 들어갈 설정들만 써주고, 내부 컴포넌트들은 한 번 더 분리해 모듈화 해주는 편이야! 페이지나 컴포넌트들이 많아지면 App.jsx에서는 로직을 분리해주는게 유지보수, 가독성에 더 좋은것 같더라구 ~!

그래서 여기서 써 준 Header, Main 컴포넌트들을 <MainLayout> 등의 상위 컴포넌트들로 만들어주고, 이 상위 컴포넌트를 App.jsx에 넣어주는 방식을 추천할게용

position: fixed;
width: 100%;
height: 5rem;
background-color: #d49898;

Choose a reason for hiding this comment

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

요런건 theme를 통해 관리해주면 색상 코드를 외우지 않아도 되어서 편하답니당 !
나중에 정말 정말 많이 쓰이기 때문에 리뷰 반영하면서 globalStyle이랑 theme는 한 번 시도해봐도 좋을것 같아!

justify-content: center;
align-items: center;

font-size: 30px;

Choose a reason for hiding this comment

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

font-size에서만 px 단위를 사용한 이유가 있는지 궁금해!!
폰트 사이즈도 반응형 구현을 위해 rem 단위를 사용하는것을 더 추천할게 !


switch (state) {
case 0:
contentContainer=<><button type="button" className="chooseYet" onClick={()=>SetState(1)}>취향대로 추천</button>

Choose a reason for hiding this comment

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

이렇게 className으로 스타일링 해줘도 좋지만! styled-component에 props로 내려줘서 조건부 스타일링 해 보는 연습도 하면 좋을것 같앙

Comment on lines +35 to +36
<Button content="이전으로" onClick={() => change(2)} state={2}></Button>
<Button content="결과보기" onClick={() => change(4)} state={state}></Button>

Choose a reason for hiding this comment

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

Button을 공통 컴포넌트로 빼서 재사용하고 있는데, 클릭시 다음 단계로 넘어가는 로직이 props에 하드 코딩 되어 있는것 같아!
다음 단계로 넘어가는 로직을 Button 컴포넌트 내부에서 처리할수 있도록 하는 방법이 있는지 고민해보면 좋을것 같앙!
요 부분은 내 코드 참고해도 좋을것 같구 아니면 언제든 질문해도 좋습니다 🖤

Comment on lines +70 to +74
const RadioLabel = styled.label`
width: 10rem;
height: 10rem;
border-radius: 15px;
background-color: ${({ selected }) => (selected ? "#f2dcdc" : "#fff")};

Choose a reason for hiding this comment

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

props에 따라 동적 스타일링 하는 부분 너무 좋습니당


return (
<Wrapper>
<div id="progress">2 / 3</div>

Choose a reason for hiding this comment

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

단계 선택도 하드 코딩으로 되어 있는것 같은데 props로 넘겨 받는 stage 값을 활용해보면 좋을것 같아!!

Choose a reason for hiding this comment

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

Title, Button과 같이 공통 컴포넌트들은 공통 컴포넌트다! 라는것을 한 눈에 확인할 수 있도록
components > common 이런식으로 폴더링을 한 번 해주면 더 좋습니댜

Copy link
Member

@nayujin-dev nayujin-dev left a comment

Choose a reason for hiding this comment

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

수고많았어용 :octocat: 수연이는 항상 시멘틱한 애들로 잘 코딩한단 말이짓 .. !! 본받아야겠서 !!!

@@ -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.

앗차 놓쳤당ㅎ

function App() {
const [type,setType]=useState(0);

const onType=(_type)=>{
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 언더바 형태로 넘겨주는 이유가 궁금해용!!

<>
<Header>
✨ 오늘의 점메추 ✨
{(type===0)||<button type="button">처음으로</button>}
Copy link
Member

@nayujin-dev nayujin-dev Nov 14, 2023

Choose a reason for hiding this comment

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

나는 이럴때 const Button = styled.button~~; 이런식으로 styled components 하나 만들어주고 <Button>처음으로</Button> 이런식으로 작성해 ㅎㅎ
근데 딱히 명확한 이유업시 그냥 보기 깔끔해서 글케해왔어서 ,, 추천하는거까진 아니고 이렇게도 할수있다! 정도 ㅎㅎ

@@ -0,0 +1,35 @@
import styled from "styled-components";

const Button =(props)=>{
Copy link
Member

Choose a reason for hiding this comment

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

마자 구조분해할당 손에 한번 익으면 이전으로 못돌아가 .

)
};

const Btn=styled.button`
Copy link
Member

Choose a reason for hiding this comment

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

엇 여기는 styled component로 잘 활용했구만 조아잉

return (
<Wrapper>
<MenuContainer>
<img src={resultMenuInfo.img} alt={recommendationList.title}/>
Copy link
Member

Choose a reason for hiding this comment

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

앗 난 놓침ㅎ

<ButtonContainer>
<Button content="다시하기"
onClick={
() => {change(0)}}></Button>
Copy link
Member

Choose a reason for hiding this comment

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

onClick={()=> change(0)} 이런식이 좀 더 깔끔할것같아!

);
};

const Wrapper = styled.div`
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 반복적으로 사용되는 스타일요소는 따로 빼서 필요에따라 import 해서 쓰면 편해!!

Suggested change
const Wrapper = styled.div`
export const Wrapper = styled.div` ~~ `;
import { Wrapper } from ~~

<RadioLabel selected={state === 0} onClick={() => setState(0)}>
<HiddenRadio
type="radio"
Copy link
Member

Choose a reason for hiding this comment

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

훔 나 또 수연이 코드 보니 생각났는데,, 나는 button으로 할 수 있는애들도 싹다 div로 했당 하핫 ,, 나 진짜 습관 못버리넹 ㅎ ,,


const Title =({title})=>{
return(
<>
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
<>
return (
<TitleSec>
<p>{title}</p>
</TitleSec>
)

나는 필요없는 fragment는 되도록 안쓰는편이얌!

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