해리의 데브로그

Vue.js 01 - 기본 개념

|

Vue.js 기본개념

1. 싱글 페이지 어플리케이션 (SPA)

싱글 페이지 어플리케이션(single-page application, SPA)란 서버로부터 완전한 새로운 페이지를 불러오지 않고 현재의 페이지를 동적으로 다시 작성함으로써 사용자와 소통하는 웹 어플리케이션이나 웹 사이트를 일컫는 말임.

단일 페이지 어플리케이션(SPA)는 현재 웹 개발의 트렌드임.

기존 웹 서비스는 요청시마다 서버로 부터 리소스들과 데이터를 해석하고 화면에 렌더링 하는 방식이었지만, SPA 형태는 브라우저에 최초에 한번 페이지 전체를 로드하고, 이후부터는 특정 부분만 Ajax를 통해 데이터를 바인딩 하는 방식임.

2. Vue.js 시작하기

Vue.js는 웹 개발을 단순화하고 정리하기 위해 개발된 대중적인 자바스크립트 프론트엔드 프레임워크이다.

1) 시작하기

  • head 태그 내 Vue.js CDN를 사용하기 위한 코드 삽입
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">	   
    </script>
</head>

2) 기본 양식 - 선언적 렌더링

“Vue.js의 핵심은 간단한 템플릿 구문을 사용해 선언적으로 DOM에 데이터를 렌더링 하는 것임”

  • vue를 통해 JS 코드를 작성하기 위해서는 vue 인스턴스를 만드는 것부터 시작함.
  • body 태그 내 element를 하나 생성(예; div)하고, 그에 대응하는 vue 인스턴스를 생성하는데 시작함. el 에는 동기화하는 element의 선택자를 입력함.
  • body 태그 내에서는 장고 문법과 동일한 방법으로 data 내 key를 통해 value값을 불러 올 수 있음.
  • vue 객체를 생성할 때는 아래와 같은 options을 포함 시킬 수 있음.
    • el : “어디에” 에 대한 개념. 연결시킬 DOM(element)의 선택자를 넣음 (예, id, class)
    • data : “무엇을” 보여줄건지를 정의
    • methods: “어떻게” 보여줄건지를 정의. 데이터를 조작하는 함수를 정의. 하기 예시의 경우 plus 함수를 실행하면 count 값이 증가됨.
  • thisdata 내 정보를 내부적으로 불러올 때 사용. 예) this.count
<body>
    <div id="app">
         - 8
    </div>
    <script>
        const app = new Vue({
            el: '#app', // 어디에
            data: {
                message: 'hello, Vue',
                count: 0
            },
            methods: {
                plus: function(){
                    this.count++
                }
            }
        })
    </script>    
</body>

3) Vue 인스턴스 내 변수 접근 방법

  • 어트리뷰트를 불러올 때는 $ 를 붙어야함 예) app.$data / app.$el / app.$methods
  • data 내 정보를 불러올 때는 app.$data.message와 같이 입력해야 하나, Vue에서는 app.message 처럼 다이렉트로 접근 가능하도록 편의성을 제공함.

4) 조건문 v-if & 반복문 v-for

  • 조건문 예시
    • v-if 안의 내용에는 자바스크립트 코드가 들어감
    • data 내 count 값을 변경하여 조건문 적용 유무를 확인 해 볼 수 있음.
<body>
    <div id="app">
         - 8
        <p v-if="count > 1"> 이건 보임 </p>
    </div>
...중략
  • 반복문 예시 (1)
<div id="app">
    <!-- 중략 -->
	<li v-for="post in posts">
        
    </li>
</div>
<script>
    const app = new Vue({
        	//...중략
        data: 
			//...중략
            posts: ['첫 게시물', '두번째 게시물', '세번째!']
        },
			//...중략
    })
</script>
  • 반복문 예시 (2) -딕셔너리
    • Vue 에서는 딕셔너리를 기본적으로 반복문을 돌릴 경우, 딕셔너리의 value 값이 출력 됨.
    • key 값을 출력하기 위해서는 Object.keys() 를 사용해야함.
    • key와 value를 둘다 출력 할 수 도 있음.
<div id="app">
    <li v-for="info in students">
    	
    </li>
    <li v-for="info in Object.keys(students)">
    	
    </li>
    <li v-for="info in Object.keys(students)">
         - 
    </li>
</div>
<script>
    const app = new Vue({
        data: {
            students: {
                name: 'Harry',
                uni: 'UVIC',
            }
        },
    })
</script>

