241127 뷰 (이벤트, 구글 차트, route, ajax)

241127 에이콘 아카데미 수업에 기반하여 작성되었음을 알립니다.

 

적어도 뷰에서 SPA를 운영할 것이라면 id를 적용한 태그 안(하나의 컴포넌트)에서만 코드를 짜주자!

과거, 현재의 데이터를 가지고 미래를 예측하자!

기술 통계를 내고 앞으로 일어날 일들에 대해 추론까지 이어가보자!

프로젝트 배포시 서버의 부하를 감당, 서버의 퍼포먼스 향상을 위한 모니터링을 진행하자!
(라이브러리를 잘 찾아서 적용!)

뷰 실습 (이벤트)

ex11event.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
    <div id="app">
        <h1>친구 목록</h1>
        <div>
            <input type="text" v-model="newName" @keyup.enter="addName" placeholder="이름 입력"/>
        </div>
        <ul>
            <li v-for="(name, index) in names" :key="index">
                {{name}}
                <button @click="removeName(index)">삭제</button>
            </li>
        </ul>
    </div>

    <script>
        const {createApp, ref} = Vue;
        createApp({
            setup(){
                const newName = ref(""); // 새로 입력할 친구 이름
                const names = ref(["홍희철","임형근"]);

                const addName = () => { // 메소드에서 선언해도 괜찮다.
                    if(newName.value.trim() !== "") {
                        names.value.push(newName.value.trim());
                        newName.value = ""; // 입력 필드 초기화
                    }
                };

                const removeName = (index) => { // 메소드에서 선언해도 괜찮다.
                    names.value.splice(index, 1); // 특정 index 1개를 names 배열에서 제거
                }
                return {newName, names, addName, removeName}; // 리턴해주면 바깥에서 사용 가능하다.
            }
        }).mount("#app");
    </script>
</body>
</html>

조회

추가

삭제


뷰 실습 (구글 차트)

JS 시각화 라이브러리

구글차트

구글 차트 사용 : https://developers-dot-devsite-v2-prod.appspot.com/chart
<script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.prod.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

심심한 웹사이트에 차트들을 추가해주자~

ex12chart.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.prod.js"></script>
    <script type="text/javascript"src="https://www.gstatic.com/charts/loader.js"></script>
    <script>
        let datas = [
            ["빅맥" , 1],
            ["트리플치즈버거" , 3],
            ["스파이시상하이" , 5],
            ["쿼터파운드치즈" , 7],
            ["더블쿼터파운드치즈" , 9],
        ]
    </script>
</head>
  <body>
    <h2>구글 차트 그리기</h2>
    먹고 싶은 햄버거 목록<br/>
    <div id="chart_div" style="height: 500px;"></div>

    <div id="app">
        <ul>
            <li></li>
        </ul>
    </div>

    <script>
        const {createApp} = Vue;
        
        createApp({
            data() {
                return {
                    dataArray:datas
                };
            },
            methods:{
            }
        }).mount("#app");

        google.charts.load("current", {packages:["corechart"]}); // googlecharts 라이브러리 로딩
        google.charts.setOnLoadCallback(drawFunc); // 콜백함수

        function drawFunc() {
            let data = google.visualization.arrayToDataTable([
                ["종류" , "갯수"], ...datas
            ]);
            let options = {title:'먹고 싶은 햄버거', is3D:true};
            let chart = new google.visualization.PieChart(document.querySelector("#chart_div")) // 데이터 양이 적을 때 PieChart() 사용, 데이터 양이 많아지면 보기 불편함
            chart.draw(data, options); // 차트 그리기
        }
    </script>
  </body>
</html>

차트 출력

출력, 증감 이벤트

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3.2.31/dist/vue.global.prod.js"></script>
    <script type="text/javascript"src="https://www.gstatic.com/charts/loader.js"></script>
    <script>
        let datas = [
            ["빅맥" , 1],
            ["트리플치즈버거" , 3],
            ["스파이시상하이" , 5],
            ["쿼터파운드치즈" , 7],
            ["더블쿼터파운드치즈" , 9],
        ]
    </script>
</head>
  <body>
    <h2>구글 차트 그리기</h2>
    먹고 싶은 햄버거 목록<br/>
    <div id="chart_div" style="height: 500px;"></div>

    <div id="app">
        <ul>
            <li v-for="(value, index) in dataArray" :key="index">
                {{value[0]}} : {{value[1]}}개
                <button @click="addFunc(index)">1 증가</button>&nbsp;
                <button @click="subFunc(index)">1 감소</button>&nbsp;
            </li>
        </ul>
    </div>

    <script>
        const {createApp} = Vue;
        
        createApp({
            data() {
                return {
                    dataArray:datas
                };
            },
            methods:{
                addFunc(index){
                    this.dataArray[index][1]++;
                    this.updateChart();
                },
                subFunc(index){
                    if(this.dataArray[index][1] > 0) {
                        this.dataArray[index][1]--;
                        this.updateChart();
                    }
                },
                updateChart(){
                    drawFunc();
                }
            }
        }).mount("#app");

        google.charts.load("current", {packages:["corechart"]}); // googlecharts 라이브러리 로딩
        google.charts.setOnLoadCallback(drawFunc); // 콜백함수

        function drawFunc() {
            let data = google.visualization.arrayToDataTable([
                ["종류" , "갯수"], ...datas
            ]);
            let options = {title:'먹고 싶은 햄버거', is3D:true};
            let chart = new google.visualization.PieChart(document.querySelector("#chart_div")) // 데이터 양이 적을 때 PieChart() 사용, 데이터 양이 많아지면 보기 불편함
            chart.draw(data, options); // 차트 그리기
        }
    </script>
  </body>
