해리의 데브로그

SWEA 5431 - 민석이의 과제 체크하기

|

문제

  • SWEA 5431- 민석이의 과제 체크하기
  • 문제링크
  • 문제의 저작권은 SW Expert Academy에 있습니다.

나의 코드

TC = int(input())

for tc in range(1, TC+1):
    N, K = map(int, input().split())
    submitted_lst = [ _ for _ in range(1, N+1)]
    Data = list(map(int, input().split()))

    for i in Data:
        submitted_lst.pop(submitted_lst.index(i))

    print('#%d'%(tc), end=' ')
    for i in submitted_lst:
        print(i, end= ' ')
    print()

2019년 7월 2주차 TIL

|

2019-07-08

  • 김정환 개발자님의 실습 UI 개발로 배워보는 순수 javascript 와 VueJs 개발 강의가 드디어 Vue.js 파트로 넘어왔다. 순수 자바스크립트 코드로 MVC 패턴에 따라 진행했던 방식을 처음 접하기도하고 익숙하지 않아 이해하는게 쉽지 않았는데, 같은 기능을 Vue.js로 다시 구현해보니 Vue의 위대함을 알 수 있었다.
  • Vue의 MVVM 패턴이 너무 직관적이고 쉽다!

2019-07-09

  • 인프런 강의의 Vue.js 파트를 마무리 지었다. Vue 기반의 MVVM 패턴으로 기능을 구현하는 방식이 크게 어렵지 않았고, 간단한 디렉티브등을 사용했던지라 빠르게 강의를 마무리 지을 수 있었던 것 같다.
  • 방과 후에 듣는 인프런 강의 외에, 주중에는 수업시간을 통해 팀 프로젝트를 진행 중에 있다. 자세한 내용을 언급하긴 어렵지만, 현재 Firebase를 통해 사용자 인증(회원가입, 로그인) 기능을 구현중에 있다.
  • 예전부터 AWS나 Firebase 등 서버사이드의 많은 기능을 제공하는 플랫폼을 한번 경험해보고 싶었기에, 파이어베이스 관련 기능을 지원했는데, 특히 Firebase는 안드로이드 등 모바일 플랫폼에서 많이 쓰이는지, 웹 개발쪽은 검색 결과가 썩 시원찮다.
  • 덕분에 공식문서를 가지고 하나하나 코드를 살펴보고 있는데, 빅스비 개발을 해봤던 경험이 많은 도움이 되고 있다!

2019-07-10

  • 강의가 마지막 파트인 “컴포넌트로 기능 구현”에 다달았다.
  • Vue에서는 마치 객체 지향 프로그래밍 처럼, 각 기능 또는 화면의 구조 등을 컴포넌트로 구분하여, 필요할 때, 그 컴포넌트를 불러와 손쉽게 사용하는 방법을 제공하고 있다.
  • 사실 현재로써는 HTML + JS 파일로 구성된 일반적인 Vue 방식이 더 익숙하고 직관적으로 이해하기 쉽긴 하지만, 프로젝트의 규모가 커진다면, traceability나 코드의 생산성을 고려해볼 때, component 방식의 프로젝트 구현이 훨씬 더 많은 도움이 될 것 같긴 하다.
  • 상위 & 하위 컴포넌트간의 데이터를 교환할 때 특별한 기능을 사용하는데, 그 쓰임새는 다음과 같다.
    • props : 상위 -> 하위 컴포넌트로 데이터 전달
    • $emit : 하위 -> 상위 컴포넌트로 데이터 전달

