본문 바로가기
IT/VueJS

VueJS 핵심 문법 정리

by 골든크랩 2025. 3. 12.
728x90
반응형

 

SFC 구조.  

아래와 같은 구조가 기본 구조이다.

<template> 태그 안에 HTML 을 사용하되, 데이터는 <script> 태그 안에 data() 함수안에 객체로 리턴받아 사용하는 구조이다.  태그 안에 사용할때 {{ 변수명 }} 를 사용해야만 한다.  data() 함수 형태는 예약어처럼 사용되기때문에 반드시 이 문법이 사용되야 함.

<style> 도 역시 한 파일안에 넣는다.

 

Props를 통해 Vue template 내에서 사용하는 모든 정보를 저장가능하다.

 

그러나, 태그의 속성값에 동적으로 넣으려면 directive를 사용해야 한다.

--------------------------------------------------------------------------------------------------------------------------------------------

<template>

    <div>{{color}}</div>
</template>

<script>

    export default {

        data() {

            return {

                color: 'red'

            }

        }

    }

</script>

<style>

</style>

--------------------------------------------------------------------------------------------------------------------------------------------

간단한 샘플 예제)

 

<template>

    <h1>{{title}}</h1>

    <hr/>

    <p>{{description}}</p>

    <ul>

        <li>{{productions[0]}}</li>

        <li>{{productions[2]}}</li>

        <li>{{productions[3]}}</li>

    </ul>

</template>

 

<script>

export default {

    data() {

        return {

            title: 'GC몰에 오신것을 환경합니다.',

            description: '자주 이용햇 주세요.^^;,

            products : ['스포티지', '투싼', '그랜져']

        }

    }

}

</script>

--------------------------------------------------------------------------------------------------------------------------------------------

CSS 를 사용한 Vue 스타일링. scopped는 현 컴포넌트에만 style이 적용된다는 의미.

<style lang="scss" scopped>

h1,

h2 {

    font-family: 'Avenir', Helvetica, Arial, sans-serif;

    text-align: center;

}

.title {

    font-family: 'Avenir', Helvetica, Arial, sans-serif;

    color: #2c3e50

    margin-top: 60px;

}

.subtitle {

    color : #4fc08d;

    font-style: italic;

}

</style>

--------------------------------------------------------------------------------------------------------------------------------------------

태그 attribute 를 사용하는 간단한 샘플 예제). vue-directive를 사용해야 함.  변수명이 ""기호안에 들어간다.

 

<template>

    <h1>{{title}}</h1>

    <hr/>

    <p>{{description}}</p>

    <img :src="imgSrc" v-if="isShowImage">

    <ul>

        <li v-for="product in products" :key="product">{{product}}</li>

    </ul>

</template>

 

<script>

export default {

    data() {

        return {

            title: 'GC몰에 오신것을 환경합니다.',

            imgSrc: 'https://static.gcmall.com/hello/youreidiot/aaa.jpeg',

            description: '자주 이용햇 주세요.^^;,

            products : ['스포티지', '투싼', '그랜져']

        }

    }

}

</script>

--------------------------------------------------------------------------------------------------------------------------------------------

 

컴포넌트의 분리.

vue extensions 를 사용해서, 각 컴포넌트 파일을 만든후, <template> 부분만 구현해도 컴포넌트는 분리됨.

App.vue 에서 갖다 사용하면 됨. 사용할때는 script 부분에서 import 하는 코드가 있어야 하고, <template> 부분에서 사용하면 됨.  그런데 실무에서는 App.vue 에서 가져오지 않음.

--------------------------------------------------------------------------------------------------------------------------------------------

Props 의 사용

자식컴포넌트로 정보를 전달하기 위해서 사용.

자식 컴포넌트는 props 라는 식별자를 선언하고, 받을 변수들을 리스트 배열로 받음.  그리고, template에서 변수명을 사용하면 됨.

--------------------------------------------------------------------------------------------------------------------------------------------

Slot

커스텀 태그(컴포넌트) 사이에 들어가는 콘텐츠인데...하나면 들어간다면 Slot을 사용하면 됨

