728x90

Vue.js에서 회원가입 폼을 만들어 실습하면서 form을 통해 데이터를 전송하는 법을 알아보자

 

<template>
    <form action="" @submit.prevent="submitForm">
        <div>
            <label for="userId">아이디</label>
            <input id="userId" type="text" v-model="username">
        </div>
        <div>
            <label for="password">패스워드</label>
            <input id="password" type="password" v-model="password"> 
        </div>
        <button type="submit">회원가입</button>
    </form>
</template>

form을 만들고 input:text, label로 아이디와 패스워드를 입력 받을 폼을 만든다. 

 

submit 버튼은 디폴트로 submit 이벤트가 발생하고 페이지가 새로고침되기 때문에 새로고침을 막아야한다. 

 

새로고침을 막기 위해서 @submit 이벤트 핸들러에 .prevent를 붙여 preventDefault를 한다. 

그리고 submit 이벤트가 발생하면 폼의 input을 전송할 함수인 submitForm을 등록한다. 

 

            <input id="userId" type="text" v-model="username">

 input 태그에 v-model이라는 속성이 붙어있다. 

v-model은 input 태그의 value를 바인딩 해주는 디렉티브이다. 

만약 v-model을 사용하지 않는다면

input 태그에 input 이벤트가 발생할 때 input 태그의 value값을 가져와 v-bind: 바인딩해주는 작업을 해야한다. 

<input
  :username="text"
  @input="event => text = event.target.value">

 

data(){
	return {
    	username: '',
        password: '',
    }
}

 

그리고 data에 username과 password를 정의해준다. 

 

다음으로 비밀번호를 표시할지 password 타입으로 **처리할지 선택할 수 있는 체크박스를 만들어보자 

 <input type="checkbox" id="show" v-model="checked" @change="showPassword"> 
 
 data(){
 	return {
    	username: '',
        password: '',
        checked: '',
    }
 }
 methods:{
            showPassword(){
                if(this.checked){
                    document.querySelector('#password').type='text';
                }else{
                    document.querySelector('#password').type = 'password';
                }
            }
        }

template의 password input 태그 아래 checkbox를 추가하고 

change이벤트 핸들러에 showPassword 함수를 등록한다. 

showPassword 함수는 v-model로 받은 checked의 값에 따라 #password 태그의 type을 text와 password로 바꿔준다. 

여기서 this.checked는 data의 checked를 의미한다. 

체크박스가 해제 되었을 땐 password 타입

checked 상태에선 text 타입인 것을 확인할 수 있다. 

 

다중 checkbox, radio, select 를 사용한 input 태그를 작성

<template>
    <form action="" @submit.prevent="submitForm">
        <div>
            <label for="userId">아이디</label>
            <input id="userId" type="text" v-model="username">
        </div>
        <div>
            <label for="password">패스워드</label>
            <input id="password" type="password" v-model="password">
            <input type="checkbox" id="show" v-model="checked" @change="showPassword"> 
        </div>
        <div>
            <h3>성별</h3>
            <input type="radio" name="gender" id="M" value="남자" v-model="picked">
            <label for="M">M</label>
            <input type="radio" name="gender" id="F" value="여자" v-model="picked">
            <label for="F">M</label>
        </div>
        <div>
            <h3>기술스택</h3>
            <input type="checkbox" id="springboot" value="springboot" v-model="checkedNames">
            <label for="springboot">SpringBoot</label>
            <input type="checkbox" id="jpa" value="jpa" v-model="checkedNames">
            <label for="jpa">JPA</label>
            <input type="checkbox" id="mybatis" value="mybatis" v-model="checkedNames">
            <label for="mybatis">MyBatis</label>
            <input type="checkbox" id="vue" value="vue" v-model="checkedNames">
            <label for="vue">Vue</label>
            <input type="checkbox" id="react" value="react" v-model="checkedNames">
            <label for="react">React</label>
        </div>
        <div>
            <h3>주소</h3>
            <select name="address" id="address" v-model="selected">
                <option disabled value="">선택</option>
                <option value="서초구">서초구</option>
                <option value="관악구">관악구</option>
            </select>
        </div><br>
        <button type="submit">회원가입</button>
    </form>
