- 본 포스팅은 아래의 프로젝트를 기반으로 다뤄진 내용입니다.
https://devraphy.tistory.com/235
1. 개요
- 본 프로젝트는 Vue3 강의에서 진행한 내용을 그대로 클론코딩한 프로젝트이다.
- 강의내용을 따라서 프로젝트를 만들다 보니, 코드와 데이터 흐름에 대한 이해가 부족함을 느꼈다.
- 이와 같은 이유로 해당 프로젝트의 핵심 기능을 분석해보려고 한다.
2. 프로젝트의 구조
- 전체적인 코드의 흐름을 분석해본 결과, Vue 프로젝트의 핵심은 main.js 라는 것을 알게 되었다.
- main.js가 하는 역할은 다음과 같다.
▶ index.html과 App.vue(뷰 어플리케이션) 연결한다.
▶ Vue 어플리케이션과 Vue 어플리케이션 개발에 필요한 Router, Store 등의 다양한 Plugin과 연결한다.
- main.js의 코드는 다음과 같다.
import {createApp} from 'vue'
import App from './App'
import router from './routes/index.js'
import store from './store'
import loadImage from './plugins/loadImage'
createApp(App)
.use(router) // 플러그인 $route, $router
.use(store) // 플러그인 $store
.use(loadImage) // 플러그인 $loadImage
.mount('#app')
a) createApp(App)
- Vue 어플리케이션 인스턴스(=객체)를 생성하는 함수다.
- 이 함수를 통해, 하나의 뷰 어플리케이션을 생성한다.
- 쉽게 말해서, Vue 프로젝트가 생성되었음을 의미하는 것이다.
b) mount('#app')
- mount가 무슨 의미일까? UFC 경기를 본적이 있다면 한번쯤은 들어봤을 것이다. Mount 포지션을 말이다.
- 이처럼 어떤 대상 위에 올라간다는 의미를 마운트라고 한다. 그렇다면 Vue 객체가 어디에 올라가는 것일까?
- '#app' 이 부분에 올라간다는 의미다. 즉, app이라는 id를 갖고있는 HTML 엘리먼트에 올라간다는 뜻이다.
- 그럼 'app'이라는 id값을 가진 HTML 엘리먼트는 어디 있을까? index.html을 살펴보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello Vue Project!</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css"
/>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@500&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
line-height: 1.4;
font-family: "Roboto", sans-serif;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
</html>
- 가장 아래쪽에 body부분을 살펴보면 <div id="app"></div> 부분에 Vue 어플리케이션 객체가 올라간다.
- 쉽게 설명하자면, Vue 컴포넌트들이 앞으로 해당 엘리먼트 부분에서 출력된다는 뜻이다.(SPA)
- 이로써 main.js를 중심으로 App.vue와 index.html이 연결된 것이다.
c) use()
- 위에 있는 main.js의 코드를 살펴보면 use() 메소드를 사용하여 router와 store를 연결한다.
- use() 메소드를 사용하여 플러그인을 Vue 어플리케이션 객체와 연결하여 사용한다.
- 이처럼 main.js는 Vue 프로젝트 개발에 필요한 다양한 플러그인 연결도 주관한다.
3. 컴포넌트와 라우트의 관계도와 역할
- 본 영화검색 프로젝트에서는 다양한 컴포넌트와 라우트를 갖고 있다.
- 각 컴포넌트와 라우트가 어떻게 이어져 있나 그 관계도를 그림으로 그려보았다.
- 각 컴포넌트의 역할은 다음과 같다.
a) Header.vue
- 해당 컴포넌트는 App.vue의 컴포넌트로 등록되어 사용된다.
- 역할: 네비게이션(메뉴)을 v-for와 <RouterLink>를 이용하여 구성한다.
- data(): navigations라는 배열 내부에 name과 href 등 링크를 위한 속성값을 갖는 객체를 삽입하여 관리한다.
- computed: 전개연산자와 ...mapState()를 사용하여 store의 'about' 모듈에 있는 state값을 가져온다.
- methods
▶ isMatch(): 특정 형식의 url 접근 시 fullpath를 콘솔에 반환한다.
▶ toAbout(): routerLink와 to 속성 없이 페이지 이동을 구현하기 위한 함수
b) Footer.vue
- 해당 컴포넌트는 App.vue의 컴포넌트로 등록되어 사용된다.
- 역할: 웹페이지 가장 아래에 표시되는 로고와 개발자 링크를 표시한다.
c) Headline.vue
- 해당 컴포넌트는 Home.vue의 컴포넌트로 등록되어 사용된다.
- 역할: Header 바로 아래에 위치하는 웹사이트 소개글을 담당한다.
d) Loader.vue
- 해당 컴포넌트는 MovieList.vue, MovieItem.vue, Movie.vue, About.vue의 컴포넌트로 등록되어 사용된다.
- 역할: 페이지 로딩 중 화면에 출력되는 로딩 아이콘(spinner)
- props: spinner의 기본 속성값을 설정하여 등록한다.
e) Logo.vue
- 역할: 로고 클릭 시 Home으로 이동하는 링크를 담당한다.
f) Search.vue
- 해당 컴포넌트는 Home.vue의 컴포넌트로 등록되어 사용된다.
- 역할: 영화 검색에 필요한 input 태그 및 필터(select - options), 검색기능을 관리한다.
- data(): 검색 필터의 기본값 및 필터의 선택 가능한 값의 범주를 설정
- methods
▶ apply(): 입력된 검색정보를 dispatch()를 통해 store - movie.js - searchMovies() 메소드의 매개변수로 전달한다.
g) MovieList.vue
- 해당 컴포넌트는 Home.vue의 컴포넌트로 등록되어 사용된다.
- 역할: 검색 정보를 바탕으로 OMDb_API 에서 반환된 영화 리스트를 출력한다.
- components: MovieItem(각 영화의 상세정보), Loader(로딩 중 출력되는 spinner)
- computed: ...mapState()를 통해 store의 'movie' 모듈에 있는 state를 가져온다.
h) MovieItem.vue
- 해당 컴포넌트는 MovieList.vue의 컴포넌트로 등록되어 사용된다.
- 역할: MovieList.vue에서 출력된 영화 리스트 중 하나를 클릭하면 해당 영화의 상세정보를 출력하는 부분
- components: Loader
- props: movie 객체를 상위 component인 MovieList.vue로 부터 받아온다.
- data(): imageLoading 변수를 이용하여 Loader의 출력 여부를 설정한다.
- mounted(): 해당 라이프 사이클 시점에서 this.init() 메소드를 실행한다.
- methods
▶ init(): 영화 poster가 로딩되는 동안 동작하는 함수로, 각 영화의 poster 유무를 확인하여 포스터가 없는 경우 예외처리 후 Loader의 작동을 종료한다. poster가 있는 경우, loadImage 플러그인을 실행 후 Loader의 작동을 종료한다.
4. 영화 검색 메카니즘 설명
1) Search.vue - 검색정보 입력
- Search.vue 컴포넌트에서 검색어 및 검색조건을 입력한다.
- apply 버튼을 누르면 apply() 메소드가 실행된다.
- store의 'movie' 모듈의 searchMovies() 메소드의 매개변수로 title, type, number, year의 데이터가 넘어간다.
2) searchMovies() 메소드
- Search.vue에서 넘어온 데이터는 payload라는 매개변수 안에 담긴다.
- _fetchMovies() 메소드에서 payload 내부의 데이터를 사용하여 OMDb_API에게 값을 넘긴다.
- serverless 함수로 지정해 놓은 API를 실행하게 되고, 올바른 요청일 경우 body에 검색 결과가 담겨서 반환된다.
- API로 부터 반환받은 데이터를 res라는 변수에 담는다.
- res를 객체구조분해를 사용하여 필요한 데이터만 store의 'movie'모듈의 movies라는 state에 저장한다.
3) MovieList.vue
- MovieList.vue 에서 computed 속성을 통해 movies라는 state값을 읽어온다.
- movies 배열에 담긴 데이터를 MovieItem.vue 컴포넌트에서 읽는다.
- MovieItem.vue 에게 movies의 단일 객체인 movie가 전달된다.
- v-for를 사용하여 영화 리스트를 출력한다.
4) MovieItem.vue
- MovieList.vue에서 넘어온 movie 객체를 props 옵션을 통해 MovieItem.vue에서 받아온다.
- MovieItem.vue가 MovieList.vue의 하위 컴포넌트로 설정 되어 있기 때문에 가능한 것이다.
- 각 MovieItem.vue가 MovieList.vue 안에서 영화 포스터를 출력하게 된다.
- 더불어, RouterLink를 사용하여 MovieItem.vue 컴포넌트를 클릭하면 해당 영화의 상세정보 페이지(Movie.vue)로 넘어간다.
- 각 영화의 고유값인 ID를 URL로 전달한다.
5) Movie.vue
- MovieItem.vue 컴포넌트를 클릭하면 RouterLink를 통해서 선택된 영화의 ID값이 URL에 담겨서 전달된다.
- URL을 통해 전달받은 ID값은 route 객체를 통해서 확인할 수 있다.
- Movie.vue에서 route객체를 통해 영화 ID를 전달 받고, 해당 ID값을 store의 'movie' 모듈에 있는 searchMovieWithId() 메소드에게 매개변수로 넘긴다.
- searchMovieWithId() 메소드에서 Movie.vue로부터 전달받은 데이터를 payload에 담는다.
- searchMovieWithId() 메소드에서 OMDb_API에게 payload를 전달하고 영화검색 정보를 반환받는다.
- API로 부터 반환 받은 개별 영화 정보는 theMovies라는 객체에 저장된다.
- Movie.vue에서 computed 속성을 통해 theMovies 객체정보를 읽어온다.
- theMovies 객체의 정보를 기반으로 한개의 영화의 상세 정보 페이지가 출력된다.
여기까지 Vue로 만든 영화검색 프로젝트의 구조와 핵심기능이 동작하는 흐름에 대해 알아보았다.
'Side Projects > 클론코딩' 카테고리의 다른 글
Vue3 - 영화 검색 사이트 (0) | 2021.06.22 |
---|---|
Vanilla JS - Pastel Browser (0) | 2020.10.17 |
HTML/CSS - 음악 플레이어(Music Player) (0) | 2020.10.13 |
코코아톡(HTML/CSS) (0) | 2020.09.20 |
댓글