그러나 여러개를 사용하면..Named Slots 를 사용해야 함.

--------------------------------------------------------------------------------------------------------------------------------------------

Named Slot

보내는 쪽에서는 태그를 사용하되, <slot name="title /> 같은 slot 태그를 추가로 넣어서, 이름을 지정해주면 됨.

받는쪽에서는 <template> 태그나 커스텀 태그 안에 v-slot:title 처럼 사용할 태그를 지정하여 사용함.  

<template v-slot:title>

    <h3>My Article Tile<h3>

</template>

- 자세한 예제는 교재 참조.

--------------------------------------------------------------------------------------------------------------------------------------------

Filters -  vue3에서 제거되서 신경 안써도 됨.

데이터 출력시 필터를 통해 데이터 가공을 할수 있음.

사용법은 <template> 태그에서 변수를 사용할 때, 변수옆에 '|' 를 넣고 사용할 필터 함수명을 지정해주면 됨

{{message | truncate }}

 

구현은 <script> 태그 안에 filters: 안에 필터 함수를 구현해 주면 됨.

- 자세한 예제는 교재 참조.

--------------------------------------------------------------------------------------------------------------------------------------------

refs : DOM 직접 참조하기

보통 this.$refs.변수명이나 proxy.$refs.변수명등을 통해 접근한다. 변수명 사용하는쪽에서는 ref 를 쓰고, script쪽에선 refs 로 접근함.

예)

<template>

    <div id="app">
        <input ref="theInput" />

   </div>

</template>

<script>

export default {

    methods: {

        focus() {

            this.$refs.theInput.focus()

        }

    }

}

</script>

--------------------------------------------------------------------------------------------------------------------------------------------

$emit : 자식이 부모로 데이터 전달하기

이 방식은 react, vue, angular 모두 동일한데, vue가 사용하기가 제일 쉬움.

 

커스텀 이벤트를 임으로 발생시킨다.  this.$emit 을 사용하거나, $emit 을 사용함.

예) 

 

data() {

    return {

        message: null

    }

},

methods: {

    send() {

        this.$emit('send', this.message);

    }

}

 

부모 컴포넌트에서 사용법 예제)

<MessageEditor @send="message = $event" />

해석 : send라는 이벤트가 발생하면 자식이 보낸데이터를 $event를 통해 받아서 message 변수에 넣어라.

 

형태 :

this.$emit('eventName', /* payload */)

 

"Payload"는 문맥에 따라 다소 의미가 달라질 수 있지만, 일반적으로 데이터전송되는 정보를 지칭하는 데 사용됨.

 

--------------------------------------------------------------------------------------------------------------------------------------------

Mixin, 믹스인

여러 Vue 컴포넌트에서 기능을 재사용하는 방법. 일종에 전역함수 같은 개념임.

<template>과 <style> 태그는 필요없고, <script> 태그만 있으면됨. 파일 확장자는 vue를 사용함.

.js 파일로 생성

독립적으로는 사용할 수 없고, 다른 컴포넌트에 mixin 되서 사용됨.

사용하는 쪽에서는 import 하고 함수명을 {{함수명()}} 이런식으로 <template> 태그에서 사용하면 됨.

 

예)

mixin 파일

export default {

    methods: {

        greet(name) {

            return `$(this.greeting) $(name)`

        }

    },

    data() {

        return {

            greeing: 'Hello'

        }

    }

}

 

사용하는 파일

<template>

    <div> {{ ('World') }} </div> 

    <h2> {{hello()}} </h2>  <-----------greeter라는 파일에 있는 함수를 그냥 {{}} 표기법으로 사용하면 됨

</template>

<script>

    import greeter from './mixins/greeter.js'  <------ mixin 임포트하기

    export default {

        mixins: [greeter]     <----- mixin 등록하기.  파일명을 그대로 쓰면 됨.

    }

</script>

--------------------------------------------------------------------------------------------------------------------------------------------

Global component

 

공통으로 사용하는 컴포넌트.

