rest인자를 통한 인자객체의 대체

함수의 인자를 설정하기 위해 인자객체의 도움을 받으면 매우 편리하게 복잡한 함수 인자설정을 처리할 수 있습니다.

하지만 인자를 위한 별도의 객체를 생성해야 한다는 점과 그 인자를 얻기 위한 추가적인 프로토콜을 배포해야 하는 점이 늘 불만이었습니다. 그래서 이번엔 좀 다른 방법으로 접근해보았습니다.

rest인자

function (…인자명) 의 스타일로 지정하면 해당 인자명은 배열형을 갖는 형태로 지정됩니다. 실제로 이렇게 인자가 지정된 경우 무한히 인자를 받을 수 있는 구조가 됩니다.

자세한 rest인자에 대한 스펙은 공식문서에서 충분히 설명하고 있습니다.

제가 rest인자에서 주요하게 바라봤던 점은 무한히 여러 개의 인자를 받을 수 있다는 점입니다. 보통 이 특징을 배열과 같은 열거형 자료에 대해서 연결시키기 쉬운데 이 부분이야말로 인자객체가 하는 짓과 다를 바 없는 부분이라고 생각했습니다.

Object, Array의 병합

실제 솔루션에 접근하기 전에 간단히 준비운동을 하겠습니다. 여러 개의 Array가 들어오면 이를 어떻게 합칠까요? 다음과 같은 간단한 코드로 병합할 수 있습니다.

function test( ...$param ):Array{

	var i:int, j:int;

	if( $param.length > 1 ){
		for( i = 1, j = $param.length ; i < j ; ++i ){
			$param[0] = $param[0].concat( $param[i] );
		}
	}

	return $param[0];
}

trace( test( [0,1], [7,8], [10, 11] ) ); // 0,1,7,8,10,11

Object의 경우는 약간 더 까다롭습니다. key를 직접 루프 돌며 생성해야 하기 때문 입니다.

function test( ...$param ):Object{

	var result:Object, key:*, i:int, j:int;

	result = {};

	for( i = 0, j = $param.length ; i < j ; ++i ){
		for( key in $param[i] ){
			result[key] = $param[i][key];
		}
	}

	return result;
}

trace( test( {a:0,b:1}, {c:7,d:8} ) ); // {a:0, b:1, c:7, d:8}

어쨌든 rest인자로 들어온 인자를 병합할 수 있는 방법은 충분히 이해하실 수 있을 거라 생각합니다.

인자설정을 위한 함수군

하나의 클래스 내에서 인자만을 위한 함수군을 작성할 때 코드힌트에서 눈에 띄게 하려면 이름규칙을 사용하는 게 젤 좋습니다. 전 인자에 대해 $를 사용하기 때문에 ‘$대문자’라는 규칙을 인자설정용 메서드에게 부여하기로 했습니다.

이제 width, height 를 받고 x,y,z를 받는 makePlane이란 메서드가 있다고 가정합시다.

그럼 이 함수를 위해 다음의 두 가지 인자설정용 함수를 지정하려고 합니다.

function $SIZE( $width:Number, $height:Number ):Object{
	return {width:$width, height:$height};
}

function $POS( $x:Number, $y:Number, $z:Number ):Object{
	return {x:$x, y:$y, z:$z};
}

그리고 위의 준비운동에서 사용했던 Object가 여러 개 들어올 때 합치던 부분을 makeParam이란 함수로 정의해보겠습니다.

function makeParam( $param:Array ):Object{

	var result:Object, key:*, i:int, j:int;

	result = {};

	for( i = 0, j = $param.length ; i < j ; ++i ){
		for( key in $param[i] ){
			result[key] = $param[i][key];
		}
	}

	return result;
}

이제 모든 재료가 갖춰졌으니 makePlane을 만들어보죠.

function makePlane( ...$param ):Plane{

	var param:Object = makeParam( $param ); //여러 개의 인자를 통합한다.

	var result:Plane = new Plane; //plane을 생성한다.

	//param에 있는 x,y,z,width.height등을 적용한다.
	for( var key:* in param ){
		result[key] = param[key];
	}

	return result;
}

실제 사용되는 스타일은 다음과 같습니다.

makePlane( $SIZE( 50, 50 ), $POS( 0, 0, 0 ) );

