javascript prototype에 대하여

열심히 prototype에 대해 공부하고 있긴 합니다만, 아직은 현재진행형인 것 같습니다.

공부하면서 가장 크게 느낀 것은, javascript의 강력을 넘어 위험해 보이는 수준의 유연성입니다.

이 언어를 어떻게 사용해야할지 정말 걱정도 되기도 합니다만 한편으로 기대도 됩니다.

아직 부족하기 합니다만, 지금까지 공부했던 부분들을 리뷰해 볼까 합니다.


순서

  1. prototype 넌 누구냐 정체를 밝혀라

  2. 상속이라 부르는 행위에 대해


prototype 넌 누구냐

prototype의 뜻은 원형입니다.

왜냐하면 자바스크립트에서 함수를 생성하면 동시에 생성되는 프로토타입을 new 키워드로 함수에서 찍어낸

모든 객체들이 이 프로토타입을 참조하기 때문입니다.

사실 어느정도 이해는 하긴 합니다만, 차근차근 어떻게 이런 말이 나왔는지 따져가며 공부해 볼까 합니다.

자바스크립트가 프로토타입 기반으로 세워진 언어이고, 앞으로 자바스크립트를 주언어로 사용할 것이라면

적어도 자기가 어디에 서있는지 정도는 알아야 한다고 생각해요.

그러면 prototype의 생성이 어떤식으로 이루어지는지부터 시작해 봐야겠네요.

먼저 함수를 생성하면 단순히 함수객체만 생성되는 것이 아니라 prototype이라는 객체가 동시에 생성이 됩니다.

그 형태는 아래 그림과 같습니다.

function

먼저 함수를 선언하면 위와 같은 구조의 2개의 객체가 생성이 됩니다.

하나는 function 객체이고, 다른 하나는 prototype 객체입니다.

단순히 2개의 객체가 생성된 것만 아니라 두 객체는 서로 이어져 있는데

함수에서는 프로토타입 객체에 prototype이라는 내부변수로 접근 할 수 있고,

프로토타입에서는 constructor라는 변수로 함수에 접근 할 수 있게 됩니다.

서로를 참조하는 레퍼런스 변수를 통해 두 객체는 접근 뿐만 아니라 변경도 가능합니다.


function Foo (){}
Foo.prototype.proto_val = '원형 값';

Foo.prototype.constructor.construct_val = "생성자 값";

console.log(Foo.prototype.proto_val); //원형 값 을 출력
console.log(Foo.construct_val); //생성자 값 을 출력
console.log(Foo.proto_val); //?! undefined를 출력

위의 예제외 같이 프로토타입에 새로운 속성을 추가할 수도 있고,

반대로 프로토타입에 접근하여 constructor 참조변수로 함수에 접근하여(함수도 객체임으로)

속성을 추가할 수도 있습니다.

하지만 맨마지막, Foo에서 직접 프로토타입에 설정한 속성에 접근하려고 하니까 접근할 수 없어

'undefined’로 출력 된 것을 볼 수 있습니다.

이 것으로 보아, 함수는 프로토타입을 생성하지만, 함수 스스로가 프로토타입으로부터 값을 얻지 못함을

알 수 있습니다.

설정은 해놓고 쓸수 없다면 의미가 없죠.

그렇다면 프로토타입이 원형 이라는 자신의 이름값을 하기 위해서는 다른 방식접근해야 하는데

그 방법은 바로 인스턴스를 생성하는 것입니다.

instance

new 키워드를 사용하여 함수로 인스턴스를 찍어낼 경우 어떤 일이 발생하는지는 다음 과 같습니다.


function Foo(){
    //var this = {}; this가 생성이 됩니다.  
    //이 this는 dunder proto라는(__proto__)라는 참조변수로 프로토타입 객체를 참조합니다.
    //return this; this가 반환됩니다.
}
var foo_instance = new Foo();

위의 코드와 같이, new를 할 경우 this 객체가 생성되며, 또한 반환되어 foo_instance에서 해당 객체를 사용할 수

있게 됩니다.

주목해야 할점은 this가 가지고 있는 내부 변수인 proto(Dunder Proto 라고 불립니다.) 가 무엇을 가르키고

있느냐입니다.

이 this는 Foo의 프로토타입을 가르키게 되고, Foo 함수객체와는 다르게 Foo의 함수에 직접 접근 할 수 있는

