같이 성장하는 프로그래밍

1-2 리액트 라우터 API 문서 (useHistory의 state) 본문

react router dom

1-2 리액트 라우터 API 문서 (useHistory의 state)

프설남 2020. 10. 12. 06:49
반응형

history

The term “history” and "history object" in this documentation refers to the history package, which is one of only 2 major dependencies of React Router (besides React itself), and which provides several different implementations for managing session history in JavaScript in various environments.

The following terms are also used:

  • “browser history” - A DOM-specific implementation, useful in web browsers that support the HTML5 history API
  • “hash history” - A DOM-specific implementation for legacy web browsers
  • “memory history” - An in-memory history implementation, useful in testing and non-DOM environments like React Native

 

history

이 문서에서 "history" 와 "history 객체"라는 용어는 history 패키지를 가르킵니다.

history 패키지는 (리액트 자체 외에) 리액트 라우터의 오직 2개뿐인 중요한 의존성들 중 하나입니다.

그리고 history 패키지는 다양한 환경에 있는 JavaScript 안에 있는 session history를 관리하기 위한 몇 가지 다른 구현들을 제공합니다.

다음 따라오는 용어들이 사용됩니다 :

  • “browser history” - HTML5 history API를 지원하는 웹 브라우저에서 유용한 특정한 DOM 구현
  • “hash history” - 기존 웹 브라우저들을 위한 특정한 DOM 구현
  • “memory history” - 테스트를 하거나 React Native 같은 DOM 환경이 없는 곳에 유용한 in-memory history 구현

 

 

--설명을 보시려면 더 보기를 눌러주세요--(스크롤 길이 주의)

더보기

--history package (history 패키지)란?

If you wish to understand React Router, you must first study history. More specifically, the history package, which provides the core functionality for React Router. It enables projects to easily add location based navigation on the client-side, which is essential for single page applications.

 

출처 : medium.com/@pshrmn/a-little-bit-of-history-f245306f48dd

 

너가 리액트 라우터를 이해하길 원한다면 너는 history를 첫번째로 공부해야만 합니다.
좀 더 자세하게는, 리액트 라우터를 위한 핵심기능을 제공하는 history 패키지.
그것은 싱글 페이지 어플리케이션(SPA)에 필수인 클라이언트 측에서의 화면이동을 기반한 location을 쉽게 추가할 수 있게 해줍니다.

 

=> 즉, 리액트 라우터를 공부하기 위해서는 첫번째로 알아둬야만 하는 것이 history 패키지입니다.
     공식문서에 적혀있는 hisotry, history 객체는 그냥 history 패키지를 뜻합니다.
     그리고 history 패키지는 리액트 라우터가 실행되는 데에 핵심기능을 제공한다는 뜻입니다.
     클라이언트 측( 즉, 홈페이지에 접속해서 사용하는 컴퓨터 측 )에서 화면이동을 하는데 필요한 게 location이고       이 location을 추가하는 데 쉽게 추가할 수 있게 도와준다는 말입니다.

=>> 더더 줄여서 설명을 하자면 history 패키지는 화면이동을 하는 데 도와주며 brower history, hash history, memory history 이 3가지를 합쳐서 부르는 말입니다.

 

--dependency (의존성)이란? 

 

dependency는 아래의 그림부터 보여드리고 설명을 하겠습니다.
(그림 1) 리액트 폴더 속 package.json
(그림 2) package.json 속 dependencies

(그림 1)의 위치에 (그림 2)와 같이 "dependencies"(dependence의 복수형)가 있습니다. 제가 개발하고 있는 프로젝트의 dependencies들입니다. 여기서 예를 들어서 설명하겠습니다.

react의 가장 큰 특징은 JSX라고 생각합니다.

 

1
2
3
4
5
import React from 'react';
 
function Example () {
    return <div>example</div>;
}
cs

1번 째 행을 보시면 'react' 로 부터 import(수입)을 해왔으니까 JSX식인 return <div>example</div>; 가 에러가 나지 않습니다.

(그림 3) 에러가 난 코드

첫번째 행을 삭제해봅니다. 그럼 이제 JSX식을 해석해 줄 'react'가 없어졌으니 (그림 3)과 같이 당연히 순수자바스크립트에서는 이 코드를 해석하지 못하고 에러가 발생하게 됩니다.

 

즉, 어떻게 개발을 해나가겠다는 생각의 순서가 이상할 수 있지만 나는 JSX식을 사용해서 개발을 할 거고 그러면 JSX식을 해석해줄 react가 필요해! 그러니 이 프로젝트는 react에 의존하게 되는 거죠 react가 없으면 JSX식을 쓸 수가 없기 때문입니다. 그리고 이 프로젝트를 배포를 할 때 이 프로젝트는 반드시 react가 있어야 해석이 가능하다는 걸 알리기 위해 (그림 2)의 dependencies에 입력해줍니다.

 

