8일차

그루트·2021년 9월 23일
0

Event Listener

이벤트 리스너란?

이벤트 리스너는 사용자가 어떤 행동(=이벤트)을 하는 지 아닌 지 지켜보다가 알려주는 것입니다.
대표적으로는 마우스 클릭, 터치, 마우스 오버, 키보드 누름 등이 자주 쓰여요!
더 많은 이벤트가 궁금하다면? →

클래스형 컴포넌트에서 event listener 구독하기

이벤트 리스너는 어디에 위치해야할까요?
클릭을 하건, 마우스를 올리건 DOM 요소가 있어야 이벤트가 발생하는 지 지켜볼 수 있겠죠?
→ 네! componentDidMount()에 넣어주면 됩니다.

어떤 행동(=이벤트 발생!) 뒤에 실행할 함수 먼저 만들어요.

hoverEvent = (e) => {
    // 콘솔로 이 이벤트가 누구에게서 일어났는 지 확인할 수 있습니다.
    console.log(e.target);
    // ref랑 같은 녀석인 지 확인해봐요!
    console.log(this.circle.current);

    this.circle.current.style.background = "yellow";
  }

(2) 이제 addEventListener()를 이용해서 이벤트를 등록합니다.

componentDidMount(){
// 리액트 요소가 잘 잡혔나 확인해봅시다!
console.log(this.circle);

// 마우스를 올렸을 때, 이벤트가 일어나는 지 확인해봅시다.
this.circle.current.addEventListener("mouseover", this.hoverEvent);

}

(3) 이벤트는 꼭 컴포넌트가 사라지면 지워주세요!

이 과정을 clean up 이라고 불러요.
개발자 도구에서 확인했듯, 이벤트는 한번 등록되면 계속 남아있거든요!
그런데 컴포넌트가 사라지면요? 이벤트가 실행되지는 않겠지만, 남아는 있을거예요. 😢
그러니 깨끗하게 정돈해주는 과정이 필요합니다! 그게 clean up이고요!

componentWillUnmount() {
    this.circle.current.removeEventListener("mouseover", this.hoverEvent);
  }

함수형 컴포넌트에서 event listener 구독하기

이벤트 리스너는 어디에 위치해야할까요?

클릭을 하건, 마우스를 올리건 DOM 요소가 있어야 이벤트가 발생하는 지 지켜볼 수 있겠죠?
→ 그럼 함수형 컴포넌트에서는 componentDidMount() 역할을 하는 친구를 가져다 써야겠네요!
useEffect() 훅을 써봅시다!

(1) useEffect()

useEffect()는 리액트 훅이에요.
라이프 사이클 함수 중 componentDidMount와 componentDidUpdate, componentWillUnmount를 합쳐둔 거라고 생각하면 금방 이해할 수 있어요!

  // 첫번째 인자는 익숙하죠! 화살표 함수! 넵, 렌더링 시 실행할 함수가 여기에 들어갑니다.
  // 두번째 인자의 []! 디펜던시 어레이라고 불러요. 여기 넣어준 값이 변하면 첫번째 인자인 콜백함수를 실행합니다.
  React.useEffect(() => {
    // 여기가 rendering 때 실행될 구문이 들어가는 부분입니다.
    // componentDidMount, componentDidUpdate일 때 동작하는 부분이 여기예요.
    // do something ...

    return () => {
        // 여기가 clean up 부분입니다.
        // componentWillUnmount 때 동작하는 부분이 여기예요.
      //do something ...
    };
  }, []);

(2) 어떤 행동(=이벤트 발생!) 뒤에 실행할 함수 먼저 만들어요.

const hoverEvent = (e) => {
    // 콘솔로 이 이벤트가 누구에게서 일어났는 지 확인할 수 있습니다.
    console.log(e.target);
    // ref랑 같은 녀석인 지 확인해봐요!
    console.log(text.current);

    text.current.style.background = "yellow";
  };

(3) 이제 addEventListener()를 이용해서 이벤트를 등록합니다.

// 첫번째 인자는 익숙하죠! 화살표 함수! 넵, 렌더링 시 실행할 함수가 여기에 들어갑니다.
  // 두번째 인자의 []! 디펜던시 어레이라고 불러요. 여기 넣어준 값이 변하면 첫번째 인자인 콜백함수를 실행합니다.
  React.useEffect(() => {
    // 여기가 rendering 때 실행될 구문이 들어가는 부분입니다.
    // componentDidMount, componentDidUpdate일 때 동작하는 부분이 여기예요.
    text.current.addEventListener("mouseover", hoverEvent);
    
    return () => {
        // 여기가 clean up 부분입니다.
        // componentWillUnmount 때 동작하는 부분이 여기예요.
        //do something ...
    };
  }, [text]);

