Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

3,208 views

Published on

.

Published in: Technology
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1. 컴포넌트 관점에서 개발하기 2017 프론트엔드 트렌드&인사이트
  2. 2. 주우영 프런트엔드 개발 시작하기 네이버는 이렇게 한다!, 위키북스 JavaScript Promise 한빛 eBook, 한빛미디어 페이스북 프론트엔드개발 그룹 운영 https://www.facebook.com/groups/webfrontend/ 레진엔터테인먼트 프론트엔드개발팀
  3. 3. 오늘, 새로운 기술과 기능을 나열하기 보다
  4. 4. 현대 UI 개발에 있어 중요히 여기는 부분에 대해 이야기합니다.
  5. 5. 현대 프레임워크의 공통된 접근법 컴포넌트 단위로 사고하고 디자인
  6. 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. 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. 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. 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. 10. WHY COMPONENT?
  11. 11. 하나의 문제를 잘 해결하는 COMPONENT 프로그램
  12. 12. 새로운 방법이 아닌 예로부터 모든 공학 분야의 공학자가 자주 사용해 오던 문제 해결 방법
  13. 13. 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.
  14. 14. $ 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
  15. 15. 사고의 분산을 막고 하나의 문제에 집중함으로써 효율적으로 개발할 수 있다
  16. 16. 설계의 오류를 쉽게 파악할 수 있고 상대적으로 테스트하기 쉽다
  17. 17. 이식성을 높일 수 있고 변경에 유연하게 대체할 수 있다
  18. 18. API UI는 자주적으로 동작하는 또 다른 프로그램, 그것을 API로 제어한다는 사고로 접근 자바스크립트를 개발할 때 우리는
  19. 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. 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. 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. 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. 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. 24. 프리젠테이션 로직은 크고 복잡. 테스트하고 유지보수하기 괴롭다
  25. 25. Functional Programming 컴포넌트 접근법은 함수형 프로그래밍하고도 관련이 깊다 부작용(Side effect)없이 하나의 일을 잘 수행하는 함수 그러한 함수를 조합 / 구성함으로써 큰 문제를 해결 F2 F3 F4 F5F1
  26. 26. {
 “type”: “oval”,
 “size”: 32
 } Functional Programming and UI Component View = Function(state)
  27. 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. 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. 29. <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?
  30. 30. <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?
  31. 31. <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
  32. 32. Snapshot Testing with Jest
  33. 33. HTML(TMPL), CSS, 자바스크립트를 분리하는게 당연한 관심사의 분리라고 생각 과거 우리는.
  34. 34. Templates separate technologies, not concerns. 템플릿은 기술의 분리일 뿐 관심사의 분리가 아니다. Pete Hunt, React developer 페이트 헌트, 리액트 개발자 “ ”
  35. 35. KingCard Component 중요한건 해결하고자 하는 문제 같은 문제를 해결한다면 관심사는 같다
  36. 36. 이때, 분리된 환경 보다 한 곳에 있는 경우가 훨씬 수월하다 관심사가 같다면 수정 시, 함께 수정해야 할 확률이 높다
  37. 37. HTML, CSS를 어떻게든 셋팅한 후 자바스크립트를 이어서 테스트 UI 테스트에 대한 자원 준비 없이 곧바로 컴포넌트를 테스트
  38. 38. 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'); });
  39. 39. "## 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 따라서 관련있는 것은 최대한 가까이에 두는 구조가 현재 유행
  40. 40. 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> ); 또는 하나의 파일 내 모든 관심사를 작성하는 방식도 선호
  41. 41. 뭘 좀 만들어 봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  42. 42. 뭘 좀 만들어 봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  43. 43. 뭘 좀 만들어 봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석
  44. 44. 뭘 좀 만들어 봅시다. 이런 UI를 개발해 달라고 했을때 가장 먼저 한가지 일을 수행하는 최소 단위를 분석 <TallCard/> 를 만들어 봅시다.
  45. 45. BEM(Block, Element, Modifier) 컴포넌트와 어울리게 마크업하기 Block 애플리케이션의 구성 요소로서 독립된 존재 Element 블록을 구성하는 작은 단위 또는 자식 개체 Modifier 블록이나 요소의 테마, 동작을 표현
  46. 46. 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) 컴포넌트와 어울리게 마크업하기
  47. 47. <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
  48. 48. 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
  49. 49. $module: 'icon-text'; .#{$module} { &__badge { /*...*/ &__up {/*...*/} } &__thumbnail { /*...*/ &__image {/*...*/} } &__information {/*...*/} &__title {/*...*/} &__meta {/*…*/} &__meta-key {/*...*/} &__mata-value {/*...*/} } Styling with SASS SASS의 interpolation은 컴포넌트 접근법과 잘 어울린다 스타일링 영역을 문법적으로 명확히 표현할 수 있다
  50. 50. "## TallCard "## TallCard.js %## TallCard.scss "## TallCard "## TallCard.js "## TallCard.html %## TallCard.scss File structure 마크업 코드를 템플릿으로 분리한 경우 마크업을 자바스크립트에 포함한 경우
  51. 51. Context Free <CardGroup/><SquareCard/> type = subscriptions 상위 컴포넌트 스타일이 하위 컴포넌트 스타일에 영향을 주면 안된다 컴포넌트는 어떤 문맥에서든 자유로워야 한다
  52. 52. badge = up sensational = true 만약 특정 문맥에서 컴포넌트가 변화한다면 해당 컴포넌트에 Context Free <SquareCard/> badge = new 새로운 변환자(Modifier)를 추가한다
  53. 53. Context Free <CardGroup/> CardList 컴포넌트는 badge가 up인 SquareCard 컴포넌트를 자식 컴포넌트로 포함하고 있을 뿐이다 type = subscriptions
  54. 54. 타인의 잣대에 얽매이면 행복할 수 없듯 컴포넌트 역시 특정 문맥에 얽매이면 행복할 수 없다
  55. 55. 감사합니다.

×