Frontend/🌐 React

[REACT] ReactJS로 영화 웹 서비스 만들기

haeunkim.on 2022. 8. 19. 00:04

(((((((((((기이이인 글 주의. 한 강의를 한 포스트에 정리함)))))))))))

 

 

 

 

당장 금요일부터 리액트를 써야한다. 

진짜 .. 큰일났다.

 

이 글은 강의의 목차를 그대로 가져와서 그 순서대로 정리할 예정이다. 목차는 다음과 같다. 

 

러닝커브가 높은 react이기 때문에 오히려 기초를 확실히 정리해두어야 할 것 같다. 

그게 바로 오늘이 되길 바라면서 시작해본다. 

더보기
1 [2021 UPDATE] INTRODUCTION
#1.1 ❤️ 무료 강의 ❤️
#1.2 Why React
#1.3 Requirements
 
2 [2021 UPDATE] THE BASICS OF REACT
#2.0 Introduction
#2.1 Before React
#2.2 Our First React Element
#2.3 Events in React
#2.4 Recap
#2.5 JSX
#2.6 JSX part Two
 
3 [2021 UPDATE] STATE
#3.0 Understanding State
#3.1 setState part One
#3.2 setState part Two
#3.3 Recap
#3.4 State Functions
#3.5 Inputs and State
#3.6 State Practice part One
#3.7 State Practice part Two
#3.8 Recap
#3.9 Final Practice and Recap
 
4 [2021 UPDATE] PROPS
#4.0 Props
#4.1 Memo
#4.2 Prop Types
#4.3 Recap
 
5 [2021 UPDATE] CREATE REACT APP
#5.0 Introduction
#5.1 Tour of CRA
 
6 [2021 UPDATE] EFFECTS
#6.0 Introduction
#6.1 useEffect
#6.2 Deps
#6.3 Recap
#6.4 Cleanup
 
7 [2021 UPDATE] PRACTICE MOVIE APP
#7.0 To Do List part One
#7.1 To Do List part Two
#7.2 Coin Tracker
#7.3 Movie App part One
#7.4 Movie App part Two
#7.5 React Router
#7.6 Parameters
#7.7 Publishing
#7.8 Conclusions
#7.9 Styles
#7.10 Next Steps
 
8 INTRODUCTION
#8.0 🚨 Read this First 🚨
#8.1 Introduction (01:20)
#8.2 Requirements (04:24)
#8.2 Theory Requirements (02:17)
#8.3 Why React (04:44)
 
9 SETUP
#9.0 Creating your first React App (05:40)
#9.1 Creating a Github Repository (02:36)
#9.2 How does React work? (06:30)
 
10 JSX & PROPS
#10.0 Creating your first React Component (06:54)
#10.1 Reusable Components with JSX + Props (09:12)
#10.2 Dynamic Component Generation (09:54)
#10.3 map Recap (06:51)
#10.4 Protection with PropTypes (08:51)

11 STATE

#11.0 Class Components and State (10:11)

#11.1 All you need to know about State (07:55)

#11.2 Component Life Cycle (08:07)

#11.3 Planning the Movie Component (05:02)

 

12 MAKING THE MOVIE APP
#12.0 Fetching Movies from API (08:43)
#12.1 Rendering the Movies (11:01)
#12.2 Styling the Movies (06:21)
#12.3 Adding Genres (06:16)
#12.4 Styles Timelapse (05:30)
#12.5 Cutting the summary (03:23)
 
13 CONCLUSIONS
#13.0 Deploying to Github Pages (07:37)
#13.1 Are we done? (04:26)
 
14 ROUTING BONUS
#14.0 Getting Ready for the Router (04:14)
#14.1 Building the Router (08:51)
#14.2 Building the Navigation (05:36)
#14.3 Sharing Props Between Routes (07:47)
#14.4 Redirecting (08:53)
 

1 [2021 UPDATE] INTRODUCTION

#1.1 ❤️ 무료 강의 ❤️

페이스북은 여전히 react js 사용하고 있으며 여전히 살아남았다. 프론트앤드 개발자로서 좋은 도구가 될 것! 

 

#1.2 Why React

왜 react js를 배우는 것이 중요한지, 그리고 얼마나 안전한지

처음엔 매우 위험했지만 오늘날엔 아주 좋은 선택. 

1. 신기술을 배울땐 누가 이 기술을 사용하는 지, 얼마나 규모가 큰 지 봐야한다. 상위 1만개의 웹 사이트중 44.76%가 리액트를 사용한다. 가장 많이 사용한다고 보면 됨. 

에어비앤비, 인스타그램, 페이스북, 넷플릭스가 사용중

2.이런 거대한 회사에서 연구하는 사람은 프론트엔드를 크게 바꾸고 싶지 않아한다. 자주 수정하거나 그럴 시간이 없음. 

안정적인것을 원한다. 이런 사람들이 이용하는 것을 배우는 것은 아주 좋은 선택이 될 것.

3. 큰 커뮤니티를 갖고 있음. 자바스크립트랑 매우 비슷하기 때문. 대부분 자바스크립트 작업임. 

이후 리액트 네이티브에 쓸 수 있음.

 

#1.3 Requirements

자바스크립트 기초가 있어야함. 바닐라 js 로 크롬앱 만들기 해오라고 했지만 이미 해봤기도 하고.. pass

 

2 [2021 UPDATE] THE BASICS OF REACT

#2.0 Introduction
 

리액트가 해결하려고 하는 문제는?

REACT JS는 UI를 interactive 하게 만들어줌.

바닐라js와 react 코드를 비교

 
#2.1 Before React
 
일반 바닐라js

 

1. html 만들기

2. javascript 가져오기

3. event 감지

4. 데이터 업데이트

