[React.JS] 강좌 9편 Router: URL 에 따라서 다른 결과물을 보여주자


liste

이 포스트는 Outdated 되었습니다. React-router v3 를 더 자세히 다루는 리액트 프로젝트에서의 라우터, React-router v3 사용하기 를 참고하세요.

이번 포스트는 React.js 의 Router 사용에 대한 강좌입니다. Router 는 사용자가 요청한 URL 의 서브디렉토리에 따라서 결과물을 렌더링해줍니다.  Apache nginx등의 웹서버처럼 각 페이지마다 다른 디렉토리 및 파일을 사용하는것과 달리,  라우터를 사용하게되면 처음부터 웹앱에서 사용 할 모든 컴포넌트들을 먼저 불러와두고, 페이지를 이동할 때 마다 그때 그때 페이지를 처음부터 로딩하지 않고 필요한 컴포넌트만 다시 렌더링 합니다. (즉, Header 같은 부분처럼 변동이 없는 부분들은 유지되어있다는 의미입니다)


0. 프로젝트 시작하기

1

▲ 우리가 앞으로 구현 할 프로젝트입니다.

 

강좌 2.1편을 참조하여 React.js 프로젝트를 생성하세요.

* 이번 강좌는 jsfiddle을 사용하지 말고 npm으로 직접 프로젝트를 생성하여 공부하세요.

 


1. React Router 설치하기

NPM 을 통하여 react-router 을 설치하세요: npm install --save react-router

그리고 index.js 의 상단에 다음 코드를 추가하세요:

import { Router, Route, browserHistory, IndexRoute } from 'react-router';

App.js의 상단에는 다음 코드를 추가하세요.

import { Link } from 'react-router'


2. Route 할 컴포넌트 만들기

이 섹션에서는 라우팅 할 컴포넌트3개를 만들어보겠습니다 :

  • Home
  • About
  • Articles

강의 편의를 위하여 모두 같은 파일 App.js 에 작성하도록하겠습니다.

class Home extends React.Component {
    render() {
        return (
            <h2>Hey, I am HOME!</h2>
        );
    }
}

class About extends React.Component {
    render() {
        return (
            <h2>Hey, I am ABOUT!</h2>
        );
    }
}

class Articles extends React.Component {
    render() {
        return (
            <h2>Hey, I am ARTCILES!</h2>
        );
    }
}

3. Router Container 컴포넌트 만들기

여기서 컨테이너는, App 컴포넌트입니다.

class App extends React.Component {
    render() {

        return (
                <div>
                    <ul>
                        <li><Link to="home">Home</Link></li>
                        <li><Link to="about">About</Link></li>
                        <li><Link to="articles">Articles</Link></li>
                    </ul>
                    {this.props.children}
                </div>
        );
    }
}

LINE 7 – 9: 여기서 <Link> 를 사용하였습니다. LinkRouter 에 내장되어있는 컴포넌트중 하나인데요, github에서 렌더링 되는 부분 코드를 살펴보면 다음과 같습니다: return <a {...props} onClick={this.handleClick} /> 결국 그냥 링크를 해주는 a 태그와 같지만 다른점은 클릭했을 때, <a href=".."> 이 태그는 페이지 자체를 리로딩해버리지만, <Link>this.props.children 부분만 리로딩해줍니다.

LINE 11: this.props.children 은 따로 설정하는 props 가 아니라, 모든 컴포넌트가 기본적으로 가지고있는 컴포넌트로서, 컴포넌트를 사용 할 때, <Component><...></Component> bold로 표시된 부분에 들어가는 부분이 this.props.children 으로 자동으로 설정됩니다.

4. ReactDOM 렌더링 및 Router 설정하기

index.js 의 최하단에 이 코드를 삽입하세요:

ReactDOM.render(<Router history = {browserHistory}>
      <Route path = "/" component = {App}>
         <IndexRoute component = {Home} />
         <Route path = "home" component = {Home} />
         <Route path = "about" component = {About} />
         <Route path = "articles" component = {Articles} />
      </Route>
   </Router>, document.getElementById('root'));

지금까지 ReactDOM 을 사용해 컴포넌트를 렌더링 할 때, 바로 <App…. /> 형태로 해왔지만 라우터를 사용할 땐 <Router …> 형태를 사용합니다. 여기선 App 컴포넌트가 Router의 props로 있는 셈이죠.

Route 는 클라이언트상에서 페이지를 라우팅 할 주소를 정의해줍니다.

browserHistoy 를 사용함으로서, 뒤로가기를 해도 페이지가 새로 리로딩되지 않고 필요한부분만 리렌더링하게끔 해줍니다.

