2009년 6월 18일 Tweener의 개발종료를 선언하다.

Programming/ActionScript 3.0 2009.08.20 00:15

본인이 ActionScript 3.0을 처음 접할 때 사막의 오아시스처럼 큰 도움을 주었던 Tweener 라이브러리가 최종 버전을 끝으로 개발을 종료한다고 한다. 현재 본인은 TweenLite, TweenMax를 사용하고 있지만 ActionScript 3.0을 재미있게 공부하고, 연구할 수 있도록 나에게 동기를 준 멋진 라이브러리가 아닌가 생각한다.

MC Tween 시절부터 플래시의 발전 속에서 역사가 깊은 라이브러리이기에 여러 가지 생각이 머리 속에서 스쳐 지나간다. Zeh Fernando씨에게 박수를 보낸다. 아래는 원문을 번역한 내용이다. 명확히 알 수 없는 내용은 포함하지 않았으며 일부 문장은 나름대로 재 해석한 관계로 내용 전달이 정확하지 않을 수도 있지만 전체적인 요점은 파악할 수 있을 것이라 생각한다.


========================================================================================
원문 : Zeh Fernando Blog Archive Tweener, 4 years later - A post mortem

우선 시작부터 이야기 하자면 나는 몇 년 전에 Flash 개발 과정에서 애니메이션이 필요하여 MC Tween를 개발했다. MC Tween는 ActionScript의 기본적인 tween을 확장하여 사용했다. 당시에 뛰어난 tween 확장 기능이 존재하고 있었지만 모두 나의 취향과 맞지 않았다. API의 대부분이 너무 추상적이었고 내가 요구하는 것과 달랐기 때문에 prototype 형식을 채용하여 MovieClip의 오브젝트형에 새로운 기능을 추가해서 만들었다. MovieClip에 새로운 기능을 추가하는 형태에 찬사를 주는 사람들이 있었기 때문에 그만한 인기를 얻을 수 있었다.

그러나 2005년 해가 되면서 MC Tween의 변화가 필요하게 되었다. MC Tween은 의도대로 움직이고 있었지만 ActionScript의 문법이나 패러다임(특히 AS2의 등장)이 변화하고 객체지향프로그래밍을 이해하기 시작하면서 MC Tween은 이미 나의 개발 워크플로우에 맞지 않는다는 것을 깨닫게 되었다. 더욱이 MC Tween의 개발이 꽤 정체되어 있었다.

그러던 그 해 6월부터 나는 클래스를 사용하여 새롭게 tween 확장 기능을 실험적으로 개발하기로 했다. 개발 당초에는 인스턴스 베이스(tween 인스턴스를 생성)였지만 이후에 addTween() 이라는 하나의 메소드에 의해서 모든 tween을 생성하는 정적인 클래스 구조로 변경했다. 그리고 stopTween(), getTween()과 같은 부속 메소드를 사용하여 여러 가지 기능을 조작할 수 있도록 했다.

이후에도 클래스는 매우 많은 변경이 있었다. 클래스명이 바뀌거나(초기에는 Twina였던 것이 Tweener가 되고 ZTweener가 되었다가 다시 Tweener로 되돌아옴)  패키지의 위치가 바뀌거나(generic *에서 zeh.easing.*으로, 그리고 다시 caurina.transitions.*)  API가 변경되거나 인스턴스 베이스에서 정적 클래스로 변경한 것은 말할 것도 없고 그 외에도 내부 구조의 변경 등이 많이 있었다. 이러한 과정에서 스스로가 많이 배웠다는 것이 이 글의 취지이지만 자신을 위해서 무엇인가를 만드는 경우, 결과를 누군가가 보고 평가하거나 더욱이 그것을 누군가가 매우 소중한 일에 사용하지 않을 것이라고 생각하고 만든다면 결과적으로 많은 실수를 허용하게 된다. 그러나 그 과정에서 보다 좋은 방법을 찾아내게 되는 것이다.

내가 이 프로젝트를 시작했을 무렵, tween 확장 기능으로서 제일 유명했던 것은 Fuse Kit이였다. 나는 이것과 겨룬다는 생각은 없었다. 다만 나 자신을 위해서 무엇인가를 만들고 자신의 요구에 맞게 변경해 나가는 자유를 즐기고 있었다. Tweener는 긴 시간동안 비공개(나 자신만 사용) 였던 것이 그러한 이유다. 무엇보다 Gringo를 위한 프로젝트를 위해서 사용하고 있었지만 공개된 것은 2007년 1월 최초의 버전을 개발하고 나서 대략 2년 후였다. Nate Chatellier가 팀에 참가하여 AS3버전의 Tweener를 만들었었기에 이대로 비공개로 하는 것은 공정하지 않다고 생각했다. 더욱이 그 시점에서 생각하기에 앞으로 API에는 큰 변화가 있을 것 같지 않았기 때문에 다른 사람들로부터 피드백을 받고 싶은 생각도 있었다.

Tweener는 특별히 참신한 것도 아니고 API는 새로운 것이 없었다. Tweener가 채용한 새로운 tween생성 방식도 다른 tween의 확장에서도 채용되고 있었다. AS1 베이스 확장에서도 채용되고 있었다. 그렇지만 그것은 그 시기에 올바른 구문이었다. 그러한 형태는 인기가 있었고 다른 언어판까지 나오게 되었다.

이는 4년전의 이야기.(로그를 보면 Tweener은 2005년 6월 15일에 개발 개시한 것으로 되어 있다.) 현재는 이 밖에도 많은 tween 확장판이 등장하고 있고, 특히 유명한 것은 Go, TweenLite / TweenMax, GTween, BetweenAS3 등이 있다. 라고 하는 것은? 그래, 또 그 때가 찾아왔다고 하는 것이다. 현재와 같이 AS3버전이 있다고 해도 Tweener가 가지는 패러다임의 대부분은 이미 시대에 뒤떨어져 버렸다. 나는 점점 불안을 느끼게 되었다. 나는 그동안 많은 것을 배웠다. 이제 움직이기 시작할 때다.

바로 움직이기 시작할 수도 있었지만, 그 전에 되돌아보고, 잘 된 것, 가지 않았던 것을 되짚어 보려고 했다. 이 글은 말하자면 Tweener의 사망선고 글, 4년간 내가 무엇을 배웠고, Tweener와 같은 라이브러리가 어떻게 변화해 왔는가 하는 점에 대한 나름의 분석이다.

그런데 AS2에서 AS3로 이동하는 시점에 고려해야 할 중요한 포인트가 한 개 있다. ‘에러’다. AS2 에러는 null 오브젝트의 프로퍼티를 변경하는 스크립트를 실행해도 FlashPlayer는 그대로 지나쳐 버린다. 물론 오브젝트가 존재하지 않기 때문에 스크립트 자체는 기능하지 않지만 그것을 알려주는 에러 메시지가 없다.

이것은 엄밀한 동작이 기대되는 경우에는 문제가 된다. 취득한 데이터가 의도한 것인지를 알기 위해서는 몇 개의 레벨의 검증이 필요하고, 그렇게 해서 다른 방법으로 유저에게 경고를 보낼 수 있었다. Tweener의 AS2 버전에서는 유저에게 보다 안전하게 동작할 수 있도록 노력했다. 구체적으로는 tween 중에 대상이 되는 오브젝트가 존재하는지를 체크하고, tween 되고 있는 프로퍼티에 디폴트의 값이 설정되어 있는지를 체크하고 있다.

하지만 AS3 버전으로 만들면서 그 기능은 필요 없게 되었다. 존재하지 않는 오브젝트나 프로퍼티에 액세스 하려고 하면 코드의 실행은 중단되게 되었다. 이것은 좋은 일이다. 유저가 자신이 무엇인가 실수를 범하고 있고, 그것을 수정하지 않으면 안 된다고 하는 것을 이해하는데 도움이 되기 때문이다.

한편, AS3 버전으로 임포트하는 시점에 Tweener는 이러한 AS2의 패러다임을 계승해 버렸다. 조금 전에 쓴 것과 같이 체크 기능도 많이 남아 있다. 또한 존재하지 않는 프롭퍼티의 값을 tween하려고 하면 FlashPlayer의 에러가 아닌, Tweener 내에서 설정한 커스텀 에러를 표시하게 되어 있다.

if (rScopes[0][istr] == undefined) {
     printError("The property '" + istr + "' doesn't seem to be a normal object property of " + String(rScopes[0]) + " or a registered special property.");
}

속성을 업데이트하기 위한 반복 루프에서도 마찬가지다. 업데이트하는 개체가 있는지 확인하고, 속성이 존재하는지 확인하고 있다. 이 방식으로 인하여 성능이 저하되고, 크기가 증가라는 부작용도 제기됐다.

새로운 tween을 적용할 때, Tweener는 비슷한 tween (동일한 개체의 동일한 속성에 적용하는 tween)이 있는지를 찾아 그들을 제거하려고 한다. 별도로 애니메이션을 하기 위해 요구되는 행동이지만, 이로 인해서 2개의 문제가 발생한다. 먼저 tween 덮어쓰기를 강제로 실행하면서 옵션은 없다는 것, 그리고 새 tween을 생성할 때 성능을 크게 저하 시킨다는 것, 이미 많은 tween이 동일한 위치에 있는 경우에는 더욱 그러하다.

최근 tween 엔진 성능을 비교해보면 문제는 더 명백해 진다. 실제 tween 갱신에 소요되는 시간과 메모리 소비 등은 나쁘지 않지만, 생성 tween 양이 늘어나면 늘어날수록 Tweener 처리는 느려진다. 새로운 tween이 생성될 때마다 기존의 모든 tween과 충돌을 체크하기 때문에 tween 생성에 소요되는 시간이 증가하는 것이다. tween 수가 1000을 초과한 상태에서 새 tween을 생성하려고 하면, 애니메이션 자체의 소요 시간을 초과하거나 FlashPlayer가 잠깐 멈추는 경우도 종종 있다. Tweener는 tween 목록을 단지 하나의 배열에 관리하고 있기 때문이다. 더 유용한 AS3 기본 기술을 이용하여 위 사항은 해결할 수 있는지도 모른다. 어쨌든 Tweener은 너무 안전하게 작동하려고 한다. 사용자가 성능을 향상시키고 싶은 처리를 허용하지 않는다.

오늘 Tweener에 대해 언급하는 것은 더 이상 최적화 문제가 아니라고 생각하기 때문이다. 보완할 수는 있다. 몇 가지 검사하는 기능을 제거, 또는 Array 대신 Dictionary와 같은 AS3의 새로운 기능을 추가하여 목록을 관리하거나, Vector 기능을 추가하거나 할 수 있다. 하지만 솔직히 말해서 Tweener는 그 전성기가 끝났다고 나는 확신한다. 그렇기 때문에 그러한 변경을 해도 곧바로 모든 것이 단순히 해소될 수 없다. 그러니 더 이상 Tweener을 업데이트 하는 것은 무의미하다. 물론(특히 동시에 1000개 이상 같은 tween을 수행할 필요가 없는 경우) 현재도 작동하고, 앞으로도 계속 도움이 될 것이다.

숨어 있던 새로운 버그가 나타나지 않는다면 Tweener를 업데이트할 생각이 없지만 최근에 있었던 이슈들을 업데이트했다. 새로운 버전의 1.33.74 Google Code 프로젝트 페이지 subversion 서버에 공개된다. 이 업데이트는 기본 tween 덮어쓰기 내용은 남겨두면서 overwrite라는 새로운 매개 변수를 추가하고 (이 값을 true로 하면 그 동안의 기능과 같다.) autoOverwrite는 새로운 정적 속성을 추가했다.

Tweener Comparison

위 그림에서 파란색 그래프는 tween없는 상태를, 오렌지 그래프는 Tweener 1.31.74를, 노란색 그래프는 Tweener 1.33.74에서 overwriting을 false로 설정한 상황을 보여주고 있다.

대단한 일이 아닐지라도, 이것은 이 프로젝트에 참여했던 몇 년간 내가 받은 지원, 조언, 배움, 그리고 훌륭한 경험에 대한 감사의 표시로 받아들여 주길 바란다. 프로젝트를 직접 지원해 준 회원 뿐 아니라, 이 프로젝트를 다양한 측면에서 지원해 준 모든 분들에게 진심으로 감사한 마음이다.

내가 Tweener의 개발을 Google Code 사이트로 옮긴 후부터 집계해온 재미있는 그래프를 보여 주겠다. 이것은 버전별 안정 버전 Tweener 다운로드 비율(전체 다운로드 회수)을 비교한 것이다. AS3가 차지하는 비중이 늘고 있다는 것을 알 수 있다. 덧붙여서,

Tweener download statistics

오늘 출시 버전은 포함하지 않았다.

Tweener download statistics

후기로 여러분에게 전달하고자 하는 것이 있다. 나는 현재 tween에 있어서 개인적으로는 Tweener에 의해 소개된 것과 다른 접근을 사용하고 있다. 내 자신의 워크플로우는 조금 바뀌었다. 색 트윈 및 기타 특별한 지름길은 사용하지 않는다. 대신 독립적인 클래스와 getter / setter 및 decorator 등을 통한 특정 기능을 사용하게 되었다. 내가 Tweener를 개발한 이유는, 자신의 개발 흐름에 맞게 무언가를 적용하고, 수요가 있으면 다른 사람들이 사용할 수 있도록 하는 즐거움에 있었다. Tweener를 공개하는 데 많은 시간을 할애한 것은 그 때문이다. 나는 현재, 모든 사람의 요구에 부응하기 위해 만들어진 것을 사용하기보다는, 특정한 누구를 위해 만들어진 것을 사용하는 것을 선호한다.

분명한 것은, "Tweener2"나 그런 것은 나오지 않는다는 것이다.
다시 한번 감사합니다!

========================================================================================

세상의 모든 것들은 시간의 흐름에 따라 변화하고, 소멸하고, 다시 새로운 것이 탄생하기를 반복한다. 플래시의 경우도 예외가 아니다. 플래시는 1년이 멀다 하고 새로운 API와, 흥미로운 기능들이 추가되며 발전해 왔다. 하지만 그 화려한 변신에 가려진 개발자들의 눈물겨운 노력은 일반 사람들에게는 잘 알려지지 않는다. 더욱이 플래시라는 분야를 재미없는, 해야만 하는 일로 생각하는 개발자라면 그 정도는 더욱 심했을 것이다.

Zeh Fernando씨가 Tweener의 사망선고하는 글을 올리기까지 얼마나 많은 노력과 시간을 투자했을지, 그리고 스스로에게 의미가 있는 프로젝트를 종료하기까지 많은 고민과 생각을 했으리라. 다시 한번 멋진 프로젝트를 진행했던 Zeh Fernando씨에게 박수를 보낸다.

어쩌면 Zeh Fernando씨가 우리에게 도움을 준 것 보다 Tweener를 의미 있는 작업에 활용하며 피드백을 주었던 수 많은 사람들로부터 받은 관심이 Zeh Fernando씨가 불특정 다수에게 감사를 표하는 가장 큰 이유일 것이다. Tweener는 종료되었지만 앞으로도 더욱 다양한 작품으로 만날 것을 기대해 본다.

신고
    

설정

트랙백

댓글

정보의 가시화 그리고 플래시 개발자의 역할

Programming/ActionScript 3.0 2009.05.31 08:12


정보의 가시화.
컴퓨터가 발달하지 않았던 시대에는 그래프의 탄생이 정보의 가시화에 혁명처럼 다가왔던 시대가 있었다. 그로부터 몇 백 년이 지난 지금은 어떠할까, 컴퓨터가 발달하고 인터넷이 빠르게 발전하면서 대량의 정보, 그 자체를 분석하고 처리하는 능력은 그때와는 비교도 할 수 없을 정도로 발전했지만 아직까지도 정보를 효과적이고 완벽하게 가시화 할 수 있는 방법은 존재하지 않는 것 같다. 가면 갈수록 컴퓨터의 정보처리 능력과 더불어 인문학이 주요 이슈로 떠오르고 있는 이유도 여기에 있는 것이 아닐까.

데이터는 데이터 자체로 의미를 지닐 수는 없다. 데이터는 인간이 이해할 수 있는 것으로부터 그 가치를 가지게 된다, 그렇다면 데이터를 인간이 이해하기 편하게 표현하려면 어떻게 해야 할까.

정보 가시화에 필요한 기본적인 시각 요소들은 사이즈, 색채, 관련성, 좌표 등이 있다. 흑백 인쇄와 같이 제한적인 환경이 아닌 인터넷에서는 이러한 기본적인 시각 요소들을 다양하게 사용할 수 있다. 더욱이 모든 이용자의 접근성을 위한 대체 컨텐츠를 제공 해야 하는 부담을 줄이기 위해서는 앞에서 열거한 모든 요소들을 적절히 활용하는 것이 필요하다.



또한 인터넷 매체로 넘어오면서 정보를 단순히 가시화해서 보여주는 것에 머무르지 않고 사용자와 시스템간에 상호 의사소통을 함으로써 인간이 데이터를 이해하는데 보다 많은 도움을 주고 있다. 그러한 관점에서 본다면 플래시는 이러한 시대를 제대로 만났다고 할 수 있다. 플래시는 발전을 거듭하면서 데이터 분석과 처리를 효율적으로 수행하고 있으며 플래시만의 특화된 디자인적인 요소를 충분히 활용함으로써 다양한 시각화를 표현 할 수 있게 되었다. 물론 좋은 펜을 가지고 있다고 하여 글을 잘 쓰는 것은 아니겠지만 정보를 효과적으로 가시화할 수 있는 발판을 마련했다는 점에서 의미가 있다고 하겠다.

플래시는 앞으로도 많은 발전을 거듭하겠지만 그 동안은 도구 자체로써 발전을 했다면 이제는 그 도구를 최대한 활용하여 효과적이고 최적화된 콘텐츠를 생산하는 인력을 키우는데 노력 해야 할 것이다. 앞에서 언급한 것처럼 정보는 정보 자체로 그 의미를 지닐 수 없듯이 말이다.

플래시 개발자의 역할.
위에서 말한 효과적인 컨텐츠를 생산하기 위해서는 플래시 개발자에게는 다양한 역량을 요구하게 된다. 플래시 기술을 보유한 엔지니어를 넘어 정보를 어떻게 가공하여 가시화 할 것인가에 대한 근본적인 문제를 효과적으로 구상할 수 있는 기획력, 그리고 그러한 표현을 현실화 할 수 있는 디자인 능력이 요구된다. 물론 요즘은 전문적인 역량을 요구하게 되면서 각 분야의 기술력이 높아졌지만 그러한 업무 분담 시스템으로 인해 결과적으로는 컨텐츠의 퀄리티가 매끄럽지 않은 경우가 많이 발생한다.

이런 문제점을 보완하기 위해서는 플래시 개발자뿐만 아니라 모든 작업자는 수동적인 작업자가 되어서는 안 된다고 생각한다. 각 분야의 전문가들의 의견을 최대한 수렴하고 존중하되, 기획, 디자인, 플래시 기술을 떠나서 최종 사용자의 입장에서 문제점을 지적하고 더 나은 결과물을 얻기 위해 노력해야 한다. 따라서 끊임없이 각 분야의 전문가들과 커뮤니케이션을 해야 하는 것이다.

사실 본인 또한 다른 작업자들을 설득하는 것에 많은 부담을 갖는 것이 사실이다. 하지만 주관적인 자신의 생각이 아닌, 사용자의 입장에서 충분히 생각하고 제시하는 객관적인 설득은 다른 작업자들도 충분히 공감을 할 수 있을 것이라고 믿는다.

플래시라는 기술은 업무의 특정이 완벽하게 구분되는 분야가 아니다. 구조적인 부분에서는 기획, 비주얼 부분에서는 디자인, 기술적인 부분에서는 프로그래밍을 항상 고민해야 하는 특성이 있다. 따라서 플래시 개발자의 역할은 모든 작업자들에게 보다 많은 커뮤니케이션을 유도하는 윤활유 역할을 수행할 필요가 있다.

각 분야의 전문가들은 자신이 바라보는 관점에서 욕심을 내기 마련이다. 하지만 그 욕심이 해당 프로젝트가 나아갈 방향에 부합하지 않거나, 사용자의 사용 패턴을 충분히 고려하지 않은 욕심이라면 다시 생각해볼 필요가 있다. 보통 일선에서는 이러한 역할을 기획에서 하는 경우가 많지만 기획자의 역량에도 한계가 있기 때문에 무조건 기획자에게 책임을 전가해서는 안 된다고 생각 한다.

