뷰 라우터

싱글페이지앱에서는 화면의 새로고침이 없이 페이지가 전환이 되어야 합니다. 그럴때 사용하는 것이 뷰 라우터로써 라우트에 컴포넌트를 맵핑 시켜 놓고 특정주소에서 해당하는 컴포넌트를 렌더링하는 방식으로 싱글페이지앱에서 페이지의 전환을 대신합니다.

1. vue-router 구성

1-1. vue-router파일 작성

참고사항

vue-router의 실제 코드를 main.js에 작성하게 되면 vue-router 편향적인 파일이 되기 때문에 삼가한다. vue-router만을 위한 파일을 생성하여 작성하도록 하자.

vue-router 설치

1
npm install vue-router

import

1
2
3
4
import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

컴포넌트 생성

렌더링할 컴포넌트 생성

1
2
const Foo = {template: '<p>foo</p>'}
const Bar = {template: '<p>bar</p>'}

VueRouter 생성

path는 주소, component는 해당 주소에서 렌더링할 컴포넌트를 맵핑

1
2
3
4
5
6
7
8
let routes = [
{path: '/foo', component: Foo},
{path: '/bar', component: Bar}
]

export const router = new VueRouter({
routes
});

1-2. main.js 작성

Vue 인스턴스 생성

1
2
3
4
5
import {router} from '라우터작성 경로';

new Vue({
router
})

뷰 라우터 태그 작성

라우터 설정시에 맵핑했었던 컴포넌트가 router-view에 렌더링 됩니다.

1
2
3
<div id="app">
<router-view></router-view>
</div>

라우터 이동

path부분에 주소를 입력 해주면 해당 라우팅 경로로 이동할수 있습니다(실제론 해당 주소로 컴포넌트를 바꿔서 렌더링 작업을 처리해준다는 것).

1
2
3
<div id="app">
<router-link to="path"></router-link>
</div>

redirect

path 경로로 들어왔을 때 리다이렉트 할수 있다.

1
2
3
4
5
6
var routes = [
{
path: '/',
redirect: '/리다이렉트할 경로'
}
]

url에 # 제거하기

mode에 history값을 주면 #을 제거 가능하다.

1
2
3
4
5
6
new VueRouter({
mode : history,
routes : [

]
})

2. 심화된 라우팅 구성

중첩된 라우트

중첩된 라우트를 사용하기 위해서는 children 속성을 이용하면 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var routes = [
{
path: '/mypage',
component: {template: '<p>안녕하세요</p><router-view></router-view>'},

// 중첩된 라우트를 사용하기 위해서는 children 속성을 이용하면 가능합니다.
children: {
path: 'myBoard',
component: {template: '<p>내 게시물</p>'}
}
}
]

new VueRouter({
routes
})

중첩된 라우트는 화면 구성 컴포넌트의 수가 적으면 유용하지만 한번에 더 많은 컴포넌트를 표시하는데는 한계가 있습니다.

다중 라우트

하나의 주소에 여러개의 컴포넌트와 매핑을 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<view-router></view-router>
<view-router name="header"></view-router>

var routes = [
{
path: '/mypage',
components: {
default: {template: '<p>기본 컴포넌트</p>'}, // view-router에 name속성의 값을 설정하지 않으면 default
header: {template: '<header>헤더 컴포넌트</header>'} // view-router에 name속성의 값을 키로 하여 컴포넌트를 맵핑
}
}
];

new VueRouter({
routes
});

상하위 관계가 아닌 동등한 레벨의 여러개의 컴포넌트를 한번에 표시가 가능합니다.

Comment and share

component 확장해서 사용하기

extends는 기존 컴포넌트를 확장해서 유사한 컴포넌트를 만들수 있는 방법입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* Hello.vue */
<template>
<div>
{{ message }}
</div>
</template>

<script>
export default {
data() {
return {
message: "Hello extends 사용하기"
}
},
created() {
console.log('Hello created 호출');
}
}
</script>

/* HelloWorld.vue */
<script>
import Hello from './Hello.vue';

export default {
name: 'HelloWorld',
extends: Hello,
data() {
return {
message: "HelloWorld extends 사용하기"
}
},
created() {
console.log('HelloWorld created 호출');
}
}
</script>