IndexRoute 의 Index 가 의미하듯, 라우터의 첫 페이지를 정의해줍니다.

5. 서버 사이드 설정

지금까지 작성한 코드를 저장하고 브라우저상에서 방금 만든 페이지를 열어보세요.
Link 들을 클릭해보면 페이지를 라우팅하는데는 문제가없지만 http://localhost/home 와 같이 직접 주소를 입력해주면
“Cannot GET /home” 라는 오류를 반환하게 됩니다.

해당 주소를 서버에 요청하면 서버쪽 라우터에서 먼저 연결할 곳이 있는지 확인해보고 없기 때문에 이런 오류가 발생한답니다.

이를 위해 서버에서 따로 설정을 해주어야하는데요,

저희가 지금 공부하면서 사용하는 webpack 개발서버는,  webpack.config.js 파일에 코드 한줄을 추가해주면 해결됩니다.

    devServer: {
        inline: true,
        port: 7777,
        historyApiFallback: true
    },

4번줄인 historyApiFallback을 활성화 해주면 이 문제가 해결됩니다.

 

하지만, express를 사용하는경우에는? 다음과 같이 작성해주세요.

var app = express();

app.get('*', function (request, response){
  response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})

하지만, 아직 React.js 를 express 와 함께 사용하는것을 강좌에서 다루지 않았죠?

조만간 다루도록 하겠습니다 !

 

마치면서..

이번 강좌에선 클라이언트상에서 URL를 라우팅하는 방법에 대하여 배워보았습니다.

프로젝트에서 사용된 코드는 GitHub 에서 열람 가능합니다.

다음 강좌에선 Component 끼리 더욱 효율적이고 쉽게 데이터를 받고 보낼 수 있게 해주는 Redux 에 대하여 배우도록 하겠습니다.