즉, (그림 2)에서 보이는 material-ui/core 부터해서 'react-scripts' 까지 제가 개발하고 있는 프로젝트에 무조건 필요한 녀석들인 겁니다. 이 녀석들에게 의존(dependence)하고 있습니다.

 

그러니 다시 원점으로 돌아와서 history 패키지는 리액트 라우터의 오직 2개뿐인 중요한 의존성들 중 하나이다.의 해석은 리액트 라우터를 실행시키기 위해서는 반드시 있어야하는 2개의 dependencies가 있는데 그 중에 하나가 history 패키지라는 겁니다.

 

 

--session history 란? 

 

The sequence of Documents in a browsing context is its session history.

 

출처 : www.w3.org/TR/2010/WD-html5-20101019/history.html 

 

해석해보면 browsing context(브라우징 맥락)안에서의 Documents 의 순서가 session history 이다.

(그림 4) 이미지 그대로 제목

 하나씩 뜯어서 찾아봅시다. ㅠㅠ

1. browsing context

A browsing context is the environment a browser displays a Document. In modern browsers, it usually is a tab, but can be a window or even only parts of a page, like a frame or an iframe.

출처 : developer.mozilla.org/en-US/docs/Glossary/Browsing_context

브라우징 맥락은 브라우저가 Document 객체를 보여주는 환경이다. 현대 브라우저들에서는, 그것은 보통 탭이다. 그러나 윈도우가 될 수 있거나 frame 또는 iframe 과 같은 페이지의 부분들 조차도 될 수 있다.

(그림 5) 예전의 Internet Explorer의 모습

 

즉, 예전에는 이렇게 윈도우 브라우저가 큰 하나의 화면 입니다.

 

(그림 6) 지금 작업 중인 저의 화면

 

지금은 (그림 6)의 빨간색 선 표시와 같이 이라는 이름으로 하나의 브라우저가 여러개로 동시에 묶여있을 수 있습니다.
(그림 7) iframe 예시 출처 : http://tcpschool.com/examples/tryit/tryhtml.php?filename=html_space_iframes_01

그리고 (그림 7)의 빨간색 선과 같이 브라우저 창안 속의 또 하나의 브라우저 창이 iframe요소로 만들어진 창입니다.  

윈도우 브라우저, 탭, iframe 공통점이 무엇입니까? 네 모두 다 하나의 브라우저 창입니다.

 

즉, 다시 돌아가서
브라우징 맥락은 브라우저가 Document 객체를 보여주는 환경이다. 현대 브라우저들에서는, 그것은 보통 탭이다. 그러나 윈도우가 될 수 있거나 frame 또는 iframe 과 같은 페이지의 부분들 조차도 될 수 있다.


이 말은 그냥 browsing context(브라우징 맥락)은 그냥 간단히 브라우저 창이라고 생각하시면 됩니다.

휴.. 이 한 문장 때문에 몇 십분을 찾아서 해석을 하고 다녔는지... ㅋㅋ

 

2. Documents 

 

The Document interface represents any web page loaded in the browser and serves as an entry point into the web page's content, which is the DOM tree. The DOM tree includes elements such as <body> and <table>, among many others. It provides functionality globally to the document, like how to obtain the page's URL and create new elements in the document.

 

Document 인터페이스는 브라우저에 실행된 모든 웹 페이지를 나타냅니다. 그리고 웹 페이지의 내용인 DOM tree(돔 트리)로 들어가는 진입점 역할을 제공합니다. DOM tree(돔 트리)는 그 중에서도 <body> 태그 그리고 <table> 태그 같은 요소들을 포함합니다. DOM tree(돔 트리)는 어떻게 페이지의 URL을 얻고 어떻게 document에 새로운 요소들을 생성하는 지 것과 같이 document에 전체적으로 기능을 제공합니다.

 

즉, Document 인터페이스는 그냥 웹 페이지 그 자체입니다. 끝.

 

그리고 여기서 이상한 거를 느끼셨을 지는 모르겠지만 맨앞에 Document는 대문자 D 인데 중간 중간의 document는 모두 소문자 입니다. 보통 Document는 인터페이스이고 document는 객체랍니다.

 

그럼 인터페이스랑 객체는 또 뭐냐? ㅠㅠ 예를 들어서 설명드리겠습니다.

의자라는 사물로 설명하겠습니다.

