mjeongriver

- vue_training/ex02/src/views/HomeView.vue

<template>
  <div class="home">

    <div>
      <p>인사: {{ message }}</p>
    </div>

    <div>
      <input type="text" v-model="input1"> <!-- v-model이 input1 변수 상자를 감지하게 됨-->
      <input type="text" v-model="input2">
      <button @click="show()">확인</button>
    </div>

    <div>
      <p>{{ output }}</p>
    </div>

    <div>
      <h3 v-if="success">성공</h3>
      <h3 v-else>실패</h3>
      <button @click="changeSuccess">바꾸기</button>
    </div>

    <ul>
      <li>{{ dogs[0].name }}</li>
      <li>{{ dogs[1].name }}</li>
      <li>{{ dogs[2].name }}</li>
    </ul>

    <ul>
      <li v-for="dog in dogs" :key="dog.id">
        #{{ dog.id }} : {{ dog.name }}
      </li>
    </ul>

    <ul>
      <li v-for="(dog, index) in dogs" :key="index">
        #{{ index + 1 }} : {{ dog.name }}
      </li>
    </ul>

    <div>
      <button @click="goToAbout()">정보 화면으로</button>
    </div>

    <div>
      <button @click="goToProfile()">프로필 화면으로</button>
    </div>

    <div>
      <button @click="requestPersonList()">리스트 요청하기</button>
    </div>

    <ul>
      <li v-for="(person, index) in persons" :key="index">
        #{{ person.id }} : {{ person.name }}, {{ person.age }}, {{ person.mobile }}
      </li>
    </ul>

    <div>
      <p class="group1">사용자 이름 : 홍길동1</p>
      <p :class="groupClass">사용자 이름 : 홍길동2</p> <!-- 변수상자라고 생각하고 밑에 data에 추가하면 group1의 스타일을 같이 적용할 수 있다. -->

      <button @click="chagneClass()">클래스 지정하기</button>
    </div>

  </div>
</template>

<style>
.group0 {
  color: blue;
}

.group1 {
  color: red;
}
</style>

<script>


export default {
  name: 'HomeView',
  data() {
    return {
      message: '안녕!',
      input1: '',
      input2: '',
      output: '',
      success: true,
      dogs: [
        {
          id: 1,
          name: '강아지1',
        },
        {
          id: 2,
          name: '강아지2',
        },
        {
          id: 3,
          name: '강아지3',
        },
      ],
      persons: [],
      groupClass: 'group0'
    }
  },
  mounted() { // 이 화면이 로딩될 때 자동으로 호출됨(onload 하고 같은 기능)
    console.log(`홈 화면의 mounted 호출 됨`);
    this.message = 'Hello!';
  },
  methods: { //함수 정의하는 곳(객체로 인식해서 콤마 붙여줄 것)

    async requestPersonList() {
      console.log(`requestPersonList 호출됨.`);

      try {
        const response = await this.axios({
          method: 'post',
          url: 'http://127.0.0.1:7001/list',
          data: {},
        })

        console.log(`응답 -> ${JSON.stringify(response.data)}`);
        this.persons = response.data;

      } catch (err) {
        console.error(`에러 -> ${err}`);
      }
    },

    show() {
      console.log(`show 함수 호출 됨.`);
      this.message = '안녕하세요!';
      this.output = `${this.input1} ${this.input2}`;
    },

    changeSuccess() {
      this.success = !this.success;
    },

    goToAbout() {
      this.$router.push({
        path: '/about',
      })
    },

    goToProfile() {
      this.$router.push({
        path: '/profile',
      })
    },

    chagneClass() {
      this.groupClass = 'group1';
    }

  }
}
</script>

 

 

https://getbootstrap.com/docs/5.3/examples/

 

Examples

Quickly get a project started with any of our examples ranging from using parts of the framework to custom components and layouts.

getbootstrap.com

 

* ex03 프로젝트 생성 후 부트스트랩 적용

- vue_training/ex02/public/index.html

<!DOCTYPE html>
<html lang="">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <title>
    <%= htmlWebpackPlugin.options.title %>
  </title>

  <!-- MDB CSS start-->
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
  <link href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/7.2.0/mdb.min.css" rel="stylesheet" />
  <!-- MDB CSS end-->

</head>

<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
        Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>

  <!-- MDB JS 추가 start -->
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/7.2.0/mdb.umd.min.js"></script>
  <!-- MDB JS 추가 start -->

</body>

</html>

 