5) MVC vs MVVM

  • Django의 MTV(MVC) pattern 처럼, Vue.js 또한 유사한 패턴을 갖고 있음.
  • Django내 model과 View 사이에서 controller의 역할을 했던 views.py 처럼, Vue가 Model과 View 사이를 데이터 바인딩을 통해 controller의 역할을 함. 여기서는 controller 대신 View-Model의 앞글자를 따서 VM이라고 함.
Pattern M V C / VM
Django Model View(Template.html) Controller(views.py)
Vue.js Model View(HTML) View-Model(Vue)

JavaScript 06 - Django 내에서 JS 적용하기 (2) (댓글 생성 기능 동적 구현)

|

JavaScript 05 - Django 내에서 JS 적용하기 (좋아요 기능 동적 구현)

|

JavaScript 04 - Event Listener & Axios를 통한 요청보내기

|

Event Listener & Axios를 통한 요청보내기

1. Event Listener: [무엇]을 [언제], [어떻게]한다.

Event Listener란 이벤트가 발생했을 때 그 처리를 담당하는 함수를 가리킴. 지정된 타입의 이벤트가 특정 요소에서 발생하면, 웹 브라우저는 그 요소에 등록된 Event Listener를 실행시킴.

Event Listener는 말그대로 해당 Event에 대해 대기중인 것을 말함. 항상 “Listen”하고 있는 상태라고 할 수 있음. 사용자가 지정한 Event가 발생했을 때, 등록한 Event Listener가 실행됨. 여기서 Event는 “ [무엇] 을 [언제], [어떻게] 한다. “ 라고 정의 할 수 있음.

버튼을 클릭하면 메세지가 나온다” 를 event Listener를 통해 구현해보도록 하자.

1) DOM Selector

  • querySelector
  • querySelectorAll()

2) Event Listener 등록

“특정한 DOM element[무엇] 이 어떠한 행동을 했을 때[언제] **, 어떻게 한다[어떻게]**”

  • id값은 html 문서에서 유일하게 존재하는 고유값으로, id값을 바탕으로 버튼을 갖고와 변수에 저장시킨다. document.querySelector('#this-button')
  • addEventListener 매개변수
    • 1번째 인자: 반응할 이벤트의 유형을 나타냄. click 은 마우스 클릭시 이벤트가 실행됨.
    • 2번째 인자: 지정한 타입의 이벤트가 발생했을 때, 알림을 받는 객체. 하기 예시의 경우 callback 함수를 받음.
  • .innerHTML : 요소 내 포함된 HTML을 가져올 때 사용
  • Event Listener에서의 콜백함수에는 arrow function 을 사용하지 않음.
<body>
    <div id="my"></div>
    <button id="this-button"> Click me </button>
    <script>
        /*
            Event Listener
            [무엇]을 [언제] [어떻게]한다.
            버튼을 클릭하면 메세지가 나온다.
        */
    
    	// 1. 무엇 -> 버튼
        const button = document.querySelector('#this-button')
        
        // 2. 언제 -> 버튼을 '클릭' 하면
        button.addEventListener('click', function(event){
            const area = document.querySelector('#my')
            area.innerHTML = '<h1>뿅!</h1>'
        })
    </script>
</body>

3) 이벤트의 유형

  • click – 마우스버튼을 클릭하고 버튼에서 손가락을 떼면 발생한다.
  • mouseover – 마우스를 HTML요소 위에 올리면 발생한다.
  • mouseout – 마우스가 HTML요소 밖으로 벗어날 때 발생한다.
  • mousemove – 마우스가 움직일때마다 발생한다. 마우스커서의 현재 위치를 계속 기록하는 것에 사용할 수 있다.
  • keypress – 키를 누르는 순간에 발생하고 키를 누르고 있는 동안 계속해서 발생한다.
  • keydown – 키를 누를 때 발생한다.
  • keyup – 키를 눌다가 떼는 순간에 발생한다.
  • load – 웹페이지에서 사용할 모든 파일의 다운로드가 완료되었을때 발생한다.
  • scroll – 스크롤바를 드래그하거나 키보드(up, down)를 사용하거나 마우스 휠을 사용해서 웹페이지를 스크롤 할 때 발생한다. 페이지에 스크롤바가 없다면 이벤트는 발생하지 않다.
  • change – 폼 필드의 상태가 변경되었을 때 발생한다. 라디오 버튼을 클릭하거나 셀렉트 박스에서 값을 선택하 는 경우를 예로 들수 있다.
  • input - input 또는 textarea 요소의 값이 변경되었을 때
  • submit - form을 submit 할 때

Axios를 이용한 요청 보내기(+ EventListener)

자바스크립트를 통해서 요청을 보내 보도록 하자. (강아지 사진을 random으로 가지고 오는 API활용)