2019-07-11

  • 컴포넌트 파트 강의 수강 완료. 정말 마지막인 Single File Component 파트만 남았다.
  • 프로젝트를 진행하면서 vuex 라는 녀석을 알게 되었다. 특정 변수를 컴포넌트와 관계없이 전역변수로 저장시켜 어디서든 쉽게 불러 올 수 있게 하는 상태관리 라이브러리 인데, store.js 라는 파일에서 코드를 작성한다.
  • 처음에는 왜 굳이 불필요하고 복잡하게 따로 vuex 라는 것을 사용하는 지 몰랐는데, 만약 최상위 컴포넌트에서 몇차례를 걸친 최히위 컴포넌트간의 데이터를 주고 받는다고 가정할 때, 얼마나 복잡하게 props & $emit 등을 통해 코드를 작성해야하는지를 생각해보면, vuex가 제공하는 기능의 편리함을 알 수 있을 듯 하다.
  • mutations, actions, commit, payload 등 겉핥기식으로 우선 코드를 살펴보았는데, 조만간 vuex에 대해 상세히 공부를 해봐야겠다.

2019-07-12

  • 김정환 개발자님의 실습 UI 개발로 배워보는 순수 javascript 와 VueJs 개발 강의를 드디어 수강 완료하였다.

  • 4시간짜리 강의를 2~3일만 집중해서 들으면 금방 들을 줄 알았던 4시간 짜리 강의를 무려 22시간 동안이나 듣게될 줄은 몰랐다..!

  • 동일한 기능을 순수 자바스크립트 -> vue -> 컴포넌트 -> 단일 컴포넌트 식으로 단계별로 기능을 구현하면서 단계별 차이점을 한눈에 쉽게 알 수 있었던 점이 인상깊었고, 또한 실습 위주로 진행되어 더 집중을 할 수 있었던 것 같다.

  • 바닐라 JS & MVC 패턴이 익숙하지 않아 이 파트에서 거의 12시간 가까이를 사용했는데, 조만간 미니프로젝트나 강의를 빠르게 복기하면서 다시한번 코드를 살펴봐야겠다.

  • 인프런에서 강의를 완료하니 수료인증서도 주더라!

2019-07-14

  • 김정환 개발자님의 강의에 이어서, Vue JS의 다른 강의를 듣고 싶어 검색을 하다가 Udemy에서 VUE JS 2 - The Complete Guide 라는 강의를 알게 되었다. 이 강의랑 파이어베이스를 함께 활용하여 Vue JS를 가르치는 다른 강의 중 고민을 하다가, 이 강의가 좀 더 Vue Js의 원론적인 내용에 집중하는 것 같아 수강을 시작 하였다.
  • 무려 22시간짜리 강의인지라 걱정이 되지만 이번에는 일일이 강의 내용을 정리하기 보다는, 강의 내용에 집중하되, 몰랐거나 노트해야할 내용위주로 강의 내용을 정리해서 강의 수강 시간을 조금이나마 단축시키고자 한다.
  • 영어로 진행되는 강의긴 하지만, 자막도 제공되고, 영어로 설명하는게 오히려 더 직관적으로 이해하기 쉬운 것 같아 좋은 듯 하다.

실습 UI 개발을 통해 배워보는 JS & Vue JS (14) - 단일 파일 컴포넌트

|

본 강의는 Inflearn의 김정환 개발자 님의 강의(실습 UI 개발로 배워보는 순수 javascript 와 VueJS 개발)를 듣고 배운 내용을 정리한 포스팅 입니다.

단일 파일 컴포넌트(Single File Component) 구현

지금까지 컴포넌트를 통해 작성한 코드를 살펴보면 index.html + component.js로 구성되어 있는 것을 알 수 있음. Vue에서는 컴포넌트를 .vue라는 확장자를 이용하여 하나의 단일컴포넌트로 구현할 수 있도록 기능을 지원하고 있음.

예를 들어, TabComponent.vue라는 파일만 보면 index.html과 TabComponent.js 파일 둘다를 볼 필요 없게됨. 가독성이 훨씬 더 증가하며, 유지보수에도 편리하다는 장점이 생김.

1) SFC 설치

npm install vue-cli global  #Vue Cli 전역으로 설치
vue list #template 확인하기
vue init webpack-simple #webpack-simple 설치
npm install #개발 서버 가동을 위해 npm install
npm run dev #개발 서버 가동

2) SFC 기본 구성 & 시작하기

  • src 폴더의 main.js가 바로 진입점. 여기서부터 시작함
