[Vue] Vue 기본정리 part3

뷰 인스타그램 프로젝트 생성 & 레이아웃 만들기

오늘의 1시간 숙제 :

하단에 인스타그램 포스팅 데이터 3개를 첨부했습니다.

이걸 App.vue에 데이터로 보관하신 후 Post.vue에 데이터바인딩해서 3개의 게시물이 잘 보이게 해보십시오.

props 문법을 잘 쓰면 됩니다.

이미지는 <div>에 background-image : url() 이걸로 집어넣으면 됩니다.

인스타 포스팅 데이터

[
  {
    name: "Kim Hyun",
    userImage: "https://placeimg.com/100/100/arch",
    postImage: "https://placeimg.com/640/480/arch",
    likes: 36,
    date: "May 15",
    liked: false,
    content: "오늘 무엇을 했냐면요 아무것도 안했어요 ?",
    filter: "perpetua"
  },
  {
    name: "John Doe",
    userImage: "https://placeimg.com/200/200/people",
    postImage: "https://placeimg.com/640/480/people",
    likes: 20,
    date: "Apr 20",
    liked: false,
    content: "흔한 자랑스타그램",
    filter: "clarendon"
  },
  {
    name: "Minny",
    userImage: "https://placeimg.com/100/100/animals",
    postImage: "https://placeimg.com/640/480/animals",
    likes: 49,
    date: "Apr 4",
    liked: false,
    content: "우리집 개는 화장실 물도 내림",
    filter: "lofi"
  }
]

오늘은 인스타그램 프로젝트를 생성하고 메인페이지 레이아웃을 완성합시다.

새로운 프로젝트 생성은 작업폴더 오픈하시고 터미널에 vue create 어쩌구 입력하시면 되며

HelloWorld라는 글자 다 지우고 시작하면 됩니다.

20220425_151503

인스타그램 메인페이지를 App.vue에 만들건데.. 페북 인스타 앱처럼 그냥 여러개의 포스팅이 보이면 됩니다.

하지만 HTML부터 쭉 짜는 것 보다는 미리 생각하고 만드는게 좋습니다.

그래서 저는 App.vue 안에 Container.vue 안에 Post.vue 여러개를 만들기로 했습니다.

실은 Container.vue는 쓸데없이 만든 것이긴 한데 배울게 좀 있어서 만들어봤습니다.

님들은 이렇게 대충만들지 말고 컴포넌트만들 때 “이걸 만들면 득이될까” 생각을 하고 만드시길 바랍니다.

App.vue 안에 들어갈 HTML과 CSS

App.vue template>안에 들어갈 내용

<div class="header">
    <ul class="header-button-left">
      <li>Cancel</li>
    </ul>
    <ul class="header-button-right">
      <li>Next</li>
    </ul>
    <img src="./assets/logo.png" class="logo" />
  </div>

  <Container />

  <div class="footer">
    <ul class="footer-button-plus">
      <input type="file" id="file" class="inputfile" />
      <label for="file" class="input-plus">+</label>
    </ul>
 </div>

App.vue style>안에 들어갈 내용

body {
  margin: 0;
}
ul {
  padding: 5px;
  list-style-type: none;
}
.logo {
  width: 22px;
  margin: auto;
  display: block;
  position: absolute;
  left: 0;
  right: 0;
  top: 13px;
}
.header {
  width: 100%;
  height: 40px;
  background-color: white;
  padding-bottom: 8px;
  position: sticky;
  top: 0;
}
.header-button-left {
  color: skyblue;
  float: left;
  width: 50px;
  padding-left: 20px;
  cursor: pointer;
  margin-top: 10px;
}
.header-button-right {
  color: skyblue;
  float: right;
  width: 50px;
  cursor: pointer;
  margin-top: 10px;
}
.footer {
  width: 100%;
  position: sticky;
  bottom: 0;
  padding-bottom: 10px;
  background-color: white;
}
.footer-button-plus {
  width: 80px;
  margin: auto;
  text-align: center;
  cursor: pointer;
  font-size: 24px;
  padding-top: 12px;
}
.sample-box {
  width: 100%;
  height: 600px;
  background-color: bisque;
}
.inputfile {
  display: none;
}
.input-plus {
  cursor: pointer;
}
#app {
  box-sizing: border-box;
  font-family: "consolas";
  margin-top: 60px;
  width: 100%;
  max-width: 460px;
  margin: auto;
  position: relative;
  border-right: 1px solid #eee;
  border-left: 1px solid #eee;
}

Container.vue 안에 들어갈 HTML과 CSS

Container.vue template>안에 들어갈 내용

<div>
  <Post/>
  <Post/>
  <Post/>
</div>

CSS는 필요없습니다.

Post.vue 안에 들어갈 HTML과 CSS

Post.vue template>안에 들어갈 내용


<div class="post">
    <div class="post-header">
      <div class="profile"></div>
      <span class="profile-name">ChanKim</span>
    </div>
    <div class="post-body"></div>
    <div class="post-content">
      <p>43 Likes</p>
      <p><strong>글쓴이아이디</strong> 임시내용</p>
      <p class="date">May 15</p>
    </div>
</div>

Post.vue style>안에 들어갈 내용

.post {
  width: 100%;
}
.profile {
  background-image: url("https://placeimg.com/100/100/arch");
  width: 30px;
  height: 30px;
  background-size: 100%;
  border-radius: 50%;
  float: left;
}
.profile-name {
  display: block;
  float: left;
  padding-left: 10px;
  padding-top: 7px;
  font-size: 14px;
}
.post-header {
  height: 30px;
  padding: 10px;
}
.post-body {
  background-image: url("https://placeimg.com/640/480/animals");
  height: 450px;
  background-position: center;
  background-size: cover;
}
.post-content {
  padding-left: 15px;
  padding-right: 15px;
  font-size: 14px;
}
.date {
  font-size: 11px;
  color: grey;
  margin-top: -8px;
}

저번시간 숙제 & style 속성 데이터바인딩

저번 시간 숙제 :

인스타그램 포스팅 데이터 3개가 있는데

이걸 App.vue에 데이터로 보관하신 후 Post.vue에 데이터바인딩해서 3개의 게시물이 잘 보이게 해보십시오.

props 문법을 잘 쓰면 됩니다.

저는 어떻게 했냐면

  1. postdata.js 로 그 긴 데이터를 보관했습니다.

  2. 그걸 App.vue에 데이터로 집어넣었습니다.

  3. 그 다음에 그 게시물데이터를 App.vue 에서 Container.vue로 props 전송했습니다.

  4. 그 다음에 그 게시물데이터를 Container.vue에서 Post.vue로 props 전송했습니다.

  5. 그리고 Post.vue에서 했다고 합니다.

별거아닙니다 그냥 props 보내는 문법 몇번 쓰면 되는 것일 뿐입니다.

답안 따라적기만 했다면 지우고 답보지말고 다시 해보는 것도 추천드립니다. 그래야 혼자서도 코드를 잘짬


마지막으로 다음 시간 넘어가기 전에

Container.vue안에

<Post/>
<Post/>
<Post/>

이거가 3개 있을겁니다.

3개를 반복문으로 짧게 줄여보십시오. 그리고 다음강의로 넘어가도록 합시다.

이미지를 꽂아넣으려면

실은 Post.vue에 프로필이미지, 포스팅이미지 두개를 꽂아넣어야하는데

이미지를 background-image : url() 이걸로 꽂아넣으셔야합니다. (제가 CSS 레이아웃을 그렇게 짰기 때문)

그래서 Post.vue 보시면

<div class="profile"></div>
<div class="post-body"></div>

이런 부분이 있는데 각각 프로필이미지, 포스팅이미지를 배경으로 집어넣으시길 바랍니다.

그니까 style=”background-image: url(이미지경로)” 이걸 집어넣으시면 됩니다.

하지만 저거 중간에 변수나 데이터 집어넣는 법을 모르겠다고요?

:style=”자바스크립트~~” 이렇게 콜론 붙이면 데이터명 같은거 집어넣을 수 있다고 했습니다.

뷰에서 style속성 데이터바인딩하는 법

:style=”” 안에 자바스크립트 데이터를 집어넣을 일이 많습니다.

그냥 콜론 붙이고 데이터바인딩 하시면 되는건 맞는데

코드가 길어지면 보기싫습니다.

그래서 편의를 위한 여러 방법을 지원하는데 가장 자주 쓰는게 object입니다.

style=”” 여기에 object 데이터 형태로 여러가지 css 속성을 집어넣을 수 있습니다.

<div :style="{ fontSize : '20px', marginTop : '10px' }">
<div :style="{ color : 'red' }">

이런 짓거리가 가능하다는 뜻입니다.

이렇게 하려면 object 자료기 때문에 CSS 속성 명은 - 대시기호를 쓸 수 없습니다. 그래서 대문자로 바꿔서 저렇게 붙여써야합니다.

전문용어로 CamelCase라고 합니다.

데이터상태에 따라서 스타일 변화가 필요할 때 (그니까 애니메이션 이런거 만들 때)도 유용하게 자주 씁니다.

<div class="post-body" :style="{ backgroundImage : 'url(이미지경로ㄷㄷ)' }"></div>

그래서 실제 Post.vue에 이미지 넣을 때 이렇게 해도 되겠습니다.

이미지경로 데이터를 문자 중간에 변수 느낌으로다가 집어넣으면 되는데

자바스크립트에서 문자 중간에 변수를 집어넣을 수 있는 방법은

