Context

모바일에 최적화 된 WebGL 라이브러리를 만들고 있는 MoGL 프로젝트에서는 MoGL이라는 최상위 클래스를 기준으로 여러 클래스들이 상속 구조를 형성하고 있다.

상속 방법은 아래와 같이 표준화 되어 있다.

1
2
3
MoGL.extend('Matrix',{
...
}

그렇다면 정적 분석을 통해서 위계(Hierarchy) 정보를 담고 있는 자료 구조를 뽑아내서 시각화 라이브러리와 버무리면, 실제 소스 코드와 언제나 Sync가 맞는 살아있는 다이어그램을 만들어 낼 수 있지 않을까?

정적 분석

소스 코드에서 .extend(을 포함하는 행만 추려서 간단한 위계 정보를 추출할 수 있다.
방법은 여러가지가 있겠지만 약간의 정규표현식과 substring()으로 다음과 같이 추출할 수 있다.

Hierarchy 정보 추출 및 구성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 표준화 된 상속 코드
var extendingPattern = '.extend(';

// 표준화 된 상속 코드를 포함하고 있는 행
// 물론 실무에서는 실제 소스 코드에서 추출해와야 된다.
var lines = [
"var BlendMode = MoGL.extend('BlendMode', {",
"return Matrix.extend('Camera',{",
"return MoGL.extend('Geometry', {",
"var Group = Matrix.extend('Group', {",
"return MoGL.extend('Material',{"
];

// child 이름을 추출하기 위한 정규표현식
var regexp0 = /'\w+'|"\w+"/;

// 추출에 사용되는 변수들
var line, splitted, parent, child, tmp, k;

// 위계 정보를 담고 있는 객체를 담는 Map
var clsMoGLMap = {};

for (k in lines) {
line = lines[k];
// parent, child 이름을 추출
if (line.indexOf(extendingPattern) > 0) {
splitted = line.split(extendingPattern);
parent = splitted[0].substring(splitted[0].lastIndexOf(' ') + 1);
tmp = regexp0.exec(splitted[1])[0];
child = tmp.substring(1, tmp.length - 1);
}

// 위계 정보를 담고 있는 객체를 구성해서 clsMoGLMap 에 추가
if (clsMoGLMap[parent]) {
clsMoGLMap[parent].childrenNames = child;
} else {
clsMoGLMap[parent] = new ClsMoGL();
clsMoGLMap[parent].name = parent;
clsMoGLMap[parent].childrenNames = child;
}

if (clsMoGLMap[child]) {
clsMoGLMap[child].parentName = parent;
} else {
clsMoGLMap[child] = new ClsMoGL();
clsMoGLMap[child].name = child;
clsMoGLMap[child].parentName = parent;
}

}

위계 정보를 담는 객체 ClsMoGLLinkedList와 비슷한 자료구조다. 애초에 단순히 텍스트에서 정보를 추출했기 때문에 텍스트를 담는 parentName, childrenNames가 추가되어 있다. 그리고 나중에 필요하다면 methodsfields 같은 필드를 추가해서 더 많은 정보를 다이어그램에 표시할 수도 있다.

부모는 하나고 자식은 여럿일 수 있으므로 자식만 배열로 하면 된다. children, childrenNames 배열에는 편리하게 그냥 값 할당을 하면 내부적으로는 push()가 호출되도록 구현했다.

Hierarchy 정보를 담는 자료 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
var ClsMoGL = function() {
var name, parentName, parent, childrenNames = [], children = [];
Object.defineProperties(this, {
'name':{
enumerable:true,
get:function() {
return name;
},
set:function(value) {
name = value;
}
},
'parentName':{
enumerable:true,
get:function() {
return parentName;
},
set:function(value) {
parentName = value;
}
},
'parent':{
enumerable:true,
get:function() {
return parent;
},
set:function(value) {
parent = value;
}
},
'childrenNames':{
enumerable:true,
get:function() {
return childrenNames;
},
set:function(childName) {
childrenNames.push(childName);
}
},
'children':{
enumerable:true,
get:function() {
return children;
},
set:function(child) {
children.push(child);
}
}
});
};

ClsMoGL을 담고 있는 clsMoGLMap를 재귀 함수 등을 이용해서 가공하면(사실 이 부분이 좀 복잡했는데 쉽게 이해할 수 있도록 설명하기는 어렵고, 이 방식이 가장 좋은 방식이라는 보장도 없으니, 필요하다면 그냥 소스 코드를 참고..) 대략 다음과 같은 json을 얻을 수 있다.

json으로 정리된 Hierarchy 정보
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"name": "MoGL",
"children": [{
"name": "BlendMode"
}, {
"name": "Geometry"
}, {
"name": "Material"
}, {
"name": "Matrix",
"children": [{
"name": "Camera"
}, {
"name": "Group"
}, {
"name": "Mesh"
}]
}, {
"name": "Primitive"
}, {
"name": "Scene"
}, {
"name": "Shader"
}, {
"name": "Shading"
}, {
"name": "Texture"
}, {
"name": "Vector"
}, {
"name": "Vertex"
}, {
"name": "World"
}]
}

자료 구조를 만들었으면 나머지 일은 D3.js 가 접수한다.

D3.js

두말이 필요없는 굉장한 시각화 라이브러리인 D3.js에는 이런 위계 정보를 나타내는 다이어그램을 쉽게 만들 수 있는 여러가지 built-in 템플릿을 제공해준다. D3.js에는 이런 built-in 템플릿을 layout이라고 부른다.

D3.js의 Cluster layout을 사용하면 쉽게(?) 다이어그램을 그릴 수 있다.

여기서는 사실 http://bl.ocks.org/mbostock/4063570 에 있는 예제 중에서 d3.json()으로 json을 외부에서 가져오는 대신 위에서 만든 내부의 json을 사용하도록 변경한 부분 외에는 거의 그대로 갖다 썼다. 그래서 ‘쉽게’라고 할 수 있지만, 사실 D3.js가 그렇게 쉽지만은 않다.

D3.js는 여러가지 다이어그램을 쉽게 그릴 수 있게 해주는 다양한 built-in API가 제공되지만, D3.js를 만든 Mike Bostock 형님도 늘 강조하듯, 화려한 시각화 뒤에 숨어있는 D3.js의 진정한 마술은 데이터와 DOM 요소를 매핑해주는 부분에 있다. 이 부분을 시각화를 이용해서 잘 설명해준 글이 있는데, 너무 좋아서 번역을 해뒀으니 여기를 참고하면 D3.js를 배우는 데 도움이 될 것이다.

결과와 소스 코드는 여기에서 볼 수 있다.

화려하진 않지만(물론 공을 더 들이면 화려하게 만들 수도 있다), 소스 코드가 바뀌면 다이어그램도 따라서 바뀌는 살아있는 문서라는 점에서 쓸모는 꽤 있을 것이다.

정리

  • JavaScript 프로젝트에서 표준화 된 상속 구문을 사용하면, 정적 분석D3.js를 엮어서 살아있는 상속 구조 다이어그램을 만들 수 있다.

크리에이티브 커먼즈 라이선스HomoEfficio가 작성한 이 저작물은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.