import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})
  • 메인 어플리케이션은 App.vue에서 만듦. template & script & style 이라는 3부분으로 구성됨.
<template>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
    }
  }
}
</script>

<style>
</style>
  • base로 사용하였던 style.css를 root 디렉토리에 붙여넣은 후, index.html과 연결시킴.
  • viewport 설정을 head 태그 내 진행
  • src 폴더 내 models 폴더 복사 & 저장
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  	<meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>base_sfc</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/build.js"></script>
  </body>
</html>
  • header 출력하기; App.vue에 코드 삽입
<template>
  <div>
    <header>
      <h2 class="container">검색</h2>
    </header>
  </div>
</template>

3) FormComponent.vue

  • src 폴더 내 components 폴더 생성
  • FormComponent.vue 생성 후 기본 구조에 따라 코드 작성 후, Component 에서 사용했던 template & script 코드를 불러옴
  • 어느 template에 사용할 것이냐에 대해 명시하는 부분이었던 template 속성은 삭제 (바로 위에 있는 template에 사용하므로 쓸 필요가 없음)
<template>
  <form v-on:submit.prevent="onSubmit">
    <input type="text" v-model="inputValue" v-on:keyup="onKeyup" 
           placeholder="검색어를 입력하세요" autofocus>
    <button v-show="inputValue.length" v-on:click="onReset" 
            type="reset" class="btn-reset"></button>
  </form>
</template>

<script>
export default {
    // template: '#search-form',
    props: ['value'],
    data() {
        return {
            inputValue: this.value
        }
    },
    watch: {
        value(newVal, oldVal) {
            this.inputValue = newVal
        }
    },
    methods: {
        onSubmit() {
            this.$emit('@submit', this.inputValue.trim())
        },
        onKeyup() {
            if (!this.inputValue.length) this.onReset()
        },
        onReset() {
            this.inputValue = ''
            this.$emit('@reset')
        },
    }
}
</script>

4) App.vue

  • 컴포넌트 import & components 속성에 추가
  • FormComponent를 출력하는 부분을 header 태그 바로 아래에 작성(이전 프로젝트에서 작성한 코드 그대로 사용)
  • components 프로젝트 내 app.js 에서 사용했던 data, created와 methods를 그대로 사용
  • Model import
<template>
  <div>
    <header>
      <h2 class="container">검색</h2>
    </header>
    <div class="container">
      <search-form v-bind:value="query" v-on:@submit="onSubmit" v-on:@reset="onReset"></search-form>
    </div>
  </div>
</template>

<script>
import SearchModel from './models/SearchModel.js'
import KeywordModel from './models/KeywordModel.js'
import HistoryModel from './models/HistoryModel.js'

import FormComponent from './components/FormComponent.vue'

export default {
  name: 'app',
  data () {
    return {
      query: '',
      submitted: false,
      tabs: ['추천 검색어', '최근 검색어'],
      selectedTab: '',
      keywords: [],
      history: [],
      searchResult: []
    }
  },
  created() {
    this.selectedTab = this.tabs[0]
    this.fetchKeyword()
    this.fetchHistory()
  },
  components: {
    'search-form': FormComponent
  },
  methods: {
    onSubmit(query) {
      this.query = query
      this.search()
    },
    onReset(e) {
      this.resetForm()
    },
    onClickTab(tab) {
      this.selectedTab = tab
    },
    onClickKeyword(keyword) {
      this.query = keyword;
      this.search()
    },
    onClickRemoveHistory(keyword) {
      HistoryModel.remove(keyword)
      this.fetchHistory()
    },
    fetchKeyword() {
      KeywordModel.list().then(data => {
        this.keywords = data
      })
    },
    fetchHistory() {
      HistoryModel.list().then(data => {
        this.history = data
      })
    },
    search() {
      SearchModel.list().then(data => {
        this.submitted = true
        this.searchResult = data
      })
      HistoryModel.add(this.query)
      this.fetchHistory()
    },
    resetForm() {
      this.query = ''
      this.submitted = false
      this.searchResult = []
    }

  }
}
</script>
  • 같은 방식으로 나머지 component도 단일 파일 컴포넌트로 변경해서 구현 할 수 있음.