5. html업데이트

 

이대로 작업하다간 아주 혼란스러워질 수 있음 

 

더 좋은 방법 : REACT

reactdhk react-dom 스크립트 추가 먼저 하자. 

<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>
추가하고 나면 콘솔에 리액트 뜨는 것을 확인할 수 있다.
#2.2 Our First React Element
React js의 규칙중 하나는 html 을 이 페이지에 직접 작성하지 않는다는 것이다.
대신 자바스크립트와 리액트 js를 통해 element를 생성할 것이다. 
 
 
먼저, react js로 element를 생성하는 어려운 방법 소개해줄거야
이것은 개발자들이 쓰는 방식이 아니다. 개발자들은 게으르기 때문에 툴을 사용한다...
react js의 본질을 배우기 위해서는 필요한 단계! 
 
스크립트에 import된 두 링크중 첫번째 링크인 react js는 어플리케이션이 interactive할 수 있도록 만들어주는 library (=ui 그림, 엔진!)이고,
react-dom은 library 혹은 package이며 모든 react element들을 html body에 둘 수 있도록 해준다.
 
<!DOCTYPE html>
<html>
    <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>
        const root = document.getElementById("root");
        const span = React.createElement("span", [content]);
        ReactDOM.render(span, root)
    </script>
</html>

 

 
render의 의미는 span(만들어진 react element)를 html로 만들어 배치한다는 뜻, 사용자에게 보여주는 것이다. 
 
render의 두번째 파라메터는 span을 어디에 둘 것인지를 적는다. 많은 사람들이 하는 방식은 id가 root인 div를 만드는 것이다. 
reactdom에게 span을 root에 두라는 뜻 
 
 
 
 
 
두번째 argument엔 property를, 세번째엔 content를 넣었다. 
 
 
우리는 span을 넣고 싶으면 그냥 html에 쓰면 된다. 
이번 강의의 요점은 지루한 예제를 코딩해봄으로써 React JS는 우리가 해왔던 방식을 거꾸로 하고 있다는 것을 설명한거야.
바닐라 js에서는 html을 그냥 먼저 만들고 그걸 js로 가져와서 html을 수정하는 식이었다면
reactjs는 모든 것이 js로 시작해 그 다음에 html이 되는 것이다. 이것이 리액트 js의 파워이다. 
js와 react js를 사용하여 element를 생성할 때에는 react js가 element를 생성하고 있다. 이말은 reactjs는 업데이트가 필요한 element를 업데이트할 것이라는 말이다. reactjs가 결과물인 html을 컨트롤 할 수 있다는 것이다. 이것이 핵심! 
 
 
#2.3 Events in React
 
이벤트를 추가할때 기존처럼 clickHandler이라는 함수를 만들어 addEventListener를 할 필요가 없다.
element를 만들때 on+Event: 를 만들어 바로 넣을 수 있다
이걸 공부하고 있자니 플러터가 그립다..
 
const root = document.getElementById("root");
const span = React.createElement(
    "h3", {
    id: "prettyspan", 
    style: {color: "blue"},
    onMouseEnter: () => console.log("mouse enter!")
	}, "Hello I'm a span");
const btn = React.createElement(
    "button", {
    onClick: () => console.log("i'm clicked")
}, "Click me");
const container = React.createElement("div", null, [span, btn]);
ReactDOM.render(container, root)
 
 
#2.4 Recap
복습하는 시간! 사실 지금도 위에거 다시 읽으면서 알아서 복습하고 있는데 복습을 위한 강의를 하나 만들어주시다니 감사할따름 ...! 
 
1. react js와 react dom 을 import했다. 각각의 기능은 위에..
2. body에 있는 div생성 
3. root div를 가져온 후에 reactDOM.render을 함
 
 createElemet의 두번째 argument인 props에서 event를 넣을 수도 있고 id를 넣을 수도있고 ..
reactjs는 스마트하기 때문에 어떤 것은 html로 가야하는데, 이벤트는 안간다. 
앞으로 react 개발자로서, createElemet를 쓸일은 없을 것이다. 
하지만 이건 핵심.! 어려운 방식인데도 이해 완
 
데이터를 업데이트 하는 법을 강의에서 다루진 않았지만, 왠지 flutter처럼 ()=>{ 여기 안에 코드를 넣으면 업데이트가 될 것 같아서 }
해봤는데 된다.! 
 
다음 강의에서는 React element를 생성하는 더 쉬운 방법을 배워볼 것이다! 
 
기대된다.  
 
 
#2.5 JSX
const span = React.createElement(
    "h3", {
    id: "prettyspan", 
    style: {color: "blue"},
    onMouseEnter: () => {

        count++;
        console.log("mouse enter! ");
        console.log(count);
    }
}, "Hello I'm a span");
const btn = React.createElement(
    "button", {
    onClick: () => console.log("i'm clicked")
}, "Click me");
 
기존 방식에서 이부분을 바꿔볼 것이다! 
그 편리한 녀석은 jsx라는 것이다. 자바스크립트를 확장한 것. 
생긴게 html이랑 비슷해서 jsx로 react 요소를 만드는 게 개발자들 입장에서는 굉장히 편함. 
 
     const Title = (
        <h3 id="title" onMouseEnter={() => console.log("mouse enter!")}>
        "Hello I'm a span"
        </h3>
    );
 
하.. 이게 끝이다. 너무 간단하고 개멋지다. 참 html이랑 비슷하면서도 flutter가 자꾸 생각나는 문법이다. 
 
h3태그를 열고 닫으면 되고, property들은 마치 html 태그의 속성처럼 적으면 된다. 
 
const Button = (
    <button 
        style={{
            backgroundColor: "tomato"
        }}
        onClick={() => console.log("i'm clicked")}
    >
        click me!
    </button>
);