권한을 가지게 됩니다.


function Foo (){} //함수 선언
Foo.prototype.proto_val = "접근 할 수있다!!"; //Foo의 프로토타입을 설정

var foo_instance = new Foo();  //Foo의 인스턴스를 생성

console.log(foo_instance.proto_val); //접근 할 수있다!! 를 출력

하지만 접근할 수있다는 의미가 반드시 직접적으로 프로토타입에 접근 할 수 있다는 의미는 아닙니다.

제가 점선으로 인스턴스와 프로토타입을 연결한 이유인데, 접근 할 수 있다는 의미보다는

프로토타입에 있는 값을 고스란희 복제해서 가져온다는 의미가 적절해 보입니다.

아래 코드를 보면서 설명하자면


function Foo(){}

Foo.prototype.proto_val = 100;    //프로토타입 속성을 설정합니다.

var foo_instance = new Foo();  //Foo의 인스턴스를 생성합니다.

console.log(foo_instance.proto_val); //프로토타입값 100을 출력을 합니다.

foo_instance.proto_val -= 1;  // 인스턴스에서 proto_val을 1줄여봅니다.

console.log(foo_instance.__proto__.proto_val); //프로토타입의 값은 그대로입니다.
console.log(foo_instance.proto_val);  //하지만 인스턴스는 1이 줄은 99가 출력이 됩니다.

프로토타입 속성 proto_val을 선언하고 100을 저장한 이후에 Foo로 인스턴스를 만들어내면

해당 인스턴스에서 Foo의 prototype에 있는 값을 사용 할 수 있게 됩니다.

그렇지만 prototype에 있는 속성을 직접 바꿀 수 있는 것은 아닙니다.

foo_instance에서 proto_val을 1줄였는데

아래 출력 값에 보면 프로토타입에 있는 값은 그대로인데

foo_instance에서 접근한 proto_val은 99가되어 있는 것을 보실 수 있습니다.

즉, prototype이 왜 원형이란 의미를 가지는지가 바로 여기서 나오는 것 같습니다.

비록 prototype에서 값을 가져오지만 어디까지나 프로토타입은 인스턴스를 찍어낼 때 참조 가능한 기본값을 가지고

각각의 인스턴스는 프로토타입으로 받은 값을 개별로 사용이 가능하게 된다는 것입니다.

그래고 한가지 더 참고를 하자면 언제 프로토타입에 있는 값을 인스턴스가 자신의 메모리로 가져오냐 입니다.


function Foo(){}

Foo.prototype.proto_val = 100;

var foo_instance = new Foo();

console.log(foo_instance.proto_val === Foo.prototype.proto_val); //true를 출력합니다.

foo_instance.proto_val = foo_instance.prototype.proto_val -1;

console.log(foo_instance.proto_val === Foo.prototype.proto_val); //false를 출력합니다.

처음에는 인스턴스에서 호출한 proto_val과 프로토타입의 proto_val값이 같다고 나옵니다.

하지만 인스턴스의 proto_val을 1 줄이자 이번에는 똑같은 표현이 false로 나옵니다.

이를 통해 어떤 방식으로 프로토타입과 객체의 메모리가 관리가 되어지고 참조가 되어지는지 유추 할 수 있습니다.

프로토타입의 속성을 인스턴스에서 접근만 한다면 프로토타입에 값이 있는지 확인한 뒤에 참조만 하지만

인스턴스에서 프로토타입 속성으로 연산을 하려고 할 경우, 인스턴스에 메모리가 주어지고, 프로토타입의 속성을

호출하여 받은 값을 연산하여 할당된 메모리에 저장이 되어집니다.

이렇게 할당받은 메모리로 인스턴스는 고유의 속성을 가지게 되는겁니다.

간단하게 말해, 인스턴스에 메모리가 프로토타입으로부터 주어지는 경우는 인스턴스에서 프로토타입의 속성을 변경하려고

할 때라고 말할 수도 있을 것 같습니다.

물론 저 표현이 애매하다고 생각 할 수도 있을 것 같습니다. 왜냐하면 인스턴스 자체에서도 프로토타입에 직접 접근하여

프로토타입에 있는 기본값을 instance에서 바꿔버릴 수도 있기 때문입니다.


function Foo(){}

