데이터 타입
기본형 vs 참조형
우선 기본형으로는 숫자, 문자, null, undefined, 심볼이 있습니다.
여기서 부터 막힌 부분이 ES6에서는 추가된 symbol이라는 것이었습니다.
심볼(Symbol)이란?
심볼 타입은 객체의 프로퍼티 키를 고유하게 설정함으로써 프로퍼티 키의 충돌을 방지하기 위해 사용된다.
그리고 심볼을 프로퍼티 키로 사용하면 얻을 수 있는 또 다른 장점으로는 프로퍼티 은닉이 가능하다.//심볼의 생성 및 활용 var sym1 = Symbol(); var sym2 = Symbol('andy'); var sym3 = Symbol('han'); //자바스크립트에서 객체의 프로퍼티 키는 보통 문자열 값이다. var obj={}; obj.propertyKey1 = 'propertyValue1'; obj['propertyKey2']= 'propertyValue2'; obj[2]= 'propertyValue3'; console.log(obj[2]); //property value3 //문자열 대신 심볼도 프로퍼티 키로 사용될 수 있다. //이 경우 기본적으로 심볼은 고유하기 때문에 심볼을 키로 값는 프로퍼티는 다른 어떤 프로퍼티와도 충돌하지 않음 var obj={}; var key1 = Symbol(); var key2 = Symbol('andy'); var key3 = Symbol('andy'); obj[key1] = 'jaehyeok'; obj[key2] = 'andy'; obj[key3] = 'han'; //충돌이 일어나지 않음 console.log(obj);
//은닉이 가능함 let s1 =Symbol("andy"); var user={[s1]: 'andy', age: 25,}; console.log(Object.keys(user)); //age만 찾음
하지만 Object.getOwnPropertySymbols()메서드를 사용하면 심볼 값을 사용한 프로퍼티 키 목록을 얻을 수 있음.
let s1 =Symbol("andy"); var user={[s1]: 'andy', age: 25,}; console.log(Object.getOwnPropertySymbols(user));//symbol인 키 값을 찾음
symbol을 이용한 enum 생성
자바스크립트에서는 enum을 지원하지 않기 때문에 Object.freeze()와 Symbol을 사용하여 enum을 흉내내어 사용할 수 있음.
여기서 잠시 enum이란🤔 열거하는 타입으로 상수 값 중에서 비슷한 종류들을 묶어두기 위한 용도로 자주 사용//Symbol을 이용한 enum 생성 const day = Object.freeze({ SUNDAY: Symbol('sunday'), MONDAY: Symbol('monday'), TUESDAY: Symbol('thusday'), WEDNESDAY: Symbol('wednesday'), THURSDAY: Symbol('thursday'), FRIDAY: Symbol('friday'), SATURDAY: Symbol('saturday'), }); const TODAY = day.WEDNESDAY; console.log(day);
내장 심볼(Built-in Symbol)
symbol함수를 이용하여 직접 심볼을 생성하고 사용할 수 도 있지만, 특별한 용도로 사용하기 위해 javascript엔지 내에 미리 생성 되어 상수로 존재하고 있는 내장 심볼 들도 존재한다. 이들의 심볼 함수의 프로퍼티로서 존재한다.(symbol 함수도 객체의 일종이기 때문에 프로퍼티를 가질 수 있음)
대표적인 예시가 바로 Symbol.iterator이다. 자바스크립트 엔진은 이 심볼을 키로 갖는 메소드가 정의된 객체를 iterable객체로 인식한다. iterable객체로 인식되는 객체들만 for...of 문법 등을 이용한 반복이 가능하다. symbol.iterator를 키로 값는 메소드를 정의애주는 내장 iterable객체들이 예시로 있다.Array.prototype[Symbol.iterator]; String.prototype[Symbol.iterator]; Map.prototype[Symbol.iterator];
Symbol.for(), Symbol.keyFor() 메서드
Symbol.for()메서드는 인자로 전달받은 문자열을 키로 사용하여 키와 심볼 값의 쌍들이 저장되어 있는 전역 심볼 레지스트리에서 해당 키와 일치하는 심볼 값을 검색할 수 있다.
🤔전역 심볼 레지스터리란?
생성된 심볼 값을 관리하는 저장소
Symbol.for()메서드로 생성된 심볼 값만 관리됨.
❗️여기서 주의할 점은 , 그냥 Symbol()함수를 호출하여 심볼 값을 생성한 경우에는 전역 심볼 레지스트리에 저장되지 않는다.let obj={[Symbol.for("hello")]:"hello"};
//Symbol.for()메소드 var sym1 = Symbol.for('foo'); //create symbol var sym2 = Symbol.for('foo'); //reuse symbol console.log(sym1===sym2); //true //인자로 전달받은 문자열 값을 키로 갖는 심볼을 전역 심볼 레지스터리에서 찾아 반환하고, 탐색에 실패한다면 그 문자열 값을 키로 값는 심볼을 새로 생성하여 전역 심볼 레지스터리에 저장한 뒤 이를 반환한다. //Symbol.keyFor()메서드 var name = Symbol('andy'); var id = Symbol.keyFor(name); console.log(id); //undefined var test = Symbol.for('andy'); var result= Symbol.keyFor(test); console.log(test); //andy //인자로 전달받은 심보을 전역 심볼 레지스터리에서 찾고, 그 심볼의 키를 반환하고, 탐색에 실패하면 undefined를 반환합니다.
for...in문법과 JSON.stringify()메서드에서의 심볼
자바스크립트가 제공하는 for...in문법에서 키가 심볼인 프로퍼티들은 열거되지 않는다.
var obj={}; obj[Symbol('a')] ='a'; obj[Symbol('b')]='b'; obj.c = 'c'; obj['d']='d'; for(var key in obj){ console.log(key); //c, d } //위와 같이 symbol로 이루어진 키값은 열거가 되지 않는다. var sym = Symbol('andy'); var obj ={[sym]: 'andy',han: 'han'}; console.log(JSON.stringify(obj)); // han만 출력 //위와 같이 객체를 JSON으로 만들때도 키가 심볼인 프로퍼티들은 무시된다.
참조형으로는 객체, 배열, 함수, 날짜, 정규표현식, map, weakmap, set, weakset 이 있습니다.
여기서 몰랐던 내용은 정규 표현식, map, weakmap , set, weakset입니다.
정규표현식 이란?
정규 표현식은 문자열에서 특정 문자 조합을 찾기 위한 패턴입니다. 자바스크립트에서는 RegExp()메서드를 사용할수 있습니다.
정규표현식에서 패턴을 만드는 방법은 크게 2가지가 있습니다.//1.)정규표현식 리터럴: 다음과 같이 슬래시로 패턴을 감싸서 작성합니다. var regExp = /a/; //2.)정규 표현식 객체 생성자 var pattern = new regExp('a'); //정규표현식으로 아래와 같은 패턴을 만들어주면, 특정 문자열이 해당 페턴이 맞는지 체크 가능 const phoneNumber = /\d{3}-\d{4}-\d{4}/; var result=phoneNumber.test('0123-123-123'); var result=phoneNumber.test('010-1234-5678'); console.log(result); var regExp = /a./; //정규표현식 중요 메서드 //여기서 .은 하나의 문자 -> .안에는 문자가 와야 하고 반드시 앞에는 a가 있어야 함 var result=regExp.exec("asdasdadasd"); //추출의 기능 var result2 =regExp.test("as"); //boolean 형태로 반환 console.log(result); // "as" console.log(result2);// true
Map 이란?
키와, 값이 쌍으로 이루어진 객체 입니다. 한 map에서의 키는 오직 단 하나만 존재 합니다.
//object 객체 에서 name가져올때 var user ={}; user.name="andy"; user.age= 25; user.job="front-end developer"; console.log(user.name); //map 객체 생성해서 name 가져올때 var userMap = new Map(); userMap.set("이름", "andy"); userMap.set("나이", 25); userMap.set("직업", "frontend-developer"); console.log(userMap.get("이름"));
map을 쓰는 이유:
기존의 object객체 에서는 키 값으로 문자열과 symbol만 들어갈수 있었는데 map객체 같은 경우는 키 값으로 객체와 함수 까지도 표현이 가능
그리고 실질적인 활용에서는 키값으로 value값을 구분할때 has() 라는 내장함수로 확실한 true 또는 false를 반환해주는 기능을 가지고 있어 보지 못하는 데이터를 판별할때 용이하다.//object var GuestArr=[ {name: "A" , city: "SEOUL"}, {name: "B" , city: "SEOUL"}, {name: "C" , city: "SEOUL"}, {name: "D" , city: "JEJU"}, {name: "E" , city: "JEJU"}, {name: "F" , city: "SEOUL"}, ]; var guest={}; GuestArr.forEach(function(item){ if(!guest[item.city]){ guest[item.city] =[]; } guest[item.city].push(item.name); }) console.log(guest); //map var mapGuest = new Map(); GuestArr.forEach(function(item){ if(!mapGuest.has(item.city)){ mapGuest.set(item.city, []); } mapGuest.get(item.city).push(item.name); }) console.log(mapGuest);
map쓸때 주의해야 할 점❗️
객체를 키 값으로 쓸때 객체 그 자체로 키를 받아오려면 기존의 객체에서 참조해야 한다.
var map = new Map(); map.set({a:'a'},{b:'b'}); map.get({a:'a'}) //error //해결 방법은 변수를 하나 생성에서 그 안에 값을 대입시킨다. var key ={a:'a'}; map.set(key, {b:'b'}); map.get(key)
Map의 문제점
var andy = { name: "andy", age: 25, } var han ={ name: "han", age: 25, andy = null; //기존 Object객체는 아래와 같이 참조를 잃을 경우 {name: 'andy"}에는 더 이상 접근할 방법이 없어자바스크립트 엔진의 가비지 컬렉션에 의해 메모리에서 삭제됩니다.하지만 map은 사라지지가 앖습니다. var map = new Map(); map.set(andy,"andy value"); map.set(han, "han value"); andy=null; for(var obj of map.keys()){ console.log(obj); // andy, han 둘 다 출력 }
객체는 참조가 끊어졌지만 이전에 map의 key로 사용되면서
map구조 안에서 잔여물처럼 존재하게됩니다. 이래서 객체 참조의 유실과 동시에 메모리 삭제까지 이루어지는 것을 원하면 weakMap을 써야만 한다.WeakMap이란?
결록적으로 먼저 말하자면 가비지 컬레팅이 잘된다. 즉 메모리에서 빨리 빨리 정리가 된다.
var weakmap= new WeakMap(); weakmap.set(andy,"andy value"); weakmap.set(han, "han value"); andy= null; console.log(weakmap.has(andy)); //false
WeakMap의 좋은점
예를 들어 사용자 정보를 나타내는 객체의 2개의 프로퍼티 값이 들어가 있는데 하나를 더 추가하려 한다. 근데 그 하나를 기존의 객체에 바로 추가하지 않고 새로운 객체를 만들어 그곳에 넣어준다.
var userObj={ name: 'andy', age: 25, }; var map= new WeakMap(); map.set(userObj, {job:'frontend-dveloper'});
Set이란?
기존의 배열과 비슷하지만 다른 큰 특징은 중복을 허용하지 않는다는 것
var set = new Set(); set.add(3); set.add(2); set.add(2); console.log(set); //대신 자료형이 다르면 추가가 가능하다. set.add('2');
(기본형 데이터 타입) 변수 선언 및 할당
var a=10; 1. 빈 메모리인 변수 영역에서 식별자 a를 집어넣을 공간을 찾는다. 2. 예:@1001 공간에 식별자로 a를 선언하고 데이터 영역에서 빈공간(@2001)에 데이터 값 10을 넣는다. 3. 변수 영역으로 돌아와 a를 검색하고 그 안에 값으로 10이 위치하고 있는 주소 값을 넣는다.
불변성
var a=10; a=5; 1. 빈 메모리인 변수 영역에서 식별자 a를 집어넣을 공간을 찾는다. 2. 변수 영역 @1001공간에 식별자를 a로 저장하고 데이터 영역에서 빈 공간 @2001에 값으로 10을 지정한다. 3. 변수 영역에서 a를 검색해 값으로 10이 위치하고 있는 주소 값을 넣는다. 4. 데이터 영역에서 5라는 값을 찾는게 값이 없으니 새로운 공간 @2002에 값으로 5를 지정한다. 5. 변수 영역에서 식별자 a가 10을 참조하고 있는 주소값을 5를 참조하는 주소값으로 바꿔준다.
이처럼 불변성이란 값을 한 공간에서 바꿔주는 것이 아니라 있으면 재활용하고, 없으면 새로 만들어 새로운 공간에 저장합니다. 결과 10과 5는 모두 별개의 데이터로써 다른 값으로 변경할 수 없습니다.
❗️ 불변성 여부를 구분 할때의 변경 가능성의 대상은 데이터 영역 메모리
❗️❗️ 불변성을 한줄로 표현하면 데이터가 변경되거나 회손되는 것을 방지하기 위해 한번 만들어진 값은 가비지 컬레팅을 당하지 않는 한 영원히 변하지 않는 것
(참조형 데이터 타입의 할당)
var obj={ name: 'andy', age: 25, }; 1. 변수 영역 빈공간 @1001에 식별자로 obj를 지정한다. 2. 데이터 영역에 값을 저장하려고 보았더니 여러개의 프로퍼티로 이루어져 있어서 이 그룹 내부의 프로퍼티를 저장하기 위해 별도의 변수 영역을 마련하고 그 영역의 주소@3001~?를 @2001에 저장한다. 3.@2001객체의 대한 변수 영역인 @3001, @3002에 각각 이름으로 name, 과 age를 지정한다. 그리고 데이터 영역에 @2002, @2003에 문자열 'andy'와 숫자 25를 저장한다. 4. 그 값들의 주소값을 다시 name 과 age가 있는 내부의 변수 영역에 값으로 저장한다.
기본형 데이터와 다르게 객체 안에 있는 내부 프로퍼티들을 저장하기 위한 별도의 변수 영역이 추가로 존재한다. 그래서 값을 확인할때는 그 객체의 변수영역에 들어가있는 주소 값을 참조해 값을 확인한다.
❗️데이터 영역에 저장된 값은 모두 불변값이다. 그러나 변수에는 다른 값을 얼마든지 대입할 수 있다.//재할당 var obj1={ a:1, b:'bbb', }; obj1.a=2; 이렇게 하였을 경우 위에서 말했듯이 객체는 데이터 영역에서 값이 바뀌는 것이 아니라 객체 내부의 변수 영역에서 값이 바뀌는 것이 주소값을 참조해서 들어가는 것이기때문에 기존의 변수 영역에 주소값은 바뀌지 않음.
변수 복사 비교
var a=10; var b=a; var obj={ a:10 }; var obj2=obj;
1.) 기본형 데이터의 복사 과정: 우선 변수 영역 빈공간 @1001에 식별자로 a를 지정한다. 숫자 10을 데이터 영역에서 검색하고 없으므로 빈 공간 @2001에 저장한 다음 이 주소를 @1001이 넣는다. 이제 여기서 복사를 하면 변수 영역의 빈 공간@1002에 식별자로 b를 지정한다. 이제 식별자 a를 검색해 그 값을 찾아와야 하는데 @1001에 저장된 값인 @2001을 들고 좀 전에 확보해둔 @1002에 값으로 대입.
2.) 참조형 데이터의 복사 과정: 변수 영역의 빈 공간 @1003을 확보해 식별자를 obj로 지정. 데이터 영역의 빈 공간 @2002을 확보하고, 데이터 그룹이 담겨야 하기 때문에 별도의 변수 영역 @3001~?을 확보해 그 주소를 저장. @3001에는 식별자 a를 입려한 다음 a에 대입할 값 10을 데이터 영역에서 검색합니다. 이미 @2001에 저장돼 있으므로 이 주소를 @3001에연결 한다. 이제 여기서 복사를 하면 변수의 영역의 빈 공간 @1004에 식별자로 obj2을 지정한다. 이제 식별자 obj를 검색해(@1003) 그 값인 @2002를 들고 @1004에 대입.
❗️변수를 복사하는 과정은 기본형 데이터와 참조형 데이터 모수 같은 주소를 바라보게 되는점에서 동일변수 복사 이후 값 변경 결과 비교
1.) 기본형은 주소값을 복사하는 과정이 한 번만 이루어지며 주소 값이 바뀐다.
2.) 참조형은 한 단계를 더 거치게 됨으로 주소 값이 바뀌지 않는다.
❗️참조형 데이터 같은 경우 객체 내부의 프로퍼티 값만 변경하였을때 주소 값이 바뀌지 않는 것이지 만약 객체 자체를 변경하였을때는 기본형 데이터와 마찬가지로 주소 값이 바뀐다.
위와 같이 객체 자체를 변경하는 것을 불변 객체라고 함.//불변 객체를 만드는 간단한 방법 var user={ name: 'andy', age: 25, }; var copyUser =function(user,newName){ return{ name: newName, age: user.age, } }; var tom= copyUser(user,"tom"); console.log(user===tom); //false /*위와 같이 copyUser함수가 새로운 객체를 반환하도록 수정*/
다만 위의 코드에서 아쉬운 점은 변경할 필요가 없은 기존 객체의 프로퍼티를 하드코딩 했다는 점이다. 이런 방식보다는 대상 객체의 프로퍼티 개수에 상관 없이 모든 프로퍼티를 복사하는 함수를 만드는 편이 더 좋다.
//기존 정보를 복사해서 새로운 객체를 반환하는 함수(얕은 복사) var copyObject =function(target){ var result={}; for(var prop in target){ result[prop] = target[prop]; } return result; };
얖은 복사 vs 깊은 복사
❗️허나 이의 문제점은 만약 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주소값만 복사한다는 의미이다. 그와 다르게 얖은 복사는 아래 단계의 값만 복사하는 것.
얖은 복사는 객체의 주소값을 복사하는 것이고 깊은 복사는 객체 자체를 복사하는 것입니다.
얖은 복사란 객체를 복사할때 기존값과 복사된 값이 같은 참조를 가리키는 것을 말합니다.
깊은 복사된 객체는 객체 안에 객체가 있을때도 원본과 완전히 끊어진 객체를 말합니다.//객체의 깊은 복사를 수행하는 범용 함수 var deepCopyObject=function(target){ var result={}; if(typeof target ==='object'&&target!==null){ for(var prop in target){ result[prop] = deepCopyObject(target[prop]); } }else{ result=target; } return result; };
//JSON을 활용한 간단한 깊은 복사 var copyobj = function(target){ return JSON.parse(JSON.stringify(target)); };
❗️그러니까 어떤 객체를 복사할 때 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데잍일 경우에는 그대로 복사하면 되지만 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다.
-객체란 변수, 자료구조, 함수, 메서드, 식별자에 의해 참조된 메모리 상의 값 등을 의미합니다.
좀더 쉽게 말하자면 실세계에 존재하거나 생각할 수 있는 것을 말합니다.
-배열이란 연관된 데이터를 모아서 관리하기 위해서 사용되는 참조형 데이터 타입입니다.
-함수란 하나의 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 프로그램 코드의 집합으로 정의할 수 있습니다.
-정규표현식이란 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어입니다.
-map이란 배열 각 요소에 대하여 주어진 함수를 수행한 결과를 모아 새로운 배열을 반환하는 메서드입니다.
둘의 크 차이는 값을 복제 하는 방법에서 방식이 다릅니다.
기본형은 값이 담긴 주소값을 복제합니다.
참조형은 값이 담긴 주소값들을 이루어진 묶음을 가리키는 주소값을 복제한다는점 입니다.
기본형은 불변성을 띕니다.
참조형은 가변성을 띕니다.
변수- 변할수 있는 수를 뜻합니다.
식별자- 어떤 데이터를 식별하는데 사용하는 이름, 즉 변수명을 뜻합니다.
변수와 상수를 구분 짓는 변경 가능성의 대상은 변수 영역
반면 불변성 여부를 구분힐 때의 변경 가능성의 대상은 데이터 영역 메모리 입니다.
var a= 'abc'; //변수 선언 동시에 값을 할당
데이터 할당의 전체 흐름
1. 변수 영역에서 빈 공간(@1003)을 확보한다.
2. 확보한 공간의 식별자를 a로 지정한다.
3. 데이터 영역에서 빈 공간(@5004)에 문자열 'abc'를 저장한다.
4. 변수 영역에서 a라는 식별자를 검색한다(@1003)
5. 앞서 저장한 문자열의 주소(@5004)를 @1003의 공간에 대입한다.
이렇게 변수 영역에 값을 직접 대입하지 않고 데이터 라는 영역을 하나 더 만들어 거치는 이유는 데이터 변환을 자유롭게 할 수 있게 하기 위해서고 메모리를 좀 더 효율적으로 관리하기 위해서입니다.
var obj1 ={
a:1,
b:'bbb'
};
참조형 데이터를 변수에 할당하는 과정
1. 컴퓨터는 우선 변수 영역의 빈 공간(@1002)를 확보하고, 그 주소의 이름을 obj1로 지정한다.
2. 임의의 데이터 저장 공간(@5001)에 데이터를 저장하려고 보니 여러 개의 프로퍼티로 이루어진 데이터 그룹입니다. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고, 그 영역의 주소(@7103~?)를 @5001에 저장합니다.
3. @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정합니다.
4. 데이터 영역에서 숫자 1을 검색합니다. 검색 결과가 없으므로 임의로 @5003에 저장하고, 이 주소를 @7103에 저장합니다. 문자열 'bbb'역시 임의로 @5004에 저장하고, 이 주소를 @7104에 저장합니다.
기본형 데이터와의 차이는 객체의 변수(프로파티)영역이 별도로 존재한다는 점입니다.
var obj ={
x=3,
arr:[3,4,5]
};
중첩된 참조형 데이터(객체)의 프로퍼티 할당
1. 컴퓨터는 우선 변수 영역의 빈 공간(@1002)를 확보하고, 그 주소의 이름을 obj로 한다.
2. 임의의 데이터 저장공간(@5001)에 데이터를 저장하려는데, 이 데이터는 연러 개의 변수와 값들을 모아놓은 그룹(객체) 입니다. 이 그룹의 각 변수(프로퍼티)들을 저장히기ㅣ 위해 별도의 변수 영역을 마련하고 (@7103~?), 그 영역의 주소를 @5001에 저장합니다.
3. @7103에 이름 x를, @7104에 이름 arr를 지정합니다.
4. 데이터 영역에서 숫자 3을 검색합니다. 없으므로 임의로 @5002에 저장하고 이 주소를 @7013에 저장합니다.
5. @7104에 저장할 값은 배열로서 역시 데이터 그룹입니다. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고(@8104~?), 그 영역의 주소 정보(@8104~?)를 @5003에 저장한 다음, @5003을 @7104에 저장합니다.
6. 배열의 요소가 총 3개이므로 3개의 변수 공간을 확보하고 각각 인덱스를 부여합니다(0,1,2)
7. 데이터 영역에서 숫자 3을 검색해서(@5002) 그 주소를 @6104에 저장합니다.
8. 데이터 영역에서 숫자 4가 없으므로 @5004에 저장하고, 이 주소를 @8105에 저장합니다.
9. 데이터 영역에서 숫자 5가 없으므로 @5005에 저장하고, 이 주소를 @8106에 저장합니다.
obj.arr ='str';
위와 같은 코드에서 다음와 같이 재할당을 내리면 데이터 영역에 문자열 'str'이 저장되므로 그 주소값이 변수에 넣어짐으로써 기존에 자신의 주소를 참조하는 변수가 하나도 없게 됩니다.
어떤 데이터의 대해 자신의 주소를 참조하고 있는 변수의 개수를 참조 카운트라고 합니다.
얖은 복사는 객체의 주소값을 복사하는 것이고 깊은 복사는 객체 자체를 복사하는 것입니다.
얖은 복사란 객체를 복사할때 기존값과 복사된 값이 같은 참조를 가리키는 것을 말합니다.
깊은 복사된 객체는 객체 안에 객체가 있을때도 원본과 완전히 끊어진 객체를 말합니다.
undefined:값을 대입하지 않은 변수, 객체 내부의 존재하지 않는 프로퍼티에 접근하려 할때, return 문이 없거나 호출되지 않는 함수의 실행 결과