</html>


뷰 실습 (라우트)

cdn

라우팅을 위한 cdn 추가
<script src="https://unpkg.com/vue@3"></script>   
<script src="https://unpkg.com/vue-router@4"></script>

ex13route.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://unpkg.com/vue-router@4"></script>
</head>
<body>
    <div id="app">
        <router-link to="/">메인</router-link> | 
        <router-link to="/mem">메뉴 1</router-link> | 
        <router-link to="/product">메뉴 2</router-link> | 
        
        <!-- 현재 라우팅에 대한 렌더링 영역 -->
        <router-view style="color: red;"></router-view>
    </div>

    <script>
        // 각 컴포넌트는 vue의 템플릿을 사용하여 HTML 구조를 정의
        // 메인 컴포넌트
        const Home = {
            template:
                `<div>
                    <h1>홈페이지</h1>
                    <p>페이지 방문을 환영</p>                    
                </div>`
            
        };

        // 메뉴 1 컴포넌트
        const Member = {
            template:
                `<div>
                    <h1>회원관리</h1>
                    <p>회원님 사랑해</p>                    
                </div>`,
        };

        // 메뉴 2 컴포넌트
        const Product = {
            template:
                `<div>
                    <h1>상품관리</h1>
                    <p>상품님 사랑해</p>
                </div>`,
        };

        // 라우터 설정 : router-link to 경로와 매핑되는 component를 정의
        const routes = [
            {path:"/", component:Home},
            {path:"/mem", component:Member},
            {path:"/product", component:Product},
        ];

        // 라우터 생성
        const router = VueRouter.createRouter({
            // 해시 기반 라우팅 방식 사용
            history: VueRouter.createWebHashHistory(),
            routes,
        });

        const app = Vue.createApp({});
        app.use(router); // 뷰 어플리케이션에 라우터를 등록
        app.mount("#app");
    </script>
</body>
</html>

별도 js 파일

메뉴 2 컴포넌트를 따로 만들어보자

ex13ProductComp.js

export default {
  template:
    `<div>
    <h1>상품관리</h1>
    <p>상품님 사랑해</p>
    </div>`,
};
💡HTML 파일에서 import를 사용하려면 <script> 태그에 type="module"을 꼭 추가해주자!
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://unpkg.com/vue-router@4"></script>
</head>
<body>
   	... 생략

    <script type="module">
        ... 생략
        // 메뉴 2 컴포넌트
        // const Product = {
        //     template:
        //         `<div>
        //             <h1>상품관리</h1>
        //             <p>상품님 사랑해</p>
        //         </div>`,
        // };

        // 컴포넌트 별도 파일로 작성 후 호출
        import Product from "./ex13ProductComp.js";
	... 생략
    </script>
</body>
</html>

작동 확인

구구단 예제

ex13GuguComp.js

export default {
  data() {
    return {
      inputNumber: "",
      result: "", // 출력결과 코드
    };
  },
  methods: {
    calcGugudan() {
        const num = parseInt(this.inputNumber);
        if(!isNaN(num) && num > 1) { // 1을 초과한 양수만 받는 검증
            this.result = Array.from({length:9}, (_, i) => // '_'는 배열의 요소를 의미, 사용안할거라 '_'로 사용
                    `${num} x ${i + 1} = ${num * (i + 1)}`).join('<br/>');
                    // 3 x 1 = 3<br/>... 
        } else {
            this.result = "올바른 숫자를 입력해라"
        }
    },
  },
  template:
    `<div>
        <h2>구구단</h2>
        단 입력 후 확인 버튼 클릭<br/>
        <input v-model="inputNumber" type="number" min="2" placeholder="단 입력" />
        <button @click="calcGugudan">확인</button>
        <div v-html="result" style="margin-top:20px; color:blue;"></div>
    </div>`,
};
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://unpkg.com/vue-router@4"></script>
</head>
<body>
	... 생략
    <script type="module">
	... 생략
        // 컴포넌트 별도 파일로 작성 후 호출
        import Product from "./ex13ProductComp.js";
        import Gugudan from "./ex13GuguComp.js";

	... 생략
    </script>
</body>
</html>


뷰 실습 (ajax)

https://jsonplaceholder.typicode.com/posts // json 연습용, cors 풀려있다. 읽어와보자

fetch

ex14ajax.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app">
    <button @click="fetchData">Ajax 실행</button>
    <button @click="clearData">결과 삭제</button>

    <div v-for="post in posts" :key="post.id">
        <h3>{{post.title}}</h3>
        <p>userId : {{post.userId}}</p>
        <p>body : {{post.body}}</p>
    </div>
</div> 

<script>
    const {createApp} = Vue;
    
    createApp({
        data() {
            return {
                posts:[]          
            };
        },
        methods:{
            fetchData(){
                fetch("https://jsonplaceholder.typicode.com/posts")
                .then(response => {
                    if(!response.ok){
                        throw new Error("네트워크 오류");
                    }
                    return response.json();
                })
                .then(json => {
                    this.posts = json.slice(0, 10);
                })
                .catch(error => {
                    console.log("읽기 오류 : " + error);
                });
            },
            clearData(){
                this.posts = [];
            },
        }
    }).mount("#app");
</script>
</body>
</html>

조회

삭제

'Study > Acorn' 카테고리의 다른 글

241129 뷰(RESTful)  (1) 2024.11.29
241128 뷰 (ajax, ajax-router)  (1) 2024.11.28
241126 뷰 (이벤트)  (1) 2024.11.26
241125 뷰 (CDN, 라이프사이클, 컴포넌트, defer, 디렉티브)  (1) 2024.11.25
241122 리액트 (총정리 문제)  (0) 2024.11.22