우리가 의자라고 생각하면 어떤 물건을 의자라고 합니까?
붙여서 앉을 수 있는 판이 있어야 겠죠?
그리고 그 판을 어느정도의 높이에 있게 해주는 다리가 필요합니다.
이렇듯 의자라고 불릴 수 있는 규격(?)을 인터페이스라고 합니다. 추상적인 겁니다.


그럼 객체는 뭐냐 의자라고 불릴 수 있는 이 추상적인 규격에 맞춰서 실제로 만든 의자가 객체입니다.
그 객체는 등받이가 있을 수도 없을 수도 있고, 앉을 수 있는 판이 네모이거나 원이거나 세모일 수도 있고, 판을 받드는 다리가 길 수도 있고 짧을 수도 있고 엄청 다양하게 만들 수 있습니다.

 

즉, Document 인터페이스는 웹 페이지라고 불릴 수 있는 꼭 있어야 하는 규격을 뜻하는 거고, document 객체는 그 규격에 맞추어서 구글이라든지 네이버, 다음 등 여러가지 웹 페이지들을 뜻하는 겁니다.


다시 원점으로 돌아가 보겠습니다.

 

The sequence of Documents in a browsing context is its session history.

 

출처 : www.w3.org/TR/2010/WD-html5-20101019/history.html 

 

해석해보면 browsing context(브라우징 맥락)안에서의 Documents 의 순서가 session history 이다.

 

위의 해석대로 다시 해석하면 브라우저 창에서 웹 페이지의 연속이 session history 이다!
와... 저도 처음에 이게 무슨 말인지 정말 몰랐는데 쓰면서 보니 이렇게 이해가 가네요 ㅠㅠ

 

즉, 지금 보고 계시는 블로그의 웹 페이지, 또 다른 탭들의 웹페이지의 연속들이 session history인 겁니다.
단순히, Crtl + H 누르시면 자신의 웹 페이지 방문 기록이 뜹니다. 방문한 웹 페이지 하나하나들의 정보(?)를 모으는 곳이 session history 라고 이해하면 될 것 같습니다. (확실하지 않습니다 ㅠㅠ)

 

 

--implementations (구현)이란? 

 

구현이라는 것은 어떠한 것을 만드는 것의 모든 과정을 뜻합니다.

즉, IT의 한 프로젝트를 구현한다고 하면 의뢰자와의 회의부터 시작해서 설계, 공정, 테스트, 배포, 판매, 유지보수까지로서 프로젝트 처음부터 끝까지 싹 다 포함해서 구현이라고 표현합니다.

 

 

'그리고 history 패키지는 다양한 환경에 있는 JavaScript 안에 있는 session history를 관리하기 위한 몇 가지 다른 구현들을 제공합니다.' 

 

이 문장은 브라우저의 웹 페이지들을 관리하기 위한 모든 것들을 제공한다는 뜻이 됩니다. 웹 페이지들을 관리하기 위한 처음부터 끝까지 모든 것들을 제공한다는 뜻이 되겠습니다. 강력한 녀석이군요...

 

 

--in-memory 란? 

 

음 in-memory history 라는 건, history 패키지는 3개로 구성되어 있습니다. brower history, hash history, memory history 이렇게 있습니다. 다른 두 brower history랑 hash history 는 DOM에서 구현을 하는데 memory history는 in-memory history 즉, 메모리를 사용해서 구현을 하는 것 같습니다.

 

여기까지 쓰는 데 찾으면서 이해하고 쓰는 데 4시간 반이 걸렸습니다 ㄷㄷ;; 바로 다음으로 가겠습니다.

 


 

history objects typically have the following properties and methods:

  • length - (number) The number of entries in the history stack
  • action - (string) The current action (PUSH, REPLACE, or POP)
  • location - (object) The current location. May have the following properties:
    • pathname - (string) The path of the URL
    • search - (string) The URL query string
    • hash - (string) The URL hash fragment
    • state - (object) location-specific state that was provided to e.g. push(path, state) when this location was pushed onto the stack. Only available in browser and memory history.
  • push(path, [state]) - (function) Pushes a new entry onto the history stack
  • replace(path, [state]) - (function) Replaces the current entry on the history stack
  • go(n) - (function) Moves the pointer in the history stack by n entries
  • goBack() - (function) Equivalent to go(-1)
  • goForward() - (function) Equivalent to go(1)
  • block(prompt) - (function) Prevents navigation (see the history docs)

 

 

