FingerEyes-Xr for HTML5 – ShapeMapLayer의 도형정보 및 속성 정보 접근

FingerEyes-Xr for HTML5의 레이어 중 ShapeMapLayer에 대해, 도형 정보 및 속성 정보에 접근하는 코드를 정리해 봅니다. 참고로 아래 코드는 Point 타입의 ShapeMapLayer에 대한 코드입니다.

var snodeLayerId = snode.layerId(); // Layer Id
var snodeNetworkId = snode.nodeId(); // 검색할 필드값
var snodeLayer = g_map.layers(snodeLayerId);
var snodeRowset = snodeLayer.shapeRowSet();
var snodeAttrRowset = snodeLayer.attributeRowSet();
var snodeFieldSet = snodeLayer.fieldSet();
var snodeRows = snodeRowset.rows();
var idxNetwordId = snodeFieldSet.fieldIndex('networkid'); // 검색 대상 필드명

for (var fid in snodeRows) {
    var attRow = snodeAttrRowset.row(fid);
    var vNetworkId = attRow.valueAsString(idxNetwordId);

    if (vNetworkId == snodeNetworkId) {
        var psRow = snodeRows[fid];
        var psData = psRow.shapeData();
        var pt = psData.data();

        alert(pt.x + " " + pt.y);

        break;
    }                    
}

Java8, Stream에 대한 병렬처리

Java 8에서 제공하는 스트림(Streams)에 대한 기능에 대해 정리한 적이 있습니다. Java 8에서 스트림은 List 등과 같은 자료구조를 통해 생성할 수 있는 메모리 상의 또 다른 자료구조인데요. 이 스트림 자료구조를 통해 Filter, Sorted, map, forEach, anyMatch, allMatch, noneMatch, count, Reduce 맴버 함수를 호출할 수 있으며, 이러한 함수 호출에 대해 하나의 스레드만을 사용해 처리할 것인지, 아니면 멀티 스레드를 통해 병렬로 처리할 것인지 개발자가 매우 간단히 정할 수 있는 매커니즘을 제공합니다.

예를 들어서, 백만개의 UUID 문자열을 담고 있는 리스트가 있다고 합시다. 즉, 아래의 코드를 통해 메모리 상에 구성될 수 있습니다.

public static void main(String args[]) {
    int max = 1000000;
    List values = new ArrayList<>(max);
		
    for (int i=0; i

11번과 12번 코드가 각각 백만개의 문자열를 갖는 리스트를 정렬하는 방식을 하나의 스레드로 할 것인지, 멀티 스레드로 처리할 것인에 대한 함수 호출입니다. 먼저 sequential 함수를 살펴보면 아래와 같습니다.

public static void sequential(List v) {
    long t0 = System.nanoTime();
    /*long count = */ v.stream().sorted().count();
    long t1 = System.nanoTime();
		
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("sequential sort took: %d ms", millis));
}

3번 코드가 스트림에서 제공하는 sorted 함수를 통해 직관적으로 정렬하는 것으로써 코드 한줄이지만 그 내부는 다소 복잡하고, 특히나 백만개에 대한 정렬이므로 상당한 CPU 리소스를 사용할 것입니다. 그리고 다음은 멀티 스레드를 통한 parallel 함수입니다.

public static void parallel(List v) {
    long t0 = System.nanoTime();
    /* long count = */ v.parallelStream().sorted().count();
    long t1 = System.nanoTime();
		
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("parallel sort took: %d ms", millis));
}

sequential 함수와의 차이점이라고는 오직 스트림을 생성하기 위해 3번 코드의 stream() 대신 parallelStream() 호출입니다. 이제 실행해 보면 제 노트북에서는 다음과 같은 결과가 나옵니다.

sequential sort took: 708 ms
parallel sort took: 244 ms