사실 본인도 그러한 역할을 조금이나마 수행하기 위해서 노력은 하고 있지만 커뮤니케이션 스킬이 많이 부족하다 보니 어려움이 많은 것이 사실이다. 하지만 이러한 노력조차 하지 않는다면 해당 프로젝트는 철학이 없는 이벤트로 끝날 가능성이 크다고 생각한다.

따라서 플래시 개발자는 모든 작업자의 편의를 위해 개발하기 보다는 최종 결과물을 받아들이는 사용자 입장을 항상 고민하고, 그것이 완벽하게 옳다고 스스로 판단되지 않을 경우에는 프로젝트의 완성도를 위한 커뮤니케이션을 회피해서는 안될 것이다.

물론 현실적으로 어려움이 있는 것은 사실이다. 프로젝트의 일정상 어려움도 있을 것이고 커뮤니케이션 과정에서 다른 작업자의 영역을 침범한다고 좋지 않게 바라볼 수도 있다. 하지만 자신이 옳다고 판단하는 것에 대해 다양한 의견을 들어보는 것만으로도 프로젝트 뿐만이 아니라 모든 작업자들에게 도움이 될 것이라고 생각한다.


 

신고
    

설정

트랙백

댓글

[AS3] 좋은 코드를 작성하기 위한 길

Programming/ActionScript 3.0 2008.12.27 11:30
플래시는 에니메이션 저작툴이라는 디자인적인 탄생 배경을 탈피하고 많은 프로그래머들이 매력을 느끼는 컴퓨터 프로그래밍 언어로 발전하였다. 나는 어린 시절 장래 희망을 물어보면 컴퓨터 프로그래머라고 이야기하던 시절이 있었다.(그때는 5.25인치 디스크 한 장에 게임 하나가 들어가던 시절이었는데 아마도 게임을 하면서 스토리를 내 마음대로 바꾸고 싶었던 것 같다.)

하지만 그런 꿈은 삶의 역경 속에서 잊혀지고 나아가 컴퓨터 프로그래머라는 직업이 돈이 되는 직업은 아니라는 것을 알았을 무렵에는 이내 나의 꿈은 컴퓨터 프로그래머가 아니었다고 부정하였다. 결국 부정하던 장래희망의 굴레에 나를 몰아넣은 것은 플래시라고 할 수 있다. 내가 플래시를 처음 접했을 때는 이런 형태로 발전할지는 꿈에도 몰랐었으니 고로 나는 플래시에게 사기를 당한 것이다. ^^


나는 배우는 과정에 서있다. 항상 플래시의 발전을 따라가느라 정신 없이 허우적거리는 생활을 반복하고 있으니 아마도 내가 플래시라는 툴에서 손을 놓을 때까지 이러한 배움의 길은 끝이 없을 듯싶다.

배우는 과정에서 느꼈던 좋은 코드를 작성하는 방법에 대하여 이야기 해 볼까 한다. 본 내용은 일본 잡지에 실린 연재 코너의 내용을 ActionScript 버전으로 본인의 생각을 덧붙여 작성한 것임을 밝혀둔다.

좋은 코드란?
좋은 코드는 조직이나 프로젝트, 프로젝트 메니저에 따라서 그 정의가 다르게 해석될 수 있다. 하지만 보편적으로 좋은 코드로서 바람직한 방향은 있다.

정확하게 동작하는 것.
정확한 결과값을 도출하는 것은 신뢰할 수 있는 코드다.

빠르고 효율적으로 동작 하는 것.
결과 값을 도출하는 방법은 여러 가지가 있을 수 있다. 좋은 코드는 적절한 퍼포먼스로 동작한다.

방어적으로 버그를 생산하지 않는 것.
방어적 프로그래밍 방법론에 기초하여 정상적인 값이 들어 올 것이라고 가정하지 않고 부정확한 값이 와도 문제가 발생하지 않도록 방어적으로 코드를 작성한다. 이것은 처음에 열거한 정확하게 동작하는 것을 도와준다.

유지보수를 하기 쉬운 것.
프로젝트는 단기간에 수명을 다할 수도 있지만 우리가 상상하는 것보다도 길게 생명을 유지하는 것도 많다. 유지보수성을 높이는 것도 좋은 코드에 있어서 중요하다.

다른 사람이 봐도 이해하기 쉬운 것.
현재 작성한 코드를 미래에 자신이 보는 것과 현재 타인이 보는 것은 비슷하다고 볼 수 있다. 미래에 자신이 봐도 이해할 수 있는 코드는 좋은 코드라 할 수 있다.

쓸데없는 부분이 없는 것.
쓸데없는 코드를 작성하는 개발자는 없을 것이다. 다만 코드의 구현과정에서 기능 추가 및 다른 로직과의 연동 과정에서 불필요하게 파생하는 코드들은 지양할 필요가 있다.


좋은 코드를 작성하면 무엇이 좋은가?
좋은 코드는 프로젝트를 추진하고 성공적으로 이끌기 위한 기본적인 요소가 된다. 프로그래밍의 달인들은 심플하고, 유지보수성이 좋고 안정적인 코드를 빠른 속도로 작성한다. 테스트가 어려운 코드를 테스트가 가능하고 검증할 수 있는 코드로 변경함으로써 품질이나 생산성을 수백 배 높이기도 한다. 이것은 프로젝트의 성공에 있어서 큰 요소이다. 물론, 좋은 코드가 있으면 반드시 프로젝트가 성공하는 것은 아니다. 개발 프로세스나 메니지먼트, 커뮤니케이션등으로 좌우되는 경우가 더 많지만 이런 것들을 제외한다면 개발에 있어서 좋은 코드의 힘은 크다고 볼 수 있다.

프로그래머로서의 평가가 높아진다.
좋은 코드를 작성하는 프로그래머는 대체적으로 프로그래머로서 신뢰한다. 프로그래머로서의 평가가 조직으로서의 실제 평가나 이익에 결합될지는 소속하는 조직의 평가 제도나 프로그램 이외의 일도 포함하여 정해지는 것이 현실이다. 그렇다고 좋은 코드를 쓸 수 있는 것이 마이너스 평가로 이어지지는 않을 것이다.

일에 대한 만족감이나 자신감을 가질 수 있다.
두 번 다시 손대고 싶지 않은, 유지보수가 불가능한 코드를 써본 적이 있을 것이다. 이러한 낮은 퀄리티의 일을 해 놓으면 일에 대한 만족감을 얻을 수 없다. 반면 자신의 의지로 적절히 좋은 코드를 작성함으로써 품질이 높고 안정된 소프트웨어를 개발했을 때는 그에 대한 만족감도 높고 자신감을 갖고 일에 임할 수 있다. 앞으로 오랜 시간을 프로그래머로써 살아갈 것이라고 생각한다면 좋은 코드를 쓸 수 있는 레벨을 목표로 하는 것은 합리적인 일이다.

사실 좋은 코드를 작성하기 위해서는 하루아침에 이룰 수 없다. 그렇다면 좋은 코드를 작성하기 위해 구체적으로 무엇을 어떻게 해야 하는지를 살펴보자.

코드 리딩 - 읽어라, 코드를 읽고, 읽고, 또 읽어라.
음악이나 회화, 건축의 세계에서도 자신만의 발상으로 작품을 완성시키는 예술가는 없다. 다른 사람들의 작품을 보고 영향을 받거나 좋은 곳을 훔치거나 해서 자신의 작품을 만드는 것으로 작품을 낳아 왔다.

프로그래밍도 같다. 좋은 코드를 작성 하려면 좋은 코드이건 나쁜 코드이건 간에 다른 사람이 작성한 코드를 평소에 의식하고 읽는 것이 중요하다. 플래시는 특히 많은 오픈 소스가 인터넷에 열려 있기 때문에 다른 사람이 작성한 좋은 코드를 언제라도 부담 없이 읽을 수 있다. ActionScript 중급 정도의 실력자이지만 구조를 어떻게 만들어야 하는지 막막한 분은 특히 주목할 필요가 있다. 이런 단계에 머물러 있는 분이라면 코드리딩을 통해서 한 단계 발전한 자신을 발견할 수 있을 것이다.

코드리딩의 좋은 점은 알아도 코드를 읽는 방법을 모른다면 시작하기 힘든 일이다. 아래의 간단한 코드를 보면서 이야기 해보자.

package classes.data{

    import flash.events.Event;
    import classes.data.*;
    import classes.net.SimpleXMLLoader;
   
    public class DProvider extends SimpleXMLLoader{ // [1] 여기부터
           
        private var _xmlUrl:String;   
        private var _keywords:Array;
       
        public function DProvider(inUrl:String):void { // [2] 여기부터
            _xmlUrl = inUrl;
            addEventListener(Event.COMPLETE, onLoadedXmlHandler);
            loadXML(_xmlUrl);
        }    // [2] 여기까지

        private function onLoadedXmlHandler(e:Event):void { // [3] 여기부터
            var xml:XML = XML(data);
            _keywords = [];
           
            var len:int = xml.item.length();
            for (var i:int = 0; i < len; i++)
            {
                // [4] 여기부터
                _keywords.push(createKeyword(xml.item[i]));
            }
        }    // [3] 여기까지
       
        private function createKeyword(inKeywordNode:XML):KeywordBase
        {
            var keyword:KeywordBase;
            switch(int(inKeywordNode.level)) {
                case 1: keyword = new Keyword1(inKeywordNode);
                break;
                case 2: keyword = new Keyword2(inKeywordNode);
                break;
                case 3: keyword = new Keyword3(inKeywordNode);
                break;
                case 4: keyword = new Keyword4(inKeywordNode);
                break;
            }
            return keyword;
        } // [4] 여기까지
       
        public function get keywords():Array { return _keywords.concat(); } // [5]
    } // [1] 여기까지
}

위 클래스는 본인이 실무에서 작업했던 태그클라우드에서 사용했던 코드이다. 클래스의 기능은 xml데이터를 불러들여 level 노드 값에 따라서 해당 클래스들을 배열로 생성하여 제공한다.

위 클래스 외적으로 사용된 클래스는 다음과 같은 기능을 포함한다.
SimpleXMLLoader : 단순히 xml 데이터를 불러오는 기능을 한다. loadXML 메소드가 포함되어 있다.
KeywordBase : 키워드 무비클립의 기본이 되는 기능을 제공한다. (마우스 다운, 오버, 아웃등)
Keyword1 ~ Keyword4 : KeywordBase 를 상속하는 각 레벨에 따라 디자인이 서로 다른 클래스들

코드리딩을 할 때는 기본적으로 nest 단위로 읽어 내려가면 된다. 중간에 나타나는 다른 클래스들은 현재 블록을 전체적으로 훑어 보면서 유추하도록 하자. 중간에 다른 파일을 열어 읽게 되면 현재 읽고 있는 흐름을 놓칠 수 있기 때문이다.

50줄도 안 되는 코드지만 이 코드만 가지고도 작업된 클래스들의 절반 이상은 이해할 수 있다. 복잡한 기능을 하는 클래스는 아니기 때문에 읽으면서 구조적으로 어떻게 작성이 되었는지 알 수 있을 것이라 믿는다. 이런 훈련을 반복 하다 보면 현재 읽고 있는 클래스 이외의 다른 클래스들도 머리 속으로 그리는 과정을 얻게 된다. 이를 통해서 하나를 보면 열을 알게 되는 경지에 다다를 수 있을 것이다. ^^

구글에서 코드 검색엔진을 통해서 웹에 있는 원시 코드들을 검색할 수 있는 서비스를 제공하고 있다. 자신이 원하는 기능의 클래스명이나 메소드명으로 검색해서 코드리딩 연습을 하는 것도 좋은 방법이다. 또한 많이 알려진 클래스나 패키지들은 구조적으로 잘 설계된 것들이 많이 있다. 구조적인 관점에서는 그런 것들을 분석해 보는 것도 큰 도움이 된다.

http://www.google.com/codesearch
인터넷상에 공개되어있는 Subversion(repository)나 archive파일 등을 기계적으로 빠르게 검색 할 수 있다.

좋은 이름을 붙이자.
우리가 프로그래밍을 하고 있을 때 가장 막히는 부분은 변수명, 메소드명, 클래스명 이름을 붙이는 것이다. 그만큼 좋은 이름을 붙이는 것은 중요하다. 좋은 이름을 붙이는 것이 중요한 이유는 위에서 언급했던 코드리딩에서도 느낄 수 있듯이 관련 기능에 부합하는 좋은 이름을 붙이지 않으면 코드를 읽기가 어려워진다.(생각해보니 위에서 예로 든 코드에서도 어려움을 느낀 분들이 있을 것 같다 ^^;)

그렇다면 좋은 이름의 조건이 있을 법하다.

좋은 변수명, 메소드명, 클래스명은 이름이 그 코드의 내용을 올바르게 나타내고 있다. 그러한 이름은 이름을 보는 것만으로도 코멘트를 읽을 필요도 없이 그 역할을 이해할 수 있다. 좋은 이름은 코드의 이해를 돕지만 나쁜 이름은 읽는 사람을 혼란 시키고 착각을 낳아 버그 발생을 조장한다.

위 코드를 예로 들면 onLoadedXmlHandler 메소드 명을 onComplete로 했을 경우에 어떤 것이 Complete 되었다는 것인지 인지하기 어렵다. 위 클래스의 경우는 단순한 클래스이지만 클래스의 내용이 많고 complete 핸들러 함수를 여러 개 포함하고 있을 경우에는 메소드의 중복이 발생할 수도 있다.

public function get keywords():Array { return _keywords.concat(); }
의 경우에는 keyword에 복수를 뜻하는 s를 붙임으로써 전달 받는 keyword가 하나가 아니라 복수이며 배열이라는 것을 유추할 수 있다. 이 밖에도 아래와 같은 방법이 있다.

어두 이외의 모음을 삭제 (image ->img)
강한 소리를 남긴다. (server -> svr)
약어의 이용 (database -> db)

일관성이 있다.
좋은 이름은 코드 전체에서 일관된 흐름을 가져야 한다.

대칭성을 유지하는 것
begin <-> end
write <-> read
on <-> off

단어의 조합의 일관성
scoreAvg
scoreAverage
avgScore
위 이름은 하나의 클래스에서 중복 사용하지 않을 것.

관용어에 따라서
언어, 프로젝트, 회사나 팀 마다 명명에 관한 관용어(관습)가 있다. 예를 들면 Java의 경우는 메소드명은 소문자로 시작하는 것이 관습이며, C#은 대문자, C++은 멤버 변수에 prefix로 m_를 붙이는 것이 일반적이다. 관용어에 따른 명명은 누가 보더라도 알기 쉬운 이름이라고 이야기 할 수 있다.(ActionScript의 경우는 보통 자바와 언어적 문법이 비슷하기 때문에 자바와 같은 명명 관용어를 사용하는 경우가 많지만 개인적인 취향에 따라 다르게 사용하는 경우가 대부분이다.)

코딩 표준에 따라서
위와 같이 좋은 코드의 조건을 채우기 위해서도 코딩 표준이나 명명 규약을 정해 팀 멤버 전원이 맞추는 것이 중요하다. 작업자 1명이 단기적으로 작업하는 프로젝트에서는 본인이 원하는 규칙을 사용하면 되겠으나 조직의 힘을 바탕으로 얻을 수 있는 큰 프로젝트의 경우는 협업의 중요성이 대두되므로 이러한 코딩 표준을 맞추는 것이 장기적으로는 바람직하다고 볼 수 있다.

좋은 이름을 붙이기 위한 습관
항상 좋은 이름을 붙이는 것을 의식한다.
좋은 이름을 붙이려면 우선 의식적으로 노력을 해야 한다. 이것은 A라는 이름이 좋은가? 아니면 B라는 이름이 좋은가를 항상 신중하게 검토해 보고 적절한 이름을 결정하는 프로세스를 반복할 필요가 있다.

코드의 리딩이나 리뷰
자신이 모르는 완전히 새로운 이름을 만드는 것은 코드를 분별없게 만들 가능성이 크다. 본인이 작성한 코드를 스스로 리딩해 보거나 다른 사람에게 리뷰를 함으로써 사용할 수 있는 이름을 조금씩 늘려간다. (본인 같은 경우는 아는 단어의 수준이 메롱이기 때문에 코드작업을 할 때 항상 사전을 열어놓고 작업을 하는 습관이 있다. 이때도 주의할 것은 일회성으로 모르는 단어를 만들어 명명하는 것 보다는 그 단어를 충분히 숙지한 상태에서 이름으로 사용하는 것이 바람직하다 – 간혹 내가 만든 이름을 내가 이해하지 못하는 어처구니 없는 경우가 발생 하기도 한다. 메롱;;)

좋은 변수명
설명적인 변수명
변수에는 값이나 오브젝트가 대입된다. 변수명을 보는 것만으로도 무엇이 어떠한 역할을 하는지 명확한 것이 좋은 이름이라 할 수 있다.

예를 들어
var n:int;                                                 // 무슨 수치?
var languages:Array = [“kr”, “en”];     // 무슨 언어?
var flg:Boolean = false;                      // 무슨 플래그?
var userAgent:UserAgent                  // 무슨 유저 에이전트?

위와 같은 변수명은 나중에 되돌아보면 변수의 역할을 알 수 없다. 아래와 같이 좀더 구체적이고 명확한 이름으로 해 두면 되돌아 보거나 다른 사람이 봐도 이해하기 쉬운 코드가 된다.

var orderCount:int;                                                // 오더수
var availableLanguages:Array = [“kr”, “en”];     // 이용 가능한 언어
var existsSameName:Boolean = false;           // 동성동명이 존재할까?(존재 true)
var unknownUserAgent:UserAgent                  // 미지의 유저 에이전트

다만 변수는 변수의 종류와 그 변수의 스코프(변수의 값을 참조할 수 있는 범위, 변수의 선언으로부터 시작되어 변수의 값을 참조할 수 없게 될 때까지의 범위)에 의해서 좋은 이름의 성질이 다르다. 스코프가 넓으면 다양한 곳으로부터 참조되므로 영향 범위가 크다고 할 수 있다. 따라서 보다 구체적인 이름이 요구된다. 종류별로 보면 아래와 같다.

필드 변수, 클래스 변수
필드 변수는 인스턴스 변수이다. 필드 변수, 클래스 변수는 로컬 변수등과 비교하면 변수의 스코프가 넓기 때문에 특히 그 변수의 의미를 올바르게 표현한 이름이 바람직하다.

메소드의 파라미터
파라미터명은 알기 쉽고 간결한 이름이 좋을 것이다. 파라미터명은 이용자가 API레퍼런스로 참조하거나 Eclipse, FlashDevelop등의 통합 개발 환경으로부터 변수명의 후보로서 이용되므로 극단적으로 짧은 이름을 붙이는 것은 피해야 한다.

로컬 변수
메소드 내에서 일시적으로 선언되는 변수이다. 스코프가 긴 것도 있지만 for문과 같이 짧은 것도 있다. 스코프가 긴 것은 필드 변수와 같이 변수의 의미를 올바르게 표현하는 구체적인 이름을 붙이는 것이 좋다. 스코프가 짧은 것은 아래와 같이 사용구분만 적당히 해주면 된다.

관용어에 따르는 또는 따르지 않는다.
배열의 내용을 순서대로 처리하는 루프 카운터 변수(i,j,k)등과 같이 잘 사용되는 관습적인 변수명은 그대로 관용어에 따르는 편이 알기 쉬운 코드가 된다.
// 루프 카운터의 변수명이 관습적이지 않고 너무 길다 [X]
for (var empCounter:int=0 ; empCounter < employes.size ; empCounter++) {
          var emp:Employee = employees[empCounter];
    ...
}

// 루프 카운터의 이름이 관습적으로 짧고, 가독성이 높다 [O]
for (var i:int=0 ; i<employes.size ; i++) {
         var emp:Employee = employees[i];
    ...
}


다만 예외적인 경우도 있다. 구체적인 이름을 변수명으로 하는 편이 알기 쉬운 경우는 그렇게 사용한다. 예를 들어 좌표계를 취급하는 경우에는 아래와 같이 사용하여 가독성을 높일 수 있다.
// i,j의 어느 쪽이 x좌표, y좌표인지를 모른다 [X]
for (var i:int=0 ; i<width ; i++) {
    for (var j:int=0 ; j<height ; j++) {
        var color:uint = getColor(i, j);
        ...
    }
}

// 변수명을 x,y로 했으므로 알기 쉽다 [O]
for (var x:int=0 ; x<width ; x++) {
    for (var y:int=0 ; y<height ; y++) {
        var color:uint = getColor(x, y);
        ...
    }
}

