컴포넌트 관점에서 개발하기
2017 프론트엔드 트렌드&인사이트
주우영
프런트엔드 개발 시작하기
네이버는 이렇게 한다!, 위키북스
JavaScript Promise
한빛 eBook, 한빛미디어
페이스북 프론트엔드개발 그룹 운영
https://www.facebook.com/groups/webfrontend/
레진엔터테인먼트 프론트엔드개발팀
오늘, 새로운 기술과
기능을 나열하기 보다
현대 UI 개발에 있어 중요히
여기는 부분에 대해 이야기합니다.
현대 프레임워크의 공통된 접근법
컴포넌트 단위로 사고하고 디자인
import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h1>Welcome to {{title}}</h1>
`,
styles: [`
h1 {
font-size: 4em;
}
`]
})
export class AppComponent {
title = 'app';
}
import {Component} from 'react';
const style = {
fontSize: '4em'
};
class HelloMessage extends Component {
static defaultProps = {
title: 'React'
};
render() {
return (
<h1 style={style}>
Welcome to {this.props.title}
</h1>
);
}
}
<style scoped>
h1 {
font-size: 4em;
}
</style>
<template>
<h1>Welcome to {{title}}</h1>
</template>
<script type="text/babel">
export default {
data () {
return {
title: 'Vue'
}
}
}
</script>
<dom-module id="hello-element">
<template>
<style>
h1 {font-size: 4em}
</style>
<h1>Welcome to {{title}}</h1>
</template>
<script>
class HelloElement extends Polymer.Element {
static get is() {return "hello-element";}
constructor() {
super();
this.title = "Polymer";
}
}
customElements.define(HelloElement.is, HelloElement);
</script>
</dom-module>
WHY
COMPONENT?
하나의 문제를 잘 해결하는
COMPONENT
프로그램
새로운 방법이 아닌
예로부터 모든 공학 분야의
공학자가 자주 사용해 오던
문제 해결 방법
Unix Philosophy
Write programs that do one thing and do it well.
Write programs to work together.
Doug Mcllroy, Unix pipeline inventor
The UNIX Philosophy in 9 paramount precepts
Mike Gancarz (a member of the team that designed the X Window System)
Small is beautiful.
Make each program do one thing well.
Write programs handle text streams, because that is a universal interface.
$ ps -ef
Processes Status
Report a snapshot of the current processes
$ grep gradle
Globally search a regular expression and print
Searching plain-text data sets for lines that match a regular expression
$ ps -ef | grep gradle
사고의 분산을 막고 하나의 문제에 집중함으로써
효율적으로 개발할 수 있다
설계의 오류를 쉽게 파악할 수 있고
상대적으로 테스트하기 쉽다
이식성을 높일 수 있고
변경에 유연하게 대체할 수 있다
API
UI는 자주적으로 동작하는 또 다른 프로그램,
그것을 API로 제어한다는 사고로 접근
자바스크립트를 개발할 때 우리는
_changeTabs() {
const fragment = this._parseFragment();
if (fragment === null) {
return;
}
_.each(fragment, (value, key) => {
const target = $(_.find(this.$sections, i => {
return $(i).dataset('type') === key);
});
// 선택한 탭으로 변경한다.
target.find('.cs-tab li').removeClass('is-on');
target.find('.cs-tab li')
.has(`a[href='#${value}']`).addClass('is-on');
// 탭에 해당하는 목록으로 변경한다.
target.find('.cs-body').removeClass('is-on');
target.find(`.cs-body#cs-${key}-${value}`)
.addClass('is-on');
});
}
_changeTabs() {
const fragment = this._parseFragment();
if (fragment === null) {
return;
}
_.each(fragment, (value, key) => {
const target = $(_.find(this.$sections, i => {
return $(i).dataset('type') === key);
});
// 선택한 탭으로 변경한다.
target.find('.cs-tab li').removeClass('is-on');
target.find('.cs-tab li')
.has(`a[href='#${value}']`).addClass('is-on');
// 탭에 해당하는 목록으로 변경한다.
target.find('.cs-body').removeClass('is-on');
target.find(`.cs-body#cs-${key}-${value}`)
.addClass('is-on');
});
}
DOM을 직접 핸들링, 장황하고 UI의 형태를 한눈에
파악하기 힘듦
_changeTabs() {
const fragment = this._parseFragment();
if (fragment === null) {
return;
}
_.each(fragment, (value, key) => {
const target = $(_.find(this.$sections, i => {
return $(i).dataset('type') === key);
});
// 선택한 탭으로 변경한다.
target.find('.cs-tab li').removeClass('is-on');
target.find('.cs-tab li')
.has(`a[href='#${value}']`).addClass('is-on');
// 탭에 해당하는 목록으로 변경한다.
target.find('.cs-body').removeClass('is-on');
target.find(`.cs-body#cs-${key}-${value}`)
.addClass('is-on');
});
}
상태를 class나 data attr에 설정 및 사용하여
전체적인 상태 흐름을 알기 힘듦
_changeTabs() {
const fragment = this._parseFragment();
if (fragment === null) {
return;
}
_.each(fragment, (value, key) => {
const target = $(_.find(this.$sections, i => {
return $(i).dataset('type') === key);
});
// 선택한 탭으로 변경한다.
target.find('.cs-tab li').removeClass('is-on');
target.find('.cs-tab li')
.has(`a[href='#${value}']`).addClass('is-on');
// 탭에 해당하는 목록으로 변경한다.
target.find('.cs-body').removeClass('is-on');
target.find(`.cs-body#cs-${key}-${value}`)
.addClass('is-on');
});
}
DOM API의 비용은 비쌈. 상태 변화를 위해 