API에 요청을 보낼 때, 순수한 자바스크립트로 요청을 보내는 코드는 매우 지저분함. (XHR 객체를 통함). 따라서 axios를 사용해보도록 하자. axios는 XHR(XMLHttpRequest)를 보내주고 그 결과를 promise 객체로 반환해주는 라이브러리이다.

1) 기본 코드 작성

  • 외부 라이브러리인 Axios를 사용하기 위해 <head> 태그에 CDN 삽입
  • EventListener를 이용하여, 마우스 클릭 시, 강아지 사진을 random으로 갖고 오게 코드를 작성
  • axios.get() 으로 요청을 보내면, .then 이하로 응답을 함.
  • 요청이 들어오면, .then 이하의 익명함수를 실행 시킴. 그렇지 않을 경우, .catch 이하의 익명함수를 실행.
<head>
	<!-- Axion CDN 추가 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <button id="dog">Dog!</button>
    <script>
        const button = document.querySelector('#dog')
        button.addEventListener('click', function(event){
            //API로 요청을 보냄
            axios.get('https://dog.ceo/api/breeds/image/random')
            	.then(function(response){
                	//handle sucess
                	console.log(response);
            })
            	.catch(function(error){
                	//handle error
                	console.log(error);
            })
        })
    </script>
</body>

※자바스크립트는 기본적으로 모든 코드가 비동기적으로 동작함. 시간이 오래걸리는 코드가 중간에 있다고 가정할경우(응답이 언제 올지 모르는 코드가 실행되었다 가정), 자바 스크립트는 그 코드가 끝나길 기다리지 않고 그다음 코드를 바로 실행시킴.

비동기적으로 작동하지 않으면(싱글 스레드이므로), 이 코드가 실행될 때 까지 브라우저는 동작하지 않고 멈추어버리기 때문임. 따라서 요청을 기다리지 않고, 브라우저가 작동할 수 있도록 비동기적으로 동작해야함.

따라서 비동기적으로 동작하는 것을 막기 위해 .then 으로 이어줌. 위의 코드는 가독성을 위해 줄바꿈을 하였지만 실제로 .then.catch 등은 앞의 코드와 이어진 총 한줄의 코드라고 할 수 있음.

2) console.log 확인

  • console.log(response) 의 결과 값을 개발자도구 console 창을 통해 확인되는 값은 아래와 같음 (오브젝트 객체로 반환됨을 알 수 있음)
  • 여기서 우리가 얻고자 하는 이미지는 data 내 message의 값으로 저장되어있음.
  • 따라서, response.data.message 로 접근하여 이미지를 가지고 올 수 있음.
{data: {}, status: 200, statusText: "", headers: {}, config: {}, }
config: {adapter: ƒ, transformRequest: {}, transformResponse: {}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", }
data: {status: "success", message: "https://images.dog.ceo/breeds/briard/n02105251_6387.jpg"}
headers: {content-type: "application/json", cache-control: "no-cache, private"}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, }
status: 200
statusText: ""
__proto__: Object

3) 코드 확장

.then 은 앞의 코드가 끝나면 뒤의 코드가 실행되게 하는 메소드임. 앞서 설명한 것 처럼, 비동기 처리를 막기 위해 사용하는 메소드인데, .then 이하의 코드가 끝난 후, 다른 코드가 실행 시키고 싶을 경우, .then 을 이어서 또 사용할 수 있음. 이 때, 앞의 익명함수의 return 값이 함수의 인자로 들어감.

.then을 여러번 사용하는 경우는 앞의 함수와 뒤의 함수가 처리하는 것이 다를 경우를 의도적으로 구분하기 위해서 사용하는 것이 일반적임.

한단계 더 나아가, .then 은 모든 함수의 메서드로 쓸 수 있는 것은 아님. .then 사용 유무는 get의 return값이 무엇이냐에 따라 결정됨. axios.get의 return 값은 Promise 객체임. 이는 응답을 보내주겠다는 약속을 하는 것! 수행되는 시간이 보장되지 못할 때 사용되며, .then 메소드는 Promise 객체의 메소드임. (.catch 도 동일함)

  • response.data.message 를 통해 이미지 주소를 갖고 올 수 있음.
  • 두번째 콜백함수의 인자인 url은 바로 위 함수의 return 값인 response.data.message를 갖고 있음.
  • document.createElement() : 새로운 태그를 만듦. 인자에 img를 넣어 img 태그르 만든 후 새롭게 선언한 변수에 저장함.
  • imgTag.src = url; : img 태그에 경로를 설정하는 속성인 src에 들어오는 네임인자인 url을 저장시킴.
  • document.querySelector('body').append(imgTag) : 이후, body 태그에 imgTag 값을 append함.
