Skip to content

URL Parameter Routing 트러블 슈팅

Choi siun edited this page Dec 7, 2022 · 3 revisions

URL Parameter Routing 트러블 슈팅

  • 해당 글은 노션 환경에서 작성한 것으로 노션 링크를 통해 보시면 더욱 편리합니다.

노션 링크 : URL Parameter routing 트러블 슈팅

작성자 : 최시운
작성일시: 221207
주제: URL Parmeter Routing 

개요

프로필 레이아웃을 만들고 URL Parameter를 사용해서 파라미터에 해당하는 유저 프로필 페이지를 보여주고자 했습니다.

그래서 기존 App.tsx의 라우팅을 아래와 같이 설정해서 중첩 라우팅이 가능하게 하였습니다.

            <Route path={`${RoutePath.PROFILE}`}>
              <Route path=":userId" element={<ProfilePage />} />
              <Route path="" element={<ProfilePage />} />
            </Route>

구현 방식으로는 뒤에 파라미터가 붙지 않는 디폴트 페이지로 이동시

const { userId } = useParams();
  const profileUserId = userId ? 
		parseInt(userId as string, 10) : authStorage.get();

//authStorage.get()은 현재 로그인한 유저의 id를 가져오는 코드.

해당 코드를 사용해서 파라미터가 있으면 파라미터의 유저 아이디를, 없으면 authStorage.get을 통해 현재 로그한 유저의 아이디를 profileUserId로 설정하게 하였습니다. 1

그 결과 현재 로그인한 ‘시운2’의 마이페이지를 들어갈 때는 예상했던 동작이 작동했고 이 때까지는 행복했습니다…

그러나 문제는 새로고침을 할 때 일어나는데..


문제의 발생

새로고침

새로고침시 예상했던 결과와 달리 아래와 같은 오류가 뜨며 페이지가 라우팅되지 않았고, 이에 대한 문제를 해결하고자 에러코드를 쫓아가봤습니다. 2

refused to execute script from 'http://localhost:3000/profile/vendors-node_modules_dayjs_dayjs_min_js-node_modules_dayjs_plugin_weekofyear_js-node_modules_-d7fb26.js' because its mime type ('text/html') is not executable, and strict mime type checking is enabled.

refused to execute script from 'http://localhost:3000/profile/index.js' because its mime type ('text/html') is not executable, and strict mime type checking is enabled.


문제 원인 파악

문제가 발생한 원인. index.js

처음 눈에 들어온 것은 해당 에러였습니다.

refused to execute script from 'http://localhost:3000/profile/index.js' because its mime type ('text/html') is not executable, and strict mime type checking is enabled.

‘ 결국 index.js를 찾지 못해서 띄우지 못하는건가..? 근데 index.js는 어디서 나온걸까..?’

라는 고민과 함께 웹팩을 뜯어봤습니다.

entry: {
    index: path.resolve(__dirname, "./src/index"),
  },

  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist"),
    clean: true, // CleanWebpackPlugin 대체
  },

그리고 그 결과 output에서 생성되는 친구인걸 알았고, 새로고침 시, 빌드 과정에서 문제가 있었고 상위 에러에서 문제가 발생했기 때문에, index.js가 생성되지 않고, 생성되지 않아 에러가 발생한다는 이유를 깨달았습니다.


사실 어디서 발생되는지 몰라서 이런식으로 확인했습니다..

3

  • 여기에 index.js에 1붙으면 얘가 범인

4

  • 범인

그리고 index.js를 불러오지 못하는 까닭은 이전 스크립트를 불러오는 과정에서 생기는 에러인

refused to execute script from 'http://localhost:3000/profile/vendors-node_modules_dayjs_dayjs_min_js-node_modules_dayjs_plugin_weekofyear_js-node_modules_-d7fb26.js' because its mime type ('text/html') is not executable, and strict mime type checking is enabled.

이것이 원인이라는 것을 알았습니다.

자바스크립트는 인터프리터 언어이기 때문에 다음과 같은 코드에서 node_modules가 빌드된 결과를 먼저 불러오고, 다음에 index.js를 불러오는데, 먼저 불러오는 node_modules에서 에러가 터져서 뒤에 index.js를 불러오지 못했겠구나..라는 생각이 들었습니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--    <base href="/" />-->
    <title>Fitory</title>
  <script defer src="vendors-node_modules_dayjs_dayjs_min_js-node_modules_dayjs_plugin_weekofyear_js-node_modules_-d7fb26.js"></script><script defer src="index.js"></script></head>
  <body>
    <div id="root"></div>
  </body>