history 객체는 일반적으로 다음과 같은 속성들과 메소드들을 가집니다 :

  • length - (number) history stack 안에 있는 항목들의 수
  • action - (string) 현재 행동 (PUSH, REPLACE, or POP)
  • location - (object) 현재 위치. 아마도 다음 속성들을 가집니다. :
    • pathname - (string) URL의 경로
    • search - (string) URL의 쿼리 문자열
    • hash - (string) URL의 해시 조각
    • state - (object) 이 location가 스택으로 push 되어졌을 때 제공되는 특정한 location의 state
                         예를 들어 push(path, state)가 사용되었을 때
  • push(path, [state]) - (function) history stack으로 새로운 항목을 넣습니다.
  • replace(path, [state]) - (function) history stack에 현재 항목을 교체합니다.
  • go(n) - (function) history stack안에서 n번째 항목으로 포인터를 움직입니다.
  • goBack() - (function) go(-1)와 동일합니다.
  • goForward() - (function) go(1)와 동일합니다.
  • block(prompt) - (function) 화면이동을 막습니다. (hisotry 문서를 보시면 됩니다.)

 

--설명을 보시려면 더 보기를 눌러주세요--(스크롤 길이 주의)

더보기

--history stack이란? 

 

history stack이란 건 stack부터 설명하겠습니다. 자료구조의 하나로서 한 쪽으로만 넣거나 뺄 수 있는 구조입니다.
요즘 가짜사나이를 즐겨 보기 때문에 탄창을 예시로 들어보겠습니다.

 

(그림 8) stack 설명을 위한 탄창 그림

 

탄창에 1번부터 10번까지 숫자가 적혀있는 탄약을 1번부터 차례대로 집어 넣습니다.

다 집어넣은 다음에 다시 빼냅니다. 여기서 빼내면 몇 번이 적힌 탄약부터 빠져나옵니까?

네. 10번 9번 8번 7번 ・・・ 1번 이렇게 넣은 순서의 역순서로 빠져나오게 됩니다.

이러한 구조를 가지고 있는 것이 stack입니다.

 

그럼 history stack이라는 건 history 객체에 어떠한 정보들을 가지는 stack 구조라는 뜻입니다.

 

 

--Length

 

Length는 history stack안에 있는 항목들의 수입니다. 즉 (그림 8)과 같이 10개의 탄약을 넣으면 이 항목의 수는 10개되는 것입니다.

 

--action  

 

현재 history 객체가 행동하고 있는 상태입니다.

하나씩 봅시다. 1. PUSH : history.push()를 실행했을 때입니다.

                    2. REPLACE : history.replace()를 실행했을 때입니다.

                    3. POP : history.push()와 history.replace()를 제외한 모든 메소드들을 실행했을 때입니다.

                               ( 초기화도 포함 )

 

--location

 

history 객체가 가지고 있는 state로서 이 location 안에 다음과 같은 속성들을 가집니다.

1. pathname : URL 경로입니다. 지금 현재 블로그 페이지 "explain-programming.tistory.com/manage/newpost
                   /?type=post&returnURL=%2Fmanage%2Fposts" 이거 그 자체입니다.

2. search : URL의 쿼리 문자열이라고 되어있습니다. 쿼리 문자열라는 것은 간단히 정보를 요청하는 문자열입니다.
              URL속에 있는 정보를 요청하여 얻는 것입니다.
              "explain-programming.tistory.com/manage/newpost/?type=post&returnURL=%2Fmanage%2Fposts"

              현재 URL에서 search에 해당하는 부분은 
              "?type=post&returnURL=%2Fmanage%2Fposts" 여기입니다.

              네. ?부터 시작하는 부분입니다. 여기서 무슨 정보를 요청해서 받느냐 자세히 보시면 type=post&
              returnURL=%2Fmanage%2Fposts가 보이실 겁니다.
              이 URL 주소는 지금 웹 페이지에 type 변수에 post라는 값 &(와/과 라는 뜻과 일맥상통합니다.)
              returnURL변수에 %2Fmanage%2Fposts라는 값을 보내고 있고 그걸 search가 받습니다.

 

(그림 9) search 설명을 위한 URL

 

(그림 10) '그림 9'에 해당하는 history 객체 속 location의 search 값

 (그림 9)와 (그림 10)을 보시는 바와 같이 history 객체는 URL에서 ?를 식별자로써 ?뒤의 모든 문자열을 쿼리 문자열로 판단하여 search에 할당합니다.

3. hash : URL의 해시 조각입니다. search와 다를 바가 없습니다. 다만 식별자가 ?가 아니라 # 해시태그가 됩니다.

 

 

(그림 11) hash 설명을 위한 URL

 

(그림 12) '그림 11'에 해당하는 history 객체 속 location의 hash 값

 