5) ResultComponent.vue

<template>
  <div v-if="data.length">
    <ul>
      <li v-for="item in data">
        <img v-bind:src="item.image"> 
      </li>
    </ul>
  </div>
  <div v-else>
     검색어로 찾을수 없습니다
  </div>
</template>


<script>
export default {
    props: ['data', 'query'],
}
</script>

6) ListComponent.vue

<template>
  <div v-if="data.length">
    <ul class="list">
      <li v-for="(item, index) in data" v-on:click="onClickList(item.keyword)">
        <span v-if="keywordType" class="number"></span>
        
        <span v-if="historyType" class="date"></span>
        <button v-if="historyType" class="btn-remove" v-on:click.stop="onRemoveList(item.keyword)"></button>
      </li>
    </ul>
  </div>
  <div v-else>
    <span v-if="keywordType">추천 검색어가 없습니다</span>
    <span v-if="historyType">최근 검색어가 없습니다</span>
  </div>
</template>


<script>
export default {
    props: ['data', 'type'],
    computed: {
        keywordType() {
            return this.type === 'keywords'
        },
        historyType() {
            return this.type === 'history'
        },
    },
    methods: {
        onClickList(keyword) {
            this.$emit('@click', keyword)

        },
        onRemoveList(keyword) {
            this.$emit('@remove', keyword)

        }
    }
}
</script>

7) TabComponent.vue

<template>
  <ul class="tabs">
    <li v-for="tab in tabs" v-bind:class="{active: tab === selectedTab}" v-on:click="onClickTab(tab)">
      
    </li>
  </ul>
</template>

<script>
export default {
    props: ['tabs', 'selectedTab'],
    methods: {
        onClickTab(tab) {
            this.$emit('@change', tab)
        }

    }
}
</script>

8) App.vue

<template>
  <div>
    <header>
      <h2 class="container">검색</h2>
    </header>
    <div class="container">
      <search-form v-bind:value="query" v-on:@submit="onSubmit" v-on:@reset="onReset"></search-form>
    </div>
    <div class="content">
      <div v-if="submitted">
        <search-result v-bind:data="searchResult" v-bind:query="query"></search-result>
      </div>
      <div v-else>
        <tabs v-bind:tabs="tabs" v-bind:selected-tab="selectedTab" v-on:@change="onClickTab"></tabs>
        <div v-if="selectedTab === tabs[0]">
          <list v-bind:data="keywords" type="keywords" v-on:@click="onClickKeyword"></list>
        </div>
        <div v-else>
          <list v-bind:data="history" type="history" v-on:@click="onClickKeyword" v-on:@remove="onClickRemoveHistory">
          </list>
        </div>
      </div>      
    </div>
  </div>
</template>

<script>
import SearchModel from './models/SearchModel.js'
import KeywordModel from './models/KeywordModel.js'
import HistoryModel from './models/HistoryModel.js'

import FormComponent from './components/FormComponent.vue'
import ResultComponent from './components/ResultComponent.vue'
import ListComponent from './components/ListComponent.vue'
import TabComponent from './components/TabComponent.vue'

export default {
  name: 'app',
  data () {
    return {
      query: '',
      submitted: false,
      tabs: ['추천 검색어', '최근 검색어'],
      selectedTab: '',
      keywords: [],
      history: [],
      searchResult: []
    }
  },
  components: {
    'search-form': FormComponent,
    'search-result':ResultComponent,
    'list':ListComponent,
    'tabs':TabComponent,
  },
  created() {
    this.selectedTab = this.tabs[0]
    this.fetchKeyword()
    this.fetchHistory()
  },
  methods: {
    onSubmit(query) {
      this.query = query
      this.search()
    },
    onReset(e) {
      this.resetForm()
    },
    onClickTab(tab) {
      this.selectedTab = tab
    },
    onClickKeyword(keyword) {
      this.query = keyword;
      this.search()
    },
    onClickRemoveHistory(keyword) {
      HistoryModel.remove(keyword)
      this.fetchHistory()
    },
    fetchKeyword() {
      KeywordModel.list().then(data => {
        this.keywords = data
      })
    },
    fetchHistory() {
      HistoryModel.list().then(data => {
        this.history = data
      })
    },
    search() {
      SearchModel.list().then(data => {
        this.submitted = true
        this.searchResult = data
      })
      HistoryModel.add(this.query)
      this.fetchHistory()
    },
    resetForm() {
      this.query = ''
      this.submitted = false
      this.searchResult = []
    }

  }
}
</script>