</html>

왜 에러가 나는걸까?

저는 원인을 처음에 중첩라우팅에서 찾았습니다.

<Route path={`${RoutePath.PROFILE}`}>
	<Route path=":userId" element={<ProfilePage />} />
	<Route path="" element={<ProfilePage />} />
</Route>
  • 만일 현재 주소가 “https://fitory.ga/profile/1” 일 때, 새로고침을 하게 되면 라우터에 이에 맞는 주소가 없으니 뜨지 않는게 정상이다.
  • 본래 이동이 됐던 까닭은 navigate를 통해 이동할 때, Route path=":userId" element={<ProfilePage />} />를 통해 라우팅 시켜준 것이지 https://fitory.ga/profile/1이라는 주소 자체가 있는게 아니기 때문이다.
  • 이를 해결하기 위해 webpack에서 historyApiFallback : true를 적용하였습니다.
devServer: {
    static: path.resolve(__dirname, "dist"),
    historyApiFallback: true, // history api route에 대한 fallback
  },

[historyApiFallback이란? (webpack.config.js)](https://basemenks.tistory.com/270)

그러나

오류는 해결되지 않았고 동일한 에러가 반복되었습니다.

5

다양한 고민과 토론의 결과에서 원인은 script src에서 노드 모듈이 번들링된 파일을 찾지 못하기 때문이라는 결론을 내렸습니다.

네트워크 탭에서 동작 확인

그래서 Network 탭으로 가서 해당 파일을 어디서 가져오는지를 확인했습니다.

정상적으로 작동할 경우

6

Request Url : http://localhost:3000/vendors-node_modules_gapi-script_index_js-node_modules_react-dom_client_js-node_modules_react-95958d.js

문제가 생긴 페이지의 경우

7

Request Url : http://localhost:3000/profile/vendors-node_modules_gapi-script_index_js-node_modules_react-dom_client_js-node_modules_react-95958d.js

문제가 발생한 원인. Node_modules

번들링된 Node_modules는 localhost:3000/ 루트에 있는데, profile에서 요청이 되었기 때문에 받아오지 못했던 것입니다.

이를 확인하기 위해 빌드 과정에서의 콘솔을 살펴봤고

[webpack-dev-server] Content not from webpack is served from '/Users/choisiun/Documents/Project/Web04-Fitory/client/dist/profile' directory 8

Entrypoint index 12.6 MiB (2.64 MiB) = vendors-node_modules_gapi-script_index_js-node_modules_react-dom_client_js-node_modules_react-95958d.js 12.1 MiB index.js 550 KiB index.013c35f8dfc93839dcdc.hot-update.js 556 bytes 27 auxiliary assets 9

노드 모듈은 엔트리포인트에 있고, profile에서 새로고침할 때 참조하는 profile에는 content가 없다.

이 과정에서 profile에서는 node_module이 번들링된 파일을 불러올 수 없겠다라고 이해했습니다.

💡 그럼 다른 페이지는 왜 작동하나요? ex)`http://localhost:3000/challenge`

챌린지의 경우 이동하는 코드는 다음과 같습니다.

 <Routes>
            {/* Home */}
            <Route path={RoutePath.HOME} element={<HomePage />} />
            <Route path={RoutePath.CHALLENGE} element={<ChallengePage />} />
            <Route path={RoutePath.RECORD} element={<RecordPage />} />
            {/* Search */}
            <Route path={RoutePath.SEARCH} element={<SearchPage />} />
            {/* Statics */}
            <Route path={RoutePath.STATICS} element={<StaticsPage />} />
            {/* Profile */}
            <Route path={`${RoutePath.PROFILE}`}>
              <Route path=":userId" element={<ProfilePage />} />
              <Route path="" element={<ProfilePage />} />
            </Route>
            <Route path={RoutePath.LOGIN} element={<LoginPage />} />
            <Route path={RoutePath.JOIN} element={<JoinPage />} />
            <Route path={RoutePath.SEARCH} element={<SearchPage />} />
            <Route path={RoutePath.FOLLOW} element={<FollowPage />} />