위 for문의 경우, 중첩되는 var y:int=0 변수 선언은 for문 밖에서 선언해 주는 것이 바람직하다. 중첩된 for문 안에서 x가 카운팅 될 때마다 메모리상에서는 y라는 변수의 어드레스를 매번 push해야 하는 불필요한 리소스 낭비가 발생하기 때문이다. (이 부분에 대한 논쟁 이슈가 있다. 이후에 이야기할 로컬 변수의 스코프 부분에서 로컬 변수의 스코프는 가능한 작게 한다는 변수의 의존성에 따른 대원칙에 위배되는 것이기 때문이다. 이것은 사용하는 상황에 따라 적절히 혼용하는 것이 좋을 것이다.)
var x:int;
var y:int;
for (x=0 ; x<width ; x++) {
    for (y=0 ; y<height ; y+) {
        var color:uint = getColor(x, y);
        ...
    }
}

변수명을 짧게 하는 편이 가독성이 향상되는 경우도 있다.
충분히 인지할 수 있는 범위인 경우로 루프내의 변수나 짧은 메소드 내의 변수처럼 일시적인 변수는 짧게 하는 편이 반대로 가독성이 향상되는 경우도 있다.
// 변수명 siteContactChangelog는 너무 길다 [X]
for (var i:int=0; i<siteContactChangelogs.length ; i++) {
    var siteContactChangelog:SiteContactChangelog = siteContactChangelog[i];
    siteNames[i] = siteContactChangelog.getSiteName();
    siteTitles[i] = siteContactChangelog.getSiteTitle();
    sitePicIds[i] = siteContactChangelog.getPicId();
    sitePicNames[i] = siteContactChangelog.getPicName();
}

// 변수명 log는 충분히 그 의미를 이해할 수 있기 때문에 가독성이 좋다.[O]
for (var i:int=0; i<siteContactChangelogs.length ; i++) {
    var log:SiteContactChangelog = siteContactChangelog[i];
    siteNames[i] = log.getSiteName();
    siteTitles[i] = log.getSiteTitle();
    sitePicIds[i] = log.getPicId();
    sitePicNames[i] = log.getPicName();
}

좋은 메소드명
좋은 메소드명은 이름으로부터 그 기능을 예상할 수 있다. 메소드는 처리를 담당함으로 메소드명은 동사+목적어 형태인 것이 많다. 아래는 자주 사용되는 메소드명의 예이다.
 메소드의 역할                                         메소드명
 값 취득에 관한 메소드                           getXXX
 값 세트에 관한 메소드                           setXXX
 생성에 관한 메소드                                create, build, make, generate
 초기화에 관한 메소드                            initialize, setupXXX
 파기에 관한 메소드                               destroy, dispose
 상태 확인에 관한 메소드                      contains, exists

인스턴스 메소드는 dataFormat.parse와 같이 오브젝트명으로 조합하므로 합쳐졌을 때는 중복 없게 의미가 통하는 이름으로 한다. 메소드와 클래스명도 이와 동일하다.
// parseDateString은 클래스명 DateFormat과
// date라는 이름이 중복되어 약간 장황
var dateFormat:DateFormat = new DateFormat("yyyy-MM-dd");
var startDate:Date = dateFormat.parseDateString("2008-04-01");

// 클래스명 DateFormat과 메소드명 parse로
// 무엇을 처리하는지 충분히 이해할 수 있음.
var dateFormat:DateFormat = new DateFormat("yyyy-MM-dd");
var startDate:Date = dateFormat.parse("2008-04-01");

좋은 클래스명
변수명이나 메소드명과 비교해서 클래스명은 가용 범위가 크기 때문에 적절하고 좋은 클래스명을 붙이는 것이 중요하다. 좋은 클래스명은 이름만으로 무엇을 실시하는 클래스인가를 알 수 있다.

클래스명이 잘 떠오르지 않을 때는 자신이 만들려고 하는 클래스의 역할을 제대로 정리 할 수 있는지 없는지를 판단해 보아야 한다. 1개의 클래스에 복수의 책무를 넣거나 역할이 애매한 기능은 없는지를 생각해보고 해당 클래스에서 해결하고자 하는 문제를 차분히 생각해보고 클래스명을 다시 선택할 필요가 있다.

클래스명의 어휘 설계 능력
이름 명명에 관하여 자신이 모르는 표현, 개념은 쉽게 생각하기 어렵다. 보다 나은 표현을 선택하려면 여러 가지 코드나 서적을 읽거나 실제로 코드를 쓰고 시험하는 것이 중요하다. 그 경험을 통해서 이름에 관한 어휘가 조금씩 늘어난다. 본인 같은 경우는 영어권의 개발자가 작성한 기능과 메소드명을 보면서 의미해석을 한다. 사실 프로그래밍 과정에서 이름을 짓는 것은 생활에서 보편적으로 사용하지 않는 단어들도 많이 존재한다. 하지만 그만큼 자주 사용되는 단어들은 한정되어 있기 때문에 틈나는 대로 기억을 되살려 사용해 보는 것이 중요하다.

좋은 패키지명
ActionScript의 패키지명은 클래스나 자원의 논리적인 그룹을 만들어 계층 구조를 나타낸다 이것은 주로 이름의 충돌을 막기 위해서 사용된다. 패키지명은 그룹의 논리적인 내용을 나타내는 간결한 명사를 선택한다.

간혹 클래스를 만들지 않고 라이브러리에서 특정 무비클립을 동적으로 참조하기 위하여 Linkage에 클래스명만 기입하여 사용하는 경우가 있다. 이럴 경우 클래스가 플래시 컨텐츠전역에 오픈 되기 때문에 팀의 협업에 있어서 클래스명의 충돌을 야기할 수 있다. 이럴 경우 해당 클래스가 없더라도 관련된 패키지명을 명시함으로써 그러한 충돌을 줄일 수 있다. 

처음부터 좋은 이름을 사용할 수 있는 사람은 없다. 좋은 이름에 대해서 의식적으로 사용하려고 노력하는 것이 중요하다. 누구라도 알기 쉽고, 이해하기 쉬운 코드라고 하는 것은 항상 의식하고 노력하다 보면 점차적으로 좋은 이름을 지을 수 있는 요령이 생길 수 있을 것이다.


스코프를 의식한 프로그래밍
사실 모든 프로그래밍에서 스코프는 존재한다. 의식적으로 스코프를 컨트롤 할 수 있으면 보다 좋은 프로그래밍 스타일에 가까워질 것이다. 이번에는 스코프에 대한 이야기를 해보자.

우선 스코프는 Wikipedia에 다음과 같이 정의되어 있다.
프로그래밍에서 스코프란, 어느 변수나 함수가 특정 이름으로 참조되는 범위, 어느 범위의 밖에 있는 변수 등은 통상 그 이름만으로는 참조할 수 없다. 이때 이러한 변수는 스코프 밖에 있어 보이지 않는다 라고 말한다.

스코프는 변수, 메소드, 클래스등이 보이는 범위다. 그렇다면 보인다는 의미는 무슨 뜻일까? 보인다라는 것은 프로그래밍상에 있다는 것이다. 변수이면 변수명을 지정해 값을 읽고 쓰기가 가능하고 메소드는 호출해 사용할 수 있는 범위에 있는 것이다.

사용할 수 있다라는 것은 바꾸어 말하면 그것에 의존한다라고 이야기할 수 있다. 하나의 코드를 변경하고 싶을 뿐인데 수십 곳을 수정할 필요가 있을 수 있다. 이것은 변경하고자 하는 코드가 여러 곳에 의존하고 있으면 이와 같은 변경이 다른 부분에 영향을 준다.

스코프 == 보이는 범위 == 사용할 수 있는 범위 == 의존하는 범위

변수나 메소드의 스코프를 작게 하는 것으로 보이는 범위가 작아져 사용할 수 있는 범위, 의존하는 범위도 작게 할 수 있다. 스코프를 작게 하는 것으로 보다 의존성이 적어 변경이 용이한 프로그램을 작성 할 수 있는 것이다.

기억해 두는 것을 줄이자.
의존이 작아지는 것 이외에도 스코프를 작게 하는 것으로부터 도움이 되는 것이 있다. 프로그래밍은 본질적으로 복잡한 것이다. 복잡함에 대한 대책으로서 문제 영역을 가능한 작고, 이해 가능한 상태로 취급하는 것이 중요하다. 스코프를 의식적으로 작게 하는 것으로 프로그래머가 기억해 두지 않으면 안 되는 것, 주의하지 않으면 안 되는 범위는 작아져서 쉽게 이해할 수 있게 된다. 이것이 스코프를 작게 하는 것의 본질이라고 할 수 있다.

로컬 변수의 스코프
로컬 변수란 메소드 내에서 선언된 일시적인 변수다. Java나 Ruby의 로컬변수는 선언된 장소로부터 스코프가 시작되어 선언된 블록이 끝나면 스코프가 종료된다. ActionScript 2.0 에서도 순차적으로 메모리 할당이 이루어 졌기 때문에 메소드 내에서 상위에 선언된 변수와 하위에서 선언된 변수가 서로 다른 영역 범위의 스코프를 갖기 때문에 컴파일 시에 충돌이 발생하지 않았다.(Java, Ruby와 동일)

반면 ActionScript 3.0에서의 로컬변수는 스코프의 시작은 컴파일러가 블록에 접근했을 때 블록 내의 위치와는 상관없이 미리 메모리 주소가 할당된다. 이는 컴파일 시에 메모리 할당이 한꺼번에 선행되어 스코프가 동일하기 때문에 변수명 중복 에러 메시지를 출력하는 것이다.

하지만 일반적인 프로그래밍 언어에서는 변수의 의존도를 최소화하여 프로그램의 변경이 쉽게 하기 위하여 [로컬변수의 스코프는 가능한 작게 한다.] 라는 것이 대원칙이다.

필드 변수의 스코프
필드 변수는 인스턴스 변수다. 필드 변수의 스코프는 private / public 등과 같은 키워드를 통해서 정해진다. ActionScript의 경우 필드 변수에 대한 가시성은 스코프가 작은 순서로 4개가 지원된다.
private    : 오브젝트 내에서만 변수에 액세스 할 수 있다.
internal    : 같은 패키지 내에서 액세스 할 수 있다.
protected : 오브젝트 내 그리고 하위 클래스로부터 액세스 할 수 있다.
public : 오브젝트 밖에서 액세스 할 수 있다.

필드 변수는 모두 private가 기본 전략이다. 필드 변수에 외부나 하위 클래스로부터 액세스 하고 싶은 경우에는 set/get 등 액세스용 메소드를 따로 만들어 공개하는 것이 캡슐화에 도움을 준다. 하지만 기본 전략이 바람직하다고는 하지만 상속에 따른 장황함을 배제하기 위해서 protected나 public 접근자를 사용하는 경우도 발생한다.

인스턴스 메소드의 스코프
인스턴스 메소드는 인스턴스에 속하는 메소드를 말한다. ActionScript에서 메소드의 스코프 가시성도 필드 변수와 같이 4개이다. 메소드의 접근 제한은 최소인 private로부터 시작하여 점차적으로 internal -> protected -> public으로 나아가는 것이 바람직하다.

메소드 파라미터의 정보량
스코프에서 조금 벗어난 이야기이지만 메소드의 인수로 건네주는 정보량을 어느 정도로 하는 것도 의존성이라는 관점에서 보면 중요한 선택이다.

예를 들면 아래의 두 코드는 사원 정보를 취득하는 메소드이지만 1번은 인수가 사원ID이고 2번은 인수가 사원 오브젝트이다. 이 두 개의 코드 중에 어느 쪽이 좋은 코드라고 할 수 있을까?
// [1] 인수가 사원ID인 경우
public function getEmployee(inEmpId:int):Employee {
    return empDao.findById(inEmpId);
}

// [2] 인수가 사원 오브젝트의 경우
public function getEmployee(inEmp:Employee):Employee {
    return empDao.findById(inEmp.getId());
}

일반적으로 1번이 인수의 정보량이 적기 때문에 의존이 적고 좋은 코드라고 할 수 있다. 반면 2번은 사원 오브젝트가 가지는 모든 정보로 액세스가 가능하기 때문에 의존이 커지게 된다. 필요한 것은 사원의 ID이므로 그것에만 의존하여 코드를 작성하는 것이 바람직하다.

메소드를 이용하는 측면에서 사원 ID만으로 메소드를 이용할 수 있는 것과 사원 오브젝트가 필요한 것은 전혀 다르다. 더욱이 2번 메소드 내에서 구체적인 처리는 사원 오브젝트의 사원 ID만을 사용해 사원 정보를 검색하고 있다. 이것이 문서화 되어 있지 않을 경우, 코드를 읽지 않으면 그 사실을 알지 못한다. 인수에 불필요한 정보가 포함되어 있으면 사원 ID가 포함된 사원 오브젝트를 건네주어야 한다는 암묵적인 요구가 발생하기 쉽다.

위와 같은 문제 때문에 메소드의 인수는 필요 최저한의 정보를 가지는 것이 의존성이 적고 좋은 코드라고 말할 수 있다.

다만 인수가 너무 많아지는 경우는 인수를 오브젝트로 변경하는 편이 좋다. 기준으로서 인수의 개수가 5개를 넘는 메소드는 인수를 오브젝트로 변경하는 것을 검토할 필요가 있다.

static 메소드
static메소드(클래스 메소드)는 클래스에 속하는 메소드이다. ActionScript에서는 static 키워드를 붙여서 선언한다. static 메소드의 가시성은 인스턴스 메소드의 가시성과 같다. 반면 다른 점은 static 메소드는 필드 변수에 액세스 할 수 없다. static 메소드는 메모리에 미리 할당이 되는 반면 인스턴스 변수는 오브젝트가 인스턴스화 된 시점에서 메모리 할당이 되기 때문에 static 메소드 내에서 인스턴스 변수를 참조할 방법이 없는 것이다.

필드 변수에서 보면 인스턴스 메소드를 static 메소드로 변경했을 경우, 자신이 영향을 주는 메소드가 줄어 들게 되어 결과적으로 스코프는 작아진다. 필드 변수에 액세스 할 필요가 없고, 오버라이드가 필요하지 않는 메소드는 static 메소드로 하는 편이 좋은 경우가 많다. 물론 무조건 static으로 하는 것이 좋은 것은 아니다. 인스턴스가 생성되지 않은 시점에서 메모리에 할당이 되기 때문에 사용하지 않는 클래스의 static 메소드가 메모리에 상주하는 메모리 차원에서의 단점도 있다.
// 인스턴스 메소드의 경우
private var count:int;                                                           ┓
...                                                                                           |
private function regexGroup(inRegex:String):String { |변수 count의 스코프
    // 필드에 액세스 가능                                                     |
    return "(" + regex + ")";                                                   |
}                                                                                             |
...                                                                                            ┛

//static 메소드로 변경했을 경우
private int count;                                                                  ┓
...                                                                                            ┛변수 count의 스코프
static private function regexGroup(inRegex:String):String {
    // 필드에 액세스 불가
    return "(" + regex + ")";
}
...     ―변수 count의 스코프


코드의 분할
몇 천행이 넘는 메소드로 너무 많은 기능을 포함하고 있거나 메소드를 너무 세세하게 분할하여 흐름을 파악하기 어려운 코드는 좋은 코드라 할 수 없다. 좋은 코드가 되기 위해서는 가독성이 좋고, 유지보수성이 우수한 것이라고 위에서 언급했다. 메소드를 적절한 단위로 분할하는 것은 좋은 코드를 만드는 것에 중요한 역할을 한다.

코드를 분할하면 무엇이 좋은가?

가독성의 향상
메소드 코드의 길이가 100줄을 넘으면 처리의 흐름을 읽는데 어려움이 있다. 반면 30행 이하이면 내용을 이해하는 것이 용이하다.

유지보수성의 향상
분할에 의해 변수의 스코프가 작아지거나 처리 기능이 본래 있어야 할 클래스나 메소드로 이동하는 것으로 의존관계가 정리되어 유지보수성이 향상된다.

재이용성 향상
코드를 분할하면 코드의 중복이 줄어들어 이용 가능한 부품으로서의 메소드나 클래스를 만들 수 있다. 이것은 OOP 언어에서 좋은 방법이다.

캡슐화를 지킬 수 있다.
객체 지향에서는 캡슐화라는 개념이 있다. 메소드나 클래스의 상속에 의한 구체적인 구현이나 오브젝트 상태를 몰라도 메소드를 호출하는 것만으로도 각각의 오브젝트가 처리를 이행해 주는 것이다.

클래스의 관점에서도 비슷한 이득을 얻을 수 있다. ActionScript는 타 언어보다 MVC 페턴을 예로 들면 view에 해당하는 클래스가 Model에 비해 상대적으로 비대하다. 따라서 구조 작업에 있어서 이러한 view 그룹을 어떻게 분할하느냐에 따라서 유지보수성의 성패를 좌우한다고도 볼 수 있다.

위에서 좋은 코드를 위한 방법에 관하여 이야기를 했다. 연재에 실린 내용과 그것을 ActionScript에 맞게 수정하는 관계로 문장이 다소 매끄럽지 않는 점이 있을 것이다. 그런 부분은  이해해 주길 바란다.

사실 플래시는 타 언어에 비해서 버그가 발생할 가능성이 크다. 그 이유는 플래시의 강점인 모션을 항상 고민해야 하는 작업이기 때문이다. 일반적인 언어에서는 어떤 오브젝트가 나타나고 사라지는 사이에 시간이라는 개념을 포함할 필요성을 인식하지 못하지만 플래시는 모션에 따라 그 사이에 시간이 존재할 수 있다. 모션이 있는 사이에 예측하지 않은 사용자 반응이 있을 경우에는 버그가 발생할 수 있다. 플래시 개발자는 항상 이런 부분까지 예상하고 방어적 프로그래밍을 해야 한다.

위에서 이야기한 좋은 코드를 작성하는 것은 플래시의 작업에 많은 도움을 주지만 그런 것이 플래시 개발의 모든 약점을 보완해 주지는 못한다. 때로는 일반적인 좋은 코드로서의 방향에 영향을 주지 않는 범위 내에서 플래시의 장점을 극대화 할 수 있는 방법을 고민 해야 하는 것이다.

간혹 좋은 코드를 작성하는 방법을 물어보시는 분들이 있다. 물론 그것이 모르는 부분을 빨리 이해할 수 있는 좋은 방법일 수 있지만 쉽게 얻은 지식은 지식으로 머물러 지혜로 발전하지 못하는 경우가 많다. 더구나 본인 또한 그 방법을 찾고자 고민하고 배워가는 입장이기 때문에 도움을 주지 못하는 경우도 있다.

또한 플래시의 발전은 지식에 목말라 스스로 지혜를 얻으려 노력했던 선배들의 도움으로 커왔다고 나는 믿고 있기 때문이기도 하다. 위에서 이야기 한 내용은 본인 또한 노력하는 부분이고 이 포스트를 읽는 분들도 지혜를 얻기 위해 노력하다보면 좋은 코드를 생산할 수 있을 것이라 믿는다.



신고
    

설정

트랙백

댓글

[AS3] swf가 임베드된 html 경로 알아내기

Programming/ActionScript 3.0 2008.12.25 02:01
swf가 임베드 되어 있는 html의 도메인 경로를 알아내기 위해서는 아래와 같이 ExternalInterface를 이용하여 자바 스크립트 문법에 도움을 받아 location 경로를 리턴 받을 수 있다.

var locationUrl:String = ExternalInterface.call("document.location.href.toString");

그러나 이렇게 했을 경우 하나의 문제가 있다.

만약 임베드된 object의 id값을 설정하지 않거나 설정하더라도 동영상 플레이어처럼 여러 개의 swf를 하나의 페이지에 임베드 할 경우에는 오브젝트의 id값이 같기 때문에 ExternalInterface를 이용하여 자바스크립트 함수를 호출할 수 없는 상황이 발생한다. ExternalInterface를 사용할 경우 아래와 같은 경우에는 자바스크립트를 호출할 수 없다.

HTML 페이지의 Flash Player 인스턴스에 제공된 이름(object 태그의 id 속성)에 JavaScript에서 연산자로 정의된 하이픈(-)이나 다른 문자가 포함되어 있으면(예: +, *, /, \, . 등) Internet Explorer에서 컨테이너 웹 페이지를 보면서 ActionScript에서 ExternalInterface를 호출할 수 없습니다.
뿐 만 아니라 Flash Player 인스턴스를 정의하는 HTML 태그(object 및 embed 태그)가 HTML form 태그에 중첩되어 있는 경우에도 ActionScript에서 ExternalInterface가 호출되지 않습니다.