</template>

 

input data를 전송하기 위한 submitForm 함수 작성 

 submitForm(){
                axios.post('https://jsonplaceholder.typicode.com/users/',{
                    username:this.username,
                    password:this.password,
                    checked:this.checked,
                    checkedNames:this.checkedNames,
                    picked:this.picked,
                    selected:this.selected
                }).then(response=>{
                    console.log(response);
                });
            },

 

axios는 비동기 요청방식 api이다. 

데이터를 서버로 전송하고 응답을 받아 처리할 수 있다. 

 

axios를 사용하기 위해 axios를 다운로드

https://github.com/axios/axios

 

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

Promise based HTTP client for the browser and node.js - GitHub - axios/axios: Promise based HTTP client for the browser and node.js

github.com

 

사이트의 installing package manager에서 본인이 사용하는 패키지 매니저에 따라 명령어를 카피하고

프로젝트 디렉터리의 터미널에서 명령어를 실행한다. 

 

정상적으로 다운로드되면 package.json의 dependencies에 axios가 추가된다. 

import axios from 'axios'

App.vue에서 axios를 사용하기 위해 import 해준다. 

 

axios.post('url','data')으로 post 요청을 한다. 

 

이때 가상 서버 역할을 해줄 api가 필요하다면 

https://jsonplaceholder.typicode.com/users/를 이용할 수 있다. 

 

 data() {
            return {
                username: '',
                password: '',
                checked: '',
                checkedNames: [],
                picked: '',
                selected: ''
            }
        },
        
        
        
 axios.post('https://jsonplaceholder.typicode.com/users/',{
                    username:this.username,
                    password:this.password,
                    checked:this.checked,
                    checkedNames:this.checkedNames,
                    picked:this.picked,
                    selected:this.selected
                }).then(response=>{
                    console.log(response);
                });

 

 

data는 {객체형식으로 넘겨주면 된다.}

다중 checkbox는 배열로 넘겨주었다. 

input태그에 값을 입력하고 회원가입을 클릭하면 

users/ 요청이 날아가고 

payload를 클릭하면 요청할 때 보낸 data를 확인할 수 있다. 

 

.then은 서버로부터 응답을 받은 후의 로직을 작성할 수 있다. 

console.log(response) 에 의해 

response가 console창에 찍혔다. 

 

https://ko.vuejs.org/guide/essentials/forms.html

 

Form 입력 바인딩 | Vue.js

 

ko.vuejs.org

 

'Vue.js' 카테고리의 다른 글

Vue.js 프로젝트 생성하기  (0) 2023.11.29
Vue.js 기초 문법  (2) 2023.11.28
Vue.js Intro  (0) 2023.11.28
728x90

#Vue 인스턴스 생성

 

[Vue2]

//Vue2 application instance 생성방법
new Vue({
	//component options
    el: '#app', // mount할 element
    template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',, //화면에 표시할 요소
    data: function() {
    	return {
        	firstName: 'choi',
            lastName: 'coding',
            alias: 'choicoding'
        }
    },
    method: {
    	changeFirstName(first) {
        	this.firstName=first
        },
    }, //화면의 동작, 이벤트 handling할 메소드 
    created: function(){}, //life cycle hook (beforeCreate, beforeMount, mounted, ..)
    watch: {
    	firstName: function (val, oldVal) {
        	console.log(oldVal,'->',val);
        }
    }, //data의 변화를 감시하여 대응할 동작

});

new Vue({
    data: {
    },

}).$mount('#app');

 

[Vue3]

//Vue3 application instance 생성 방법
Vue.createApp({
	data() {
    	return {
           	a: 'adata'
        }
    }
}).mount('#app');

 

 

#Vue Directive[ v-on ]

 

이벤트함수 등록

 

v-on:이벤트="function" 

 

>> v-on:은 @으로 대체될 수 있다. 

ex) @click="function"