</Routes>
<s.ChallengeButton type="button" onClick={() => navigate(RoutePath.CHALLENGE)}>

navigation으로 이동하는 위치의 경우 라우터에 주소가 명시되어 있기 때문에 새로고침시에도 라우팅이 되어 문제가 발생하지 않습니다.

그러나 문제가 되었던 주소 Route.Path.PROFILE/300과 같은 경우 라우터에 주소 자체가 명시되어있지 않기 때문에 문제가 발생했습니다.


이를 해결하기 위해.

두가지 방법이 있었습니다.

  1. index.html에 base 경로을 설정하여 경로를 고정
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <title>Fitory</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
<script>
  console.log("./");
</script>
</html>
  1. Webpack에서 publicPath를 설정하여 경로를 고정
output: {
    filename: "[name].js",
    publicPath: "/",
    path: path.resolve(__dirname, "dist"),
    clean: true, // CleanWebpackPlugin 대체
 },

결국 해결방법은 둘 다 동일하게 경로를 가장 Root로 설정하여 어디에 위치하건 스크립트를 참조할 때, /를 기준으로 탐색하게 하는 것이었습니다.

어느 위치에 있더라도 /를 기준으로 탐색하면 노드 모듈이 번들링된 위치인 http://localhost:3000/노드 모듈 번들링 결과.js에서 참조할 수 있기 때문에 문제가 해결되었습니다.


트러블 슈팅 과정에서 학습한 것

  • 웹팩과 웹 작동 방식에 대해서 학습하게 되었습니다.

문제를 해결하기 위해 가장 기본적인 질문, 원래는 index.html에 없던 script가 왜 생기는지, 갑자기 스크립트 태그 안에 나타난 defer란 무엇인지에 대해서 공부할 수 있는 계기가 되었습니다.

더불어 웹팩의 공식문서를 보면서 이전에는 단순히 쓸만한 기능을 가져오는데서 그쳤다면, 이제는 어떻게 작동하고 각각이 어떠한 기능을 하는지를 이해했습니다.

  • 공식 문서를 보는 능력이 성장하였습니다.

해당 문제를 해결하기 위해 다양한 구글링 키워드를 사용했습니다.

  1. react router refresh
  2. 중첩 라우터 새로고침
  3. etc…

등등 다양한 키워드를 검색해보았지만 잘 나오지 않았고, 그렇기 때문에 공식문서를 보았습니다.

그 과정에서 react-router-v6, webpack의 공식문서를 확인했고, 그 과정에서 몰랐던 사실, 앞으로 유용하게 사용할 법한 기능들에 대해서도 학습하였습니다.

  • 문제를 찾는 능력이 향상되었습니다.

문제를 해결하기 위해 이전에는 단순히 콘솔을 찍어보고, 거기서 나오는 오류들을 확인하면서 예상을 했고, 네트워크 탭도 단순히 서버에서 주는 http 상태코드만 보았던 경우가 많았습니다.

그러나 이번 기회를 통해 조금 더 자세히 들여다 보게 되었고, 이것이 강력한 무기가 될 수도 있다는 것을 깨달았습니다.


후기

이번 트러블 슈팅 과정에서 정말 오랜시간을 썼던 것 같습니다.

검색이 잘 되지 않은 것도 있고, 사실 index.html은 잘 들여다보지도 않을 뿐 더러 webpack도 초기 설정을 하고 들여다 보지 않았는데, 이번 기회를 통해 리액트에서 매몰되는 것에서 살짝 벗어나고 조금 더 넓은 시야를 가질 수 있었습니다.


참고자료

[Refused to execute script from because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled]

[MIME type ('text/html') is not executable, and strict MIME type checking is enabled]

[MIME-Type,Content-Type이란?]

[DevServer | webpack]

[Public Path | webpack]

[Route v6.4.4]

[React Router v6 튜토리얼]

[HTML 페이지에 자바스크립트 코드와 링크를 넣는 위치 선정과 실행 순서의 이해]

💻 Projects

🤝 Rules

🎙️ Meeting

👾 Trouble Shootings

🛠 Tech Semina

🔰 초심자를 위한 기술 가이드

🏃‍♂️ Sprint

✏️ Reviews

💎 Mentoring

💬 Scrums

Week 1
Week 2
Week 3
Week 4
Week 5
Week 6
Clone this wiki locally