(4) 이벤트는 꼭 컴포넌트가 사라지면 지워주세요!

useEffect에서 clean up은 return 구문을 이용해서 합니다!

// 첫번째 인자는 익숙하죠! 화살표 함수! 넵, 렌더링 시 실행할 함수가 여기에 들어갑니다.
  // 두번째 인자의 []! 디펜던시 어레이라고 불러요. 여기 넣어준 값이 변하면 첫번째 인자인 콜백함수를 실행합니다.
  React.useEffect(() => {
    // 여기가 rendering 때 실행될 구문이 들어가는 부분입니다.
    // componentDidMount, componentDidUpdate일 때 동작하는 부분이 여기예요.
    text.current.addEventListener("mouseover", hoverEvent);
    
    return () => {
        // 여기가 clean up 부분입니다.
        // componentWillUnmount 때 동작하는 부분이 여기예요.
        text.current.removeEventListener("mouseover", hoverEvent);
    };
  }, [text]);

라우팅이란?

SPA란?

Single Page Application!
말 그대로 서버에서 주는 html이 1개 뿐인 어플리케이션이에요.
전통적인 웹사이트는 페이지를 이동할 때마다 서버에서 html, css, js(=정적자원들)을 내려준다면, SPA는 딱 한번만 정적자원을 받아옵니다.

  • 왜 굳이 html을 하나만 줄까?

    → 많은 이유가 있지만, 그 중 제일 중요한 건 사용성 때문입니다.

    페이지를 이동할 때마다 서버에서 주는 html로 화면을 바꾸다보면 상태 유지가 어렵고, 바뀌지 않은 부분까지 새로 불러오니까 비효율적이거든요.
    (사용자가 회원가입하다가 적었던 내용이 날아갈 수도 있고,
    블로그같은 경우, 페이지마다 새로 html을 받아오면 바뀐 건 글 뿐인데 헤더와 카테고리까지 전부 다시 불러와야 합니다.)

  • 단점은 없나?

    → 단점도 있어요. SPA는 딱 한 번 정적자원을 내려받다보니, 처음에 모든 컴포넌트를 받아옵니다.

    즉, 사용자가 안들어가 볼 페이지까지 전부 가지고 옵니다. 게다가 한 번에 전부 가지고 오니까 아주아주 많은 컴포넌트가 있다면 첫 로딩 속도가 느려집니다.

라우팅이란?

SPA는 주소를 어떻게 옮길 수 있을까?
html은 딱 하나를 가지고 있지만, SPA도 브라우저 주소창대로 다른 페이지를 보여줄 수 있어요.
이렇게 브라우저 주소에 따라 다른 페이지를 보여주는 걸 라우팅이라고 부릅니다.

  • 전부 직접 구현하나요?

    → 이미 만들어진 라우팅 라이브러리가 있습니다!

    우리는 리액트 사용자들이 가장 많이 쓰는 라우팅 라이브러리를 가져와서 사용해볼거예요.

리액트에서 라우팅 처리하기 (1)

  • react-router-dom 패키지 설치하기

    • 먼저 새프로젝트를 만들어주세요! ( ⇒ Route_ex 프로젝트 만들기!)

      이번 강의부터는 함수형 컴포넌트로만 진행할거예요!

    • react-router-dom 설치

      yarn add react-router-dom
  • 7) react-router-dom 공식 문서를 보자!

    react-router-dom 공식 문서를 호다닥 살펴봅시다. 🙂
    공식문서는 대부분 비슷하게 생겼으니까, 한 번 살펴보면 앞으로는 혼자서도 잘 읽을 수 있을 거예요.
    공식문서 보러가기→

리액트에서 라우팅 처리하기 (2)