따라서 하나의 html 페이지에 여러 개의 같은 swf를 임베드 할 경우에는 각각의 object 아이디를 다르게 설정해 주어야 한다. 그러나 상황에 따라서 중첩되는 object태그들의 id를 서로 다르게 부여하기 위해서는 여러 가지 난제가 있을 수 있다. 그렇다면 object 의 id 값을 같게 하더라도 자바스크립트 함수를 호출할 수 있는 방법이 없을까? 위와 같은 상황이라면 IE가 파이어폭스처럼 object id가 같은 상황이라도 ExternalInterface가 자바스크립트 함수를 호출해 줄 수 있을 때까지 기다리거나 ExternalInterface가 위와 같이 호출할 수 없는 상황이 발생하지 않기를 바랄 수밖에 없다. 그렇다고 해결할 수 없는 문제로 남기기에는 플래시가 서운할 것이다. object id가 같아도 ExternalInterface가 자바스크립트 함수를 호출할 수 있도록 만들어 보자.

방법은 ActionScript에서 요긴하게 사용할 수 있는 SharedObject 객체이다. SharedObject객체는 기본적으로 도메인당 최대 100KB의 데이터로 공유 객체를 만들 수 있다. SharedObject의 data속성에는 String, Array, Number, Boolean, ByteArray, XML와 같은 공유 객체를 담을 수 있다.

위에서 언급한 상황처럼 같은 swf가 여러 개 임베드 되어 있고 object id가 같을 경우 IE에서는 첫번째 임베드 되어 있는 swf에서는 정상적으로 ExternalInterface를 통하여 자바스크립트를 호출해 준다. 그러나 아래에 임베드된 swf에서는 자바스크립트를 호출하지 못한다. 따라서 첫번째 임베드된 코드에서 제대로 리턴 받은 경로를 SharedObject를 이용하여 flush(쓰기)를 하고, 반면 ExternalInterface를 통해서 리턴 받은 값이 “null”일 경우에는 SharedObject를 통해서 저장된 기존의 domain 경로를 불러와서 사용하는 것이다.

아래는 그러한 방법으로 처리한 코드 예이다.

private function getLocationUrl():String
{
    var domain:String = String( ExternalInterface.call(" function(){ return document.location.href.toString();}"));
    var so:SharedObject = SharedObject.getLocal("location");
   
    if (domain != "null"){
        so.data.domain = domain;
        so.flush();
    }else{
        domain = so.data.domain
    }
   
    return domain;
}

위 방법은 그럴듯 하다. 그런데 문제가 있다. 브라우저가 자바스크립트를 호출해 주는 object를 먼저 참조하지 않는다는 것. 따라서 위 방법으로 했을 경우 페이지를 한 차례 재로드해 줘야 제대로 적용이 된다.

결국 object id 값을 서로 다르게 설정하는 것으로 가야만 했다는... 삽질의 길은 멀고도 험하구나...

신고
    

설정

트랙백

댓글

[AS3] interface 에 관한 이해

Programming/ActionScript 3.0 2008.12.17 17:04

이웃 블로그를 순방하다가 오랜만에 블로그에 글을 남기게 된다. 자수 블로그가 한동안 무척이나 푹~ 잠들었었다. 개인적으로 풀리지 않는 일들도 있고, 회사 일도 그렇고, 여러 가지로 말문을 열지 못하게 요인들은 앞으로 과감히 무시하고 이제는 조금씩 잃어버린 여유를 찾아서 나를 더욱 여유롭지 않게 해야겠다는 생각을 하게 된다.

오랜만에 땡굴이 형님의 불로그에 방문했다가 IEventDispatcher 인터페이스에 관한 설명들을 보고 보충해서 설명하고자 포스트를 쓴다. 일단 프로그래밍 언어(OOP언어)에서 인터페이스에 관한 것은 책에 많이 기술되어 있기 때문에 그 개념은 어느 정도 느끼고 있지만 그 활용 방법에 대해서는 구체적으로 떠오르지 않는 분들이 많은 것 같다. 사실 나 또한 그러하다. 설명하고자 하는 내용은 지극히 개인적인 작업을 바탕으로 설명하는 것임을 밝혀둔다.

단적으로 말해서 인터페이스는 open 개념이 아닌 close 개념에 가깝다. 이 이야기는 객체를 개방하는 것이 아닌 하나의 종속된 틀 속에 관련 객체를 구속하여 외부의 다른 잡음으로부터 보호하고 다른 객체와 대체할 수 있는 기반을 만드는 것이 주 목적 이라는 것이다.

예를 들어 “문” 이라고 일컬어 지는 것들은 대부분 공통의 요소가 존재한다. 손잡이, 열림, 닫힘…, 이런 요소들은 문이 창문이건, 여닫이, 미닫이이건 간에 상관없이 공통으로 사용할 수 있는 요소일 것이다. 따라서 프로그래밍 언어에서는 이러한 공통의 요소를 하나의  interface로 만들어 implements 키워드를 통해서 구체적인 구현을 클래스에서 이행하게 되는 것이다.

그렇다면 의문이 생길 수 있다. 이렇게 한다고 하여 개발자 입장에서 더 좋은 것이 무엇이냐고…, 물론 인터페이스를 굳이 사용할 필요가 없는 곳에서 사용을 하게 되면 손가락만 피곤하다. 하지만 위에서 언급한 “문”이라는 객체가 프로젝트 안에서 하나가 아니라 대문, 창문, 화장실문, 서랍장문… 등이 사용되고 이러한 문들은 행위자(문을 사용하는 사람)에 따라서 어떤 문이건 간에 손잡이를 잡고 열고 닫을 수 있어야 한다면 이야기는 달라진다.

여기서 간단한 클래스 구조를 통해서 위에서 이야기한 부분에 대해서 다시 한번 생각해 보도록 하자.

문인터페이스 : IDoor
package {
    public interface IDoor {
        function open():void;
        function close():void;
    }
}


대문 : Gate <- IDoor
package {
    public class Gate implements IDoor {
        public function Gate() {
        }
        public function open():void {
            trace("open in Gate Class");
        }
  
        public function close():void {
            trace("close in Gate Class");
        }
    }
}

창문 : Window <-- IDoor
package {
    public class Window implements IDoor {
        public function Window() {
   
        }
        public function open():void {
            trace("open in Window Class");
        }
  
        public function close():void {
            trace("close in Window Class");
        }
    }
}

행위자 : Person
package {
    import flash.events.Event;
    import flash.events.EventDispatcher;
    public class Person extends EventDispatcher{
  
        static public const OPEN_DOOR:String = "openDoor";
        static public const CLOSE_DOOR:String = "closeDoor";
        private var _door:IDoor;
  
        public function Person(inDoor:IDoor) {
            _door = inDoor; 
        }

        private function doAction(isOpen:Boolean):void {
            if(isOpen){
                _door.open();
                dispatchEvent(new Event(OPEN_DOOR));
            }else {
                _door.close(); 
                dispatchEvent(new Event(CLOSE_DOOR));
            }
        }
    }
}

메인 클래스 : MainDocument
package {
    import flash.events.Event;
    public class MainDocument extends Sprite {
        private var _person:Person;
        public function MainDocument() {
            _person = new Person(new Gate()); // or _person = new Person(new Window());
            _person.addEventListener(Person.OPEN_DOOR, onOpenHandler);
          _person.addEventListener(Person.CLOSE_DOOR, onCloseHandler);

            _person.doAction(true); // or _person.doAction(false);
        }
  
        private function onOpenHandler(e:Event):void {
            trace("open in Person Class");
        }
  
        private function onCloseHandler(e:Event):void {
            trace("close in Person Class");
        }
    }
}

위에서 설명한 바와 같이 클래스들을 보면 어느 정도 이해할 수 있으리라 생각한다. Person 클래스에서 정의한 속성 _door은 IDoor 인터페이스 형으로 선언이 되었다. 그리고 이 속성에는 메인 클래스에서 Gate 또는 Window 인스턴스를 생성하여 Person 생성자에 인자로 전달하여도 문제 없이 객체를 담을 수 있다. 이렇게 할 수 있는 이유는 IDoor 인터페이스에서 정의한 함수(open, close)를 두 객체(Gate, Window)에서 포함하고 있으며 IDoor 인터페이스를 implements 키워드를 통해서 이행하고 있기 때문에 가능한 일이다. 다시 풀어서 이야기하면 _door 속성에 할당된 오브젝트는 무조건 open, close 함수를 사용할 수 있다는 것으로 곧 IDoor 인터페이스를 이행하고 있는 클래스라면 모두 허용한다는 것이다.

따라서 위와 같이 클래스 구조를 작성하게 되면 MainDocument 클래스 내에서 Person을 생성할 때 인자로 전달하는 객체는 IDoor 인터페이스를 이행하고 있는 모든 클래스를 담을 수 있게 되는 것이다. 이것이 OOP언어에서 장점으로 이야기하는 폴리모피즘(다형성)에 기초한다.

추가적으로 위에서 Person 클래스에서는 EventDispatcher를 상속 받고 있다. EventDispatcher 클래스는 다시 IEventDispatcher 인터페이스를 이행하는 클래스로서 자신으로부터 이벤트를 전달하는 역할을 할 수 있는 권한을 갖게 된다.(권한을 갖는다는 의미는 EventDispatcher클래스를 상속함으로써 EventDispatcher클래스 안에서 이미 정의되어 있는 함수들을 사용할 수 있다는 의미)

그러므로 위 코드와 같이 Person 클래스 내에서는 doAction 함수의 분기에 따라서 이벤트를 전달(방송)할 수 있으며, MainDocument 클래스 내에서는 생성한 Person 객체의 인스턴스로부터 전달되는 이벤트를 청취할 수 있도록 addEventListener 메소드를 통해서 핸들러 함수를 정의할 수 있는 것이다.

그렇다면 EventDispatcher 클래스만 상속하면 모든 문제가 해결될 것 같다. 하지만 그렇지만은 않다. ActionScript는 extends 키워드를 통해서 상속 받을 수 있는 것은 하나의 슈퍼클래스만 가능하다. 자바의 경우는 복수의 슈퍼클래스 정의할 수 있으나 ActionScript는 그렇지 않다. 따라서 Person 클래스가 Kind라는 클래스를 상속해야만 하는 상황이라면 어떻게 해결할 수 있을까? 이럴 때는 Person클래스에서 Kind 클래스를 상속하고 implements 키워드를 통해서 IEventDispatcher 인터페이스를 이행하여 IEventDispatcher 인터페이스에 있는 함수들을 구체적으로 기술하는 것으로 해결할 수 있다.

또 한가지 방법은 OOP에서는 상속이라는 기술만이 폴리모피즘을 구현할 수 있는 것이 아니라 위임 또는 합성을 사용할 수도 있다. 이는 extends 키워드를 통해서 특정 슈퍼클래스를 상속하는 형태가 아니라 생성자, 또는 함수를 통해서 전달된 객체를 클래스 내부에서 연결 구현해줌으로써 다형성을 만들어 낼 수가 있다. 이때 필요한 것이 바로 인터페이스라 할 수 있다.(인터페이스는 implements 키워드를 통해서 복수의 인터페이스를 이행할 수 있다.)

쓰다 보니 내용이 길어졌다. 위 코드는 포스트를 작성하면서 개념을 설명하기 위하여 임의로 작성한 코드인 관계로 실제 컴파일에서 에러가 발생할 수 있다.

신고
    

설정

트랙백

댓글

[AS3] ActionScript의 방향성

Programming/ActionScript 3.0 2008.09.03 23:50
FlashPlayer9 발표 이후 adobe에서는 ActionScript를 ECMA-262 Edition 4 (ES4)에 준하도록 진행한다는 이야기가 있었다. ES4는 현재 Adobe, Mozilla, Opera, Google등이 주축이 되어 표준화가 진행 중이지만 작년 Microsoft와 Yahoo 주도로 ECMA-262 Edition 3.1(ES 3.1)의 워킹 그룹이 결성된 이후 위와 같이 두 개가 다른 ES3 후속 안으로 병존하는 상황이 계속되고 있다고 한다.

ES3.1은 ES4의 부분집합이 아니기 때문에 ActionScript 3.0은 ES3.1와 호환되지 않는다. 예를 들어 ES3.1의 문법에는 namespace, package가 포함되지 않는다. ES3.1은 추가 버전업을 진행하더라도 이러한 문법은 포함하지 않을 것을 분명히 하고 있다.

이러한 상황에 대해서 Adobe측에서는 정식으로 코멘트를 하고 있지 않지만 Adobe의 오픈소스팀 디렉터 Dave는 블로그를 통해서 아래와 같이 이야기를 전하고 있다.

* ECMAScript 표준화 위원회 참가 기업의 이해 불일치에 의하여 사양의 일원화가 어려운 상황이다.
* Web에 공통의 언어를 가지는 것이 필요하다는 관점으로부터 Harmony에 찬성한다.
* Web의 혁신을 더욱 진행하기 위해 ActionScript의 확장은 계속된다.
* 오픈소스 커뮤니티 활동의 발전도 표준화 위원회와의 활동과 함께 중요하게 인식하고 있다.

ECAMScript의 표준화에는 계속 참가하지만 ES3.1의 레벨까지 AS3의 기능을 되돌리는 것은 생각하지 않는 것으로 보인다. 눈에 보이는 제품의 성능에 대해서는 어느 것이 좋다 나쁘다로 판단할 근거가 명확하지만 이러한 프로그래밍 언어적 문법에 대한 표준화에는 상당한 이해관계가 얽혀 있는 듯하다.

처음 언어를 배우는 사람에게는 영어이건, 독일어이건 처음 배우는 과정에서 생산되는 엔트로피는 비슷하겠지만 이미 영어를 배우고 익숙한 학생이 독일어를 접하면 적지 않게 혼란스러운 것이 사실이다. 물론 자연어와 다르게 컴퓨터로 명령을 내리는 프로그래밍 언어의 경우는 그 안의 알고리즘 측면에서 본다면 크게 다르지 않겠으나 나와 같이 천천히 흘러가는 뇌의 전류로는 감당하기 버거울 것이다.

AS2에서 AS3로 넘어오면서 가장 나를 혼란스럽게 했던 것은 Void와 void, _alpha와 alpha, _alpha = 100과 alpha=1이었다. 지금도 그 추억에 사로잡혀 멤버변수로 _alpha와 같이 쓰고 있다. 쿠쿠

신고
    

설정

트랙백

댓글

[CS4] Adobe Creative Suite 4 (CS4) 의 발표일 공개

Programming/ActionScript 3.0 2008.09.03 23:11
Adobe 사이트에서 CS4의 발표 일정이 공개되었다. 한국판이 나오기 까지는 한두달 소요될 것으로 예상되지만  영문판이 9월 23일 예정이므로 실질적으로 사용자 컴퓨터에 FlashPlayer10 업데이트가 되는 것은 올해 안으로 50%가 넘지 않을까 싶다.

신고
    

설정

트랙백

댓글

[AS3] DigiMix

Programming/ActionScript 3.0 2008.07.23 13:27
http://www.digimix.com/blog/

http://www.digimix.com/alpha/















사용자 삽입 이미지

신고
    

설정

트랙백

댓글

[FlashPlayer10] Flash Player 10에서 보완된 기능 리스트

Programming/ActionScript 3.0 2008.07.21 14:55
< 사용자 필터&효과 >

After Effects CS3에서도 사용되고 있는 Adobe Pixel Bender를 사용하여 사용자 필터, 브랜드 모드 등을 작성할 수 있습니다. Pixel Bender는 하이 퍼포먼스 화상 처리 언어입니다. Pixel Bender를 사용하여 Flash Player의 업데이트를 통해서 주어진 효과를 사용하는 것이 아닌 개발자의 독자적인 효과나 필터를 추가할 수 있습니다. 독자적인 효과나 필터는 기존의 Flash Player에 탑재되고 있는 필터와 함께 사용할 수 있습니다. 또 벡터, 비트맵, 비디오 등 모든 오브젝트에 적용할 수 있으며 오브젝트가 가지고 있는 인터랙티브적인 성질은 그대로 유지합니다. 사용자 효과는 런타임으로 적용할 수 있습니다. 또한 사용자 필터의 사이즈는 보통 1KB정도의 파일 사이즈를 갖기 때문에 플래시 콘텐츠의 용량 증가의 문제가 발생하지 않습니다.

 

< 3 차원 3D효과 >

모든 2차원 오브젝트를 3차원 형태로 변형할 수 있습니다. 사용자 필터와 같이 2차원 오브젝트가 가지고 있던 인터랙티브 성질은 그대로 유지됩니다. 기존의 Papervision3D나 Sandy, Away3D를 사용하여 3차원 처리를 고속으로 처리할 수 있습니다. 플래시툴에 기본적으로 제공하는 3차원 변형을 통해서 복잡한 3차원 처리를 간단한 코드로 기술할 수 있습니다.

 

< 새로운 텍스트 엔진 >

새로운 텍스트 엔진은 매우 유연한 텍스트 레이아웃을 제공합니다. 기존에 텍스트, 컨트롤을 통해 새로운 텍스트를 만드는 기술 보완했으며, 사용하기 쉬운 API를 제공합니다. 이로서 다양한 환경에서의 텍스트를 사용할 수 있게 되었습니다. Right-to-Left 식의 세로 형태의 레이아웃 타이포그래피도 지원됩니다.

 

<  텍스트 레이아웃 컴퍼넌트 >

레이아웃과 스타일 텍스트, 테이블, 인라인 이미지, 그리고 열 정렬을 지원합니다. 이로써 신문과 같이 복잡한 테이블 형태를 지원하게 되어 텍스트필드를 다양하게 사용할 수 있게 되었습니다.(기존에는 이러한 텍스트 필드 자체에서 지원되는 기능적 제약이 있어서 디스플레이 오브젝트에서 처리 프로세스를 따로 만들어야 하는 번거로움이 있었다.)

 

< 랜더링 API의 개선 >

기존에 화면처리에서는 특정 shape를 화면에 표시하고자 할 때 기존에 화면상에서 존재하던 그래픽 객체를 지우고 다시 그려야 하는 문제로 인하여 메모리나 성능 면에서 불필요한 시스템 자원을 낭비했었습니다. 이번 버전에서는 그러한 문제점을 보완하기 위해서 특정 shape의 일부분만을 교체할 수 있는 것이 가능하게 되어 메모리와 성능 면에서 효율적으로 처리할 수 있게 되었습니다.

 

< 컬러 관리 >

Flash Player 10은 비교적 정확한 색을 재현할 수 있게 되었다. 컬러 관리에서 swf를 sRGB 색공간(color space)으로 변경가능하며, 컬러 관리는 모니터의 ICC 칼라 프로파일과 동조하여 SWF의 색을 정확하게 재현합니다. 이는 런타임에서 실시간 컬러를 swf에 적용할 수 있습니다.

 

< 시각적인 퍼포먼스 개선 >

기존의 랜더링은 CPU가 모두 처리하였던 것을 GPU를 통해서 처리할 수 있게 되었습니다. 이로써 기존에 CPU처리 만으로 한계가 있던 화면처리 포퍼먼스를 향상시킬 수 있는 길이 열렸습니다. 결과적으로 그래픽 카드(하드웨어가속)을 이용하여 어플리케이션이 보다 매끄러운  반응 속도를 낼 수 있도록 향상되었습니다. (길이 열렸다는 의미는 아직은 GPU를 사용한 최적화에 따른 개발적인 이슈와 그에 대한 개발 가이드가 아직 나와 있지 않은 상태이기 때문이다. 앞으로 개발에 따른 문제점은 보완될 것이라 기대해 본다.)

 

< GPU로의 화상 처리 >

화상을 합성·필터의 적용·비디오의 재생이 보다 빠르게 실시할 수 있게 되었습니다. 모든 래스터 오브젝트는 비디오 카드를 사용해 처리됩니다. 하드웨어로 비트맵, 필터, 비디오를 처리하는 것은 기존의 소프트웨어로 랜더링하던 것보다도 빠르게 재생할 수 있습니다. GPU로 화상 처리하는 것은 HTML 파라미터로 사용 여부를 설정할 수 있으며 하드웨어를 사용할 수 있는 사용자 시스템에서만 적용됩니다. 사용자 하드웨어가 GPU를 사용할 수 없는 경우에는 기존대로 소프트웨어로 처리 합니다. Open GL 2.0으로 GLSL를 지원하고 있는 비디오 카드가 있는 경우에만 이 기능을 사용할 수 있습니다. Flash Player의 베타판에서는  GPU 가속으로 처리되고 있을 때는 좌측 상단에 녹색의 사각이 표시됩니다.

 

< GPU로의 비트맵 전송 >