button element도 마찬가지로 만들어주었다. 

이대로 live server를 돌리면 에러가 뜨는데,

 
우린 브라우저가 jsx를 이해할 수 있도록 
 
babel로 와서 우리의 jsx 코드를 넣어주면, 우리가 이미 적어봤던 코드가 나온다.

https://babeljs.io/docs/en/babel-standalone

우리는 지금 배우고 있어서 이 방식으로 진행하는 거지, 혼자할 때는 절대 이렇게 할 일 없을것 느리기때문. 더 나은 방식이 있음

 

babel을 import해주고 script 안에 type을 지정해주면 이렇게 잘 뜨는 것을 확인할 수 있다.  

 
#2.6 JSX part Two
 
오키.. 이제 해줄거는 위의 코드를 다른 방식으로 적어볼거임 
왜냐면 나는 rendering을 하고싶지 않음.
 
container도 createElement를 없애버리기 위해 위와 같이 jsx 문법을 사용해 content에 Title Button 이라고 적으면 그냥 텍스트가 나와버린다.. 그래서 해야하는 것은 Title과 Button을 함수로 만드는 것이다. 
 
function Title() {
    return(
    <h3 id="title" onMouseEnter={() => console.log("mouse enter!")}>
    "Hello I'm a span"
    </h3>
    );
};

const Button = ()=> (
    <button 
        style={{
            backgroundColor: "tomato"
        }}
        onClick={() => console.log("i'm clicked")}
    >
        click me!
    </button>
);
 
두개가 아예 똑같음.
arrow funtion을 만드는 것은 위와같이 return하여 나타내는 것과 같다. 
 
이제 container에 넣기 위해서는 
const Container = (
    <div>
        <Title />
        <Button />
    </div>
);
 
이렇게 태그로 넣어주면 된다. 세상에 이렇게 쉬울수가! 
우린 지금 여러 컴포넌트들이 합쳐진 구성을 만든 것이다. 
div태그를 렌더링 하고있는 컴포넌트가 하나 있는데, title과 button에 관련된 코드를 포함시키고 있는 것이다.
컴포넌트는 전부 대문자로 시작해야한다 (html과의 혼란 방지)
 
헤더 가면 바뀐 코드 볼 수 있다.
 
 
3 [2021 UPDATE] STATE
#3.0 Understanding State
 
<script type="text/babel">
    const root = document.getElementById("root");
    let counter = 0;
    function countUp(){
        counter = counter+1;
    }
    const Container = () => (
        <div> 
            <h3>Total clicks: {counter}</h3>
            <button onClick={countUp}> click me!</button>
        </div>
    );
    ReactDOM.render(<Container />, root)
</script>
 
 
이 코드에서 실행을 시켜보면
counter은 잘 올라가지만 ui가 바뀌지 않고 있는 것을 확인할 수 있다. 이것은 Container을 한번만 렌더링 하고 있기 때문이다.

우리가 countUp 함수가 실행될때마다 render을 다시 실행해주어야 한다. 

<script type="text/babel">
    const root = document.getElementById("root");
    let counter = 0;
    function countUp(){
        counter = counter+1;
        render()
    }
    function render(){
        ReactDOM.render(<Container />, root)
    }
    const Container = () => (
        <div> 
            <h3>Total clicks: {counter}</h3>
            <button onClick={countUp}> click me!</button>
        </div>
    );
    render();
</script>

 이렇게 render함수를 만들어주면 매번 ui가 변경된다. 

하지만 이건 best가 아니다.

 

 

그 전에!&nbsp; vanilla.js와 다르게 리액트는 업데이트 될때 바뀐부분만 업데이트를 해주고 있다는 점 확인하고 가기~ 다른 부분만 파악하는 ReactJS
 
 
#3.1 setState part One
앞서 살펴본 방법은 업데이트가 필요할때마다 render() 함수를 실행시켜주어야하기 때문에 효율적이지 못하다.
우리는 counter가 0일때 최초의 render을 시키고 그 이후엔 하나씩 + 될때마다 렌더링이 된다.
 
<script type="text/babel">
    const root = document.getElementById("root");
    function Container (){
        return (
            <div> 
                <h3>Total clicks: 0</h3>
                <button> click me!</button>
            </div>
        );
    }
    ReactDOM.render(<Container />, root)
</script>
 
다시 이 코드로 돌아와서..! 
 
const [counter, modifier] = React.useState(0);
 
counter와 modifier에 길이가 2인 배열의 요소들을 하나씩 넣어주는 코드. 
useState는 디폴트값이 0이라는 뜻  (counter 에 0이 들어감)
 
 
#3.2 setState part Two
<script type="text/babel">
    const root = document.getElementById("root");
    function App (){
        const [counter, setCounter] = React.useState(0);
        return (
            <div> 
                <h3>Total clicks: {counter}</h3>
                <button onClick={()=>{setCounter(counter+1)}}> click me!</button>
            </div>
        );
    }
    ReactDOM.render(<App />, root)
</script>
 
 
강의에서는 onClick이라는 함수를 선언해서 사용하는데 나는 그냥 바로 버튼안에 함수 넣어버렸다.
코드가 정말 간단하고 아름답다 ....✨
 
 
#3.3 Recap
#3.4 State Functions
 
위와 같은 코드로 counter을 컨트롤 하게되면 
이전 단계의 state를 이용해서 현재 state를 바꾸려 했지만, 결과가 예상과 다르게 나올 수 있음
 
setCounter(counter+1);
setCounter((current)=>current+1);
 
이렇게 하면 현재 값을 기준으로 바뀌는 것을 보장할 수 있음.
 
 
#3.5 Inputs and State
jsx에서 알아야 기억해야할 것. class 대신 className, for 대신 htmlFor을 사용해야함. 
 
