[React Project] 이상형 월드컵

SeokHun·2021년 1월 17일
2

Projects

목록 보기
3/3
post-thumbnail

기능 소개

  • Display
    • 각 음식에 대한 사진을 보여준다
    • 마우스를 올리면 사진이 확대된다
  • Select
    • 해당 음식의 사진을 클릭하면 그 음식은 승리하고 다음으로 넘어간다
  • Make Winner
    • 단 하나의 음식이 남을 때 까지 반복된다

참조 영상

React.js 를 사용해 이상형 월드컵 만들기

코드

GitHub : pyo-sh


1. Main Features

1-1. 구조

  • APP : GlobalStyle 을 적용
  • FlexBox : styled-component
    • 화면의 전체를 차지하게 되고 flex-1 의 위치를 정하게 된다
  • Title : 프로그램이 무엇을 나타내는지 h1 태그로 보여준다
    • position이 absolute로 독자적인 위치를 가진다 (FlexBox 하위 태그)
  • flex-1 : 이상형 월드컵 게임 기능
    • 기능을 수행하는 이벤트가 있다
    • 사용자에게 직관적인 인터페이스를 제공한다

1-2. Item State

1) 아이템 불러오기

보통 정보를 가져올 때 API로 가져오거나 한다

영상에서는 큰 프로젝트가 아니므로 사진을 저장하고 이를 전역 변수로 저장하는 형태로 구현한 것 같다

사진은 src/img 에 저장해서 구현했다

const items = [
  {
    name: "국밥",
    src: require('../../img/GukBob.jpg').default
  },
  {
    name: "햄버거",
    src: require('../../img/Hamburger.jpg').default
  },
  {
    name: "피자",
    src: require('../../img/Pizza.jpg').default
  },
  {
    name: "초밥",
    src: require('../../img/Sushi.jpg').default
  },
];

React 에서 Image를 불러오는 방법

  1. img src에 import 변수 from '경로'; 선언 후 변수를 통해 부여
  2. img src에 require('경로').default 부여
    (require('경로') 를 하면 모듈 Object로 들고 있더라...)
  3. 사진을 public에 저장하고 img src에 절대 경로로 지정

2) 배열에서 랜덤 나열

Math.random() 함수는 0 ~ 1 사이의 랜덤한 값을 가진다

Javascript 배열에서 sort 함수를 이용해 배열을 정렬할 때, Math.random() - 0.5 값을 통해 임의의 -, + 값이 오게 만들어 무작위로 정렬 되도록 한다

전역변수 items로 부터 받은 값을 컴포넌트의 첫 랜더링에만 저장하도록 하기 위해 useEffect(~~, []) 를 사용하였다

const [foods, setFoods] = useState([]);

useEffect(() => {
  items.sort(() => Math.random() - 0.5);
  setFoods(items);
  // ~~~
}, []);

1-3. Display State

해당 코드에서는 Display 하는 item을 따로 State에 크기가 2인 배열로 저장한다

const [displays, setDisplays] = useState([]);

useEffect(() => {
  // ~~~
  setDisplays([items[0], items[1]]);
}, []);

이 State만을 Rendering 해서 사용자에게 보여준다

return // ~~
  {displays.map(item => {
     return <div
     className="flex-1"
     key={item.name}
    onClick={clickHandler(item)}
      >
       <img className="food-img" src={item.src} alt="food"/>
       <div className="name">{item.name}</div>
    </div>
  })}
// ~~

1-4. Make Winner

1) 사용자 인터페이스

이미지에 마우스를 올려놨을 때 아래의 효과들로 사용자가 이를 클릭할 수 있다는 암시를 준다

  • 사진이 흐려진다 (배경이 회색으로 되어있어 살짝 어두워 진다)
  • 사진이 확대된다 (화면 밖으로 나가지는 않는다)
  • 커서가 바뀐다
  • 이름의 크기를 크게 하여 자신이 선택하는 사진이 무엇인지 알려준다

이를 styled-component인 FlexBox 안에 하위 클래스로 아래처럼 css로 적용하였다

const FlexBox = styled.div`
    // ~~~
    .flex-1 {
        flex: 1;
        min-width: 500px;
        overflow: hidden;
        background-color: gray;
        position: relative;
    }
    .food-img {
        width: 100%;
        height: 100%;
        transition: 0.5s;
        cursor: pointer;
    }
    .food-img:hover{
        transform: scale(1.1);
        opacity: 0.8;
    }
    .name{
        position: absolute;
        z-index: 3;
        color: #ffffff;
        bottom: 10%;
        font-size: 90px;
        left: 50%;
        transform: translateX(-50%);
    }
`

2) 클릭 이벤트

사진을 클릭했을 때 사용자의 선택받은 아이템은 따로 저장되고 다음 아이템들이 Display 되어야 한다

이때 승리한 아이템을 넣는 변수를 따로 두어 저장하도록 한다

const [winners, setWinners] = useState([]);

배열에서 아이템들을 빼내어 Display 하다보면 length가 2 이하일 때까지 반복하게 된다

const clickHandler = food => (event) => {
  // ~~
  else if(foods.length > 2){
    setWinners([...winners, food]);
    setDisplays([foods[2], foods[3]]);
    setFoods(foods.slice(2));
  }
}

length가 2 이하일 때는 사용자가 전체 아이템을 살펴보았다는 말이 된다

그 이후에는 사용자가 선택한 아이템 중에서 월드컵을 진행하고 선택된 아이템이 하나만 남을 때 까지 아이템을 선정한다

const clickHandler = food => (event) => {
  if(foods.length <= 2){
    if(winners.length === 0){
      setDisplays([food]);
    } else{
      let updatedFood = [...winners, food];
      setFoods(updatedFood);
      setDisplays([updatedFood[0], updatedFood[1]]);
      setWinners([]);
    }
  //~~
}

클릭 이벤트의 전체 코드는 위 두개의 코드를 합친 것과 같다

2. Sub Features

2-1. styled-component

1) GlobalStyle

App.js 에 다음과 같은 styled-component를 사용하여 전체 css를 적용하였다

import React from "react";
import { createGlobalStyle } from 'styled-components';

import Game from './components/Game/Game';

const GlobalStyle = createGlobalStyle`
  * {
    margin: 0,
    padding: 0;
    box-sizing: border-box;
  }
`;

function App() {
  return (<>
    <GlobalStyle></GlobalStyle>
    <Game/>
  </>);
}

export default App;

2) FlexBox

게임을 실행하는 div 태그를 하나의 styled-component로 두어 style.js 에 따로 저장한다

import styled from 'styled-components';

export const FlexBox = styled.div`
	// ...
`;

이를 div 태그에서 사용하여 가독성을 높인 것(?) 같다

import React, { useState, useEffect } from 'react';
import { FlexBox } from './style';

// ~~
const Game = () => {
  // ~~
  return <FlexBox>
    // ~~
  </FlexBox>;
}

2-2. 고정 된 타이틀

Favorite WorldCup 임을 알리는 타이틀은 아래와 같은 태그를 가진다

<h1 className="title">Favorite WorldCup</h1>

이는 styled-component 안에 다음과 같이 css로 지정되어 상단 중앙에 위치하게 된다

  .title {
      position: absolute;
      z-index: 2;
      top: 0;
      left: 50%;
      transform: translateX(-50%);
      background-color: #ffffff;
      padding: 0px 30px;
      padding-bottom: 10px;
      text-transform: uppercase;
   }

후기

React에 대해 심화적으로 했다기 보다 styled-component 사용에 중점에 둔 프로젝트였던 것 같다

0개의 댓글