extends를 사용할 경우 data, computed, methods, filters는 오버라이딩이 된다. 하지만 라이프사이클은 모두 순서대로 호출이 된다.

Comment and share

vue를 사용하게 되면 DOM요소에 대한 관리를 vue가 해주기 때문에 직접 접근할 일이 드물지만, 특별한 케이스에 한해서 DOM 요소에 접근을 해야할 경우가 생길수 있습니다.

1. DOM 요소에 접근

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div>
<span ref="message"></span>
</div>
</template>

<script>
export default {
created() {
this.$refs.message.innerHTML = "ref 테스트"
}
}
</script>

this.$refs.ref이름 으로 DOM요소에 접근 가능

2. 자식 컴포넌트의 메서드 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<childComponent ref="child"/>
</div>
</template>

<script>

/* parentComponent */
import childComponent from './childComponent'

export default {
components: {
childComponent
},
created() {
this.$refs.child.displayConsole()
}
}

/* childComponent */
export default {
methods: {
displayConsole() {
console.log('ref 테스트')
}
}
}
</script>

Comment and share

watch는 데이터에 대한 변화를 감지할 때 주로 사용하게 됩니다.

데이터가 Array나 Object인 경우

대상이 array나 Object인 경우에 문제가 발생합니다. object의 프로퍼티나 array의 자식 값의 변화에는 감지를 하지 못합니다. 그래서 set를 이용하여 중첩된 관계에 대한 반응성을 추가 할수 있습니다.

Comment and share

computed를 사용하면서 느낀 점

computed는 기본 데이터를 가공 처리 작업 후에 데이터 바인딩을 해주어야 하는 경우에 주로 사용하는 거 같습니다. 기본 데이터가 변경 되면 computed가 실행이 되어서 처리 로직을 실행 한 후에 결과를 변경후 렌더링합니다. 기존 데이터가 변경이 되는 걸 감지한다는 점에 있어서 watch와 비슷한 기능을 하는 것 처럼 보이지만 실제로는 watch를 사용하는 것보다는 computed를 사용하는게 더 적절한 경우가 많다고 생각합니다.

computed VS watch

watch는 해당 데이터의 변화를 감지하면 메소드가 실행이 됩니다. computed도 내루 로직에서 사용 하는 데이터가 변경이 되면 감지하여 메소드가 실행이 됩니다. 이를 보면 두개의 속성이 비슷하게 사용되는 것 처럼 느낄수 있습니다. 둘 모두를 사용했을 때 같은 결과를 얻어 낼수 있다면 computed를 사용하는 것이 좋다고 생각합니다. watch를 사용 할 때는 주로 사용자의 입력등 비동기인 경우에 사용하는 것이 좋다고 생각합니다.

computed VS methods

computed와 methods의 가장 큰 차이는 캐싱입니다. 두 속성 모두 사용자가 원하는 결과를 낼수 있지만 computed 같은 경우에는 종속 된 대상(실행부에서 사용한 인스턴스 데이터 등)의 값이 변화할 때만 다시 실행을 하게 됩니다. 그렇게 때문에 computed를 사용해도 되는 경우에는 methods 보다는 computed를 사용합시다.

computed는 필수적으로 return 받은 값으로 데이터 바인딩 합니다.

computed 속성의 메서드에서 await를 썼더니 원하지 않는 결과 도출(당연하게도 await를 메서드에 설정하면 해당 메서드는 Promise를 return하기 때문)

GET | SET

computed의 프로퍼티는 직접 수정으로 인한 값이 수정 되지 않는다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default {
data() {
return {
num1: 10,
num2: 20
}
},
computed: {
sum() {
return num1 + num2
}
},
methods: {
updateSum() {
this.sum = 100; // 이렇게 직접 값을 주어도 변경 되지 않는다 => 이런식의 코딩은 노노
}
}
}

sum 앞에 아무런 키워드를 입력하지 않으면 get이 생략된 것으로써 값을 가져오는 기능을 한다
sum 앞에 set 키워드를 넣어서 해당 sum을 동작하게 하는 데이터를 변경하는 식으로 코드 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default {
data() {
return {
num1: 10,
num2: 20
}
},
computed: {
get sum() {
return num1 + num2
},
set sum() {
this.num = 10 // sum을 직접 수정하는게 computed 메서드를 동작하게 하는 데이터를 변경하는 식으로 작성
}
}
}