<div id="app">
    <button v-on:click="minus">-</button>
    <p>{{count}}</p>
    <button v-on:click="plus">+</button>
</div>
<script>
    Vue.createApp({
        data(){
            return{
                count: 0
            }
        },
        methods: {
            plus(){
                this.count++;
            },
            minus(){
                this.count--;
            }
        }
    }).mount('#app');
</script>

 

#Vue Directive[ v-for ]

 

리스트 렌더링

 

v-for="alias in list

 

<div id="app">
    <ul>
        <li v-for="item in items">{{item}}</li>
    </ul>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data(){
            return{
                items: ['1번','2번','3번','4번']
            }
        }
    }).mount('#app');
</script>

실행결과

 

**shortcut

ul>li +tap : ul와 하위 li 태그 동시 생성

 

 

#Vue Directive[ v-if / v-else-if / v-else ]

 

조건부 렌더링

 

v-if="조건

<body>
    <div id="app">
        <p v-if="a">a가 존재하면 렌더링될 태그</p>
        <p v-else-if="a==10">a=10이면 렌더링될 태그</p>
        <p v-else>다 아니면 렌더링될 태그</p>
    </div>
</body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data(){
            return{
                a:1
            }
        }
    }).mount('#app');
</script>

 

v-show="조건"

 

 <p v-show="a">a가 존재하면 렌더링될 태그</p>

 

v-if 와 v-show의 차이점

 

v-if는 조건에 따라서 선택적으로 렌더링되고 

v-show는 일단 렌더링되고 조건이 false이면 css로 display:none 이 된다. 

 

v-if는 전환비용이 높기 때문에 만약 사용자가 v-if의 조건을 자주 바꾼다면 

v-if가 전환될 때마다 재렌더링을 해야한다. 

반면, v-show는 최초 렌더링 할때 같이 렌더링해두고 css만 전환하면 되기 전환비용이 낮다. 

대신 최초 렌더링할때 비용이 더 높기 때문에 

전환이 자주 발생한다면 v-show를, 전환비용보다 최초 렌더링시 비용이 더 크다면 v-show를 선택하는 것이 좋다. 

 

#v-if 와 v-for 함께 사용하기 

v-if와 v-for를 함께 사용하는 것은 권장되지 않는다. 

//잘못된 코드
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
  >
    {{ user }}
  </li>
</ul>

위 코드의 목표는 v-for로 users를 순회하면서 user의 isActive로 조건을 걸어 리스팅하는것이다.

하지만 위 코드를 실행하면 user프로퍼티를 찾을 수 없어 오류가 발생한다. 

v-if가 v-for보다 우선순위가 높기 때문에

users의 user를 설정하기 전에 if의 user 프로퍼티를 먼저 찾기 때문에 찾을 수 없다는 것이다. 

 

v-if와 v-for를 함께 사용하기 위해선

users를 먼저 필터링하거나 template 태그를 이용해 for지시어를 먼저 선언해주는 방법이 있다. 

 

* computed property 사용하기

<ul>
  <li
    v-for="user in activeUsers" //계산된 프로퍼티 내를 순회한다.
  >
    {{ user }}
  </li>
</ul>

computed: {
  activeUsers() { //users를 필터링하여 activeUsers를 정의한다. 
    return this.users.filter(user => user.isActive)
  }
}

 

* template tag 사용하기 

 

<ul>
  <template v-for="user in users"> //li를 래핑하고 li의 상위 레벨에서 for지시어를 사용함으로써 if보다 우선하게 한다
    <li v-if="user.isActive">
      {{ user }}
    </li>
  </template>
</ul>

 

#Vue Directive[ v-bind ]

데이터, id, class, style 바인딩 

 

   Vue.createApp({
        data(){
            return {
                textClass: 'primary',
                sectionid: 'tab1',
                sectionStyle: {color:'red'},
                isActive:true,
                hasError:false,
                classObject:{
                    active:true,
                    'text-danger':false
                }
            }
        }
    }).mount('#app');

 

* v-bind: 은 매우 자주 사용되기 때문에 v-bind를 생략하고 :만 붙여 사용할 수 있다. 

