[연습장] Vue3 : API를 통한 영화 목록을 만들어보자. (1) 메인화면 생성
node.js를 통해 Vue프로젝트도 만들어봤고, 부트스트랩, 웹팩에 대해 알아봤으니, 이제 이것들 복습하는 기분으로
API를 통해 영화정보를 받아와 게시판을 만드는 연습을 해보려 한다. router은 나중에...
아마 길어질 것 같아서 연습장이라는 카테고리도 만들어 나눠 놓았다. 처음이라 잘못 된 게 많아도 이해하길.. 연습이라..
참고로 SPA형식으로 만들 예정이다. 실무에서는 그렇게 사용하지 않는다지만 아직 Vue프로젝트 구조를 완전히
이해 하지 못한 상태라.. 그럼 시작해보자
프로젝트 폴더 생성
============================================================================
Vue 프로젝트를 생성 할 폴더를 생성해주자.
프로젝트 폴더 이동 및 프로젝트 생성
============================================================================
VScode를 켜고, 생성한 폴더를 열어준다. 파일 -> 폴더열기를 통해 열어주면 된다.
그럼 아래와 같이 내가 생성한 폴더가 좌측에 뜨는 것을 확인 할 수 있다.
그리고 터미널을 열어 Vue 프로젝트를 생성해준다. ( Vue 개발환경이 설치 되었다는 가정하에 진행한다. )
vue create [프로젝트명]
vue create movie-project -- 내가 만든 프로젝트
그럼 아래와 같이 Vue 버전을 선택할 수 있는데, 나는 Vue3 버전을 선택하도록 하겠다.
생성이 완료 되면 기존 movie-project 폴더안에 movie-project라는 프로젝트가 생성 된 것을 확인 할 수 있다.
그럼 이제 화면을 만들어 보도록 하자. 기존의 Vue프로젝트 구조를 활용하려고 한다. 화면을 만들기 위해
부트스트랩을 활용하여 화면을 만들겠다
부트스트랩 설치 및 적용
============================================================================
아래 명령어를 입렵해 부트스트랩을 설치한다.
npm install --save bootstrap
그럼 package.json에 부트스트랩이 설치 된 것을 확인할 수 있다.
이제 main.js에 아래 css와 라이브러리를 import한다.
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
이렇게 부트스트랩까지 적용하였으니, 우선 화면을 꾸며보자. 아래 링크에서 원하는 디자인을 가져온다.
https://getbootstrap.kr/docs/5.0/getting-started/introduction/
시작하기
세계에서 가장 인기있는 프레임워크인 Bootstrap으로 반응형, 모바일 우선 사이트를 jsDelivr와 스타터 페이지 템플릿을 통해 시작해보세요.
getbootstrap.kr
예제 템플릿 같은 거에서 소스 가져오자. 아래는 내가 조립한 소스.
MovieList.vue
<template>
<div>
<section class="py-5 text-center container">
<div class="row py-lg-5">
<div class="col-lg-6 col-md-8 mx-auto">
<h1 class="fw-light">Album example</h1>
<p class="lead text-muted">Something short and leading about the collection below—its contents, the creator, etc. Make it short and sweet, but not too short so folks don’t simply skip over it entirely.</p>
<p>
<a href="#" class="btn btn-primary my-2">Main call to action</a>
<a href="#" class="btn btn-secondary my-2">Secondary action</a>
</p>
</div>
</div>
</section>
<div class="album py-5 bg-light">
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card shadow-sm">
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
MoiveHeader.vue
<template>
<div>
<div class="collapse bg-dark" id="navbarHeader">
<div class="container">
<div class="row">
<div class="col-sm-8 col-md-7 py-4">
<h4 class="text-white">About</h4>
<p class="text-muted">Add some information about the album below, the author, or any other background context. Make it a few sentences long so folks can pick up some informative tidbits. Then, link them off to some social networking sites or contact information.</p>
</div>
<div class="col-sm-4 offset-md-1 py-4">
<h4 class="text-white">Contact</h4>
<ul class="list-unstyled">
<li><a href="#" class="text-white">Follow on Twitter</a></li>
<li><a href="#" class="text-white">Like on Facebook</a></li>
<li><a href="#" class="text-white">Email me</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="navbar navbar-dark bg-dark shadow-sm">
<div class="container">
<a href="#" class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="me-2" viewBox="0 0 24 24"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
<strong>Album</strong>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</div>
</template>
MovieFooter.vue
<template>
<footer class="text-muted py-5">
<div class="container">
<p class="float-end mb-1">
<a href="#">Back to top</a>
</p>
<p class="mb-1">Album example is © Bootstrap, but please download and customize it for yourself!</p>
<p class="mb-0">New to Bootstrap? <a href="/">Visit the homepage</a> or read our <a href="/docs/5.1/getting-started/introduction/">getting started guide</a>.</p>
</div>
</footer>
</template>
App.vue
<template>
<div>
<MovieHeader />
<MovieList />
<MovieFooter/>
</div>
</template>
<script>
import MovieList from './components/MovieList.vue'
import MovieHeader from './components/MovieHeader.vue'
import MovieFooter from './components/MovieFooter.vue'
export default {
name: 'App',
components: {
MovieList,
MovieHeader,
MovieFooter
}
}
</script>
부트스트랩을 이용했기 때문에 따로 style태그는 사용하지 않았고, 기능적인 면도 만들지 않았기 때문에
import를 하고 있는 App.vue를 제외하고는 <script>도 사용하지 않았다. 정말로 화면만 만든 것이다.
MovieList.vue는 코드가 조금 긴데, 어차피 나중에 영화 정보를 받아오면 v-for문을 돌려줄거기 때문에
짧아질거다. 그럼 결과 화면을 보도록 하자.
예쁘다. 있는 거 그대로 가져왔으니, 안예쁠 수가 없지. 그럼 다음은 API를 통해 데이터를 가져와 각 패널?에 넣는 것까지 해보도록 하자.