컴포넌트 단위의 개발
개요
처음 프론트앤드를 배우면 보통 각 페이지에 해당하는 html파일을 만들고 사용한다.
이렇게 하다보면 요소가 중복되고 코드가 길어지면 유지보수가 힘들어진다.
그럼 이런 생각을 해볼수도 있다.
자바스크립트는 페이지를 동적으로 조작하기 위해 사용한다. 즉, 자바스크립트를 통해서 요소를 만들고 삭제하는등의 작업이 가능하다.
그렇다면 한 화면을 각 부분으로 분리해서 관리한다면 좀 더 스마트하게 작업할 수 있지 않을까?
만약 페이지의 어떤 부분을 수정하고싶다면 그 부분을 렌더링하는(화면에 그리는) 자바스크립트 부분을 손보면 된다.
이것이 바로 리엑트나 뷰같은 프론트 개발 툴의 기본 원리다.
개념
개인적인 경험을 소개해보자면
자바스크립트의 템플레이팅 라이브러리중에서 Handlebars라는 라이브러리가 있다.
데이터를 끼워넣을 비어있는 html 코드 뭉치를 만들고 데이터와 함께 컴파일한다.
그럼 완성된 html코드를 리턴하고 타겟 html.innerHtml을 사용하여 넣어주면 된다.
처음에는 템플릿 함수를 하드코딩해서 정의하고 사용했었다. 그러니 템플레이팅해야하는 부분이 많아지면 그만큼 비슷한 코드를 정의해서 사용해야했다.
그러다 생각이 든게 함수에서 동적으로 바뀌는 부분을 인자로 받아서 사용하는 것이었다.
인자로는 템플레이팅하기위한 데이터와 타겟 html을 넣어서 사용했었다. 관리하기 편하고 알아보기도 쉬웠다.
이런 아이디어를 활용해서 이번에는 화면의 각 부분을 컴포넌트화해서 사용하는 방법을 생각해볼 수 있다.
요소를 화면에 표현하는 클래스를 하나 만들고 인자를 전해주어 렌더링하는 것이다.
이렇게 하면 정적인 HTML을 마치 객체지향 프로그램을 작성하듯 작업할 수 있다.
헤더 컴포넌트, 상세 항목 컨테이너 컴포넌트, 상세 항목 컴포넌트와 같이 나누고 상세 항목 컨테이너 컴포넌트에서 상세 항목 컴포넌트의 리스트를 받아서 렌더링 시키면 되겠다.
모듈
자바스크립트에서도 다른 프로그래밍언어와 마찬가지로 패키지를 나누고 import해서 사용하는 방법이 생겼다.
ES2015에 추가된 개념으로 import와 export를 통해 다른 파일에서 사용할 수 있다.
예를 들어 하나의 자바스크립트 파일에서 다음과 같이 사용할 수 있다.
// sample.js
const sample = 'sample value';
export { sample };
하나의 모듈에서 export하고
// main.js
import { sample } from './sample.js'
console.log(sample); // 'sample value'
다른 모듈에서 import해서 사용한다.
위에서는 하나의 값을 내보내기해서 사용했고 export default
구문을 사용해서 모듈을 대표하는 값을 지정하고 사용할 수 있다.
//sample.js
export default 'sample value';
import { sample } from './sample.js'
console.log(sample); // 'sample value'
Let’s get started!!
기본환경 셋팅
먼저 파슬 번들러부터 설치해보자.
npm init -y
npm i parcel -D
이렇게 하면 pakage.json
파일이 생긴다.
여기에 개발 서버를 띄우기 위한 스크립트와 실제 배포를 위한 스크립트를 아래와 같이 정의해준다.
// package.json
"scripts": {
"dev": "parcel ./index.html",
"build" : "parcel build ./index.html"
},
가장 메인 경로에 index.html 파일을 만들어주고 id가 root인 div요소를 하나 만들어주고 js모듈을 연결한다.
div요소의 id는 다른 이름을 사용해도 괜찮지만 클라이언트 사이드 렌더링 방식의 프레임워크의 개념을 미리 체험해보기 위해 root로 설정한다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="module" src="./src/main.js" defer></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
//main.js
import App from './App.js'
const root = document.getElementById('root')
root.append(new App().el)
export default class App {
constructor() {
this.el = document.createElement('div')
this.el.textContent = 'Hello, Web Components!'
}
}
이렇게 작성하고 npm run dev
명령어를 통해 개발 서버로 접속해보면 잘 출력된다.
개발자 도구를 열어 확인해보면 id가 root인 div요소 안에 위에서 작성한 문구가 잘 출력되는 걸 볼 수 있다.
이런 식으로 컴포넌트를 만들어서 root요소의 하위 요소로 넣어주는 식의 작업 방식이다.
src 폴더 내부에 core라는 이름으로 폴더를 생성해준다. 프로젝트를 만들며 핵심 기능을 하는 파일들을 여기에 모아서 작성해준다.
프로젝트의 핵심 기능을 하는 모듈을 작성해준다.
// Component
export class Component {
constructor(payload = {}) {
const { tagName = 'div' } = payload;
this.el = document.createElement(tagName);
this.render();
}
render() {
//...
}
}
클래스를 하나 정의하고 생성자에서 태그의 이름을 받은다음 전달 받은 이름으로 요소를 생성한다. 만약 전달받은 인자가 없을 경우 div로 생성되게 해놓았다.
이걸 사용할 모듈에서는 다음과 같이 작성해준다.
import { Component } from './core/Main'
export default class App extends Component {
constructor() {
super();
}
render() {
this.el.innerHTML = 'Hello world'
}
}
import 키워드를 통해 모듈을 가져오고 상위 클래스에서 결정하기 어려운 render함수의 내용을 하위 요소에서 결정한다.
Component 클래스를 상속받은 App클래스에서 super()를 호출함으로써 Component의 render()함수를 실행시키도록 한다.
만약 super생성자의 안에 ‘h1’과 같이 태그 내임을 넣어주면 h1태그로 출력된다.
import { Component } from './core/Core'
export default class App extends Component {
constructor() {
super({
tagName: 'h1'
})
}
render() {
this.el.textContent = 'Hello world'
}
}