본문 바로가기

22 - 1학기/팀 스터디

[웹 FE 스터디] ReactJs ~6.4

TAB 2022 2학기 학회활동 [팀 스터디]

 TIL 포스팅입니다.

작성자 : 40기_박지민


노마드 코더 - ReactJS로 영화 웹 서비스 만들기


▶ 시간↔분 전환 프로그램 예제

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HourMinute_converter</title>
</head>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> 
<script type="text/babel"> 
    function App(){
    	const [amount,setAmount] = React.useState(0);
        const [inverted,SetInverted] = React.useState(false);
        const onChange = (event) =>{
        	setAmount(event.target.value);
        };
        const reset=()=>setAmount(0);
        const onInvert = ()=>{
            reset();
            setInverted((current)=>!current);
        };
        return(
        <div>
            <div>
                <h1>Converterrrr</h1>
                <label htmlFor="minutes">Minutes</label>
                <input 
                    value ={inverted?amount*60:amount}
                    type = "number"
                    placeholder ="Minutes"
                    id="minutes"
                    onChange ={onChange}
                    disabled={inverted}
                />
            </div>
            <div>
                <label htmlFor="hours">Hours</label>
                <input 
                    value ={inverted?amount:Math.round(amount/60)}
                    type = "number"
                    placeholder ="Hours"
                    id="hours"
                    onChange ={onChange}
                    disabled={!inverted}
                />
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onInvert}>
                {inverted?"Turn back":"Invert"}
            </button>
        </div>

        );
        
    }
    const root = document.getElementById("root");
        ReactDOM.render(<App />,root);
</script>
</html>

- onChange의 기능 

: input 태그의 이벤트를 감지. 값 변경 시 setAmount를 이용해 변경된 값을 state안에 삽입. 

데이터 수정을 위해 modifier function (=setAmount) 이 사용되었기 때문에 그 함수를 사용할 때마다 컴포넌트는 새 데이터와 함께 새로고침 됨.

* onChange = { onChange } 가 없을 경우 우변의 onChange prop에 담긴 modifier function이 작동하지 않는다. 이벤트 발생 시 수정된 값으로 업데이트 시켜주는 기능이 사라졌기 때문에 input이 키보드이벤트를 감지해도 그 내부의 값은 디폴트값 0에서 업데이트가 이뤄지지 않는다.

 

- 삼항연산자

: 다른 프로그래밍 언어와 동일한 방식

  (조건문? 참일 경우 실행시킬 내용 : 거짓일 경우 실행시킬 내용) 으로 표현한다.

컴포넌트 내에서 jsx 형태로 {}에 넣어 표현할 수 있다. 

 

- Math.round()

: 반올림 함수.  소수 첫째자리에서 반올림시켜준다.

 

