nuxt.js 시작하기 - 004
쇼핑 상품 목록 페이지와 상세 페이지 개발
상품 목록 표시 기능 구현
pages/main.vue
<template>
<div>
<p>상품 페이지입니다</p>
<div>
<ul>
<li v-for="product in products" :key="product.id">
<img :src="product.imageUrl" :alt="product.name" />
<p></p>
<p></p>
</li>
</ul>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
async asyncData() {
const response = await axios.get('http://localhost:3000/products')
const products = response.data.map((item) => {
return {
...item,
imageUrl: `${item.imageUrl}?random=${Math.random()}`,
}
})
return { products }
},
data() {
return {
products: [],
}
},
async created() {},
}
</script>
<style>
</style>
template 에 상품을 보기좋게 표시하도록 ul 로 정리함
각 항목별로 다른 이미지가 나오도록 수정하기 위해 map 을 이용하여 상품데이터의 imageUrl 을 변형
라우팅 및 스타일링 정리
pages/main.vue 의 내용을 pages/index.vue 로 복사하고 main.vue 는 삭제
pages/main.vue 수정
<template>
<div class="app">
<main>
<div>
<input type="text" />
</div>
<ul>
<li class="item flex" v-for="product in products" :key="product.id">
<img
class="product-image"
:src="product.imageUrl"
:alt="product.name"
/>
<p></p>
<span></span>
</li>
</ul>
</main>
</div>
</template>
...
<style scoped>
.flex {
display: flex;
justify-content: center;
}
.item {
display: inline-block;
width: 400px;
height: 300px;
text-align: center;
margin: 0 0.5rem;
cursor: pointer;
}
.product-image {
width: 400px;
height: 250px;
}
.app {
position: relative;
}
.cart-wrapper {
position: sticky;
float: right;
bottom: 50px;
right: 50px;
}
.cart-wrapper .btn {s
display: inline-block;
height: 40px;
font-size: 1rem;
font-weight: 500;
}
</style>
layouts/default.vue 수정
<template>
<div>
<header>
<h1>Nust Shopping</h1>
</header>
<Nuxt />
</div>
</template>
...
상품 상세 페이지 구현을 위한 사전 작업
pages/index.js 의 상품리스트에 클릭이벤트, 메소드 추가
<template>
...
<ul>
<li
v-for="product in products"
:key="product.id"
class="item flex"
@click="moveToDetailPage(product.id)"
>
...
</template>
...
<script>
...
methods: {
moveToDetailPage(id) {
console.log(id)
},
},
</script>
상품 li 선택 시 상품 id 가 콘솔에 표시됨
Nuxt 의 동적 라우팅
pages/index.vue 수정
<script>
...
methods: {
moveToDetailPage(id) {
console.log(id)
this.$router.push(`detail/${id}`)
},
}
...
</script>
detail/${id} 페이지는 pages/detail/_id.vue 로 라우팅 할 수 있음
pages/detail/_id.vue 생성
<template>
<div><h1>상세 페이지</h1></div>
</template>
<script>
export default {
created() {
console.log(this.$route.params)
},
}
</script>
<style>
</style>
상품 상세 정보 조회 기능 개발
api 함수 모듈화 api 파일 생성
api/index.js 생성
import axios from 'axios'
const instance = axios.create({
baseURL: 'http://localhost:3000/',
})
function fetchProductById(id) {
return instance.get(`/products/${id}`)
}
export { fetchProductById }
pages/detail/_id.vue 에 fetchProductById 를 이용하여 데이터 가져와서 표시
<template>
<div>
<h1>상세 페이지</h1>
<div>
<img :src="product.imageUrl" :alt="product.name" />
<p>name : </p>
<span>price : </span>
</div>
</div>
</template>
<script>
import { fetchProductById } from '@/api/index'
export default {
async asyncData({ params }) {
const response = await fetchProductById(params.id)
const product = response.data
return { product }
},
}
</script>
<style>
</style>
context 속성 안내
asyncData 의 context 속성
https://joshua1988.github.io/vue-camp/nuxt/data-fetching.html#asyncData의 파라미터
https://nuxtjs.org/docs/internals-glossary/context/
상품 상세 페이지 레이아웃 정리 및 전역 스타일시트 설정
전역 스타일시트 파일의 위치는 nuxt.config.js 에서 설정가능함
nuxt.config.js 수정
...
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
'@/assets/css/reset.css'
],
...
learn-nuxt 의 assets/css/reset.css 파일을 복사하여 프로젝트 폴더로 붙여넣기
https://github.com/joshua1988/learn-nuxt/blob/master/assets/css/reset.css
상세페이지 레이아웃 정리
learn-nuxt 의 상세페이지 파일 참고하여 작성
// pages/detail/_id.vue
<template>
<div>
<div class="container">
<div class="main-panel">
<img
class="product-image"
:src="product.imageUrl"
:alt="product.name"
/>
</div>
<div class="side-panel">
<p class="name"></p>
<p class="price"></p>
<button type="button" @click="addToCart">Add to Cart</button>
</div>
</div>
</div>
</template>
<script>
import { fetchProductById } from '@/api/index'
export default {
async asyncData({ params }) {
const response = await fetchProductById(params.id)
const product = response.data
return { product }
},
}
</script>
<style scoped>
.container {
display: flex;
justify-content: center;
margin: 2rem 0;
}
.product-image {
width: 500px;
height: 375px;
}
.side-panel {
display: flex;
flex-direction: column;
justify-content: center;
width: 220px;
text-align: center;
padding: 0 1rem;
}
</style>