DOM API 호출이 남용됨
_changeTabs() {
const fragment = this._parseFragment();
if (fragment === null) {
return;
}
_.each(fragment, (value, key) => {
const target = $(_.find(this.$sections, i => {
return $(i).dataset('type') === key);
});
// 선택한 탭으로 변경한다.
target.find('.cs-tab li').removeClass('is-on');
target.find('.cs-tab li')
.has(`a[href='#${value}']`).addClass('is-on');
// 탭에 해당하는 목록으로 변경한다.
target.find('.cs-body').removeClass('is-on');
target.find(`.cs-body#cs-${key}-${value}`)
.addClass('is-on');
});
}
UI 상태 변화를 위한 render 외의 부가적 메서드
다량 정의
프리젠테이션 로직은 크고 복잡.
테스트하고 유지보수하기 괴롭다
Functional Programming
컴포넌트 접근법은 함수형 프로그래밍하고도 관련이 깊다
부작용(Side effect)없이 하나의 일을 잘 수행하는 함수
그러한 함수를 조합 / 구성함으로써 큰 문제를 해결
F2 F3 F4 F5F1
{

“type”: “oval”,

“size”: 32

}
Functional Programming and UI Component
View = Function(state)
function Header({title, desc}) {
return (
<header className="header">
<div className="header__inner">
<h1 className="header__title">{title}</h1>
<p className="header__description">{desc}</p>
</div>
</header>
);
}
View = Function(state)
REMEMBER
Header({
title: 'Hello World!',
desc: 'This is Awesome.'
});
<header class="...">
<div class="...">
<h1 class="...">Hello World!</h1>
<p class="...">This is Awesome.</p>
</div>
</header>
<header class="...">
<div class="...">
<h1 class="...">Hello World!</h1>
<p class="...">This is Awesome</p>
</div>
</header>
<header class="...">
<div class="...">
<h1 class="...">Welcome</h1>
<p class="...">This is Awesome</p>
</div>
</header>
Equal?
<header class="...">
<div class="...">
<h1 class="...">Hello World!</h1>
<p class="...">This is Awesome</p>
</div>
</header>
<header class="...">
<div class="...">
<h1 class="...">Welcome</h1>
<p class="...">This is Awesome</p>
</div>
</header>
Equal?
<header class="...">
<div class="...">
<h1 class="...">Hello World!</h1>
<p class="...">This is Awesome</p>
</div>
</header>
<header class="...">
<div class="...">
<h1 class="...">Welcome</h1>
<p class="...">This is Awesome</p>
</div>
</header>
Equal? FAIL
Snapshot Testing with Jest
HTML(TMPL), CSS, 자바스크립트를 분리하는게
당연한 관심사의 분리라고 생각
과거 우리는.
Templates separate technologies, not concerns.
템플릿은 기술의 분리일 뿐 관심사의 분리가 아니다.
Pete Hunt, React developer
페이트 헌트, 리액트 개발자
“
”
KingCard Component
중요한건 해결하고자 하는 문제
같은 문제를 해결한다면 관심사는 같다
이때, 분리된 환경 보다
한 곳에 있는 경우가 훨씬 수월하다
관심사가 같다면 수정 시,
함께 수정해야 할 확률이 높다
HTML, CSS를 어떻게든 셋팅한 후
자바스크립트를 이어서 테스트
UI 테스트에 대한 자원 준비 없이
곧바로 컴포넌트를 테스트
Test IconText Component
it('iconDirection 속성에 left를 지정하면 아이콘이 좌측에 배치된다.', () => {
// Given
// When
const wrapper = shallow(
<IconText
label="친구초대"
icon="add-friend"
iconDirection="left"
/>
);
// Then
const children = wrapper.children('.icon-text__inner').children();
expect(children.first()).to.have.className('icon-text__icon');
expect(children.last()).to.have.className('icon-text__label');
});
"## Header
$ "## Header.js
$ "## Header.test.js
$ %## style.scss
"## TallCard
$ "## TallCard.js
$ "## TallCard.test.js
$ %## style.scss
"## KingCard
$ "## KingCard.js
$ "## KingCard.test.js
$ %## style.scss
%## StickyBar
"## StickyBar.js
"## StickyBar.test.js
%## style.scss
따라서 관련있는 것은
최대한 가까이에 두는
구조가 현재 유행
const Button = (props) => (
<button className={'large' in props && 'large'}>
{props.children}
<style jsx>{`
button {
padding: 20px;
background: #eee;
color: #999
}
.large {
padding: 50px
}
`}</style>
</button>
);
또는 하나의 파일 내 모든 관심사를
작성하는 방식도 선호
뭘 좀 만들어 봅시다.
이런 UI를 개발해 달라고 했을때
가장 먼저 한가지 일을 수행하는
최소 단위를 분석
뭘 좀 만들어 봅시다.
이런 UI를 개발해 달라고 했을때
가장 먼저 한가지 일을 수행하는
최소 단위를 분석
뭘 좀 만들어 봅시다.
이런 UI를 개발해 달라고 했을때
가장 먼저 한가지 일을 수행하는
최소 단위를 분석
뭘 좀 만들어 봅시다.
이런 UI를 개발해 달라고 했을때
가장 먼저 한가지 일을 수행하는
최소 단위를 분석
<TallCard/>
를 만들어 봅시다.
BEM(Block, Element, Modifier)
컴포넌트와 어울리게 마크업하기
Block
애플리케이션의 구성 요소로서 독립된 존재
Element
블록을 구성하는 작은 단위 또는 자식 개체
Modifier
블록이나 요소의 테마, 동작을 표현
Block
TallCard
Element
Information
Modifier
BadgeUp
Thumbnail
tall-card
tall-card__badge
tall-card__badge-up
tall-card__thumbnail
tall-card__image
tall-card__information
tall-card__title
tall-card__meta
tall-card__meta-key
tall-card__meta-value
BEM tree
BEM(Block, Element, Modifier)
컴포넌트와 어울리게 마크업하기
<div class="tall-card tall-card_badge_up">
<div class="tall-card__badge">
<i class=“tall-card__badge-up">
<span class="blind">UP</span>
</i>
</div>
<div class="tall-card__thumbnail">
<img class="tall-card__image" src="" alt=""/>
</div>
<div className="tall-card__information">
<h5 className="tall-card__title">레바툰</h5>
<dl class="tall-card__meta">
<dt class=“tall-card__meta-key">장르</dt>
<dd class=“tall-card__meta-value">개그 / 일상</dd>
<dt class=“tall-card__meta-key">작가</dt>
<dd class=“tall-card__meta-value">레바</dd>
</dl>
</div>
</div>
Markup with BEM
const TallCard = ({title, image, badge, metadata = []}) => (
<div className={`tall-card ${badge === 'up' ? ‘tall-card_badge_up' … `}>
<div className="tall-card__badge">
<i className=“tall-card__badge-up”/>
</div>
<div className="tall-card__thumbnail">
<img className="…" src={image} alt={`${title}의 썸네일`}/>
</div>
<div className="tall-card__information">
<h5 className="tall-card__title">{title}</h5>
{metadata.length > 0 ? (
<dl className=“tall-card__meta">
${metadata.map(({key, value}) => ([
<dt className=“tall-card__meta-key">{key}</dt>,
<dt className=“tall-card__meta-key">{value}</dt>
]))}
</dl>
) : null}
</div>
</div>
);
Create React Component
$module: 'icon-text';
.#{$module} {
&__badge {
/*...*/
&__up {/*...*/}
}
&__thumbnail {
/*...*/
&__image {/*...*/}
}
&__information {/*...*/}
&__title {/*...*/}
&__meta {/*…*/}
&__meta-key {/*...*/}
&__mata-value {/*...*/}
}
Styling with SASS
SASS의 interpolation은
컴포넌트 접근법과 잘 어울린다
스타일링 영역을 문법적으로
명확히 표현할 수 있다
"## TallCard
"## TallCard.js
%## TallCard.scss
"## TallCard
"## TallCard.js
"## TallCard.html
%## TallCard.scss
File structure
마크업 코드를 템플릿으로 분리한 경우
마크업을 자바스크립트에 포함한 경우
Context Free
<CardGroup/><SquareCard/> type = subscriptions
상위 컴포넌트 스타일이 하위 컴포넌트 스타일에 영향을 주면 안된다
컴포넌트는 어떤 문맥에서든 자유로워야 한다
badge = up sensational = true
만약 특정 문맥에서 컴포넌트가 변화한다면 해당 컴포넌트에
Context Free
<SquareCard/>
badge = new
새로운 변환자(Modifier)를 추가한다
Context Free
<CardGroup/>
CardList 컴포넌트는 badge가 up인 SquareCard 컴포넌트를
자식 컴포넌트로 포함하고 있을 뿐이다
type = subscriptions
타인의 잣대에 얽매이면 행복할 수 없듯
컴포넌트 역시 특정 문맥에 얽매이면
행복할 수 없다
감사합니다.