새로운 HTML 파라미터의 설정을 통해서 SWF를 브라우저에 표시 할 때에 비디오 카드를 사용하도록 할 수 있습니다. 이 기능은 SWF를 새롭게 컴파일 할 필요 없이 사용할 수 있습니다. 이 기능은 Flash Player 9 Update 3으로 도입된 풀 스크린으로의 비디오 표시에 하드웨어 가속화 기능을 확장한 것으로, 랜더링 포퍼먼스가 개선되어 CPU 사용도 효율적으로 관리할 수 있습니다. 결과적으로 비디오나 화상을 사용하는 어플리케이션의 포퍼먼스가 개선됩니다.

 

< Anti-Aliasing 제거 엔진(Saffron 3.1) >

Saffron를 개량하여 특히 아시아권의 언어를 렌더링 할 때의 Anti-Aliasing 제거의 포퍼먼스나 품질이 개선되었습니다. 또 stroke 폰트를 지원하여 필요한 메모리를 경감시켰습니다.

 

< Vector 데이터형 >

Flash Player 10의 ActionScript 3.0에서는 ECMAScript 4를 통해서 제안되고 있는 Vector 데이터형을 지원합니다 .Vector 데이터형은 배열과 비슷하지만 요소가 모두 같은 형태가 아니면 안 되는 제약이 있습니다. 요소의 형태에 제약을 두는 것으로 배열 조작보다 포퍼먼스가 획기적으로 개선되었습니다. (일반 배열을 사용할 때 보다 10배 이상의 속도향상이 기대됨)

 

< 리치 미디어 >

Flash Player 10에서는 다음의 Adobe Flash Media Server나 다른 Adobe 제품으로 제공될 예정인 새로운 오디오·비디오 기능을 탑재했습니다.

 

< 다이나믹스 트리밍 >

네트워크의 상황에 따라 항상 최적인 비디오 품질을 유지하는 것이 가능하게 되었습니다. bit rate를 변화시키는 것을 통해서 비디오가 재생 중에 끊김이 없도록 할 수 있습니다. 이는 비디오 시청자의 대역에 맞는 비디오 전달이 가능하다는 것을 의미합니다. 앞으로 Flash Media Server에서는 네트워크의 상황에 따라 RTMP로의 비디오 전달로 bit rate를 변화시킬 수 있습니다. 리얼타임에 네트워크 상황과 CPU의 상황을 ActionScript로 확인할 수 있으므로 개발자는 그러한 정보를 바탕으로 비디오 전달을 컨트롤 할 수 있습니다. ( 동영상 플레이어로 동영상을 보다가 플레이되는 속도보다 다운로드되는 속도가 느릴 경우 중간에 버퍼링 시간으로 딜레이 되는 것을 개선할 수 있다는 의미)

 

< RTMFP (Real Time Media Flow Protocol) >

RTMFP는, 기존의 RTMP over TCP로 가고 있던 시큐어인 데이터 전달을 UDP로 실시하는 것입니다.이 기능을 이용하기 위해서는 앞으로 릴리스 되는 Flash Media Server나, 다른 Adobe 제품을 사용할 필요가 있습니다. UDP는 영상 전달을 위한 효율적인 프로토콜입니다. RTMFP로의 통신은 암호화를 위해서 영상을 보호할 수 있습니다. 이 기술은 2006년에 매수한 Amicima, Inc.에 의하는 것입니다 .

 

< File Reference runtime access >

Flash Player 9에서도 FileReference를 사용해서 로컬 파일에 액세스 하는 것은 가능합니다만 이 기능은 파일의 업 로드/다운로드를 수행하기 위한 것으로 Flash 어플리케이션에서는 일단 서버를 경유하지 않으면 로컬 파일의 데이터를 사용할 수가 없었습니다. 하지만 Flash Player 10에서는 파일의 읽고 쓰기를 위해서 서버를 사용할 필요가 없이 플래시 어플리케이션에서 읽어 들인 데이터를 바로 사용할 수 있게 되었습니다.

 

Flash Player 10의 flash.net.FileReference 클래스에 추가된 API

public function get data():ByteArray // 읽힌 데이터 (독해 전용의 프롭퍼티)

public function load():void          // 지정된 파일의 읽기 개시

public function save(data:*, name:String = null):void  // 저장처를 선택하는 다이얼로그를 표시, 그 후 데이터 저장

 

 

< 다이나믹 사운드 제너레이션 >

사운드 클래스는 사운드 오브젝트에 등록된 이벤트 리스너를 통하여 동적으로 생성된 오디오를 재생 가능하게 되었습니다.

 

< Large Bitmap Support >

Flash Player 10에서는 기존에 2880 x 2880픽셀로 제한했던 비트맵 최대 사이즈를16,777,216 픽셀(4096×4096 픽셀)까지 비트맵을 취급할 수 있게 되었습니다. (AIR를 통해서 듀얼 모니터에서 스트린캡쳐한 비트맵 데이터를 가공할 때 2880 사이즈를 벗어나 생겼던 에러로 고민한 적이 있는데 이를 통해서 쉽게 해결되었다.)

 

< context menu >

context menu용 ActionScript API를 사용하여 context menu를 좀더 자유롭게 제어가 가능하게 되었습니다. 표준 텍스트와 리치 텍스트의 양쪽 모두를 지원하고 있습니다. 클립보드 메뉴를 통해서 안전하게 제어된 방법으로 클립보드에 액세스 할 수 있으므로 텍스트의 페이스트용 핸들러를 쓸 수도 있습니다.

 

http://labs.adobe.com/technologies/flashplayer10/releasenotes.html


 

 

신고
    

설정

트랙백

댓글

[AS3] Andre Michelle의 AudioTool

Programming/ActionScript 3.0 2008.07.08 23:03
요즘 Andre Michelle이 음향 관련 놀이를 자주 한다는 생각을 했는데 결국 이런 멋진 결과물을 만들어 냈다. 오디오나 음향 관련 지식이 부족한 나에게 이걸 가지고 놀아보라고 하면 아마도 헬리콥터 장난감을 물 속에서 가지고 놀지 싶다. 플래시와 자바를 가지고 개발 했다고 하는데 앙드레의 고집스런 뚝심을 따라가려면 아직도 멀었다는 생각이 든다.










사용자 삽입 이미지

http://www.hobnox.com/index.1056.en.html
신고
    

설정

트랙백

댓글

[Flash Player10] 의 GPU 서포트 기능에 관하여...

Programming/ActionScript 3.0 2008.05.24 12:27
Flash Player10 의 GPU 서포트에 대해 Flash Player 팀의 스페셜리스트 Tinic이 blog에서 설명하고 있다.(What does GPU acceleration mean?)

Flash Player10에서는 direct 와 gpu 모드가 새롭게 추가된다.

•    direct 모드 : 화면에 최단 패스로 표시 하고 싶을 때에 사용한다.이 모드에서는 브라우저가 표시하는 영역과 Flash Player의 표시 영역이 겹쳐도 대체로 브라우저 측이 무시된다고 한다. 주로 비디오 재생에서 사용될 것으로 보인다.
•    gpu 모드 : 그래픽 카드의 기능을 이용해 오브젝트의 합성을 실시한다. 대부분의 무비 클립의 랜더링은 기존의 방식대로 소프트웨어가 처리한다.
이러한 GPU 서포트 모드를 이용하기 전에 우선 아래와 같은 사항을 주의해야 한다고 한다.
그래픽 카드의 기능을 사용했다고 반드시 랜더링이 빨리 되는 것은 아니다. 오히려 늦어지는 경우가 더 많을 가능성도 있다고 한다. GPU의 효과를 끌어 내려면 컨텐츠를 디자인할 때 GPU의 동작을 이해해 거기에 맞추어 설계할 필요가 있다고 한다. 그것을 위한 정보나 전용의 가이드는 가능한 한 빠른 시기에 제공할 수 있도록 할 것이라고 이야기 한다.

flashPlayer10에서는 아직은 시작 단계인 기능들이 새롭게 추가되고 있다. GPU기능은 아직은 시기상조이지만 앞으로 플래시의 성능을 높이는데 큰 역할을 할 것이라 본다. FileReference 또한 플래시가 서버와의 통신에 제한적이었던 부분을 좀더 자유로운 개발이 가능하도록 하고 있다.

New Text Engine이나 Text Layout Components 들은 html상의 레이아웃을 플래시 안에서 TextField로 구현이 가능할 것으로 본다. 다만 한글의 경우도 제대로 표현되기를 바란다.

이러한 성능의 발전 방향을 볼때 앞으로 플래시가 온라인과 오프라인의 경계를 무너뜨리는 크로스어플리케이션을 만드는 프로그래밍 언어의 중심이 될 수 있을 것으로 본다.
신고
    

설정

트랙백

댓글

[AS3] TweenMax Bezier Tweening Released by Jack

Programming/ActionScript 3.0 2008.04.06 21:33
TweenLite와 TweenFilterLite를 만들었던 jack이 이번에 TweenMax를 릴리즈하였다. 이번 TweenMax에서는 bezier 곡선을 지원한다.

http://blog.greensock.com/tweenmaxas3/


Tweener와 속도 비교해보면 많은 오브젝트를 tween 시킬 때의 속도 차이를 느낄 수 있다.

http://blog.greensock.com/bezier-speed-test/
신고
    

설정

트랙백

댓글

[AS3] FlashPlayer UPDATE3의 System.gc()

Programming/ActionScript 3.0 2008.02.09 22:52
예전에 System.gc 내장 함수가 없었던 것으로 알고 있는데 업데이트 버전에서 생겨난 듯 하다. 기존에는 개발자가 강제로 CG를 실행시킬 수 없어서 완전한 메모리 테스트는 사실상 불가능 했다. 물론 강제로 많은 오브젝트를 생성하는 과정에서 일정한 메모리(FlashPlayer가 예상하는 적정 사용영역)을 벗어날 경우를 임의로 만들어서 확인할 수는 있었으나 이것 또한 어느 정도의 메모리 영역을 CG가 동작하는 시작점으로 보는가를 알 수 없기 때문에 어려움이 있었다.

System 클래스에서 지원하는 메소드에 gc가 있다. 이는 자바에서의 System.gc와 같이 이 함수를 실행하는 시점에서 강제로 CG가 동작하게 된다. 문제는 자바와 같이 플래시도 full gc를 할 경우 시스템 리소스 낭비가 예상된다는 것이다. System.gc 실행은 개발 테스트 과정에서 사용하되 작업물에서의 동적인 강제 실행은 피하는 것이 좋을 듯 싶으나 이는 이미 웹상에서는 System.gc가 작동하지 않는 듯 싶다. 경험상 flashPlayer9버전의 가비지콜렉터는 상당히 안정적이고 신뢰 할만 하다.

아래 예제로 만든 것을 보면 replay 버튼을 누름과 동시에 Event.ENTER_FRAME 이벤트가 등록된 sprite를 통해서 이벤트가 발생할 때마다 임의로 TextField를 생성과 소멸을 반복한다. 50개의 TextField가 생성되는 시점에서 Event.ENTER_FRAME가 등록된 sprite를 메모리해서 해제시킨다.

여기서 많은 개발자들이 Event.ENTER_FRAME 이벤트가 적용된 상태에서 removeEventListener로 이벤트를 해제하지 않고 sprite를 메모리 해제 시도를 했기 때문에 메모리에서 지워지지 않는다고 생각하는 듯 하다. 나도 예전에는 이 부분이 상당히 모호했다.  addEventListener로 강참조를 할 경우라도 역참조가 아닌 이상은 이벤트가 적용된 오브젝트가 메모리에서 해제하고 GC를 통해서 free memory가 되면 자동으로 등록되어 있는 이벤트도 발생하지 않는다.

여기서 혼돈했던 이유는 개발자가 강제로 가비지콜렉션을 실행하지 못하는 상황에서 sprite가 메모리에서 해제되지 않아서 이벤트가 계속 발생하는 것인지, gc가 동작하지 않아서 해제되지 않는 것인지 모호했기 때문이다.

아래의 예제에서 gc 동작버튼을 누르지 않더라도 일정한 시간이 지나면 flashplayer가 적당한 시점에서 gc를 가동하여 Event.ENTER_FRAME 이벤트가 등록된 sprite를 메모리에서 해제하면서 Event.ENTER_FRAME 이벤트도 발생하지 않게 된다. System.gc를 이러한 gc의 동작을 강제로 실행하므로써 개발자에게 즉시 free memory가 되었다는 것을 알려주므로 개발과정에서 요긴하게 사용될 듯싶다.

아래 swf상에서는 System.gc 함수가 실행되지 않는다. fla를 다운로드하여 로컬상에서 테스트 해보길 바란다.




신고
    

설정

트랙백

댓글

[AS3] Tween Engine (TweenLite)

Programming/ActionScript 3.0 2008.02.05 19:28
TweenBencher 을 이용해서 AS3용 Tween 엔진을 테스트 해 봤다. 1등은 단연 Simple AS3 Tween으로 나왔는데 이것은 따로 클래스화 되어 있는 것이 아니고 fl.motion.easing.Exponential 의 easeInOut 함수만을 따로 빼서 직접 width을 적용한 형태라서 빠를 수 밖에 없다. 테스트를 하지 않은 fl 기본 트윈 클래스나 기타 다른 것들은 비교할만한 속도를 내지 못하여 제외 하였다.

비교 대상이 되는 것들은 Tweener TweenLite이다. Tweener은 그 사용법과 Bezier 곡선 처리 기능을 제공하기 때문에 사용성면에서 좋은 엔진이지만 많은 기능들이 포함되어 있는 만큼 속도는 약간 떨어진다.








Benchmark results: Simple AS3 Tween
------------------
125 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 57
250 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 57
500 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 55
1000 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 47
2000 Sprites    ::    Start Lag: 0.01 seconds    ::    FPS: 38
4000 Sprites    ::    Start Lag: 0.06 seconds    ::    FPS: 26

Benchmark results: TweenLite
------------------
125 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 61
250 Sprites    ::    Start Lag: 0 seconds    ::    FPS: 55
500 Sprites    ::    Start Lag: 0.01 seconds    ::    FPS: 40
1000 Sprites    ::    Start Lag: 0.01 seconds    ::    FPS: 31
2000 Sprites    ::    Start Lag: 0.02 seconds    ::    FPS: 23
4000 Sprites    ::    Start Lag: 0.18 seconds    ::    FPS: 19

Benchmark results: Tweener
------------------
125 Sprites    ::    Start Lag: 0.11 seconds    ::    FPS: 56
250 Sprites    ::    Start Lag: 0.02 seconds    ::    FPS: 61
500 Sprites    ::    Start Lag: 0.06 seconds    ::    FPS: 46
1000 Sprites    ::    Start Lag: 0.2 seconds    ::    FPS: 33
2000 Sprites    ::    Start Lag: 0.45 seconds    ::    FPS: 25
4000 Sprites    ::    Start Lag: 1.4 seconds    ::    FPS: 17

TweenLite는 filter에 관련된 기능들은 TweenFilterLite로 따로 빠져있다. 특별한 기능이 아닌 오브젝트의 모션 처리는 x,y,scaleX, scaleY, width, height, alpha 등과 추가하면 color 적용일 것이다. TweenLite는 tint 속성으로 그것을 대신하고 있다. 기본적인 사용법은 아래와 같다.

import gs.TweenLite;
TweenLite.to(mc, 1, {x:46, y:43, scaleX:1, scaleY:1, rotation:0, alpha:1, tint:0x3399ff});