v-bind를 이용하면 컴포넌트의 data를 바인딩 할 수 있다.

v-bind는 컴포넌트의 {{property}}뿐만 아니라 id, class, style 등 태그의 속성도 바인딩할 수 있다.

바인딩하는 data는 객체 또는 배열도 가능하다.

 

<div :class="[textClass, {active: isActive}]">클래스 바인딩</div>

 

active의 클래스 여부가 isActive에 의해 결정된다. 

isActive가 true이므로 :class="[textClass, active] 배열이 되고 

class="primary active"가 된다. 

<div :id="sectionid" :style="sectionStyle">아이디 스타일 바인딩</div>

 

아이디와 스타일 바인딩의 예시이다. 

아이디는 문자열로, 스타일은 객체로 바인딩 되었다. 

 

<div class="static" :class="classObject">클래스 객체로 바인딩</div>

 

class="static"이 이미 존재하고 classObjecct가 바인딩된다. 

이때, classObject는 true인 active만 바인딩되고 false인 'text-danger'는 바인딩 되지 않는다. 

바인딩 결과는 static active가 된다.

 

<div :class="isActive ? textClass : ''">삼항연산 클래스 토글</div>

삼항 표현식을 사용할 수도 있다.

isAcitve이면 textClass가 바인딩된다.

삼항 조건의 결과값에는 문자열 또는 바인딩될 data가 들어간다. 

 

 

#Vue Component 등록

 

app이라는 root 컴포넌트에 자식 컴포넌트들을 등록한다.

 

components: {

   컴포넌트명: {body}

}

<div id="app">
    <app-header></app-header>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        components: {
            //'component이름' :{body}
            'app-header': {
                template: '<h1>app 하위 컴포넌트 "app-header"</h1>'
            }
        }
    }).mount('#app');
</script>

#app 태그 내에  

<component명으로 태그를 작성한다.>

실행결과

dev tool의 Vue에 

<Root> 하위 <AppHeader>컴포넌트가 등록된 것을 확인할 수 있다.

 

 

 

#Component간 통신

 

Component는 event와 props로 데이터와 신호를 주고받는다. 

 

동일 레벨의 컴포넌트 간에 직접 통신은 불가능하다. 

상위 컴포넌트에 event를 발생시켜 데이터를 전달할 다른 하위 컴포넌트로 prop을 전달하는 방식으로 통신한다. 

 

Props

 

상위 컴포넌트에서 하위 컴포넌트에 전달하는 프로퍼티

 

* 하위 프로퍼티에 props 배열을 작성한다.

* html의 컴포넌트 태그에서 props를 바인딩한다. 

 

v-bind:prop명="prop에 바인딩할 상위 컴포넌트의 프로퍼티명"

 

<div id="app">
    <app-header v-bind:prop-title="title"></app-header>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appHeader = {
        props:['propTitle'],
        template: `<p>{{propTitle}}</p>`,
    }
    Vue.createApp({
        data(){
            return {
                title: 'Title'
            }
        },
        components: {
            'app-header': appHeader,
        }
    }).mount('#app');
</script>

 

EventEmit

 

하위 컴포넌트에서 상위 컴포넌트로 이벤트를 발생시킴

 

* 하위 컴포넌트에서 this.$emit('발생시킬이벤트명') 을 작성한다.

* html의 컴포넌트 태그에서 이벤트 핸들러로 상위 컴포넌트에 메소드를 동작하게 한다. 

 

v-on:하위컴포넌트에서 발생시킨 이벤트명="상위컴포넌트에서 동작할 메소드명"

 

<div id="app">
    <app-header :prop-title="title"></app-header>
    <app-contents @set-title="changeProp"></app-contents>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appHeader={
        props:['propTitle'],
        template:`<p>{{propTitle}}</p>`
    }
    const appContents={
        template: `<button v-on:click="sendEvent">제목</button>`,
        methods:{
            sendEvent(){
                this.$emit('setTitle');
            }
        }
    }
    Vue.createApp({
        data(){
            return{
                title:''
            }
        },
        methods:{
            changeProp(){
                this.title='newTitle'
            }
        },
        components:{
            'app-header':appHeader,
            'app-contents':appContents
        }
    }).mount('#app');