'문자' + 변수 + '문자'
`문자 ${변수} 문자`

둘 중 하나를 선택해서 사용하면 됩니다.


서버로 ajax 요청하는 더보기 버튼 만들기

오늘의 15분 숙제 :

버튼을 한번 더 누르면 두번째 게시물을 가져와서 보여주도록 코드를 짜오십시오.

https://codingapple1.github.io/vue/more1.json으로 GET 요청하면 둘째 게시물을 줍니다.

강의에서 사용할 URL : https://codingapple1.github.io/vue/more0.json

여기로 GET요청하면 더보기용 게시물을 하나 줍니다.

일단 서버가 뭐냐면

서버는 그냥 요청하면 데이터주는 간단한 프로그램일 뿐입니다.

예를 들어 네이버 웹툰 서버는 뭐겠습니까. 웹툰 달라고 하면 주는 프로그램일 뿐입니다.

근데 서버에게 데이터를 요청하려면 GET 요청 / POST 요청 이라는 방법을 꼭 써야합니다.

GET은 데이터를 가져올 때 사용하며 서버가 요구하는 URL을 적어야합니다.

POST는 서버로 데이터를 보낼 때 사용하고 역시 서버가 요구하는 URL을 적어야합니다.

그래서 우리도 더보기 버튼을 누르면 서버로 추가 게시물을 요청할건데

제가 임시로 서버를 하나 만들어왔습니다.

위에 적은 URL로 GET 요청을 하면 추가 게시물을 보내줍니다.

그게 제가 만든 서버 전부임

Ajax 요청을 하려면 axios를 씁시다

fetch라는 자바스크립트 기본함수를 써도 되는데 호환성 때문에

axios를 자주 쓰게 될 것입니다.

빨리 터미널 켜서 npm install axios를 입력합시다.

뭔가 안되면 yarn 있으면 yarn add axios를 입력합시다.

(미리보기 띄우던 터미널은 끄고 설치하시길 바랍니다)

import axios from 'axios';

axios.get('서버URL').then( 결과 => {
  GET요청 성공시 실행할 코드~~
  console.log(결과);
})

이렇게 쓰면 원하는 URL로 GET요청을 할 수 있습니다.

그리고 .then() 안에 function(){} 콜백함수를 추가해주면 되는데

그 안에는 GET요청 성공시 실행할 코드를 적으시면 됩니다.

GET요청으로 가져온 데이터는 저기 그 결과라는 파라미터에 담겨있습니다.

import axios from 'axios';

axios.get('서버URL').then( 결과 => {
  GET요청 성공시 실행할 코드~~
}).catch( ()=>{
  실패시 실행할 코드
})

ajax요청이 실패시 특정 코드를 실행하고 싶으면 .catch 안에 적으면 됩니다.

URL을 잘못 쓰거나 서버가 다운되거나 그러면 ajax 요청이 실패할 수 있습니다.

import axios from 'axios';

axios.post('서버URL', '보낼데이터').then( 결과 => {
  POST요청 성공시 실행할 코드~~
}).catch( ()=>{
  실패시 실행할 코드
})

POST 요청을 보낼 수도 있습니다.

POST 요청은 서버로 원하는 데이터를 전송할 수 있습니다. 문자, object 다 가능합니다.

아무튼 제가 만들어놓은 서버로 GET 요청을 하셔서 게시물을 하나 더 가져와보시길 바랍니다.

그리고 가져온 그걸로 를 하나 더 생성해주면 되겠습니다. 를 하나 더 생성 어떻게 하냐고요? 는 반복문 돌려놨던 기억이 납니다. 게시물이라는 데이터 갯수만큼 반복문 돌려서 생성하라고 해놨는데 그럼 여러분이 코드로 짜야할 것은 "라는 HTML 하나 추가해주세요~"가 아니라 "게시물 데이터를 하나 증가시켜주세요~" 입니다. Vue는 항상 데이터중심적으로 개발합니다. 오늘의 숙제 : 버튼을 한번 더 누르면 두번째 게시물을 가져와서 보여주도록 코드를 짜오십시오. https://codingapple1.github.io/vue/more1.json으로 GET 요청하면 둘째 게시물을 줍니다. ------- 전 어떻게 했냐면 그냥 버튼을 한번 누르면 more0.json 두번 누르면 more1.json으로 요청을 하라고 코드를 짰습니다. 근데 그러려면 당연히 버튼 누른 횟수를 어디다 기록해둬야하지 않을까요? 그래서 데이터로 만들어서 적어뒀습니다. ``` data(){ return { 게시물 : postdata, 더보기 : 0, } }, ``` 그 다음 more() 함수를 이렇게 바꿨습니다. ``` more(){ axios.get(`https://codingapple1.github.io/vue/more${this.더보기}.json`) .then( 결과 => { this.게시물.push(결과.data); this.더보기++; }) } ``` 더보기라는 데이터가 0이면 more0.json 더보기라는 데이터가 1이면 more1.json 이런 URL로 GET요청하라고 써놨고 ajax요청 성공시 더보기라는 데이터를 ++ 해버렸다고 합니다. 끝! ---------------- ### 탭 만들기 & 탭으로 사진 업로드 페이지 만들기 탭 UI 만드는 법 Vue에서 탭기능 만들라고 하면 어떻게 만들어야합니까. ```