사용법이 Tweener와 많은 부분 흡사하다. mc는 타겟이되는 Object, 1은 모션 time이고 {} 오브젝트 형태로 적용 속성들과 모션 함수를 적용할 수가 있다. ease 속성으로 모션 함수를 적용할 수가 있는데 함수는 fl.motion.easing 패키지 안에 있는 함수를 적용하게 되므로 fl.motion.easing 패키지를 import 시켜서 사용하면 된다. 그리고 Tweener에서와 같이 onStart, onUpdate, onComplete 를 지원한다. 아래 예,

        import gs.TweenLite;
        import fl.motion.easing.Back;
        TweenLite.to(mc, 5, {alpha:0.5, x:120, ease:Back.easeOut, delay:2,
                                        onComplete:onFinishTween, onCompleteParams:[5, clip_mc]});
        function onFinishTween(argument1:Number, argument2:MovieClip):void {
                trace("The tween has finished! argument1 = " + argument1 + ",
                and argument2 = " + argument2);
        }



USAGE

TweenLite.to(target:Object, duration:Number, variables:Object);

  • Description: Tweens the target's properties from whatever they are at the time you call the method to whatever you define in the variables parameter.
  • Parameters:
    1. target: Target MovieClip (or any object) whose properties we're tweening
    2. duration: Duration (in seconds) of the tween
    3. variables: An object containing the end values of all the properties you'd like to have tweened (or if you're using the TweenLite.from() method, these variables would define the BEGINNING values). Putting quotes around values will make the tween relative to the current value. For example, x:"-20" will tween x to whatever it currently is minus 20 whereas x:-20 will tween x to exactly -20. Here are some examples of properties you might include:
      • alpha: The alpha (opacity level) that the target object should finish at (or begin at if you're using TweenLite.from()). For example, if the target.alpha is 1 when this script is called, and you specify this parameter to be 0.5, it'll transition from 1 to 0.5.
      • x: To change a MovieClip's x position, just set this to the value you'd like the MovieClip to end up at (or begin at if you're using TweenLite.from()).

      Special Properties (**Optional**):

      • delay: The number of seconds you'd like to delay before the tween begins. This is very useful when sequencing tweens
      • ease: You can specify a function to use for the easing with this variable. For example, fl.motion.easing.Elastic.easeOut. The Default is Regular.easeOut.
      • easeParams: An array of extra parameter values to feed the easing equation. This can be useful when you use an equation like Elastic and want to control extra parameters like the amplitude and period. Most easing equations, however, don't require extra parameters so you won't need to pass in any easeParams.
      • autoAlpha: Same as changing the "alpha" property but with the additional feature of toggling the "visible" property to false if the alpha ends at 0. It will also toggle visible to true before the tween starts if the value of autoAlpha is greater than zero.
      • volume: To change a MovieClip's volume, just set this to the value you'd like the MovieClip to end up at (or begin at if you're using TweenLite.from()).
      • tint: To change a MovieClip's color, set this to the hex value of the color you'd like the MovieClip to end up at(or begin at if you're using TweenLite.from()). An example hex value would be 0xFF0000. If you'd like to remove the color from a MovieClip, just pass null as the value of tint. Before version 5.8, tint was called mcColor (which is now deprecated and will likely be removed at a later date although it still works)
      • onStart: If you'd like to call a function as soon as the tween begins, pass in a reference to it here. This can be useful when there's a delay and you want something to happen just as the tween begins.
      • onStartParams: An array of parameters to pass the onStart function.
      • onUpdate: If you'd like to call a function every time the property values are updated (on every frame during the time the tween is active), pass a reference to it here.
      • onUpdateParams: An array of parameters to pass the onUpdate function (this is optional)
      • onComplete: If you'd like to call a function when the tween has finished, use this.
      • onCompleteParams: An array of parameters to pass the onComplete function (this is optional)
      • overwrite: If you do NOT want the tween to automatically overwrite any other tweens that are affecting the same target, make sure this value is false.




TweenLite.from(target:Object, duration:Number, variables:Object);

  • Description: Exactly the same as TweenLite.to(), but instead of tweening the properties from where they're at currently to whatever you define, this tweens them the opposite way - from where you define TO where ever they are now (when the method is called). This is handy for when things are set up on the stage where the should end up and you just want to animate them into place.
  • Parameters: Same as TweenLite.to(). (see above)




TweenLite.delayedCall(delay:Number, onComplete:Function, onCompleteParams:Array);

  • Description: Provides an easy way to call any function after a specified number of seconds. Any number of parameters can be passed to that function when it's called too.
  • Parameters:
    1. delay: Number of seconds before the function should be called.
    2. onComplete: The function to call
    3. onCompleteParams [optional] An array of parameters to pass the onComplete function when it's called.




TweenLite.killTweensOf(target:Object, complete:Boolean);

  • Description: Provides an easy way to kill all tweens of a particular Object/MovieClip. You can optionally force it to immediately complete (which will also call the onComplete function if you defined one)
  • Parameters:
    1. target: Any/All tweens of this Object/MovieClip will be killed.
    2. complete: If true, the tweens for this object will immediately complete (go to the ending values and call the onComplete function if you defined one).




TweenLite.killDelayedCallsTo(function:Function);

  • Description: Provides an easy way to kill all delayed calls to a particular function (ones that were instantiated using the TweenLite.delayedCall() method).
  • Parameters:
    1. function: Any/All delayed calls to this function will be killed.


EXAMPLES


As a simple example, you could tween the alpha to 50% (0.5) and move the x position of a MovieClip named "clip_mc" to 120 and fade the volume to 0 over the course of 1.5 seconds like so:
  1. import gs.TweenLite;
  2. TweenLite.to(clip_mc, 1.5, {alpha:0.5, x:120, volume:0});



If you want to get more advanced and tween the clip_mc MovieClip over 5 seconds, changing the alpha to 50% (0.5), the x coordinate to 120 using the Back.easeOut easing function, delay starting the whole tween by 2 seconds, and then call a function named "onFinishTween" when it has completed and pass in a few parameters to that function (a value of 5 and a reference to the clip_mc), you'd do so like:
  1. import gs.TweenLite;
  2. import fl.motion.easing.Back;
  3. TweenLite.to(clip_mc, 5, {alpha:0.5, x:120, ease:Back.easeOut, delay:2, onComplete:onFinishTween, onCompleteParams:[5, clip_mc]});
  4. function onFinishTween(parameter1_num:Number, parameter2_mc:MovieClip):void {
  5.     trace("The tween has finished! parameters: " + parameter1_num + ", and " + parameter2_mc);
  6. }


If you have a MovieClip on the stage that is already in its end position and you just want to animate it into place over 5 seconds (drop it into place by changing its y property to 100 pixels higher on the screen and dropping it from there), you could:
  1. import gs.TweenLite;
  2. import fl.motion.easing.Elastic;
  3. TweenLite.from(clip_mc, 5, {y:"-100", ease:Elastic.easeOut});

FAQ

  1. Can I set up a sequence of tweens so that they occur one after the other?
    Of course! Just use the delay property and make sure you set the overwrite property to false (otherwise tweens of the same object will always overwrite each other to avoid conflicts). Here's an example where we colorize a MovieClip red over the course of 2 seconds, and then move it to a _y coordinate of 300 over the course of 1 second:
    1. import gs.TweenLite;
    2. TweenLite.to(clip_mc, 2, {tint:0xFF0000});
    3. TweenLite.to(clip_mc, 1, {y:300, delay:2, overwrite:false});

  2. Do the properties have to be in a specific order?
    Nope. The only thing that matters is that the first parameter is the object you're tweening, the second parameter is the time (in seconds), and the third parameter contains all the properties you want to tween (in any order). So TweenLite.to(clip_mc, 1, {scaleX:120, y:200, x:1}) is the same as TweenLite.to(clip_mc, 1, {x:1, y:200, scaleX:120});

  3. Why are TweenLite and TweenFilterLite split into 2 classes instead of building all the functionality into one class?
    1. File size. Only a portion of projects out there require tweening of filters. Almost every project I work on uses TweenLite, but only a few require tweening filters (TweenFilterLite). TweenLite is 2k whereas TweenFilterLite is 5k. Again, one of the stated purposes of TweenLite is to minimize file size & code bloat. If someone only wants to use TweenFilterLite, fine. But I think many people appreciate being able to use the most lightweight option for their needs and shave off the 3k when possible.
    2. Speed. Tweening filters is a more complex task. There are additional if/else statements and calculations in the rendering loop inside TweenFilterLite which could potentially slow things down a bit, even for non-filter tweens (I doubt anyone would notice a difference unless they’re running hundreds or thousands of simultaneous tweens, but I’m a big fan of keeping things as efficient & fast as possible)

  4. Do I have to pay for a license to use this code? Can I use it for commercial purposes?
    Feel free to take the code and use it as you wish, even for commercial purposes. Some people have requested the ability to donate money to reward the work I put into the class(es), so i put a PayPal "donate now" button at the top and bottom of the page, but you certainly don't need to donate anything. I'm just glad to help the Flash community.

  5. Are TweenLite and TweenFilterLite better than Tweener, Fuse, MC Tween, and all the other tweening engines out there?
    Maybe, maybe not. It all depends on your objectives, coding style, etc. I certainly don't claim that TweenLite & TweenFilterLite are superior to all other tweening engines, but in terms of the power-to-file-size ratio, I certainly haven't seen anything that comes close. Feedback has been overwhelmingly positive. I've used TweenLite for many years and it hasn't let me down. I never found myself needing features that are available in another tweening engine. But hey, to each his own.


OBJECTIVES

  • Minimize file size
  • Maximize flexibility and efficiency by extending the TweenLite class. That way, if you don't need to tween filters, you can just use TweenLite (about 2k); otherwise, this class will only add another 3k (5k total)
  • Minimize the amount of code required to initiate a tween
  • Maximize performance
  • Allow for very flexible callbacks (onComplete, onUpdate, onStart, all with the ability to pass any number of parameters)

FITLERS & PROPERTIES:

  • type:"Blur" -
    blurX, blurY, quality
  • type:"Glow" -
    alpha, blurX, blurY, color, strength, quality
  • type:"Color" -
    colorize, amount, contrast, brightness, saturation, hue, threshold, relative
  • type:"DropShadow" -
    alpha, angle, blurX, blurY, color, distance, strength, quality
  • type:"Bevel" -
    angle, blurX, blurY, distance, highlightAlpha, highlightColor, shadowAlpha, shadowColor, strength, quality

USAGE

TweenFilterLite.to(target:DisplayObject, duration:Number, variables:Object);

  • Description: Tweens the target's properties from whatever they are at the time you call the method to whatever you define in the variables parameter.
  • Parameters:
    1. target: Target DisplayObject whose properties we're tweening
    2. duration: Duration (in seconds) of the tween
    3. variables: An object containing the end values of all the properties you'd like to have tweened (or if you're using the TweenFilterLite.from() method, these variables would define the BEGINNING values). Putting quotes around values will make the tween relative to the current value. For example, x:"-20" will tween x to whatever it currently is minus 20 whereas x:-20 will tween x to exactly -20. Here are some examples of properties you might include:
      • blurX
      • blurY
      • color: An example for red would be 0xFF0000. Several filters use this property, like DropShadow and Glow
      • colorize: Only used with a type:"Color" tween to colorize an entire MovieClip.
      • amount: Only used to control the amount of colorization.

      Special Properties:

      • type: REQUIRED. A string that indicates what type of filter you're tweening. Possible values are: "Color" (for all image effects like colorize, brightness, contrast, saturation, and threshold), "Blur", "Glow", "DropShadow", or "Bevel"
      • delay: Number of seconds to delay before the tween begins. This can be very useful when sequencing tweens.
      • ease: You can specify a function to use for the easing with this variable. For example, fl.motion.easing.Elastic.easeOut. The Default is Regular.easeOut.
      • easeParams: An array of extra parameter values to feed the easing equation. This can be useful when you use an equation like Elastic and want to control extra parameters like the amplitude and period. Most easing equations, however, don't require extra parameters so you won't need to pass in any easeParams.
      • autoAlpha: Same as changing the "alpha" property but with the additional feature of toggling the "visible" property to false if the alpha ends at 0. It will also toggle visible to true before the tween starts if the value of autoAlpha is greater than zero.
      • volume: To change a MovieClip's volume, just set this to the value you'd like the MovieClip to end up at (or begin at if you're using TweenFilterLite.from()).
      • tint:To change a MovieClip's color, set this to the hex value of the color you'd like the MovieClip to end up at(or begin at if you're using TweenLite.from()). An example hex value would be 0xFF0000. If you'd like to remove the color from a MovieClip, just pass null as the value of tint. Before version 5.8, tint was called mcColor (which is now deprecated and will likely be removed at a later date although it still works)
      • onStart: If you'd like to call a function as soon as the tween begins, pass in a reference to it here. This can be useful when there's a delay and you want something to happen just as the tween begins.
      • onStartParams: An array of parameters to pass the onStart function.
      • onUpdate: If you'd like to call a function every time the property values are updated (on every frame during the time the tween is active), pass a reference to it here.
      • onUpdateParams: An array of parameters to pass the onUpdate function (this is optional)
      • onComplete: If you'd like to call a function when the tween has finished, use this.
      • onCompleteParams: An array of parameters to pass the onComplete function (this is optional)
      • overwrite: If you do NOT want the tween to automatically overwrite any other tweens that are affecting the same target, make sure this value is false.



TweenFilterLite.from(target:DisplayObject, duration:Number, variables:Object);

  • Description: Exactly the same as TweenFilterLite.to(), but instead of tweening the properties from where they're at currently to whatever you define, this tweens them the opposite way - from where you define TO where ever they are now (when the method is called). This is handy for when things are set up on the stage the way the should end up and you just want to tween them to where they are.
  • Parameters: Same as TweenFilterLite.to(). (see above)



TweenLite.delayedCall(delay:Number, onComplete:Function, onCompleteParams:Array);

  • Description: Provides an easy way to call any function after a specified number of seconds. Any number of parameters can be passed to that function when it's called too.
  • Parameters:
    1. delay: Number of seconds before the function should be called.
    2. onComplete: The function to call
    3. onCompleteParams [optional] An array of parameters to pass the onComplete function when it's called.




TweenFilterLite.killTweensOf(target:Object, complete:Boolean);

  • Description: Provides an easy way to kill all tweens of a particular Object/MovieClip. You can optionally force it to immediately complete (which will also call the onComplete function if you defined one)
  • Parameters:
    1. target: Any/All tweens of this Object/MovieClip will be killed.
    2. complete: If true, the tweens for this object will immediately complete (go to the ending values and call the onComplete function if you defined one).




TweenFilterLite.killDelayedCallsTo(function:Function);

  • Description: Provides an easy way to kill all delayed calls to a particular function (ones that were instantiated using the TweenFilterLite.delayedCall() method).
  • Parameters:
    1. function: Any/All delayed calls to this function will be killed.


EXAMPLES

As a simple example, you could tween the blur of clip_mc from where it's at now to 20 over the course of 1.5 seconds by:
  1. import gs.TweenFilterLite;
  2. TweenFilterLite.to(clip_mc, 1.5, {type:"Blur", blurX:20, blurY:20});

If you want to get more advanced and tween the clip_mc MovieClip over 5 seconds, changing the saturation to 0, delay starting the whole tween by 2 seconds, and then call a function named "onFinishTween" when it has completed and pass in a few arguments to that function (a value of 5 and a reference to the clip_mc), you'd do so like:
  1. import gs.TweenFilterLite;
  2. import fl.motion.easing.Back;
  3. TweenFilterLite.to(clip_mc, 5, {type:"Color", saturation:0, delay:2, onComplete:onFinishTween, onCompleteParams:[5, clip_mc]});
  4. function onFinishTween(argument1_num:Number, argument2_mc:MovieClip):void {
  5.     trace("The tween has finished! argument1_num = " + argument1_num + ", and argument2_mc = " + argument2_mc);
  6. }
If you have a MovieClip on the stage that already has the properties you'd like to end at, and you'd like to start with a colorized version (red: 0xFF0000) and tween to the current properties, you could:
  1. import gs.TweenFilterLite;
  2. TweenFilterLite.from(clip_mc, 5, {type:"color", colorize:0xFF0000});

FAQ

  1. Can I set up a sequence of tweens so that they occur one after the other?
    Of course! Just use the delay property and make sure you set the overwrite property to false (otherwise tweens of the same object will always overwrite each other to avoid conflicts). Here's an example where we colorize a MovieClip red over the course of 2 seconds, and then blur it over the course of 1 second:
    1. import gs.TweenFilterLite;
    2. TweenFilterLite.to(clip_mc, 2, {type:"color", colorize:0xFF0000, amount:1});
    3. TweenFilterLite.to(clip_mc, 1, {type:"blur", blurX:20, blurY:20, delay:2, overwrite:false});

  2. Do the properties have to be in a specific order?
    Nope. The only thing that matters is that the first parameter is the object you're tweening, the second parameter is the time (in seconds), and the third parameter contains all the properties you want to tween (in any order). So TweenFilterLite.to(clip_mc, 1, {type:"color", colorize:0xFF0000, amount:1}) is the same as TweenFilterLite.to(clip_mc, 1, {amount:1, colorize:0xFF0000, type:"color"});

  3. Can I use TweenFilterLite to tween things other than filters?
    Sure. It extends TweenLite, so you can tween any property you want. TweenFilterLite.to(my_mc, 1, {x:200}) gives you the same result as TweenLite.to(my_mc, 1, {x:200}). However, I'd recommend using TweenLite to tween properties other than filters for two reasons:
    1. In order to accommodate the specialized nature of filters, TweenFilterLite's code is a bit lengthier which translates into more work for the processor. It's doubtful that anyone would notice a performance hit unless you're tweening hundreds or thousands of instances simultaneously, but I'm a bit of an efficiency freak.
    2. TweenLite can tween any property of ANY object whereas TweenFilterLite tweens properties of DisplayObjects (like MovieClips, Sprites, etc.)

  4. Why are TweenLite and TweenFilterLite split into 2 classes instead of building all the functionality into one class?
    1. File size. Only a portion of projects out there require tweening of filters. Almost every project I work on uses TweenLite, but only a few require tweening filters (TweenFilterLite). TweenLite is 2k whereas TweenFilterLite is 5k. Again, one of the stated purposes of TweenLite is to minimize file size & code bloat. If someone only wants to use TweenFilterLite, fine. But I think many people appreciate being able to use the most lightweight option for their needs and shave off the 3k when possible.
    2. Speed. Tweening filters is a more complex task. There are additional if/else statements and calculations in the rendering loop inside TweenFilterLite which could potentially slow things down a bit, even for non-filter tweens (I doubt anyone would notice a difference unless they’re running hundreds or thousands of simultaneous tweens, but I’m a big fan of keeping things as efficient & fast as possible)

  5. Do I have to pay for a license to use this code? Can I use it for commercial purposes?
    Feel free to take the code and use it as you wish, even for commercial purposes. Some people have requested the ability to donate money to reward the work I put into the class(es), so i put a PayPal "donate now" button at the top and bottom of the page, but you certainly don't need to donate anything. I'm just glad to help the Flash community.

  6. Are TweenLite and TweenFilterLite better than Tweener, Fuse, MC Tween, and all the other tweening engines out there?
    Maybe, maybe not. It all depends on your objectives, coding style, etc. I certainly don't claim that TweenLite & TweenFilterLite are superior to all other tweening engines, but in terms of the power-to-file-size ratio, I certainly haven't seen anything that comes close. Feedback has been overwhelmingly positive. I've used TweenLite for many years and it has never let me down. I've never found myself needing features that are available in another tweening engine. But hey, to each his own.
신고
    

설정

트랙백

댓글

[AS3] Linkage 클래스를 필요할때 참조

Programming/ActionScript 3.0 2008.01.29 18:09

















public function getDefinitionByName(name:String):Object

플래시에서의 Public 함수 중에서 getDefinitionByName() 함수는 name 매개 변수로 지정된 클래스의 클래스 객체에 대한 참조를 반환해 준다.

var c:Class = getDefinitionByName("m"+2) as Class;  // 라이브러리에 있는 Linkage 클래스명 "m2"의 참조를 반환한다.

package {
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.utils.getDefinitionByName;

public class GetDefinitionByNameExample extends Sprite {
private var bgColor:uint = 0xFFCC00;
private var size:uint = 80;

public function GetDefinitionByNameExample() {
var ClassReference:Class = getDefinitionByName("
flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
instance.graphics.beginFill(bgColor);
instance.graphics.drawRect(0, 0, size, size);
instance.graphics.endFill();
addChild(DisplayObject(instance));
}
}
}

신고
    

설정

트랙백

댓글

[AS3] getTileColor 메소드를 이용한 이미지

Programming/ActionScript 3.0 2008.01.28 19:11
BitmapData의 일정한 간격의 pixel 컬러를 추축하는 메소드를 작성하고 이미지를 뽑아 보았다. 땡굴이 형님도 비슷한 작업을 진행중인 듯 싶다. 원본 이미지의 사이즈와 상관없이 16x16픽셀 아이콘도 이러한 확대한 결과물을 만들 수 있어서 여러가지로 사용할 수 있을 듯 싶다.

랜덤한 형태에서 좀더 관련이 있는 부분들을 조합하면 불규칙적이지만 규칙이 있는 재질을 만들어 낼 수 있을 듯 싶다.









사용자 삽입 이미지

신고
    

설정

트랙백

댓글

Flickr API UML - as3flickrlib

Programming/ActionScript 3.0 2008.01.28 14:57

Project Page: http://code.google.com/p/as3flickrlib/
Project Group: http://groups.google.com/group/as3flickrlib

위 구글 코드에서 오픈되어 있는 flickr API adobe 소스를 UML로 변환해 보았다. 필요하신 분들은 참고하시면 좋을 듯 싶다. 기본적인 상속과 합성관계만 형성해 놓은 상태, 대부분 합성으로 페키지가 구성되어 있다.

참고. 새벽에 작성한 관계로 틀린 부분이 있을 수 있음.










사용자 삽입 이미지

신고
    

설정

트랙백

댓글

Wise를 확장한 SimpleFanwise, SimpleArchwise 클래스 응용

Programming/ActionScript 3.0 2008.01.23 13:53
예전에 만들었던 호 그리는 클래스를 응용하여 부채꼴, 활꼴 모양을 그리는 Shape를 만들고 응용해보았다.
아래 CREATE 버튼을 클릭하면 왼쪽은 SimpleFanwise를 이용한 것이고 오른쪽은 SimpleArchwise를 이용하여 20개 오브젝트 생성후 사라지는 모션을 적용해 봤다.














신고
    

설정

트랙백

댓글

[AS3] private, protected, internal, public을 어떻게 결정하면 좋은가

Programming/ActionScript 3.0 2007.12.31 03:07
AS3의 private, protected, internal, public을 어떻게 결정하면 좋은가

일본의 ASer의 포스트을 보고 나를 포함한 많은 플래시 작업자들이 개념적으로는 이해 하고 있지만 실전에서 흐지부지 하게되는 것을 지양하기 위해서 그 분의 포스트을 토대로 접근자들을  정리해 본다.

각 접근자의 개념은 아래와 같다.

내부 이용자(자기 자신)               -> private
계승이용자(서브 클래스)             -> protected
외부 이용자(동일 패키지의 제삼자) -> internal
외부 이용자(다른 패키지의 제삼자) -> public
 




이하에 패키지 A_package의 클래스 A_class를 예로 들어 아래와 같을 경우, A_package 패키지 내에서는 모두 접근이 가능하다.
package A_package {
public class A_class {

private var p1:uint;
protected var p2:uint;
internal var p3:uint;
public var p4:uint;

public function A_class() {

p1 = 1; // OK
p2 = 2; // OK
p3 = 3; // OK
p4 = 4; // OK

}
}
}
동일 패키지의 상속 클래스는 private를 제외한 속성들에 접근이 가능하다.
package A_package {
public class B_class extends A_class {

public function B_class() {

v1 = 1; // error
v2 = 2; // OK
v3 = 3; // OK
v4 = 4; // OK

}
}
}
동일 패키지의 다른 클래스는 protected 속성에 액세스할 수 없지만 internal에는 액세스할 수 있다.
package A_package {
public class C_class {

public function C_class() {

var a:A_class = new A_class();

a.v1 = 1; // error
a.v2 = 2; // error
a.v3 = 3; // OK
a.v4 = 4; // OK

}
}
}
다른 패키지의 상속 클래스에서는 protected에는 액세스할 수 있지만 internal에는 액세스할 수 없다.
package B_package {

import A_package.A_class;

public class D_class extends A_class {

public function D_class() {

v1 = 1; // error
v2 = 2; // OK
v3 = 3; // error
v4 = 4; // OK

}
}
}
다른 패키지의 다른 클래스에서는 public밖에 액세스할 수 없다.
package B_package {

import A_package.A_class;

public class E_class {

public function E_class() {

var a:A_class = new A_class();

a.v1 = 1; // error
a.v2 = 2; // error
a.v3 = 3; // error
a.v4 = 4; // OK

}
}
}
예전에는 이런 구분 자체가 내 머리속을 어지럽게 하여 private와 public만을 사용했었다. AS3로 넘어오면서 상속, 합성, 오버라이드를 이용한 작업이 늘어나면서 유기적인 클래스들과의 관계 중심으로 구조를 설계하다보니 클래스 간에 접근 규정을 코멘트로 달아놓지 않는 한 스스로 혼란스럽게 되었다. 보통 private로 해두고 protected→internal→public 방향으로 작업을 하면 되겠으나 이것 또한 중간중간 에러체크를 감당해야 하는 부담을 지울 수가 없다.

확실히 private, protected, internal, public을 구분해서 작업 하는 것이 현재 작업자에게도 큰 도움이 된다. 가장 바람직한 방법으로는 작은 프로젝트라도 작업에 들어가기 전에 구조를 설계하고 노트에 UML 비스무리한 낙서를 하고 시작하는 것이 좋을 것 같다.

 

신고
    

설정

트랙백

댓글

[AS3] TextField.htmlText 속성 문제

Programming/ActionScript 3.0 2007.11.12 22:33
작업한 파일을 주말 동안 브라우저에 올려 놓고 몇 시간을 놔두었더니 CPU 점유율이 서서히 올라간다. 어디서 문제가 있는지 고민해 보았는데 이상하게도 메모리에는 이상이 없는 것 같은데 유독 CPU 점유율만 서서히 상승하는 것이다. 그래서 몇 개의 클래스를 두고 하나 하나 테스트를 해봤는데 결국 예전에 만들었던 DZeroNumber.as 클래스에서 문제가 있는 것으로 나타났다.

그런데 그 클래스 내에서도 특별히 문제가 될만한 곳을 발견하지 못하였는데 결국 생각지도 못한 곳에서 문제가 발생하고 있었다. DZeroNumber 클래스에서는 지정한 텍스트 필드에 00000 부터 지정한 숫자만큼 다이나믹하게 넘버링 하는 기능을 하고 있는데 그 클래스에서 000에 해당하는 부분과 실제 숫자에 해당하는 부분의 색을 달리하기 위해 TextField.htmlText 속성에 html 태그를 포함한 String을 넣고 있었다. 그런데 이 htmlText 속성이 문제가 있다.

TextField.text = "String" 을 넣을 때에는 CPU 점유율이 고정이지만 하나의 TextField에 htmlText속성을 통해서 html 태그를 포함한 String을 주기적으로 넣었을 때는 html 태그를 렌더링하는 과정에서 CPU 점유율이 점차적으로 상승한다. 그리고 이 상승하는 CPU 점유율은 swf파일이 종료되지 않는 이상 떨어지지 않는다.

TextField.htmlText = "<b> text </b>"

위와 같은 형태로 html 태그를 계속해서 TextField.htmlText에 넣게 되면 점차적으로 CPU 점유율이 100%를 향해 달려가니 주의가 필요하다.
신고
    

설정

트랙백

댓글

Variable Notation for ActionScript

Programming/ActionScript 3.0 2007.11.07 21:55

 플래시 액션스크립트 언어가 3.0으로 접어들면서 변수명 지정에 있어서 스스로 한계를 느껴서 변수명 표기법을 만들어 보았다. 기본적인 규칙은 헝가리안 표기법에 따랐으며 플래시의 특성에 맞는 타입들은 플래시 액션스크립트에서 권장하는 접미어를 사용 하였다.

 

사실 아래 이미지와 같이 표기법을 만들어 놓았지만 지금은 이와 같이 변수명을 지정하지 않고 있다. 이 테이블에서는 Scope 형태를 표기하기 위해서 g_, m_, l_, p_ 접두어를 사용하였는데, ActionScript 3.0으로 접어들면서 글로벌 변수의 개념이 불명확해 졌으며 접두어를 사용할 경우 변수명을 볼 때 한번 더 생각을 하게 만들기 때문에 읽기 어려운 점도 있는 듯하다. 그래서 이 테이블을 만들어 놓고도 사용하는 방법은 다음과 같다.

 

g_를 사용하는 글로벌 변수 표기는 제거 하고 클래스 속성변수(멤버변수)의 경우에는 _를 사용하며, 로컬 변수의 경우는 _를 없앴다. 그리고 파라미터로 넘겨받은 변수의 경우는 로컬변수에 포함하여 _가 없는 변수명을 하도록 했다. 따라서 이러한 표기법을 사용하면 아래와 같은 형태를 취한다.


package{
        public class VariableNotation extends Sprite{
                // ----------------- properties ---------------//
private var _strMsg:String; private const COUNT:int = 12;    // 상수는 대문자 표기
   // ----------------- Methods ----------------// public function VariableNotation():void{ _strMsg = “멤버변수 표기법”; initStart(); // 메소드는 대문자로 단어를 구분 } private function initStart ():void{ var strMsg:String = “로컬변수 표기법”; trace(“_strMsg : ”+_strMsg, “strMsg : ”+strMsg); } } }

표기법은 개인의 취향에 따라 다양한 형태를 나타낼 수 있지만 그때 그때 다른 표기법은 자신은 물론 다른 사람이 코드를 분석할 때도 어려움을 줄 수 있을 것 같다. 딱히 표기법이 없는 분들에게 권유해 본다.

사용자 삽입 이미지


신고
    

설정

트랙백

댓글

「Flash Performance Tips Part 1」의 한국어 번역

Programming/ActionScript 3.0 2007.11.04 20:15
「BIG SPACESHIP LABS / Flash Performance Tips Part I」 에서 Flash의 최적화에 대한 이야기를 하고 있어서 포스팅한다. 내용은 번역 과정에서 잘 못된 부분이 있을 수 있으므로 이해가 필요하다. Flash사이트를 최적화를 위해 부드럽게 재생시키려면 93%의 노력과6%의 지식과4%의 땀과2%정도의 butterscotch(버터를 넣은 캔디)가 필요합니다.(영화 「Charlie & the Chocolate Factory」의 대사로부터의 인용 했다고 합니다)
농담은 접어두고, 많은 사람들이Flash Player의 퍼포먼스에 고민을 하고 있지만 사이트를 부드럽게 재생시키기 위해서 당신이 할 수 있는 것이 있습니다. 부드러운 재생을 위해서 우리(BIG SPACESHIP스탭)이 매일 사용하고 있는 수법을 몇 개 소개하겠습니다.

마스크는 안된다
모든 마스크를 사용해서는 안된다는 것은 아닙니다. 마스크가 매우 도움이 되는 것은 이미 아시는 바지요. 그렇지만, 마스크는 재생 퍼포먼스를 떨어뜨리는 원인의 주범입니다. 어떤 대상을 마스크 했을 때 Flash player는 매 프레임 마다 무엇을 표시 하고 무엇을 숨기는지를 계산하고 있습니다. 마스크와 능숙하게 친해지려면 어떻게 하면 좋을까요? 거기에는 조금의 인내와 트릭인 층 구조(예를 들면 표시시키고 싶은 영역 사이즈로 구멍을 뚫은 상위 층을 올려놓고 그것을 아래의 층에 씌운다든가)를 사용하면 퍼포먼스 저하를 막으면서 같은 표시 결과를 얻을 수 있겠지요.

알파 채널 첨부PNG와 비디오에 대해
마스크와 같이 이것도 사용하지 않을 수 없을 때도 있지만 이 때도Flash Player는 알파 아래 부분의 표시 계산 처리를 강요당하고 있습니다. 경우에 따라서 우리는 절반 사이즈의 알파 비디오를 Flash위에서 확대해 사용하기도 합니다.
그리고 알파 첨부의 동영상에 대해입니다만 알파 비디오 대신에 연속된 번호의PNG를 사용하는 방법도 시험하는 가치가 있을 거라고 생각합니다. 대부분의 경우는 비디오 형식이 약간 퍼포먼스가 좋은 듯 하지만 그 때 마다 실험해 볼 가치는 있습니다.

frame rate
여러 가지 논의가 있습니다만 세상에 말하는 「매직 frame rate」같은 것은 없습니다. 우리는25, 30fps로 만들고 있습니다.(내가 알기로는) 그것이 베스트라고 생각하고 있습니다. 우리는 그 때마다 어느 쪽이 보다 좋은가를 테스트해서 결정을 하고 있습니다. 그렇지만 일반적으로 말하면 frame rate가 사이트의 재생 스피드가 저하하는 주된 원인은 되지 않을 것입니다. 아무튼 통상의 사용에서는30fps이상은 별로 추천하지 않습니다. 그렇게 아주 대단한 스피드로 렌더링 시켜도…

cacheAsBitmap 과 BitmapData
cacheAsBitmap을 이용한 벡터의 아이템의 rasterize화는 상황을 선택해 사용하도록 해야 한다. 분명히Flash는 심볼을draw하는 것은 한 번으로  끝납니다. 하지만 심볼을 확대 축소나 회전시킬 때는 결코cacheAsBitmap하지 말아야 한다. 이 때 Flash는 매프레임 마다 렌더링과 캡쳐를 반복하므로 처리가 빨라야할 어딘가가 늦어져 버립니다.
Da Vinci CodeNike Air 의 사이트에서 우리는 보이는 부분의 screen shot를 찍어 그것을 하나의 무비 클립에draw시켜서 screen shot을 애니메이션 시키는 처리를 하고 있습니다. 이것은 많은 엘리먼트를 개별적으로 움직이거나 하는 것보다도 상당히 빠릅니다.이 수법은 꽤 추천합니다.

alpha보다 visible로 하는 편이 좋아

alpha = 0과 visible = false는 실은 전혀 별개입니다. alpha는 대상이 되는 클립의 투명도를 결정하는 것입니다. Visible는 FlashPlayer위에서 실제로 그 클립을 표시 할까, 하지 않을까를 결정하는 것입니다. 만약, 무언가를 비 표시로 설정하고 싶으면 visible프롭퍼티를 사용하도록 합시다.

onEnterFrame 과 setInterval
이러한 처리를 사용했을 때는 각각 onEnterFrame = null;이나 clearInterval(myInterval); 하고, 메모리로부터 클리어 해 두도록 합시다.그것을 하지 않는 채 방치하는 것은 전화의 통화 후에 수화기를 올린 채로 떠나는 것입니다.

수학 연산은 사전에 미리 해둡시다.
표시 직전의 단계에서 싸인 웨이브(※ -1~1를 취하는 파형)을 연산하고 있지 않습니까? 그것은 매회 같은 싸인 커브는 아닙니까? 그럴 때는 수치를 배열 안에 하드 코드 해 둡시다. 사전에 수학 연산을 해 두는 것으로, 직전에 복잡한 처리를 하지 않는 것이 좋습니다. 나는 트윈 시키고 싶을 때에 사전에 배열에 모든 트윈치를 배열에 넣어두고 nextFrame()으로 매 프레임 마다 이동시킨 적도 있습니다.

무음 사운드
우리도 마지막 수단으로서 사용하고 있습니다만 기억해 두는 가치가 있는 수법이라고 생각합니다. 타임 라인상의 독립된 층에 무음의 사운드 파일을 「스트리밍 재생•루프」로 설정해 놓아두면 FlashPlayer는 사운드에 동기 시키려고 하고 자동적으로 프레임을 솎아내 조정해 줍니다.

//////////////////////////////////////////////////////////////////////////////////

위 내용은 「BIG SPACESHIP LABS / Flash Performance Tips Part I」의 내용이다. 이 밖에서 작업자 입장에서 FlashPlayer를 웃을 수 있게 해주는 방법은 여러 가지가 있을 듯 싶다. 현재 생각나는 몇 가지만 추가하고 그 이후 자신이 알고 있는 방법이 있으면 코멘트로 소통했으면 좋겠다.

1.    위에서 visible = false; 로 하는 것은 화면상의 표시 오브젝트에서 제외 되지만 그 안에서 연산하고 있는 처리는 그래도 FlashPlayer의 몫이다. 따라서 visible = false를 적용한 오브젝트 안에서 이루어지는 연산도 멈춰 주는 것이 바람직하다.

2.    for(var i:int=0;i<m_arr.length;i++) 와 같이 for문의 비교연산에서 직접 m_arr.length 값을 취득하는 것 보다 상위에 var l_intLen:int = m_arr.length; 변수를 미리 정의하여 사용하는 것 이 좋다. 이는 for문이 비교연산을 처리할 때 매번 m_arr 배열의 개수를 취득하기 위해서 연산처리를 시도하기 때문이다.

3.    _mcParent._mcChild1._mcChild2; 와 같은 참조를 여러 번 사용할 때는 상위에 var _mcChild2:MovieClip = _mcParent._mcChild1._mcChild2; 변수를 미리 선언하여 _mcChild2를 사용하는 것이 좋다. 이는 .연산자를 통해서 매번 depth 탐색이 이루어 지기 때문이다.

4.    _mcParent[“_mcChild1”]와 같은 참조 보다는 _mcParent._mcChild1과 같은 참조가 빠르다. _mcParent[“_mcChild1”]와 같은 형태로 참조할 경우에는 해시테이블 탐색을 통해서 string의 값을 비교 연산을 하기 때문이다.

5.    onEnterFrame or Timer and Interval과 같은 빠른 속도로 처리되는 function 안에서는 특정 기능을 하기 위한 코드들을 외부 function을 호출하는 형태로 분리 하는 것 보다는 직접 function 안에 쓰는 것이 좋다.(이는 유지 보수와 재 사용상의 문제가 있을 수 있음)

6.    for문을 사용하여 연속된 연산처리를 수행하는 것 보다는 절차지향으로 라인으로 풀어 쓰는 것이 빠르다.(onEnterFrame이나 interval과 같은 function 안에서 for문을 사용하는 것은 치명적이다. 이럴 때는 라인에 풀어 쓰는 것이 좋다)

이 밖에도 많은 테스트들이 있었을 것으로 생각한다. 각자 가지고 있는 노하우가 있으면 댓글을 달아 주길 바란다.

신고
    

설정

트랙백

댓글

[AS3] 스테이지에 올려놓은 TextField의 autoSize 설정

Programming/ActionScript 3.0 2007.11.03 18:49
회사 작업으로 만들었던 클래스를 이리저리 가지고 놀다가 플래시 툴에서 stage에 만들어 놓은 TextField의 autoSize 설정이 제대로 먹히지 않는 경우가 발생하여 한참을 고생했다. new TextField()로 생성한 TextField의 경우에는 제대로 먹히는데 stage에 직업 올려놓은 TextField의 설정이 처음에는 되었다가 어느 순간부터 먹히지 않거나 중간에 사이즈가 늘어나다 끊기는 현상이 발생했다.

문제의 이유는 확인하지 못하였는데 여러가지 테스트를 해보다가 결국은 기존에 있던 텍스트 필드를 삭제하고 다시 텍스트 필드를 만들어서 같은 속성으로 적용을 해보니 이번에는 제대로 먹는다...

stage에 있는 TextField의 폰트를 임베드 시키고 폰트를 다른 폰트로 바꾸게 되면 그때부터 autoSize 속성이 적용이 되지 않는다. 텍스트필드에 filter를 적용해도 autoSize 속성이 먹통이 되버린다...

저 처럼 stage에 올려놓은 텍스트필드의 autoSize가 먹히지 않을 때에는 주저하지 말고 기존에 있던 텍스트 필드를 지우고 새롭게 텍스트 필드를 만들어서 테스트 해보길 바란다.



신고
    

설정

트랙백

댓글

[AS3] 호(arc)를 그리는 클래스

Programming/ActionScript 3.0 2007.09.15 03:08
호를 그리는 클래스를 만들어 봤다. 여러가지 자료들을 급조하여 기본적인 호를 그리는 형태를 제작한 관계로 -값이나 예상치 않은 입력 값에 따른 예외는 처리하지 않은 상태다. 사용하실 분들은 개인적으로 자료 정리를 위해서 패키지 설정을 해 놓은 상태이니 원하는 폴더 구조 및 패키지를 설정하여 사용하거나 패키지(jasu.display)를 제거하고 fla와 같은 폴더에 복사 후 사용 하면 된다.









신고
    

설정

트랙백

댓글

[AS3] Pen 클래스 테스트

Programming/ActionScript 3.0 2007.09.14 12:52
Pen 클래스를 이용해서 드로잉 테스트겸 만들어 봤다. Graphics 객체는 드로잉을 하면 할수록 CPU를 많이 잡아먹는디 Graphics 객체 안에서 어떤 일이 벌어지길에 이런 현상이 발생하는지 모르겠다... 그리는 과정에서 비트맵데이타로 처리해버리면 될거 같은디 귀찮스러워서 그냥 올려 놓는다. 테스트 하시는 분들은 너무 많이 그리지 마셔용...쿠쿠











 
 
신고
    

설정

트랙백

댓글

[AS3] UIScrollBar 동적으로 스킨 적용하기

Programming/ActionScript 3.0 2007.09.12 06:43
UIComponent에 해당하는 컴포넌트들의 스킨을 적용하는 방법에 대해서 FlashCS3의 레퍼런스에서는 컴포넌트를 라이브러리에 등록하고 등록된 컴포넌트를 스테이지에 끌어다 놓고 무비클립을 직접 수정하는 방법 이외에는 이렇다 할 설명을 하지 않고 있는 듯 하다.

개인적으로 웹상에 올려지는 스크립트들을 이쁘게(?!) 보이도록 만들어 보려고 AScodeViewer를 작업하고 있는데, 외부에 있는 Style xml 데이터를 로드하여 사용자가 작성한 xml에 따라서 UIScrollBar와 기타 화면상의 DisplayObject들의 스타일을 적용하는 과정에서 컴포넌트의 스킨을 동적으로 적용을 해야 했다. 웹상에서 찾아보았지만 그와 관련된 자료를 찾지 못하여 임의로 적용해 보게 되었다.

일단은 컴포넌트의 스킨을 동적으로 적용하기 위해서는 기존의 컴포넌트들이 어떠한 형태로 스킨을 적용하고 있는지, 구조를 파악할 필요가 있다. 컴포넌트를 스테이지에 올려놓고 안으로 들어가보면 해당 컴포넌트에서 사용되는 기능별 무비클립들을 볼 수 있는데 이러한 무비클립들은 2 프레임에 위치하고 있기 때문에 외부에서 직접 해당 무비클립에 접근 할 수는 없다.

라이브러리에 있는 Component Assets -> ScrollBarSkins 폴더에 보면 사용되는 무비클립들을 볼 수 있는데 이들 무비클립들에는 각각 내부 클래스가 지정되어 있다. 이 클래스들이 각각의 무비클립을 대변한다고 볼 수 있다.

그럼 각각의 무비클립의 색을 변경하기 위해서는 어떻게 해야 할까… 여러가지로 실험도 해보고 고민을 해보았는데 일단은 아래 방법은 개인적으로 작업하는 과정에서 만들어 낸 결과이기 때문에 옳은 방법이 아닐 수 있음을 밝혀둔다.

UIScrollBar 컴포넌트의 상속 관계에서 setStyle 메소드를 발견할 수 있는데 이 메소드는 스타일 적용에 필요한 default 클래스를 custom 클래스로 대체할 수 있도록 해준다. 따라서 setStyle 메소드를 통해서 기존에 등록되어 있는 각 무비클립의 클래스를 자신이 원하는 기능을 하는 클래스로 대체할 수 있다는 이야기다. 아래는 디폴트로 지정되어 있는 클래스들이다.

ScrollArrowDown_disabledSkin
ScrollArrowDown_downSkin
ScrollArrowDown_overSkin
ScrollArrowDown_upSkin
ScrollThumb_downSkin
ScrollBar_thumbIcon
ScrollThumb_overSkin
ScrollThumb_upSkin
ScrollTrack_Skin
ScrollArrowUp_disabledSkin
ScrollArrowUp_downSkin
ScrollArrowUp_overSkin
ScrollArrowUp_upSkin

그리고 아래는 디폴트 클래스와 매칭되는 문자열들이다.

downArrowDisabledSkin
downArrowDownSkin
downArrowOverSkin
downArrowUpSkin
thumbDownSkin
thumbIcon
thumbOverSkin
thumbUpSkin
trackSkin
upArrowDisabledSkin
upArrowDownSkin
upArrowOverSkin
upArrowUpSkin

일단 적용한 형태를 보면 아래와 같다.
private function setScrollBarStyle(s:UIScrollBar):void{
s.setStyle("downArrowDisabledSkin", DScrollArrowDown_disabledSkin);
s.setStyle("downArrowDownSkin", DScrollArrowDown_downSkin);
s.setStyle("downArrowOverSkin", DScrollArrowDown_overSkin);
s.setStyle("downArrowUpSkin", DScrollArrowDown_upSkin);

s.setStyle("upArrowDisabledSkin", DScrollArrowUp_disabledSkin);
s.setStyle("upArrowDownSkin", DScrollArrowUp_downSkin);
s.setStyle("upArrowOverSkin", DScrollArrowUp_overSkin);
s.setStyle("upArrowUpSkin", DScrollArrowUp_upSkin);

s.setStyle("thumbDownSkin", DScrollThumb_downSkin);
s.setStyle("thumbIcon", DScrollBar_thumbIcon);
s.setStyle("thumbOverSkin", DScrollThumb_overSkin);
s.setStyle("thumbUpSkin", DScrollThumb_upSkin);
s.setStyle("trackSkin", DScrollTrack_Skin);
}

위 코드에서는 적용할 UIScrollBar를 전달 받아서 setStyle 메소드를 통해서 각 무비클립의 클래스들을 새로운 클래스(D로 시작하는 클래스들)들로 대체했다. 새로 대체한 클래스에서는 각각 디폴트로 지정되어 있는 클래스들을 extends 시켰다. 이렇게 한 이유는 라이브러리에 있는 컴포넌트 무비클립들을 그대로 사용하기 위함이다.

이렇게 작성하게 되면 컴포넌트에서 사용하는 각각의 무비클립에서 원하는 기능을 수행할 수 있는 새로운 클래스들로 대체했기 때문에 새로 등록된 클래스 안에서 무슨 짓을 하더라도 플래시가 용서해 준다…쿠쿠

각 클래스 안에서는 자신의 무비클립의 색을 변경하는 클래스를 통해서 색과 알파값등을 세팅하도록 하였고 셋팅하는 방법으로는 중앙에 Singleton 클래스를 만들어 놓고 xml에서 전달받은 스타일을 각 무비클립에 입히는 방법으로 사용했다. 참고로 색 변경에 사용한 간단한 클래스도 올려 놓는다.
package viewer.skins{
import flash.display.DisplayObject;
import flash.geom.Transform;
import flash.geom.ColorTransform;

public class TintColor{
public function TintColor():void{}
public static function setTintColor(d:DisplayObject, c:uint, a:Number):void{
var resultColorTransform:ColorTransform = new ColorTransform();
resultColorTransform.alphaMultiplier = a;
resultColorTransform.color = c;

var transformation:Transform = d.transform;
transformation.colorTransform = resultColorTransform;
}
}
}

이 과정중에서 버그를 발견하게 되었는데 다른 것들은 위에서 언급한 문자열을 통해서 클래스를 대체할 수 있었으나 유독 trackSkin(스크롤바의 기본 바탕이 되는 배경)의 경우 클래스를 대체하지 못하는 문제가 있다. 이 문제로 검색을 해 보니 adobe에 버그로 등록되어 있는 것 같은데 그 해결책은 찾지 못하였다.. 그렇다고 그냥 지나칠 수 있낭…자존심이 허락하는 범위 안에서 꼼수를 부렸다.

적용한 방법은 쿠쿠 웃기는 이야기이긴 하지만 컴포넌트 스킨에서 스크롤바의 배경이 되는 무비클립을 투명으로 처리하고 스크롤바 아래에 무비클립을 두어 스테이지가 변경될 때 스크롤바의 크기와 위치에 따라 이동하는 배경 무비클립을 따로 만들어 놓고 그 무비클립의 색을 변경하는 방법으로 적용했다…쿠쿠 약간 찜찜하지만 trackSkin 버그 문제가 해결되기 전까지 임시방편으로 적용해 두기로 했다.

스테이지 상에서 무비클립들을 직접 수정하여 사용할 경우 일단 사용된 최초 컴포넌트만을 사용하기 때문에 서로 다른 스킨을 적용하기가 어렵지만 이러한 방법을 사용하면 컴포넌트 각 인스턴스에 개별적인 스킨을 적용할 수 있다.

AScodeViewer는 거의 85% 정도 완성된 것 같다. FlashDevelop의 as 코드 스킨과 스크립트 창의 배경까지 직접 xml을 편집하여 사용할 수 있도록 하였기 때문에 자신의 FlashDevelop 환경과 거의 비슷한 화면을 웹상에 올려놓을 수 있을 듯 싶다. FlashDevelop에서 사용하는 xml데이터를 그대로 사용하다보니 AS 코드 뿐만이 아니라 Java나 기타 언어에서도 사용 할 수 있을 듯 싶다. 중요 포인트는 풀스크린 모드 기능을 지원한다는 것이 아닐까 싶다....
신고
    

설정

트랙백

댓글

[FlashCS3] Auto Format 기능 주의...

Programming/ActionScript 3.0 2007.09.06 03:16
코드 100줄 이상 넘어가는 것들은 대부분 FlashDevelop에서 작업을 하지만 그보다 적은 코딩 테스트를 할 경우에는 따로 열기 귀찮아서 FlashCS3에 내장된 스크립트 창을 사용하는데, Flash CS3의 Auto format 기능이 여러 가지로 문제가 좀 있다. CS3에서 ActionScript 3.0으로 버전업 하면서 스크립트 자체 기능은 강화되었다지만 FlashCS3 툴은 완성도는 다소 떨어지는 느낌이다. Sepy나 FlashDevelop 처럼 안되더라도 그에 부합하는 기능 보강은 필요할 듯싶다.

아래 코드의 경우 예상되는 결과값은 9가 되어야 하지만 스크립트 창에 있는 Auto Format 기능을 실행해서 코드를 정렬하고 퍼블리시를 해보면 결과는 엉뚱하게 7이 나온다.



trace(getResult());
function getResult(a:int=1, b:int=2, c:int=3):Number {
return (a + b) * c;
}

Auto Format 기능을 실행하게 되면 return 이후에 나타나는 ()는 리턴값으로 간주하여 ()를 제거해 버린다. 이 때문에 결과로 내보낼 사칙연산 순서가 b*c + a로 되어버린다. 실제로 Auto Format 기능을 실행하면 아래처럼 코드상에서 ()가 제거된 것을 확인 할 수 있다.
trace(getResult());
function getResult(a:int=1, b:int=2, c:int=3):Number {
return a + b * c;
}

간단한 코드의 경우에는 문제가 되는 부분을 찾을 수 있겠지만 덩치가 큰 코드의 경우에는 Auto Format 기능 한방으로 예상치 못한 에러 아닌 에러를 내는 곳을 찾기란 쉽지 않다. 그나저나 FlashDevelop은 왜 Auto Format 기능 안 넣는겨….


신고
    

설정

트랙백

댓글

[AS3] BitmapData.lock and unlock 기능

Programming/ActionScript 3.0 2007.09.03 09:45
BitmapData에 있는 lock/unlock 메소드는 레퍼런스에 기능이 명시 되어 있지만 짧게 설명되어 있어서 지나치기 쉬운 것 같다. 아래는 레퍼런스에 나와 있는 내용이다.

public function lock():void
언어 버전 :     ActionScript 3.0
Player 버전 :     Flash Player 9
이 BitmapData 객체를 변경할 때 BitmapData 객체를 참조하는 어떤 객체(예: Bitmap 객체)도 업데이트되지 않도록 이미지를 잠급니다. 성능을 높이려면 setPixel() 또는 setPixel32() 메서드를 여러 차례 호출하기 전후에 이 메서드를 unlock() 메서드와 함께 사용합니다.

레퍼런스의 내용만으로는 정확히 어떠한 역할을 하는지 알기 쉽지 않다. lock 메소드는 BitmapData의 정보를 화면에 draw하는 처리과정을 일시적으로 사용하지 않도록 하여 불필요한 처리과정을 제거함으로써 퍼포먼스를 향상시킬 수 있다. 예를 들면

보통 BitmapData를 처리하기 위해 addChild(new Bitmap(bitmap))와 같이 addChild한 이후에 setPixel() 또는 setPixel32() 메소드를 사용하는 경우가 많은데, 이때 setPixel() 또는 setPixel32()와 같은 메소드는 그 메소드 자체 처리만으로도 addChild 되어 있는 경우 화면에 draw하는 기능을 함께 처리해 버린다. 따라서 addChild되어 있는 BitmapData에 setPixel() 또는 setPixel32() 메소드를 여러 번 사용하여 복잡한 처리를 할 경우에 setPixel() 또는 setPixel32() 메소드를 호출될 때마다 화면에 draw 하기 때문에 불필요한 CPU 처리를 하게 되는 것이다.

물론 setPixel() 또는 setPixel32()메소드의 처리 과정을 화면에 표시해야 하는 경우라면 별로 효력이 없겠으나 setPixel() 또는 setPixel32() 메소드를 여러 번 처리한 이후 결과만을 화면에 표시할 경우는 lock 메소드를 통해서 화면에 표시하는 기능을 잠그고 일련의 처리를 완료한 이후에 unlock 메소드를 통해서 잠근 기능을 풀어주면 불필요한 처리를 하지 않으면서 화면에 처리한 결과를 표시할 수 있다.

아래 예제를 돌려보면 lock/unlock를 사용하지 않았을 때와 사용했을 때의 차이를 확인 할 수 있다.

lockTest(700, 700);

function lockTest(w:uint, h:uint) {
var bm:BitmapData = new BitmapData(w,h);
addChild(new Bitmap(bm));

trace("lock/Unlock 사용 안함 : "+drawBitmap(bm) + "ms");

bm.lock();
trace("lock/Unlock 사용 : "+drawBitmap(bm) + "ms");
bm.unlock();
}
function drawBitmap(bm:BitmapData):uint {
var start:uint = getTimer();
var w:uint=bm.width;
var h:uint=bm.height;

for (var x:uint=0; x < w; x++) {
for (var y:uint=0; y < h; y++) {
bm.setPixel(x,y,Math.random()*0xFFFFFF);
}
}
return getTimer() - start;
}

// output
lock/Unlock 사용 안함 : 214ms
lock/Unlock 사용 : 157ms

output 결과는 시스템에 따라 다소 차이가 있을 수 있는데 테스트한 컴퓨터 스펙은 아래와 같다.

소니 바이오 노트북 VGN-SZ18LP
Genuine Intel® CPU T2400 @ 1.83GHz
메모리 1.50GB RAM

신고
    

설정

트랙백

댓글

[AS3] Head First - OOAD AS3로 변환 두번째

Programming/ActionScript 3.0 2007.08.26 12:11
이전에 포스팅한 기타 검색 프로그램의 두 번째로 객체지향 원리를 살린 cohesive버전을 AS3로 변환하였다. 변환 과정에서 Java에서 제공하고 있는 HashMap의 경우는 AS3로 기본적인 기능만을 하는 HashMap class를 만들었다.

메인 document class에서 inventory. addInstrument() 메소드를 실행할 때, 마지막 파라미터 값으로 new InstrumentSpec(HashMap)을 전달하게 되는데, 이때 HashMap 클래스의 경우 Array을 이용하여 데이터를 테이블 형태로 저장하고 있기 때문에 put과 remove과정에서 하나의 Array를 참조하므로 마지막 데이터만을 저장하게 되어 검색이 제대로 되지 않는다.

이는 addInstrument 메소드를 실행하는 시점에서 참조 형태로 HashMap 데이터 Array를 전달하기 때문에 발생하는 문제다. 이를 해결하기 위해 HashMap에 있는 Array를 복제(clone)할 필요가 생겼다. 그래서 HashMap 클래스 내에 clone() 메소드를 만들어 사용되고 있는 Array를 byteArray로 변환하여 복제하였다.

이 프로그램을 변환하면서 다시 한번 느끼는 건 단순해 보이지만 단순하지 않은, 간결하고 함축적인, 사용자가 사용하기 편리한 것과 개발자의 정신분열 증상은 항상 반비례한다는 것이다. 
Java의 버전업을 통해 지원하고 있는 클래스들을 분석하는 것은 충분히 가치가 있는 일이다. 이미 입증된 알고리즘으로 제작되었기 때문에 AS3로 변환하면 충분히 사용성에서 좀더 나을 방법을 찾을 수 있을 듯싶다.

사용자 삽입 이미지

// FindInstrumentTester.as
package{
import flash.display.Sprite;
public class FindInstrumentTester extends Sprite{

public function FindInstrumentTester():void{
var inventory:Inventory = new Inventory();
initializeInventory(inventory);

var properties:HashMap = new HashMap();
properties.put("builder", Builder.GIBSON);
properties.put("backWood", Wood.MAPLE);
var whatBryanLikes:InstrumentSpec = new InstrumentSpec(properties);

var matchingInstruments:Array = inventory.search(whatBryanLikes);
if(matchingInstruments.length != 0){
trace("Bryan, you might like these instruments:");
var ilen:int = matchingInstruments.length;
for(var i:int=0;i<ilen;i++){
var instrument:Instrument = matchingInstruments[i];
var spec:InstrumentSpec = instrument.getSpec();

trace("We have a " + spec.getProperty("instrumentType") +
" with the following properties:");

var hashMap:HashMap = spec.getProperties();
var jlen:int = hashMap.length;
for(var j:int=0;j<jlen;j++){
var propertyName:String =
String(hashMap.getHashTable()[j].key);
trace(" " + propertyName + ": "
+spec.getProperty(propertyName));
}
trace(" You can have this " +
spec.getProperty("instrumentType") +
" for $" + instrument.getPrice() + "\n---");
}
}else{
trace("Sorry, Bryan, we have nothing for you.");
}
}
private function initializeInventory(inventory:Inventory):void {
var properties:HashMap = new HashMap();
properties.put("instrumentType", InstrumentType.GUITAR);
properties.put("builder", Builder.COLLINGS);
properties.put("model", "CJ");
properties.put("type", Type.ACOUSTIC);
properties.put("numStrings", 6);
properties.put("topWood", Wood.INDIAN_ROSEWOOD);
properties.put("backWood", Wood.SITKA);
inventory.addInstrument("11277", 3999.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.MARTIN);
properties.put("model", "D-18");
properties.put("topWood", Wood.MAHOGANY);
properties.put("backWood", Wood.ADIRONDACK);
inventory.addInstrument("122784", 5495.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.FENDER);
properties.put("model", "Stratocastor");
properties.put("type", Type.ELECTRIC);
properties.put("topWood", Wood.ALDER);
properties.put("backWood", Wood.ALDER);
inventory.addInstrument("V95693", 1499.95,
new InstrumentSpec(properties.clone()));
inventory.addInstrument("V9512", 1549.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.GIBSON);
properties.put("model", "Les Paul");
properties.put("topWood", Wood.MAPLE);
properties.put("backWood", Wood.MAPLE);
inventory.addInstrument("70108276", 2295.95,
new InstrumentSpec(properties.clone()));

properties.put("model", "SG '61 Reissue");
properties.put("topWood", Wood.MAHOGANY);
properties.put("backWood", Wood.MAHOGANY);
inventory.addInstrument("82765501", 1890.95,
new InstrumentSpec(properties.clone()));

properties.put("instrumentType", InstrumentType.MANDOLIN);
properties.put("type", Type.ACOUSTIC);
properties.put("model", "F-5G");
properties.put("backWood", Wood.MAPLE);
properties.put("topWood", Wood.MAPLE);
properties.remove("numStrings");
inventory.addInstrument("9019920", 5495.99,
new InstrumentSpec(properties.clone()));

properties.put("instrumentType", InstrumentType.BANJO);
properties.put("model", "RB-3 Wreath");
properties.remove("topWood");
properties.put("numStrings", 5);
inventory.addInstrument("8900231", 2945.95,
new InstrumentSpec(properties.clone()));
}
}
}
//  Inventory.as
package{
        public class Inventory{
                private var inventory:Array;
                public function Inventory():void{
                        inventory = new Array();
                }
                public function addInstrument(serialNumber:String,
                price:Number,
                spec:InstrumentSpec):void{
                        var instrument:Instrument = new Instrument(serialNumber,
                        price,
                        spec);
                        inventory.push(instrument);
                }
                public function get(serialNumber:String):Instrument{
                        var len:int = inventory.length;
                        for(var i:int=0;i<len;i++){
                                if(inventory[i].getSerialNumber() == serialNumber){
                                        return inventory[i];
                                }
                        }
                        return null;
                }
                public function search(searchSpec:InstrumentSpec):Array{
                        var matchingInstruments = new Array();
                        var len:int = inventory.length;
                        for(var i:int=0;i<len;i++){
                                if(inventory[i].getSpec().matches(searchSpec)){
                                        matchingInstruments.push(inventory[i]);
                                }
                        }
                        return matchingInstruments;
                }
        }

}
// Instrument.as
package{
        public class Instrument{
                private var serialNumber:String;
                private var price:Number;
                private var spec:InstrumentSpec;
                public function Instrument(serialNumber:String,
                price:Number,
                spec:InstrumentSpec) {
                        this.serialNumber = serialNumber;
                        this.price = price;
                        this.spec = spec;
                }
                public function getSerialNumber():String {
                        return serialNumber;
                }
                public function getPrice():Number{
                        return price;
                }
                public function setPrice(newPrice:Number):void{
                        price = newPrice;
                }
                public function getSpec():InstrumentSpec{
                        return spec;
                }
        }
}
// InstrumentSpec.as
package{
        public class InstrumentSpec{
                private var properties:HashMap;
                public function InstrumentSpec(properties:HashMap=null):void{
                        if (properties == null) {
                                this.properties = new HashMap();
                        } else {
                                this.properties = new HashMap(properties);
                        }
                }
                public function getProperty(propertyName:String):*{
                        return properties.getValue(propertyName);
                }
                public function getProperties():HashMap{
                        return properties;
                }
                public function matches(otherSpec:InstrumentSpec):Boolean{
                        var otherHashMap:HashMap = otherSpec.getProperties();
                        var len:int = otherHashMap.length;

                        var hashTable:Array = otherHashMap.getHashTable();

                        for (var i:int=0; i<len; i++ ) {
                                var propertyName:String = String(hashTable[i].key);

                                if (String(properties.getValue(propertyName)) !=
                                String(otherHashMap.getValue(propertyName))) {
                                        return false;
                                }
                        }
                        return true;
                }
        }
}
// HashMap.as
package{
        import flash.utils.ByteArray;
        public class HashMap{
                private var hashTable:Array;
                public function HashMap(hm:HashMap=null):void{
                        if (hm == null) {
                                hashTable = new Array();
                        } else {
                                hashTable = hm.getHashTable();
                        }
                }
                public function put(k:*, v:*):*{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        hashTable[i].key = k;
                                        hashTable[i].value = v;
                                        return v;
                                }
                        }
                        hashTable.push({key:k, value:v});
                        return v;
                }
                public function remove(k:*):Boolean{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        hashTable.splice(i,1);
                                        return true;
                                }
                        }
                        return false;
                }
                public function getValue(k:*):*{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        return hashTable[i].value;
                                }
                        }
                        return null;
                }
                // this function for test
                public function allTrace():void{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                trace(String(hashTable[i].key)+" : "+String(hashTable[i].value));
                        }
                }
                public function getHashTable():Array{
                        return hashTable;
                }
                public function setHashTable(hashTable:Array):void{
                        this.hashTable = hashTable;
                }
                public function get length():int{
                        return hashTable.length;
                }
                public function clone():HashMap{
                        var hm:HashMap = new HashMap();
                        var hashTableBA:ByteArray = new ByteArray();
                        hashTableBA.writeObject(hashTable);
                        hashTableBA.position = 0;
                        var ary:Array = hashTableBA.readObject() as Array;
                        hm.setHashTable(ary);
                        return hm;
                }
        }
}
// Builder.as
package{
        public class Builder{
                public static const FENDER = "Fender";
                public static const MARTIN = "Martin";
                public static const GIBSON = "Gibson";
                public static const COLLINGS = "Collings";
                public static const OLSON = "Olson";
                public static const RYAN = "Ryan";
                public static const PRS = "PRS";
        }
}

// InstrumentType.as

package{
        public class InstrumentType{
                public static const GUITAR = "Guitar";
                public static const BANJO = "Banjo";
                public static const DOBRO = "Dobro";
                public static const FIDDLE = "Fiddle";
                public static const BASS = "Bass";
                public static const MANDOLIN = "Mandolin";
        }
}

// Type.as

package{
        public class Type{
                public static const ACOUSTIC = "acoustic";
                public static const ELECTRIC = "Belectric";
        }
}

// Wood.as

package{
        public class Wood{
                public static const INDIAN_ROSEWOOD = "Indian Rosewood";
                public static const BRAZILIAN_ROSEWOOD = "Brazilian Rosewood";
                public static const MAHOGANY = "Mahogany";
                public static const MAPLE = "Maple";
                public static const COCOBOLO = "Cocobolo";
                public static const CEDAR = "Cedar";
                public static const ADIRONDACK = "Adirondack";
                public static const ALDER = "Alder";
                public static const SITKA = "Sitka";
        }
}



 

신고
    

설정

트랙백

댓글

[FlashCS3] AIR for FlashCS3 업데이트

Programming/ActionScript 3.0 2007.08.25 23:44
AIR이 공식적으로 FlashCS3에 서포트 되었다. 예전 포스트 grant skinner의 Flash CS3용 Adobe AIR extension 에서 개인이 만든 AIR extension 소개한 적이 있는데 adobe에서 공식적으로 AIR을 FlashCS3에서 생성할 수 있도록 지원하는 update 파일을 배포했다. 버전은 현재 AIR 1.0이다.  업데이트 파일은 아래 링크에서 다운로드 할 수 있다.

http://labs.adobe.com/wiki/index.php/AIR:Flash_CS3_Professional_Update

air파일 정보 세팅과 air파일 생성은 Commands 메뉴에 추가된 AIR-Application and Package Settings 와 AIR-Pacakge AIR File 명령을 통해서 세팅 및 생성할 수 있다.





사용자 삽입 이미지






신고
    

설정

트랙백

댓글