<script type="text/babel">
    const root = document.getElementById("root");

    function App (){
        const [minutes, setMinutes] = React.useState();
        const onChange = (event) => {
            setMinutes(event.target.value);
        }
        return (
            <div> 
                <label for="minutes">Minutes</label>
                <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}/>
                <h4>You want to convert {minutes}</h4>
                <label>Hours</label>
                <input placeholder="Hours" type="number"/>
            </div>
        );  
    }
    ReactDOM.render(<App />, root)
</script>
 
input값을 h4에 넣어주려면 event를 이용해서 event.target.value를 setMinutes를 이용해서 함수 안에 넣어주면 된다.
 
그러면 이렇게 숫자를 입력할때마다 아래의 minutes 값이 바로 바뀜슨

근데 여기서 onChange property를 지워주면 아무리 입력하려 해도 디폴트값으로 그대로 고정됨. 그래서 value와 onchange 둘다 필요

#3.6 State Practice part One

<script type="text/babel">
    const root = document.getElementById("root");

    function App (){
        const [minutes, setMinutes] = React.useState();
        const onChange = (event) => {
            setMinutes(event.target.value);
        }
        return (
            <div> 
                <h1>Super Converter</h1>
                <label for="minutes">Minutes</label>
                <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}/>
                <label for="hours">Hours</label>
                <input value={minutes/60} placeholder="Hours" type="number" disabled/>
                <button onClick={()=>{setMinutes(0)}}>reset</button>
            </div>

        );  
    }
    ReactDOM.render(<App />, root)
</script>

minute을 hour로 변환

 
#3.7 State Practice part Two
 
<script type="text/babel">
    const root = document.getElementById("root");
    function App (){
        const [minutes, setMinutes] = React.useState(0);
        const [hours, setHours] = React.useState(0);
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event) => {
            if(!flipped) {
                setMinutes(event.target.value);
                setHours(event.target.value/60);
            }
            else {
                setHours(event.target.value);
                setMinutes(event.target.value*60);
            }
        };
        const reset = () => {
            setMinutes(0);
            setHours(0);
        };
        const onFlip = () => {
            setFlipped((current)=> !current);
            reset();
        };
        return (
            <div> 
                <h1>Super Converter</h1>
                <label for="minutes">Minutes</label>
                <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled ={flipped}/>
                <label for="hours">Hours</label>
                <input value={hours} id="hours" placeholder="Hours" type="number"  onChange={onChange} disabled ={!flipped} />
                <button onClick={reset}>reset</button>
                <button onClick={onFlip}>flip</button>
            </div>

        );  
    }
    ReactDOM.render(<App />, root)
</script>
 
flip기능과 reset기능까지 완성한 전체코드
강의에서는 amout로 minutes과 hours를 합쳐서 사용했지만 난 조금더 직관적인게 좋아서 이렇게 했다.
강의에선 flip을 기준으로 value를 if 써서 계산함
 
 
#3.8 Recap
 

이렇게 하면 버튼 더 user-friendly 

<button onClick={onFlip}>{flipped? "분->시로 바꾸기" : "시->분으로 바꾸기"}</button>
 
 
#3.9 Final Practice and Recap
 
const root = document.getElementById("root");

function MinutesToHours(){
    const [minutes, setMinutes] = React.useState(0);
    const [hours, setHours] = React.useState(0);
    const [flipped, setFlipped] = React.useState(false);
    const onChange = (event) => {
        if(!flipped) {
            setMinutes(event.target.value);
            setHours(event.target.value/60);
        }
        else {
            setHours(event.target.value);
            setMinutes(event.target.value*60);
        }
    };
    const reset = () => {
        setMinutes(0);
        setHours(0);
    };
    const onFlip = () => {
        setFlipped((current)=> !current);
        reset();
    };
    return (
        <div> 
            <label for="minutes">Minutes</label>
            <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled ={flipped}/>
            <label for="hours">Hours</label>
            <input value={hours} id="hours" placeholder="Hours" type="number"  onChange={onChange} disabled ={!flipped} />
            <button onClick={reset}>reset</button>
            <button onClick={onFlip}>{flipped? "분->시로 바꾸기" : "시->분으로 바꾸기"}</button>
        </div>
    );  
};

function KmToMiles(){
    const [km, setKm] = React.useState(0);
    const [miles, setMiles] = React.useState(0);
    const [flipped, setFlipped] = React.useState(false);
    const onChange = (event) => {
        if(!flipped) {
            setKm(event.target.value);
            setMiles(event.target.value/1.609344);
        }
        else {
            setMiles(event.target.value);
            setKm(event.target.value*1.609344);
        }
    };
    const reset = () => {
        setKm(0);
        setMiles(0);
    };
    const onFlip = () => {
        setFlipped((current)=> !current);
        reset();
    };
    return (
        <div> 
            <label htmlFor="km">KM</label>
            <input value={km} id="km" placeholder="km" type="number" onChange={onChange} disabled ={flipped}/>
            <label htmlFor="miles">miles</label>
            <input value={miles} id="miles" placeholder="miles" type="number"  onChange={onChange} disabled ={!flipped} />
            <button onClick={reset}>reset</button>
            <button onClick={onFlip}>{flipped? "km->miles로 바꾸기":"miles->km로 바꾸기"}</button>
        </div>
    ); 
};

function App (){
    const [index, setIndex]=React.useState("xx");
    function onSelected (event){
        setIndex(event.target.value);
    }
    return (
        <div> 
            <h1>Super Converter</h1>
            <select value={index} onChange={onSelected}>
                <option value="xx">
                    Selet
                </option>
                <option value="0">
                    Minutes to Hours
                </option>
                <option value="1">
                    Km to Miles
                </option>
            </select>

            {index==="0" ? <MinutesToHours /> : null}
            {index==="1" ?<KmToMiles /> : null}
        </div>
    );  
};
ReactDOM.render(<App />, root)

 

