수동으로 새로 고치거나 쓸 때 반응 라우터 URL이 작동하지 않습니다


653

React-router를 사용하고 있으며 링크 버튼을 클릭하는 동안 제대로 작동하지만 웹 페이지를 새로 고칠 때 원하는 것을로드하지 않습니다.

예를 들어, 나는 localhost/joblist링크를 누르면 여기에 도착했기 때문에 모든 것이 정상입니다. 그러나 웹 페이지를 새로 고치면 다음과 같은 결과가 나타납니다.

Cannot GET /joblist

기본적으로 이와 같이 작동하지 않았습니다. 처음에 나는 같은 내 URL을했다 localhost/#/그리고 localhost/#/joblist그들은 완벽하게 정상적으로 작동했다. 그러나 나는 이런 종류의 URL을 좋아하지 않기 때문에 그것을 지우려고 노력했다 #.

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

이 문제는에서 발생하지 않으며 localhost/항상 원하는 것을 반환합니다.

편집 : 이 응용 프로그램은 단일 페이지이므로 /joblist서버에 아무것도 요구하지 않습니다.

EDIT2 : 내 전체 라우터.

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>
    </Route>
);

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

htaccess를 사용하여 메인 투어링 페이지를로드하고 라우터가 location.pathname을 사용하도록 요청하지 않으면 작동하지 않습니다.
Charles John Thompson III

#기호를 어떻게 지웠습니까? 감사합니다!
SudoPlz 2016

4
S3 버킷에서 반응 앱을 호스팅하는 경우 오류 문서를로 설정하면됩니다 index.html. 이것은 index.html무엇이든 상관없이 명중 될 것입니다.
Trevor Hutto

내 경우, 창문에 있지만 리눅스에서 잘 작동
Ejaz 카림

이것은 내 문제를 해결하는 데 도움이 된 참고 자료입니다 : github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

답변:


1094

허용되는 답변에 대한 의견과이 질문의 일반적인 특성 ( '작동하지 않음')을 살펴보면 여기에 관련된 문제에 대한 일반적인 설명을하기에 좋은 장소라고 생각했습니다. 따라서이 답변은 OP의 특정 사용 사례에 대한 배경 정보 / 정교화로 의도되었습니다. 저를 참아주세요.

서버 측과 클라이언트 측

이것에 대해 가장 먼저 이해해야 할 것은 이제 URL이 해석되는 곳이 2 곳이고 '이전 날'에는 1 곳만 존재했다는 것입니다. 과거에는 수명이 단순했을 때 일부 사용자 http://example.com/about가 서버에 요청을 보냈는데 , URL의 경로 부분을 검사하여 사용자가 정보 페이지를 요청한 후 해당 페이지를 다시 보냈습니다.

React-Router가 제공하는 클라이언트 측 라우팅을 사용하면 일이 덜 간단합니다. 처음에는 클라이언트에 아직 JS 코드가로드되어 있지 않습니다. 따라서 첫 번째 요청은 항상 서버에 대한 것입니다. 그러면 React 및 React Router 등을로드하는 데 필요한 스크립트 태그가 포함 된 페이지가 반환됩니다. 해당 스크립트가로드 된 경우에만 2 단계가 시작됩니다. 2 단계에서 사용자가 예를 들어 '정보'탐색 링크를 클릭하면 URL이 로컬로만 변경 되고 http://example.com/about( History API에서 가능 ) 서버에 대한 요청이 없습니다.. 대신, React Router는 클라이언트 측에서 작업을 수행하고 렌더링하고 렌더링 할 React보기를 결정합니다. 정보 페이지에서 REST 호출을 수행 할 필요가 없다고 가정하면 이미 완료된 것입니다. 서버 요청이 발생하지 않고 홈에서 회사 소개로 전환했습니다.

따라서 기본적으로 링크를 클릭하면 페이지를 새로 고치지 않고 주소 표시 줄의 URL을 조작하는 일부 Javascript가 실행되어 React Router가 클라이언트 측에서 페이지 전환을 수행합니다 .

그러나 주소 표시 줄에 URL을 복사하여 붙여 넣어 친구에게 전자 메일로 보내면 어떻게되는지 생각해보십시오. 친구가 아직 웹 사이트를로드하지 않았습니다. 다시 말해, 그녀는 여전히 1 단계에 있습니다. 그녀의 컴퓨터에서 아직 React Router가 실행되고 있지 않습니다. 그래서 그녀의 브라우저는 서버 요청http://example.com/about합니다.

그리고 이것이 당신의 문제가 시작되는 곳입니다. 지금까지는 서버의 웹 루트에 정적 HTML을 배치하는 것만으로도 벗어날 수 있습니다. 그러나 서버에서 요청하면404 다른 모든 URL에 오류가 발생 합니다 . React Router가 라우팅을 수행하기 때문에 동일한 URL 이 클라이언트 측 에서 잘 작동 하지만 서버 에서 URL을 이해하지 않으면 서버 측에서 실패 합니다.

서버 및 클라이언트 측 라우팅 결합

http://example.com/aboutURL이 서버 측과 클라이언트 측 모두에서 작동하게하려면 서버 측과 클라이언트 측 모두 에서 URL에 대한 경로를 설정해야합니다. 말이 되나요?