Comment and share

상위 - 하위 컴포넌트간의 데이터전달

각 컴포넌트는 고유한 유효범위를 가지고 있기 때문에 다른 컴포넌트의 값을 직접 참조할수가 없습니다. 그래서 컴포넌트간의 데이터를 전달 하는 방법을 알아야 합니다.

1. 상위 컴포넌트에서 하위 컴포넌트로 값 전달

이 방법은 상위 컴포넌트에서 하위 컴포넌트로 v-bind 디렉티브를 이용해서 값을 전달 할수 있습니다.

상위 컴포넌트

상위 컴포넌트에서는 하위 컴포넌트에 v-bind 디렉티브를 이용해서 속성에 값을 전달합니다.

1
2
3
4
5
6
7
8
9
10
11
<child-componenet v-bind:msg="message"></child-componenet>

<script>
export default {
data() {
return {
message: '안녕하세요'
}
}
}
</script>

하위 컴포넌트

하위 컴포넌트에서는 상위 컴포넌트에서 속성의 값을 props 속성을 이용하여 전달 받을 수 있습니다.

1
2
3
4
5
6
7
<script>
export default {
props: {
msg: String // 속성명 : 전달 받을 타입으로 작성
}
}
</script>

2. 하위 컴포넌트에서 상위 컴포넌트로 값 전달

하위 컴포넌트에서 상위 컴포넌트로 값을 전달하기 위해서는 이벤트를 활용합니다. 이벤트를 발생시키는 $emit, $on을 이용하여 값을 전달 합니다.

하위 컴포넌트

하위 컴포넌트는 상위컴포넌트로 값을 전달 할 상황이 생기면 $emit을 이용하여 이벤트를 발생시키면서 데이터를 전달 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<button @click="onClick">클릭</button>

<script>
export default {
data() {
return {
msg : '안녕하세요.'
}
},

methods: {
onClick() {
this.$emit('@click', this.msg);
}
}
}
</script>

상위 컴포넌트

하위 템플릿에 추가한 이벤트에서는 상위 컴포넌트의 메서드를 호출 할수 있기 때문에 하위 컴포넌트에서 $emit과 함께 전달한 데이터를 상위 컴포넌트로 전달 할수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
<child-component @@click="onSubmit"></child-component>

<script>
export default {
methods: {
onSubmit(msg) {
console.log(msg);
}
}
}
</script>

단점 : 트리구조가 복잡하게 얽혀있는 구조인 경우에 하나의 데이터를 변경하려고 해도 연관된 상하위컴포넌트 전체를 변경해야 하기 때문에 유지 보수의 어려움

3. 이벤트 버스

상위-하위 컴포넌트 간의 데이터 통신은 상-하위 관계가 복잡하게 이루어져 있을 경우에 연관된 props와 이벤트 발생가 많이 생성이 되어서 하나를 수정해야 한다면 관련된 모든 props와 이벤트를 수정하여야 하기 때문에 유지보수의 어려움이 있습니다. 그래서 이벤트 버스를 활용하면 직접 관계가 없는 컴포넌트끼리 데이터를 전송할수 있습니다.

이벤트 버스 또한 $emit와 $on을 활용 합니다.

1) 이벤트 버스 생성

Vue의 프로토 타입으로 $EventBus(이름은 상관 없음) 뷰 인스턴스를 생성합니다(이때 아무런 옵션을 설정하지 않습니다). 이렇게 생성하면 컴포넌트 내부에서 this.$EventBus로 접근이 가능합니다.

1
2
3
import Vue from 'vue'

Vue.prototype.$EventBus = new Vue();

2) 데이터를 전달 하려는 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
<button @click="onClick">클릭</button>

<script>
export default {
methods: {
onClick() {
this.$EventBus.$emit('@click', '안녕하세요');
}
}
}
</script>

3) 데이터를 전달 받는 컴포넌트

1
2
3
4
5
6
7
8
9
<script>
export default {
created() {
this.$EventBus.$on('@click', (msg) => {
console.log(msg);
});
}
}
</script>