- vue_training/ex03/src/views/HomeView_old.vue

<template>
  <div class="home">

    <div>
      <p>홈 화면입니다.</p>

      <button type="button" class="btn btn-primary" data-mdb-ripple-init>확인</button>
    </div>

    <div>
      <input type="text" v-model="input1">
      <input type="text" v-model="input2">
    </div>

    <div class="row">
      <div class="col-3">

      </div>

      <div class="col-6">
        <div class="card text-center">
          <div class="card-header">카드 헤더</div>
          <div class="card-body">
            <h5 class="card-title">카드 본문</h5>
            <p class="card-text">본문 내용</p>

            <div class="form-outline" data-mdb-input-init>
              <input type="text" id="formControlSm" class="form-control form-control-sm" />
              <label class="form-label" for="formControlSm">사용자 이름</label>
            </div>

            <button type="button" class="btn btn-primary" data-mdb-ripple-init>확인</button>

            <button style="margin-left: 1em;" type="button" class="btn btn-primary" data-mdb-ripple-init data-mdb-modal-init data-mdb-target="#exampleModal">대화상자 띄우기</button>
          </div>
          <div class="card-footer text-muted">카드 푸터</div>
        </div>
      </div>

      <div class="col-3">

      </div>

    </div>

  </div>

  <!-- 대화 상자 start-->
  <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
          <button type="button" class="btn-close" data-mdb-ripple-init data-mdb-dismiss="modal"
            aria-label="Close"></button>
        </div>
        <div class="modal-body">...</div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-mdb-ripple-init data-mdb-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary" data-mdb-ripple-init>Save changes</button>
        </div>
      </div>
    </div>
  </div>
  <!-- 대화 상자 end -->

</template>

<style scoped></style>

<script>

export default {
  name: 'HomeView',
}
</script>

 

- vue_training/ex03/src/views/HomeView.vue

<template>
  <div class="container">

    <div class="card mt-4">

      <div class="card-header d-flex align-items-center justify-content-center">
        <p>고객 리스트</p>
      </div>

      <div class="card-body">
        <div class="row">
          <div class="col-md-1">

          </div>
          <div class="col-md-3">
            <span>이름</span>
          </div>
          <div class="col-md-3">
            <span>전화번호</span>
          </div>
          <div class="col-md-5">
            <span>기능</span>
          </div>

        </div>

        <ul class="list-group">

          <li v-for="(person, index) in persons" :key="index" class="list-group-item">

            <div class="row">
              <div class="col-md-1 d-flex align-items-center justify-content-center">
                <img src="images/person.png" style="width:2em;">
              </div>
              <div class="col-md-3 d-flex align-items-center justify-content-center">
                <span>{{ person.name }}</span>
              </div>
              <div class="col-md-3 d-flex align-items-center justify-content-center">
                <span>{{ person.mobile }}</span>
              </div>
              <div class="col-md-5">
                <button class="btn btn-primary btn-sm">수정</button>
                <button class="btn btn-danger btn-sm ms-1">삭제</button>
              </div>
            </div>

          </li>
        </ul>

      </div>

      <div class="card-footer">
        <div class="row d-flex justify-content-end">
          <div class="col-md-3">
            <button @click="goToAdd()" class="btn btn-primary btn-sm">추가</button>
          </div>
        </div>
      </div>

    </div>
  </div>
</template>

<style scoped></style>

<script>

export default {
  name: 'HomeView',
  data() {
    return {
      persons: []
    }
  },
  mounted() {
    console.log(`mounted 호출됨`);

    //고객 리스트 요청하기 
    this.requestPersonList();
  },
  methods: {

    goToAdd() {
      this.$router.replace({
        path: '/add'
      })
    },

    async requestPersonList() {
      console.log(`requestPersonList 호출됨.`);

      try {
        const response = await this.axios({
          method: 'post',
          url: 'http://127.0.0.1:7001/list',
          data: {},
        })

        console.log(`응답 -> ${JSON.stringify(response.data)}`);
        this.persons = response.data;

      } catch (err) {
        console.error(`에러 -> ${err}`);
      }
    },

  }
}
</script>

 

https://www.iconfinder.com/search?q=person

 

Icons - Iconfinder

Download 7,957,005 icons. Available in PNG and SVG formats. Ready to be used in web design, mobile apps and presentations.

www.iconfinder.com

 

- vue_training/ex03/main.js

* 터미널에서 다운로드

- yarn.cmd add axios
- yarn.cmd add vue-axios

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// axios 설정
import axios from 'axios'
import VueAxios from 'vue-axios'