(그림 10)과 (그림 11)을 보시는 바와 같이 history 객체는 URL에서 #를 식별자로써 #뒤의 모든 문자열을 해시 조각으로 판단하여 hash에 할당합니다.

 

4. state : state는 메소드들을 설명한 후에 설명드리겠습니다. ㅎㅎ

 

--push(path, [state])

 

history stack으로 새로운 항목을 넣습니다. 이전글 1-1 리액트 라우터 API 문서 (Hooks, useHistory)에서도 잠깐 나왔습니다. 조금 더 자세히 설명을 해보겠습니다. ㅎㅎ

 

 </p

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory,
from "react-router-dom";
 
 
export default function BasicExample() {
  
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.push('/about');
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>go about</button>
    </div>
  );
}
 
function About() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}
 
function Dashboard() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}
 
cs

(코드 1) history.push()를 설명하기 위한 코드

 

 

(그림 13) '코드 1'에 해당하는 출력된 화면

 

 

'https://bnpsd.csb.app/'이라는 URL로 접속을 하면 처음에는 초기화가 진행됩니다.

그래서 history stack에 history 객체가 action이 POP인 상태로 1개가 쌓이게 됩니다.

그리고 여기 go about 버튼을 누른다면 52번째 줄의 history.push('/about') 코드로 인하여

URL은 'https://bnpsd.csb.app/about'으로 입력이 될 것이고 history stack에는 history 객체가 action이 PUSH인 상태로 또 1개가 쌓여서 총 2개가 쌓여있게 됩니다.

아래 결과 그림을 보여드리겠습니다.

 

(그림 14) '그림 13' 화면에서 go about 버튼을 누른 결과 그림

 

 

(그림 14)를 보시면 length가 1에서 2로 action이 POP에서 PUSH로 바뀐 것을 보실겁니다.

즉, history.push(path, [state]) 는 path의 위치로 이동하고 그에 해당하는 history 객체를 history stack에 저장시킵니다.

그리고 [state] 의 [] 대괄호는 생략해서 함수를 호출해도 된다는 의미입니다. (코드 1)의 52번째 줄 history.push('/about')을 보시면 history.push('/about', state에 관한 값)의 형식이 아닙니다.

즉, 상황에 따라 넣어도 되고 안 넣어도 되는 옵션입니다. 대신 반대로 []대괄호가 없으면 필수로 넣어야하는 인수(parameter)가 되는겁니다. 여기선 path가 필수로 넣어야하는 인수가 되는 겁니다.

 

--replace(path, [state]) 

 

history stack에 현재 항목을 교체합니다. 이번엔 교체입니다.

바로 예시 보시겠습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory,
from "react-router-dom";
 
 
export default function BasicExample() {
  
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.replace('/about');
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>go about</button>
    </div>
  );
}
 
function About() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}
 
function Dashboard() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}
 
cs

(코드 2) history.replace()를 설명하기 위한 코드

 

 

(코드 2)에서 바뀐 코드는 (코드 1)의 52번째 줄 history.push('/about')이 history.replace('/about')으로 바뀐 것 뿐입니다.

마찬가지로 'https://bnpsd.csb.app/'이라는 URL로 접속을 하고 처음에는 초기화가 진행됩니다.

그래서 history stack에 history 객체가 action이 POP인 상태로 1개가 쌓이게 됩니다.

그리고 여기 go about 버튼을 누른다면 어떻게 되겠습니까?

바로 결과 화면 보시겠습니다.

 

(그림 15) '코드 2'의 화면과 실험 결과 그림

 

(그림 15)를 보시면 (그림 14)와는 많이 다릅니다. 일단 length가 1에서 그대로 1입니다.

그리고 action도 POP에서 REPLACE입니다.

action이 REPLACE가 되는 건 action에 대한 설명을 했는 거 그대로 입니다.

그럼 length는 어떻게 1이 되었는가 입니다.

처음에 초기화가 시작될 때는 (그림 14)와 똑같습니다. 초기화된 history 객체가 history stack에 들어가게 된 것입니다.

그럼 go about 버튼을 누르고 나서는 어떤 현상이 일어났는가에 대해서는 history stack구조는 (그림 8)의 탄창과 같은 것이기 때문에 초기화한 history 객체가 먼저 들어가 있고, 그 다음 REPLACE history 객체를 넣은 다음 초기화한 history 객체를 빼내는 건 있을 수 없는 일입니다.

즉, history stack에 현재 항목을 교체합니다. 이므로 먼저 초기화한 history 객체를 빼냅니다 그럼 length가 0인 history stack이 되겠습니다. 그리고 이제 REPLACE history 객체를 집어넣어서 교체를 합니다.