실습 UI 개발을 통해 배워보는 JS & Vue JS (13) - Tab Component 구현

|

본 강의는 Inflearn의 김정환 개발자 님의 강의(실습 UI 개발로 배워보는 순수 javascript 와 VueJS 개발)를 듣고 배운 내용을 정리한 포스팅 입니다.

Tab Component 구현

1) 기본 코드 작성

  • TabComponent.js 생성
export default {
    template: '#tabs'
}
  • index.html 코드 수정
<div class="content">
    <div v-if="submitted">
        <search-result v-bind:data="searchResult" 
                       v-bind:query="query">
        </search-result>
    </div>
    <div v-else>
        <tabs></tabs>
        <!-- <ul class="tabs">
                <li v-for="tab in tabs" 
                	v-bind:class="{active: tab === selectedTab}"
                	v-on:click="onClickTab(tab)">
                	
                </li>
			</ul> -->

        
<!-- tabs -->
<template id="tabs">
    <ul class="tabs">
        <li v-for="tab in tabs" v-bind:class="{active: tab === selectedTab}" 
            v-on:click="onClickTab(tab)">
            
        </li>
    </ul>
</template>
  • app.js 내 컴포넌트 import & 추가
import TabComponent from './components/TabComponent.js'

  components: {
    'tabs' : TabComponent,
  },

2) 탭 구현

  • 실제 탭을 출력하기위한 탭 데이터를 v-bind를 사용하여 넘김
    • v-bind:tabs="tabs" 에서 "tabs" 는 app.js에 저장된 변수임.
  • 선택된 탭에 스타일 속성을 적용하기 위해 바인딩 설정
    • 속성을 정의할 때는 하이픈 사용
  • 탭이 클릭될 때 마다 추천 검색어 & 최근 검색어를 바꿔서 보여줘야하므로 클릭 이벤트 정의
    • @change 이벤트가 발생하면 onClickTab 함수 실행
  • 기존의 코드와 어떻게 변경했는지를 비교해서 살펴보자
<!-- 컴포넌트 사용 코드 -->
<tabs v-bind:tabs="tabs" v-bind:selected-tab="selectedTab"
      v-on:@change="onClickTab"></tabs>

<!-- 이전 코드 -->
<ul class="tabs">
    <li v-for="tab in tabs" v-bind:class="{active: tab === selectedTab}"
        v-on:click="onClickTab(tab)">
        
    </li>
</ul>
export default {
    template: '#tabs',
    props: ['tabs', 'selectedTab'],
    methods: {
        onClickTab(tab) {
            this.$emit('@change', tab)
        }
    }
}

실습 UI 개발을 통해 배워보는 JS & Vue JS (12) - Result & List Component 구현

|

본 강의는 Inflearn의 김정환 개발자 님의 강의(실습 UI 개발로 배워보는 순수 javascript 와 VueJS 개발)를 듣고 배운 내용을 정리한 포스팅 입니다.

Result Component 구현

  • ResultComponent.js 작성
export default {
    template:'#search-result'
}
  • index.html내 검색 결과를 나타내는 태그를 잘라내 파일 하단으로 이동 후 template 태그로 묶어줌
<div class="content">
    <div v-if="submitted">
        <!-- 코드 이동 -->
    </div>
    
<template id="search-result">
  <div v-if="searchResult.length">
    <ul>
      <li v-for="item in searchResult">
        <img v-bind:src="item.image"> 
      </li>
    </ul>
  </div>
  <div v-else>
     검색어로 찾을수 없습니다
  </div>