▶시간↔분 / 미터↔킬로미터 변환 프로그램 예제

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HourMinute_converter</title>
</head>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> 
<script type="text/babel"> 
    function MinutesToHours(){...
    function KmToMiles(){... // 앞 내용과 유사하여 생략
    function App(){
    	const [index,setIndex] = React.useState("0")
        const onSelect = (event) =>{
            setIndex(event.target.value);
        };
        return(
        <div>
            <h1>Super Converter</h1>
            <select value = {index} onChange={onSelect}>  
                <option value= "0">Minutes & Hours</option>
                <option value ="1">Km & Miles</option>    
            </select>
            {index ==="0"?<MinutesToHours />: null}
            {index==="1"?<KmToMiles />:null}
        </div>

        );
        
    }
    const root = document.getElementById("root");
        ReactDOM.render(<App />,root);
</script>
</html>

-  { index ==="0"?<MInutesToHours />: null }

: 만약 index의 값이 0이라면 MinutesToHours 컴포넌트를 렌더링하고, 아니라면 아무것도 보여주지 않는다는 뜻.

  현 index의 디폴트값이 0이기 때문에 자동으로 MinutesToHours컴포넌트가 렌더링 된 상태.

  그걸 원하지 않는다면 function App(){} 의 내용을 아래와 같이 변경할 수 있다

function App(){
    	const [index,setIndex] = React.useState("x")
        const onSelect = (event) =>{
            setIndex(event.target.value);
        };
        return(
        <div>
            <h1>Super Converter</h1>
            <select value = {index} onChange={onSelect}> 
            	<option value = "x">Select your units</>
                <option value= "0">Minutes & Hours</option>
                <option value ="1">Km & Miles</option>    
            </select>
            {index ==="0"?<MinutesToHours />: null}
            {index==="1"?<KmToMiles />:null}
        </div>

        );
        
    }

index의 초기값을 x로 변경하고 그 경우의 옵션을 추가하였기 때문에 처음 실행 시 select창에는 "Select your units"문구가 뜨고 index의 값이 0도 1도 아니기 때문에 어떤 컴포넌트도 실행되지 않는다.

이 이후 선택지를 클릭하면 index의 value가 변경되며 리렌더링 되는데

이때 console.log("render w/", index);를 통해 그 변화를 확인할 수 있다.


▶Props

부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법

function Btn({text,big}){
        
        return <button style={{
            backgroundColor:"tomato",
            color:"black",
            padding : "10px 20px",
            border:0,
            borderRadius:10,
            fontSize:big?18:16,
        }}>{text}</button>;
    }
    
    function App(){    	
        return(
        <div>         
          <Btn text = "Save Changes" big={true}/>
          <Btn text = "Continue" big = {false}/>           
        </div>
        );        
    }

- Btn() 괄호 내의 {text,big}이 Btn의 인자이자 Btn으로부터 전달받는 props.

- 첫 번째 함수형 컴포넌트에 의해 text자리에 " Save Changes"가 들어간 버튼이 생성된다. 부모 컴포넌트로부터 big의 값은 true라는 데이터가 Btn 컴포넌트에 보내졌기 때문에 ( fontSize : big? 18 : 16 ) 에 의해 폰트 크기는 18이 된다. 

- <Btn style = {{color:"pink"}} text = {value} big =  {true}  />  로 작성해도 style은 반영되지 않는다. 이곳에 작성한 style은 button이 아니라 Btn컴포넌트에 달린 style이기 때문에 Btn에서 prop를 가져와서 적용시켜야 한다. 

 

▶React.memo( ) 

props가 변경되지 않으면 리렌더링 할 필요가 없도록 한다. 

function Btn({text,onClick}){
        console.log(text, "was rendered")
        return ( 
        <div>
        <button 
        onClick={onClick}
        style={{
            backgroundColor:"tomato",
            color:"black",
            padding : "10px 20px",
            border:0,
            borderRadius:10,
            fontSize:16,
        }}>{text}</button></div>);
    }
    const MemorizedBtn = React.memo(Btn)
     
    function App(){
    	const [value,setValue] = React.useState("Save Changes");
        const changeValue = () => setValue ("Revert Changes");
        return(
        <div>
          
          <MemorizedBtn text = {value} onClick = {changeValue} />
          <MemorizedBtn text = "Continue" />
            
        </div>

        );
        
    }
    const root = document.getElementById("root");
    ReactDOM.render(<App />,root);

- 첫 번째 버튼이 클릭될 때 onClick에 의해 value가 변경되지만 두 번째 버튼은 변경되는 것이 없다.

이 때 MemorizedBtn은 React.memo(Btn)이기 때문에 prop의 변경이 없는 두 번째 버튼은 리렌더링 되지 않는다.

* 콘솔에 Continue was rendered 문구가 뜰 일이 없음 

 

▶PropTypes

props 타입 지정. 타입 오류 시 경고문구가 올라온다.

<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>

PropTypes 사용을 위해 위의 내용을 필요로 한다.

 

- 사용 방식

function 컴포넌트명({prop}){ return {prop};}
컴포넌트명.propTypes ={ prop(종류): PropTypes.자료형};

Ex) Btn.propTypes ={ text: PropTypes.string, fontSize : PropTypes.number,};

 

- .isRequired

: 타입 지정을 확실히 하고 싶을 때, 즉 특정한 컴포넌트가 특정 prop만을 가지고 렌더링된다는 것을 확실히 하고 싶을 때 사용.

  특정 prop.PropTypes.자료형.isRequired 형태.


▶Create-react-app

Node.js의 설치를 필요로 한다. 

Node.js (nodejs.org)

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

다음 페이지에서 설치 후 터미널에서 node -v 와 npx 를 입력해 정상적으로 설치되었는지 확인한다.

npx create-react-app 프로젝트명
code 프로젝트명

으로 Vscode에서 프로젝트를 열 수 있다.

터미널에 npm start 를 입력해주면 개발용 서버가 자동으로 준비된다.  

( *package.json : 모든 스크립트 존재 , src: 모든 파일들이 들어갈 폴더

* 컴포넌트 든 파일에 import 컴포넌트명 from "./컴포넌트명"

* App.js 메인 파일에 export default 컴포넌트명 추가해야 App.js에서 컴포넌트를 가져올 수 있다. )

▶Proptypes 사용

터미널에 npm i prop-types 입력해서 설치.

PropTypes사용될 파일에 import PropTypes from "prop-type" 추가

사용방식은 

 function 컴포넌트명({prop}){ return <tag>{prop}</tag>;}
 컴포넌트명.propTypes ={ prop(종류): PropTypes.자료형.isRequired}; 으로
 create-react-app으로 작업하지 않았을 때와 동일.

 

▶CSS

기존 css의 추가 방법

1. css파일 생성 후 index.js에 import "./파일명.css"; 추가
 => 해당 태그 가진 모든 파일이 css 내 내용을 갖게 된다 (global css style)
      css파일이 적용하려는 파일의 모든 부분에 import된다
2. style prop 사용 style = {{ css 내용 }}
 => 직접적이라 번거롭다

 

∴ 컴포넌트명.module.css형태의 파일 생성.
해당 컴포넌트가 든 파일에  import styles from "./컴포넌트명.module.css" 추가
컴포넌트 내에서 css가 들어갈 태그에 className = {sytles.css클래스명} 추가
동일한 class이름과 컴포넌트를 다른 파일 내에서도 사용할 수 있다.


▶useEffect

state가 변화할 때 모든 컴포넌트는 다시 실행되고, 모든 코드들도 다시 실행.

이 때 무슨 일이 있어도 코드가 특정 상황에서만 실행되도록 하기 위해 useEffect 사용.

import {useState ,useEffect} from "react";

- > useEffect 사용할 파일에 들어가야 할 코드

 

const iRunOnlyOnce = () => { console.log("i run only once.")};
useEffect(iRunOnlyOnce, [] ); //[]: 빈 배열

useEffect 안 함수는 state의 변화에 관계없이 API를 한번만 호출하고 다시는 하지 않는다.

component가 처음 렌더링 될 때 실행되고 다시는 실행되지 않는다.

    useEffect(()=>{
     console.log("i run only once.");
    },[]);

다음과 같이 함수명을 따로 지정하지 않고도 사용 가능하다. 

 

useEffect = (()=>{
	if (keyword!==""&& keyword.length<6){
    	console.log("Search For",keyword);
        }
    },[ keyword ]);

빈 배열 [ ] 내에 특정 state를 넣으면 그 값이 변경될 때만  실행되도록 할 수 있다. 두 개 이상의 state가 들어와도 됨.