SPA가 S3 Static hosting에서 404 error 낼 때

Sungyong
4 min read6 days ago

--

AWS S3의 좋은 기능 중 하나가 Static hosting 기능이다. Vue나 React로 만든 SPA를 별도의 web server 사용하지 않고 S3에 올리고, 그걸 Static hosting하면 웹페이지를 서비스를 할 수 있다.

이 때 흔히 겪게 되는 문제가 F5 키 눌러 page refresh했을 때 다음과 같이 404 error 나는 것이다.

이유는 S3 static hosting은 client side routing을 지원하지 않기 때문이다. 즉 URL 그대로 보고 해당 경로에 없으면 404 error를 내는 것이다.

해결 방법은 S3 설정과 SPA 설정 두군데 동시에 해주면 된다.

먼저 S3단에서 할 것은 error handling을 client가 하도록 바꾸는 것이다.

bucket 설정“Error document” field를 “index.html”로 바꿔서 SPA에서 처리하도록 하는 것이다.

테라폼 설정 코드로 나타내면 다음과 같다.

resource "aws_s3_bucket_website_configuration" "website_config" {
bucket = aws_s3_bucket.website_bucket.id
index_document {
suffix = "index.html"
}
error_document {
key = "index.html" # Changed from "error.html" to "index.html"
}
}

그 다음 SPA에서 설정이다. 내가 사용하는 Quasar Framework이다.

routes/routes.js 를 아래처럼 수정해서 error page 보여줄 지 여부를 MainLayout에서 결정하도록 한다.

  {
path: "/:catchAll(.*)*",
component: () => import("pages/Error404.vue"),
},


=>


{
path: "/:catchAll(.*)*",
component: () => import("layouts/MainLayout.vue"),
},

마지막으로 MainLayout.vue에 routing 할 곳이 없을 때 NotFound.vue를 rendering하도록 하면 되겠다.

<template>
<q-layout>
<q-page-container class="bg-grey-2">
<router-view v-if="routeExists" />
<NotFound v-else />
</q-page-container>
</q-layout>
</template>

<script>
import { defineComponent, ref, computed } from "vue";
import { useQuasar } from "quasar";
import { useRoute, useRouter } from 'vue-router';
import NotFound from 'components/NotFound.vue';

export default defineComponent({
name: "CescoLayout",
components: { NotFound },

setup() {
const leftDrawerOpen = ref(false);
const q = useQuasar();
const route = useRoute();
const router = useRouter();

const routeExists = computed(() => {
const currentPath = route.path;
return router.getRoutes().some(r => {
const regexPath = r.path.replace(/:\w+(\?)?/g, '([^/]+)$1');
const routeRegex = new RegExp(`^${regexPath}$`);
return routeRegex.test(currentPath);
});
});

return {
q,
leftDrawerOpen,
routeExists,
toggleLeftDrawer() {
leftDrawerOpen.value = !leftDrawerOpen.value;
},
};
},
});
</script>

--

--