https://nwstudio.tistory.com/56
7. 뷰 템플릿
서블릿도 사용하기 편하게 개선했고, html에서 모델의 데이터를 바로 사용할 수 있게 되면서 html의 작성 편의성도 올라갔다. 이렇게까지 했는데도 더 할 일이 남아있을까? 프레임워크에 종속되는
nwstudio.tistory.com
여기에서 Nuxt프레임워크에 대해 간단하게 언급했었다. Nuxt를 사용하면 서버사이드에서도 Vue.js를 사용할 수 있기에, 서버사이드의 작업은 Thymeleaf로 하고 클라이언트 사이드 작업은 Vue.js로 하는 불편을 덜 수 있다고 했다. 그리고 nuxt프레임워크가 Vue.js를 서버사이드에서 실행하기 위해 필요한 Node.js에 대해 알아보았고, 모듈에 대해서도 알아보았다.
여러분은 상술한 이유로 Nuxt를 사용하고 싶지만, 사실 Nuxt는 Vue.js를 사용하기 좋도록 세팅해놓은 프레임워크일 뿐이다. 따라서 여러분이 Nuxt를 사용하고 싶다면 먼저 Vue.js를 사용할 줄 알아야 한다.
앞서서 모듈이 오늘날 자바스크립트의 표준이라고 했다. 이 말은 단순히 오늘날 자바스크립트를 지원하려면 모듈 관련 키워드를 지원해야 한다는 뜻을 넘어서서, 모든 자바스크립트 관련 산출물이 모듈 단위로 되어있다는 의미이다. 그리고 여러분은 앞으로 단순히 자바스크립트 코드를 넘어서, 웹 프론트앤드 전반을 모두 모듈 단위로 개발하게 될 것이다. 미리 말해두자면, 여러분은 html 마크업도 모듈 내에서 수행해야 한다.
Vue.js를 가장 단순하게 사용하는 방법은 매우 간단하다. 그냥 vue.js에서 제공하는 URL을 script로 임포트하고, Vue인스턴스를 생성하면 된다. 그냥 로컬 아무 경로에서나 index.html을 생성해서 아래와 같이 작성하고, 브라우저로 열어보면 작동하는것을 확인할 수 있다.
Vue는 뷰 템플릿이고, 뷰 템플릿의 역할은 모델에 들어있는 데이터를 브라우저에 나타나는 DOM에 실제로 그려주는 것이다. 따라서 Vue인스턴스는 이러한 역할에 맞는 속성들을 가지고 있다. el 속성을 통해 자신이 관리할 DOM(브라우저에 그려지는 document 요소)을 지정할 수 있고, data를 통해 자신이 관리할 데이터를 정의할 수 있다. 즉, MVC패턴에서 뷰와 모델을 관리할 수 있는 것이다.
따라서 아래와 같이 해 두면, Vue인스턴스에 의해 "app"이라는 ID가 부여된 DOM 요소의 내용은 message의 값으로 치환되어 랜더링 된다. 구체적으로 어떤 부분을 message로 치환할지는 {{}}를 통해 명시할 수 있다. 아래의 예시와 같은 경우, Vue인스턴스는 우선 app이라는 ID를 가진 DOM을 찾아낸 후(div를 발견!) 그 안에서 {{}}로 감싸인 부분을 찾는다. 그런 후 자신의 data에 해당 키값이 있으면 {{}}영역을 해당 키값에 해당하는 값으로 치환한다.
이렇게 사용할때는 모듈 같은것이 보이지 않는다. 위 html에 포함된 자바스크립트에는 export도 없고, import도 없다. 여러분은 그냥 기존에 자바스크립트를 사용하던 대로 js파일을 하나 임포트했고, 거기서 제공되는 Vue 생성 메소드를 사용해서 Vue인스턴스를 생성했을 뿐이다. 물론 vue.js 내부에서는 자기들끼리 모듈을 사용해서 구현되어 있기는 하지만, 일단 여러분은 직접적으로 모듈을 사용하지 않았다.
하지만 이것은 아직 여러분의 사이트가 제공하는것이 별것 없기 때문에 가능한 일이다. 지금 여러분의 사이트는 "안녕하세요 Vue!"를 표시해주는것 이외에 아무런 기능이 없지 않은가? 이제 요구사항들이 들어올 것이다. 예를 들어 상품정보를 보여주는 페이지, 고객정보를 보여주는 페이지 두개가 필요하다고 하자. 그리고 이들에게는 공통적으로 맨 위 공간을 차지하는 '헤더'가 필요하다고 하자.
가장 단순하게 생각해볼 수 있는 솔루션은 이것이다. 다른 페이지를 하나 더 만든 후에 html을 복붙하고, 데이터만 바꿔서 끼워주는 것이다. 하지만 쉽게 알 수 있듯이 좋은 방법이 아니다. 일단 공통으로 사용되는 헤더의 코드가 중복으로 들어가 있어서, 이런식으로 페이지 100개를 만들었는데 나중에 헤더를 고쳐야 한다면 100개의 파일을 모두 수정해야 한다.
일단 순수하게 html로만 마크업을 해서는 이런 문제를 해결할 수가 없다. 따라서 각각의 뷰 템플릿들이 이런 방식을 지원한다. SSR에 사용되는 Thymeleaf를 예로 들자면, Thymeleaf는 fragment 를 지원하여 이러한 중복문제를 해결한다.
위와 같이 th:fragment를 통해 fragment를 선언하고, 다른 html 파일에서 th:insert를 통해 가져올 수 있다. 헤더를 이런식으로 작성해 둔다면, 헤더 fragment만 수정하면 다른 페이지들에서도 헤더가 수정될 것이다.
한편, Vue는 이러한 문제를 '모듈'로써 해결한다. 아니 그런데 지금은 마크업(html작성)을 하는 중이지 않은가? 모듈은 javascript에서 사용하는 개념이 아닌가? 모듈을 통해 마크업의 문제를 해결한다는 것이 어떻게 가능할까?
위의 Thymeleaf의 예시가 단순히 마크업을 조금 더 편리하게 돕는 수준이었다면, Vue.js의 모듈 기반 개발 방법론은 아예 마크업의 패러다임 자체를 바꾼다. 물론 마크업을 하는 담당자가 해야 하는 작업의 내용은 크게 다르지 않지만, 내부의 기술적으로는 순수 html로만 마크업을 진행할 때와 많은 차이가 있다.
결론부터 말하자면, Vue.js는 모듈들을 모아서 페이지를 구성한다. 이때 각각의 모듈은 하나의 Vue인스턴스를 export한다. 그리고 각각의 Vue인스턴스는 자신이 그려야 할 html 마크업을 'template'라는 속성에 가지고 있다.
이것은 한가지 중요한 사실을 내포한다. Vue 인스턴스들이 모여서 페이지를 그리고 각각의 마크업을 스스로가 들고있다는 말은, index.html 자체는 아무런 마크업을 가지고있지 않다는 것을 의미한다. 그리고 특정한 조건에 따라 어느 Vue인스턴스가 작동하고, 어느 인스턴스는 작동하지 않을지를 결정할 수만 있다면, index.html 페이지 하나로 원하는 모든것을 표시할 수 있다.
이렇게, 하나의 페이지(index.html)로 모든 서비스를 표시한다는 뜻에서 이런 방식으로 개발된 서비스를 SPA(Single Page Application)이라고 부른다. 이 방식으로 개발된 웹 사이트는 유저가 사용할때 모든 웹문서 응답이 index.html하나 뿐이다.
Vue를 사용해본 사람이라면 Router에 URL을 명시해서 여러 페이지를 제공할 수 있지 않느냐고 반문할 수 있겠지만, 사실 이러한 URL로 요청하더라도 index.html이 작동하고, 거기의 main.js가 작동하여 Vue의 router에 명시된 컴포넌트를 표시할 뿐이다. 실제로 개발자모드의 네트워크탭을 열어보면, 응답으로 온 html문서는 index.html하나뿐인것을 확인할 수 있다. 그 이후에 어떤 컴포넌트가 보여질지 Vue.js가 결정함으로써 여러분이 의도한 페이지가 보여지는 것이다.
물론, Vue를 사용하더라도 SPA만 개발 가능한것은 아니다. 예를 들어 Vue의 빌드 산출물(index.html과 js, css파일들)을 nginx에 올리고, 또 nginx에 별도 설정을 하고 그 경로에 다른 html파일을 올려둠으로써 멀티 페이지 애플리케이션(SPA와 반대인 MPA)을 개발할 수도 있다. 하지만 특별한 이유가 있는것이 아니라면 이렇게 할 이유가 없을 것이다.
물론 검색엔진 최적화(SSO)를 위해서는 이런 별도의 html이 필요하지 않느냐고 반문할 수도 있지만, (크롤러는 대게 자바스크립트를 실행하지 않는다고 했다), 다행히도 Vue와 Vue프레임워크인 nuxt 등에서 router관련 기능이 서버사이드에서 작동하도록 지원한다. 따라서 SPA로 개발하더라도 SSO가 가능하다.
예를 들어 MAINURL/test/page1에 대해서 응답으로 온 index.html과, MAINURL/test/page2에 대해서 응답으로 온 index.html의 내용이 서로 다르게 할 수 있다는 뜻이다. 하나의 index.html이지만, Vue가 서버사이드에서 작동하도록 하면 이러한 기능을 사용할 수 있다. 또한 이렇게 하는 경우, 개발자모드에서 네트워크를 찍어보면 응답 문서의 이름이 index가 아니라, 해당 페이지의 URL로 되어있는것을 확인할 수 있다.
상술했듯이 Vue로 웹페이지를 개발하는것은 모듈과 관련된 작업을 필요로 하고, 따라서 앞서 언급했던 '빌드'과정이 필요하다. 그리고 이러한 빌드 프로세스를 세팅하는것은 몹시 번거로운 작업이다. 유저에게 요청이 왔을때 제일 먼저 작동하기 시작할 자바스크립트가 무엇인지 지정해야 하고, 사용할 Vue인스턴스들을 정의한 모듈들이 있는 위치도 지정해야 한다. 또한 빌드엔진으로 사용할 웹팩도 설정해야 한다.
이런 번거로운 문제를 해결하기 위해, Vue-cli가 존재한다. 이것을 설치하고 vue init 명령을 통해 간단하게 이 모든것들이 셋업된 프로젝트를 생성할 수 있다. 그러면 마치 Spring프레임워크 위에서 개발하듯이, 모든것이 세팅된 환경에서 편하게 개발을 진행할 수 있다.
npm i -g @vue/cli
(Vue-cli를 설치한다)
vue init webpack test3
(Vue 프로젝트를 생성한다. 여기서 test3는 프로젝트 이름이므로, 마음대로 바꿔도 된다. webpack은 사용할 빌드 엔진의 이름인데, 특별한 이유가 없다면 그냥 webpack을 사용하자.)
이렇게 프로젝트를 세팅하고, npm install, npm run build를 수행한 후의 프로젝트 상태이다. npm install을 통해 필요한 모듈들이 node_modules에 설치되고, npm run build를 통해 빌드 산출물이 dist폴더에 생성된 것을 확인할 수 있다.
npm install은 뒤에 특정한 모듈을 생략하면 package.json의 내용을 모두 설치해주는 npm명령어이다. package.json은 vue프로젝트를 init할때 자동으로 생성되어 있다.
또한, npm run build는 특별한 명령어 같은것이 아니라 그냥 node.js를 통해(npm run) build.js를 실행하는 것이다.
이 상태에서 main.js의 내용이 사용자에게 웹 문서가 내려갔을때 처음으로 실행될 내용이다. Vue인스턴스를 생성하고, app이라는 ID를 가진 DOM을 찾아서 그것을 자신이 관리하겠다고 선언하고 있다. 사용자에게 index.html이 내려갔을 때 이것이 어떻게 실행되는지는 빌드 산출물을 살펴보면 확인할 수 있다. 빌드 산출물은 dist경로에 있다. (npm run build를 해야 생성된다)
빌드 산출물이 담기는 dist폴더의 index.html이 결국 유저에게 전달되는 웹 문서인데, 여기에서 main.js를 포함한 자바스크립트들을 "app" ID가 부여된 DOM 뒤에 배치하고 있다. 따라서 이 문서를 받은 브라우저는 우선 app이라는 DOM(div)를 만들고, 뒤에 붙어있는 자바스크립트(main.js의 내용을 포함한)들을 실행해서 Vue인스턴스를 생성하게 된다.
한편, main.js의 Vue인스턴스 생성문을 다시 보자. 여기서 components는 Vue인스턴스 자신이 사용할 모듈들을 의미하는데, 자신과 형제 경로에 있는 App.vue를 명시하고 있다.(확장자는 생략 가능) 또한 아까 템플릿에는 자신이 그려야 할 마크업을 가지고 있다고 설명했는데, 여기서는 App 컴포넌트(모듈)가 제공하는 template를 사용하겠다고 선언하고 있다. 그러면 결국 이 프로젝트 사이트에서 제공할 모든것들은 저 App모듈에 작성되어 있을 것이다. App모듈을 확인해보면 아래와 같이 template 를 작성할 수 있는 공간이 명시되어있다.
Vue프로젝트는 이와 같은 디렉토리 구조를 가지고 있다. build 디렉토리 안에는 npm run build를 명령했을때 작동할 build.js스크립트가 들어있다. 이 빌드 스크립트에서는 config 디렉토리에 있는 설정 js파일들도 사용한다. 이 파일들의 내용은 기본적으로는 건드릴 필요가 없다.
물론, 배포환경에 따라 다른 DB주소 등을 이유로 배포환경으로 분기하여 빌드할 필요가 있다. 이런 경우에는 build스크립트를 추가하고 config의 js파일도 필요에 따라 추가해야 하며, npm명령어로 사용할 빌드 스크립트를 명시하는 package.json의 scripts항목에 빌드 스크립트를 명시해 주어야 한다.
예를들어 npm run build-qa 이러한 명령으로 QA환경에 배포할 빌드를 진행하고 싶다면, 우선 build-qa.js따위를 build.js와 동일하게(필요한 값만 변경해서)만들고, 이를 package.json의 scripts항목에 등록한다. 그리고 또 webpack.prod.conf.js의 사용처들을 참고하여 적절하게 webpack.qa.conf.js파일을 생성해 바꿔주면 된다.
dist 디렉토리는 빌드 결과물이 들어가는 디렉토리이고, node_modules는 앞에서 모듈을 다룰때 살펴보았듯이 모듈들이 들어가는 디렉토리다. 따라서 가장 중요하며 실질적으로 많이 사용하게 될 디렉토리는 src디렉토리 내부이다.
초기 프로젝트 상태에서는 assets, components, router디렉토리만 존재한다. assets에는 정적으로 제공할 이미지등이 들어가며, components에는 Vue 컴포넌트들(Vue모듈들)이 들어간다. router에는 브라우저의 요청 URL에 따라 어떤 모듈을 제공할지 결정하는 라우터 스크립트가 들어간다.
Vue프로젝트는 기본적으로 router 디렉토리의 index.js를 통해 각각의 Vue컴포넌트들이 대응할 URL을 결정하고, 각각의 Vue컴포넌트의 내용을 작성하는 방식으로 개발한다. 하지만 Vue자체의 본격적인 사용 가이드는 본문의 범위를 넘기 때문에, 공식문서를 참고하도록 한다.
10. 프로젝트 구조에 따른 배포 방식과 도커, 쿠버네티스 (0) | 2021.12.13 |
---|---|
8. Javascript 환경과 모듈 (0) | 2021.11.01 |
7. 뷰 템플릿 (0) | 2021.10.22 |
6. HTML을 작성하기가 너무 불편하다. (0) | 2021.10.22 |
5. 로직을 더 개선하기 (0) | 2021.10.21 |