만약 this내부가 아니라 외부에서 test라는 클래스 소속으로 사용했다면 아래와 같이 변합니다.

var test:Test = new Test;
test.makePlane( test.$SIZE( 50, 50 ), test.$POS( 0, 0, 0 ) );

기존의 인자객체라면 아마도 다음과 같은 코드였을 겁니다.

var test:Test = new Test;
test.makePlane( (new TestParam).SIZE( 50, 50 ).POS( 0, 0, 0 ) );

인자를 설정할 때 인자객체는 인자함수 후에 점만 찍으면 바로 바로 인자함수 리스트가 나오지만 rest를 이용한 스타일에서는 컴마 찍고 소속클래스 적고 점 찍고 $까지 써줘야 원하는 인자목록리스트를 IDE에서 얻을 수 있습니다.

결론

불편하냐구요? 그건 좀 생각해볼 문제입니다. 인자객체는 해당 메서드에 매핑되는 인자클래스를 얻는 방법을 알고 있다고 가정하고 있습니다.

예를 들어 위의 예에서

  1. TestParam이란 클래스가 있다는 것도 알고 있을 것이며,
  2. TestParam은 매번 new로 생성하는 것이며,
  3. TestParam은 Test클래스의 makePlane에서 사용되는 인자객체다

라는 적어도 3가지를 가정하고 있습니다. 이 3가지는 완전히 가정이라 본인 외에 다른 사람은 알 수 없습니다. 그에 반해 클래스에 내장된 인자용 함수는 가정하고 있는 두 가지 밖에 없고 교육하기 편리합니다.

  1. $로 시작하는 대문자 이름의 메서드는 인자설정용 함수다.
  2. rest 인자를 받는 메서드의 경우 인자설정용 함수를 사용한다.

큰 프레임웍에선 오히려 이 부분이 편리성 보다 더 중요하다는 사실을 깨달았다고 할까요.

해서 TAF는 인자객체를 사용하지 않고 해당 인자생성 책임을 그 객체에게 부여합니다.

Browser does not supports flash movie


관련된 글:

  1. 인자객체의 그 다음
  2. CSexpression
  3. 함수의 위임호출과 생성자 위임호출
  4. 2010年 Coding Rule 1.0
  5. CcircleStack