Foo.prototype.proto_val = 100;

var foo_instance = new Foo();

console.log(foo_instance.proto_val); //100을 출력합니다.

foo_instance.proto_val -= 1; //1을 빼봅니다.

foo_instance.__proto__.proto_val = 50; // 원형 값을 바꿀 수 있습니다.

var foo_instance2 = new Foo();  //바꾼 뒤에 Foo로 부터 생성된 객체는 다른 기본 값을 가지게 됩니다.

console.log(foo_instance2.proto_val);  // 50을 출력합니다.

console.log(foo_instance.proto_val); //여전히 99를 출력합니다. 

foo_instance.proto.proto_val을 50으로 설정하자 다음 생성된 객체 foo_instance2.proto_val이 50을 가르키는 것을

볼 수 있습니다.

객체가 자신의 기본값을 바꿔버린거죠.

어찌보면 편리하다고 생각할 수 있는 기능일 수도 있겠으나 한번더 생각해 보면 무시무시한 기능 같습니다.

다른 언어 같은 경우 class를 걸어 넣고 객체를 생성할 경우 객체에서는 클래스를 건드릴 수 없습니다.

그렇기 때문에 각각의 객체들이 원형이 변경될 위험으로부터 보호받을 수 있지만

자바스크립트에서는 생성자인 함수뿐만 아니라 생성자로부터 만들어진 객체까지 원형에 손을 댈 수있고,

그 과정이 컴파일 과정도 아니고 런타임에서 일어난다는 것은 매우 위험해 보입니다.

그래서 더욱 자바스크립트의 프로토타입 개념을 사용 할 때는 조심을 기해야 겠다는 생각이 듭니다.


상속이라 부르는 행위에 대해

사실 자바스크립트의 프로토타입 개념을 통해 상속을 한다는 언에에 대해 거부감을 가지고 있습니다.

제가아는 상속이라는 개념과는 약간 거리가 있기 때문입니다.

상속이라 하면 부모가 자식에게 재산, 혹은 기술등 물질적, 혹은 무형의 재산을 물려주는 것으로 생각합니다.

자바같은 언에서는 그게 통했습니다. 클래스라는 어머니가 있으면 자식(객체들이) 어머니의 능력혹은 속성을 고스란히

물러받는거죠.

자바스크립트도 그런 행위가 가능합니다만, 제 눈에 보이는 것은 많이 달라 보입니다.

가장 달라보이는 부분은 바로 객체도, 생성자도 모두 프로토타입에 접근 할 수 있으며, 접근 뿐만 아니라 변경까지도

런타임중에 실행 할 수있다니,

부모와 자식의 상속이라는 개념으로 따지자면, 자식이 자기가 상속받을 물건들을

자기맘대로 바꿔놓고는 자신이 상속받았다고 주장하는 것은 매우 이상해 보입니다.

그래서 제목을 상속이라 부르는 행위라고 해봤습니다.

사실 다른 언어를 하던 사람이 이 자바스크립트의 이상한 행동(나쁘게 말하자면 폐륜으로 보이는???) 에 대해

이해하기 위해 어쩔수 없이 상속이라는 말을 가져 온게 아닌가 합니다.

제 눈에는 상속이라는 단어보다는 프로토타입의 공유라는 표현이 적절해 보입니다.

프로토타입을 참조하는 변수를 가진 모든 객체가 값을 변경하거나 가져올 수 있다면, 그건 상속이라기보다는

프로토타입에 있는 자원을 공유한다고 보는게 더 이해하기 쉬워 보입니다.

지금까지 제 사견이었습니다.

그래도 상속이 일반적으로 쓰이는 단어이니 이 곳에서는 상속이라는 단어를 쓰겠습니다.

자바스크립트의 상속은 우리가 모르는 사이에 이미 우리가 사용하고 있습니다.

inheritance

우리는 함수를 선언한 순간 바로 상속을 사용하게 됩니다.

그림을 보면 Foo.prototype이 Object.prototype을 레프런스 변수로 참조하고 있는 것이 보이는데,

상속은 Foo.prototype이__proto 를 통해 다른 프로토타입을 참조할 수

있다는 사실에 기반하여 가능해집니다.

여기서 프로토타입 체인이라는 말이 나오는데

단순하게 말해서 프로토타입이 다른 프로토타입을 참조하고, 그 참조된 프로토타입도 또한 다른 프로토타입을 참조하는