제 노트북이 멀티 코어 CPU이므로 동일한 연산이지만 병렬로 정렬하는데 소요되는 시간이 훨씬 더 빠른 처리된 결과를 볼 수 있습니다. Java 8에서 제공하는 스트림을 통해 데이터의 덩어리를 매우 직관적이며 매우 쉽게 병렬로 처리할 수 있다는 것을 알 수 있습니다.

요즘 golang과 같은 최신의 언어에서도 그렇고... Java나 C++과 같은 언어에서 람다 지원을 통해 작성된 코드를 살펴봐도 그렇고.. OOP 보다는 함수 지향적인 코딩이 상당히 강조되는 것을 느낍니다. OOP 적인 사고방식 보다는 컴포지션(Composition)과 재귀적 호출을 통한 함수 작성 그리고 짧은 코드로 구성한 단위 함수들의 조합을 통한 프로그래밍 방식에 익숙해 지는 것이 필요할 듯 합니다.

[Golang] 변수가 어떤 interface의 매서드를 제공하는지 여부 확인하기

다음과 같은 타입이 있다고 치십시다.

type Boy int

func (b Boy) fight() {
	fmt.Println("Kaho-!")
}

func (b Boy) laugh() {
	fmt.Println("Kuwek-!")
}

type Girl float32

func (b Girl) laugh() {
	fmt.Println("kaka~")
}

또 다음과 같은 interface가 있다고 치자구요.

type Laughter interface {
	laugh()
}

type Fighter interface {
	fight()
}

이제 main 함수에서 Boy와 Girl 타입에 대한 객체를 정의할껀데요. 이 2개의 객체 변수가 Laughter, Fighter 인터페이스를 충족하는지의 여부를 검사하는 코드는 아래와 같습니다.

func main() {
	var b Boy
	var g Girl

	if _, ok := interface{}(b).(Laughter); ok {
		fmt.Println("Boy is laughter.")
	} else {
		fmt.Println("Boy is not laughter.")
	}

	if _, ok := interface{}(g).(Fighter); ok {
		fmt.Println("Girl is fighter.")
	} else {
		fmt.Println("Girl is not fighter.")
	}
}

그 결과는 예상하는 것처럼 아래와 같습니다.

Boy is laughter.
Girl is not fighter.

[Golang] 구조체 객체 생성

언어 Go는 필드를 하나의 개념으로 묶는 구조체 타입을 제공합니다. 아래는 (X, Y) 좌표를 나타내는 Point 타입을 나타냅니다.

type Point struct {
    x float64
    y float64
}

이 Point 타입을 객체로 만드는 방법은 너무….. 많아도 너무 마나. (마나? 히어로즈에서 제이나는 늘 마나가 부족하지..) 큭.. 여튼.. 많지만, 알고보면 일관성이 있어 이해가 어렵지 않은데요. 아래와 같은 다양한 방식을 지원합니다.

{
    var p Point
    p.x = 10.0
    p.y = 10.0
    fmt.Println(p)
}

{
    var p Point = Point{10.0, 10.0}
    fmt.Println(p)
}

{
    var p = Point{10.0, 10.0}
    fmt.Println(p)
}

{
    var p = Point{x: 10.0, y: 10.0}
    fmt.Println(p)
}

{
    var p *Point = new(Point)
    p.x = 10.0
    p.y = 10.0
    fmt.Println(p)
}

객체 생성 방식이 5개 정도 되는데요. 실행해 보면 다음과 같습니다.

{10 10}
{10 10}
{10 10}
{10 10}
&{10 10}

마지막 방식은 new를 통해 포인트 변수로 객체를 생성했습니다. 불행인지, 아니면 의도적인 것인지.. new를 통해 바로 구조체의 필드값을 지정할 수 없는데요. 이럴 때는 하나의 패턴으로 다음처럼 생성자 함수를 만들어 활용할 수 있습니다.

func NewPoint(x, y float64) *Point {
    return &Point{x, y}
}

위의 함수의 사용은 다음과 같습니다.

{
    var p = NewPoint(10.0, 10.0)
    fmt.Println(p)
}