21 Comments

  1. 허남진 says:

    + _+) 바로 따라하기..

  2. 찬익 says:

    음.. Array 병합의 경우, return [].concat($param); 으로는 안될까요? :’)
    프레임웍 도입에는 확실히 교육 문제가 최우선.. ㅎㅎ
    보다 일반적인 인터페이스로 교육이 필요 없으면 제일 좋겠지만..
    프로그래머들 뇌구조도 제각각이고, 그보다 우선적으로 운영에는 좋은 인력을 잘 안 쓰는 기업들이 대부분이니..

    • admin says:

      인자로 넘어온 객체를 보호하기 위해 새로운 객체로 부터 시작하는게 좋죠 ^^
      귀찮아서 살짝 ㅎㅎ

      • 찬익 says:

        아.. return [].concat.apply(this, $param); 이군요..
        한 줄로 줄이자는 의도였는데.. 전혀 다른 코드를 올렸네요. ㅎㅎ

        • admin says:

          넵 맞아용 그건 전통의 코드 ^^;
          하지만 여기엔 인자함수의 개념 때문에 단순버전을 올려서 그런데, 사실은 rest를 합체하거나 처리할 때 여러가지 공정이 끼어드는게 보통이에요. 그래서 실제 rest병합은 경우에 따라 복잡한 처리를 거치죵. 대표적으로 rest로 넘어온 인자들의 형을 일일히 확인해야한다던가 벨리데이션을 돌린 후에 통과시키는 절차들이 일반적으로 들어갈거잖아요.
          그래서 결국 for를 쓰게 되는데 프레임웍의 일부를 카피해서 포스팅하다보니 자연스레 코드 조각이 넘어온거죠.

          • 찬익 says:

            아하.. 종종 “어? 여긴 왜 이렇게 되어있지?” 하는 곳들이 있었는데, C&P의 폐해였군요 ㅎㅎ

  3. vulcan says:

    생성자에서 ..rest를 사용할 수 있는 방법이 있을 까요?
    var classRef:Class

    var instance:* = new classRef();
    이런식의 코드에서 생성자에 인수를 가변적으로 전달할 수 있는 방법이 있는지 궁금해 졌습니다.
    var instance:* = new classRef.apply(null, rest);
    이런 구문이 통하면 좋을텐데 말이죠. 필요한적이 있었는데 결국 해결 못한 문제였습니다.

    • admin says:

      ㅎㅎ 질문이 잘못되었습니다.

      생성자는 물론 rest를 사용할 수 있습니다. 즉 아래와 같은 코드가 성립합니다.

      class a{
        public function a( …$param ){}
      }

      하지만 원하시는 질문은 생성자의 인자에 상관없이 위임하여 new를 apply로 대체할 수 있냐는 것이죠 ^^
      일단 이 질문의 답은 안됩니다 라는 것이죠. 즉 new를 통해 생성되는 객체의 생성자는 apply와 call을 적용할 수 없습니다.
      그럼 생성위임은 불가능하냐구요? 당연히 이빨이 안되면 잇몸입니다 ^^

      switch( temp.length ){
      case 1: return new cls( temp[0] );
      case 2: return new cls( temp[0], temp[1] );
      case 3: return new cls( temp[0], temp[1], temp[2] );
      ..
      ..
      case 35: return new cls( temp[0]…. );
      case 0:default: return new cls;
      }

      이러면 된답니다. 인자를 몇개나 커버해야하냐구요? 그거야 당연히 개발자 맘이죠 ^^; 제가 프레임웍을 짤 땐 플래시 API를 조사해서 가장 인자갯수가 많은 걸 찾았더니 10개 였습니다. 그래서 case10까지만 짰습니다.
      하지만 3500까지 커버하신다고 누가 머라 할 사람은 ^^;

  4. vulcan says:

    좀 오래되긴 했지만 Perl의 모토가 생각나네요..
    There Is More Than One Way To Do It.
    버그없이 돌아가면 그것이 좋은코드인 것인데
    가끔 주객이 전도될때가 있단말이죠..ㅎㅎ

    • admin says:

      약간 다른 관점이긴 하지만 저는 그러한 사건을 바라보는 뷰가 방법을 찾아보는게 아닙니다 ^^
      오너다보니 어떤 사건이 발생하면 해결해야만한다 해결하지 않다고 괜찮다로 보죠 ㅋㅋ
      해결해야만 하니까 해결을 하는 거죠. 예를 들어

      이런 구문이 통하면 좋을텐데 말이죠. 필요한적이 있었는데 결국 해결 못한 문제였습니다.

      이게 해결안해도 괜찮다라면 넘어가지만 해결해야만 한다면 어떻게든 난리를 쳐서 위와 같은 결과가 나오는 ^^

      • vulcan says:

        ㅎㅎ 저역시도 결국엔 제시하신 방법으로 해결했지만 그게 볼때마다 눈에 좀 거슬려서 (눈으로 보기엔 계단식으로 아주 정리가 잘되있는 듯 보이긴 하지만..) 저걸 어떻게좀 했으면 좋겠는데..하면서 한동안 만족스럽지 못하고 찝찝했다는 표현이였습니당~
        코드를 생산하다 보면 마치 코드 품질측정이라도 하듯이 저급,고급 레벨의 코딩 수준을 스스로 만들게 되어 그틀에 갖쳐버리게 되는 경우가 생기는듯 합니다. 그렇다고 code draft같은 책들이 말하는게 다 쓸데없는 얘기같다 라는게 아니고…ㅎㅎ그런 수준도 안되니 패씁니다요~^^

        댓글 수정버튼이 없어서 인지 여기는 왠지 글올리기가 아슬아슬하네요 ㅎㅎ

        • admin says:

          ㅋㅋ 보통 고급개발자의 시작은 집착은 버리는데서 온다고 하죠.
          하지만 동시에 고급개발자는 원칙을 준수하는데 원칙을 준수하는 것이야말로 집착에서 오는 행위입니다.

          모든 분야가 그렇듯이 이 미묘한 모순을 상황에 맞게 적절히 대처할 수 있으면 보통 장인이라고 부르는 것 같습니다 ^^

        • 찬익 says:

          음.. 아무래도 두 분의 환경(?)의 차이 문제인듯한 ㅎㅎ

          Flex로 기업용 어플리케이션을 주로 개발하시는 vulcan님의 경우, 이러한 패턴 도입으로 효과를 볼 수 있는 경우는 정말 드뭅니다.
          Flash와 ActionScript 프로젝트, 특히 게임 개발 빈도가 잦으신 기완님의 경우, 이와 같은 여러가지 패턴과 프레임웍의 도입 없이는, 적정 품질/비용/기간 내에 개발이 어려운 경우가 많겠구요. ㅎㅎ 워낙 극한의 작업이다 보니..

          물론 그 외에도 외주 프로젝트인지 또는 내부 프로젝트인지 여부, 팀 내 이직률이나 직무 변경 빈도, 기타 여러가지 상황에 따라서, 패턴 도입의 가치/합리성이 달라지기도 하지만…

          분명 vulcan님께서 맡으신 ‘냄새’는, ‘언젠가 다른 문제를 야기할 위험한 코드’로서 vulcan님의 탄탄한 경험을 토대로한, 직관에 의한 것일 겁니다. :’) 그런 코드는, 정말 언젠가는 꼭 문제를 일으키곤 하잖아요. ㅎㅎ
          반면에 기완님의 프레임웍 내에서는, 해당 코드는 그러한 냄새가 매우 미약한 코드겠지요. 해당 패턴을 써야할 확실한 이유가 있고, 또한 충분히 깔끔하게 문제를 해결한 코드 :’)

          • admin says:

            약간 답변이 해깔리는게 이 글자체에 대한 말씀이신건지 아니면 생성자 댓글을 주고받은것에 대한 말씀이신건지 잘모르겠어요.
            flex프레임웍을 쓴다는 것과 인자함수와는 원천적으로 무관한 주제이니 아마도 생성자관련된 부분인 거 같은데, 생성자를 자동화하는 함수를 생각해보셔서 고민하시고 질문을 올리신 것은 오히려 vulcan님 쪽이십니다 ^^
            저는 실제적으로 그러한 생성자에 대한 자동화니즈가 거의 없습니다. 훨씬 광범위한 리플리케이션으로 기저층을 작성하기 때문이죠.
            단지 vulcan님의 환경에서 그러한 코드가 문제가 될 가능성 있다는 것은 프로그래밍 언어적으로보면 당연한 일입니다.
            모든 언어는 제한적인 능력을 갖고 있지만 프로그래밍적인 철학은 그것과 무관합니다. 따라서 그러한 철학에 입각한 해법을 해당 언어로 구현할 때는 여러가지 일이 생기는 법이죠. 실제적으로 저렇게 단축하여 구현하지 마시고 매번 describeType을 이용하여 method의 parameter를 일일히 검증하여 받아온 인자를 검사한 뒤 생성한다면 원천적으로 동적 생성을 위임한 불안감에서 런타임오류 격리수준까지 안전도를 올릴 수 있습니다. 컴파일타임으로 옮기고 싶다면? 그거야 당연히 Class형이나 String형을 받을게 저 함수의 원래 시그니쳐인데 범용형을 받는 함수가 컴파일타임으로 검사가 옮겨갈리가 있나요 ^^

          • 찬익 says:

            아.. 원래는 vulcan님께 쓴 글인데 답글이 밑에 붙어서 마치 기완님께 쓴 글처럼 되어버렸네요.
            우선 생성자 관련은 아니고.. (그 답글은 지금 봤네요 ㅎㅎ)
            그렇다고 딱히 인자 함수만 딱 꼬집어 이야기 한 것은 아닙니다. ㅎㅎ

            아무래도 Flex 분야 쪽에서는 ‘누구나 이해할 수 있는 코드’가 중시되는 편이고..
            또 그 중 대부분은 일반적인 코딩 스타일 만으로도 충분히 해결이 가능한지라.. ㅎㅎ

        • 찬익 says:

          헛. 답글이 밑에 가서 붙는군요. ㅎㅎ

          아마도 제 생각에는, vulcan님께서 ‘패턴 남용의 문제’를 살포시 짚고 넘어가시려다가 실패하신 듯. ㅎㅎ
          두 분이야 이러한 패턴들을 언제 어떻게 써야하는지 잘 아시지만, 반면에 입문자분들께서는 자칫 적절치 못한 곳에 도입하여, 크나 큰(?) 재난을 겪으시는 경우가 자주 있으니.. (하시려던 이야기가 맞나요? ㅎㅎ)

          저는 그게 글 쓸 때 고민이 가장 많이 되는 부분이에요. ‘어차피 기술이란 것이 다 거기서 거기인지라, 지금 쓰려는 내용이 딱히 고도한 것도 아니고, 정작 유사한 상황이 닥치면 누구나 쉽게 생각해낼 수 있는 것인데, 굳이 미리 적어놓는 것이 읽는 분들께 도움이 될까? 혹시라도 불필요하게 패턴 남용의 문제를 유도하게 되는 것은 아닐까?’ 하는 고민.. ㅎㅎ 그래서 초기에는 꼬박꼬박 주의 문구를 달아서 글을 올리다가, 한동안은 주로 사상적인 내용을 주로 담았었지요. ㅎㅎ

          그런데 요즘엔 그 생각도 점차 변해가는 듯.. ‘어차피 패턴 남용 문제도 누구나 꼭 겪어야하는 문제인데, 그냥 적어버리자.’
          참 그런 것 같아요. 뭔가 해법 하나를 적으면, 해당 패턴이 사용되는 상황은 백 가지 중에 한 두 가지 정도.. 그렇다고 그 상황을 일일히 다 기술하자니 책으로 치면 한 챕터는 족히 나올테고… 시간이 충분하다면 또 그렇게 하겠지만, 일단 밥 값은 해가며 살아야 하기 때문에 결국엔 한 페이지 내로 축약.. ㅎㅎ

          아… 다음 주에 또 이사가네요.. 이번에 가는 집은 터가 꽤 좋은 것 같아서, 아예 사무실을 그리로 이전할까 생각 중이에요. ㅎㅎ
          점점 남하하는 우듯.. 이왕 내려가는 김에 내년 쯤엔 제주도까지 갔으면.. ㅎㅎ

          • admin says:

            이게 전 우매하여 지금도 그런 의도를 전혀 파악하지 못하고 있다는 ^^;
            여튼 제가 말씀드릴 수 있는건 다이버스터는 매우 기획적인 사이트라는 점입니다.
            타겟도 명확하고 포스팅하는 주제도 매우 엄밀하게 선정되어있으며 트래픽을 유도하거나 검색어에 최적화되거나 소스코드가 방문자를 유도하는 방식이 전부 의도되어있습니다.

            사실 이 사이트는 기술컨텐츠 사이트가 트래픽을 확보하는 기획안에 대한 제 실험실입니다 ^^
            실제로 타겟을 명확하게 정하고 그들의 특성을 예상한뒤 이를 검색어분석을 통해 매칭시킨 후 해당 검색어를 커버하는 컨텐츠를 기획하여 올리는 식입니다.

            그렇게 하여 트래픽을 측정하고 성과를 분석하여 앞에 얘기한 실험에 대한 소소한 결론을 모아가는 것으로 향후 어떠한 식의 운영과 기획을 해야하는가에 대한 기획자로서의 제 감을 유지하는 게 다이버스터 운영에 대한 또 다른 목적이기도 합니다 ^^

            해서 전 찬익님처럼 포스팅할때 광범위한 수비라인을 구축하거나 커버하지 않습니다 ㅋㅋ
            이미 핀포인트타겟팅을 하고 있기 때문에 통계사이트나 캠페인 레포트도 메인타겟으로부터 트래픽이 60%를 차지하도록 설계되어있습니다.

            초보개발자와 모든 개발자와 지구평화는 아무래도 저보단 더 훌륭한 사람들이 해야겠죠 ^^
            전 왼손처럼 작은 세그먼트 그룹을 거들뿐.

          • 찬익 says:

            제 블로그도 마찬가지입니다. ㅎㅎ
            다만 타겟이 다를 뿐이겠지요. :’p

            저도 사업하는 사람이라, RoI가 명확하지 않으면 움직이지 않지요.
            재미있게 들리겠지만, 사실 제 블로그는 메인 고객이 개발자는 아닙니다.
            (종종 본분을 잊고 기술 관련 글로 도배를 하긴 합니다만..)

            그러다보니 가끔 오해도 있고 하네요. ㅎㅎ

            어쨌든 저는 재미있게 잘 보고 있습니다.

        • vulcan says:

          아이코 두분이 너무 말씀을 잘하셔서리~ㅎㅎ
          그냥 “촉”이 좋아야한다는 걸로 알아듣겠습니당^^

Leave a Reply