<head>
	<!-- Axion CDN 추가 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <button id="dog">Dog!</button>
    <script>
        const button = document.querySelector('#dog')
        button.addEventListener('click', function(event){
            //API로 요청을 보냄
            axios.get('https://dog.ceo/api/breeds/image/random')
            	.then(function(response){
                	//handle sucess
                	console.log(response);
                	return response.data.message
            })
            	.then(function(url){
                	const imgTag = document.createElement('img')
                    imgTag.src = url;
                	document.querySelector('body').append(imgTag)
            })
            	.catch(function(error){
                	//handle error
                	console.log(error);
            })
        })
    </script>
</body>

JavaScript 03 - 배열 조작 메서드(forEach, map, filter, find, every, some, reduce)

|

JavaScript 기본 문법 (3)

7. Array Helper Methods - 배열 조작 메서드

1) forEach

  • ES5 표준화 버전에서는 반복문을 돌릴때 var 메서드를 사용했었음
var colors = ['red', 'blue', 'green']
for (var i=0; i<colors.length, i++){
    console.log(colors[i]);
}
  • ES6+ 에는 forEach 메서드 사용
    • 가장 기본적인 형태의 반복문으로, 단순 반복을하고 return 값을 따로 가지지 않음.
    • 함수 자체가 파라미터로 넘어가서 동작하는 것을 callback 함수라고 하며 JS에서 가장 대표적으로 사용하는 방법임. callback 함수로서 동작하게 코드를 아래와 같이 작성 할 수 도 있다.
const colors = ['red', 'blue', 'green']

// 기본적인 방법
const f = function(color){
    console.log(color)
}
colors.forEach(f)

// callback 함수로서 동작
colors.forEach(function(color){
    console.log(color)
})

2) map

  • ES5
var numbers = [1,2,3]
var doubleNumbers = []

for (var i=0; i<numbers.length; i++){
    doubleNumber.push(numbers[i]*2)
}

console.log(doubleNumbers)
  • ES6+ 에는 map 메서드 사용
    • return 값을 가지며 새로운 배열을 만듦.
const numbers2 = [1,2,3]
const doubleNumbers2 = numbers2.map(function(number2){
    return number2 * 2
})
console.log(doubleNumber2)

3) filter

  • 원하는 값들롤만 새로운 배열을 만듦.
  • 해당 조건이 true인 값에 대해서만 가져와 배열에 넣음
const products = [
    {name: 'cucumber', type:'vege'},
    {name: 'banana', type: 'fruit'},
    {name: 'onion', type:'vege'},
    {name: 'apple', type: 'fruit'},
]

const fruitproducts = products.filter(function(product){
    return product.type == 'fruit'
})

console.log(fruitproducts)

4) find

  • 단 하나의 결과만을 출력함.
  • 조건을 만족하는 값을 찾을 경우, 반복문을 바로 종료시킴.
const users = [
    {name: 'HARRY'},
    {name: 'ADMIN'},
    {name: 'MANSU'},
]

const foundUser = users.find(function(user){
    return user.name == 'ADMIN'
})

console.log(foundUser)

5) every & some

  • every: 배열안의 모든 데이터가 조건을 만족하는 경우 true를 반환함. else, false를 반환.
  • some: 배열안의 데이터 중 하나라도 조건을 만족하는 경우 true를 반환함. else, false를 반환.
const computers = [
    {name: 'macbook', ram: 16},
    {name: 'gram', ram: 8},
    {name: 'series9', ram:32},
]

const everyComputersAavailable = computers.every(function(computer){
    return computer.ram > 16
})

const someComputersAvailable = computers.some(function(computer){
    return computer.ram > 16
})

console.log(everyComputerAvailable)
console.log(someComputerAvailable)

7-1. reduce

배열의 각 요소에 대해 주어진 reduce 함수를 실행시키고 하나의 결과 값을 반환함.

1) 구문

arr.reduce(callback[, initalValue])

2) 매개변수

  • callback
    • accumulator: 콜백의 반환값을 누적. 콜백의 이전 반환 값 또는 콜백의 첫번째 호출이면서 initialValue를 제공한 경우 initalValue의 값임.
    • currentValue: 배열 내 현재 처리되고 있는 요소
    • currentIndex(optional): 배열 내 현재 처리되고 있는 요소의 인덱스. initalValue를 제공한 경우 0, 아니면 1부터 시작함.
    • array(optional): reduce()를 호출한 배열
  • initialValue(optional): callback의 첫호출 때 첫번째 인수에 제공하는 값. 초기값을 제공하지 않을 경우, 배열의 첫번째 요소를 사용함