createApp(App).use(store).use(router).use(VueAxios, axios).mount('#app')

 

- vue_training/ex03/src/views/addView.vue

<template>
  <div>

    <div class="card mt-4">
      <div class="card-header">
        <h3>고객 정보 추가</h3>
      </div>

      <div class="card-body">

        <!-- 이름 입력 상자 start-->
        <div class="row mt-2">
          <input v-model="nameInput" type="text" class="form-control" placeholder="이름" aria-lable="name">
        </div>

        <!-- 이름 입력 상자 end -->

        <!-- 전화번호 입력상자 start-->
        <div class="row mt-2">
          <input v-model="mobileInput" type="text" class="form-control" placeholder="전화번호" aria-label="mobile">
        </div>

        <!-- 전화번호 입력상자 end-->
      </div>
      <div class="card-footer">
        <button @click="saveItem()" class="btn btn-primary btn-sm">저장</button>
        <button class="btn btn-primary btn-sm ms-4">닫기</button>

      </div>

    </div>

  </div>
</template>

<script>
export default {
  name: 'AddView',
  data() {
    return {
      nameInput: '',
      mobileInput: '',
    }
  },
  methods: {

    saveItem() {
      console.log(`saveItem 호출됨`)
      this.requestPersonAdd();
    },

    async requestPersonAdd() {
      console.log(`requestPersonAdd 호출됨.`);

      try {
        const response = await this.axios({
          method: 'post',
          url: 'http://127.0.0.1:7001/add',
          data: {
            name: this.nameInput,
            age: '0',
            mobile: this.mobileInput,
          },
        })

        console.log(`응답 -> ${JSON.stringify(response.data)}`);

        this.$router.replace({
          path: '/'
        })

      } catch (err) {
        console.error(`에러 -> ${err}`);
      }
    },
  }
}
</script>

 

* 공간정보 관련 데이터 가져오기

<sql>

CREATE TABLE `coffeeshop` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL,
`tel` VARCHAR(32) NOT NULL,
`location` GEOMETRY NOT NULL,
PRIMARY KEY (`id`),
SPATIAL INDEX `location` (`location`)
)

 