final practice 완료 

 

 
 
4 [2021 UPDATE] PROPS
#4.0 Props
 
props 는 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법이다
<MinutesToHours>, <KmToMiles> 컴포넌트는 부모 컴포넌트인 App의 데이터를 필요로 하지 않는다. 
둘은 독립적으로 존재할 수 있다. 
 
우리는 데이터를 보낼 한 예시를 만들거다. 
 
const root = document.getElementById("root");
function Btn({bgcolor, text, big}){
    return <button style={{
        backgroundColor: bgcolor,
        color: "tomato",
        padding:"10px 20px",
        margin: "10px 5px",
        border: "1px solid black" ,
        borderRadius: 10,
        fontSize: big ? 18 : 16
    }}> {text} </button>;
}
function App (){
    return (
        <div> 
            <Btn text="Save Changes" bgcolor="white" big={true}/>
            <Btn text="Continue" bgcolor="black"  big={false}/>
        </div>
    );  
}
ReactDOM.render(<App />, root)
 
여기부터 6-1까지 정리했던 부분이 다 날라갔다... 진짜 눈물난다....................
뭐 어쩌겠어,, 다시 정리해야지.. 
당장 내일 해커톤인데 왜 나에게 이런 시련이 .. 
 
#4.1 Memo
const MemorizedBtn = React.memo(Btn);


function App (){
    const [value, setValue] = React.useState("Save Changes");
    function changeValue(){
        setValue("Revert Changes");
    }
    return (
        <div> 
            <MemorizedBtn text={value} onClick={changeValue} />
            <MemorizedBtn text="안녕" />
        </div>
    );  
}
 
onClick 을 사용한 버튼만 rerender된다
전체를 다 rerendering하게 되면 어플이 느려질 수가 있기 때문에 중요하다.
 
 
#4.2 Prop Types
 
<script type="text/babel">
    const root = document.getElementById("root");
    function Btn({text,onClick}){
        console.log(text," was rendered");
        return <button 
        onClick={onClick}
        style={{
            backgroundColor: "black",
            color: "tomato",
            padding:"10px 20px",
            margin: "10px 5px",
            border: "1px solid black" ,
            borderRadius: 10,
        }}
        > {text} </button>;
    }
    const MemorizedBtn = React.memo(Btn);
    MemorizedBtn.propTypes={
        text: PropTypes.string.isRequired,
        onClick: PropTypes.func,
    };
    function App (){
        const [value, setValue] = React.useState("Save Changes");
        function changeValue(){
            setValue("Revert Changes");
        }
        return (
            <div> 
                <MemorizedBtn text={value} onClick={changeValue} />
                <MemorizedBtn text="안녕" />
            </div>
        );  
    }
    ReactDOM.render(<App />, root)
</script>
 
요기 참조 
 
팀원의 실수 막을 수 있음
 
import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // prop가 특정 JS 형식임을 선언할 수 있습니다.
  // 이것들은 기본적으로 모두 선택 사항입니다.
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 랜더링 될 수 있는 것들은 다음과 같습니다.
  // 숫자(numbers), 문자(strings), 엘리먼트(elements), 또는 이러한 타입들(types)을 포함하고 있는 배열(array) (혹은 배열의 fragment)
  optionalNode: PropTypes.node,

  // React 엘리먼트.
  optionalElement: PropTypes.element,

  // React 엘리먼트 타입 (ie. MyComponent)
  optionalElementType: PropTypes.elementType,

  // prop가 클래스의 인스턴스임을 선언할 수 있습니다.
  // 이 경우 JavaScript의 instanceof 연산자를 사용합니다.
  optionalMessage: PropTypes.instanceOf(Message),

  // 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 여러 종류중 하나의 종류가 될 수 있는 객체
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 특정 타입의 행렬
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 특정 타입의 프로퍼티 값들을 갖는 객체
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 특정 형태를 갖는 객체
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // 추가 프로퍼티에 대한 경고가 있는 객체
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),

  // 위에 있는 것 모두 `isRequired`와 연결하여 prop가 제공되지 않았을 때
  // 경고가 보이도록 할 수 있습니다.
  requiredFunc: PropTypes.func.isRequired,

  // 모든 데이터 타입이 가능한 필수값
  requiredAny: PropTypes.any.isRequired,

  // 사용자 정의 유효성 검사기를 지정할 수도 있습니다.
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
  // `oneOfType`안에서는 작동하지 않으므로 `console.warn` 혹은 throw 하지 마세요.
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // `arrayOf` 와 `objectOf 에 사용자 정의 유효성 검사기를 적용할 수 있습니다.
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
  // 유효성 검사기는 배열(array) 혹은 객체의 각 키(key)에 대하여 호출될 것입니다.
  // 유효성 검사기의 첫 두 개의 변수는 배열 혹은 객체 자신과 현재 아이템의 키입니다.

  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};
 
#4.3 Recap 
pass
 
5 [2021 UPDATE] CREATE REACT APP
#5.0 Introduction
create-react-app을 이용하면 개발 서버에 접근한다던가, 자동으로 새로고침을 해준다던가 즉각적으로 어플리케이션에 css를 포함시켜주는 등 아주 좋다..
 
node.js에 가서 다운로드 받고 node -v 로 확인 그리고 npx 도 입력
실행은 npm start
 
 
필요없는 것 다 빼고 만든 ver.
 
 
 
 
#5.1 Tour of CRA
 

Button.js