내용0
내용1
내용2
``` 탭이 뭐냐면 저런 HTML에서 버튼1을 누르면 내용1만 보여야 되는 UI입니다. 고민할 것도 없이 그냥 예전에 이야기했던 UI 만드는 법 그거 쓰시면 됩니다. 1. 현재 탭의 상태를 data로 저장 (지금 몇번째 내용이 보여야하는지) 2. data에 따라서 HTML 어떻게 보여야하는지 작성 이게 끝인데얌 알아서 해보십시오. 이건 답인데 원래 개발은 혼자하는겁니다 ``` data(){ return { step : 0 } } ``` 1. 데이터를 하나 만들었습니다. 현재 탭의 상태를 기록해둘 데이터요. 탭의 상태는 0,1,2 이렇게 표현할 수 있지 않을까요 그래서 숫자로 저장해놨습니다. ```
내용0
내용1
내용2
``` 2. 그리고 데이터 상태에 따라 HTML이 어떻게 보일지 작성해놨습니다. step : 0이면 0번 내용만 보여줄겁니다. 이게 끝인데얌 이제 버튼0 누르면 step을 0으로 이제 버튼1 누르면 step을 1로 변경하기만 하면 탭 잘 동작합니다. ------ 사진 업로드화면 만들기 인스타그램은 이미지 업로드시 이미지를 꾸미고 글적는 페이지가 있습니다. 그래서 우리도 필터선택 + 글작성을 할 수 있는 페이지들을 만들어볼 것인데 ![20220425_153643](/assets/20220425_153643.png) Container.vue 안에다가 대충 우측 2개처럼 생긴 레이아웃을 만들겁니다. (왼쪽건 저번시간에 이미 만들었죠?) 근데 라우터 이런거 쓰기 귀찮으니 탭으로 구분되게 만들어봅시다. 경우에 따라서 왼쪽, 중간, 오른쪽 중 1개를 보여주도록 코드를 짜면 되겠군요. 하지만 HTML CSS강의가 아니기 때문에 Container.vue 에 들어갈 HTML CSS는 복붙으로 개발해보도록 합시다. ##### Container.vue 안에 들어갈 HTML CSS template> 안에 들어갈 내용 style> 안에 들어갈 내용 ``` .upload-image{ width: 100%; height: 450px; background: cornflowerblue; background-size : cover; } .filters{ overflow-x:scroll; white-space: nowrap; } .filter-1 { width: 100px; height: 100px; background-color: cornflowerblue; margin: 10px 10px 10px auto; padding: 8px; display: inline-block; color : white; background-size: cover; } .filters::-webkit-scrollbar { height: 5px; } .filters::-webkit-scrollbar-track { background: #f1f1f1; } .filters::-webkit-scrollbar-thumb { background: #888; border-radius: 5px; } .filters::-webkit-scrollbar-thumb:hover { background: #555; } .write-box { border: none; width: 90%; height: 100px; padding: 15px; margin: auto; display: block; outline: none; } ``` ---- 그리고 step이라는 데이터를 하나 만드신 후에 (App.vue에 만드십시오 왜냐면 App.vue도 필요할 것 같으니까요) 이게 0이면 첫화면 1이면 둘째화면 2면 셋째화면을 보여주도록 코드를 짜놓으십시오. 그럼 오늘 할일 끝입니다. (오늘의 교훈) 다른 페이지가 필요하면 vue-router 써도 되지만 간단한 UI들은 탭으로 만드셔도 상관없습니다. --------- ### 서버없이 업로드한 이미지 다루기 (잡기술) 오늘의 5분 숙제 : 업로드한 이미지를 step 1, step 2 화면에서 파란박스란에 보여주려면 코드 어떻게 짜야할까요. background-image : url() 이 속성으로 집어넣으면 되는데 유저가 업로드한 이미지를 집어넣어야합니다. 알아서 다음시간까지 짜오도록 합시다. 오늘은 Vue 문법 시간은 아니고 업로드한 이미지 다루는 법을 알아봅시다. 실제 웹개발 잘하려면 Vue 문법만 알아야하는게 아니라 웹브라우저의 기능들을 많이 알아야합니다. Vue는 웹브라우저 다루는걸 도와주는 도구일 뿐임 옛날엔 사용자가 업로드한 이미지를 바로 브라우저에 보여주기 어려웠습니다. 그래서 서버로 보내서 저장하고, 저장한 이미지 URL을 다시 유저에게 보내서 그걸 img src=""> 여기 집어넣어서 보여줬습니다. 근데 IE11 이상에선 브라우저 내에서 직접 이미지 조작이 가능합니다. 1. FileReader() 를 쓰면 이미지를 글자로 변환할 수 있고 (글자니까 어디 저장하고 넣을 수도 있고) 2. URL.createObjectURL() 을 쓰면 이미지 URL을 잠깐 만들어줍니다. (다만 새로고침하면 사라짐) 가상으로 호스팅해준다고 생각해도 되겠습니다. 그래서 2번을 사용해볼 겁니다. App.vue에 있는 업로드버튼을 누르면 업로드한 이미지를 url로 만들어주는 코드를 실행해보도록 합시다. 그래야 img> 태그에 꽂던가 background-image로 넣던가 그럴 수 있으니까요. 길거 같으니까 함수를 만듭시다. ``` <input @change="upload()" type="file" id="file" /> ``` ``` methods : { upload(e){ let 파일 = e.target.files; let url = URL.createObjectURL(파일[0]); console.log(url); this.step++ } } ``` 프로그래밍책 같은거 읽을 때 가장 거지같은 점 중에 하나가 코드 100줄 따라치라고 하고 "이해되시죠? 넘어갑니당" 이런 진행방식인데 위에 쓴 코드는 3줄이라 다행입니다. 1. 파일업로드시에 e.target.files 라는 코드를 활용하면 업로드한 파일을 리스트로 알려줍니다. 자바스크립트 사용법일 뿐입니다. 그리고 파일 하나 업로드해도 [] 여기에 담아줌 2. 그리고 URL.createObjectURL() 여기다가 업로드한 파일을 담으시면 가상의 url을 하나 생성해줍니다. img src=""> 이런데다가 꽂으면 이미지가 보이는 신비한 url입니다. 3. 그리고 업로드하면 다음페이지로 넘어가야합니다. 끝 ----------- ### 글 발행기능 만들기 (이쯤 되면 혼자서도 잘함) 오늘의 5분 숙제 : App.vue에 있는 발행버튼을 누르면 업로드한 이미지와 작성한 글이 적용된 새로운 게시물이 메인페이지에 뿅 뜨도록 만들어봅시다. 저번 시간에 이미지 띄우는건 숙제였는데 어떻게 했냐면 그 이미지 url을 App.vue의 데이터로 저장했습니다. 그리고 그걸 props로 Container.vue로 보내서 Container.vue 안에 적절한 곳에 데이터바인딩했다고 합니다. Next 버튼 기능개발하기 상단에 Next 버튼이 있습니다. 이걸 누르면 다음 step으로 넘어가도록 코드를 잘 짜보시길 바랍니다. 그리고 Next 라는 버튼은 메인페이지에선 (step이 0이면) 안보여야합니다. @ click 이것도 달고 v-if 이것도 달면 되겠군요. (App.vue에 있던 Next 버튼) ``` <li v-if="step == 1" @click="step++">Next</li> ``` --------- 이번엔 발행버튼의 기능개발을 좀 해볼건데 발행을 누르면 내가 업로드한 이미지와 작성한 글이 메인페이지에 하나의 게시물 그러니까 이걸로 뿅 떠야합니다. 그럼 대체 어떻게 코드를 짜야하는 것입니까. 라는 HTML을 하나 생성해주면 되겠네요? 하지만 HTML 생성은 Vue가 알아서 하고 있습니다. 예전에 반복문 돌려서 "게시물 갯수만큼 생성해주셈~" 했잖아요. 그래서 여러분은 게시물 데이터만 만지면 Vue가 알아서 실시간으로 HTML을 재렌더링 해줍니다. 그러니까 결론은 발행버튼을 누르면 게시물 데이터를 3개에서 4개로 만들면 됩니다. 일단 발행 버튼을 만들려면 아까 Next 버튼 옆에다가 만들면 되겠군요. 근데 이 버튼은 step == 2 일 때만 보여야합니다. (App.vue에 있던 Next 버튼) <li v-if="step == 1" @click="step++">Next</li> <li v-if="step == 2" @click="publish()">발행</li> 그래서 버튼은 잘 보입니다. 이제 발행버튼을 누르면 publish() 함수가 동작하는데 여기는 무슨 코드를 짜야 게시물이 잘 추가가 될까요? 아까 말했던거 짜면 됩니다. 게시물이라는 array 에다가 내 게시물을 하나 추가하면 끝입니다. 게시물 발행기능 그래서 게시물이라는 data 에 내가 발행한 게시물을 추가하려면 ``` methods : { publish(){ var 내게시물 = { name: "Kim Hyun", userImage: "https://placeimg.com/100/100/arch", postImage: "https://placeimg.com/640/480/arch", likes: 36, date: "May 15", liked: false, content: "내가입력한글", filter: "perpetua" }; this.게시물.unshift(내게시물); this.step = 0; }, } ``` 게시물 data에 들어있는 { } 이거랑 똑같은 자료를 만드신 다음에 this.게시물에 추가하면 되는 것 아니겠습니까. unshift() 저거는 array에다가 데이터 하나 추가하는 자바스크립트 문법입니다. 그리고 업로드시 step을 0으로 이동시켜줘야합니다. 지금은 대충만든 임시게시물이 발행되는데 여러분이 실제로 입력한 이미지와 글이 발행되도록 숙제로 알아서 해오시길 바랍니다. 오늘의 5분 숙제 답안 : App.vue에 있는 발행버튼을 누르면 업로드한 이미지와 작성한 글을 이용해서 새로운 게시물이 메인페이지에 뿅 뜨도록 만들어봅시다. 답 누르기 전에 최소 2일 고민했나요? 발행 버튼을 누르면 게시물을 추가해주는건 수업시간에 다 했습니다. 근데 게시물이 임시게시물이라서 이것만 수정하면 됩니다. 실제 유저가입력한 1. 글과 2. 이미지를 App.vue 안에 그 var 내게시물 = {} 안에 잘 입력하면 기능개발 끝입니다. 일단 유저가 입력한 글을 거기 집어넣으려면 일단 유저가 입력한 글은 Container.vue에 있습니다. 거기 textarea>에 입력할 수 있는게 거기 입력한 글을 App.vue로 보내려면 저는 다음과 같이 커스텀이벤트 문법으로 App.vue에게 사용자가 입력한 글을 전송했습니다. (Container.vue) ``` <textarea @input="$emit('write', $event.target.value)" class="write-box">write!</textarea> ``` 커스텀 이벤트 문법을 까먹었으면 다시 복습 ㄱㄱ 그 다음 App.vue는 이 데이터를 저장할 공간을 하나 마련하고 이벤트를 수신하면 그곳에 저장하기로 했습니다. (App.vue) ``` <Container @write="작성한글 = $event" /> data(){ return { 작성한글 : '', } } ``` 그럼 사용자가 @ input 할 때마다 App.vue에 작성한글 부분에 데이터가 저장됩니다. 끝 그 다음에 App.vue에 있던 publish함수를 다시 찾아서 var 내게시물 = {} 이걸 꾸며봅시다. 내 게시물 {} object에 실제 입력한 글과 이미지를 집어넣는겁니다. 그럼 마무리될 듯요 (App.vue) ``` publish(){``` var 내게시물 = { name: "Kim Hyun", userImage: "https://placeimg.com/100/100/arch", postImage: this.이미지, likes: 36, date: "May 15", liked: false, content: this.작성한글, filter: "perpetua" }; this.게시물.unshift(내게시물); this.step = 0; }, ``` 이렇게 작성했다고 합니다. 그럼 이제 사용자가 입력한 이미지, 글이 제대로 반영되어서 포스팅이 생성됩니다. 이해가 안되는데 코드 베껴적기만 하는건 의미가 없습니다. 뭔가 이해되지 않는게 있다면 게시판을 적극 활용합시다. --------- ### 업로드한 이미지 인스타그램 필터 기능 만들기 (잡기술) 오늘의 숙제 : FilterBox>를 여러개 만들어놨는데 각각 인스타그램 필터가 적용되어 보이도록 코드를 짜오십시오. 인스타그램 필터명을 class="" 안에 넣으면 필터가 적용됩니다. 근데 이미 부착되어있는 클래스명은 지우면 안됩니다요. ![20220425_160710](/assets/20220425_160710.png) FilterBox.vue에 들어갈 HTML CSS template> 안에 들어갈 내용 ```
``` style>안에 들어갈 내용 ``` .filter-item { width: 100px; height: 100px; margin: 10px 10px 10px auto; padding: 8px; display: inline-block; color : white; background-size: cover; background-position : center; } ``` 인스타그램 필터명 [ "aden", "_1977", "brannan", "brooklyn", "clarendon", "earlybird", "gingham", "hudson", "inkwell", "kelvin", "lark", "lofi", "maven", "mayfair", "moon", "nashville", "perpetua", "reyes", "rise", "slumber", "stinson", "toaster", "valencia", "walden", "willow", "xpro2"] cssgram 설치 파일 https://cdnjs.cloudflare.com/ajax/libs/cssgram/0.1.12/cssgram.min.css 이거 직접 다운받아서 index.html에 집어넣거나 cdn 방식으로 첨부된 link 태그를 직접 index.html에 넣으십시오 -------- 오늘할 내용은 FilterBox.vue를 만들겁니다. 그 업로드화면에서 하단에 있는 작은 박스들 있죠? 그걸 FilterBox>라는 컴포넌트로 만들겁니다. 굳이 컴포넌트화 할 필요는 없어보이지만 학습을 위해 만들어봅시다. 거기 들어갈 HTML CSS 내용은 상단에 첨부되어있습니다. 그 다음에 FilterBox>를 step 1 화면 안에 여러개 만들어줍시다. 몇개 만드냐면 실제 인스타그램 필터 갯수만큼 많이 만들어주시면 됩니다. 그리고 우리가 업로드한 이미지도 FilterBox> 안에 보시면 div 하나 있는데 거기에 background-image로 집어넣어주십시오. 그래서 Container.vue는 이렇게 보여야합니다. ```
<div class="upload-image" :style="`background-image:url(${이미지})`">
<FilterBox :이미지="이미지" v-for="a in 필터들" :key="a"> </FilterBox>
</div> ``` ``` data(){ return { 필터들 : [위에있던 필터목록 array], } } ``` 하단엔 필터들 데이터 저장해놓고 그거 갯수만큼 보여달라고 하면 됩니다. 그리고 업로드한 이미지도 FilterBox.vue로 보내서 이미지로 띄워보시길 바랍니다. Q. 왜 data를 App.vue에 저장안하고 여기 Container.vue에다가 저장하냐고요? App.vue에선 필요없을 것 같아서요. 원래 data를 끌어다가 사용할 컴포넌트들 중 최상위 컴포넌트에 저장해두면 됩니다. ------ ###### CSSgram 이라는 라이브러리는 CSS 속성만으로 인스타그램 필터들을 흉내낼 수 있습니다. 왜냐면 채도, 명도, 선명도, 블러 이런 것들을 줄 수 있는 css 속성이 있기 때문입니다. 예를 들면 filter : contrast(150%); 이렇게 주시면 대비를 50%줄 수 있습니다. 100%가 기본값이고 0~200% 가능합니다. filter와 linear-gradient 속성만으로 인스타그램 필터를 재현한 라이브러리가 cssgram 입니다. (유사한거 많음) 설치는 쉽습니다 cssgram의 css 파일 하나만 첨부하시면 끝임 설치 후 사용법은 원하는 이미지에 class="hudson" 이런 식으로 인스타그램 공식 필터명을 class로 집어넣으면 인스타그램 필터가 먹습니다. 지원하는 필터 명은 상단에 정리되어있습니다. 설치하셨다면 오늘 숙제를 해보도록 합시다. **그래서 실은 최근 몇강은 Vue 문법 보다는 잡기술을 많이 배워가는 시간입니다.
Vue, React 잘 안다고 웹개발 잘하는게 아닙니다. 그건 도구일 뿐이기 때문에
여러가지 브라우저 기능들을 잘 알아야 신기하고 독특한 남들이 모르는 것들을 만들어낼 수 있습니다. 그러면 코딩 잘해보임** ---- ### props 싫으면 slot은 어떻습니까 오늘의 상식 : Vue에선 div>안에 class=""를 여러개 사용할 수 있습니다. ``` <div :class="데이터" class="filter-item"> </div> ``` 이런 식으로 작성해도 잘먹습니다. slot 문법 자식이 부모데이터를 사용하고 싶으면 어떻게합니까. 당연히 props로 전송해주셔야합니다. 1. 전송하고 2. 등록하고 3. 사용해야합니다. 근데 이게 귀찮으면 대용으로 쓸 수 있는 간편한 문법이 하나 있는데 slot 입니다. 사용법은 1. 자식은 slot></slot 이라는 HTML 태그를 이용해서 데이터가 꽂힐 곳을 정해놓음 2. 부모는 <자식컴포넌트>데이터 이렇게 자식컴포넌트 사이에 데이터를 작성해서 보냄 그럼 부모가 가진 데이터가 자식의 slot> 자리에 자동으로 꽂힙니다. props 등록 그런거 필요없음 하지만 유의점은 props는 src, style 속성 이런 곳에서도 사용가능하지만 slot은 HTML 태그기 때문에 HTML 태그처럼만 사용가능합니다. ------- #### Named Slots 부모가 전해줄 데이터가 많아서 slot>을 여러개 쓰고싶을 때 사용합니다. 을 여러개 쓰려면 각각 구분지어줘야하는데 그건 name 속성으로 이름을 다르게 지으면 됩니다. 1. 자식컴포넌트는 ``` ``` 이렇게 name을 설정 가능합니다. 2. 그러면 부모는 ``` <자식컴포넌트> ``` v-slot: 옆에 이름을 적으면 특정 slot에 데이터를 보낼 수 있습니다. 그니까 위의 예제는 a라는 이름의 slot에 데이터를 보낼 수 있습니다. 이름은 자유작명가능합니다. ------ Slot Props 부모가 slot으로 데이터를 전해줄 때 가끔 자식의 데이터를 사용하고 싶을 때가 있습니다. 그럴 때 쓰는 문법입니다. 1. 자식컴포넌트는 slot을 사용할 때 ``` <slot :데이터="데이터"> ``` 이러면 자식이 가진 데이터를 부모로 보낼 수 있습니다. 2. 그러면 부모는 이걸 사용하고 싶으면 ``` <자식컴포넌트> ``` v-slot: 옆에 default라고 적으면 자식이 보낸 데이터 수신이 가능합니다. 그걸 slot사용할 때 자유롭게 활용 가능합니다. (실은 그럴일 별로 없음) ---- ### 멀리 있는 컴포넌트간 데이터전송할 땐 mitt 오늘의 숙제 : 필터를 고르면 1. 그 필터가 우리가 업로드한 이미지에도 적용되어야합니다. 2. 그리고 글을 발행했을 때도 선택했던 필터가 적용되어보여야합니다. 알아서 코드짜오십시오 ##### mitt 라이브러리를 씁시다 우리가 먼 곳에 떨어진 컴포넌트에 데이터 전송할 일이 가끔 있습니다. 강의에선 FilterBox.vue 여기서 App.vue까지 데이터를 전송하고 싶은데 이거 상위 - 상위 컴포넌트 아닙니까. 그럴 경우 코드가 매우 길어집니다. 그럴 때 mitt 라는 외부 라이브러리 쓰시면 쉽게 전송이 가능합니다. Vue 2버전에서는 EventBus라는걸 썼는데 이제는 그걸 못써서 mitt라는 라이브러리를 따로 설치해주셔야합니다. 혹은 나중에 업데이트 될 수도 있겠네요. 설치법은 그냥 npm install mitt 혹은 yarn add mitt 해주시면 됩니다. mitt 셋팅은 main.js 파일을 이렇게 수정해주면 됩니다. ``` import { createApp } from 'vue' import App from './App.vue' import mitt from 'mitt' let emitter = mitt(); let app = createApp(App); app.config.globalProperties.emitter = emitter; app.mount('#app') ``` 혹은 차이점을 분석해서 잘 반영하시길 바랍니다. 참고로 app.config.globalProperties 이 부분이 모든 컴포넌트에서 사용할 수 있는 변수같은걸 등록할 수 있게 도와주는 일종의 object 자료입니다. 글로벌 변수 보관함이라고 보시면 되겠네요. 이런 데다가 자주 쓰는 axios 이런 것도 보관하면 편리합니다. import 안해와도 axios.get 할 수 있음 다만 this.axios.get 이렇게 하셔야합니다. mitt 사용법 데이터 보내고 싶은 곳에서 ``` this.emitter.emit('이벤트명작명', '데이터') ``` 이러면 전송가능합니다. 데이터 수신하고 싶은 곳에서 ``` this.emitter.on('이벤트명작명', (a)=>{ 데이터수신시 실행할 코드 a는 출력해보면 데이터나옴 }) ``` 이러면 수신가능합니다. 보통 수신하는 코드는 mounted() 안에 적습니다. 뭔가 자바스크립트 이벤트 리스너랑 사용법이 비슷합니다. \ ------------ ### 저번 시간 숙제와 Vuex 1 : 사용하는 이유 저번 시간 숙제 : 필터를 고르면 1. 그 필터가 우리가 업로드한 이미지에도 적용되어야합니다. 2. 그리고 글을 발행했을 때도 선택했던 필터가 적용되어보여야합니다. 이것은 어떻게 해결했냐면 1. FilterBox안에 있는 div를 클릭하면 mitt를 이용해서 이벤트를 발사해주세요~ (필터명을 넣어서) 2. Container, App은 이벤트발사시 이벤트를 수신해서 데이터로 저장해주세요~ 라고 코드를 짰습니다. 그 다음에 그 데이터를 알맞은 위치에 꽂아넣었다고 합니다. 끝! 알맞은 위치는 아마 Container.vue 안의 큰 이미지있는 부분과 App.vue 에서 내 게시물이라는 변수 만드는 부분이었습니다. #### Vuex 쓰는 이유 인터넷 보면 Vuex 써야된다고 하는데 이건 필수 라이브러리가 아닙니다. 1. 여러분들이 props와 custom event로 데이터 주고받는게 힘들면 씁니다. 왜냐면 Vuex를 설치하면 js 파일하나에다가 모든 데이터를 다 저장할 수 있습니다. 그럼 모든 컴포넌트들은 그 데이터를 직접 꺼내쓰고 수정할 수 있습니다. 이제 props 그딴거 필요없이 모든 컴포넌트가 데이터에 직접 접근가능합니다. 2. Vue파일과 데이터가 너무 많으면 씁니다. 왜냐면 Vuex 라는 라이브러리를 상태관리 (데이터관리) 라이브러리라고 하는데 예를 들어 name이라는 데이터를 컴포넌트 100만개에서 쓰고 있는데 갑자기 삑나면 어디서 삑났는지 name을 쓰는 곳 100개를 다 뒤져야하겠죠? 근데 Vuex를 쓰면 데이터를 한 곳에서 관리해주기 때문에, 데이터 수정하는 방법도 한 곳에서 관리하기 때문에 디버깅이 쉽습니다. 그래서 큰 프로젝트 만들 땐 필수입니다. 지금 만드는 프로젝트에선 배보다 배꼽이 더 클 수 있습니다. 어떻게 쓰는지는 일단 설치, 셋팅부터 하고 데이터 저장하는 법 부터 배워봅시다. 설치는 https://next.vuex.vuejs.org/ 들어가면 설치명령어를 구경가능합니다. 아니면 애초에 프로젝트 만들 때 vuex 선택해도 됩니다. #### Vuex 셋팅 src안에 아무데나 store.js를 만들고 다음 코드를 작성해줍니다. ``` import { createStore } from 'vuex' const store = createStore({ state(){ return { } }, }) export default store ``` 그 다음에 store.js 파일을 main.js에 등록해주어야합니다. 등록안하면 큰일남 (main.js에 추가하셈) ``` import store from './store.js' app.use(store).mount('#app') ``` 물론 차이점만 잘 분석해서 붙여넣으시길 바랍니다. 그럼 이제 store.js에 저장한 데이터들을 모든 컴포넌트가 가져다쓸 수 있습니다. 데이터 저장은 store.js에 저장하시면 되고 출력하려면 vue파일에서 하시면 됩니다. 함수나 mounted 이런 곳에서 쓰려면 this.$store.state.어쩌구 하시면 됩니다. ### Vuex 2 : store에 있는 state 데이터 바꾸는 법 오늘의 숙제 : 인스타그램 사진을 누르면 좋아요 갯수가 +1 되게 만들어보십시오. 그런데 사진을 다시 누르면 좋아요 갯수가 -1 되게 만들어야합니다. 이거 하려면 일단 좋아요 갯수를 store.js에 저장한 후 Post.vue에 꽂아넣어야겠죠? 그 다음에 좋아요/좋아요 취소 기능을 만들어보십시오. 사진 3개 중에 일단 하나만 만들어봅시다. Vuex에 있는 state 데이터를 변경하려면 직접 vue파일에서 state를 수정하면 안된다고 했습니다. 해도 되긴 하는데 그럴거면 Vuex 쓸 이유가 없습니다. name : 'kim' 이라는 state데이터가 하나 있고 100개의 Vue 파일에서 name : 'kim'을 수정한다고 칩시다. 근데 개발하다가 갑자기 'kim'이 아니라 123 이라는 숫자가 되어버리는 버그가 발생한겁니다. 그럼 대체 어떤 컴포넌트를 뒤져야합니까. 100개를 다 뒤져야합니다. 이 불상사를 방지하기 위해서 **state 수정은 store.js만 할 수 있게 코드를 짜놓는게 좋습니다.** 그러면 앞으로 state가 이상해지는 버그가 발생할 때 store.js만 뒤지면 버그를 잡을 수 있으니까요. 이런걸 멋진 개발용어로 'state 관리가 용이하다'라고 부릅니다. 그래서 Vuex를 상태(state)관리 라이브러리라고도 부릅니다. 그래서 state 변경하려면 코드 어떻게 짜야되냐면 #### mutations 라는 항목을 만들어서 거기에 데이터 수정방법을 정의합니다. 아시겠습니까 mutations라는 object 항목을 만든 다음에 거기다가 state 수정방법을 정의합니다. (함수로 만들어줍니다.) 그 다음에 이 함수를 부르는 방식으로 사용합니다. 예를 들어 .vue파일에서 어떤 버튼을 누르면 age라는 state를 1 더해주는 기능을 만들고 싶습니까. 그럼 age라는 state의 수정방법을 미리 만들어야합니다. ``` const store = createStore({ state () { return { name : 'kim', age : 20, } }, mutations :{ 한살더하기(state){ state.age++ } }, } ``` 한살더하기()를 만들었습니다. 저렇게 파라미터 하나 추가하면 위에 있는 state를 뜻합니다. 그래서 그거 가지고 마음대로 지지고 볶으면 됩니다. 그 다음에 vue 파일에서는 뭔가 버튼을 누르면 age를 +1 해주고 싶습니까. 그러면 store.js에게 부탁만 하면 됩니다. 한살더하기()를 실행해달라고요. 근데 store.js에게 부탁할 땐 정중히 부탁해야합니다. (App.vue) ``` <button @click="$store.commit('한살더하기')">버튼</button> ``` vue파일은 $store.commit(함수명) 이런 형식에 맞춰야 부탁을 할 수 있습니다. 안그러면 부탁안들어줌 아무튼 그러면 저거 버튼 누르면 store.js에게 한살더하기 실행해달라고 부탁합니다. 그럼 store.js는 한살더하기() 실행해줍니다. 그럼 age에 1이 더해집니다. 이게 Vuex에 있는 state 수정방법입니다. Vuex 쓰면 state 수정하는 것도 매우 귀찮아집니다. 하지만 나중에 버그 찾는다고 Vue파일 100만개 뒤지는 것 보다는 낫지 않겠습니까. ---------- ### 숙제해설 : 좋아요 & 좋아요 취소기능 만들기 저번시간 숙제 : 인스타그램 사진을 누르면 좋아요 갯수가 +1 되게 만들어보십시오. 그런데 사진을 다시 누르면 좋아요 갯수가 -1 되게 만들어야합니다. 이거 하려면 일단 좋아요 갯수를 store.js에 저장한 후 Post.vue에 꽂아넣어야겠죠? 그 다음에 좋아요/좋아요 취소 기능을 만들어보십시오. 어려운건 아니고 그냥 사용자가 좋아요 눌렀는지 안눌렀는지에 대한 정보도 어디다가 저장해둬야 어떤 식으로 동작할 지 판단이 가능하지 않을까요? 1. 좋아요를 눌렀는지 안눌렀는지를 state로 저장해둡니다. 그 다음에 사진을 클릭하면 2. 좋아요 누른 상태면 좋아요 -1 3. 안눌렀으면 좋아요 +1 해주면 끝인 것입니다. 그리고 좋아요를 +1 수정할 땐 당연히 mutations 함수를 만들고 App.vue는 그걸 부탁하는 식으로 사용해야합니다. (응용) 모든 사진에 좋아요 기능을 개발하고 싶으면 어떻게합니까. 지금은 사진 하나만 동작하는데 말입니다. 당연히 모든 사진에 대해서 "이 사진 좋아요 눌렀냐" 라는 데이터를 어딘가에 저장해둬야합니다. 사진이 3개면 [false, false, false] 이런 array 같은거 써도 되겠군요. 물론 게시물 정보 전부를 Vuex로 옮겨와서 거기다가 기록하는게 가장 좋은 방법입니다. 데이터를 찢어놓는 것 보다 한 곳에 모아놓는게 좋죠. ------------ ### Vuex 3 : actions 항목을 알아보자 Vuex에선 state를 수정할 때 mutations 함수를 만들어서 그걸 이용해서 수정하라고 배워봤는데 가끔 서버에서 데이터를 가져와서 수정하고 싶을 때가 있습니다. 그럴때 당연히 서버로 ajax 요청을 날리면 되는데 그런건 mutations에 직접 적지 않고 actions라는 항목에 적으셔야합니다. 왜냐면 mutations 함수들을 만들 때 내부에 ajax처럼 오래걸리는 코드를 적어놓는다면 나중에 길게 코드짤 때 힘들어져서 그렇습니다. 예를 들어서 차례로 name과 age 데이터를 수정하고 싶다고 가정해봅시다. 무조건 name을 수정하고 그 다음에 age를 수정하고 싶은 겁니다. 그러려면 그냥 ``` name수정하는함수() age수정하는함수() ``` 이렇게 사용하면 끝입니다. 근데 님들이 name수정하는함수() 안에 3초 넘게 걸리는 ajax 요청을 담아놨다면 ``` name수정하는함수() //3초걸림 age수정하는함수() ``` 이렇게 사용하면 age 수정이 먼저 될 수도 있습니다. 왜냐면 자바스크립트는 ajax처럼 오래걸리는 코드는 (정확히 말하면 비동기식 처리를 지원하는 코드는) 잠깐 제껴두고 다음줄 부터 실행하려는 경향이 있기 때문입니다. 그럼 나중에 의도치않은 버그가 생길 수 있고 코드가 길어질 수 있습니다. 그래서 state를 수정하는 mutations 함수는 ajax 그런거 넣지말고 순수하게 state 변경만 하는 함수로 만들어두시길 바랍니다. ajax 그런건 actions에 맡겨줍시다. ---- actions 만드는 법 그낭 mutations처럼 actions 항목을 만드신 후에 함수형태로 만드시면 됩니다. ``` actions : { 데이터가져오기(){ axios.get('').then(()=>{ 성공시 실행할 코드 }) } } ``` 그럼 이제 App.vue 이런 곳에서 $store.dispatch('데이터가져오기') 라고 사용했을 때 진짜 데이터가져와줍니다. 그리고 당연히 actions 함수를 부탁할 땐 $store.dispatch라고 정중히 부탁하셔야합니다. 데이터가져온 직후에 그걸로 state도 변경하고 싶으면 ``` actions : { 데이터가져오기(context){ axios.get('').then(()=>{ context.commit('mutations함수명') }) } } ``` 이렇게 사용합니다. 왜냐고요? state 변경할 땐 무조건 mutations 함수 만들어쓰셔야하니까요. 그리고 mutations함수를 사용할 땐 예외없이 commit() 이라고 쓰셔야하는데 그걸 쓰기 위해선 함수에 context라는 파라미터 (자유작명가능) 하나 추가해주면 쓸 수 있습니다. 그 파라미터는 $store 변수같은 거라고 생각해도 좋습니다. 뭔가 복잡하지만 익숙해지면 이게 코드 관리하기 좋구나라는 생각을 하게 될겁니다. 아닐 수도 있지만 결국 프로젝트 커지면 Vuex는 필수기 때문에 이에 능숙해지고 싶으면 Vuex를 이용해서 인스타그램이나 부동산사이트를 다시 만들어보는건 어떨까요. 아니면 개인 프로젝트 하나 해보라고 했었던 기억이 나는데 거기 적용해보든가요. 강의 듣는게 중요한게 아닙니다. 꼭 제발 처음부터 직접 프로젝트 만들어보는 연습을 해보도록 합시다. ---- ### Vuex 4 : mapState를 사용하면 편리할 수도 있음 state를 vue파일에서 꺼내쓸 때 $store.state.name 이런 식으로 꺼내썼는데 이게 길고 귀찮으면 mapState라는 함수를 써보십시오. 그 전에 computed 라는 항목에 대해 알아야합니다. 잠깐 알아보는 computed 함수만들 때 methods : {} 안에 만들라고 했는데 실은 computed : {} 안에 만들 수도 있습니다. ``` computed : { now2(){ return new Date() } }, methods : { now(){ return new Date() } } ``` 똑같은 기능의 함수를 2개 만들어봤는데 차이점은 methods 안에 만든 함수는 함수를 부를 때마다 안의 코드가 실행됨 computed 안에 만든 함수는 함수를 불러도 안의 코드가 실행안됨 이런 차이가 있습니다. computed는 그냥 컴포넌트로드시 한번 실행되고 그 값을 계속 저장해서 씁니다. computed는 일종의 계산결과 저장공간이라고 보시면 됩니다. - computed 함수는 return 안쓰면 안됩니다. - computed 함수를 가져다가 쓸 때는 소괄호없이 함수명만 쓰면 됩니다. 참고로 알아두셈 다시 Vuex로 돌아갑시다. -------- computed 쓰면 state 꺼내는 코드 짧아짐 맨날 $store.state.name 이렇게 하면 코드가 얼마나 길고 더러워지겠습니까. 데이터가 좀만 복잡해도 $store.state.name.name2[0].name3 이런 식으로 써야합니다. 아이디러 그럴 땐 자주 꺼내쓰는 state를 computed에 넣어놓으면 나름 짧게 사용가능합니다. methods 이런데 넣어도 되지만 그냥 computed가 적절한듯 ``` computed : { name(){ return this.$store.state.name } } ``` (script>태그 안에서 $store쓰려면 this.$store 입니다) 그러면 이제 위에가서 이렇게 쉽게 state를 꺼내쓸 수 있는 것입니다. 이제 HTML이 안디러움 mapState 쓰면 computed 코드 짧아짐 하지만 꺼낼 state가 100개 있으면 computed도 100개 만들어야하네요? 그것도 드러울듯 그렇다면 mapState라는걸 꺼내쓰시면 됩니다. 그러면 알아서 computed 에다가 state를 등록해줌 ``` import {mapState} from 'vuex' computed : { ...mapState(['state이름1', 'state이름2']) } ``` 이러면 끝입니다. 위에서 import 해와야 쓸 수 있습니다. 이제 정말 안더러움 ``` import {mapState} from 'vuex' computed : { ...mapState({ 작명 : 'state이름1'}) } ``` 혹은 object 자료형을 넣으면 state가져올 때 이름변경도 가능합니다. ``` import {mapState, mapMutations} from 'vuex' computed : { ...mapState(['state이름1', 'state이름2']), ...mapMutations([ '좋아요', 'setMore' ]) } ``` 이러면 mutations 함수도 쉽게 가져다 쓸 수 있습니다. 이제 $store.commit('좋아요') 이게 아니라 좋아요() 이렇게 쓸 수 있는 것임 참고로 mapActions 이런 것도 있습니다. 코드가 더러워지면 써보시길 바랍니다. ------------ ### Progressive Web App & 셋팅 우리가 만든 웹사이트도 앱으로 발행가능합니다. PWA를 사용하시면 웹사이트를 그대로 모바일앱처럼 쓸 수 있게 만들 수 있습니다. 실상은 "웹페이지 홈화면에 바로가기 추가"를 고상한 말로 표현해놓은 건데 일반 유저는 앱이라고 생각할걸요. 웹브라우저 주소창이 제거된 상태로 보여주니까요. PWA 발행하려면 라이브러리 설치가 필요합니다. ``` vue add pwa ``` 라고 터미널에 입력하면 설치 끝입니다. 설치만 하시면 이제 여러분은 npm run build 입력할 때 PWA가 알아서 셋팅되어 나옵니다. 배포하면 바로 PWA로 사용가능 PWA 구성요소 브라우저가 사이트를 PWA로 인식하면 "설치하시겠습니까?" 팝업같은 것도 띄워줍니다. PWA로 인식하려면 두개의 파일이 필요합니다. service-worker.js 그리고 manifest.json 입니다. manifest.json은 앱의 이름, 아이콘, 테마색상 이런 것들을 설정할 수 있는 파일이며 형식에 맞춰 작성하면 됩니다. service-worker.js는 어떤 html css js 파일들을 캐싱해놓을건지를 명시하는 파일입니다. 캐싱해놓은 파일들은 오프라인에서 이용가능합니다 (모바일앱처럼요) 근데 두개의 파일은 npm run build할 때 자동생성됩니다 직접 만들 필요는 없습니다. PWA 설정은 뭔가 세부설정을 수정하고싶으면 직접 manifest.json 파일을 건드리진 않습니다. build할 때마다 새로 생성되기 때문에 직접 건드리진 않는겁니다. 그래서 설정을 바꾸고 싶으면 vue.config.js라는 파일을 vuestagram 이런 프로젝트 폴더안에 하나 생성합니다. ``` module.exports = { pwa: { name: '님 앱이름', themeColor: '#4DBA87', msTileColor: '#000000', workboxOptions: { exclude: [/\.map$/, /manifest\.json$/, 'index.html'] } } } ``` ▲ 그리고 위의 코드를 붙여넣기 하시면 됩니다. 구글의 workbox 라는 라이브러리 사용법대로 설정을 채워주시면 되는데 앱의 이름, 색상 이런 것도 커스터마이징 가능하고 exclude 항목에는 특정 파일들을 캐싱하기 싫으면 저기다가 입력해주면 됩니다. (정규식도 잘 먹습니다) ------- 실제 구글 플레이스토어에 등록가능한 모바일 앱으로 발행하고 싶으면 Vue 문법으로 iOS, Android 개발을 도와주는 툴들이 몇개 있습니다. 그거 사용하라고 권장합니다. 전 안써봐서 모름 https://v3.vuejs.org/guide/mobile.html#introduction 하지만 신규 서비스 출시는 모바일 앱보다 웹이 더 나을 수 있습니다. - 요즘 사람들은 게임 아니면 신규 앱 설치 안하는 경향이 있고 - 그렇다보니 앱설치유도는 웹방문유도에 비해 마케팅비용이 3배 이상인 점 - 인앱결제 수수료 30% - 귀찮은 앱 개발자 등록비용 - 버전업데이트할 때마다 플랫폼 2개를 관리해야함 이런것도 생각해야하지 않을까요 ----------- ### 버그찾고 싶으면 Vue devtools 설치합니다 (짧아서 글로 진행합니다) 간혹 코드짜다보면 props를 분명 전해줬는데 에러나고 멈추고 하는 경우가 있습니다. 그 경우 터미널이나 크롬 개발자도구 console 탭으로 들어가면 대부분의 에러는 해결가능한데 조용하게 에러나는 경우도 많습니다. 예를 들어 라우터 이런 것들은 뭔가 틀려도 에러로 알려주지 않습니다. 그래서 크롬 확장프로그램 중에 Vue-devtools 라고 설치하시면 좀 더 자세히 버그나는 부분을 파악가능합니다. https://chrome.google.com/webstore/category/extensions 여기 들어가서 Vue.js devtools 라고 찾아서 설치합니다. ![20220425_170854](/assets/20220425_170854.png) ▲ (주의) 두개 뜨는데 둘 중 나중에 출시한 확장프로그램으로 설치하시길 바랍니다. 예전에 나온건 Vue 2 만을 위한 확장프로그램입니다. 설치하시면 localhost:8080에서 우리 사이트를 미리볼 때 개발자도구에 Vue라는 메뉴가 생깁니다. ![20220425_170925](/assets/20220425_170925.png) ▲ 누르면 신세계가 펼쳐집니다. 왼쪽은 무슨 컴포넌트 안에 무슨 컴포넌트가 있는지 DOM 트리처럼 구조화해서 보여주고 오른쪽은 컴포넌트를 택했을 때 거기 안에 들어있는 data, vuex state, props 목록을 쭉 보여줍니다. 그래서 state가 잘 변하고 있는지 확인하고 싶으면 여기서 확인하면 되는 겁니다. props도 잘 보냈는지 확인해볼 수 있고요 ![20220425_170942](/assets/20220425_170942.png) ▲ 매우 신기한 버튼들도 제공하는데 과녁버튼 누르고 브라우저 내에 원하는 요소를 찍으면 그 컴포넌트를 검사해줍니다. <> 버튼 누르면 그 컴포넌트에 해당하는 요소를 element 탭에서 보여주고 네모난 버튼 누르면 VScode에서 해당 컴포넌트 열어줍니다. 신기함 - 프로젝트에 라우터를 설치하셨으면 현재 route, parameter 이런것도 다 확인할 수 있습니다. - state 누르면 브라우저에서 수정도 해볼 수 있습니다. - Timeline 메뉴에선 지금까지 어떤 이벤트가 동작했는지 체크도 가능합니다. 아무튼 신기한 기능이 많아 재밌으니 버튼 이것저것 눌러보시길 바랍니다. 혼자 코드짜다가 의도대로 동작하지 않을 때 자주 활용해봅시다. ----------- ### Composition API 사용법 (팔로워 페이지 만들기) 오늘의 5분 숙제 : 코드 잘 짜서 다음 사진처럼 만들어오십시오. follower.json에 있던 5명의 이름, 이미지가 잘 박혀있어야합니다. ![20220425_171025](/assets/20220425_171025.png) follower.json 에 들어갈 데이터 ``` [ { "id" : 0, "name" : "_Limvely", "image" : "https://placeimg.com/200/200/animals/grayscale" }, { "id" : 1, "name" : "salmon_X", "image" : "https://placeimg.com/200/250/people" }, { "id" : 2, "name" : "360noscope", "image" : "https://placeimg.com/200/250/animals" }, { "id" : 3, "name" : "Jeony_1", "image" : "https://placeimg.com/200/200/people/sepia" }, { "id" : 4, "name" : "mihyeon", "image" : "https://placeimg.com/200/200/tech" } ] ``` .vue 파일이 좀 길어지면 단점이 있을 수 있습니다. 안에 데이터 100개 methods 200개 computed 300개 있으면 특정 데이터와 관련된 기능을 찾으려면 멀리 여러곳을 이동해야한다는 단점이 있습니다. age라는 데이터를 다루는 곳을 찾고 싶으면 각각 150번줄 380번줄 670번줄을 찾고 이래야 하는 것이지요. 이게 싫으시면 Vue 3버전 부터는 코딩스타일을 하나 더 제공합니다. Composition API 라는 건데 computed, methods, watch, data() 이런걸로 파일을 쪼갤 필요 없이 그냥 관련 기능을 한 곳에서 쭉 코드짜고 싶으면 사용하시길 바랍니다. 이걸 쓰면 기존 방식보다 문법이 약간 귀찮을 순 있습니다. 마이페이지를 만들어봅시다 그냥 내 팔로워 목록을 보여주는 간단한 페이지 만들어보며 Composition API를 배워봅시다. 빨리 MyPage.vue를 만드시고 ``` ``` HTML 부분에 이걸 채워넣으시길 바랍니다. 그럼 vue 파일 완성 그리고 Container.vue에는 ```
``` 이렇게 컴포넌트를 사용해보시길 바랍니다. 당연히 이렇게 사용하려면 import 해오고 components에 등록하고 쓰십시오. 그리고 App.vue는 ``` data(){ return { step : 3 } } ``` step이라는 데이터항목을 3으로 강제로 변경시켜줍시다. Composition API 식으로 데이터만드는 법 팔로워 이름들을 저장할 곳이 필요합니다. 그래서 array 데이터를 하나 만들고 싶어졌습니다. 이전엔 data(){} 여기 집어넣었는데 이거랑은 약간 다릅니다. 일단 기본적으로 setup() 이라는 hook 같은걸 만들고 항상 그 안에 코드짜면 됩니다. (MyPage.vue script태그안에 들어갈 내용) ``` import { ref } from 'vue' export default { name : 'mypage', setup(){ let follower = ref([]); return { follower } }, } ``` 이렇게 적어주시면 follower : [] 이라는 데이터를 만든거랑 똑같은 효과입니다. ref() 라는 함수를 import 해와서 var 데이터이름 = ref(데이터) 안에 항상 데이터를 저장해줘야합니다. 안그러면 데이터 변경시 실시간 재렌더링이 안됨 그리고 마지막에 return { 데이터이름 } 이렇게 퉤 뱉어주셔야 위에 HTML란에 가서 이렇게 사용이 가능합니다. 안그러면 데이터바인딩 못함 ------ Ajax 요청 & 데이터 변경하는 법 Composition API라고 ajax 요청하는 법이 다른건 아니고 똑같습니다. axios 대충 쓰셈 근데 데이터 수정하는 법은 살짝 이상합니다. ``` import { ref } from 'vue' import axios from 'axios' export default { name : 'mypage', setup(){ let follower = ref([]); axios.get('/follower.json').then((a)=>{ follower.value = a.data }) return { follower } }, } ``` get요청으로 아까 저장한 json 파일을 가져옵시다. 그러면 a.data 안에 그 [{},{},{},{},{}] 이렇게 생긴 데이터가 딸려오는데 그걸 follower라는 곳에 저장하고 싶으면 follower = a.data가 아니라 follower.value = a.data 라고 써주셔야합니다. 굳이 설명하자면 ref() 이걸로 데이터를 만드는게 object자료형 이런걸로 데이터를 싸매는 행위기 때문에 .value로 object 안의 데이터를 수정해주셔야하는 것인데 이해할 필요는 없고 그냥 그렇게 쓰라고 되어있으니 쓰도록 합시다. Lifecycle hook 쓰는 법 컴포넌트가 부착될 때, 업데이트될 때 뭔가 실행하고 싶으면 created() mounted() beforeUpdate() 이런거 쓸 수 있다고 했습니다. Composition API 안에서는 이거 함수명이 약간 다릅니다. ``` import { ref, onMounted } from 'vue' import axios from 'axios' export default { name : 'mypage', setup(){ let follower = ref([]); onMounted(()=>{ axios.get('/follower.json').then((a)=>{ follower.value = a.data }) }) return { follower } }, } ``` 1. onMounted() 이런 훅을 import 해오시고 2. onMounted(()=>{ 마운트후에 실행할 코드 }) 이렇게 작성합니다. 그래서 저는 ajax 요청을 mount 후에 실행하고 싶어서 이렇게 작성한 것입니다. 끝 여기서의 lifecycle 함수명은 그 원래 이름 앞에다가 on만 붙이면 됩니다. beforeUpdate() 이런건 예를 들어서 onBeforeUpdate() 이렇게 사용하시면 됩니다. (참고) created() 이건 onCreated() 이렇게 쓰면 안됩니다. 이런 함수 없음 왜냐면 setup() 이 공간 자체가 그냥 created() 이거랑 매우 유사하다고 생각하시면 되겠습니다. 거기다가 코드짜면 그냥 created() 안에 코드 짠거랑 비슷하게 동작함 (오늘의 교훈) 귀찮으면 Options API를 사용합니다. 이전 시간까지 우리가 썼던 문법이 Options API입니다. 다 했으면 다음시간까지 5개 프로필데이터 받아온걸 HTML로 5개 모두 보여주는 코드를 빠르게 작성해오십시오. ------ ### Composition API 사용법 2 & 간단한 검색기능 오늘의 숙제 : 팔로워 검색기능을 만들어오십시오. 인풋에 s라고 입력하면 이름에 s가 들어있는 팔로워들만 보여야합니다. 이건 자바스크립트를 잘해야합니다. Vue 문법 그딴건 원래 중요하지 않습니다. (팁) 데이터를 바꾸면 HTML은 알아서 변경됩니다. 저번시간 숙제는 별거아니라서 ```

팔로워

<div class="post-header" v-for="(a,i) in follower" :key="i"> <div class="profile" :style="`background-image:url(${a.image})`">
</div> </div> ``` HTML 이렇게 건드리면 5명의 팔로워들이 잘 보입니다. 실은 ref 말고 reactive도 있습니다. 데이터 만들 때 ref 쓰라고 했는데 그거 말고 reactive라는 함수도 똑같이 사용가능합니다. (import 해와야함) ``` import { ref, reactive } from 'vue' export default { setup(){ let follower = ref([]); let test = reactive({name : 'kim'}) return { follower } }, } ``` ref를 쓰든 reactive를 쓰든 똑같은 역할을 하는데 reactive는 object, array 같은 reference data type을 주로 담고 ref는 숫자, 문자같은 primitive data type을 담게 되어있습니다. 근데 실은 구분해서 쓰기 귀찮아서 전부 ref 씁니다. ref라는 함수 까보면 "그냥 reactive 해주세요~" 그거밖에 없습니다 둘다 똑같은 함수입니다. ------- ####3 props 사용법 composition API를 써서 개발할 때 setup() 함수안에 코드짜면 된다고 했는데 안타깝게도 setup() 함수 안에서는 위에 등록된 props를 this.props 이런 식으로 가져다쓸 수 없습니다. 그래서 props 가져와서 뭔가 개발하고 싶을 땐 이렇게 쓰셔야합니다. ``` import { ref, toRefs } from 'vue' export default { setup(props){ let follower = ref([]); let { 프롭스명 } = toRefs(props) console.log(프롭스명.value) return { follower } }, } ``` setup() 함수 안에 파라미터를 두개까지 집어넣을 수 있는데 첫째는 자동으로 props가 되고 둘째는 이상한 context라는게 됩니다. 둘째는 잘 안쓰니 한번 찾아보시고 아무튼 첫째 파라미터를 출력해보면 props가 다 담겨있습니다. 그걸 근데 그냥 쓰시면 안되고 ref 안에 담아 쓰셔야합니다. 안담아쓰면 props가 바뀌어도 실시간 반영이 안됨 근데 담아쓸 때는 toRefs라는걸 이용하는데 그냥 ref 여러번 해주는 함수입니다. (import 해와야함) 그리고 등호 왼쪽에 {props이름, props이름2 ~~ } 이런 식으로 잘 적어주면 됩니다. 그럼 이제 props이름.value라고 찍으면 props 나옵니다. 이걸로 개발하시면 됩니다. 끝 그리고 까먹었을까봐 다시 강조하는데 props는 자식컴포넌트가 수정하시면 안됩니다. ##### watch 사용법 composition API를 써서 개발할 때 setup() 안에서 watch 같은 걸로 데이터변화를 감시하고 싶으면 ``` import { ref, watch } from 'vue' export default { setup(props){ let follower = ref([]); watch( 데이터명, ()=>{ 데이터 변화시 실행할 코드 } ) return { follower } }, } ``` watch함수를 import 해와서 저렇게 쓰면 됩니다. 그럼 이제 데이터 감시 잘해줌 props도 감시가능합니다. ---- ##### computed 사용법 이쯤되면 어떻게 써야할지 예상가능합니다. ``` import { ref, computed } from 'vue' export default { setup(props){ let follower = ref([]); let 어쩌구 = computed( ()=>{ return 10 } ) console.log(어쩌구.value) return { follower } }, } ``` computed는 데이터 연산결과를 잠깐 저장하는 곳이라고 했습니다. computed를 import 해온 뒤에 소괄호안에 함수 하나 집어넣으시면 되는데 그 함수는 연산결과를 return으로 퉤 뱉는 함수가 되면 됩니다. 그걸 변수에 담아서 쓰십시오. computed도 일종의 데이터취급이라서 .value라고 찍어야 제대로 잘 출력됩니다. ----- ##### methods 사용법 그냥 일반 함수 만들고 싶을 땐 methods에 넣었는데 composition API에서는 ``` import { ref } from 'vue' export default { setup(props){ let follower = ref([]); function hello(){ } return { follower, hello } }, } ``` 아무데나 함수 만드시면 그게 methods 입니다. 그리고 그걸 return 안에다가 적으시면 위에 HTML 란에서도 사용가능합니다. ##### Vuex store 사용법 setup함수에서 $store.state.name 이런거 어떻게 쓰냐면 ``` import { ref } from 'vue' import { useStore } from 'vuex' export default { setup(props){ let follower = ref([]); let store = useStore(); console.log(store.state.name) return { follower } }, } ``` 1. useStore라는걸 vuex에서 import 해옵니다. 2. useStore()라고 쓰시면 이게 예전의 $store랑 똑같은 의미입니다. 이제 자유롭게 state 뽑든가 아니면 commit 하든가 dispatch 하든가 하시면 됩니다. (참고) mapState 그런건 setup 안에서 못씁니다. 나중에 Vuex 5버전 출시하면 그때 한번 설치해서 써보십시오. 지금 4버전과는 문법이 약간 달라질겁니다. ------------ 오늘의 숙제 : 팔로워 검색기능을 만들어오십시오. 힌트는 array.filter() 함수를 쓰면 array에서 내가 원하는 것만 남길 수 있습니다. 이건 두시간 고민해도 코드 한줄 못짜는 바보를 위한 힌트 이렇게 코드짜면 끝 아닙니까 1. input>이 이미 있는데 여기다가 검색어를 입력하면 2. follower라는 [{},{},{},{},{}] 이렇게 생긴 array에서 그 검색어가 들어있는 항목만 [{},{}] 이런 식으로 남깁니다. 이게 끝인데요 왜냐면 follower라는 state가 변하면 HTML은 자동으로 알아서 바뀝니다. v-for로 HTML을 follower 갯수만큼 생성되게 만들어놨잖아요. -------- 답은 누르기 전에 최소 2일은 고민해야합니다 하지만 검색기능 쬐끔 만들었다고 끝이 아닙니다 대소문자 포함 검색, 자음검색 은 어떻게 할 것임? 이런 응용도 한번 생각해보도록 합시다. 물론 어려움을 느낀다면 자바스크립트 문법이 익숙하지 않거나 논리력 부족이라 그렇습니다. 어릴때 씽크빅 논리왕 문제집을 안푼 것임 1. input>이 이미 있는데 여기다가 검색어를 입력하면 2. follower라는 [{},{},{}] 이렇게 생긴 array에서 그 검색어가 들어있는 항목만 [{},{}] 이런 식으로 남깁니다. 1번을 자바스크립트로 번역하면 ``` <input placeholder="?" @input="search($event.target.value)" /> setup(){ let follower = ref([]); function search(검색어){ } return {follower, search} } ``` 이렇게 되겠는데요 검색어를 입력할 때마다 search() 함수를 실행하라고 했습니다. 여기 안에 2번기능 개발하면 되겠는데요 그리고 $event.target.value하면 입력한 검색어가 나올텐데 그걸 search() 함수 안에 파라미터로 입력하라고 해놨습니다. 2번 기능 개발하기 전에 잠깐 filter() 함수 설명하자면 filter()라는 함수는 array에 붙일 수 있는 JS 기본 함수인데 이름처럼 array에서 원하는 자료만 남기고 싶을 때 쓰는 함수입니다. filter((a)=>{ return 조건식 }) 이렇게 쓰라고 되어있는데 a라는 파라미터는 무조건 array안에 있던 자료가 되고 조건식을 작성할 수 있는데 조건식이 참일 경우에만 그 자료를 남겨줍니다. ``` [1,2,3].filter((a)=>{ return a == 2 }) ``` 예를 들어 이렇게 작성하면 [2] 이것만 그 자리에 남는다는 소리입니다. 2번을 자바스크립트로 번역하면 ``` setup(){ let follower = ref([]); function search(검색어){ let newFollower = follower.value.filter((a)=>{ return a.name.indexOf(검색어) != -1 }); follower.value = [...newFollower] } return {follower, search} } ``` 이렇게 되겠는데요 1. follower.value에다가 filter()를 붙였습니다. 안에 조건식은 a.name이라는거 안에 검색어($event.target.value) 가 들어있냐라고 작성했습니다. 2. 그리고 그건 let newFollower = 이런 식으로 변수에 담아 써야합니다. (그냥 정해진 JS 기본 filter함수 사용법입니다) 3. 그리고 그걸 follower.value에 집어넣습니다. 안전하게 카피본을 만들어 집어넣읍시다. (근데 그럴 필요까진 없어보이긴합니다) ![20220425_172058](/assets/20220425_172058.png) 그럼 검색 잘 됩니다. 근데 뭔가 문제가 있는데 Q. 뭔가 검색하고 백스페이스로 지우면 팔로워가 아예 영구적으로 사라져있는데요? 검색 계속 하다보면 팔로워가 아예 텅 빕니다. 언제나 버그는 원인을 파악하셔야 해결가능하지 쳐다보기만 한다고 해결할 수 없습니다. follower라는 데이터가 의심가면 콘솔창 출력이나 Vue devtools를 켜보도록 합시다. 이 문제는 예전에 부동산사이트의 sort버튼, 원래대로버튼 기능개발할 때 경우랑 유사합니다. 위 코드는 1. s를 검색하면 follower라는 데이터안에 s가 들어있는 2개의 사람만 [{},{}] 남습니다. 2. s라는 검색어를 삭제하면 원래대로 5명을 다 보여줘야 하지만 이미 follower 라는 array는 [{},{}] 이것만 남아있습니다. follower라는 데이터를 영구적으로 변형시켜버렸으니 원래대로 복구를 못시키는 것임 그래서 예전에 했던 것 처럼 원본 보존을 해놓으시면 되겠습니다. ----- ``` setup(){ let follower = ref([]); let followerOriginal = ref([]); onMounted(()=>{ axios.get('/follower.json').then((a)=>{ follower.value = a.data; followerOriginal.value = [...a.data]; }) }); function search(검색어){ let newFollower = followerOriginal.value.filter((a)=>{ return a.name.indexOf(검색어) != -1 }); follower.value = [...newFollower] } return {follower, search} }, ``` 1. followerOriginal이라고 원본을 저장해둘 state를 만들어둡니다. 2. followerOriginal에다가 filter를 줘봅니다. 그리고 그 filter한 결과를 follower.value에 집어넣습니다. 그려면 이제 님들 검색어 입력할 때마다 followerOriginal에서 filter를 수행하고 그 filter 결과를 follower에다가 반영해줍니다. 이제 s를 입력해도 follower는 변형되지만 followerOriginal은 언제나 그대로 남아있습니다. s를 입력한걸 지우면 followerOriginal에 있던 모든걸 follower 안에 집어넣어줄겁니다. 이해가 되었다면 빨리 응용이나 해봅시다 이해가 안되었다면 이것은 자바스크립트 문법과 자료형이 익숙하지 않아서 생기는 문제일 뿐입니다. 그래서 맨날 자바스크립트 연습할 겸 코드 혼자 짜보라는 것임





© 2021.03. by yacho

Powered by github