4) 이벤트 해제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
export default {
methods: {
created() {
this.$EventBus.$on('@click', () => {
console.log('생성')
});
},
destroyed() {
this.$EventBus.$off('@click')
}
}
}
</script>

장점

서로 연관이 없는 컴포넌트끼리 직접적으로 쉽게 데이터를 전송하고 받을수 있다.

단점

이벤트 버스를 너무 남용하게 되면 데이터의 흐름을 파악하기 힘들다(이벤트를 emit 했는데 누가 받고 있는지 실제 소스 코드를 확인하기 전까지는 확인이 어렵다).

대안

데이터 중앙집중식 방식을 채용하고 있는 vuex를 사용한다.

사용처

spinner(페이지 전환시에 로딩 처리)는 이벤트 버스가 좋다고 한다. 이벤트 버스는 훅에 선언하는 것이 좋다.

주의

이벤트 버스를 등록 하는 경우 필히 beforeDestory(destroyed)에 $off를 해주어야 한다. 하지 않으면 무한 증식한다.

Comment and share

1. vue-cli 설치

1
npm install -g vue-cli

2. vue-cli3 설치(최신 버전으로 설치)

1
npm i -g @vue/cli

3. vue-cli3를 통한 프로젝트 생성

1
vue create 프로젝트명

4. 프로젝트 서버 시작

1
npm run serve

5. eslint 끄기

  1. package.json와 동일한 depth에 vue.config.js 만들기
  2. 아래 코드 넣기
    1
    2
    3
    module.exports = {
    lintOnSave: false
    }

6. cli 2.x vs cli 3.x

  1. 명령어

    • 2.x : vue init 프로젝트이름 파일위치
    • 3.x : vue create 프로젝트이름
  2. 웹팩 설정파일

    • 2.x : 노출되어 있다 => 웹팩 설정 가능
    • 3.x : 노출되어 있지 않다 => 웹팩 설정을 위한 다른 방법을 학습하여야 함
  3. 프로젝트 구성

    • 2.x : 깃헙의 템플릿을 다운로드 후 사용하게 됨
    • 3.x : 플러그인 기반으로 기능 추가
  4. ES6 이해도

    • 2.x : 필요없다
    • 3.x : 필요하다

Comment and share

오라클에서는 유저가 생성한 테이블을 한번에 모두 삭제하는 query문이 없습니다. 하나씩 일일이 삭제하는 방법은 매우 귀찮은 작업이기 때문에 조금이지만 더 편한 방법이 있습니다. 유저가 생성한 테이블을 조회해서 DROP TABLE QUERY 문자열로 조합하는 방법입니다.

1
2
3
SELECT  'DROP TABLE ' || object_name || ' CASCADE CONSTRAINTS;'
FROM user_objects
WHERE object_type = 'TABLE';

Comment and share

LOCK걸린 테이블 조회

1
2
3
4
5
6
7
8
9
10
11
SELECT A.SID
, A.SERIAL#
, object_name
, A.SID || ', ' || A.SERIAL# AS KILL_TASK
FROM V$SESSION A
INNER JOIN V$LOCK B
ON A.SID = B.SID
INNER JOIN DBA_OBJECTS C
ON B.ID1 = C.OBJECT_ID
WHERE B.TYPE = 'TM'
AND OBJECT_NAME = '테이블 네임';

LOCK 해제

LOCK 조회한 결과에서 SID와 SERIAL# 번호를 입력 하면 해제가 가능합니다.

1
ALTER SYSTEM KILL SESSION 'SID, SERIAL#';

Comment and share

1. 숫자를 천자리마다 콤마찍기

사용조건

1
2
1. 숫자만 가능
2. 숫자를 변수에 저장 후 가능

소스코드

1
2
var number = 123
var result = number.toLocaleString()

정리

  1. 크롬에서는 작동을 하나 모든 곳에서 통하는 소스코드인지는 아직 확인 불가
  2. 심플하게 만들수 있다는 장점
  3. 숫자값인 문자열로는 사용불가능하고 필히 숫자 타입으로 바꿔줘야 함
  4. 문자열을 대상으로 toLocaleString 메서드를 호출하면 그냥 그대로 나옴
  5. 결과값은 당연하게도 문자열

Comment and share

Moon Star

author.bio


author.job