</template>
  • app.js 내 컴포넌트 import 후 components에 추가
import ResultComponent from './components/ResultComponent.js'

  components: {
    'search-result' : ResultComponent
  },
  • index.html에 컴포넌트를 기존의 위치에 입력
    • Vue 인스턴스가 갖고 있는 검색 결과 값을 v-bind를 이용하여 ResultComponent에 넘겨 줄 것.
    • ResultComponent에서 props로 data를 받음
    • 결과값을 뿌려주는 부분에 대한 변수명을 변경(search.length -> data.length)
<div class="content">
    <div v-if="submitted">
        <search-result v-bind:data="searchResult"></search-result>
    </div>
export default {
    template:'#search-result',
    props: ['data'],
}
<template id="search-result">
  <div v-if="data.length">
    <ul>
      <li v-for="item in data">
        <img v-bind:src="item.image"> 
      </li>
    </ul>
  </div>
  <div v-else>
     검색어로 찾을수 없습니다
  </div>
</template>
  • 검색어(query) 또한 ResultComponent로 넘겨줌
<div class="content">
    <div v-if="submitted">
        <search-result v-bind:data="searchResult" v-bind:query="query"></search-result>
    </div>
export default {
    template:'#search-result',
    props: ['data', 'query'],
}

List Component 구현

추천 검색어와 최근검색어는 형식이 유사함. 이들을 출력할 수 있는 ListComponent를 만들어 공유해서 쓰도록 하자.

1) 기본 코드 작성

  • index.html를 보면 분기문에 따라 추천 검색어 또는 최근 검색어 코드가 작성되어있음. 둘중 하나를 써서 공유할 에정이므로, 하나만 잘라내어 파일 하단에 template 태그 안으로 이동
  • v-if & v-for의 변수는, 추천/최근 검색어에 공통으로 쓰므로, 변수명을 수정
    • keywords.length -> data.length
    • (item, index) in keywords -> (item, index) in data
    • onClickKeyword -> onClickList
  • 최근 검색어 목록에만 존재하는 날짜와 삭제 버튼에 대한 내용도 추가로 갖고온 후, 변수명 또한 수정
    • onClickRemoveHistory -> onRemoveList
// ListComponent
export default {
    template: '#List'
}
<!-- index.html -->
<div v-if="selectedTab === tabs[0]">
    <!-- 코드 이동 -->
</div>

<template id="List">
  <div v-if="data.length">
    <ul class="list">
      <li v-for="(item, index) in data" v-on:click="onClickList(item.keyword)">
        <span class="number"></span> 
        
        <span class="date"></span>
        <button class="btn-remove" 
                v-on:click.stop="onRemoveList(item.keyword)"></button>  
      </li>
    </ul>
  </div>
  <div v-else>
    추천 검색어가 없습니다
  </div>
</template>
  • app.js 내 컴포넌트 import 및 등록
import ListComponent from './components/ListComponent.js'

  components: {
    'search-form': FormComponent,
    'search-result' : ResultComponent,
    'list' : ListComponent,
  },
  • index.html 내 추천 검색어 & 최근 검색어를 출력하는 부분을 수정
    • list 디렉티브 사용 & v-bind를 이용하여 Vue 인스턴스가 갖고 있는 값을 ListComponent로 넘김
<div v-if="selectedTab === tabs[0]">
    <list v-bind:data="keywords"></list>
</div>

<div v-else>
    <list v-bind:data="history"></list>
</div>
  • Vue 인스턴스로부터 받아온 값을 props에 저장 & methods 내 기본 함수 틀 생성
export default {
    template: '#List',
    props: ['data'],
    methods: {
        onClickList(keyword) {

        },
        onRemoveList(keyword) {
            
        }
    }
}

2) 결과 화면 다듬기

  • 추천 검색어 리스트에는 불필요한 기능(날짜, x버튼)이 나타나고 있음. 리스트가 추천 검색어인지 최근 검색어인지 식별할 수 있는 방법이 필요함. => type 데이터 사용 & props에 추가