즉, history.replace(path, [state]) 는 path의 위치로 이동하고 그에 해당하는 history 객체를 history stack에 맨 위에 있는 history 객체와 교체시킵니다.

state 설명은 나중에 하겠습니다.

 

--go(n)

history stack안에서 n번째 항목으로 포인터를 움직입니다.

숫자 n 만큼 설정하신 만큼 화면을 이동하는 겁니다.

예를 들어 history.go(1)을 실행시키면 앞으로 가기 1번을 실행한 것과 같습니다.

history.go(-3)이면 뒤로가기 3번과 같은 결과를 보여줍니다.

여기서도 조금 더 자세한 설명을 해보겠습니다. history stack이 어떻게 되는지 말입니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory,
from "react-router-dom";
 
 
export default function BasicExample() {
  
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.go(-2);
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>go(-2)</button>
    </div>
  );
}
 
function About() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}
 
function Dashboard() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}
cs

(코드 3) history.go()를 설명하기 위한 코드

 

history.go()의 실험을 할 코드입니다. 바뀐 것은 역시 마찬가지로 52번째 줄 history.replace('/about') 에서 history.go(-2) 로 바뀌였고 58번째 줄 <button>태그의 텍스트가 go about에서 go(-2)로 바뀌었습니다.

 

(그림 16) '코드 3'에 해당하는 출력 화면

 

여기서 실험은 Home 화면을 초기화시킵니다. => About으로 갑니다 => Dashboard로 갑니다 =>

마지막으로 Home으로 옵니다. 그리고 go(-2) 버튼을 누릅니다. 각각 history stack이 어떻게 쌓여가는 지 보여드리겠습니다.

 

 

(그림 17) '코드 3'에 대한 실험 결과

length : 1 action : POP 초기화된 Home 화면
length : 2 action : PUSH 초기화된 About 화면
length : 3 action : PUSH 초기화된 Dashboard 화면
length : 4 action : PUSH 초기화된 Home 화면
length : 4 action : POP 초기화된 About 화면

이게 결과입니다.

실험은 Home 화면을 초기화시킵니다. => About으로 갑니다 => Dashboard로 갑니다 =>

Home으로 옵니다. => go(-2) 버튼을 누릅니다. 이였습니다.

 

여기서 알 수 있는 점은 크게 2가지입니다.

 

1. <Link to="/about"></Link>의 링크태그로 화면 이동을 한 것은 history.push('/about')과 같은 기능을 한다 입니다.

 

2. history.go(-2)를 하면 history stack의 수가 줄지 않고 그냥 포인터만 이동을 한다. 즉, 여기 실험에서는 4번째 스택까지 쌓여있었는데 거기서 -2만큼 했으니 2번째 스택인 About 화면이 보이게 되는 것입니다.

 

그럼 갑자기 여기서 궁금해졌습니다.

 

go(-2)로 스택 2번째인 history 객체를 가리키게 되었습니다. 여기서 replace를 하면 어떻게 될 것 같습니까? 저도 모릅니다. 한 번 해보겠습니다. ㅎㅎ

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory,
from "react-router-dom";
 
 
export default function BasicExample() {
  
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.go(-2);
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>go(-2)</button>
    </div>
  );
}
 
function About() {
  let history = useHistory();
  console.log(history);
  
   const handleClick = () => {
    history.replace('/dashboard');
  }
  
  return (
    <div>
      <h2>About</h2>
      <button onClick={handleClick}>go dashboard</button>
    </div>
  );
}
 
function Dashboard() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}
 
cs

(코드 4) 궁금한 게 생겨서 만들어 본 코드

 

지금 오후 10시부터 글 쓰기 시작해서 새벽 5시인데 코드 설명은 간단히 하겠습니다 ㅠㅠ

About 컴포넌트에 68번째 줄 history.replace('/dashboard') 가 되도록 버튼을 추가하였습니다.

마찬가지로 실험 순서는 Home 화면을 초기화시킵니다. => About으로 갑니다. => Dashboard 로 갑니다. => Home으로 갑니다. => go(-2)버튼을 눌러 스택 2번째인 About 화면이 나타납니다 => 여기서 Dashboard로 가는replace버튼을 누릅니다. => 뒤로가기를 누릅니다.

 

(그림 18) '코드 4'에 관한 실험에 대한 결과

length : 1 action : POP 초기화된 Home 화면
length : 2 action : PUSH 초기화된 About 화면
length : 3 action : PUSH 초기화된 Dashboard 화면
length : 4 action : PUSH 초기화된 Home 화면
length : 4 action : POP 초기화된 About 화면
length : 4 action : REPLACE 초기화된 Dashboard 화면
length : 4 action : POP 초기화된 Home 화면

 