import PropTypes from "prop-types";
import style from "./Button.module.css"
function Button({text}){
    return (
        <button className={style.btn}>{text}</button>
    );
}

Button.propTypes = {
    text : PropTypes.string.isRequired,
};

export default Button;
 
 
 
app.js
 
App.module.css 

 

.title{
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    font-size: 18px;
}​
 
특정 컴포넌트를 위한 css를 만드는 것이 가능해졌다. 
 
 
6 [2021 UPDATE] EFFECTS
#6.0 Introduction
 
마지막으로 가장 중요한 이야기!
바뀔 필요가 없는 요소들까지 다시 렌더링 되는 문제를 해결하기 위함이다. 
 
 
 
 
 
#6.1 useEffect
 
아래 코드랑 합칠래.. 
코드를 언제 실행할 지 우리가 선택할 수 있도록 만드는 도구!
 
 
#6.2 Deps
 

Dependency

import {useState, useEffect} from "react";

function App() {
  const [counter, setValue] = useState(0);
  const [keyword, setKeyword] = useState("");
  const onClick = () => setValue((prev)=>prev+1);
  const onChange = (event) => setKeyword(event.target.value);
  console.log("I run all the time");
  useEffect(()=>{
    console.log("CALL the API...");
  }, []);
  useEffect(()=>{
    console.log("Search for", keyword);
  }, [keyword]);
  return (
    <div>
      <input 
        value={keyword} 
        onChange={onChange} 
        type="text" 
        placeholder="Search here..." 
      />
      <h1> {counter} </h1>
      <button onClick={onClick} >click</button>
    </div>
  );
}

export default App;
 
키워드가 변경할때만 안의 코드가 실행된다.
 
 
그냥 useEffect에 넣은 CALL ~ 은 처음 렌더링 될때만 한번 실행된다. Search~ 는 input 박스 안의 값이 바뀔 때 실행되고 I run all~은 서칭을 할때든, 버튼을 클릭할때든 무언가 바뀌면 항상 작동한다.

 

useEffect(()=>{
    if(keyword.length>5)
      console.log("Search for", keyword);
  }, [keyword]);

이렇게 조건도 추가할 수 있다. 

 

[] 이 안의 것을 리액트가 지켜보고 있다고 생각하면 쉽다. 아무것도 없는 건 지켜봐도 아무일이 없으니까 처음 실행될 때만 콘솔로그가 작동하는 것이다. 리스트이기 때문에 [counter, keyword] 이렇게 넣어도 된다. 둘 중 하나가 실행될 때 코드가 작동한다. 

 

 

#6.3 Recap
 
#6.4 Cleanup
import {useState, useEffect} from "react";

function Hello(){
  useEffect(()=>{
    console.log("I'm here :)");
    return () => console.log("destroyed :(")
  },[]);
  return <h1>Hello</h1>
}
function App() {
  const [showing, setShowing] = useState(false);
  const onClick = () => setShowing((prev)=>!prev);
 
  return (
    <div>
      {showing ? <Hello /> : null}
      <button onClick={onClick}>{showing?"hide":"show"}</button>
    </div>
  );
}

export default App;
 