실행 결과는 아래와 같습니다.

&{10 10}

[Golang] 객체 Pool

Golang에서 객체 재활용을 위한 pool을 제공하는데요. 이 객체 풀에 대한 내용을 정리해 봅니다.

먼저 풀에 넣고 재활용하고자 하는 객체 타입을 아래처럼 정의합니다.

type DB struct {
    bConnected bool
}

func (c *DB) connect() {
    c.bConnected = true
    fmt.Println("[connection completed]")
}

func (c *DB) query() {
    if c.bConnected {
        fmt.Println("    [query completed]")
    } else {
        fmt.Println("    [could not query]")
    }
}

데이터베이스 객체에 대해 흉내를 낸 DB 타입인데요. DB는 미리 DB 서버에 연결을 한번 맺어 놓고 계속 쿼리를 날립니다. 이 DB 타입은 DB 서버에 연결하는 connect 매서드와 쿼리를 날리는 query 매서드가 있고, DB 서버에 연결이 되어 있는지를 확인할 수 있는 bConnected 필드 변수를 가지고 있는데, 이 필드 변수는 connect 매서드를 호출하면 true로 변경됩니다.

자, 이제 이 DB 객체를 미리 연결해 놓고 풀에서 가져다 재활용하는 코드를 살펴보겠습니다.

func main() {
    pool := sync.Pool{
        New: func() interface{} {
            r := new(DB)
            r.bConnected = false
            return r
        },
    }

    for i := 0; i < 10; i++ {
        go func() {
            c := pool.Get().(*DB)
            if !c.bConnected {
                c.connect()
            }

            c.query()
            pool.Put(c)
        }()
    }

    fmt.Scanln()
}

Key Point가 되는 부분만을 살펴보면, 먼저 Golang의 Pool 객체를 생성하기 위해서 2~8번처럼 New 필드에 재사용할 객체를 생성해주는 함수를 정의해줘야 합니다. 4번처럼 앞서 DB 타입의 객체를 생성하고, 아직 connect 매서드를 호출하지 않았으므로 5번에서 bConnected를 false로 지정합니다. 물론 지정하지 않아도 Null 값으로 false가 저장되어 있지만 명확히 하는 것이 좋으니 이리 했습니다. 그리고 6번에서 생성한 DB 객체를 반환합니다. 이 New 함수는 풀에 객체가 부족할때만 호출되고 재활용 가능한 여분의 객체가 있다면 New 함수 호출 없이 여분의 객체 중 하나를 가져다가 사용합니다.

이제 10번의 for 문에서 10번에 걸쳐 db 객체를 통해 쿼리를 날립니다. go 루틴으로 쿼리를 동시에 10번 날리는 경우인데요. go 루틴의 함수를 보면.. 먼저 12번에서 DB 객체를 풀에서 가져옵니다. 여기서 타입단언(Type Assertion)을 통해 DB 타입이라는 것을 분명히 합니다. 그리고 13번에서 아직 연결이 되어 있지 않다면 connect 함수를 호출하구요. 17번에서 쿼리를 날립니다. DB 객체를 다 사용했다면 18번 코드에서 다시 풀에 반환합니다.

만약 풀을 사용하지 않았다면 10번의 DB 연결과 10번의 DB 쿼리가 발생할 것입니다만.. 우리는 풀을 사용했기 때문에 DB 쿼리는 10번을 했지만 DB 연결은 그보다 적을 것입니다. 제 PC에서는의 실행 결과는 아래와 같습니다.

[connection completed]
    [query completed]
    [query completed]
    [query completed]
    [query completed]
    [query completed]
    [query completed]
    [query completed]
[connection completed]
    [query completed]
    [query completed]
[connection completed]
    [query completed]

실행할때마다 그 결과는 매번 달라지기는 했지만, 분명한 것은 DB 쿼리에 대한 query completed 문자열은 항상 10번 표시되고 connection completed는 그보다 적게 표시된다는 것입니다.