보통 별도의 디렉토리를 하나 만들고, component라는 메서드를 사용해서 등록을 해야 함.

main.js 에서 다음과 같은 형태로 사용함.  @는 절대경로로 root로부터 시작되는 경로임.

app이 실행하면서 바로 메모리에 올라감.

 

// 페이지 매김 구성요소
import Pagination from '@/components/Pagination'

 

const app = createApp(App)

 

// 전역 구성요소 장착
app.component('DictTag', DictTag)
app.component('Pagination', Pagination)
app.component('TreeSelect', TreeSelect)
app.component('FileUpload', FileUpload)
app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)

 

 

//사용법은 따로 import를 하지 않아도 되고(main.js에서 했으므로), 그냥 넣어서 사용하면 됨.

예)

   <pagination
         v-show="total > 0"
         :total="total"
         v-model:page="queryParams.pageNum"
         v-model:limit="queryParams.pageSize"
         @pagination="getList"
      />

 

 예2)

  --> 사용(호출)하는 곳의 코드

  <global-button @click="buttonCokeced">눌러주세요</global-button>

 

methods : {

    buttonCliked() {

        console.log('button cliked...")  

    }

}

 

--> global component 를 만드는 부분

<template>

    <button @click="$emit('click', $event)">

        <slot />

    </button>

</template>

<script>

export default {

}

</script>

<style scoped>

    button {

        color: brown;

    }

</style>

 

--------------------------------------------------------------------------------------------------------------------------------------------

라우팅 (클라이언트 사이드 라우팅)

main.js에서 router 라이브러리를 사용해야 함.   해당 코드는 다음과 같다.

app.use(router)   <---------------use는 plugin 을 사용하겠다는 개념임.

 

src/router/index.js  에다가 라우팅 경로를 잡아준다. 여기에 설정된 경로들은 서버측으로 전달되는게 아니라, 클라이언트측 라우팅을 하도록 만든다.

 

 

 

<script setup>
import { useRoute, useRouter } from 'vue-router'

const route = useRoute();
const router = useRouter();
const { params, query } = route
const { path } = params

router.replace({ path: '/' + path, query })
</script>

 

 

src/router/index.js 에서 -> src/view/redirect/index.vue 로 전달하는 구조?

 

 

 

 

src/router/index.js  내용

------------------------------------------------------------------------------------------

import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'

/**
 * Note: 라우팅 구성 항목
 *
 * hidden: true                     // true로 설정하면 경로가 401, 로그인 등과 같은 페이지 또는 /edit/1과 같은 일부 편집 페이지의 사이드바에 더 이상 표시되지 않습니다
 * alwaysShow: true                 // 경로 아래에 자식이 선언한 경로가 1개 이상 있으면 자동으로 구성 요소 페이지와 같은 중첩 패턴이 됩니다
 *                                  // 한 가지만 있는 경우 장군 서브루트가 존재 사이드바에 루트 경로로 표시됩니다--예를 들어 부트스트랩 페이지
 *                                  // 아래 라우팅을 무시하고 싶다면 children 선언 수는 루트 경로를 보여줍니다.
 *                                  // 당신은 설정할 수 있습니다 alwaysShow: true, 이렇게 하면 이전에 정의된 규칙을 무시하고 항상 루트 경로를 표시합니다.
 * redirect: noRedirect             // 설정시 noRedirect 탐색경로 내비게이션에서 해당 경로를 클릭할 수 없는 경우
 * name:'router-name'               // 경로 이름을 설정하고, 그렇지 않은 경우에는 반드시 입력하십시오.<keep-alive>다양한 문제가 발생할 수 있습니다
 * query: '{"id": 1, "name": "ry"}' // 액세스 경로에 대한 기본 전달 매개변수
 * roles: ['admin', 'common']       // 라우팅에 액세스할 수 있는 권한
 * permissions: ['a:a:a', 'b:b:b']  // 접근 경로 메뉴권한
 * meta : {
    noCache: true                   // true로 설정하면 그렇지 않습니다. <keep-alive> 캐시(기본값은 false)
    title: 'title'                  // 이 경로의 사이드바 및 탐색경로에 표시되는 이름을 설정합니다.
    icon: 'svg-name'                // 경로의 아이콘과 해당 경로를 설정합니다. src/assets/icons/svg
    breadcrumb: false               // false로 설정하면, 탐색경로에 존재breadcrumb  표시 않을 것이다 
    activeMenu: '/system/user'      // 경로가 이 속성을 설정하면 해당 사이드바가 강조 표시됩니다.
  }
 */

// 공개 경로
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue') 

        <------ lazy-loading. 최초 바이트 도달시간(서버 응답 시간...TTFB, Time to First Byte)

        <------ 의미는 이 페이지는 나중에 실제 로딩될때, 서버에서 가져와라라는 의미.
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login'),
    hidden: true
  },
  {
    path: '/register',
    component: () => import('@/views/register'),
    hidden: true
  },
  {
    path: "/:pathMatch(.*)*",
    component: () => import('@/views/error/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error/401'),
    hidden: true
  },
  {
    path: '',
    component: Layout,
    redirect: '/index',
    children: [
      {
        path: '/index',
        component: () => import('@/views/dashboard/index'),
        name: 'Index',
        meta: { title: 'home', icon: 'dashboard', affix: true }
      }
    ]
  },
  {
    path: '/user',
    component: Layout,
    hidden: true,
    redirect: 'noredirect',
    children: [
      {
        path: 'profile',
        component: () => import('@/views/system/user/profile/index'),
        name: 'Profile',
        meta: { title: '프로필', icon: 'user' }
      }
    ]
  }
]