useEffect 함수의 return으로 함수를 주면, hide버튼을 눌렀을 때 destroyed :( 가 프린트 된다. 
이것을 Cleanup function이라고 부른다. 
 
 
import {useState, useEffect} from "react";

function Hello(){

  function effectFn(){
    console.log("created :)");
    return byeFn
  }
  function byeFn(){
    console.log("bye~ .. ")
  }
  useEffect(effectFn,[]);
  return <h1>Hello</h1>
}
function App() {
  const [showing, setShowing] = useState(false);
  const onClick = () => setShowing((prev)=>!prev);
 
  return (
    <div>
      {showing ? <Hello /> : null}
      <button onClick={onClick}>{showing?"hide":"show"}</button>
    </div>
  );
}

export default App;
 
함수를 따로 빼서 조금 더 직관적으로 보이게 한 코드 
 
니꼬says
react 앱을 만들 때 이 byeFn이 주로 필요하지는 않다. 가끔 해야할 때가 있을 수 있다.
보통은 함수 따로 안빼고 useEffect안에 다 넣어버린다. 
 
7 [2021 UPDATE] PRACTICE MOVIE APP
#7.0 To Do List part One
import { useState } from "react";

function App() {
  const [toDo, setToDo] = useState("");
  const [toDos, setToDos] = useState([]);
  const onChange = (event) => setToDo(event.target.value);
  const onSubmit = (event) => {
    event.preventDefault();
    if (toDo === "") {
      return;
    }
    setToDos((currentArray) => [toDo, ...currentArray]);
    setToDo("");
  };
  return (
    <div>
      <h1>My To Dos ({toDos.length})</h1>
     
      <form onSubmit={onSubmit}>
        <input
          onChange={onChange}
          value={toDo}
          type="text"
          placeholder="Write your to do..."
        />
        <button>Add To Do</button>
      </form>
    </div>
  );
}

export default App;
 
toDo를 수정하는 역할을 하는 함수인 setToDo를 사용해야한다. 직접적인 수정 x
setToDos 함수에서 currentArray 로 현재 상태를 불러와서 새로 받은 toDo를 앞에서 추가해준다. 
 
   setToDos((currentArray) => [toDo, ...currentArray]);
   setToDo("");
 
이 두개를 비교해보자면,
 
우선 setToDo는 단순이 값을 보내는 것이다. 
이에 반해 setToDos는 함수를 보내는 방법이다.
함수를 보낼때 react.js는 함수의 첫번째 argument로 현재 state를 보낸다. 그러면 우린 이것을 이용해서 새로운 toDos를 만들어 return할 수 있는 것이다. 
 
 
#7.1 To Do List part Two
 
import { useState } from "react";

function App() {
  const [toDo, setToDo] = useState("");
  const [toDos, setToDos] = useState([]);
  const onChange = (event) => setToDo(event.target.value);
  const onSubmit = (event) => {
    event.preventDefault();
    if (toDo === "") {
      return;
    }
    setToDos((currentArray) => [toDo, ...currentArray]);
    setToDo("");
  };
  return (
    <div>
      <h1>My To Dos ({toDos.length})</h1>
     
      <form onSubmit={onSubmit}>
        <input
          onChange={onChange}
          value={toDo}
          type="text"
          placeholder="Write your to do..."
        />
        <button>Add To Do</button>
      </form>
      <hr />
      {toDos.map((item, index)=>
        <li key={index}>{item}</li>
        
      )}
       
    </div>
  );
}

export default App;
 
 
 
#7.2 Coin Tracker
 
 이번엔 react에서 처음으로 api를 가져와서 띄워볼 것이다.
처음 실행될 때만 render시키고 싶기 때문에 useEffect를 이용한다. 
 
api 주소
 
import { useState, useEffect } from "react";

function App() {
  const [loading, setLoading]= useState(true);
  const [coins, setCoins] = useState([])
  useEffect(() => {
    fetch("http://api.coinpaprika.com/v1/tickers")
    .then((response) => response.json())
    .then((json) => {
      setCoins(json);
      setLoading(false);
    });
  }, []);
  return (
      <div>
        <h1>The Coins!({coins.length})</h1>
        {loading ? <strong>Loading...</strong>: 
        <ul>
          {coins.map((coin)=> (
            <li>
              {coin.name} ({coin.symbol}): ({coin.quotes.USD.price}) USD
            </li>
          ))}
        </ul>
        }
      </div>
  );
}

export default App;​
 
안불러와져서 꽤나 고생했는데, 내가 실수한 부분은 map의 arrow function 부분이었다. ()=>{} 이렇게 썼었는데, map에서 그렇게 썼더니 못불러왔다. 
 
 
 
돈을 입력하고나서 코인 종류를 select 하여 몇개의 코인을 살 수 있는지 계산해주는 응용 프로그램을 짜봤다 
 
 
import { useState, useEffect } from "react";

function App() {
  const [coins, setCoins] = useState([])
  const [input, setInput] = useState(0);
 
  const [selected, setSelected] = useState(0);
  function onChanged(event){
    setInput(event.target.value);
    console.log(input);
  }
 
  function onSelected(event){
    setSelected(event.target.value);
    console.log(selected);
  }

  useEffect(() => {
    fetch("http://api.coinpaprika.com/v1/tickers")
    .then((response) => response.json())
    .then((json) => {
      setCoins(json);
    
    });
  }, []);
  return (
      <div>
        <h1>The Coins!({coins.length})</h1>
        <form>
          <input value={input} onChange={onChanged} type="number" placeholder="input money"></input>
         
        </form>
        {input!==0 ? 
          
          <div>
            <select value={selected} onChange={onSelected}>
              {coins.map((coin, index)=> (
                <option key={index} value={coin.quotes.USD.price}>
                  {coin.name} ({coin.symbol}): ({coin.quotes.USD.price}) USD
                </option>
              ))}
            </select>
            <h1>{input/selected}</h1>  
           
          </div>
          : null
        }
       
      </div>
  );
}

export default App;
 
 
원랜 버튼을 누르면 아래 select가 나오게 하고 싶었는데, 결국은 그 기준을 input으로 하게 되었다. 
해커톤이 다가오니 시간때문에 쫄린다 ....ㅜ
 
 
 
 
#7.3 Movie App part One
 
import { useState, useEffect } from "react";

function App() {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")
    .then((response) => response.json())
    .then((json) => {
      console.log(json);
    
    });
  }, []);
  return (
    <div>
      {loading ? <h1>Loading...</h1> : null}
    </div>
  );
}

export default App;
 
 
1. 여기까지 작성하고 우선 console 살펴보기 
우리가 원하는 정보는 data.movies에 있는 것을 확인할 수 있다.
 
 
useEffect(() => {
    fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")
    .then((response) => response.json())
    .then((json) => {
      setMovies(json.data.movies);
      console.log(movies);
      setLoading((cur)=>!cur);
    });
  }, []);
 
 movies 에 넣어주기 
 
import { useState, useEffect } from "react";

function App() {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(true);
  const getMovies = async() => {
    const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
    setMovies(json.data.movies);
    setLoading(false);
  }
  useEffect(() => {
    getMovies();
  }, []);
  return (
    <div>
      {loading ? <h1>Loading...</h1> : null}
    </div>
  );
}

export default App;
 
 .then() 대신 getMovies 함수를 만들어 사용하는 방식이다. 
import { useState, useEffect } from "react";

function App() {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(true);
  const getMovies = async() => {
    const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
    setMovies(json.data.movies);
    setLoading(false);
  }
  useEffect(() => {
    getMovies();
  }, []);
  console.log(movies);
  return (
    <div>
      {loading 
      ? <h1>Loading...</h1> 
      : <div>
        {movies.map(movie=>
          <div key={movie.id}>
            <img src={movie.medium_cover_image}></img>
            <h2>{movie.title}</h2>
            <p>{movie.summary}</p>
            <ul>
              {movie.genres.map((g)=>
              <li key={g}>{g}</li>)}
            </ul>
          </div>
           
          )}

      </div>
      }
    </div>
  );
}

export default App;​
 
 
 
 
#7.4 Movie App part Two
 