것입니다.

Object.prototype.say = function(){
    console.log(this.greet); //Object의 프로토타입에 say라는 메서드를 추가합니다.
}

function Foo (){
    this.greet = "hello world";    // Foo 생성자에서 greet이라는 속성을 추가합니다.
}

var foo_instance = new Foo();     //인스턴스를 생성합니다.

foo_instance.say(); //hello world를 출력합니다.

위의 코드에서 Foo의 프로토타입은 이미 Object의 프로토타입을 상속받고 있기 때문에 Foo의 인스턴스에서

say 메서드를 사용하여 불러오는 것이 가능해집니다.

say를 호출하는 과정은,

  1. 인스턴스 내에 say가 있는지 확인한다.

  2. 없다면 인스턴스가 참조하는 프로토타입에 say가 있는지 확인한다.

  3. 없다면 인스턴스가 참조하는 프로토타입이 참조하는 프로토타입에 say가 있는지 확인한다.

  4. 이 과정을 반복하되, null이 나올 경우 중단하며, say에 undefined를 할당한다.

  5. 찾을 경우 say를 참조하여 호출한다.

이런 식으로 프로토타입의 체인을 거슬러 올라가며 속성이나 메서드를 찾아갑니다.

위의 예제의 경우 함수를 선언할경우 기본적으로 형성되는 구조입니다.

Object 뿐만 아니라 다른 객체끼리도 상속을 할 수 있습니다.

function Vehicle (){}

Vehicle.prototype.wheels = 4;
Vehicle.prototype.getWheels = function(){ //Vehicle의 프로토타입의 속성을 설정합니다.
    console.log(this.wheels);
}

function Bicycle(){
    this.wheels = 2;
}

Bicycle.prototype.__proto__ = Vehicle.prototype; //Bicycle의 프로토타입이  Vehicle의 프로토타입을 참조하게 합니다.

var bicycle = new Bicycle(); //객체를 생성합니다. 

bicycle.getWheels();  // 2를 출력합니다.

bicylce의 내부에 getWheel이라는 메서드가 없었음에도 불구하고 Vehicle 프로토타입의 getWheel을 참조하여

작동 할 수 있었습니다.

위 코드의 구성을 그림으로 표현하자면 아래와 같습니다.

inheritance2

Vehicle의 프로토타입이 Object의 프로토타입을 참조하고 있고,

Bicycle의 프로토타입이 Vehicle의 프로토타입을 참조하고 있습니다.

그리고 마지막으로 Bycicle의 인스턴스는 Bicycle의 프로토타입을 참조하고 있는데,

인스턴스가 무언가를 호출 할경우, 찾거나 혹은 null이 나올때까지 연결된 프로토타입을 따라 올라갑니다.

그러다가 vehicle에서 원하는 것을 찾았으니 참조합니다.

분명히 그렇게 어려운 개념은 아니었던 것 같은데 왜 그렇게 헤맸는지 모르겠습니다.

제가 헷갈려 했던 이유는 인스턴스가 반드시 자신을 찍어낸 함수의 프로토타입만을 참조한다는 고정관념때문이었습니다.

재미있고, 한편으로 무섭게도 인스턴스가 자신이 상속받을 프로토타입 체인을 선택가능합니다.

function LineA1(){} //A 프로토타입 체인입니다.
function LineA2(){}

LineA1.prototype.minusOne = function(){ //LineA1 프로토타입에 minusOne을 메서드를 추가합니다.
    console.log(--this.val);
}
LineA2.prototype.__proto__ = LineA1.prototype; //LineA2가 LineA1으로부터 상속받습니다. 

function LineB1(){} //B 프로토타입 체인입니다.
function LineB2(){}

LineB1.prototype.plusTwo = function(){ //LineB1 프로토타입에 plusOne을 메서드를 추가합니다.
    console.log(this.val+=2);
}
LineB2.prototype.__proto__ = LineB1.prototype; //LineB2가 LineB1으로부터 상속받습니다. 

var outsider = { //전혀 관계없어 보이는 객체를 만듭니다.
    val:10
}

outsider.__proto__ = LineA2.prototype; //outsider의 __proto__를 LineA2 프로토타입에 연결합니다.

outsider.minusOne(); //10보다 하나 줄은 9를 출력합니다.

