자바스크립트 코딩을 하던 중 제 머리를 아프게 했던 문제가 있었습니다.
약 30분 가량을 이 문제로 씨름하다가 단 한줄로 문제를 해결했습니다.
자바사크립트로 oop를 구현하는게 일반적인 요즘, 자칫 간과하기 쉬운 객체 초기화의 중요성을 다시 한 번 상기시켜 준 문제였기에 공유합니다.
제가 설계한 객체는 기본적으로 자바스크립트의 배열을 사용하고 배열을 조작할 몇 가지 부가 기능을 첨가한 단순한 객체였습니다.
function TemporaryBasket(){
this.basket = [];
}
TemporaryBasket.prototype.add = function(){
var temp = [];
for(var i=0,max=arguments.length; i
this.basket.push(arguments[i]);
}
return this.size();
}
TemporaryBasket.prototype.remove = function(obj){
for(var idx in this.basket){
if(this.basket[idx] == obj){
this.basket.splice(idx,1);
}
}
}
TemporaryBasket.prototype.removeAt = function(idx){
return this.basket.splice(idx,1);
}
(이하 메서드 생략)
그리고 이 TemporaryBasket를 상속받고 몇몇 메서드들을 오버라이드하는 SelectedItemBasket 이라는 객체가 있습니다.
function SelectedItemBasket(){}
SelectedItemBasket.prototype = new TemporaryBasket;
SelectedItemBasket.prototype.compareSequence = function(a,b){
return a.sequence - b.sequence;
}
SelectedItemBasket.prototype.add = function(obj){
var cnt = TemporaryBasket.prototype.add.call(this,obj);
Consol.print("add> selected itmes: " + this.size());
Consol.print('board type: ' + obj.fldtype);
this.basket.sort(this.compareSequence);
return cnt;
}
SelectedItemBasket.prototype.remove = function(obj){
var removedItem = TemporaryBasket.prototype.remove.call(this,obj);
Consol.print("delete> selected itmes: " + this.size());
this.basket.sort(this.compareSequence);
return removedItem;
}
(이하 메서드 생략)
정말 단순한 객체죠?
하지만 그 단순함에 속아 커다란 문제점을 놓치고 말았습니다.
어떤 페이지에 SelectedItemBasket객체의 인스턴스를 두 개 이상 생성하였는데 문제가 발생하였습니다.
인스턴스가 한 개 일땐 전혀 일어나지 않던 문제였기에 무척이나 당황스러웠죠;;
문제는, 각 인스턴스가 자신의 속성으로 갖고 있는 Array객체인 basket 을 공유하는 것이었습니다.
분명 독립적으로 생성된 서로 다른 인스턴스지만 이상하게도 basket을 공유해서 한 쪽에서 add() 메서드를 호출하면 다른 쪽 basket에도 항목이 추가되어 결국 두 개의 인스턴스가 동일한 basket을 갖게 되었습니다.
결론부터 말하자면 문제의 원인은 SelectedItemBasket의 생성자 함수 였습니다.
function SelectedItemBasket(){}
prototype을 TemporaryBasket의 인스턴스로 하였기 때문에 당연히 초기화는 필요 없으리라 생각했는데 오산이었습니다.
SelectedItemBasket.prototype = new TemporaryBasket;
위와 같이 SelectedItemBasket 생성자 함수 아래에 prototype을 지정하였지만 이 코드는 페이지 로딩시 단 한 번 실행될 뿐입니다.
SelectedItemBasket 객체를 생성할때마다 호출 되는 것이 아니기 때문에 SelectedItemBasket 객체를 생성할 때마다 처음 생성된 TemporaryBasket 객체를 그대로 쓰게 됩니다. (개인적인 추측입니다;;;)
때문에 SelectedItemBasket의 생성자 함수에서 초기화 하는 작업이 필요합니다.
function SelectedItemBasket(){
this.constructor();
}
물론 위 코드는 TemporaryBasket와 SelectedItemBasket 의 생성자 함수가 동일 하기 때문에 SelectedItemBasket만의 초기화 코드가 없습니다. 그저 TemporaryBasket의 생성자 함수를 빌려 쓰면 되는 것이지요.
'초기화'의 부재로 인해서 일어난 문제였던 만큼 다른 코드들을 건드리지 않고 쉽게 마무리가 되었습니다만, '초기화'의 중요성을 다시 한번 느끼게 되었습니다^^;; (C언어 책을 다시 보게 만드네요...)