</script>

 

동일 레벨의 컴포넌트간 통신이 이루어지는 과정

 

-> appContents내의 버튼에서 클릭이벤트가 발생하면

-> appContents 컴포넌트에 setTitle 이벤트를 발생시킨다. 

->  setTitle 이벤트 핸들러에 의해 Root 컴포넌트의 changeProp함수가 실행된다.

->  changeProp 함수가 data를 갱신하고 

->  data가 변경됨에 따라 props의 propTitle도 변경된다. 

 

 

**html 태그 작성시 주의할점 

html 파일에서 vue를 사용할 때 카멜케이스를 사용하면 인식하지 못하기 때문에 -(하이픈)을 사용한다.

이러한 문제는 .vue 파일 형식과는 관계없이 .html 파일에만 해당한다. 

.vue 파일에는 카멜케이스로 작성하여도 된다. 

 

<div id="app">
    <app-contents @set-title="changeProp"></app-contents> //setTitle을 set-title로 changeProp은 속성명이 아닌 ""내의 문자열 값이기 때문에 관계 없다.
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appContents={
        template: `<button v-on:click="sendEvent">제목</button>`, 
        methods:{
            sendEvent(){
                this.$emit('setTitle'); //카멜케이스
            }
        }
    }
    Vue.createApp({
        methods:{
            changeProp(){ //카멜케이스
                this.title='newTitle'
            }
        },
        components:{
            'app-contents':appContents //컴포넌트명은 html에 작성될 태그명이므로 - 사용
        }
    }).mount('#app');
</script>

 

 

'Vue.js' 카테고리의 다른 글

Vue.js form submit  (0) 2023.11.30
Vue.js 프로젝트 생성하기  (0) 2023.11.29
Vue.js Intro  (0) 2023.11.28
728x90

Vue.js

UI, 라우팅, SSR( Server Side Rendering ) 등의 프론트엔드 개발을 지원하는 프레임워크


Vue.js 공식문서

 

#Vue3 코드 작성 방식

OptionsAPI, CompositionAPI 두가지 방식이 존재하고 

공식문서의 네비게이션바에서 각 방식에 따른 API 설명을 확인할 수 있다. 

 

-OptionsAPI

 

클래스 기반 컴포넌트 인스턴스(this) 구조 

<script>
export default {
  // data()에서 반환된 속성들은 반응적인 상태가 되어 `this`에 노출됩니다.
  data() {
    return {
      count: 0
    }
  },

  // methods는 속성 값을 변경하고 업데이트 할 수 있는 함수.
  // 템플릿 내에서 이벤트 헨들러로 바인딩 될 수 있음.
  methods: {
    increment() {
      this.count++
    }
  },

  // 생명주기 훅(Lifecycle hooks)은 컴포넌트 생명주기의 여러 단계에서 호출됩니다.
  // 이 함수는 컴포넌트가 마운트 된 후 호출됩니다.
  mounted() {
    console.log(`숫자 세기의 초기값은 ${ this.count } 입니다.`)
  }
}
</script>

<template>
  <button @click="increment">숫자 세기: {{ count }}</button>
</template>

 

 

-CompositionAPI

 

재사용성이 좋은 컴포넌트 구조

<script setup>
import { ref, onMounted } from 'vue'

// 반응적인 상태의 속성
const count = ref(0)

// 속성 값을 변경하고 업데이트 할 수 있는 함수.
function increment() {
  count.value++
}

// 생명 주기 훅
onMounted(() => {
  console.log(`숫자 세기의 초기값은 ${ count.value } 입니다.`)
})
</script>

<template>
  <button @click="increment">숫자 세기: {{ count }}</button>
</template>

 

 

#개발 환경

 

Node.js https://nodejs.org/en

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

Visual Studio Code https://code.visualstudio.com/ 

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

 

*plugins 

- Vue VSCode Snippets

- Live Server

- Material Icon Theme 