INSERT INTO test.coffeeshop(NAME, tel, location)
VALUES
('스타벅스 서울세관 사거리점', 
'1522-3232',
ST_GEOMFROMTEXT('POINT(127.0357329 37.5148388)') -> googlemap url

 

* SELECT id, NAME, tel, 
ST_X(location) AS longitude,
ST_Y(location) AS latitude
FROM test.coffeeshop
WHERE NAME like '스타벅스%'

 

* SELECT id, NAME, tel, 
ST_X(location) AS longitude,
ST_Y(location) AS latitude,
ST_DISTANCE(ST_GEOMFROMTEXT('point(127.0357329 37.51555)'), location) * 111195 AS distance
FROM test.coffeeshop
WHERE  
ST_DISTANCE(ST_GEOMFROMTEXT('point(127.0357329 37.51555)'), location) * 111195 < 1000
ORDER BY 
 DISTANCE

 

node_training2에서 controller, sql, server01_03.js에 코드 추가

export const coffeeshopListController = async (req, res) => {
    console.log(`/coffeeshop_list 요청 경로로 요청됨`);

    const params = req.query;
    console.log(`point -> '${params.point}'`)

    try {
        const rows = await database.execute(sql.coffeeshopList, [`${params.point}`, `${params.point}`]);
        sendResponse(res, JSON.stringify(rows));
    } catch (err) {
        sendError(res, err);
    }
}

export const coffeeshopSearchController = async (req, res) => {
    console.log(`/coffeeshop_search 요청 경로로 요청됨`);

    const params = req.query;
    console.log(`point -> '${params.point}'`)
    console.log(`name -> '${params.name}'`)

    try {
        const rows = await database.execute(sql.coffeeshopSearch, [`${params.point}`, `${params.name}%`]);
        sendResponse(res, JSON.stringify(rows));
    } catch (err) {
        sendError(res, err);
    }
}

 

    coffeeshopList: `SELECT id, name, tel, 
	                    ST_X(location) AS longitude,
	                    ST_Y(location) AS latitude,
	                    ST_DISTANCE(ST_GEOMFROMTEXT(?), location) * 111195 AS distance
                    FROM test.coffeeshop
                    WHERE  
                        ST_DISTANCE(ST_GEOMFROMTEXT(?), location) * 111195 < 1000
                    ORDER BY 
                        DISTANCE`,
    coffeeshopSearch: `SELECT id, name, tel, 
                            ST_X(location) AS longitude,
                            ST_Y(location) AS latitude,
                            ST_DISTANCE(ST_GEOMFROMTEXT(?), location) * 111195 AS distance
                        FROM test.coffeeshop
                        WHERE NAME like ?`

 

router.route('/coffeeshop_list').get(coffeeshopListController);
router.route('/coffeeshop_search').get(coffeeshopSearchController);

 

https://vue-map.netlify.app/

 

Vue 3 Google maps

 

vue-map.netlify.app

 

* 지도 넣어주기

terminal에서 yarn.cmd add vue3-google-map 다운로드

vue_training/ex03/views/HomeView.vue에서 지도 부분 추가, api-key 발급 받아서 넣어줄 것!

<template>
  <div class="container">

    <div class="card mt-4">

      <div class="card-header d-flex align-items-center justify-content-center">
        <p>고객 리스트</p>
      </div>

      <div class="card-body">
        <div class="row">
          <div class="col-md-1">

          </div>
          <div class="col-md-3">
            <span>이름</span>
          </div>
          <div class="col-md-3">
            <span>전화번호</span>
          </div>
          <div class="col-md-5">
            <span>기능</span>
          </div>

        </div>

        <ul class="list-group">

          <li v-for="(person, index) in persons" :key="index" class="list-group-item">

            <div class="row">
              <div class="col-md-1 d-flex align-items-center justify-content-center">
                <img src="images/person.png" style="width:2em;">
              </div>
              <div class="col-md-3 d-flex align-items-center justify-content-center">
                <span>{{ person.name }}</span>
              </div>
              <div class="col-md-3 d-flex align-items-center justify-content-center">
                <span>{{ person.mobile }}</span>
              </div>
              <div class="col-md-5">
                <button class="btn btn-primary btn-sm">수정</button>
                <button class="btn btn-danger btn-sm ms-1">삭제</button>
              </div>
            </div>

          </li>
        </ul>

      </div>

      <div class="card-footer">
        <div class="row d-flex justify-content-end">
          <div class="col-md-3">
            <button @click="goToAdd()" class="btn btn-primary btn-sm">추가</button>
          </div>
        </div>
      </div>

    </div>
    <!-- 지도 -->
    <GoogleMap style="width:100%; height: 300px" api-key="" :center="{ lat: 37.51555, lng: 127.034983 }" :zoom="{ zoom }">
      <!-- components로 넣고 태그로 사용 -->
      <Marker v-for="(coffeeshop, index) in coffeeshopList" :key="index" :options="{ position: {lat: coffeeshop.latitude, lng: coffeeshop.longitude } }"></Marker>
    </GoogleMap>

    <button @click="requestCoffeeshopList()">커피숍 조회</button>

  </div>

</template>

<style scoped></style>

<script>
import { GoogleMap, Marker } from 'vue3-google-map';

export default {
  name: 'HomeView',
  components: {
    GoogleMap,
    Marker
  },
  data() {
    return {
      zoom: 15,
      markerOptions: {
        position: {
          lat: 37.51555,
        lng: 127.034983,
        }
      },
      persons: [],
      coffeeshopList: [],
    }
  },
  mounted() {
    console.log(`mounted 호출됨`);

    //고객 리스트 요청하기 
    this.requestPersonList();
  },
  methods: {

    goToAdd() {
      this.$router.replace({
        path: '/add'
      })
    },

    async requestPersonList() {
      console.log(`requestPersonList 호출됨.`);

      try {
        const response = await this.axios({
          method: 'post',
          url: 'http://127.0.0.1:7001/list',
          data: {},
        })

        console.log(`응답 -> ${JSON.stringify(response.data)}`);
        this.persons = response.data;

      } catch (err) {
        console.error(`에러 -> ${err}`);
      }
    },

    async requestCoffeeshopList() {
      console.log(`requestCoffeeshopList 호출됨.`);

      try {
        const response = await this.axios({
          method: 'get',
          url: 'http://127.0.0.1:7001/coffeeshop_list?point=POINT(127.0357329 37.51555)',
        })

        console.log(`응답 -> ${JSON.stringify(response.data)}`);
        this.coffeeshopList = response.data;

      } catch (err) {
        console.error(`에러 -> ${err}`);
      }
    },
    

  }
}
</script>

 

profile

mjeongriver

@mjeongriver

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그