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

Map 타입

in javascript, es6

1. Map 생성하기

1
2
3
4
5
6
7
let map = new Map();
let map = new Map([
["key1", "value1"],
["key2", "value2"],
["key3", "value3"],
["key4", "value4"],
])

new Map([{id: 1},{id: 2},{id: 3},{id: 4}])이런식으로 작성하려고 하니까 안됨. Map생성시 파라미터는 이차원배열로 넣어야 할듯

2. Map에 값 추가하기

1
2
let map = new Map();
map.set('key', 'value')

값을 추가하는 부분에서 Map와 Object의 차이점이 나타남. Map의 경우에는 Key로 문자열이외에도 모든걸 Key로 가능

3. Map의 값 호출하기

1
2
3
4
let key = {name: 'name'}
let map = new Map();
map.set(key, 'value')
map.get(key)

4. Map의 사이즈 확인

1
2
3
4
5
let key = {name: 'name'}
let map = new Map();
map.set(key, 'value')

console.log(map.size)

5. 배열로 만들기

1
2
3
4
5
6
let map = new Map();
map.set('key1', 'value1')
map.set('key2', 'value2')

let array = [...map]
console.log(map) // map: [['key1', 'value1'], ['key2', 'value2']]

Object와는 다르게 Map은 [...map]을 통해서 Array로 만들수 있습니다. 결과값은 [[key, value], [key, value]]의 형태로 만들어 진다.

6. 순회하기

1
2
3
4
5
6
7
8
9
let map = Map()

for (let [key, value] of map) {

}

map.forEach((key, value) => {

})

Object와는 다르게 forof와 forEach를 사용할수 있다.
Array에서 제공하는 map, filter등에 대한 메서드는 제공하지 않는 것으로 판단된다.

7. Iterator 사용하기

1
2
3
4
5
let map = new Map()

map.keys() // key모음
map.values() // value모음
map.entries() // key와 value 한쌍의 모음

Iterator이다 보니 for문과 함께 사용하면 좋을 듯

기타 메서드

has : 해당 Key 존재 여부

1
2
3
4
let map = new Map()
map.set('key', 'value')

console.log(map.has('key')) // true

delete : 해당 Key의 값 삭제

1
2
3
4
5
let map = new Map()
map.set('key', 'value')
console.log(map.delete('key')) // true => 존재했으면 true 없었으면 false

console.log(map.has('key')) // false

clear : 모든 데이터 삭제

1
2
let map = new Map()
map.clear()

Comment and share

JSON파일 출력(파일 작성)

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void writeJSON() {
String jsonString = "json파일에 저장할 내용",
path = "C:/json/",
fileName = "jsonTest.json";

FileWriter fileWriter = null;

File dirFile = new File(path),
fullFile = new File(path + fileName);

if (!dirFile.exists()) {
dirFile.mkdirs();
}

try {
fileWriter = new FileWriter(fullFile);
fileWriter.write(jsonString);
fileWriter.flush();
} catch(Exception e) {

} finally {
fileWriter.close();
}
}

설명

json파일을 작성하는 것은 기타 다른 파일을 작성하는 것과 별반 차이가 없다. 예제에서는 일반 string문자열을 저장하고 있지만 실제 JSON 데이터를 저장할 때도 JSONData를 JSONString으로 변환후에 저장하면 된다.

JSON파일 호출

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
import com.google.gson.Gson;

public void readJSON() {
String path = "C:/json/",
fileName = "jsonTest.json";

JsonParser jsonParser = new JsonParser();

FileReader fileReader = null;

File file = new File(path + fileName);

if (file.isFile()) {
throw new Exception("존재하지 않는 파일입니다.");
}

try {
fileReader = new FileReader(file); // 1. FileReader 인스턴스를 생성합니다.

Object resultObj = gson.fromJson(fileReader, Object.class); // gson의 fromJson메서드의 첫번째 파라미터로는 Reader를 두번째 파라미터로는 Json 데이터 타입을 넘겨주면 된다.

// Map map = (Map) gson.fromJson(fileReader, Object.class); // 타입을 Object로 하고 casting 가능
// Map map = gson.fromJson(fileReader, Map.class); // 타입을 바로 Map으로 해서 casting 없이 사용 가능
} catch(Exception e) {

} finally {
fileReader.close();
}
}

설명

해당 소스코드는 구글에서 제공하는 Gson을 이용해서 parse를 한다. Gson의 fromJson은 첫번째 파라미터로 Reader와 두번째 파라미터로 데이터 타입을 받는다. 두번째 파라미터로 Object.class를 넘겨주니 모든 데이터 타입(원시 타입 포함)의 값을 오류없이 가져올수 있었다.

gson.fromJson(Reader, Type) : JSON 파일로 부터 데이터 가져오기
gson.fromJson(String, type) : JSONString 으로부터 각 데이터 타입으로 변환

Comment and share

1. json 출력

1
2
3
4
5
6
7
8
9
10
11
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", "아이언맨");
map.put("name", "어벤저스");

Gson gson = new Gson();

String path = "C:\\test\\test1.json";

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF-8"));
writer.write(gson.toJson(map, Map.class));
writer.close();

2. json 입력

1
2
3
4
5
6
7
8
9
Gson gson = new Gson();

String path = "C:\\test\\test1.json";

BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));

Map<String, Object> map = gson.fromJson(reader, Map.class);

System.out.println(map);

json의 입력은 Gson을 사용하면 간편하게 처리할수 있다

Comment and share

1. 텍스트 파일 출력

1
2
3
4
5
6
7
8
9
10
String path = "./test.txt",
text = "안녕하세요\n"
+ "어서오세요\n"
+ "인사합니다";

BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(path), "UTF-8"));

writer.write(text);
writer.close();

2. 텍스트 파일 입력

1
2
3
4
5
6
7
8
9
10
11
String path = "./test.txt",
str = "";

StringBuilder sb = new StringBuilder();

BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), "UTF-8"));

while((str = reader.readLine()) != null) {
sb.append(str);
}

FileReader/FileWriter는 인코딩 설정이 불가능 하므로 FileInputStream/FileOuputStream을 쓰자
많은 양의 데이터를 입출력 할시에 Buffer를 쓰는것과 안 쓰는것의 차이가 크다 => 버퍼를 쓰자
InputStream/OutputStream은 단위가 바이트단위라서 한글이 깨지므로 InputStreamReader/OutputStreamReader를 쓰자

Comment and share

Moon Star

author.bio


author.job