- Night Owl (테마)

- ESLint, TSLint Vue (문법 어시스턴스), Auto Close Tag

- Prettier( Code Formatter ), Project Manager

- GitLens

- Atom Keymap, Jetbrains IDE Keymap ( Shortcut )

[ Vue2 - Vetur / Vue3 - Volar ] 

 

 

 

WindowsOS 선택사항

(cmder https://cmder.app/)

 

Cmder | Console Emulator

Total portability Carry it with you on a USB stick or in the Cloud, so your settings, aliases and history can go anywhere you go. You will not see that ugly Windows prompt ever again.

cmder.app

 

Chrome Extension Devtool

Vue.js devtools

https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd

 

Vue.js devtools

Browser DevTools extension for debugging Vue.js applications.

chrome.google.com

 

 

CDN( Content Delivery Network )

https://ko.vuejs.org/guide/quick-start.html

 

빠른 시작 | Vue.js

 

ko.vuejs.org

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

 

**

vscode shortcut

#app + tap

tag_name + tap

 

 

기본 예제

- OptionsAPI

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app">{{ message }}</div>

<script>
  const { createApp } = Vue //distructuring 문법

  createApp({ //vue를 사용할 application instance
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app') // id에 app인 태그에 mount(부착)
</script>

 

 

SSR도 가능하다더니 thymeleaf 사용하는 느낌이 난다.. 

message라는 프로퍼티와 mount 함수 프로퍼티를 가진 createApp 클래스를 만들고 

html에 표현식으로 createApp 객체의 프로퍼티를 사용해 렌더링 하는 느낌이다. 

 

distructuring 대신 Vue.createApp으로 간단하게 사용할 수 있다. 

<script>
    Vue.createApp({
        data(){
            return {
                message: 'hello vue'
            }
        }
    }).mount('#app');
</script>

 

 

-CompositionAPI

<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const message = ref('Hello vue!')
      return {
        message
      }
    }
  }).mount('#app')
</script>

 

 

#Vue 작동 원리 ( reactivity ) 

 

javascript의 Proxy Api를 사용하여 Vue의 작동원리를 알아본다. 

 

 const data={
    a:10
   }

   const app = new Proxy(data,{
    get() {
        console.log('프록시를 이용해 mocking data의 값에 접근하면 나오는');
    },
    set() {
        console.log('실제 data에는 변경되지 않지만 프록시를 이용해 mocking data의 값을 변경하면 나오는');
    }
   });

실행결과

Proxy는 data를 mocking할 뿐, 실제 data에 접근하는 것이 아니기 때문에 실제 data의 값은 변함 없다.

 

   const data={
    a:10
   }
   function render(newValue){
    let app = document.querySelector('#app');
    app.innerHTML=newValue;
   }
   const app = new Proxy(data,{
    get() {
        console.log('프록시를 이용해 mocking data의 값에 접근하면 나오는');
    },
    set(target,prop, newValue) {
        target[prop] = newValue; //data.a=newValue
        render(newValue); //app이라는 객체의 값이 바뀌면 dom 객체의 html이 바뀐다. 
    }
   });

 

app이란 객체는 app이라는 div 태그의 html을 동적으로 변경해주는 객체가 된다. 

 

즉, 나는 동적으로 바뀔 부분을 컴포넌트로 코드를 작성하고, 그 컴포넌트(App)를 

돔 트리의 특정 객체(div id='App')로 만들면

Vue라는 프록시가 ref의 값을 감시하다가 값이 변경되면

그 돔 객체의 html을 수정해준다. 

 

여기서 주의할 점은 Vue라는 프록시를 통해 값을 변경해야 돔 객체를 수정해주지

직접 ref의 원본인 data에 접근하여 변경하면 render()를 실행하지 않기 때문에 돔 객체를 수정해주지 않는다. 

 

'Vue.js' 카테고리의 다른 글

Vue.js form submit  (0) 2023.11.30
Vue.js 프로젝트 생성하기  (0) 2023.11.29
Vue.js 기초 문법  (2) 2023.11.28

+ Recent posts