liste

  • sagnol

    감사합니다. 리액트는 처음이라, 혼자서 끙끙 고민했던 부분인데 이렇게 포스팅 해주셔서 감사합니다.

    • 도움이 되었다니 영광이에요 😉

  • Cha HyunTak

    react-route를 redux랑 쓰려면 다른 라이브러리를 같이 써야하나요? 찾아보니, react-router-redux 같은게 존재하기는 하는데,
    처음 배우는거라 그런지 너무 복잡해서요 ㅠㅠ..
    다른 redux 설정을 모두 마쳤는데 마지막 Provider를 어떻게 결합해야 할지 난감하네요~

    • 결론부터 말하자면 다른 라이브러리를 같이 쓸 필요는 없습니다.

      react-redux-router라는 라이브러리가 있긴한데요 이 라이브러리는 라우팅을 할때 이전 데이터들을 보존하고 싶을때 사용합니다. 예를들어 뒤로가기를 했을때 상태를 아까 상태를 되돌리는거죠 새로 설정하는게 아니라

      그런데 이건 필수가 아니에요.

      거기 README에 보면 이렇게 적혀있어요.

      This library is not necessary for using Redux together with React Router. You can use the two together just fine without any additional libraries. It is useful if you care about recording, persisting, and replaying user actions, using time travel. If you don’t care about these features, just use Redux and React Router directly.

      그냥 Provider로 Router를 감싸시면 됩니다.

      한번 제 코드를 참고해보세요: https://github.com/velopert/react-codelab-memopad/blob/master/src/index.js

      위 프로젝트는 https://memo.hoah.xyz/ 에서 프리뷰 하실 수 있습니다.

      이 프로젝트를 작성하는 과정을 적은 포스트를 조만간 올릴 예정이에요 🙂

  • Dev_Senna

    강좌 초기와 조금 다른 코드를 쓰셔서 살짝 당황했습니다. 저번 강좌에선 index.js를 사용하셨는데 이번 과에서는 다른 코드를 쓰셔서 처음부터 다시 세팅하고 진행했습니다. 바쁘신 와중에도 좋은 강좌 감사 드립니다^^ 건강한 하루 되세요~!

    • 엇.. 기존에 사용하던 index.js 를 사용하는 구조로 작성을 하셔도 상관이 없답니다.
      나중에 시간날때 동일한 구조로 진행하게끔 수정을 하도록 할게요 🙂

  • 임동섭

    강의감사합니다. react-router가 왜 설치가 안될까요? ㅠㅠ 맥,윈도우 모두안되네요.

    • 어떤 오류가 발생하는지 여기에 스크린샷 혹은 로그복사해서 올려주시겠어요?

      • 임동섭

        제가 설정을 잘못한거 같습니다.^^ 이제는 잘되네요 감사합니다.

  • 서재민

    react-router 가 설치가되지 않습니다…

    npm install –save react-router -f 로 설치하면 설치가되긴하지만 클린설치가 되지 않고 에러문구가 같이 붙어서 설치됩니다…

    • package.json에서 name 부분에 react-router를 다른 이름으로 설정해보세요. react-router-sample 이라던지…

      제가 보기엔 npm 프로젝트명이랑 모듈이름이랑 동일해서 발생하는 오류일거예요

    • 그, 설치가 안되는 이유는 아마 npm 프로젝트명이 react-router여서 안되는걸꺼예요

      package.json에 name 확인해보세요. react-router로 하셨다면 react-router-sample로 변경해보세요.

      이 강좌에선 편의상 모든 코드를 App에 작성했었어요. ReactDOM.render또한 App에서 하게 했구요.

      만약에 index.js에 작성하시는거라면 작성하신대로, 따로 모듈화 하셔도 전혀문제되지 않습니다 🙂 원래 그렇게 해요.

      한 파일에 여러개의 컴포넌트를 작성하게 됐을땐 App은 export default class, 나머지는 export class로 하시면

      나중에 import App, { Home, 어쩌고저쩌고 } from ‘App’;

      이렇게하시면 됩니다. 그런데 ㅋㅋㅋ이렇게 하실필요없어요. 아까 말씀드렸다시피 한 파일에 작성한건 따로 이유가 있는게 아니라 이 글 작성할 시절 귀찮아서 합친거라서요..

      이 강좌도 조만간 리뉴얼 해야할것같아요 ㅎㅎ 동영상시리즈 끝나구나면

      추석인데 열공하시네요… 즐거운 추석 + 리액팅되세요!!

      • 서재민

        답변감사합니다…

        프로젝트명 변경으로 해결하였습니다! 감사합니다.

        두번째는 index.js를 기존대로 두고 App.js 내에서 Route를 사용하여 해결하였습니다.
        https://medium.com/@dabit3/beginner-s-guide-to-react-router-53094349669#.ixmfjo2jo
        위의 글을 참조하면서 따라하였더니 App.js를 컨테이너 역할로 유지하였습니다( 쓸데없는 집착…)

        주변에서 React에 대한 글을 많이 접하다보가.. 연휴에 작정하고
        덤볐는데 재밋네요.. 항상 좋은 강의 감사합니다. 즐거운 추석되세요~

  • SyaLot

    만약 주소가 없는 url 로 접근했을시는 따로 처리하는 방법이 있을까요?

    • (대문자, Router 입니다. 덧글 시스템이 이상하게 소문자로 만드네요)
      를 작성하시면 되겠습니다. NoMatch 는 404 전용 컴포넌트를 만드세요.

      • SyaLot

        정말 감사합니다. 그리고 염치를 불구하고 조금만더 묻겠습니다 ㅎㅎ;;;

        `http://localhost:7777/test/1` ~ `http://localhost:7777/test/100` 등등
        `localhost` 의 `test` 에서 뒤에 `100` 이라는 값을 받기 위해 찾아보니까
        를 쓰면 된다 해서
        사용하였더니 로 접근을 하면 문제가 없이 잘나오고 값도 잘 가져오는데

        주소창에 직접 접근을 하면 에러가 나는데 이부분도 처리가 가능할까요???
        또한 `100`부분을 빼고는 직접 접근을 해도 문제가 없습니다…

        • 제가 검토해본 결과:

          이부분에 문제가 있었습니다.

          index.html 에서 bundle 불러올때 앞에 / 를 붙여서 절대경로로 불러와주세요 🙂

          react-skeleton을 좀 수정해야겠습니다.. .

          • SyaLot

            오 답변 감사합니다 ㅎㅎ 말씀하신데로 절대경로로 가져오니 잘 되네요

          • 그저께 React-Router v4 가 pre-release 되었습니다.
            나중에 한번 살펴보세요~~

  • 이동주

    매번 좋은 강의 감사합니다.
    현재 App.js에서 ReactDom.render()를 해주고 있는데 그럴때 엔트리 포인트인 index.js 의 코딩은 어떤식으로 해야하는지 알수있을까요?

    • 이 강좌도 마찬가지로 이전 강좌들 파일 구조를 수정하고 제대로 수정을 안했네요.

      현재는 App.js가 entry로 설정이 되어있습니다.

      index.js를 entry로 쓴다면,

      App.js하단에 ReactDOM 을 제거하고 export default App; 을 불러와주세요.

      그 다음에 App 를 포함한 컴포넌트들을 import하고 ReactDOM.render를 하세요.

      이 강좌 또한 빠른시일내에 업데이트하겠습니다

  • ggoban

    강좌와 서재민님 댓글 / 답변 댓글을 보고 이해했네요. 현재 강좌 내 소스대로 진행하면 index.js 에서 route 해줄때 Home.. 등 App 내에 선언한 다른 클래스는 define 되지 않았다고 나오더라구요. 진행당시에는 app.js 에서 진행하신거였군요. 아래 서재민님 블로그 보고 해결과 그리고 댓글에서 알려주신 import App { } 으로 두가지 다 실행확인했습니다. ㅎㅎ
    그리고 세미콜론에 대해 질문 드립니다. 강좌에는 세미콜론이 들어가는 부분, 없는 부분들이 있는데 실제로는 세미콜론을 사용하지 않아도 문제가 발생하지 않고 문법에도 세미콜론은 사용하지 않는것을 권장하는듯 하던데 맞나요??

    • 세미콜론의 경우 필수사항이 아니여서 사실상 쓸꺼면 모든곳에 쓰고 안쓸거면 모든곳에 안쓰는게 맞는데,
      저 같은 경우는 모든곳에 쓰려고 하고 있지만.. 실수로 빼먹은곳이 좀 있는 것 같습니다 🙁

      이럴 땐 Lint 시스템에서 규칙을 만들어 사용하면 좋습니다 ㅎㅎㅎ

  • Junho Park

    안녕하세요. 블로그 잘 보고 있습니다. 질문 하나만 드릴게요.

    리덕스와 라우터를 함께 사용할 때, store 데이터를 router에서 전달하는 방법은 없나요?

    의 경우

    이런식으로 보낼 수 있는지요?

    • 답이 늦었군요, 죄송합니다.

      이렇게 한다면, About 컴포넌트 내부에서

      this.props.route.something 으로 접근 할 수 있습니다.

      저의 경우엔 보통 페이지별로 connect 를 해줍니다. 그게 훨씬 관리하기 편해서요!

    • 혹은, ()}/> 와 같은 방법으로도 할 수 있습니다.

  • Sim Isaac

    안녕하세요. 올리신 강좌를 보며 react를 공부하고 있는데요,
    가장 기초편에 대해서 공부하다 보니… Contact 만들기에서 file태그를 이용한 이미지 업로드와 img 태그를 이용한 미리보기 기능을 함께 사용할 경우 오동작하는 케이스를 발견했어요. img 태그를 이용한 미리보기 기능을 빼면 원래 의도대로 제대로 잘 동작하는데 이상하게 img 태그를 넣기만 하면 에러가 마구 발생하더라구요.

    구글링을 해보다가도 도무지 알 길이 없어서 벨로퍼트님께 질문드립니다.

    • 어떤 오류가 나는지 스크린샷을 찍어주실 수 있을까요?

      이런식으로 태그를 닫았는지도 체크해주세요.

    • Sim Isaac

      헛… 답신을 매우 빨리 주셨었네요.
      이미지 프리뷰가 url로 제대로 생성되지 않는 현상으로, 해결하였습니다. 왜 안 생기는지는 모르겠고, 구글링해서 그런 현상이 있으면 url 변환에 문제가 있는지 살펴보라고 하여 그대로 따라했더니 해결되었습니다…

      벨로퍼트님의 강좌로 정말 리액트를 생각보다 빠른 시일내에 흡수해가고 있습니다. 혼자 공부하려면 한참 걸렸을 건데… 감사합니다.

  • 신민창

    안녕하세요.
    매번 강좌를 보면서 공부하고 있는데 라우터 관련해서 공부하던도중
    데이터 추가 컴포넌트에서 데이터를 추가한뒤 history.push를 사용하여 리스트 페이지로 이동시켰는데 추가 시킨 데이터가 안보입니다.
    그래서 history.push를 사용하지않고 document.location.href로 이동시켰더니 그때는 페이지가 새로고침되서 데이터가 정상적으로 보입니다.
    그래서 제가 판단하기엔 라우터를 통해 페이지를 이동할 경우 페이지 새로고침 일어나지 않아서 사용하기 어려워보이는데
    데이터 갱신이 일어나는 경우에는 라우터를 사용할 방법이 없는건가요?

  • 안녕하세요 … 다름이 아니라 히스토리로 지정한 경로로 페이지 이동할때 어떤코드가 필요한가여 진짜 도무지 못찾겠네여 …