관리 메뉴

사과하는 제라스

#2. JSX & PROPS 본문

ReactJS 영화 웹 서비스 만들기

#2. JSX & PROPS

Xerath(제라스) 2021. 10. 17. 01:08

목차

    728x90
    반응형
    • React에서 어떻게 작동을 하는지가 아닌 어떻게 보여주는지에 대해서 알아볼 것임.

    <index.js> 에서 다음 부분의 <App />가 HTML처럼 보이지만 사실 HTML이 아니다.

    이것이 Component(컴포넌트)이다. 

    ReactDOM.render(<App />, document.getElementById('potato'));

    ㄴ이때 <App /> 대신 App을 쓸 수는 없다. React는 컴포넌트가 HTML의 역할을 하게 할때 사용하기 때문이다.

    이때 JavaScript와 HTML 사이의 조합을 JSX라고 한다. (ex) function Potato(){......})

    이 개념이 React의 유일한 custom한 기능이다. (나머지는 대부분 Javascript에서 배운걸 쓰는 것이다.)

     

    <App.js>에서 다음 부분은 App 컴포넌트인데 보이다시피

    컴포넌트는 HTML을 반환해주는 함수이다.

    function App() {
      return (
        <div>
          <h1>Hello</h1>
        </div>
      )

     

    1. 나만의 컴포넌트를 만들어보자!

    1) src/Potato.js라는 파일을 만듦

     

    2) 컴포넌트를 만들때 반드시 다음을 적어주어야 한다. 적지 않는다면 React는 해당 파일에 있는 (JSX가 있는) 컴포넌트를 사용할 수가 없다.

     

    import React from "react";

    3) Potato.js라는 Javascript에 Potato라는 컴포넌트를 다음과 같이 작성한다.

     

    function Potato(){
        return <h3>I love Potato</h3>;
    }

    4) 이 컴포넌트를 사용하고 싶다면 다음과 같이 export 또한 해주어야 한다.

    export default Potato;

    5) 이 컴포넌트를 index.js로 옮겨서 index.html에서 보여주는 방식으로 사용할 수 있는데 index.js에서 사용할 수 있도록 다음과 같이 import해준다.

    import Potato from "./Potato";

    이때 주의할 점!

    React는 render할 때 오직 한 component만을 할 수 있다.

    -> ReactDOM.render("이 곳에 2개 이상의 컴포넌트가 오게 하지 말자!", ... );

     

    6) 대신 다음과 같이 다른 컴포넌트(<App.js>)에 import해서 그곳에서 사용하도록 하자.

    <App.js>

    import React from 'react';
    import Potato from './Potato';
    function Food(){
      return <h1>I like Poatato!</h1>
    
    }
    
    function App() {
      return (
        <div>
          <h1>Hello</h1>
          <Potato />
        </div>
      )
    }
    
    export default App;

     

    7) Potato 파일을 삭제하고 대신 App.js에 컴포넌트를 작성하자.

    <App.js>

    import React from 'react';
    
    
    function Potato(){
      return <h1>I love Potato</h1>;
    }
    
    function App() {
      return (
        <div>
          <h1>Hello</h1>
          <Potato />
        </div>
      )
    }
    
    export default App;

    8) 다음과 같이 컴포넌트에 정보를 전달할 수 있다. ( 다음과 같이 name이란 prop을 kimchi라는 value를 줌.) 

    <App.js>

    import React from 'react';
    
    function Food(props){            # props를 인자로 받아오거나 function Food({fav})와 같이 props객체 내에서 원하는 항목만 따올 수도 있다.
      return <h1>I like {props.fav} </h1>   # 이와 같이 응용하여 사용할 수 있다. 혹은 props대신 {fav}와 같이 일부를 가져왔다면 I like {fav} 와 같이 쓸 수도 있다.
    
    }
    
    function App() {
      return (
        <div>
          <h1>Hello</h1>
          <Food                      # 이런 식으로 props를 줄 수 있다.
            fav="kimchi"
            something={true}
            ...
          />
        </div>
      )
    }
    
    export default App;

    9) 웹사이트에 동적 데이터를 추가하는 방법(정적으로 데이터를 미리 입력해서 구동시키는게 아니라 웹사이트에서 입력받은 데이터를 이용해서 사용하는 방법)을 알아보자.

     

    일단, 웹사이트에서 입력받는걸 구현하지 못했으니 다음 foodILike 배열로 입력받아왔다고 치자.(즉, 데이터가 API에서 넘어왔다고 치자는 거다.)

    <App.js>

    const foodILike = [
      {
        name: "Kimchi",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_01-.jpg"
      },
      {
        name: "Samgyeopsal",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_03-1-1-150x150.jpg"
      },
      {
        name: "Bibimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_05-1-1.jpg"
      },
      {
        name: "Donkatsu",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_04-1-1.jpg"
      },
      {
        name: "Kimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_09-1-1-150x150.jpg"
      }
    ] //food 객체의 배열임

    이후, Food 컴포넌트를 동적으로 rendering할 것인데 이는 javascript의 map이라는 함수를 활용하여 할 수 있다.

     

    map 함수 활용법을 알아보자.

    <Console창에서 실행해보는 예시>

    //아래와 같이 friends 배열을 선언해주고
    const friends = ["Yoon", "Kang", "Kim", "Choi"]
    
    //각 element들을 console창에 나열해주고 싶다면 아래와 같이 current라는 매개변수를 통해 이용할 수 있다. 또한 return 0를 해주기 때문에 각 item들이 0인 배열을 결과값으로서 갖는다.
    friends.map(current => {
    	console.log(current);
        return 0;
    }
    //혹은 아래처럼 이전 Javascript 방식으로도 쓸 수 있다.
    friends.map(function(current) => {
    	console.log(current);
        return 0;
    }
    
    
    //return에 대한 다른 예시를 보이자면 이렇게 뒤에 "man"을 각각 붙일 수도 있다.
    friends.map(function(current) => {
        return current + "man";
    }

    위와 같은 map함수를 알아봤으니 한번 코드에 적용해보자.

    아래처럼 foodILike의 각 element들을 dish라는 매개변수를 이용해서 각 element들의 1. name 과 2. image 요소들을 각각 1. fav와  2. img라는 prop요소로 Food 컴포넌트에 넘겨준다.(이때 dish는 object(객체)이다.)(이때 img 태그 설정에서 alt를 적어주지 않으면 Warning이 뜨므로 아래와 같이 "profileimg"라는 설명을 적어주었다. alt 속성은 주로 불러온 이미지가 깨졌을 때를 대비해서 그 부분에 대한 설명을 마우스on시 보여줌으로서 문제되는 부분을 쉽게 찾을 수 있도록 도와준다.)

     

    <App.js>

    function Food(props){           
      return (
        <div>
          <h1>I like {props.fav} </h1>
          <img src={props.img} alt="profileimg"/>
        </div>
      )
    }
    
    
    
    function App() {
      return (
        <div>
          {foodILike.map(dish => 
            <Food fav={dish.name} img = {dish.image}/>
    
          )}
        </div>
      )
    }

     

     

    정리를 하자면 App 컴포넌트에서 foodILIke이라는 배열 데이터에 대해 map함수를 활용해서 dish라는 매개객체를 통해 전달하고자 하는 props를 Food 컴포넌트에 전달하고 Food 컴포넌트에서는 각 props들에 대해 일정한 javascript를 실행하는 것이다.

     

    이들을 잘 수행하면 foodILike 배열에 적어둔 name들과 이미지 사진들이 순서대로 잘 실행된다.

     

    10) 이번엔 map함수에 dish라는 단순 매개객체를 주는 것이 아닌 renderFood라는 함수를 통해 props를 전달하게 하자.

    (다음과 같이 작성한다면 props 내 요소는  name과 picture이다.)

     

    <App.js>

    function Food(props){           
      return (
        <div>
          <h1>I like {props.name} </h1>
          <img src={props.picture} alt="profileimg"/>
        </div>
      )
    }
    
    function renderFood(dish){
      return (<Food name={dish.name} picture={dish.image} />)
    }
    
    function App() {
      return (
        <div>
          {foodILike.map(renderFood)}
        </div>
      )
    }

     

    11) 다시 9) 때의 과정으로 돌아오자.(함수를 너무 쓰지 말자주의인 셈~) 이때 개발자 도구에서 발생하고 있는 다음의 Warning을 해결해보자.

     

    Warning: Each child in a list should have a unique "key" prop.

    해석 : 각각 List 내의 child는 유니크한 key prop을 가져야 한다.

     

    즉, key라는 이름으로 각 객체들에 대한 개별적인 prop을 전달해주어야 한다는 것이다.

    그래서 다음과 같이 foodILike의 각 객체들에 id라는 element들을 주고 그것들을 Food 컴포넌트에서 받을 때 key라는 값으로 받도록 하면 된다. App 컴포넌트에서 전달한 'key' prop은 사용을 해도 되고 안해도 된다. 

     

    <App.js>

    const foodILike = [
      {
        id:1,
        name: "Kimchi",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_01-.jpg"
      },
      {
        id:2,
        name: "Samgyeopsal",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_03-1-1-150x150.jpg"
      },
      {
        id:3,
        name: "Bibimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_05-1-1.jpg"
      },
      {
        id:4,
        name: "Donkatsu",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_04-1-1.jpg"
      },
      {
        id:5,
        name: "Kimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_09-1-1-150x150.jpg"
      }
    ]
    
    
    function App() {
      return (
        <div>
          {foodILike.map(dish => 
            <Food key = {dish.id} fav = {dish.name} img = {dish.image}/>
    
          )}
        </div>
      )
    }

    12) Food 컴포넌트에서 alt 값을 다음과 같이 {props.fav}로 바꾸자. 시각장애인들을 위해선 단순히 profileimg다 라는 설명보다는 그 이미지가 나타내는 것이 무엇인지 그 name을 말해주는게 좋으니까!

     

    <App.js>

    function Food(props){           
      return (
        <div>
          <h1>I like {props.fav} </h1>
          <img src={props.img} alt={props.fav}/>
        </div>
      )
    }

    13) 이젠 props에 대해 부모 컴포넌트(여기서는 App 컴포넌트)로부터 전달받은 props가 자식 컴포넌트(여기서는 Food 컴포넌트)에서 쓸 수 있는 이름인지 확인하는 기능을 갖출 것임.

     

    먼저, 다음과 같이 rating이라는 element를 foodILike 배열에 추가하자. 그리고나서 이 rating이란 element를 다음과 같이 Food에 넘겨주도록 하자.

     

    <App.js>

    const foodILike = [
      {
        id:1,
        name: "Kimchi",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_01-.jpg",
        rating: 5.0
      },
      {
        id:2,
        name: "Samgyeopsal",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_03-1-1-150x150.jpg",
        rating: 5.9
      },
      {
        id:3,
        name: "Bibimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_05-1-1.jpg",
        rating: 1.6
      },
      {
        id:4,
        name: "Donkatsu",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_04-1-1.jpg",
        rating: 10.3
      },
      {
        id:5,
        name: "Kimbap",
        image: "https://www.aeriskitchen.com/wp-content/uploads/2008/04/buckwheat_guksu_09-1-1-150x150.jpg",
        rating: 4.2
      }
    ]
    
    function Food(props){           
      return (
        <div>
          <h1>I like {props.fav} </h1>
          <h4>{props.rating}/20.0</h4>
          <img src={props.img} alt={props.fav}/>
        </div>
      )
    }
    
    function App() {
      return (
        <div>
          {foodILike.map(dish => 
            <Food 
              key = {dish.id} 
              fav = {dish.name} 
              img = {dish.image} 
              rating = {dish.rating}
            />
    
          )}
        </div>
      )
    }

    이후, 다음 명령어로 prop-types를 설치해준다. 설치 후 package.json에 가면 설치가 잘 되어있는지 확인할 수 있다.

    npm i prop-types

    이제 propTypes를 활용해보자. 다음과 같이 Food 컴포넌트에 대한 propTypes를 정의해주면 각각의 요소들에 대하여 타입이 맞는지 혹은 정의가 되어있는지 등을 확인할 수 있다.

    Food.propTypes = {
      fav: PropTypes.string.isRequired,
      img: PropTypes.string.isRequired,
      rating: PropTypes.string.isRequired
    }

    예를 들면 rating은 number타입의 요소이다. 하지만 propTypes에서는 이것이 string인지 아닌지를 확인한다. 그래서 Console창에서는 다음과 같은 Warning 문구를 보내준다.

    Warning: Failed prop type: Invalid prop `rating` of type `number` supplied to `Food`, expected `string`.

    이때 만약 rating이란 prop에 대해서 App 컴포넌트에서 rating이란 이름 대신 rate = {dish.rating} 이런 식으로 적어둔다면 rate란 이름에 대해서 Food 컴포넌트에서는 prop으로 정의되어 있지 않으므로 undefined로 Warning을 띄울 것이다

     

    혹은 만약 여기서 rating element에 대해서 반드시 필요로 한지 여부를 검사할 필요가 없다면, 즉, foodILike이란 데이터에서 어떤 객체는 rating element를 갖지 않더라도 상관이 없다면 .isRequired를 안 붙이면 된다.

     

     

     

    728x90
    반응형

    'ReactJS 영화 웹 서비스 만들기' 카테고리의 다른 글

    #5. CONCLUSIONS  (0) 2021.11.02
    #4. MAKING THE MOVIE APP  (0) 2021.11.02
    #3. STATE  (0) 2021.10.26
    #1. SETUP  (0) 2021.10.15
    #0. INTRODUCTION  (0) 2021.10.15