// 동태 경로, 사용자 권한에 따라 동태 to load
export const dynamicRoutes = [
  {
    path: '/system/user-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:user:edit'],
    children: [
      {
        path: 'role/:userId(\\d+)',
        component: () => import('@/views/system/user/authRole'),
        name: 'AuthRole',
        meta: { title: '역할 할당', activeMenu: '/system/user' }
      }
    ]
  },
  {
    path: '/system/role-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:role:edit'],
    children: [
      {
        path: 'user/:roleId(\\d+)',
        component: () => import('@/views/system/role/authUser'),
        name: 'AuthUser',
        meta: { title: '사용자 할당', activeMenu: '/system/role' }
      }
    ]
  },
  {
    path: '/system/dict-data',
    component: Layout,
    hidden: true,
    permissions: ['system:dict:list'],
    children: [
      {
        path: 'index/:dictId(\\d+)',
        component: () => import('@/views/system/dict/data'),
        name: 'Data',
        meta: { title: '사전 데이터', activeMenu: '/system/dict' }
      }
    ]
  },
  {
    path: '/monitor/job-log',
    component: Layout,
    hidden: true,
    permissions: ['monitor:job:list'],
    children: [
      {
        path: 'index/:jobId(\\d+)',
        component: () => import('@/views/monitor/job/log'),
        name: 'JobLog',
        meta: { title: '로그 스케줄링', activeMenu: '/monitor/job' }
      }
    ]
  },
  {
    path: '/tool/gen-edit',
    component: Layout,
    hidden: true,
    permissions: ['tool:gen:edit'],
    children: [
      {
        path: 'index/:tableId(\\d+)',
        component: () => import('@/views/tool/gen/editTable'),
        name: 'GenEdit',
        meta: { title: '빌드 구성 수정', activeMenu: '/tool/gen' }
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  },
});

export default router;

 

 

 

 

 src/view/redirect/index.vue  내용

------------------------------------------------------------------------------------------

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

<script setup>
import { useRoute, useRouter } from 'vue-router'

const route = useRoute();
const router = useRouter();
const { params, query } = route
const { path } = params

router.replace({ path: '/' + path, query })
</script>

 

 

--------------------------------------------------------------------------------------------------------------------------------------------

router-link   : 페이지 이동을 위한 링크를 생성

