[115] clean fe development_윤지수

1,498 views

Published on

[115] clean fe development_윤지수

Published in: Technology
0 Comments
17 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,498
On SlideShare
0
From Embeds
0
Number of Embeds
87
Actions
Shares
0
Downloads
67
Comments
0
Likes
17
Embeds 0
No embeds

No notes for slide

[115] clean fe development_윤지수

  1. 1. CLEAN FRONTEND DEVELOPMENT YOUN JISU, CodeSquad DEVIEW 2016
  2. 2. Intro. 요즘 JavaScript 개발 이야기
  3. 3. Intro 오늘 이야기는 이쯤에 맞춥니다 IE6 Modern Browser vanilla Full Framework static Web page SPA
  4. 4. imgae: https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f#.mdtm38i8o Intro JavaScript Fatigue >.<
  5. 5. http://stateofjs.com/2016/opinions/ JavaScript 개발은 복잡하다 Intro
  6. 6. http://stateofjs.com/2016/opinions/ JavaScript 개발은 빠르게 변하고 있 다 Intro
  7. 7. http://stateofjs.com/2016/opinions/ 하지만 올바른 방향으로 가는 중 Intro
  8. 8. Frameworks 의 불편함 우량한 아이들 새로운 아이들 예측하기 어려운 미래 전략 병주고 약주고.. (SPA + Server-side rendering?) 브라우저 지원범위. 커뮤니티, 문서화, 생태계, 급격한 변화 다양한 Syntax와 Pattern 이 존재 새로운 Learning costs Intro 공통적으로
  9. 9. On January 12, 2016 Microsoft announced end of support for IE10, IE9, and IE8. > Progressive Enhancement 전략과 함께 CSS3, ES 최신 버전의 사용도 가능 주변은, Intro Mobile Web Browser 의 꾸준한성장 > static view 중심의 서비스 > data 처리보다는 seamless한 animation 처리가 더 중요 ES2015,ES2016,ES2017 (thanks to jQuery, underscor, lodash) > 다양한 readable syntax 제공. 다양한 라이브러리와의 이별 Standard transpilers (not Typescript, coffeeScript, JSX) with Builders > ES를 더 땡길 수 있는 기회!
  10. 10. 우리 서비스는 지속가능해야 한다 성능을 깊게 개선할 수 있어야 한다 동료가 쉽게 이해할 수 있는 코드를 구현해야 한다 다양한 환경에서 동작가능해야 한다 항상 더 좋은 UX를 고민하자 jQuery 개발자보단 Front-end 개발자가 되면 좋겠다 한편, FE개발자의 본능적 미션 ! Intro “ ”
  11. 11. standard Code, with all tools 이런 전략은 어떨까? Intro standard Code, with all tools
  12. 12. “Polyfill, Transpiler" based Architecture transpiling ES6 based service codes ES5 Codes Polyfills Utilities codes Production code Intro
  13. 13. FUTURE ! Intro transpiling ES6 based service codes ES5 Codes Polyfills Utilities codes Production code
  14. 14. jQuery Free
  15. 15. <!-- jQuery --> <script src="vendor/jquery/jquery.min.js"></script> <!-- Bootstrap Core JavaScript --> <script src="vendor/bootstrap/js/bootstrap.min.js"></script> <!-- Plugin JavaScript --> <script src=“vender/jquery/jquery.easing.min.js”></script> <!-- Theme JavaScript --> <script src="js/agency.min.js"></script> 간단한 modal control, scroll effect 에 필요한 JavaScript 파일들 jQuery Free
  16. 16. Scroll 효과 정도는 Vanilla로 충분 let mainNav = document.querySelector(“#mainNav"); window.addEventListener("scroll", (evt) => { let currentScrollY = window.pageYOffset; if(currentScrollY > MY_STATIC.SCROLL_TOP_CHANGE) { if(DOWNMODE) return; mainNav.classList.add("affix"); DOWNMODE = true; } else { mainNav.classList.remove("affix"); DOWNMODE = false;; } }); jQuery Free let mainNav = document.querySelector(“#mainNav"); window.addEventListener("scroll", (evt) => { let currentScrollY = window.pageYOffset; if(currentScrollY > MY_STATIC.SCROLL_TOP_CHANGE) { if(DOWNMODE) return; mainNav.classList.add("affix"); DOWNMODE = true; } else { mainNav.classList.remove("affix"); DOWNMODE = false;; } }); demo
  17. 17. Event Delegation el.addEventListener("click", (evt) => { evt.preventDefault(); let ellink = evt.target.closest(".portfolio-link"); if(!ellink) return; let sHTML = this.myAsync.execAsyncAwait(ellink("href").substring(1)); document.querySelector("footer").insertAdjacentHTML('afterend', sHTML); }); jQuery Free Element.closest, matches 메서드만 잘써도 :-)
  18. 18. 은근 다 있다 new XMLHttpRequest() xhr.addEventListener document.createElement setAttribute getElementsByTagName requestAnimationFrame getBoundingClientRect Object.prototype.toString.call JSON.parse bind JSON.stringify unshift splice Object.keys forEach isArray Date.now evt.stopPropagation Math.round parseInt cloneNode insertBefore appendChild localStorage.setItem evt.changedTouches[0].pageY querySelector jQuery Free
  19. 19. jQuery Free if(bMouseEvent) { pageX = evt.pageX; pageY = evt.pageY; } else { pageX = evt.changedTouches[0].pageX; pageY = evt.changedTouches[0].pageY; } this.nStartPosX = Math.floor(pageX); this.nStartPosY = Math.floor(pageY); this.nStartTranslateX = _cu.getTranslate3dX(this.elTarget); this.nTouchStartTime = Date.now(); } handlerTouchMove(evt) { evt.stopPropagation(); if(this.bAnimationing) return; if(!this.bSwipe) return; let bMouseEvent = evt.type.substr(0,5) === "mouse"; let htCurrentEvt = (bMouseEvent) ? evt : evt.changedTouches[0]; this.nMovePosX = Math.floor(htCurrentEvt.pageX); this.nMovePosY = Math.floor(htCurrentEvt.pageY); …. Mobile Web & Vanilla 자연스럽다
  20. 20. DOM Append Performance var $c = $("<div />").addClass("container"); for (var i=0; i<10000; i++) { $c.append($("<div />").addClass("test-div")); } $("BODY").append($c); 432.524ms var c = document.createDocumentFragment(); for (var i=0; i<10000; i++) { var e = document.createElement("div"); e.className = "test-div"; c.appendChild(e); } document.body.appendChild(c); 16.237ms https://howchoo.com/g/mmu0nguznjg/learn-the-slow-and-fast-way-to-append-elements-to-the-dom jQuery Pure JavaScript jQuery Free
  21. 21. https://medium.com/the-vue-point/vue-2-0-is-here-ef1f26acf4b8#.7rbhdnuyp rendering layer benchmark Oct,1,2016 by VueJS DOM rendering layer performance jQuery Free
  22. 22. jQuery Plugin vanillaJS Component 최근 5년간 jQuery Plugin Trend jQuery Free
  23. 23. Clean jQuery Free VanillaJS는 더 지속 가능한 코드다. 모바일웹에서 VanillaJS는 훌륭한 조합. PC에서도 polyfill을 잘 활용하며 jQuery Free를! pure DOM API가 대부분 상황에서는 가장 빠르다. jQuery Free
  24. 24. Modules
  25. 25. Modules import (ES2015) import { member } from "module-name"; export default function (…) { … } good-bye AMD API (Require.js)..
  26. 26. CLASS import tc from '../lib/TabComponent.js'; class MyAsync{ constructor() { this.name = "xhr"; } simpleFetch(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest(); req.addEventListener("load", function() { let sData = JSON.parse(req.responseText); if(typeof sData !== "object") reject("wrong data"); else resolve(sData); }); req.open("GET", url); req.send(); }); } Modules
  27. 27. Code scroll effect코드를 import, export로 모듈화 Modules
  28. 28. Clean Modules Use Class & Import & Export !! Modules
  29. 29. Two-way Data Binding
  30. 30. Two way binding Two-way data binding 데이터가 변경될때, UI를 변경 UI가 변경될때, 데이터를 변경
  31. 31. Two way binding Dirty Checking (digest cycle) 작은 변화에 과한 렌더링. 지속적인 CPU 부하 Proxy (ES2015) Interception API
  32. 32. Two way binding let msgData = {}; let elMessage = document.getElementById("message"); let msgProxy = new Proxy(msgData, { get: function(target, name, receiver) { return target[name]; }, set: function(obj, prop, value) { obj[prop] = value; elMessage.value = value; document.getElementById("text-count").textContent = value.length; return true; } }); elMessage.addEventListener("input", (evt)=>{ msgProxy.content = evt.target.value; }); Proxy
  33. 33. Two way binding 그런데 Proxy는 좀더 걸릴 듯 하다.. babel polyfill 로도 지원하지 않는 중
  34. 34. Two way binding Simple Accessor ES5 getter/setter 통해 비교적 간단히 구현가능. Object.defineProperty(obj, prop, descriptor)
  35. 35. Two way binding bindModel(obj, property, elView, fnCallback) { Object.defineProperty(obj, property, { get: function() { return elView.value; }, set: function(newValue) { elView.value = newValue; fnCallback(newValue); }, configurable: true }); } Simple Accessor
  36. 36. Two way binding demo Data binding using ‘bindModelInput’
  37. 37. Two way binding Clean Two-way data binding Two-way data binding은 framework에서만 가능한 건 아님. Proxy API를 지켜보자. 간단한 data binding은 defineProperty 로 간단히 처리!
  38. 38. Asynchronous
  39. 39. simpleAjax("../data/first.json", function(data) { try { var name = data.user.name; var imgUrl = "../data/img/" + name + ".json"; simpleAjax(imgUrl, function(data2) { try { setTimeout(function(){ try { var aImage = data2.images; aImage.forEach((v) => logMsg(elLog,v)); } catch (err) { console.log('error 3' , err ); } },500); } catch (err) { console.log('error 2' , err ); } }); } catch (err) { console.log("error 1: ", err); } }); Asynchronous 가끔 브라우저에서도 Hell
  40. 40. Asynchronous 다행인지, Pure JavaScript에서는 그 해법이 쏟아져 나옴. > Synchronous-looking style
  41. 41. Asynchronous Promise A+ • 비동기 로직에서 콜백의 분리 • thenable • async의 밑바탕 기술 function simpleFetch(url) { return new Promise(function(resolve, reject){ var req = new XMLHttpRequest(); req.addEventListener("load", function() { let htData = JSON.parse(req.responseText); if(typeof htData !== "object") reject("wrong data"); else resolve(htData); }); req.open("GET", url); req.send(); }); } simpleFetch("../data/first.json") .then(function(data) { var name = data.user.name; var imgUrl = "../data/img/" + name + ".json"; return simpleFetch(imgUrl); }) .then(function(data2) { return simpleSetTimeoutPromise(500,data2); }) .then(function(data3) { let elLog = document.querySelector(".log"); let aImage = data3.images; aImage.forEach((v) => logMsg(elLog,v)); }) .catch(function(err){ console.log("error : " + err); })
  42. 42. Asynchronous generator/yield 딱 비동기 문제를 위한 것만은 아니다. Promise와 조합하는게 좋고, 단, generator를 조정하는 역할을 하는 코드를 구현 함(runner) function *myGeneratorWithPromise() { try { var data = yield simpleFetch("../data/first.json"); var url2 = "../data/img/" + data.user.name + ".json"; var data2 = yield simpleFetch(url2); var aImage = data2.images; //do something.. } //code : https://davidwalsh.name/async-generators. function runGenerator(g) { var it = g(), ret; (function iterate(val){ ret = it.next( val ); if (!ret.done) { if (typeof ret.value === "object" && ("then" in ret.value)) { ret.value.then( iterate ); } else { setTimeout( function(){ iterate( ret.value ); }, 0 ); } } })(); } fnlist.generatorAndPromise = function() { runGenerator(myGeneratorWithPromise); }
  43. 43. Asynchronous wow! async/await ! 뭐 이건…좋다. async 함수는 promise를 반환한다는 점을 주 ES2017 이라는 소문 +.+ 하지만 Babel과 함께라면 당장! (generator 로 변환) fnlist.asyncAwait = async function () { try { var data = await simpleFetch("../data/first.json"); var url2 = "../data/img/" + data.user.name + ".json"; var data2 = await simpleFetch(url2); var aImage = data2.images; //parallel task using Promise.all function. var data3 = await Promise.all([ simpleSetTimeoutPromise(100, aImage), simpleSetTimeoutPromise(500, "dummy"), simpleSetTimeoutPromise(1000, "dummy"), ]); var elLog = document.querySelector(".log"); data3[0].forEach((v) => logMsg(elLog,v)); } catch (err) { console.log("error during myGenerator : ", err); } } } thanks to WebPack&Babel
  44. 44. Asynchronous observable (rx.js) 아.. 음.. functional programming의 필요성을 진심 느끼는 분들께. var chainPromise = Rx.Observable.just('../data/first.json') .flatMap(simpleFetch) .map( (x) => { return ("../data/img/" + x.user.name +".json")}) .flatMap(simpleFetch) .delay(500) .subscribe((x) => { let elLog = document.querySelector(".log"); let aImage = x.images; aImage.forEach((v) => logMsg(elLog,v)); }); thanks to WebPack&Babel
  45. 45. Asynchronous Clean Asynchronous 그럭저럭 콜백 지옥 상황에선, > 콜백 몸체를 분리하는 정도만으로 충분 빈번한 콜백 지옥이라면, > async,await 와 같은 다양한 방법으로 코드를 clean하게 구현
  46. 46. Templating
  47. 47. Pure Templating Engine var templateRunner = function(sHtml){ return function(oData){ for(var value in oData){ var re = "{%s?" + value + “s?%}"; sHtml = sHtml.replace(new RegExp(re, "ig"), oData[value]) } return sHtml; }; }; simple ! Templating
  48. 48. 서비스 logic 은 clean하게 유지. Template HTML은 쉽게 관리. PreCompile 로 좀더 갈끔하게 template html template html template html PreCompile [Build Step] template function [Service Step] 복잡한 상황에서는 ? Templating
  49. 49. PreCompile loaders: [ { test : /-tpl.hbs$/, loader : 'handlebars' }, … Webpack handlebar-loader Templating
  50. 50. Templating with ES2015 Modules var listTpl = require('/template/portfolioModal-tpl.hbs'); listTpl( { name: ‘hello codeSquad' } ); import modalTpl from '/template/portfolioModal-tpl.hbs'; listTpl( { name: ‘hello codeSquad' } ); Templating
  51. 51. Clean Templating 간단한 templating 은 replace 로 간단히 구현 복잡한 작업에서는 로직과 html을 잘 분리 Templating
  52. 52. Virtual DOM view 처리의 또다른 해법
  53. 53. VIEW조작의 복잡도를 해결하기 위해 virtual DOM 이 필요하고, 그래서 React를 써야 한다며? Virtual DOM > 일단, Virtual DOM 자체가 pure DOM보단 항상 빠른 건 아니다.
  54. 54. https://vdom-benchmark.github.io/vdom-benchmark/ virtual DOM은 React가 짱인가? simple한 virtual DOM 라이브러리가 2~3배 빠르다 Virtual DOM
  55. 55. <ul className=”list”> <li>item 1</li> <li>item 2</li> </ul> h(‘ul’, { className: ‘list’ }, h(‘li’, {}, ‘item 1’), h(‘li’, {}, ‘item 2’), ); 참고, Virtual DOM 만들기 과정 JSX Transpiled code JavaScript Object thanks to WebPack&Babel Virtual DOM { type: ‘ul’, props: { className: ‘list’ }, children: [ { type: ‘li’, props: {}, children: [‘item 1’] }, { type: ‘li’, props: {}, children: [‘item 2’] } ] }
  56. 56. function renderMaquette() { return h("ul.ul-wrap", [ aData.map(function(value, index) { return h("li" , [ h("span", [""+ value]) ]); }) ]); } projector.replace(domNode, renderMaquette); template library 와 비스무리. Virtual DOM Simple VD example(maquette.js)
  57. 57. Simple VD example(maquette.js) Virtual DOM
  58. 58. Clean Virtual DOM VD는 성능이 분명 빠를 때 사용하기 사용해야하는 경우 코드의 복잡도는 더 올라갈 수 있음. 가벼운 VD 라이브러리가 좋은 선택지가 될 수 있음. Virtual DOM
  59. 59. Loose Coupling Dependency Injection
  60. 60. MODAL Loose Coupling XHR JSONP Local Storage 의존성있는 동일한 행위의 Class들과의 관계 느슨하게 유지하기 XHR JSONP Local Storage Loose Coupling
  61. 61. let dependencyContainer = { modalDependency : { storagePlugin : new XHR(), otherPlugin : new MyAsync() } } let oModal = new Main(dependencyContainer.modalDependency); Loose Coupling 조금 아쉬움이 있다 직접 class를 쓴 거랑 큰 차이는 안나고, Lazy instantiation 이 안되고. Static Dependency Injection
  62. 62. Loose Coupling Register1 {XHR, Other2} new mainClass(...aDepClassInstance) getInstance2 Injection3 DI Factory XHR JSONP Local Storage Other1 Other2 Other3 Dependencies Class XHR Other2 Main Class Dependency Injection
  63. 63. Loose Coupling class Service { constructor(storagePlugin, otherPlugin) { this.storagePlugin = storagePlugin; this.otherPlugin = otherPlugin; } getStorageData() { return this.storagePlugin.getData(); } getOtherData() { return this.otherPlugin.getData(); } } Main Class Dependency Injection 의존성 있는 Class과의 관계는 느슨하다.
  64. 64. Loose Coupling //register depedencies let myDI = new DIFactory(); myDI.register('xhr', XHR); myDI.register('jsonp', JSONP); myDI.register('other', Other); //dependency injection let cInstance = myDI.getInstance(Service, ["xhr","other"]); console.log(cInstance.getStorageData()); Service code Dependency Injection Dependency 관계를 미리 정의하고, 인스턴스를 나중에 생성
  65. 65. Loose Coupling class DIFactory { constructor() { this.matching = {}; } register(name, dClass) { this.matching[name] = new dClass(); } getInstance(mainClass, aDepClass) { let aDepClassInstance = aDepClass.map((v) => { return this.matching[v]; }); return new mainClass(...aDepClassInstance); } } DI Factory 이렇게 처리하기 위해 필요한 DI Factory 는 겨우 이정도
  66. 66. Code Dependency Injection using simple factory
  67. 67. Register function User custom function subscribe publish ‘FN_AFTER_FOCUS’ ‘FN_AFTER_SUBMIT’ Remove methods of Dependencies. Resuable Codes Plugin function Emit Event Plugin function Loose Coupling Custom Function & Event Driven
  68. 68. Resuable Codes Common Component AA Component BB Component CC Component A-1 plugin A-2 plugin B-1 plugin B-2 plugin C-1 plugin C-2 plugin utility library Component의 공통 기능 option 설정 plugin 등록 모든 파일에서 참고하는 유틸리티 메서 animation 처리 css 속성 추가 null 체크 .. Components 해당 component에 필요한 기능 Plugins option으로 제공되는 추가기능. LooseCoupling -> Resuable Component ! Loose Coupling
  69. 69. Clean Loose Coupling Dependency를 줄이는 패턴은 어렵고 복잡할 필요는 없다. 간단한 방법도 있다 커다란 Framework에서만 구현 가능한 것도 아니다. 다양하게 구현된 Module, Component 상황에 최적의 방법이 무엇인지 고민하 Loose Coupling
  70. 70. 사용된 코드는 여기서, https://github.com/nigayo/cleanfe
  71. 71. Standard code with all tools, 사용자에게 더 좋은 UX를 제공하고, 개발에서 또 다른 즐거움을 느끼길 바랍니다 :-) 마무리 그리고, 앞으로 더 많은 분들과 함께, 이런 고민을 하게 되길 바라겠습니다 윤지수, codesquad crong@codesquad.kr

×