메인 페이지의 본문 영역을 Bootstrap의 Card(카드)를 사용하여 콘텐츠(게시물)가 나타나게 구성하겠습니다.
먼저 Bootstrap의 Grid System(그리드 시스템)을 사용하여 Row columns(행(row)에 대한 열(col))을 설정합니다.
1. C:\workspaces\nodeserver\testvue\src\views\Home.vue 파일을 오픈합니다.
<template> 태그에서 메인 페이지 상단 영역 다음에 콘텐츠를 표시하기 위한 영역으로 <div> 태그를 추가합니다. Bootstrap의 Grid System(그리드 시스템)을 사용하여 반응형으로 웹 브라우저의 가로 화면의 크기에 따라 행(row)에 몇 개의 콘텐츠가 나타나게 할지 설정합니다.
<div class="py-5">
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
<div class="col" style="padding:100px 100px; border:solid 1px black; background-color:white;">
col1
</div>
<div class="col" style="padding:100px 100px; border:solid 1px black; background-color:white;">
col2
</div>
<div class="col" style="padding:100px 100px; border:solid 1px black; background-color:white;">
col3
</div>
</div>
</div>
</div>
화면에 영역을 보이기 위해서는 <div>에 style 속성과 텍스트를 추가했습니다. 테스트를 위해 추가한 것으로 테스트 후 삭제하시기 바랍니다.
row-cols-md-3은 가로 화면 크기가 768px보다 크거나 같으면 행(row)에 열(col)이 3개씩 배치됩니다.
row-cols-sm-2는 가로 화면 크기가 576px보다 크거나 같으면 행(row)에 열(col)이 2개씩 배치됩니다.
row-cols-1은 행(row)에 열(col)이 1개씩 배치됩니다.
웹 브라우저의 가로 화면 크기가 768px 이상일 때는 한 행(row)에 열(col)이 3개씩 배치됩니다.
웹 브라우저의 가로 화면 크기가 576px 이상일 때는 한 행(row)에 열(col)이 2개씩 배치됩니다.
모바일 사이즈(웹 브라우저의 가로 화면 크기가 576px 이하이면)에서는 한 행(row)에 열(col)이 1개만 배치됩니다.
Bootstrap - Card
Bootstrap의 Card(카드)는 기본적으로 카드 보디(card-body)에 카드 타이틀(card-title)과 카드 텍스트(card-text)로 구성됩니다. 그리고 이미지를 카드 보디의 상단이나 하단 또는 사이드에 배치시킬 수 있습니다.
그리고 카드 헤더(card-header)나 카드 푸터(card-footer)를 포함해서 구성할 수 있습니다.
<div class="card">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Card Content</p>
</div>
</div>
<div class="card">
<img class="card-img-top" src="..." alt="...">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Card Content</p>
<a href="#" class="btn btn-primary">Link</a>
</div>
</div>
<div class="card">
<div class="card-header">Card Header</div>
<img class="card-img-top" src="..." alt="...">
<div class="card-body">
<h5 class="card-title">Card Title</h5>
<p class="card-text">Card Content</p>
</div>
<div class="card-footer">Card Footer</div>
</div>
현재 Node.js 레스트 API 서버에는 이미지가 업로드 기능이 없고 제목과 내용만 있는 간단한 레스트 API 서버입니다.
그래서 Bootstrap Card(카드)로 카드 타이틀(card-title)과 카드 서브타이틀(card-subtitle), 링크, 날짜로 구성하겠습니다.
2. C:\workspaces\nodeserver\testvue\src\views\BoardList.vue 파일을 오픈하여 <script>에서 data와 methods에 있는 getBoardList() 메서드를 복사하여 Home.vue 파일의 <script>에 추가합니다.
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue';
import * as bootstrap from 'bootstrap';
export default {
name: 'Home',
components: {
HelloWorld
},
data : function() {
return {
boardList : []
};
},
methods : {
mainTopCarouselPrevClick() {
var mainTopCarousel = document.querySelector('#mainTopCarousel');
var carousel = bootstrap.Carousel.getInstance(mainTopCarousel);
carousel.prev();
},
mainTopCarouselNextClick() {
var mainTopCarousel = document.querySelector('#mainTopCarousel');
var carousel = bootstrap.Carousel.getInstance(mainTopCarousel);
carousel.next();
},
mainTopCarouselPlayPauseClick(event) {
var mainTopCarousel = document.querySelector('#mainTopCarousel');
var carousel = bootstrap.Carousel.getInstance(mainTopCarousel);
if (mainTopCarousel.classList.contains("pause")) {
mainTopCarousel.classList.remove("pause");
event.target.classList.remove("bi-play-fill");
event.target.classList.add("bi-pause-fill");
carousel.cycle();
} else {
mainTopCarousel.classList.add("pause");
event.target.classList.remove("bi-pause-fill");
event.target.classList.add("bi-play-fill");
carousel.pause();
}
},
getBoardList() {
this.axios.get("http://localhost:9000/boards").then((res)=>{
console.log(res);
this.boardList = res.data.data;
}).catch((err) => {
console.log(err);
});
}
},
mounted() {
this.getBoardList();
}
};
</script>
향후 Node.js 레스트 API 서버에 기능을 추가하여 변경하도록 하겠습니다.
<template>에서 추가한 콘텐츠 영역 부분을 <script>의 data에 선언된 배열 객체(boardList)를 이용하여 열(col)를 생성하게 변경합니다.
<div class="py-5">
<div class="container text-start">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
<div class="col" v-for="boardItem in boardList" v-bind:key="boardItem.no">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{boardItem.subject}}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{boardItem.writer}}</h6>
</div>
</div>
</div>
</div>
</div>
</div>
현재 배열 객체(boardList)에는 콘텐츠 내용이 없기 때문에 카드 텍스트(card-text)는 사용하지 않았습니다.
그리고 카드 서브타이틀(card-subtitle)을 사용하여 작성자 아이디가 표시되도록 하였습니다.
3. 카드 보디(card-body)에 보기 버튼과 작성일자를 추가합니다.
<div class="py-5">
<div class="container text-start">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
<div class="col" v-for="boardItem in boardList" v-bind:key="boardItem.no">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{boardItem.subject}}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{boardItem.writer}}</h6>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary" @click="boardNoClick(boardItem)">보기</button>
</div>
<small class="text-muted">{{boardItem.writedate}}</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
.justify-content-betweend으로 버튼과 작성일자는 양쪽으로 정렬됩니다. (.justify-content-betweend은 justify-content: space-between 과 동일합니다.)
버튼을 .btn-group 안으로 추가한 이유는 수정 버튼을 추후에 추가하기 위해서입니다.
BoardList.vue 파일의 <script>에서 methods에 boardNoClick() 메서드를 Home.vue 파일의 <script>에 추가합니다.
boardNoClick(boardItem) {
this.$router.push({name : 'BoardView', query : {boardNo : boardItem.no}});
}
보기 버튼을 클릭하면 기존의 게시판의 뷰 페이지로 이동합니다.
모바일 사이즈에서는 다음과 같이 나타납니다.