App.js
import { useState, useEffect } from "react";
import Movie from "./Movie";
function App() {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(true);
  const getMovies = async() => {
    const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
    setMovies(json.data.movies);
    setLoading(false);
  }
  useEffect(() => {
    getMovies();
  }, []);
  console.log(movies);
  return (
    <div>
      {loading ? <h1>Loading...</h1> 
      : <div>
        {movies.map(movie=>
          <Movie
            key={movie.id}
            coverImg={movie.medium_cover_image}
            title={movie.title}
            summary={movie.summary} 
            genres={movie.genres}
          />
        )}
        </div>
      }
    </div>
  );
}

export default App;
 
Movie.js
import PropTypes from "prop-types";

function Movie({ coverImg, title, summary, genres}){
    return(
    <div>
        <img src={coverImg} alt={title}></img>
        <h2>{title}</h2>
        <p>{summary}</p>
        <ul>
            {genres.map((g)=>
                <li key={g}>{g}</li>)}
        </ul>
    </div>
    );
}

Movie.propTypes={
    coverImg: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    summary:  PropTypes.string.isRequired,
    genres: PropTypes.arrayOf(PropTypes.string).isRequired

};

export default Movie;
 key 는 App.js 에서만 사용하면 된다.
img 에 alt 꼭 넣어줘야 한다.
 
 
#7.5 React Router
 
다음은 라우팅!!
npm install react-router-dom 을 입력하여 다운로드 한다. 
 
 
 
route 폴더를 만들어 페이지별 파일에 코드를 넣어두고 
component 폴더를 만들어 아까 분리해뒀던 컴포넌트인 Movie.js 를 넣어준다. 
App.js는 이제 router을 render해주는 역할을 하게 된다. 
router 은 URL을 보고 있는 component다.
 
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
  return <Router>
    <Switch>
    <Route path="/movie">
        <Detail />
      </Route>
      <Route path="/">
        <Home />
      </Route>
    </Switch>
  </Router>;
}
export default App;
 
라우팅 너무 쉬운데..? 그동안 spring에서 mybatis로 mapping 하고 있던거 생각하니까 너무 빡친다. ....  . ..
 
📍
2022/12/02 해커웹사이트 업데이트 
react-router-dom이 업데이트 되면서 라우팅 방식이 좀 바뀌었다. switch 대신 routes를 써줘야하고 Route의 element로 태그 안에 입력해야함.
 
import {
  BrowserRouter as Router,
  Route,
  Routes,
} from "react-router-dom";
import HomePage from "./routes/HomePage";
import AboutPage from "./routes/AboutPage";

function App() {
  return <Router>
    <Routes>
      <Route path="/" element={<HomePage />}>
      </Route>
      <Route path="/about" element={<AboutPage />}>
      </Route>
    </Routes>
  </Router>;
}
export default App;
 
 
 
 
html 에서 사용한 방식으로 <a href=""> 으로 하면 전체가 새로고침된다는 단점이 있다. 
이걸 보완한게 바로 <Link>이다. 
 
import PropTypes from "prop-types";
import {Link} from "react-router-dom";

function Movie({coverImg, title, summary, genres}){
    return(
    <div>
        <img src={coverImg} alt={title}></img>
        <h2>
            <Link to="/movie">{title}</Link>
        </h2>
        <p>{summary}</p>
        <ul>
            {genres.map((g)=>
                <li key={g}>{g}</li>)}
        </ul>
    </div>
    );
}
 
이렇게 Link to 로 감싸주면 새로고침되지 않는다. 
 
 
 
#7.6 Parameters
 
이번엔 movie/123 이런식으로 dynamic url 을 만들어볼 것이다. 
 
 <Link to={`/movie/${id}`}>{title}</Link>
 
property로 id를 추가하고 (key랑 똑같은 id써도 상관 없음)
링크를 수정해주면 
 잘 나온다.
 
<Route path="/movie/:id">
	<Detail />
</Route>
 
:id 이 부분에 오는 값이 뭔지 확인하는 건 아주 쉽다.
 
#7.7 Publishing
 
 github pages는 github에서 제공하는 무료 서비스
npm i gh-pages
 
 
우선 깃헙에 repo 하나 파서 그동안 작업한거 모두 올려두고
 
 
package.json 의 제일 아랫부분에 다음과 같이 추가한다. 
    ]
  },
  "homepage":"https://chamroro.github.io/ReactMovieService"
}
 
 "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "deploy": "gh-pages -d build",
    "predeploy": "npm run build"
  },

위와 같이 추가하면 build 폴더를 삭제하고 npm run deploy이렇게 하면 predeploy 가 된 후에 buld를 한다. 

 

 
이렇게 하면 웹사이트가 업데이트 되게 되는데, 이건 5분정도 걸린다고 한다. 
 
무언가 수정을 한 뒤에 npm run deploy를 해주면 자동으로 프로젝트가 build가 되고 자동으로 github io페이지에 올라가게 된다.
 
#7.8 Conclusions
 
#7.9 Styles
 
스타일 먹이기 완!
 
 
#7.10 Next Steps
 
react.js는 업데이트가 되어도 이전 방식을 그대로 사용할 수가 있는데, 이건 업데이트 될 때마다 추가만 하기 때문이다. 
useState와 useEffect 가 베스트 조합이고, 옛날 방식은 this, binding, constructor등 알아야하는 것들이 많다.
react.js는 처음부터 props와 state를 가지고 있었고 예전방식은 그냥 코드를 작성하는 다른 방식일 뿐이다. 
다음 영상들은 예전 방식으로 같은 앱을 만드는 것이 나오는 것이다. 
따라서,., 난 안할거다 
 
 
 
 
 
 
 
 
 
 
 
마지막으로 나의 프로젝트를 올리며 
해커톤 벼락치기 끝! 

 

React App

 

chamroro.github.io

 

반응형