(1) index.js에 BrowserRouter 적용하기

  • BrowserRouter 적용하기

    import React from 'react';
    import ReactDOM from "react-dom";
    import { BrowserRouter } from "react-router-dom";
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    // 이부분이 index.html에 있는 div#root에 우리가 만든 컴포넌트를 실제로 랜더링하도록 연결해주는 부분입니다.
    ReactDOM.render(
      <BrowserRouter>
        <App />
      </BrowserRouter>,
      document.getElementById("root")
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();

BrowserRouter(브라우저라우터)는 웹 브라우저가 가지고 있는 주소 관련 정보를 props로 넘겨주는 친구입니다.
현재 내가 어느 주소를 보고 있는 지 쉽게 알 수 있게 도와줘요.

- (2) 세부 화면 만들기

- Home.js

    ```jsx
    import React from "react";

    const Home = (props) => {

        return (
            <div>메인 화면이에요.</div>
        )
    }

    export default Home;
    ```

- Cat.js

    ```jsx
    import React from "react";

    const Cat = (props) => {

        return (
            <div>고양이 화면이에요.</div>
        )
    }

    export default Cat;
    ```

- Dog.js

    ```jsx
    import React from "react";

    const Dog = (props) => {

        return (
            <div>강아지 화면이에요.</div>
        )
    }

    export default Dog;
    ```

- (3) App.js에서 Route 적용하기

  • Route 사용방법 1: 넘겨줄 props가 없을 때

    <Route path="주소[/home 처럼 /와 주소를 적어요]" component={[보여줄 컴포넌트]}/>
  • Route 사용방법 2: 넘겨줄 props가 있을 때(우리 버킷리스트 앱은 App.js에서 list를 props로 넘겨주죠! 그럴 땐 이렇게 쓰면 됩니다!)

<Route path="주소[/home 처럼 /와 주소를 적어요]" render={(props) => (<BucketList list={this.state.list} />)} />
  • App.js에 적용해보자
import React from 'react';
import logo from './logo.svg';
import './App.css';
// Route를 먼저 불러와줍니다.
import { Route } from "react-router-dom";

// 세부 페이지가 되어줄 컴포넌트들도 불러와주고요!
import Home from "./Home";
import Cat from "./Cat";
import Dog from "./Dog";

class App extends React.Component {

  constructor(props){
    super(props);
    this.state={};
  }
  
  render(){
    return (
      <div className="App">
        {/* 실제로 연결해볼까요! */}
        <Route path="/" component={Home} />
        <Route path="/cat" component={Cat} />
        <Route path="/dog" component={Dog} />
      </div>
    );
  }
}

export default App;
  • 주소창에 "/", "/cat", "/dog"을 입력해보자!

주소에 /를 입력했을 때는 Home컴포넌트만 뜨는데 왜 /cat에서는 Home과 Cat이 다 뜨는걸까요?

(4) exact 적용하기

왜냐하면 "/" 이 기호가 "/cat"과 "/dog"에도 포함되어 있어서 그렇습니다!
아래처럼 exact를 추가하고 다시 주소를 이동해 봐요.

왜냐하면 "/" 이 기호가 "/cat"과 "/dog"에도 포함되어 있어서 그렇습니다!
아래처럼 exact를 추가하고 다시 주소를 이동해 봐요.

import React from 'react';
import logo from './logo.svg';
import './App.css';
// Route를 먼저 불러와줍니다.
import { Route } from "react-router-dom";

// 세부 페이지가 되어줄 컴포넌트들도 불러와주고요!
import Home from "./Home";
import Cat from "./Cat";
import Dog from "./Dog";

class App extends React.Component {

  constructor(props){
    super(props);
    this.state={};
  }
  
  render(){
    return (
      <div className="App">
       
        {/* 실제로 연결해볼까요! */}
        <Route path="/" exact component={Home} />
        <Route path="/cat" component={Cat} />
        <Route path="/dog" component={Dog} />
      </div>
    );
  }
}

export default App;

(5) URL 파라미터사용하기

  • 웹사이트 주소에는 파라미터와 쿼리라는 게 있어요. 우리는 그 중 파라미터 사용법을 알아볼 거예요!

  • 이렇게 생겼어요!
    → 파라미터: /cat/nabi
    → 쿼리: /cat?name=nabi

  • 파라미터 주는 방법

//App.js
...
// 파라미터 주기
<Route path="/cat/:cat_name" component={Cat}/>

...
  • 파라미터 사용 방법
//Cat.js
import React from "react";

const Cat = (props) => {

    console.log(props.match);

    return (
        <div>고양이 화면이에요.</div>
    )
}

export default Cat;

useParams 훅을 사용해서 이동하기

꼭 props에서 받아오지 않아도, useParams 훅을 사용하면 간단히 파라미터에 접근할 수 있어요! 엄청 편하겠죠. 😍

import React from "react";
import { useParams } from "react-router-dom";
const Cat = (props) => {
    const cat_name = useParams();
    console.log(cat_name);
    // console.log(props);
  return (
      <div>고양이 화면입니다!</div>
  );
};

export default Cat;

(6) 링크 이동 시키기

매번 주소창을 찍고 페이지를 돌아다닐 순 없겠죠! react-router-dom으로 페이지를 이동하는 방법을 알아봅시다!

  • 사용 방법

링크 컴포넌트는 html 중 a 태그와 비슷한 역할을 해요. 리액트 내에서 페이지 전환을 도와줍니다.

<Link to="주소">[텍스트]</Link>
  • App.js에 메뉴를 넣어보자!

→ 우리가 만든 메뉴처럼 바깥에 있는 돔요소는 페이지가 전환되어도 그대로 유지됩니다. (편리하죠!)

-2) history 사용하기

