React-Router refresh Error

React-Router의 문제

  • 최근 React-Router를 활용하여 깃허브페이지나 AWS로 배포를 할 때 겪었던 문제에 대해 호기심이 생겨 알아보았고, 이유를 알게되었다.

😡 문제발생

  • localhost에서 React-Router를 활용하여 특정 뷰에 Path를 적용시켜주었을 때에는 새로고침을 해도 뷰가 잘 구성이 된다.
  • 깃허브페이지나 AWS를 통한 배포(이하 배포로 통일하겠음)를 하였을 때에는, 404에러를 반환한다.

🧐 왤까?

  • 원인은 React 자체의 특성에 있었다.(알고있던 특징이였는데 전혀 예상을 못했다;)

React는 CSR

  • React는 기본적으로 클라이언트 사이드 렌더링이다. React-Router로 path이동을 시키는것처럼 보여지지만, 그냥 이름만 지어줄 뿐 그 사실이 변하지 않는다.
  • 사용자가 최초 접속 이후 서버에 요청을 하면, build된 index.html파일을 보내준다.
  • 그 html파일에는 번들링된 js나 css파일이 연결되어있는 상태이다.
  • html파일이 읽히면서, js와 css 파일이 작동이 된 이후, React-Router가 작동되는 형식이다. 즉, path이동과 routing은 작동을 하지만 모든것이 클라이언트 사이드에서 이뤄지고 서버는 아무런 관계가 없다.
  • 만약, 이 상태에서 클라이언트 사이드에서 Routing이 이뤄지고, 새로고침을 하면 클라이언트에서 서버에 새롭게 파일을 요청하게 되는데, 이미 url은 React-Router의 path로 얼룩져 있는 상태이기 때문에, 서버는 그것이 무엇을 반환해달라는 말인지 알 지 못한다. 따라서 404 Not Found 에러를 반환하는것이다. 당연히, 아무런 path가 없는 첫 화면은 ’/’ 상태이기 때문에 index.html을 잘 받아와 에러가 생기지 않는 것이다.

😄 해결방법

HashRouter

  • 가장 쉬운 방법으로는 BrowserRouter -> HashRouter 변경하는것이다.
  • 서버가 해당 요청 url을 읽을 때, # 뒤로는 인식(?)하지 못한다고 한다. 따라서 / 만 읽고, index.html을 잘 받아오는것이다.

근데 중요한점은 HashRouter를 쓰면 url앞에 /#/가 들어가게되는데 이게 너무 싫다.

index.html 리다이렉션 - 깃허브

  • 깃허브페이지는 서버에서 404에러를 반환했을 때, 깃허브에서 기본적으로 제공하는 404.html파일을 출력시키도록 되어있다.
  • 따라서 우리는 build될 때, 직접 만든 404.html파일또한 포함되게 하면 되는데, 해당 html은 즉시실행 메소드로 현재의 url에서 router로 인해 생성된 path 앞에 서버가 읽지 못하는 /?/를 포함시켜 window.location.replace로 해당 주소로 이동시킨다.
  • 당연히 서버는 ? 이후는 읽지 못하고 앞의 깃허브페이지 메인 주소만 읽고 index.html을 반환하고, 반환되는 index.html파일에서 ? 등의 불필요한 부분을 지워주는 메소드가 가장 먼저 실행되도록 한다(main script가 읽히기 전). 그 이후로 js파일의 React-Router가 작동되어 원하는 화면을 볼 수 있게된다.
  • 404->index.html

index.html 리다이렉션 - S3, CloudFront

  • AWS의 S3나 CloudFront는 서버가 해당 url에대한 적절한 반환값을 찾지 못하였을 때(404, 403 등등) 리다이렉션을 시켜줄 파일을 지정해 줄 수 있었다.
  • 스크립트 내부에서 작동하는 http통신과는 다른문제 인듯 함! 애초에 스크립트 파일이 읽힌다는것 자체가 첫 화면으 뿌려주는데에는 문제가 없었고, js내부에서 요청을 할 때 발생하는 문제이기 때문에 내부적으로 처리해야하는것 같음.
  • EC2로 서버도 같이올린다면. 서버에서 인식할 수 없는 주소로 요청이왔을때 404.html이 아닌 그냥 index.html을 반환하게하면 스크립트가 실행될 때 자연스럽게 react-router가 작동되겠지?

SSR은 별개의 이야기

  • 하지만 SSR은 CSR과 다르게 서버에서 View를 구성한 다음 클라이언트에게 완성된 View를 보내준다.
  • 즉, 새로고침을 하였을 때 CSR에서의 서버는 그 path가 뭔지 몰라 아무것도 보내주지 못하지만, SSR에서의 서버는 자기가 만들었던 View이기 때문에, 그 path를 읽고 다시 View를 보내줄 수 있는 것 이다.

지금까지 본 글들을 읽어보면, 내 생각에는 이런것 같다.


@SangMin
👆 H'e'story

🚀GitHub