그리고 이것은 당신의 선택이 시작되는 곳입니다. 솔루션은 부트 스트랩 HTML을 반환하는 모든 경로를 통해 문제를 우회하는 것부터 서버와 클라이언트가 동일한 JS 코드를 실행하는 완전 동형 접근 방식까지 다양합니다.

.

문제를 모두 우회 : 해시 기록

함께 해시 역사 대신에 브라우저 역사 , 이런 걸 보이는 것에 대한 페이지에 대한 URL : http://example.com/#/about 해시 (후 일부 #) 기호를 서버에 전송되지 않습니다. 따라서 서버는 http://example.com/예상대로 색인 페이지 만보 고 보냅니다. React-Router가 #/about부품 을 픽업 하고 올바른 페이지를 표시합니다.

단점 :

  • '못생긴'URL
  • 이 방법으로는 서버 측 렌더링이 불가능합니다. 검색 엔진 최적화 (SEO)에 관한 한 귀하의 웹 사이트는 내용이 거의없는 단일 페이지로 구성됩니다.

.

포괄

이 방법을 사용하면 사용 브라우저 기록을 할 수 있지만, 단지 포괄 전송 서버의 설정 /*에를 index.html효과적으로 당신에게 해시 역사와 많은 같은 상황을 제공합니다. 그러나 깨끗한 URL이 있으며 나중에 모든 사용자의 즐겨 찾기를 무효화하지 않고도이 체계를 개선 할 수 있습니다.

단점 :

  • 더 복잡한 설정
  • 여전히 좋은 SEO

.

잡종

하이브리드 방식에서는 특정 경로에 대한 특정 스크립트를 추가하여 포괄적 인 시나리오를 확장합니다. 콘텐츠가 포함 된 사이트의 가장 중요한 페이지를 반환하도록 간단한 PHP 스크립트를 만들 수 있으므로 Googlebot은 적어도 페이지의 내용을 볼 수 있습니다.

단점 :

  • 훨씬 더 복잡한 설정
  • 당신이 특별한 대우를 제공하는 노선에 대해서만 좋은 SEO
  • 서버 및 클라이언트에서 컨텐츠를 렌더링하기위한 코드 복제

.

동형

Node JS를 서버로 사용 하여 양쪽 에서 동일한 JS 코드를 실행할 수 있다면 어떨까요? 이제 모든 라우트를 단일 반응 라우터 구성에 정의했으며 렌더링 코드를 복제 할 필요가 없습니다. 이것은 말하자면 '성배'입니다. 서버는 클라이언트에서 페이지 전환이 발생한 경우와 똑같은 마크 업을 보냅니다. 이 솔루션은 SEO 측면에서 최적입니다.

단점 :

  • 서버 는 반드시 JS를 실행할 수 있어야합니다 . Java icw Nashorn을 실험했지만 작동하지 않습니다. 실제로 이는 대부분 노드 JS 기반 서버를 사용해야 함을 의미합니다.
  • 많은 까다로운 환경 문제 ( window서버 측 등에서 사용)
  • 가파른 학습 곡선

.

어느 것을 사용해야합니까?

멀리 갈 수있는 것을 선택하십시오. 개인적으로 포괄은 설정하기에 충분히 간단하다고 생각합니다. 이 설정을 통해 시간이 지남에 따라 개선 할 수 있습니다. Node JS를 서버 플랫폼으로 이미 사용하고 있다면 동형 응용 프로그램을 수행하는 것이 확실합니다. 예, 처음에는 힘들지만 일단 중단되면 실제로는 문제에 대한 매우 우아한 해결책입니다.

기본적으로 저에게는 이것이 결정적인 요소입니다. 내 서버가 노드 JS에서 실행되면 동형이됩니다. 그렇지 않으면 Catch-all 솔루션을 선택하고 시간이 지남에 따라 SEO 요구 사항에 따라 솔루션을 확장합니다 (하이브리드 솔루션).

React를 사용하여 동형 ( '유니버설'이라고도 함) 렌더링에 대해 더 배우려면이 주제에 대한 유용한 자습서가 있습니다.

또한 시작을 위해 스타터 키트를 살펴 보는 것이 좋습니다. 기술 스택에 대한 선택과 일치하는 것을 선택하십시오 (React는 MVC의 V 일 뿐이므로 전체 앱을 빌드하려면 더 많은 것이 필요함을 기억하십시오). Facebook 자체에서 출판 한 것을 살펴보십시오.

또는 지역 사회에서 많은 사람 중 하나를 선택하십시오. 이제 모든 사이트를 색인 생성하는 멋진 사이트가 있습니다.

나는 이것들로 시작했다.

현재 위의 두 스타터 키트에서 영감을 얻은 자체 제작 버전의 범용 렌더링을 사용하고 있지만 현재는 구식입니다.

당신의 탐구에 행운을 빕니다!


2
위대한 게시물 Stijn! 동형 반응 앱용 스타터 키트를 사용하는 것이 좋습니다? 그렇다면 선호하는 예를 들어 줄 수 있습니까?
Chris

1
@ Paulos3000 사용중인 서버에 따라 다릅니다. 기본적으로 경로를 정의하고 /*HTML 페이지에 응답합니다. 여기서 까다로운 점은이 경로로 .js 및 .css 파일에 대한 요청을 가로 채지 않도록하는 것입니다.
Stijn de Witt

2
@ Paulos3000 관련 질문은 여기를 참조하십시오 : Apache / php , Express / js , J2E / Java .
Stijn de Witt

1
안녕하세요, 해시 솔루션을 시도하고 있지만 운이 없습니다. 가져 오기 createMemoryHistory is not a function오류가 발생했습니다. 마음 좀 봐? stackoverflow.com/questions/45002573/...
레온 Gaban에게

5
@LeonGaban이 답변이 작성된 이후 React Router가 구현을 변경 한 것 같습니다. 이제 서로 다른 히스토리에 대해 서로 다른 라우터 인스턴스가 있으며 백그라운드에서 히스토리 구성을 수행합니다. 기본 원칙은 여전히 ​​동일합니다.
Stijn de Witt

115

여기에 대한 답변은 모두 매우 도움이되며 경로를 기대하도록 Webpack 서버를 구성하는 것이 효과적이었습니다.

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

historyApiFallback 은이 문제를 해결했습니다. 이제 라우팅이 올바르게 작동하고 페이지를 새로 고치거나 URL을 직접 입력 할 수 있습니다. 노드 서버의 해결 방법에 대해 걱정할 필요가 없습니다. 이 답변은 분명히 웹팩을 사용하는 경우에만 작동합니다.

편집 : 이것이 필요한 이유에 대한 더 자세한 이유는 여기에 내 대답을 참조하십시오 : https://stackoverflow.com/a/37622953/5217568


16
Webpack 팀 은 프로덕션 환경에서 dev 서버를 사용 하지 않는 것이 좋습니다 .
Stijn de Witt 2016 년

5
일반적인 개발 목적으로 이것이 가장 좋은 솔루션입니다. historyApiFallback충분하다. 다른 모든 옵션에 대해서는 플래그를 사용하여 CLI에서 설정할 수도 있습니다 --history-api-fallback.
Marco Lazzeri

2
@ Kunok하지 않습니다. 이것은 개발을위한 빠른 수정이지만 여전히 프로덕션을 위해 무언가를 알아 내야합니다.
Stijn de Witt

contentBase : ': /'URL에서 앱 파일에 액세스 할 수 있습니다.
Nezih

85

.htaccess파일을 변경 하고 다음을 삽입 할 수 있습니다 .

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
</IfModule>

나는 사용 react: "^16.12.0"하고 react-router: "^5.1.2" 있으며이 방법은 모두 포괄적이며 아마도 가장 쉬운 방법입니다.


3
이것은 잘 작동합니다! 앱을 재구성하고 싶지 않은 경우 가장 쉬운 방법
mhyassin

7
RewriteEngine On을 첫 줄로 잊지 마십시오
Kai Qing

3
이 답변은 Apache 서버의 구성을 나타냅니다. 이것은 React 앱의 일부가 아닙니다.
Colton Hicks

위의 답변을 이해하지 못했습니다. 어떤 교사도 내 링크가 전혀 작동하지 않는다고 말하지 않았습니다. 그러나이 간단한 htaccess 파일이 작동했습니다. 감사합니다
Thomas Williams

이것은 next.js 정적 내보내기에서도 작동합니다. 바람둥이는 노드와 유사하지 않으므로 next.js 정적 내보내기를 위해이 추가 구성이 필요합니다.
giri-jeedigunta

62

들어 라우터 V4의 반작용 사용자 :

다른 답변에서 언급 한 해시 기록 기술 로이 문제를 해결하려고하면

<Router history={hashHistory} >

V4에서는 작동하지 않습니다 HashRouter. 대신 사용하십시오 .

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

참조 : HashRouter


HashRouter가이 문제를 해결하는 이유에 대해 자세히 설명해 주시겠습니까? 귀하가 제공 한 링크는 저에게 설명하지 않습니다. 또한 경로에서 해시를 숨기는 방법이 있습니까? BrowserRouter를 사용했지만이 404 문제가 발생했습니다.
Ian Smith

29

내비게이션이 클라이언트에서 발생하는지 아니면 서버에서 발생하는지에 따라 두 가지 다른 방식으로 라우터를 호출 할 수 있습니다. 클라이언트 측 작업을 위해 구성했습니다. key 매개 변수는 두 번째로 run 메소드 인 위치입니다.

React Router Link 컴포넌트를 사용하면 브라우저 탐색을 차단하고 transitionTo를 호출하여 클라이언트 측 탐색을 수행합니다. HistoryLocation을 사용하고 있으므로 HTML5 히스토리 API를 사용하여 주소 표시 줄에서 새 URL을 시뮬레이션하여 탐색의 환상을 완성합니다. 구형 브라우저를 사용하는 경우 작동하지 않습니다. HashLocation 구성 요소를 사용해야합니다.

새로 고침을 누르면 모든 React 및 React Router 코드가 무시됩니다. 서버가 요청을 /joblist받고 무언가를 반환해야합니다. 서버 run에서 올바른보기를 렌더링하려면 메소드에 요청 된 경로를 전달해야합니다 . 동일한 경로지도를 사용할 수 있지만 다른 전화 번호가 필요할 수 있습니다.Router.run 있습니다. Charles가 지적했듯이 URL 재 작성을 사용하여이를 처리 할 수 ​​있습니다. 다른 옵션은 node.js 서버를 사용하여 모든 요청을 처리하고 경로 값을 위치 인수로 전달하는 것입니다.

예를 들어, 다음과 같이 보일 수 있습니다.

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

요청 경로가 (으)로 전달되고 run있습니다. 이렇게하려면 렌더링 된 HTML을 전달할 수있는 서버 측 뷰 엔진이 있어야합니다. renderToString서버에서 React를 사용 하고 실행할 때 고려해야 할 사항이 몇 가지 있습니다 . 페이지가 서버에서 렌더링되고 나면 앱이 클라이언트에로드되면 다시 렌더링되어 필요에 따라 서버 측 렌더링 된 HTML이 업데이트됩니다.


2
실례 합니다만, pls에 다음 문장을 설명해 주시겠습니까? "새로 고침을하면 모든 React 및 React Router 코드를 무시합니다"? 왜 그런가요?
VB_

리 액트 라우터는 일반적으로 브라우저 내에서만 다른 '경로'를 처리하는 데 사용됩니다. 이전 스타일 해시 경로와 최신 기록 API의 두 가지 일반적인 방법이 있습니다. 브라우저를 새로 고치면 서버가 요청하여 클라이언트 측 반응 라우터 코드를 무시합니다. 서버에서 경로를 처리해야합니다 (히스토리 API를 사용하는 경우에만). 이를 위해 반응 라우터를 사용할 수 있지만 위에서 설명한 것과 비슷한 작업을 수행해야합니다.
Todd

알았다. 그러나 서버가 JS가 아닌 언어로되어 있다면 어떻게해야할까요?
VB_

2
죄송합니다. "루트가 아닌"경로를 렌더링하려면 빠른 서버가 필요하다는 의미입니까? 나는 동 형사상 정의에 개념에 데이터 페치가 포함되어 있지 않다는 사실에 놀랐다. (서로 데이터 뷰를 사용하여 뷰를 렌더링하고 싶을 때는 필연적으로 복잡하지만, SEO의 필요성은 분명하지만 동 형사상이라고 주장한다). 이제 저는 "WTF 반응"과 같습니다. 진지하게 ... 내가 틀렸다면 수정하십시오. ember / angular / backbone은 경로를 렌더링하기 위해 서버가 필요합니까? 나는 노선을 사용하기 위해이 부풀린 요구 사항을 정말로 이해하지 못합니다
Ben

1
내 반응 응용 프로그램이 동형이 아닌 경우 반응 라우터의 새로 고침이 작동하지 않음을 의미합니까?
Matt

28

index.html head에서 다음을 추가하십시오.

<base href="/">
<!-- This must come before the css and javascripts -->

그런 다음 webpack dev 서버로 실행할 때이 명령을 사용하십시오.

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback 중요한 부분입니다


이것은 훌륭하게 작동했습니다! 해당 솔루션을 얻는 방법에 대한 자세한 내용을 볼 수있는 링크가 있습니까?
justDan

1
미안 시도와 진실의 시행 착오 기술을 통해 발견했습니다.
Efe Ariaroo 2016 년

그것의 가치가 언급 당신이 아닌 기본 href 사용하는 경우 /다음 HashRouter(당신이 해시 라우팅을 사용하는 경우)하지 않습니다 일
로비 에버 릴

<base href = "/"> 태그가 나를 위해 해냈습니다. 고마워 Webpack dev 서버가 경로 / <bundle>에서 번들을 가져 오려고 시도했지만 실패했습니다.
Malisbad

27

방금 create-react-app를 사용하여 웹 사이트를 만들었고 여기에 동일한 문제가 표시되었습니다. 패키지 BrowserRouting에서 사용 react-router-dom합니다. Nginx 서버에서 실행 중이며 나를 해결 한 것은 다음을 추가하는 것입니다./etc/nginx/yourconfig.conf

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

.htaccessAppache를 실행하는 경우에 다음을 추가 하는 것과 일치합니다.

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

이것은 또한 Facebook 자체에서 제안한 솔루션 인 것처럼 보이고 여기 에서 찾을 수 있습니다


1
와우, nginx를위한 그런 간단한 해결책; 새로운 nginx 사용자로서 매력처럼 작동합니다. 복사하여 붙여 넣기 만하면됩니다. 공식 fb 문서와의 링크 +1 잘 했어.
user3773048

기록을 위해 : nginx 및 Angular 앱과 함께 AWS 인스턴스를 사용하고 있습니다. 작동합니다.
Diego Sarmiento

당신은 저를 저장합니다. 나는 어제부터 내 문제를 해결하려고 노력했지만 몇 가지 솔루션을 피곤했지만 실패했습니다. 마지막 으로이 라우터 문제를 해결하도록 도와주었습니다. 아 고마워
Sharifur Robin

"반응": "^ 16.8.6", "react-router": "^ 5.0.1"에 필요한 다른 것이 있습니까? 나는이 솔루션 (nginx와 아파치 모두)을 시도했지만 작동하지 않습니다.
Mark A. Tagliaferro

Facebook은 이에 관한 문서를 변경하지 않았으므로 절차가 동일하다고 가정 할 수 있습니다. 나는 잠시 동안 그것을 시도하지 않았습니다.
Aidin

17

이것은 당신의 문제를 해결할 수 있습니다

프로덕션 모드의 ReactJS 응용 프로그램에서도 동일한 문제에 직면했습니다. 다음은이 문제에 대한 2 가지 해결책입니다.

1. 대신 브라우저 기록 대신 라우팅 기록을 "hashHistory"로 변경하십시오.

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

이제 명령을 사용하여 앱을 빌드하십시오.

sudo npm run build

그런 다음 빌드 폴더를 var / www / 폴더에 넣으십시오. 이제 응용 프로그램은 각 URL마다 # 태그를 추가하여 정상적으로 작동합니다. 처럼

localhost / # / home localhost / # / aboutus

해결 방법 2 : browserHistory를 사용하여 # 태그없이

라우터에서 히스토리 = {browserHistory}를 설정하고 sudo npm run build를 사용하여 빌드하십시오.

404를 찾을 수 없음 페이지를 해결하려면 "conf"파일을 작성해야합니다. conf 파일은 다음과 같아야합니다.

아래 명령을 터미널 유형을여십시오

cd / etc / apache2 / sites-available ls nano sample.conf 아래 내용을 추가하십시오.

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

이제 다음 명령을 사용하여 sample.conf 파일을 활성화해야합니다

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

그런 다음 sudo service apache2 reload 또는 restart를 사용하여 Apache 서버를 다시로드하라는 메시지가 표시됩니다

그런 다음 localhost / build 폴더를 열고 아래 내용의 .htaccess 파일을 추가하십시오.

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

이제 앱이 정상적으로 작동합니다.

참고 : 0.0.0.0 ip를 로컬 IP 주소로 변경하십시오.

이와 관련하여 의문이 있으면 의견을 자유롭게 제기하십시오.

나는 그것이 다른 사람들에게 도움이되기를 바랍니다.


첫 번째 솔루션이 작동 "react-router-dom": "^5.1.2"합니까?, 사용하고 있습니다<BrowserRouter>
151291

16

AWS Static S3 Hosting & CloudFront를 통해 반응 앱을 호스팅하는 경우

이 문제는 S3 폴더에 / some / other / path가 존재할 것으로 예상 되었기 때문에 CloudFront가 403 액세스 거부 메시지로 응답했지만 그 경로는 React의 react-router를 통한 라우팅에만 내부적으로 존재합니다.

해결책은 배포 오류 페이지 규칙을 설정하는 것입니다. CloudFront 설정으로 이동하여 배포를 선택하십시오. 그런 다음 "오류 페이지"탭으로 이동하십시오. "사용자 정의 오류 응답 작성"을 클릭하고 403에 대한 항목을 추가하십시오. 이것이 오류 상태 코드이기 때문입니다. 응답 페이지 경로를 /index.html로 설정하고 상태 코드를 200으로 설정하십시오. 최종 결과는 간단합니다. 색인 페이지는 제공되지만 URL은 브라우저에서 유지되므로 반응 앱이로드되면 URL 경로를 감지하고 원하는 경로로 이동합니다.

오류 페이지 403 규칙


1
이것이 바로 내가 필요한 것입니다. 감사합니다.
Jonathan

그래서 .. 모든 URL은 작동하지만 상태 코드 403을 반환합니까? 올바르지 않습니다. 예상 상태 코드는 2xx 범위에 있어야합니다.
Stijn de Witt

@StijndeWitt 정확하게 이해하고 있는지 잘 모르겠습니다. CloudFront는 모든 것을 처리합니다. 클라이언트는 403 상태 코드를 볼 수 없습니다. 경로 재 지정은 서버 측에서 발생하며 색인 페이지를 렌더링하고 URL을 유지 보수하며 결과적으로 올바른 경로를 제공합니다.
th3morg

@ th3morg 아 그래 이제 본다. 응답 상태 코드를 200으로 채울 수도 있다는 사실을 놓쳤습니다. 기본적으로 상태 403을 상태 200에 매핑하는 중입니다 ... 다른 이유로 인해 403을 얻는 경우를 제외하고는 괜찮습니다. 이 때문에 볼 수 없습니다.
Stijn de Witt

1
이 @ th3morg에 감사드립니다. 이 설정은 다단계 경로에도 적용됩니까? domain / signup, domain / login, domain / <documentId>와 같은 루트 수준의 경로가 있으면 나에게 효과적이지만 domain / document / <documentId>와 같은 다중 수준 경로에는 작동하지 않습니다.
Pargles

15

Webpack Dev Server에는이를 활성화하는 옵션이 있습니다. 를 열고 package.json추가하십시오 --history-api-fallback. 이 솔루션은 저에게 효과적이었습니다.

반응 라우터 자습서


3
괜찮지 webpack-dev-server만 프로덕션 빌드 에서이 문제를 어떻게 해결합니까?
Hussain

12

IIS에서 반응 앱을 호스팅하는 경우 다음을 포함하는 web.config 파일을 추가하십시오.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

이것은 IIS 서버에게 404 오류 대신 메인 페이지를 클라이언트에 반환하도록 지시하며 해시 기록을 사용할 필요가 없습니다.


고마워 친구 !!!! 일이야!
탄 바오

12

이것을 다음에 추가하십시오 webpack.config.js:

devServer: {
    historyApiFallback: true
}

개발자에게는 좋지만 프로덕션 빌드에는 도움이되지 않습니다.
KFunk

11

생산 스택 : React, React Router v4, BrowswerRouter, Express, Nginx

1) 예쁜 URL을위한 사용자 브라우저 라우터

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
        <Router>
           // your routes here
        </Router>
    )
  }
}

2) 알 수없는 모든 요청에 ​​index.html을 사용하여 추가하십시오. /*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

3) 번들 웹팩 webpack -p

4) 실행 nodemon server.js또는node server.js

편집 : nginx가 서버 블록에서 이것을 처리하고 2 단계를 무시하게 할 수 있습니다.

location / {
    try_files $uri /index.html;
}

정의되지 않은 경로를 얻는
Goutham

@Goutham server.js 파일 상단에 import path from 'path또는const path = require('path')
Isaac Pak

2 단계 (로 모두 잡기 /*)가 내 하루를 저장했습니다. 감사합니다! :)
Atlas7

이것은 redux를 사용하고 연결된 라우터에 관심이있는 사람들을 위해이 예제를 참조하십시오 : github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson

10

React 앱 작성을 사용하는 경우 :

당신이 찾을 수 많은 주요 호스팅 플랫폼에 대한 솔루션이 문제의 비록 큰 거리에있다 여기 반작용 만들기 앱 페이지가. 예를 들어, 프론트 엔드 코드에 React Router v4 및 Netlify를 사용합니다. 내 공용 폴더 ( "_redirects")에 파일 1 개와 해당 파일의 한 줄의 코드 만 추가하면됩니다.

/*  /index.html  200

이제 내 웹 사이트는 브라우저에 입력하거나 누군가가 새로 고침을 할 때 mysite.com/pricing과 같은 경로를 올바르게 렌더링합니다.


7

아래 코드를 사용하여 공용 폴더 안에 ".htaccess"파일을 추가하십시오.

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]  

고마워, 이것은 아파치와 함께 Fedora 28에서 나를 위해 일한 것입니다. 위의 다시 쓰기 규칙이나 CRA 페이지의 규칙 중 어느 것도 나를 위해 일하지 않았습니다. 별도의 .htaccess 파일 대신 가상 호스트 설정에 추가했습니다.
boerre

6

index.html로 대체 된 경우 index.html 파일에 다음이 있는지 확인하십시오.

<script>
  System.config({ baseURL: '/' });
</script>

프로젝트마다 다를 수 있습니다.


2
귀하의 HTML에 추가 head: <base href = "/">
Efe Ariaroo

4

Firebase를 사용하는 경우 호스팅 섹션의 앱 루트에있는 firebase.json 파일에 다시 쓰기 속성이 있는지 확인해야합니다.

예를 들면 다음과 같습니다.

{ 
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]    
  }
}

이것이 다른 누군가를 좌절시키고 시간을 낭비하지 않기를 바랍니다.

행복한 코딩 ...

주제에 대한 추가 정보 :

https://firebase.google.com/docs/hosting/full-config#rewrites

Firebase CLI : "단일 페이지 앱으로 구성 (모든 URL을 /index.html에 다시 작성)"


당신은 전설입니다
Perniferous


3

아직 서버 측 렌더링을 사용하지 않지만 Link가 대부분 제대로 작동하는 것처럼 보이지만 매개 변수가있을 때 실패한 OP와 같은 문제가 발생했습니다. 누군가에게 도움이되는지 여기에 내 솔루션을 문서화 할 것입니다.

내 주요 jsx에는 다음이 포함됩니다.

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

이것은 첫 번째로 일치하는 링크에서 잘 작동하지만 : id가 <Link> 해당 모델의 세부 사항 페이지에 중첩 된 표현식 되면 브라우저 표시 줄에서 URL이 변경되지만 페이지의 컨텐츠는 처음에 링크 된 모델을 반영하도록 변경되지 않았습니다.

문제는에서 props.params.id를 사용하여 모델을 설정 했다는 것 입니다 componentDidMount. 구성 요소는 한 번만 마운트되므로 첫 번째 모델은 페이지에 고정 된 모델이며 후속 링크는 소품을 변경하지만 페이지는 변경되지 않은 상태로 둡니다.

모두 컴포넌트 상태 모델 설정 componentDidMount및 예약 componentWillReceiveProps(이 다음 소품에 기초) 문제를 해결하고, 페이지 콘텐츠는 원하는 모델을 반영하도록 변경한다.


1
서버 측 렌더링을 시도하려는 경우 구성 요소 생성자 (에 액세스 권한이있는 props) iso 를 사용하는 것이 좋습니다 componentDidMount. componentDidMount브라우저에서만 호출 되기 때문 입니다. 목적은 이벤트 리스너를 연결할 body수없는 등 의 DOM과 같은 작업을 수행하는 것입니다 render.
Stijn de Witt

3

이 주제는 약간 오래되었고 해결되었지만 간단하고 명확하며 더 나은 솔루션을 제안하고 싶습니다. 웹 서버를 사용하는 경우 작동합니다.

각 웹 서버에는 http 404의 경우 사용자를 오류 페이지로 리디렉션하는 기능이 있습니다.이 문제를 해결하려면 사용자를 색인 페이지로 리디렉션해야합니다.

Java 기본 서버 (tomcat 또는 Java 응용 프로그램 서버)를 사용하는 경우 솔루션은 다음과 같습니다.

web.xml :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

예:

  • http://example.com/about 가져 오기
  • 이 페이지가 서버 측에 없기 때문에 웹 서버에서 http 404가 발생합니다.
  • 오류 페이지 구성은 index.jsp 페이지를 사용자에게 다시 보내도록 서버에 지시합니다.
  • 그러면 클라이언트 측의 URL이 여전히 http://example.com/about 이므로 JS는 clien 측에서 나머지 작업을 수행합니다 .

즉, 더 이상 마술이 필요하지 않습니다.)


대단 했어! Wildfly 10.1 서버를 사용하고 있으며 위치를 '/'로 설정 한 경우를 제외하고는 web.xml 파일을 업데이트했습니다. 내 반응 코드에서 다음과 같은 브라우저 기록을 사용했기 때문입니다.const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
이것이 SEO에 영향을 미치지 않습니까? 실제로 존재하는 페이지에 404 상태를 제공합니다. 사용자는 이것을 알지 못할 수도 있지만 봇은 실제로 페이지를 긁지 않도록 많은주의를 기울입니다.
subharb

3

백엔드에서 Express 또는 다른 프레임 워크를 사용하는 경우 아래와 비슷한 구성을 추가하고 구성에서 Webpack 공용 경로를 확인할 수 있습니다. BrowserRouter를 사용하는 경우 다시로드 할 때도 정상적으로 작동합니다

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

이것이 가장 간단한 해결책입니다. 가야이 경로를 참고 그것이 캐치 전부로 다른 루트
dcsan

3

새로 고치거나 URL을 직접 호출 할 때 "GET / URL을 할 수 없음"오류 수정

주어진 링크가 이와 같은 경로를 갖도록 webpack.config.js 를 구성하십시오 .

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },

2

.Net Core MVC를 사용함에 따라 다음과 같은 것이 도움이되었습니다.

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var url = Request.Path + Request.QueryString;
            return App(url);
        }

        [Route("App")]
        public IActionResult App(string url)
        {
            return View("/wwwroot/app/build/index.html");
        }
   }

기본적으로 MVC 측에서는 일치하지 않는 모든 경로가에 Home/Index지정된대로 떨어집니다 startup.cs. 내부 Index에서 원래 요청 URL을 가져 와서 필요할 때마다 전달할 수 있습니다.

startup.cs

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });

웹 API가 앱과 분리되어 있으면 어떻게 처리합니까?
dayanrr91 19

@ dayanrr91 프로젝트에 웹 API가있는 경우 어쨌든 서버 측 라우팅이 필요하지 않습니다. 그리고 귀하의 반응 라우터는 URL을 읽고 적절한 라우팅을 수행해야한다고 생각합니다. 아무것도 막을 수 없음
Elnoor

귀하의 의견은 매우 높이 평가되지만 질문이 있습니다. 방금 HashRouter를 추가했지만 이제는 URL을 수동으로 입력하면 내 집으로 리디렉션하는 대신 내 집 구성 요소로 리디렉션 한 다음 시도하려는 구성 요소로 리디렉션됩니다 들어가기위한 해결 방법이 있습니까?
dayanrr91 19

@ dayanrr91 실제로는 아무것도 몰랐습니다. 장애자 작동 방식은 브라우저 라우터와 유사해야합니다. 또한이 샌드 박스에서 방금 테스트 했으며 , 훌륭한 codesandbox.io/s/25okp1mny 작동 하고 URL 25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

IIS에서 호스팅하는 경우; 이것을 webconfig에 추가하면 문제가 해결되었습니다.

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

다른 서버에 대해서도 비슷한 구성을 할 수 있습니다


1

Laravel을 사용하여 React JS SPA에 대한 솔루션을 찾는 사람이 여기 있습니다. 허용되는 답변은 그러한 문제가 발생하는 이유에 대한 가장 좋은 설명입니다. 이미 설명한 것처럼 클라이언트 쪽과 서버 쪽을 모두 구성해야합니다. 블레이드 템플릿에 js 번들 파일을 포함 시키십시오. 다음 URL facade과 같이 사용 하십시오.

<script src="{{ URL::to('js/user/spa.js') }}"></script>

경로에서이를 블레이드 템플릿이있는 기본 끝점에 추가하십시오. 예를 들어

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');
});

위는 블레이드 템플릿의 기본 끝점입니다. 이제 선택적 경로를 추가하십시오.

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');
});

발생하는 문제는 먼저 블레이드 템플릿이로드 된 다음 반응 라우터입니다. 그래서 때있는 거로드 '/setting-alerts', 그것은 HTML과 JS를로드합니다. 그러나 load '/setting-alerts/about'하면 서버 측에서 먼저로드됩니다. 서버 측에서이 위치에 아무것도 없으므로 찾을 수 없습니다. 선택적 라우터가 있으면 동일한 페이지를로드하고 리액터 라우터도로드 된 후 리액터 로더가 표시 할 구성 요소를 결정합니다. 도움이 되었기를 바랍니다.


하나의 정확한 URI를 서버에로드 한 다음 서버가 React로 리디렉션 할 수 있습니까? 예를 들어, load 할 때 /user/{userID}범용 /userHTML보기 를 반환 하고 ID를 가져 와서 AJAX 또는 axios를 사용하여 호출하면됩니다. 그렇게 할 수 있습니까? 그들은 SEO 친절한가?
Ryuujo

1

IIS 10을 사용하는 사람들에게는 이것이 바로 올바른 일입니다. 이것을 사용하여 browserHistory를 사용하고 있는지 확인하십시오. 참고로 라우팅 코드를 제공하지만 중요한 것은 아닙니다. 중요한 것은 아래 구성 요소 코드 다음 단계입니다.

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />    
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

문제는 IIS가 클라이언트 브라우저로부터 요청을받는 것이므로 URL은 마치 페이지를 요청하는 것처럼 URL을 해석 한 다음 사용 가능한 페이지가 없기 때문에 404 페이지를 반환합니다. 다음을 수행하십시오.

  1. IIS 열기
  2. 서버를 펼친 후 사이트 폴더를여십시오.
  3. 웹 사이트 / 응용 프로그램을 클릭하십시오
  4. 오류 페이지로 이동
  5. 목록에서 404 오류 상태 항목을 엽니 다.
  6. "정적 파일의 컨텐츠를 오류 응답에 삽입"옵션 대신 "이 사이트에서 URL 실행"으로 변경하고 "/"슬래시 값을 URL에 추가하십시오.

그리고 이제는 잘 작동합니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

도움이 되길 바랍니다. :-)


1

WebPack을 사용하고 있는데 동일한 문제가 있습니다. Solution => server.js 파일에서

const express = require('express');
const app = express();

app.use(express.static(path.resolve(__dirname, '../dist')));
  app.get('*', function (req, res) {
    res.sendFile(path.resolve(__dirname, '../dist/index.html'));
    // res.end();
  });

새로 고친 후에도 응용 프로그램이 렌더링되지 않는 이유는 무엇입니까?


이것은 나를 위해 그것을했다! 처음에는 res.sendFile (path.JOIN (publicPath, "index.html")); 위의 예와 같이 "join"을 "resolved"로 변경했습니다. res.sendFile (path.resolve ( "./ dist", "index.html")); 또한 __dirname으로 놀고 있었지만 실제로 이해하지 못하거나 작동하지 않아서 index.html이 제공되는 위치이므로 "./dist"로 수동으로 복사했습니다. 나는 또한 그렇게 일찍 선언했다 : app.use (express.static ( "./ dist"));
logixplayer

1

사용 HashRouter과 나를 위해 일한 돌아 오는 단지 대체도 :

import {
  Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <Router history={history}> //replace here saying Router
            <Layout/>
        </Router>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

에:

import {
  HashRouter //replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <HashRouter history={history}> //replaced with HashRouter
            <Layout/>
        </HashRouter>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

0

나는이 같은 문제를했고, 솔루션은 우리를 위해 일했다 ..

배경:

동일한 서버에서 여러 앱을 호스팅하고 있습니다. 서버를 새로 고칠 때 서버는 해당 특정 앱의 dist 폴더에서 인덱스를 찾을 위치를 이해할 수 없습니다. 위의 링크는 우리를 위해 일한 것에 당신을 데려 갈 것입니다 ... 우리가 우리의 요구에 대한 해결책을 찾는 데 꽤 시간을 보냈기 때문에 이것이 도움이되기를 바랍니다.

우리는 사용하고 있습니다 :

package.json

"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}

내 webpack.config.js

webpack.config.js

/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'
});

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  ],
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      },
      exclude: /node_modules/
    }]
  },
  plugins: [HTMLWebpackPluginConfig]
}

내 index.js

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
})
// persistStore(store).purge()


ReactDOM.render(
    <Provider store={store}>
      <div>
        <Routes history={history} />
      </div>
    </Provider>,
  document.getElementById('mount')
)

내 app.js

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

app.get('/*', function (req, res) {
    res.render('index');
});

app.listen(8081, function () {
  console.log('MD listening on port 8081!');
});

0

나는 이것을 처리하는 방법을 좋아한다. 이 문제를 해결하려면 서버 측에 yourSPAPageRoute / * 를 추가하십시오 .

네이티브 HTML5 History API조차도 페이지 새로 고침에서 올바른 리디렉션을 지원하지 않기 때문에이 접근법을 사용했습니다 (아는 한).

참고 : 선택된 답변은 이미이 문제를 해결했지만 더 구체적으로하려고합니다.

급행 노선

테스트-히스토리 API 테스트를 거쳐 공유하고 싶었습니다.

도움이 되길 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.