<!-- index.html -->
<div v-if="selectedTab === tabs[0]">
    <list v-bind:data="keywords" type="keywords"></list>
</div>
<div v-else>
    <list v-bind:data="history" type="history"></list>
</div>
// ListComponent.js
export default {
    template: '#List',
    props: ['data', 'type'],
}
  • type의 값에 따라 인덱스 & 날짜 & x버튼을 출력하고 감추게 분기문 작성
<!-- index.html -->
<li v-for="(item, index) in data" v-on:click="onClickList(item.keyword)">
    <span v-if="type === 'keywords'" class="number"></span> 
    
    <span v-if="type === 'history'" class="date"></span>
    <button v-if="type === 'history'"class="btn-remove" 
            v-on:click.stop="onClickRemoveList(item.keyword)"></button>
</li>

3) 추천 검색어 클릭 시, 검색 기능 동작 구현

  • ListComponent.js 에서 클릭 이벤트가 발생했을 때, 이벤트를 재정의 하여 app.js로 넘겨주면 됨. $emit 사용
  • 최근 검색어의 경우, 검색어를 삭제하는 이벤트에 대한 코드를 추가로 입력
methods: {
    onClickList(keyword) {
        this.$emit('@click', keyword)
    },
    onRemoveList(keyword) {
        this.$emit('@remove', keyword)
    }
}
<div v-if="selectedTab === tabs[0]">
    <list v-bind:data="keywords" type="keywords" v-on:@click="onClickKeyword"></list>
</div>
<div v-else>
    <list v-bind:data="history" type="history" v-on:@click="onClickKeyword"
          v-on:@remove="onClickRemoveHistory"></list>
</div>

4) Computed

  • type값이 keyword 인지, 혹은 history인지에 따라서 출력하는 부분이 달라짐.
  • template 태그 안에 조건문 같은 코드가 있을 경우, 코드의 가독성이 떨어짐. 이때 Vue에서 제공하는 computed를 사용.
<span v-if="type === 'keywords'" class="number"></span> 

<span v-if="type === 'history'" class="date"></span>
  • ListComponent.js 내 computed 속성 추가
    • type이 keywords 인지 history 인지를 반환하는 함수 작성
computed: {
    keywordType() {
        return this.type === 'keywords'
    },
    historyType() {
        return this.type === 'history'
    },
},
  • computed 내 함수를 호출하여 사용
<div v-if="data.length">
    <ul class="list">
      <li v-for="(item, index) in data" 
          v-on:click="onClickList(item.keyword)">
        <span v-if="keywordType" class="number"></span> 
        
        <span v-if="historyType" class="date"></span>
        <button v-if="historyType"class="btn-remove" 
                v-on:click.stop="onRemoveList(item.keyword)"></button>
      </li>
    </ul>
  </div>
  <div v-else>
    <span v-if="keywordType">추천 검색어가 없습니다</span>
    <span v-if="historyType">최근 검색어가 없습니다</span>    
  </div>

5) watch - 추천 검색어를 검색폼에 띄우기

추천 검색어를 클릭하여 결과가 뜬 후, devtools를 통해 디버깅을 해보면, 아래와 같은 결과를 얻을 수 있다. 검색폼에 클릭한 추천 검색어가 뜨지 않는 이유는 searchForm 내 inputValue 값이 저장되어 있지 않았기 때문임.

  • <Root>
    • data - query: “이탈리아”
  • <SearchForm>
    • props - value: “이탈리아”
    • data - inputValue: “”
  • FormComponent 내 watch 속성 추가
  • watch:어떤 View Model을 감시하고 있다가, 그 값이 변경되면, 행동을 수행하는 함수
  • watch 함수에 props 내 value를 호출 함.
    • 이전 값과 새로운 값을 인자로 받아와, inputValue 에 새로운 값을 저장시킴.
export default {
    template: '#search-form',
    props: ['value'],
    data() {
        return {
            inputValue: this.value
        }
    },
    watch: {
        value(newVal, oldVal) {
            this.inputValue = newVal
        }
    },