와.. 대박입니다. replace로 Dashboard 화면으로 가서 뒤로가기를 눌렀더니 Home화면이 나오고 더이상 뒤로가기는 되지 않았습니다.

이 말은 즉슨 go()로 이동하여 history stack안에서 가리키고 있는 그 history 객체가 replace로 인해 교체가 된다는 말이 됩니다.

그러면 (그림 8)에서 탄창얘기는 성립이 안되게 됩니다. ㅠㅠ 좀 특수한 탄창이라고 덧붙이면 되겠습니다.
각 탄약층마다 새로운 탄약을 옆에 밀어넣어서 빼낼 수 있는 공간이 있는 탄창이다 라고 말입니다 ㅠㅠ
정리하자면 기본적으로 stack 구조이지만 특수한 경우에 배열처럼 해당 스택을 빼고 교체할 수 있다는 거로 이해가 됩니다.

 

저는 탄약이 만약에 [ 1 , 2 , 3 , 4 ] 로 있다면 go(-2)를 하면 [ 3 , 4 , 1 , 2 ] 로되고 replace를 하면 [ 3 , 4 , 1 , 5 ]  처럼 history stack안의 history 객체가 순서를 움직여서 2번째 스택이 제일 위가 되니까 replace되는 줄 예상을 했습니다. 근데 저기서 더이상 뒤로가기가 안되는 걸 보고는 [ 1 , 5 , 3 , 4 ] 이렇게 순전히 포인터가 그 스택자리까지 가서 그 스택자리를 교체한다는 것을 깨달았습니다. 

 

 

--goBack()

go(-1)와 동일합니다. 즉, 뒤로가기 1번과 같습니다. 설명 한 번 더 하면 뒤질 것 같습니다 진짜 ㅠ

 

 

--goForward()

go(1)와 동일합니다. 즉, 앞으로가기 1번과 같습니다.

 

 

--block(prompt)

화면의 이동을 막습니다. 말 그대로 입니다. 화면의 이동을 막아서 아무것도 일어나지 않습니다.

prompt는 developer.mozilla.org/ko/docs/Web/API/Window/prompt MDN 자료를 보시면 이해가 되실 겁니다.

alert()랑 비슷한 놈입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory,
from "react-router-dom";
 
 
export default function BasicExample() {
  
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.block(prompt("블로그 글 쓰기 어렵니?","네ㅠㅠ"));
     console.log(1);
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>block</button>
    </div>
  );
}
 
 
cs

(코드 5) history.block()을 설명하기 위한 코드

 

history.block()은 어떻게 진행이 되는 지 알아보기 위해 (코드 5)와 같이 코드를 짜봤습니다.

 

(그림 19) '코드 5'에 대한 결과 그림

 

결과는 화면 이동같은 건 되지않고 prompt()를 실행 시킵니다. 그리고 여기서 확인이나 취소버튼을 누르게 되면

 

(그림 20) '그림 19'의 다음 화면

URL을 보시면 화면 이동은 되지않았고, 콘솔에는 1이 찍힙니다. 즉, 화면 이동만 안 될뿐이지 뒷 코드는 차질없이 실행이 됩니다.
가장 중요한 쓰임새로는 제가 개인적으로 생각해봤을 때, 화면 이동이 되는 코드인데 특정한 조건에 이 블럭으로 막아줘야할 때 쓰일 것 같습니다...만 솔직히 잘 모르겠습니다 ㅋㅋ

 

--location의 state

 

이 location가 스택으로 push 되어졌을 때 제공되는 특정한 location의 state 예를 들어서 push(path, state)가 사용되었을 때

 

라는 공식문서의 설명입니다. location의 state의 설명을 마지막으로 한 이유는 'location가 스택으로 PUSH되어졌을 때' 이 문장 때문입니다. 이제 이해가 가실 거라 믿습니다 history 객체의 location이 history stack으로 push 되어졌을 때라는 의미입니다. 여기서 push 되어졌을 때라는 말은 action이 PUSH를 가리키는 말이 아닙니다. 순전히 history stack에 새로운 history 객체가 들어왔을 때를 의미하는 것입니다. 그러므로 history 객체의 메소드들 중에서 entry(항목)을 넣는 메소드는 push와 replace 뿐이므로 둘 다 인수로 (path, [state])에서 [state]를 가지는 것입니다.

 