outsider.__proto__ = LineB2.prototype; //outsider의 __proto__를 LineB2 프로토타입에 연결합니다.

outsider.plusTwo(); //9에서 2를 더한 11을 출력합니다.

LineA라는 프로토타입 체인이 있고, LineB라는 또다른 프로토타입 체인이 있는데

어디서 굴러온지 모를 객체가 __proto로 LineA 또는 LineB의 프로토타입을 참조하자 해당 프로토타입 체인을 탈 수 있게 되었습니다.

그래서 LineA의 프로토타입 체인을 탔을 경우 minusOne이라는 메서드를 이용해 9를 출력하고,

LineB의 프로토타입 체인을 탔을 경우 plusTwo라는 메서드를 통해 11을 출력할 수 있었습니다.

생성자로 인스턴스를 찍어내어도, 그 인스턴스는 반드시 생성자의 프로토타입을 참조할 필요가 없이

어떤 프로토타입체인이든 갈아 탈 수가 있다는점은 자바스크립트의 위험한 유연성을 보여주는 것 같습니다.

그래서 책을 찾아보니 객체 스스로가 프로토타입을 바꿔 타는 행위는 삼가하라고 되어 있더군요.


결론

글을 쓰다보니 개발 새발로 쓰고 있다는게 느껴져서, 사실 쓸 글은 더 많은데 여기서 끊고 다른 것으로 넘어갈까

합니다. 글을 너무 못써서 안타깝네요 ㅠㅠ

여하튼, 여러모로 공부가 많이 되었던 것 같습니다.

분명히 강의 들으며 공부할 때는 알았던 것 같은데, 프로토타입은 직접 부딪쳐 보고 나서야

그 유연성에 감탄하게 되는 것 같습니다.

공부할때 도와 주신 @ho1234c 감사드리며

앞으로도 공부 정진하겠습니다.


5개의 좋아요

재미있게 읽으셨다니 다행인 것 같습니다. 사실 글을 써가며 배우고 다시쓰는, 정리라고는 하나도 안된 글이라

걱정이 많았는데 그렇게 말씀해 주시니 감사해요!

프로토타입 부분은 아직도 공부 할 것이 많아 보입니다. ㅠㅠ

부족한 글이지만 누군가에게 도움이 됬다니 다행이네요.

1개의 좋아요
function Foo(){}

Foo.prototype.proto_val = 100;    //프로토타입 속성을 설정합니다.

var foo_instance = new Foo();  //Foo의 인스턴스를 생성합니다.

console.log(foo_instance.proto_val); //프로토타입값 100을 출력을 합니다.
console.log(foo_instance.hasOwnProperty('proto_val'));// 인스턴스 내부에 proto_val이라는 속성을 가지고 있는지 확인 prototype체인 순회를 안하는것이 특징
//----------------------------------
foo_instance.proto_val -= 1;  // 인스턴스에서 proto_val을 1줄여봅니다. 
//위표현식은
foo_instance.proto_val = foo_instance.proto_val - 1;// 과 같음
//----------------------------------
console.log(foo_instance.hasOwnProperty('proto_val')); // true 인스턴스 내부에 proto_val이라는 프로퍼티가 할당되었기떄문에 더 이상 프로토타입 체인을 순회하지않습니다.
console.log(foo_instance.proto_val); // 98
console.log(foo_instance.__proto__.proto_val); //프로토 타입 프로퍼티는 존재하지만 foo_instance 내부에 proto_val이라는 동일이름의 프로퍼티가 존재하기때문에 인스턴스 변수로 호출시 인스턴스의 프로퍼티값을 가져옵니다..

// delete 연산자를 이용해서 객체에 foo_instance.proto_val이라는 프로퍼티를 제거 인스턴스 내부에 proto_val이라는 프로퍼티를 가지고있는지 확인
delete foo_instance.proto_val;
console.log(foo_instance.hasOwnProperty('proto_val')); //false 인스턴스내부에 프로퍼티가 지워진걸 알 수 있습니다.
console.log(--foo_instance.proto_val);  // 전위 감소 연산자 사용
console.log(foo_instance.__proto__.proto_val); // 결과값 100
console.log(foo_instance.proto_val); // 99
console.log(foo_instance.hasOwnProperty('proto_val')); //true 전위 연산자를 통해서 다시 proto_val이라는 프로퍼티가 인스턴스에 할당됨.