Link 컴포넌트를 클릭하지 않고 페이지를 전환하는 방법 두 가지를 알아봅시다!

props로 history 객체를 받아 이동하기

import React from "react";

const Dog = (props) => {
// props의 history 객체를 살펴봅시다.
console.log(props);

// 그리고 history.push('/home')으로 페이지 이동도 해봐요!

return (
  <div
    onClick={() => {
      props.history.push("/home");
    }}
  >
    강아지 화면이에요.
  </div>
);
};

export default Dog;

useHistory 훅을 사용해서 이동하기
꼭 props에서 받아오지 않아도, useHistory 훅을 사용하면 간단히 history 객체에 접근할 수 있어요! 페이지 이동할 때 써먹으면 엄청 편하겠죠. 😍

import React from "react";
import { useHistory } from "react-router-dom";

const Home = (props) => {
let history = useHistory();
return (
  <>
    <div>메인 화면이에요.</div>

    <button
      onClick={() => {
        history.push("/cat");
      }}
    >
      cat으로 가기
    </button>
  </>
);
};

export default Home;

잘못된 주소 처리하기

exact로 중복 주소를 처리하는 방법은 이미 배웠습니다!
이번엔 우리가 미리 정하지 않은 주소로 들어온 경우를 다뤄볼게요.
(저는 버킷리스트 프로젝트에서 진행합니다!)

  • 일단 NotFound.js 파일을 만들고 빈 컴포넌트를 만들어주세요.
import React from "react";

const NotFound = (props) => {
  return <h1>주소가 올바르지 않아요!</h1>;
};

export default NotFound;
  • App.js에서 불러옵니다.

    import NotFound from "./NotFound";
import NotFound from "./NotFound";
  • Switch를 추가해주고,

    ...
    import { Route, Switch } from "react-router-dom";
    ...
    
        return (
          <div className="App">
            ...
              <Switch>
                <Route
                  path="/"
                  exact
                  render={(props) => (
                    <BucketList
                      list={this.state.list}
                      history={this.props.history}
                    />
                  )}
                />
                <Route path="/detail" component={Detail} />
              </Switch>
            ...
          </div>
        );
      
    ...
    • NotFound컴포넌트를 Route에 주소 없이 연결하면 끝!

      ...
      					<Switch>
                  <Route
                    path="/"
                    exact
                    render={(props) => (
                      <BucketList
                        list={this.state.list}
                        history={this.props.history}
                      />
                    )}
                  />
                  <Route path="/detail" component={Detail} />
                  <Route component={NotFound} />
                </Switch>
      ...

<NotFound/>에 뒤로가기 버튼을 달아보자!

  • NotFound.js에서 useHistory를 가져오는 게 먼저!

    import { useHistory } from "react-router-dom";
  • 버튼을 만들어주고,

import React from "react";
import { useHistory } from "react-router-dom";

const NotFound = (props) => {
  return (
    <div>
      <h1>주소가 올바르지 않아요!</h1>
      <button>뒤로가기</button>
    </div>
  );
};

export default NotFound;
  • useHistory를 사용해서 뒤로가기를 만들어요!

    import React from "react";
    import { useHistory } from "react-router-dom";
    
    const NotFound = (props) => {
      let history = useHistory();
      return (
        <div>
          <h1>주소가 올바르지 않아요!</h1>
          <button
            onClick={() => {
              history.goBack();
            }}
          >
            뒤로가기
          </button>
        </div>
      );
    };
    
    export default NotFound;

이후 리덕스를 조금 더 공부하고 끝냈다. 리덕스는 내일 이어서 적겠다.

개인적으로 라우터까지는 이해도 되고 공부하는대 문제가없었던거같다. 물론 반복해서 직접 쓸수있는 능력을 치워야 겠지만...
근데 리덕스 같은 경우는 흐름은 이해하겠는데 작성하는방법을 따라가기가 너무 힘들었다.
계속 돌려봐야겠다...
그나저나 과제를 하다보면 시간이 없을꺼 같은데 그게 걱정이다.

profile
i'm groot

0개의 댓글