reduce의 return 값에는 배열이 들어 갈 수도 있으며, 특정한 값도 들어갈 수 있음.

3) reduce() 작동방식

  • 1부터 5까지 있는 배열 내 숫자들의 합 계산하기
var arr = [1,2,3,4,5]
var cnt = 0
var sum = arr.reduce(function(acc, value){
    cnt ++ 
    return acc + value
});

console.log(cnt) // 4
console.log(sum) // 15

콜백은 4번 호출되며, 각 호출의 인수와 반환값은 다음과 같음.

  • initialValue 의 값을 따로 지정하지 않았기 때문에, 배열의 첫번째 요소(1)이 initialValue 으로 사용되었으며, 콜백의 누적값인 accumulator에 저장되어있음.
  • 배열의 첫번째 요소(1)을 사용하였으므로 순서대로 currentValue 에는 2가 들어가고 첫번째 반환값은 3이된다. 같은 방식으로 콜백은 총 4번 호출되며 각 호출의 인수와 반환값은 아래와 같이 정리할 수 있음.
  • cnt 를 통해 몇번 콜백이 되었는지 쉽게 알 수있음.
call back acc (accumulator) value(currentValue) 반환값(return)
1st 호출 1 2 3
2nd 호출 3 3 6
3rd 호출 6 4 10
4nd 호출 10 5 15
  • initalValue 값을 5로 지정했을 경우, 값이 아래와 같이 달라짐을 알 수 있다.
  • 5라는 초기값이 새로 주어졌으므로 callback은 5번 호출되며, 최종 합은 20으로 변한다.
var arr = [1,2,3,4,5]
var cnt = 0
var sum = arr.reduce(function(acc, value){
    cnt ++ 
    return acc + value
}, 5);

console.log(cnt) // 5
console.log(sum) // 20

4) reduce vs map

  • reduce
    • initalValue 으로 빈 배열을 우선 만들었음. 이에 따라, value 에는 순차적으로 1,2,3이 들어가고 콜백이 총 3번됨.
    • 사이사이에 삽입한 console.log를 면밀히 살펴 보면 어떠한 프로세스로 진행되는지를 알 수 있음.
var num = [1,2,3]
var count = 0
var doubleNum = num.reduce(function(acc, value){
    console.log(acc, value)
    acc.push(value*2)
    console.log(acc)
    count ++
    return acc
}, []);

console.log(count) // 3
console.log(doubleNum) // [ 2, 4, 6 ]
call back acc (accumulator) value(currentValue) acc.push(value*2) 반환값(return)
1st 호출 [] 1 [2] 2
2nd 호출 [2] 2 [2,4] 4
3rd 호출 [2,4] 3 [2,4,6] 6

5) reduce vs filter

  • reducer
    • map 때와 동일한 방식으로, initalValue 으로 빈 배열을 우선 만듦. 그 후, 분기문에 따라, type== 'vegetable' 을 만족하는 값에 대해서만 배열에 추가한 후, 배열을 return 함.
const products = [
    {name: 'banana', type:'fruit'},
    {name: 'onion', type:'vegetable'},
    {name: 'apple', type: 'fruit'},
    {name: 'lettuce', type: 'vegetable'},
]

var vegetableProducts = products.reduce(function(acc, value){
    if (value.type == 'vegetable') {
        acc.push(value)
    }
    return acc
}, [])
console.log(vegetableProducts)
  • filter
const products2 = [
    {name: 'banana', type:'fruit'},
    {name: 'onion', type:'vegetable'},
    {name: 'apple', type: 'fruit'},
    {name: 'lettuce', type: 'vegetable'},
]

const vegetableProducts2 = products2.filter(function(product){
    return product.type == 'vegetable'
})

console.log(vegetableProducts2)

6) reduce vs find

  • reduce
    • find의 속성은 원하는 값을 찾을 경우 반복문이 바로 끝나야함. 이 조건을 reduce 에서 걸어주기 위해 type of acc == 'undefined' 인 조건을 추가로 삽입하였음.
const users = [
    {name:'HARRY'},
    {name:'ADMIN'},
    {name:'MANSU'}
]

const findUser = users.reduce(function(acc, value){
    if (typeof acc == 'undefined' && value.name == 'ADMIN'){
        acc = value;
    }
    return acc;
}, undefined)

console.log(findUser)
  • find
const users2 = [
    {name:'HARRY'},
    {name:'ADMIN'},
    {name:'MANSU'}
]

const findUser2 = users2.find(function(user){
    return user.name == 'ADMIN'
})

console.log(findUser2)