컴포넌트 관점에서 개발하기

  • 1.
    컴포넌트 관점에서 개발하기 2017프론트엔드 트렌드&인사이트
  • 2.
    주우영 프런트엔드 개발 시작하기 네이버는이렇게 한다!, 위키북스 JavaScript Promise 한빛 eBook, 한빛미디어 페이스북 프론트엔드개발 그룹 운영 https://www.facebook.com/groups/webfrontend/ 레진엔터테인먼트 프론트엔드개발팀
  • 3.
  • 4.
    현대 UI 개발에있어 중요히 여기는 부분에 대해 이야기합니다.
  • 5.
    현대 프레임워크의 공통된접근법 컴포넌트 단위로 사고하고 디자인
  • 6.
    import {Component} from'@angular/core'; @Component({ selector: 'my-app', template: ` <h1>Welcome to {{title}}</h1> `, styles: [` h1 { font-size: 4em; } `] }) export class AppComponent { title = 'app'; }
  • 7.
    import {Component} from'react'; const style = { fontSize: '4em' }; class HelloMessage extends Component { static defaultProps = { title: 'React' }; render() { return ( <h1 style={style}> Welcome to {this.props.title} </h1> ); } }
  • 8.
    <style scoped> h1 { font-size:4em; } </style> <template> <h1>Welcome to {{title}}</h1> </template> <script type="text/babel"> export default { data () { return { title: 'Vue' } } } </script>
  • 9.
    <dom-module id="hello-element"> <template> <style> h1 {font-size:4em} </style> <h1>Welcome to {{title}}</h1> </template> <script> class HelloElement extends Polymer.Element { static get is() {return "hello-element";} constructor() { super(); this.title = "Polymer"; } } customElements.define(HelloElement.is, HelloElement); </script> </dom-module>
  • 10.
  • 11.
    하나의 문제를 잘해결하는 COMPONENT 프로그램
  • 12.
    새로운 방법이 아닌 예로부터모든 공학 분야의 공학자가 자주 사용해 오던 문제 해결 방법
  • 13.
    Unix Philosophy Write programsthat do one thing and do it well. Write programs to work together. Doug Mcllroy, Unix pipeline inventor The UNIX Philosophy in 9 paramount precepts Mike Gancarz (a member of the team that designed the X Window System) Small is beautiful. Make each program do one thing well. Write programs handle text streams, because that is a universal interface.
  • 14.
    $ ps -ef ProcessesStatus Report a snapshot of the current processes $ grep gradle Globally search a regular expression and print Searching plain-text data sets for lines that match a regular expression $ ps -ef | grep gradle
  • 15.
    사고의 분산을 막고하나의 문제에 집중함으로써 효율적으로 개발할 수 있다
  • 16.
    설계의 오류를 쉽게파악할 수 있고 상대적으로 테스트하기 쉽다
  • 17.
    이식성을 높일 수있고 변경에 유연하게 대체할 수 있다
  • 18.
    API UI는 자주적으로 동작하는또 다른 프로그램, 그것을 API로 제어한다는 사고로 접근 자바스크립트를 개발할 때 우리는
  • 19.
    _changeTabs() { const fragment= this._parseFragment(); if (fragment === null) { return; } _.each(fragment, (value, key) => { const target = $(_.find(this.$sections, i => { return $(i).dataset('type') === key); }); // 선택한 탭으로 변경한다. target.find('.cs-tab li').removeClass('is-on'); target.find('.cs-tab li') .has(`a[href='#${value}']`).addClass('is-on'); // 탭에 해당하는 목록으로 변경한다. target.find('.cs-body').removeClass('is-on'); target.find(`.cs-body#cs-${key}-${value}`) .addClass('is-on'); }); }
  • 20.
    _changeTabs() { const fragment= this._parseFragment(); if (fragment === null) { return; } _.each(fragment, (value, key) => { const target = $(_.find(this.$sections, i => { return $(i).dataset('type') === key); }); // 선택한 탭으로 변경한다. target.find('.cs-tab li').removeClass('is-on'); target.find('.cs-tab li') .has(`a[href='#${value}']`).addClass('is-on'); // 탭에 해당하는 목록으로 변경한다. target.find('.cs-body').removeClass('is-on'); target.find(`.cs-body#cs-${key}-${value}`) .addClass('is-on'); }); } DOM을 직접 핸들링, 장황하고 UI의 형태를 한눈에 파악하기 힘듦
  • 21.
    _changeTabs() { const fragment= this._parseFragment(); if (fragment === null) { return; } _.each(fragment, (value, key) => { const target = $(_.find(this.$sections, i => { return $(i).dataset('type') === key); }); // 선택한 탭으로 변경한다. target.find('.cs-tab li').removeClass('is-on'); target.find('.cs-tab li') .has(`a[href='#${value}']`).addClass('is-on'); // 탭에 해당하는 목록으로 변경한다. target.find('.cs-body').removeClass('is-on'); target.find(`.cs-body#cs-${key}-${value}`) .addClass('is-on'); }); } 상태를 class나 data attr에 설정 및 사용하여 전체적인 상태 흐름을 알기 힘듦
  • 22.
    _changeTabs() { const fragment= this._parseFragment(); if (fragment === null) { return; } _.each(fragment, (value, key) => { const target = $(_.find(this.$sections, i => { return $(i).dataset('type') === key); }); // 선택한 탭으로 변경한다. target.find('.cs-tab li').removeClass('is-on'); target.find('.cs-tab li') .has(`a[href='#${value}']`).addClass('is-on'); // 탭에 해당하는 목록으로 변경한다. target.find('.cs-body').removeClass('is-on'); target.find(`.cs-body#cs-${key}-${value}`) .addClass('is-on'); }); } DOM API의 비용은 비쌈. 상태 변화를 위해 
 DOM API 호출이 남용됨
  • 23.
    _changeTabs() { const fragment= this._parseFragment(); if (fragment === null) { return; } _.each(fragment, (value, key) => { const target = $(_.find(this.$sections, i => { return $(i).dataset('type') === key); }); // 선택한 탭으로 변경한다. target.find('.cs-tab li').removeClass('is-on'); target.find('.cs-tab li') .has(`a[href='#${value}']`).addClass('is-on'); // 탭에 해당하는 목록으로 변경한다. target.find('.cs-body').removeClass('is-on'); target.find(`.cs-body#cs-${key}-${value}`) .addClass('is-on'); }); } UI 상태 변화를 위한 render 외의 부가적 메서드 다량 정의
  • 24.
    프리젠테이션 로직은 크고복잡. 테스트하고 유지보수하기 괴롭다
  • 25.
    Functional Programming 컴포넌트 접근법은함수형 프로그래밍하고도 관련이 깊다 부작용(Side effect)없이 하나의 일을 잘 수행하는 함수 그러한 함수를 조합 / 구성함으로써 큰 문제를 해결 F2 F3 F4 F5F1
  • 26.
    {
 “type”: “oval”,
 “size”: 32
 } FunctionalProgramming and UI Component View = Function(state)
  • 27.
    function Header({title, desc}){ return ( <header className="header"> <div className="header__inner"> <h1 className="header__title">{title}</h1> <p className="header__description">{desc}</p> </div> </header> ); } View = Function(state) REMEMBER
  • 28.
    Header({ title: 'Hello World!', desc:'This is Awesome.' }); <header class="..."> <div class="..."> <h1 class="...">Hello World!</h1> <p class="...">This is Awesome.</p> </div> </header>
  • 29.
    <header class="..."> <div class="..."> <h1class="...">Hello World!</h1> <p class="...">This is Awesome</p> </div> </header> <header class="..."> <div class="..."> <h1 class="...">Welcome</h1> <p class="...">This is Awesome</p> </div> </header> Equal?
  • 30.
    <header class="..."> <div class="..."> <h1class="...">Hello World!</h1> <p class="...">This is Awesome</p> </div> </header> <header class="..."> <div class="..."> <h1 class="...">Welcome</h1> <p class="...">This is Awesome</p> </div> </header> Equal?
  • 31.
    <header class="..."> <div class="..."> <h1class="...">Hello World!</h1> <p class="...">This is Awesome</p> </div> </header> <header class="..."> <div class="..."> <h1 class="...">Welcome</h1> <p class="...">This is Awesome</p> </div> </header> Equal? FAIL
  • 32.
  • 33.
    HTML(TMPL), CSS, 자바스크립트를분리하는게 당연한 관심사의 분리라고 생각 과거 우리는.
  • 34.
    Templates separate technologies,not concerns. 템플릿은 기술의 분리일 뿐 관심사의 분리가 아니다. Pete Hunt, React developer 페이트 헌트, 리액트 개발자 “ ”
  • 37.
    KingCard Component 중요한건 해결하고자하는 문제 같은 문제를 해결한다면 관심사는 같다
  • 38.
    이때, 분리된 환경보다 한 곳에 있는 경우가 훨씬 수월하다 관심사가 같다면 수정 시, 함께 수정해야 할 확률이 높다
  • 39.
    HTML, CSS를 어떻게든셋팅한 후 자바스크립트를 이어서 테스트 UI 테스트에 대한 자원 준비 없이 곧바로 컴포넌트를 테스트
  • 40.
    Test IconText Component it('iconDirection속성에 left를 지정하면 아이콘이 좌측에 배치된다.', () => { // Given // When const wrapper = shallow( <IconText label="친구초대" icon="add-friend" iconDirection="left" /> ); // Then const children = wrapper.children('.icon-text__inner').children(); expect(children.first()).to.have.className('icon-text__icon'); expect(children.last()).to.have.className('icon-text__label'); });
  • 41.
    "## Header $ "##Header.js $ "## Header.test.js $ %## style.scss "## TallCard $ "## TallCard.js $ "## TallCard.test.js $ %## style.scss "## KingCard $ "## KingCard.js $ "## KingCard.test.js $ %## style.scss %## StickyBar "## StickyBar.js "## StickyBar.test.js %## style.scss 따라서 관련있는 것은 최대한 가까이에 두는 구조가 현재 유행
  • 42.
    const Button =(props) => ( <button className={'large' in props && 'large'}> {props.children} <style jsx>{` button { padding: 20px; background: #eee; color: #999 } .large { padding: 50px } `}</style> </button> ); 또는 하나의 파일 내 모든 관심사를 작성하는 방식도 선호
  • 43.
    뭘 좀 만들어봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  • 44.
    뭘 좀 만들어봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  • 45.
    뭘 좀 만들어봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  • 46.
    뭘 좀 만들어봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석 <TallCard/> 를 만들어 봅시다.
  • 47.
    BEM(Block, Element, Modifier) 컴포넌트와어울리게 마크업하기 Block 애플리케이션의 구성 요소로서 독립된 존재 Element 블록을 구성하는 작은 단위 또는 자식 개체 Modifier 블록이나 요소의 테마, 동작을 표현
  • 48.
  • 49.
    <div class="tall-card tall-card_badge_up"> <divclass="tall-card__badge"> <i class=“tall-card__badge-up"> <span class="blind">UP</span> </i> </div> <div class="tall-card__thumbnail"> <img class="tall-card__image" src="" alt=""/> </div> <div className="tall-card__information"> <h5 className="tall-card__title">레바툰</h5> <dl class="tall-card__meta"> <dt class=“tall-card__meta-key">장르</dt> <dd class=“tall-card__meta-value">개그 / 일상</dd> <dt class=“tall-card__meta-key">작가</dt> <dd class=“tall-card__meta-value">레바</dd> </dl> </div> </div> Markup with BEM
  • 50.
    const TallCard =({title, image, badge, metadata = []}) => ( <div className={`tall-card ${badge === 'up' ? ‘tall-card_badge_up' … `}> <div className="tall-card__badge"> <i className=“tall-card__badge-up”/> </div> <div className="tall-card__thumbnail"> <img className="…" src={image} alt={`${title}의 썸네일`}/> </div> <div className="tall-card__information"> <h5 className="tall-card__title">{title}</h5> {metadata.length > 0 ? ( <dl className=“tall-card__meta"> ${metadata.map(({key, value}) => ([ <dt className=“tall-card__meta-key">{key}</dt>, <dt className=“tall-card__meta-key">{value}</dt> ]))} </dl> ) : null} </div> </div> ); Create React Component
  • 51.
    $module: 'icon-text'; .#{$module} { &__badge{ /*...*/ &__up {/*...*/} } &__thumbnail { /*...*/ &__image {/*...*/} } &__information {/*...*/} &__title {/*...*/} &__meta {/*…*/} &__meta-key {/*...*/} &__mata-value {/*...*/} } Styling with SASS SASS의 interpolation은 컴포넌트 접근법과 잘 어울린다 스타일링 영역을 문법적으로 명확히 표현할 수 있다
  • 52.
    "## TallCard "## TallCard.js %##TallCard.scss "## TallCard "## TallCard.js "## TallCard.html %## TallCard.scss File structure 마크업 코드를 템플릿으로 분리한 경우 마크업을 자바스크립트에 포함한 경우
  • 53.
    Context Free <CardGroup/><SquareCard/> type= subscriptions 상위 컴포넌트 스타일이 하위 컴포넌트 스타일에 영향을 주면 안된다 컴포넌트는 어떤 문맥에서든 자유로워야 한다
  • 54.
    badge = upsensational = true 만약 특정 문맥에서 컴포넌트가 변화한다면 해당 컴포넌트에 Context Free <SquareCard/> badge = new 새로운 변환자(Modifier)를 추가한다
  • 55.
    Context Free <CardGroup/> CardList 컴포넌트는badge가 up인 SquareCard 컴포넌트를 자식 컴포넌트로 포함하고 있을 뿐이다 type = subscriptions
  • 56.
    타인의 잣대에 얽매이면행복할 수 없듯 컴포넌트 역시 특정 문맥에 얽매이면 행복할 수 없다
  • 57.