읽기 좋은글이지만 잘못된 내용이 있습니다.

“만약 인스턴스에서 속성을 변경을 하려고 할 시에는 인스턴스에 메모리를 할당하되 프로토타입의 기본값을 주어 할당해줍니다.”

자바스크립트 객체에서 위와같은 동작은 존재하지않습니다.
전위 증감연산자나 복합연산자는 단순히 값을 뺴는것이 아닌
프로퍼티가 할당안된 상태에서의 proto_val 프로퍼티 호출이기떄문에 prototype의 프로퍼티들을 순회하게되고 순회하면서 찾은 프로토타입 프로퍼티의 값을 인스턴스의 프로퍼티로 할당하는 과정이기때문에
저런결과가 나오는거라고 생각됩니다.

좋은글써주셔서 감사합니다!

1개의 좋아요

답변 감사합니다.

그런데, 어떤 점에서 제가 부족했는지 잘 이해가 안갑니다.

function Foo(){}

Foo.prototype.val = 100;
Foo.prototype.val2 = "hello";
Foo.prototype.val3 = function(){}

var foo_instance = new Foo();

console.log(foo_instance.hasOwnProperty('val')); //false
console.log(foo_instance.hasOwnProperty('val2'));  //false
console.log(foo_instance.hasOwnProperty('val3'));  //false

foo_instance.val--;
foo_instance.val2 = "world";
foo_instance.val3 = function(){}

console.log(foo_instance.hasOwnProperty('val')); //true
console.log(foo_instance.hasOwnProperty('val2'));  //true
console.log(foo_instance.hasOwnProperty('val3'));  //true

단순히 증감 연산 뿐만 아니라 다른 타입들의 대입연산도

오버라이팅이 일어나기전까지는 인스턴스내에 프로펄티가 존재하지 않습니다.

그리고 오버라이팅이 발생한 뒤에서야 그제서야 인스턴스의 프로펄티로 확인이 됩니다.

그렇다면 메모리가 오버라이팅 할 때 할당된다는 얘기가 되는데,

제가 빼먹은 부분은 프로토타입 체인을 순회하여 속성을 찾는다는 점인가요?

어떤 부분에서

제가 잘못 알고 있는지 잘 모르겠습니다.

인스턴스에 새프로퍼티를 할당할때 프로토타입의 기본값을 부여된다고 하셧던부분을 말씀드린건데 설명이 뭔가조금부족햇나보네용 죄송합니다. 인스턴스에 프로퍼티에 어떤값을 할당할때에는 프로토타입에 있는 같은명의 프로퍼티와는 아무런관계가 없다는걸 설명드리고 싶었습니다.

1개의 좋아요

이제야 이해가 갔습니다. 제가 이해를 잘 못하고 있었네요.

foo_instance.proto_val -= 1; //제가 사용한 표현

foo_instance.proto_val = foo_instance.proto_val - 1; //@tawon2137님이 쓰신 표현 

이 기능상 같기는 하지만, 제 표현이 좀 애매했던 것 같습니다.

저도 제 표현때문에 헷갈렸네요.

그러니까, 프로토타입의 속성을 가져와서 바꾸는 것이 아니라

프로토타입값을 호출해 변경한 값을 새로운 proto_val이라는 변수에 넣는다는 말씀을 하시고 싶으셨던 건가요?

그러면 분명 foo_instance의 proto_val과 foo_instance.protoptype.proto_val과 전혀 다른 변수인듯 합니다.

덕분에 배워가네요.

1개의 좋아요

좋은 정보 잘 보고 갑니다
자바스크립트 프로토타입은 입양에서 매우 Free하군요
마을의 모든 어른들이 다 부모님이나 마찬가지겠군요
엄마,아빠가 집 안에 없어서 자신을 지원해주지 못하면 톰 아저씨에게 도움을 요청하고 톰 아저씨도 도움을 줄 상황이 안되면 안나 아줌마에게 가서 도움을 요청했는데 안나 아줌마 집에 가서 도움을 요청하고 안나 아줌마가 도움을 줄 여건이 되면 그 자리에서 부모가 되는군요

글에서 정성스러움이 느껴져서 비교적 쉽게 대략적으로 이해하게 되었습니다
감사합니다