Upgrade to Pro — share decks privately, control downloads, hide ads and more …

프론트엔드 모던 프레임워크 _ 한성민 [GDG DevFest Seoul 2017]

프론트엔드 모던 프레임워크 _ 한성민 [GDG DevFest Seoul 2017]

Sungmin Han

October 15, 2023
Tweet

More Decks by Sungmin Han

Other Decks in Technology

Transcript

  1. import { Component, ViewChild, ElementRef } from '@angular/core';
    const DEFAULT_INITIALIZE_FOOD_LIST: string[] = [ '치킨', '탕수육',
    '닭도리탕' ];
    @Component({
    selector: 'mukkit-list',
    templateUrl: './mukkit_list.html',
    styleUrls: [ './mukkit_list.css' ]
    })
    export class MukkitListComponent {
    public foodList: string[] = [...DEFAULT_INITIALIZE_FOOD_LIST];
    public newFood: string;
    @ViewChild('input') inputEl: ElementRef;
    ngAfterViewInit() {
    this.focusFood();
    }
    focusFood(): void {
    this.inputEl.nativeElement.focus();
    }
    enterFood($event: KeyboardEvent): void {
    if ($event.keyCode === 13) {
    this.addFood();
    }
    }
    addFood(): void {
    if (this.foodList.indexOf(this.newFood) === -1) {
    this.foodList.push(this.newFood);
    this.newFood = '';
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    }
    mukkit_list.component.ts

    View full-size slide

  2. mukkit_list.html


    먹킷리스트



    {{food}}


    #input
    [(ngModel)]="newFood"
    (keypress)="enterFood($event);">
    먹킷리스트 추가


    View full-size slide

  3. mukkit_list.html


    먹킷리스트



    {{food}}


    #input
    [(ngModel)]="newFood"
    (keypress)="enterFood($event);">
    먹킷리스트 추가


    export class MukkitListComponent {
    public foodList: string[] = [...DEFAULT_INITIALIZE_FOOD_LIST];
    public newFood: string;
    @ViewChild('input') inputEl: ElementRef;

    enterFood($event: KeyboardEvent): void {

    }
    addFood(): void {

    }
    }
    mukkit_list.component.ts
    1way binding (viewmodel -> view)
    2way binding (videmodel <-> view)
    view query (it is not bind)
    1way binding (view <- viewmodel)

    View full-size slide

  4. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  5. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  6. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  7. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  8. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  9. MukkitList.js
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './MukkitList.css';
    const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ];
    class MukkitList extends Component {
    constructor(props) {
    super(props);
    this.state = {
    foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],
    newFood: ''
    };
    }
    changeFood(event) {
    this.setState({'newFood': event.target.value});
    }
    enterFood(event) {
    if (event.key === 'Enter') this.addFood();
    }
    addFood(event) {
    if (this.state.foodList.indexOf(this.state.newFood) === -1) {
    this.setState({
    foodList: [...this.state.foodList, this.state.newFood],
    newFood: ''
    });
    } else {
    alert('해당 음식은 이미 있습니다.');
    }
    }
    render() {
    return (



    먹킷리스트


    {this.state.foodList.map((food, index) => {food})}

    View full-size slide

  10. MukkitList.vue



    {{food}}


    ref="input"
    v-model="newFood"
    v-on:keypress.enter="addFood">
    먹킷리스트 추가



    <br/>const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ]<br/>export default {<br/>name: 'MukkitList',<br/>data () {<br/>return {<br/>foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],<br/>newFood: ''<br/>}<br/>},<br/>mounted () {<br/>this.focusFood()<br/>},<br/>methods: {<br/>focusFood () {<br/>this.$refs.input.focus()<br/>},<br/>addFood () {<br/>if (this.foodList.indexOf(this.newFood) === -1) {<br/>this.foodList.push(this.newFood)<br/>this.newFood = ''<br/>this.focusFood()<br/>} else {<br/>alert('해당 음식은 이미 있습니다.')<br/>}<br/>}<br/>}<br/>}<br/>

    View full-size slide

  11. MukkitList.vue



    {{food}}


    ref="input"
    v-model="newFood"
    v-on:keypress.enter="addFood">
    먹킷리스트 추가



    <br/>const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ]<br/>export default {<br/>name: 'MukkitList',<br/>data () {<br/>return {<br/>foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],<br/>newFood: ''<br/>}<br/>},<br/>mounted () {<br/>this.focusFood()<br/>},<br/>methods: {<br/>focusFood () {<br/>this.$refs.input.focus()<br/>},<br/>addFood () {<br/>if (this.foodList.indexOf(this.newFood) === -1) {<br/>this.foodList.push(this.newFood)<br/>this.newFood = ''<br/>this.focusFood()<br/>} else {<br/>alert('해당 음식은 이미 있습니다.')<br/>}<br/>}<br/>}<br/>}<br/>
    2way binding (videmodel <-> view)

    View full-size slide

  12. MukkitList.vue



    {{food}}


    ref="input"
    v-model="newFood"
    v-on:keypress.enter="addFood">
    먹킷리스트 추가



    <br/>const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ]<br/>export default {<br/>name: 'MukkitList',<br/>data () {<br/>return {<br/>foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],<br/>newFood: ''<br/>}<br/>},<br/>mounted () {<br/>this.focusFood()<br/>},<br/>methods: {<br/>focusFood () {<br/>this.$refs.input.focus()<br/>},<br/>addFood () {<br/>if (this.foodList.indexOf(this.newFood) === -1) {<br/>this.foodList.push(this.newFood)<br/>this.newFood = ''<br/>this.focusFood()<br/>} else {<br/>alert('해당 음식은 이미 있습니다.')<br/>}<br/>}<br/>}<br/>}<br/>
    1way binding (viewmodel -> view)

    View full-size slide

  13. MukkitList.vue



    {{food}}


    ref="input"
    v-model="newFood"
    v-on:keypress.enter="addFood">
    먹킷리스트 추가



    <br/>const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ]<br/>export default {<br/>name: 'MukkitList',<br/>data () {<br/>return {<br/>foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],<br/>newFood: ''<br/>}<br/>},<br/>mounted () {<br/>this.focusFood()<br/>},<br/>methods: {<br/>focusFood () {<br/>this.$refs.input.focus()<br/>},<br/>addFood () {<br/>if (this.foodList.indexOf(this.newFood) === -1) {<br/>this.foodList.push(this.newFood)<br/>this.newFood = ''<br/>this.focusFood()<br/>} else {<br/>alert('해당 음식은 이미 있습니다.')<br/>}<br/>}<br/>}<br/>}<br/>
    1way binding (view -> viewmodel)

    View full-size slide

  14. MukkitList.vue



    {{food}}


    ref="input"
    v-model="newFood"
    v-on:keypress.enter="addFood">
    먹킷리스트 추가



    <br/>const DEFAULT_INITIALIZE_FOOD_LIST = [ '치킨', '탕수육', '닭도리탕' ]<br/>export default {<br/>name: 'MukkitList',<br/>data () {<br/>return {<br/>foodList: [...DEFAULT_INITIALIZE_FOOD_LIST],<br/>newFood: ''<br/>}<br/>},<br/>mounted () {<br/>this.focusFood()<br/>},<br/>methods: {<br/>focusFood () {<br/>this.$refs.input.focus()<br/>},<br/>addFood () {<br/>if (this.foodList.indexOf(this.newFood) === -1) {<br/>this.foodList.push(this.newFood)<br/>this.newFood = ''<br/>this.focusFood()<br/>} else {<br/>alert('해당 음식은 이미 있습니다.')<br/>}<br/>}<br/>}<br/>}<br/>
    view query (it is not bind)

    View full-size slide

  15. function View_MukkitListComponent_0(_l) {
    return __WEBPACK_IMPORTED_MODULE_1__angular_core__["_25" /* ɵvid */]
    (0, [__WEBPACK_IMPORTED_MODULE_1__angular_core__["_22" /* ɵqud */]
    (402653184, 1, { inputEl: 0 }),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_8" /* ɵeld */](1, 0, null, null, 6, "div", [["class", "mukkit-list-
    container"]], null, null, null, null, null)),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n "])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_8" /* ɵeld */](3, 0, null, null, 0, "img", [["src",
    "data:image/svg+xml;base64,…"], ["width", "180"]], null, null, null, null, null)),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n "])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_8" /* ɵeld */](5, 0, null, null, 1, "h2", [], null, null, null, null,
    null)),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\uBA39\uD0B7\uB9AC\uC2A4\uD2B8"])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n"])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n\n"])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_8" /* ɵeld */](9, 0, null, null, 17, "ul", [["class", "mukkit-list"]],
    null, null, null, null, null)),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n "])),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_3" /* ɵand */](16777216, null, null, 1, null, View_MukkitListComponent_1)),
    __WEBPACK_IMPORTED_MODULE_1__angular_core__["_7" /* ɵdid */](
    12, 802816, null, 0, __WEBPACK_IMPORTED_MODULE_2__angular_common__["c" /* NgForOf */],
    [__WEBPACK_IMPORTED_MODULE_1__angular_core__["R" /* ViewContainerRef */],
    __WEBPACK_IMPORTED_MODULE_1__angular_core__["N" /* TemplateRef */],
    __WEBPACK_IMPORTED_MODULE_1__angular_core__["u" /* IterableDiffers */]],
    { ngForOf: [0, "ngForOf"] }, null),
    (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_24" /* ɵted */](-1, null, ["\n "])),

    View full-size slide

  16. {{interpolation}}
    [1way data binding]
    [(2way data binding)]
    (1way data binding (event))
    {{interpolation with pipe | pipeName}}
    *ngFor *ngIf
    [ngSwitch] [hidden] [innerHTML] [ngClass]
    (click) (keypress) (blur) (input) (change)

    v-directive-name:parameter
    {{interpolation | filter}}
    v-bind:id v-if v-html
    v-for v-else v-else-if
    v-show v-bind:class v-bind:style
    v-on:event-name
    v-bind:bind-target-name
    {{interpolation}}
    :store-name
    v-model

    View full-size slide

  17. 1
    shouldComponentUpdate()
    Dealing control by element types
    (aka. Pair-wise diff)
    2 3
    Dealing control by key
    (aka. List-wise diff)
    Update DOM

    View full-size slide

  18. Object.defineProperty()

    View full-size slide

  19. new webpack.DllReferencePlugin({
    context: root,
    manifest: manifest
    });

    View full-size slide