본 내용은 인프런 강의 실습 UI 개발로 배워보는 순수 javascript와 VueJS 개발 후기다.
순수 자바스크립트를 이용해서 목록 출력, 목록 검색, 최근 검색을 작게 구현 해보았다.
VS code, node, lite-server(npm 설치 모듈)
링크를 통해서 다운로드를 해도 되고 homebrew, npm install을 통해 설치해도 된다.
크게 controllers, model, views 폴더로 나뉘어 있다.
Dom이 로드되는 시점에 MainController.js의 init 함수 호출을 담당한다.
DOMContentLoaded 더 알아보기
import MainController from './controllers/MainController.js'
//DOMContentLoaded
//브라우저가 HTML을 전부 읽고 DOM 트리를 완성하는 즉시 한다.
//이미지 파일이나 스타일시트 등의 기타 자원은 기다리지 않는다.
document.addEventListener('DOMContentLoaded', () => {
MainController.init()
})
View, Model 영역의 파일들을 모두 import하여 사용한다.
각각 파일은 default export 되어있기 때문에 불러오는 클래스에 중괄호를 사용하지 않고 불러올 수 있다.
default export 의 특징
가져오기 할 때 개발자가 원하는 대로 이름을 지정할 수 있다. 하지만 실무에선 같은 걸 가져오는데도 이름이 달라 혼란의 여지가 생길 수 있기 때문에 파일 이름과 동일한 이름을 사용하도록 팀원끼리 내부 규칙을 정할 수 있다.
import FormView from '../views/FormView.js'
import ResultView from '../views/ResultView.js'
import TabView from '../views/TabView.js'
import KeywordView from '../views/KeywordView.js'
import HistoryView from '../views/HistoryView.js'
import SearchModel from '../models/SearchModel.js'
import KeywordModel from '../models/KeywordModel.js'
import HistoryModel from '../models/HistoryModel.js'
init 메서드를 통해 import된 모듈 중에서 상태가 변한 View를 감지한다.
모든 모듈(View.js)들은 setup이라는 메서드를 가지고 있고 특정 element를 통해 이벤트를 감지 받은 후 각각의 모듈에서 화면 변화에 맞는 메서드를 호출한다.
이후 컨트롤러에 태그(@submit, @reset, @change 등등)를 반환해 관련한 메서드를 호출 시킨다.
//MainController init 함수
init() {
FormView.setup(document.querySelector('form'))
//on은 메서드 체이닝
.on('@submit', e => this.onSubmit(e.detail.input))
.on('@reset', e => this.onResetForm())
TabView.setup(document.querySelector('#tabs'))
.on('@change', e => this.onChangeTab(e.detail.tabName))
KeywordView.setup(document.querySelector('#search-keyword'))
.on('@click', e => this.onClickKeyword(e.detail.keyword))
HistoryView.setup(document.querySelector('#search-history'))
.on('@click', e => this.onClickHistory(e.detail.keyword))
.on('@remove', e => this.onRemoveHistory(e.detail.keyword))
ResultView.setup(document.querySelector('#search-result'))
this.selectedTab = '추천 검색어'
this.renderView()
},
//검색할 단어를 매개변수로 받아 search
search(query) {
FormView.setValue(query)
SearchModel.list(query).then(data => {
this.onSearchResult(data)
})
},
//... 메서드 생략
View.js
공통으로 사용할 메서드가 정의되어 있다.
각각의 View.js에서 이 모듈을 import하여 새 객체를 만들어 사용한다.
const tag = '[View]'
export default {
init(el) {
if (!el) throw el
this.el = el
return this
},
on(event, handler) {
this.el.addEventListener(event, handler)
return this
},
emit(event, data) {
const evt = new CustomEvent(event, { detail: data })
this.el.dispatchEvent(evt)
return this
},
hide() {
this.el.style.display = 'none'
return this
},
show() {
this.el.style.display = ''
return this
}
}
다른 모듈들은 단순히 import를 하여 사용하였으나 View.js을 사용할 때는 import 후 Object.create를 하여 객체를 만들어 사용하였다.
그 이유는 객체의 상속을 통해 부모 객체의 기능을 물려받아 새로운 기능을 추가하여 사용하기 위함이라고 한다.
자바스크립트에서 상속과 프로퍼티, 인스턴스의 개념을 더 이해해야 할 필요가 보인다.
Object.create(객체)
상속을 통해 부모 객체의 기능을 물려받고, 본인만의 새로운 기능을 추가할 수 있다. 특이한 점은 Object.create을 통해 생성할 경우 생성자 코드를 실행하지 않는다. 생성자를 호출할 경우는 Object.create 보다 New를 통하여 객체를 호출 해주는 것이 낫다.
Object.create의 매개변수Object.create(prototypeObect,propertyOberct)
- 첫번째 매개변수 : 프로토타입
- 두번째 매개변수 : 생성할 객체의 프로퍼티 키와 디스크립터 객체 전달
- 디스크립터 객체란?
프로퍼티 상태를 나타내는 객체, 프로퍼티 어트리뷰트라고도 한다.
상속 new, object.create 더 알아보기
예시) FormView.js
아래 소스를 보면 Object.create를 사용해 View의 기능을 물려받아 FormView라는 객체를 생성해주었다.
import View from './View.js'
const tag = '[FormView]'
//Object.create 사용
const FormView = Object.create(View)
setup 함수에서 기본 상태와 상태 변화를 감지했을 경우의 필요한 함수를 정의한다.
FormView.setup = function (el) {
this.init(el)
this.inputEl = el.querySelector('[type=text]')
this.resetEl = el.querySelector('[type=reset')
this.showResetBtn(false)
this.bindEvents()
return this
}
이후 UI에 관련된 함수와 데이터의 상태 변화 함수를 정의한다.
데이터의 상태 변화 함수는 emit 메서드로 태그를 전달하여 컨트롤러에게 상태 변화를 알린다.
FormView.bindEvents = function() {
this.on('submit', e => e.preventDefault())
this.resetEl.addEventListener('click', e => this.onClickReset())
}
FormView.onClickReset = function() {
//컨트롤러에서 @reset일 때 함수 호출
this.emit('@reset')
this.showResetBtn(false)
}
각 model에서는 필요한 데이터와 처리를 담당하였다.
컨트롤러에서 모듈로 import 한 후 뷰에서 데이터 처리시 태그를 보내고 컨트롤러에서 태그를 받아 관련 모델 메서드를 호출한다.
예시) HistoryModel.js
export default {
data: [
{ keyword: '검색기록2', date: '12.03' },
{ keyword: '검색기록1', date: '12.02'},
{ keyword: '검색기록0', date: '12.01' },
],
list() {
return Promise.resolve(this.data)
},
add(keyword = '') {
keyword = keyword.trim()
if (!keyword) return
if (this.data.some(item => item.keyword === keyword)) {
this.remove(keyword)
}
const date = '12.31'
this.data = [{keyword, date}, ...this.data]
},
remove(keyword) {
this.data = this.data.filter(item => item.keyword !== keyword)
}
}
추천 검색어, 최근 검색어, 검색, 기록 삭제, 검색 결과 리스트
데이터 연결 없이 객체 통해서 바로 뿌려볼 수 있어서 편했고, 각각 파일들의 역할이 명확해서 모듈로 만들어 사용할 때의 이점을 알 수 있었다.
자바에서 import 해서 사용하는 것과 비슷해 보이는데 자바스크립트에서는 import, export 방식 구현이 더 다양해 보인다.
자바에서도 클래스 자체를 import하는 것에 별로 생각을 안해봤는데 다른 방식이 있는지 찾아봐야겠다.
또 ES6, 프로퍼티, 속성에 대해서 공부를 좀 더 해야겠다.