그럼 이것을 어떻게 사용을 하느냐 다음 코드를 보시겠습니다. history.push()로 예시를 보이겠습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useHistory
from "react-router-dom";
 
export default function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
        </ul>
 
        <hr />
 
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
 
function Home() {
  let history = useHistory();
  console.log(history);
  
  const handleClick = () => {
    history.push('/about',{key:"value",key2:"value2"})
  }
  
  return (
    <div>
      <h2>Home</h2>
      <button onClick={handleClick}>go about</button>
    </div>
  );
}
 
function About() {
  let history = useHistory();
  console.log(history);
  
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}
cs

 

(코드 6) location의 state를 설명하기 위한 코드

 

중요하게 보실 부분은 43번째 행의 history.push('/about',{key:"value",key2:"value2"}) 이 부분입니다.

history.push(path, [state])에서 state부분을 {key:"value",key2:"value2"} 이렇게 객체로 넣었습니다.

그리고 push를 하면 history에 어떤 변화가 생길까요?

 

(그림 21) '코드 6'에 대한 결과 화면

 

보시면 처음 초기화된 history 객체에는 location의 state가 undefined로 비어있습니다.
하지만 history.push('/about',{key:"value",key2:"value2"})로 state에
{key:"value",key2:"value2"}를 넣어보낸 결과 history 객체의 location의 state에 보낸 그대로의 객체가 표시됩니다.

즉, 어떠한 정보를 다음 화면으로 보낼 때 이 state를 이용해서 보내는 것 같습니다.

어? search랑 hash로도 정보를 받아올 수도 있지 않냐 왜 굳이 state로 보내느냐 라고 생각하실 수도 있겠습니다.

아마 get, post 전송 방식과 같다고 생각하시면 될 것 같습니다. search와 hash는 URL에 붙어있는 정보를 받기 때문에 노출되어 있습니다. 그럼 노출되어서는 안되는 정보들이 있을 겁니다. 그것들을 state로 보낼 때 사용할 것 같습니다.

또한 아마 search와 hash는 글자 제한이 있을 겁니다. 그 글자 제한보다 많은 데이터를 보낼 때도 state로 사용해서 보낼 것입니다.

 


history is mutable

The history object is mutable. Therefore it is recommended to access the location from the render props of <Route>, not from history.location. This ensures your assumptions about React are correct in lifecycle hooks. For example:

 

 

history는 변할 수 있다.

history 객체는 변할 수 있다. 그러므로 history.location으로 부터가 아닌 <Route>의 render 속성으로부터 location에 접근하는 것이 추천된다. 이것은 생명주기 hooks 에서 리액트에 대한 너의 가정들이 올바른 지 보장해준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class Comp extends React.Component {
  componentDidUpdate(prevProps) {
    // will be true
    const locationChanged =
      this.props.location !== prevProps.location;
 
    // INCORRECT, will *always* be false because history is mutable.
    const locationChanged =
      this.props.history.location !== prevProps.history.location;
  }
}
 
<Route component={Comp} />;
cs

 

(코드 7) hisotry는 변할 수 있다에 대한 공식문서의 예시 코드

 

코드 그대로 해석하자면 <Route>로 화면을 이동할 때 리액트 라우터는 항상 history, location, math 이렇게 3가지 객체를 전송합니다. 그리고 location 객체와 history.location 객체는 가지고 있는 state도 같고 모양이 같습니다. 

만약 화면이동하기 전의 location값과 지금 화면의 location 값을 비교해야할 경우가 생겼을 때는 history.location은 값이 변하기 때문에 항상 같지 않을 거랍니다.

그러므로 화면이동하기 전의 location값과 지금 화면의 location 값을 비교할 때는 history.location이 아니라 location 객체를 사용하셔야 합니다.

 


 

 

Additional properties may also be present depending on the implementation you’re using. Please refer to the history documentation for more details

 

 

 

부가적인 속성들은 당신이 사용하고 있는 구현에 따라 존재할 수 있다. 더 자세한 건 history 문서를 참조하시길 바랍니다.

 

 

 


 

9시간 만에 드디어 길고 긴 하나의 글이 완성이 되었습니다.

 

생각보다 엄청 길어졌습니다 ㅠㅠ

 

이렇게 긴 글 끝까지 읽어주신 분들, 필요한 부분만 읽어주신 분들, 그냥 방문해주신 분들 모두들 감사합니다

 

다음 글은 useLocation 에 관한 글을 적어보도록 하겠습니다.

 

다음 주말에 뵙겠습니다 ㅎㅎ

 

이해가 안되시거나 부족한 부분이 있으면 언제든지 댓글 남겨주시면 감사하겠습니다!

반응형
Comments