router-view  : 현재 URL에 맞는 컴포넌트를 렌더링.

    사용자 요청 URL에 따라 해당 최신 뷰를 로딩해주는 Functional 컴포넌트. 

    런타임 시 다이나믹하게 컨텐츠 렌더링.   

    하위 컴포넌트 렌더링, 라우팅 경로에 따라 컴포넌트 마운트/언마운트

 

App.vue에서....

<template>
  <router-view />
</template>

 

 

src/layout/components/Navbar.vue 소스에서.....

<template #dropdown>
            <el-dropdown-menu>
              <router-link to="/user/profile">
                <el-dropdown-item>프로필</el-dropdown-item>
              </router-link>
              <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
                <span>레이아웃 설정</span>
              </el-dropdown-item>
              <el-dropdown-item divided command="logout">
                <span>로그아웃</span>
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>

 

 

--------------------------------------------------------------------------------------------------------------------------------------------

아래 2가지 경우의 차이점.

 

<router-link to="/user/profile">
                <el-dropdown-item>프로필</el-dropdown-item>
</router-link>

<router-link class="link-type" :to="'/login'">기존 계정을 사용하여 로그인</router-link>

1. to 앞에 콜론이 없는 경우 (to="/user/profile")

  • to의 값이 문자열(String)로 고정됩니다.
  • 여기서는 "/user/profile"이 그대로 전달되며, 정적인 값을 의미합니다.
  • 즉, 이 경우에는 /user/profile로 항상 동일한 경로로 이동합니다.

예시:

html
<router-link to="/user/profile">
    <el-dropdown-item>프로필</el-dropdown-item>
</router-link>
  • 이 코드는 단순히 고정된 경로(/user/profile)로 이동합니다.

2. to 앞에 콜론이 붙은 경우 (:to="'/login'")

  • 바인딩된 값을 사용하며, 동적인 데이터를 표현할 수 있습니다.
  • JavaScript 표현식이나 Vue의 데이터 속성을 통해 값이 할당됩니다.
  • 콜론은 v-bind 디렉티브의 축약형입니다.

예시:

html
<router-link :to="'/login'">
    기존 계정을 사용하여 로그인
</router-link>
  • 이 경우, :to의 값은 JavaScript 표현식으로 평가됩니다.
  • 예를 들어, '${baseUrl}/login'과 같은 동적 값을 전달하거나, 컴포넌트의 데이터 속성(this.somePath)을 바인딩할 수 있습니다.

정리

형태설명
to="/user/profile" 정적 값, 항상 문자열로 해석됨 ('/user/profile'로 고정).
:to="'/login'" 동적 값, Vue의 데이터나 JavaScript 표현식을 바인딩하여 값이 변동될 수 있음.

즉, :(콜론)은 Vue.js에서 속성을 정적으로 사용하느냐, 동적으로 바인딩하느냐를 결정짓는 중요한 요소임.

 

--------------------------------------------------------------------------------------------------------------------------------------------

메뉴 링크 설정, Navigation links

 

프로그래머틱하게 접근법

function back() {
  if (proxy.$route.query.noGoBack) {
    proxy.$router.push({ path: "/" });  
  } else {
    proxy.$router.go(-1);                        <--- 한칸 뒤로 가기
  }
}

 

잘못된 경로 요청시...아래 코드를 쓰면, 서버로 요청을 보내지 않고 404 Not found 페이지를 보여준다.

  {
    path: "/:pathMatch(.*)*",
    component: () => import('@/views/error/404'),
    hidden: true
  },

 

--------------------------------------------------------------------------------------------------------------------------------------------

Navigation Guards

 

Navigatin 경로를 들어갈때, 권한이 있는지 체크.

 

 

--------------------------------------------------------------------------------------------------------------------------------------------

상태관리 (state management)

 

로컬 state management : 단순함. 많이지면 무거워짐.

 

글로벌에 데이터를 저장(global store)

- vuex 사용

 

app.use(store)   <------------------------vuex 를 쓰겠다는 의미임.

 

 

src/store 라는 디렉토리가 생기고, index.js 파일에 다음 내용이 있음.

 

const store = createPinia()
export default store

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

728x90
반응형

댓글