• JavaScript 

    1. 사전 지식

    • Blob

      • 정의
        • Blob 는 일반적으로 미디어(이미지, 사운드, 비디오) 파일과 같은 큰 용량의 파일을 말한다.
      • Blob Object

        • Blob Object 는 File 과 같은 불변 객체를 나타내며, raw data 를 가진다.

          • 추가로 File 인터페이스Blob 인터페이스 의 모든 특성들을 상속받는다.

      • Blob API in MDN
      • Blob in Terms
      • Blob in Can I Use
    • JavaScript Typed Array

      • 정의

        • Typed Array 는 raw binary data 에 접근하기 위한 방법을 제공한다.

          • 즉 자바스크립트로 binary data 를 다루기 위해 사용한다.
        • 유연성효율성을 위해 bufferview 로 나눠 구현되어있다.

          • buffer

            • ArrayBuffer 는 고정된 크기의 raw binary data 를 나타내기 위해 사용된다.
            • ArrayBuffer 클래스 통해 생성된 buffer데이터 청크를 나타내는 객체이다.

                // 12 bytes buffer 나타낸다
                var buffer = new ArrayBuffer(12);
              
            • buffer저장된 데이터를 접근하기 위한 방법을 제공하지 않는다.

            • 데이터를 다루기 위해서는 반드시 view 를 사용해야한다.
          • view

            • DataView

              DataView viewbuffer 에 저장된 데이터로 부터 값을 읽고, 쓰기 위한 low-level 인터페이스 를 제공한다.(getter/setter API 제공)

                // 12 bytes byffer
                var buffer = new ArrayBuffer(12);
                // view 를 생성한다.
                var view = new DataView(buffer, 2, 2);
              
                // 해당 view 가 시작하는 위치를 반환한다.
                console.log(view.byteOffset); // 2
              

              데이터를 다루기위한 DataView 의 특성들은 아래와 같다.

            • Typed Array Views

              DataView 를 상속한 아래 클래스들을 통해 buffer 에 저장된 데이터를 다룰 수 있게된다.

              Int8Array, Uint8Array, Int32Array, Uint32Array 등 …

              위 클래스 중 Int32Array 를 통해 생성된 view 는 아래와 같은 특성들을 가지게된다.

              아래 그림은 각 view 에 따라 나눠지는 메모리 공간을 나타낸다.

                /*
              
                ArrayBuffer(20 bytes)
                8bit == 1 byte
              
                ArrayBuffer / 1 byte = 20;
              
                 */
                var buffer = new ArrayBuffer(20);
                // 부호 없는 1 byte 정수 배열
                var uint8View = new Uint8Array(buffer);
              
                console.log(uint8View.length); // 20
              
                /*
              
                 ArrayBuffer(20 bytes)
                 32bit == 4 byte
              
                 ArrayBuffer / 4 byte = 5;
              
                 */
              
                var buffer = new ArrayBuffer(20);
                // 부호 없는 4 byte 정수 배열
                var uint32View = new Uint32Array(buffer);
              
                console.log(uint32View.length); // 5
              

              unsigned or signed view test

                // unsigned int 8(1 bytes)
              
                var buffer = new ArrayBuffer(20);
                var uint8View = new Uint8Array(buffer);
              
                // 0 ~ 255(unsigned int 8(1 bytes) 로 표현 가능한 수)
                uint8View[0] = 0;
                uint8View[1] = 255;
              
                console.log(uint8View); // [0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
              
                // signed int 8(1 bytes)
              
                var buffer = new ArrayBuffer(20);
                var int8View = new Int8Array(buffer);
              
                // -127 ~ 128(signed int 8(1 bytes) 로 표현 가능한 수)
              
                // signed 의 경우 부호(양수/음수)를 나타내기 위해 총 8bit 중 1 비트(0: 양수, 1: 음수) 사용하기 때문에, 나머지 7bit(-127 ~ 128(표현 가능한 수))를 통해 숫자를 표현하게 된다.
              
                int8View[0] = -128;
                int8View[1] = 127;
              
                console.log(int8View); // [-128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
              
                // unsigned int 16(2 bytes)
                var buffer = new ArrayBuffer(20);
                var uint16View = new Uint16Array(buffer);
              
                // 0 ~ 65535(unsigned int 16(2 bytes) 로 표현 가능한 수)
                uint16View[0] = 65535;
              
                console.log(uint16View); // [65535, 0, 0, 0, 0, 0, 0, 0, 0, 0]
              
        • ArrayBuffer

        • DataView

        • DataView API Test

        • JavaScript Typed Arrays

        • JavaScript Typed Arrays(번역)

        • signed 와 unsigned 의 차이

        • signed 가 음수 표현하는 방법(상세 설명)

        • 나는 unsigned 가 싫어요

        • signed or unsigned 자료형의 범위

        • Binary Convert

    • Little-Endian or Big-Endian

      • 정의

        • Endian 은 컴퓨터에서 데이터가 저장되는 순서(byte order)를 말한다.

        • 정리:

          • 메모리는 하위 주소에서 상위 주소로 데이터가 저장된다.

          • Little Endian: 하위 바이트부터 데이터가 저장되는 방식.

            • Little Endian 방식의 장점: 산술연산유닛(ALU)에서 메모리를 읽는 방식이 메모리 주소가 낮은 쪽에서부터 높은 쪽으로 읽기 때문에 산술 연산의 수행이 더 쉽다.(연산 처리 과정에서 이런 장점이 있는 정도로만 알고 넘어가자…)
          • Big Endian: 상위 바이트부터 데이터가 저장되는 방식.

      • 적용 이유:

        • **CPU**(Intel / Spac) **타입**에 따라 차이를 보이는 byte order(**데이터 저장 순서**)는, 동일한 시스템 안에서만 데이터를 주고 받는다면, Endian 에 대해 전혀 신경쓸 필요가 없지만, 이기종간에 데이터를 주고 받을 경우, 서로간의 데이터 저장 방식 차이로 인해 전혀 엉뚱한 결과를 반환하게 된다.
      • 서로 다른 Endian 간의 데이터 통신 해결책:

        • 공통되는 Endian(약속된 Endian 규칙)으로 변환 후, 데이타를 주고/받는 방법.

          • 즉 서로간에 사용할 Endian(Little Endian or Big Endian) 을 하나로 통일시켜 데이터를 주고 받는 것이다.
        • 또 하나의 방법은 byte order(바이트 저장 순서) 를 신경쓸 필요가 없는, 데이터 타입을 사용하는 것이다. char 타입은 1byte 의 크기를 가지기때문에, byte order 에 대해 전혀 신경쓸 필요가 없다. 예를 들면 12345678 을 int 형으로 보내는 대신 문자열 “12345678” 로 변환시켜 전송하면 된다.

      • 엔디언

      • Little-Endian or Big-Endian(개념 잡기 좋은 문서)

      • Little-Endian or Big-Endian 개념

      • Endian 에 대해서

    2. JavaScript 를 통해 Binary Data 조작하기

    • 예제 소스에서는 NodeJSSocket.IO 와 관련된 내용은 최대한 배제 하였습니다.(특별히 포스트 내용과 관련 없다고 판단한 내용)

    • 파일 업로드

      • FileFileReader API 를 지원하는 브라우저를 통해 파일 업로드 기능을 만들 수 있다.

        • 하지만 IE(10/11 포함) 브라우저는 지원하지 않는다고 보면된다.

        • Source Example

          • Cliend Side(use JS)

          • 먼저 저장소로 부터 내려받은 파일을 include 한다.

              <script src="siofu_client.js"></script>
            
              // socket 서버에 연결
              var socket = io.connect('http://localhost:9090');
            
              // socket 객체를 SocketIOFileUpload 클래스로 전달한다.
              var uploader = new SocketIOFileUpload(socket);
            
              // listenOnSubmit 메서드에 input[type="button"] 및 input[type="file"] Element 를 전달한다.
              uploader.listenOnSubmit($('#btn_upload').get(0), $('#siofu_input').get(0));
            
              // KiB === byte 단위
              // KB === KByte 단위
            
              // 한번에 로드될 chunks 파일 사이즈
              // chunkSize 를 0으로 할당하면, chunk 를 사용하지 않게 된다.
              uploader.chunkSize = 1024 * 100; // 102400 byte 로 chunk 단위를 나눈다.
            
              uploader.addEventListener("start", function(event){
                  console.log('started upload of file');
              });
            
              // progress 이벤트를 통해 현재 진행 상황을 볼 수 있다.
              uploader.addEventListener("progress", function(event){
                  var percent = event.bytesLoaded / event.file.size * 100;
                  console.log("File is", percent.toFixed(2), "percent loaded");
              });
            
              // 파일 업로드가 끝날을때 이벤트가 발생한다.
              uploader.addEventListener("complete", function(event){
                  console.log('completed file upload');
              });
            
          • Server Side(use nodeJS)

                var uploader = new siofu();
                uploader.dir = "uploads";
                uploader.listen(socket);
          
                // Do something when a file is saved:
                uploader.on("saved", function(event){
                    console.log(event.file);
                });
          
                // Error handler:
                uploader.on("error", function(event){
                    console.log("Error from uploader", event);
                });
          
        • 참고 사이트

          socket io file upload module

          File API

          FileReader API

    • 이미지 효과

      • 서버에서 내려받은 ArrayBuffer(이미지 데이터) 로 view(uInt8Array) 를 생성 후, 버퍼에 저장된 데이터를 조작한다.

      • Source Example

        • Cliend Side(use JS)

          
            var cw = 327;
            var ch = 125;
          
            // canvas Element 를 가져온다.
            var canvas = document.querySelector('canvas');
          
            // context 를 생성한다.
            var ctx = canvas.getContext('2d');
          
            // view(부호 없는 1byte 정수 배열)를 생성한다.
            var uInt8Array = new Uint8Array(payload.buffer);
          
            // view를 통해 Blob Object 를 생성한다.
            var blob = new Blob([uInt8Array], {type: 'image/jpeg'});
          
            var originalImgData = null;
          
            // Blob Object를 참조하는 URL를 생성한다.
            var url = URL.createObjectURL(blob);
            var img = new Image;
          
            // 이미지 로드 이벤트
            $(img).bind('load', function(){
                canvas.width = img.width;
                canvas.height = img.height;
          
                // 캔버스에 해당 이미지를 그린다.
                ctx.drawImage(img, 0, 0, img.width, img.height);
          
                // 각 px 에 대한 정보(r,g,b,a)가 담긴 이미지 데이터를 가져온다.
                originalImgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          
                // 반전 효과를 준다.
                // invert();
          
                // 흑백 효과를 준다.
                empty();
            });
          
            // Blob 객체를 참조하는 URL을 img.src 에 할당 후 로드한다.
            img.src = url;
          
            // px 단위의 이미지 데이터를 조작하여, 반전 효과를 준다.
            function invert(){
          
                originalImgData = ctx.getImageData(0, (canvas.height / 2), canvas.width, canvas.height);
                var data = originalImgData.data;
          
                for (var i = 0; i < data.length; i += 4) {
          
                    data[i] = 255 - data[i];     // red
                    data[i + 1] = 255 - data[i + 1]; // green
                    data[i + 2] = 255 - data[i + 2]; // blue
                }
          
                ctx.putImageData(originalImgData, 0, (canvas.height / 2));
            };
          
            // px 단위의 이미지 데이터를 조작하여, 흑백 효과를 준다.
            function empty(){
          
                originalImgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                var data = originalImgData.data;
          
                for (var i = 0; i < data.length; i += 4) {
          
                    // 각 픽셀의 밝기만 조사하여 R, G, B 색상 요소를 균일하게 만들면 회색이 된다.(색상 정보를 아래 공식(각 요소(R, G, B)가 밝기에 미치는 영향은 29:58:11로 전문가에 의해 계산되어 있다)으로 R,G,B 요소에서 제거한다)
          
                    // 128 이상은 흰색으로, 128 이하는 검정색으로 만들어 버림으로써, 흰색과 검정색 두 가지만 남긴다. 경계값인 128을 조정하면 밝기가 달라진다.
                    var gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
          
                    if (gray > 128){
                        gray = 255;
                    }
                    else{
                        gray = 0;
                    }
          
                    data[i] = gray;     // red
                    data[i + 1] = gray; // green
                    data[i + 2] = gray; // blue
                }
          
                ctx.putImageData(originalImgData, 0, 0);
            };
          
        • Server Side(use nodeJS)

            var fs = require('fs');
          
            // 파일을 읽은 후 클라이언트로 버퍼를 전달한다.
            fs.readFile('./lib/img/nmms_20823487.jpg', function (err, buf) {
                // it's possible to embed binary data
                // within arbitrarily-complex objects
                socket.emit('onSocketMsg', {
                    type: 'resultImageData',
                    payload: {
                        buffer: buf
                    }
                });
            });
          
      • 적용 결과

        • 원본 이미지

        • invert 함수 적용 이미지

        • empty 함수 적용 이미지

      • 참고 사이트

    • ArrayBuffer 로 내려받은 비디오 플레이

      • 서버에서 내려받은 ArrayBuffer(영상 데이터) 로 view(in Typed Array Views) 를 생성 후, 버퍼에 저장된 데이터를 조작한다.

        • 영상 및 오디오 데이터의 경우, 브라우저 지원 여부지원 포맷에 대해 반드시 확인해봐야한다.

        • 아래 소스는 Chrome 브라우저에서 *.mp4*.webm 포맷으로만 테스트되었습니다.

      • Source Example

        • Cliend Side(use JS)

            var vw = 327;
            var vh = 125;
          
            // video Element 를 가져온다.
            var video = document.querySelector('video');
            video.width = vw;
            video.height = vh;
          
            // view(부호 없는 1byte 정수 배열)를 생성한다.
            var uInt8Array = new Uint8Array(payload.buffer);
          
            // view를 통해 Blob Object 를 생성한다.
            var blob = new Blob([uInt8Array], {type: 'video/webm'});
            //var blob = new Blob([uInt8Array], {type: 'video/mp4'});
          
            // Blob Object를 참조하는 URL 를 생성한다.
            var url = URL.createObjectURL(blob);
          
            // Blob 객체를 참조하는 URL을 video.src 에 할당 후 로드한다.
            video.src = url;
          
        • Server Side(use nodeJS)

            fs.readFile('redcliff450.webm', function (err, buf) {
                socket.emit('onSocketMsg', {
                    type: 'resultVideoData',
                    payload: {
                        buffer: buf
                    }
                });
            });
          
    • Chunk 방식으로 내려받은 비디오 플레이

      • 서버에서 Chunk 방식으로 내려받은 ArrayBuffer(영상 데이터) 로 view(in Typed Array Views) 를 생성 후, 버퍼에 저장된 데이터를 조작한다.

      • MediaSource API 를 통해 내려받은 영상 데이터를 조작할 수 있다.

      • 영상 및 오디오 데이터의 경우, 브라우저 지원 여부지원 포맷에 대해 반드시 확인해봐야한다.

      • 아래 소스는 Chrome 브라우저에서 *.webm(vorbis 및 vp8 코덱) 포맷으로만 테스트되었습니다.

      • Source Example

        • Cliend Side(use JS)

        • 저장소로 부터 내려받은 파일을 include 한다.

            <script src="socket.io-stream.js"></script>
          
            // stream 메서드에 socket 객체를 전달 후 해당 이벤트를 바인딩한다.
            ss(socket).on('onSocketMsg', function(data) {
          
                data = data || {};
          
                var type = data.type;
                var payload = data.payload;
          
                if (type === 'resultChunkVideoData') {
          
                    var vw = 1024;
                    var vh = 768;
          
                    // video Element 를 가져온다.
                    var video = document.querySelector('video');
                    video.width = vw;
                    video.height = vh;
          
                    console.log(payload.stream); // 내려받은 stream 데이터
          
                    // MediaSource 객체를 생성한다.
                    var ms = new MediaSource();
          
                    // MediaSource 객체를 참조하는 URL 를 생성한다.
                    var url = URL.createObjectURL(ms);
          
                    // MediaSource 객체를 참조하는 URL을 video.src 에 할당 후 로드한다.
                    video.src = url;
          
                    // MediaSource 객체에 각 이벤트를 바인딩 시킨다.
                    ms.addEventListener('sourceopen', callback, false);
                    // ms.addEventListener('webkitsourceopen', callback, false);
                    ms.addEventListener('sourceended', function(e) {
                        console.log('mediaSource readyState: ' + this.readyState);
                    }, false);
          
                    function callback() {
          
                        // 재생하려는 영상 소스를 추가한다.
                        var sourceBuffer = ms.addSourceBuffer('video/webm; codecs="vp8"');
                        // var sourceBuffer = ms.addSourceBuffer('video/webm; codecs="vp8,vorbis"');
          
                        sourceBuffer.addEventListener('updatestart', function (e) {
                            // console.log('updatestart: ' + ms.readyState);
                        });
          
                        sourceBuffer.addEventListener('update', function () {
                            // console.log('update: ' + ms.readyState);
                        }, false);
          
                        sourceBuffer.addEventListener('updateend', function (e) {
                            console.log('updateend: ' + ms.readyState);
                        });
                        sourceBuffer.addEventListener('error', function (e) {
                            console.log('error: ' + ms.readyState);
                        });
                        sourceBuffer.addEventListener('abort', function (e) {
                            console.log('abort: ' + ms.readyState);
                        });
          
                        payload.stream.on('data', function (data) {
          
                            // chunk data
                            console.log(data);
          
                            // 버퍼에 내려받은 스트림 데이터를 할당한다.
                            sourceBuffer.appendBuffer(data);
          
                        });
          
                        // 데이터 전송이 완료되었을 경우 발생한다.
                        payload.stream.on('end', function () {
                            console.log('endOfStream call');
                            // 스트림을 종료한다.
                            ms.endOfStream();
                        });
                    }
                }
            });
          
        • Server Side(use nodeJS)

          
            var ss = require('socket.io-stream');
          
            ss(socket).on('onSocketMsg', function(data) {
          
                    data = data || {};
          
                    var type = data.type;
                    var payload = data.payload;
          
                    var stream = ss.createStream();
          
                    if (type === 'downloadChunkVideo') {
          
                        // webm 포맷의 영상을 가져온다.
                        var filename = path.basename('feelings_vp9-20130806-244.webm');
                        // 파일 스트림을 생성한다.
                        fs.createReadStream(filename).pipe(stream);
          
                        ss(socket).emit('onSocketMsg', {
                            type: 'resultChunkVideoData',
                            payload: {
                                stream: stream
                            }
                        });
                    }
                });
          
        • 테스트 결과

      • 참고 사이트

    Read more


  • designPattern 

    1. 정의

    • C 와 C++(또는 C# 등) 에서 구조화 된 데이터를 처리할 때, Struct 를 사용하는데 이를 구조체라 부른다.

      • 최초의 구조체는 C 언어에서 다양한 자료구조를 하나의 집합으로 만들어 관리하기위해 만들어졌다.

        • 구조체는 하나 이상의 자료구조를 가진 또 하나의 데이터 타입을 정의한다.
      • 이 글은 구조체를 다루는 여러 언어 중 [C# 구조체] 를 기준으로 하고있다.

      • 코드화
        public struct Song
        {
            public int lengthInSeconds, yearRecorded;
      
            // 생성자
            public Song(int p1, int p2)
            {
                lengthInSeconds = p1;
                yearRecorded = p2;
            }
        }
      
        class TestSong
        {
            static void Main()
            {
                // Initialize:
                // 생성된 객체는 Stack 메모리에 객체가 할당된다.
                Song song1 = new Song(213, 1994);
                Song song2 = new Song(248, 1988);
      
                // Display results:
                Console.WriteLine("lengthInSeconds = {0}, yearRecorded = {1}", song1.lengthInSeconds, song1.yearRecorded);
      
                Console.WriteLine("lengthInSeconds = {0}, yearRecorded = {1}", song2.lengthInSeconds, song2.yearRecorded);
      
            }
        }
      
        /* Output:
            lengthInSeconds = 213, yearRecorded = 1994
            lengthInSeconds = 248, yearRecorded = 1988
        */
      
      • 구조체Class 와 같이 여러 특성(생성자, 상수, 필드, 메서드, 속성, 인덱서, 연산자, 이벤트 등)들을 그룹화 하는데 사용된다.

        • 구조체클래스는 매우 비슷한 구조를 가지고 있으며, **사용 방법** 및 **적용 이유**가 유사하다.

    2. C# 구조체의 특징

    • 값 타입이다.

      • 값 형식 및 참조 형식

      • [Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing]

        • http://www.codeproject.com/Articles/76153/Six-important-NET-concepts-Stack-
    • 생성된 객체는 Stack 메모리에 할당된다.

    • 구조체는 보통 작은 데이터를 다루는데 적합하다.

      • 하지만 이것클래스표현할 수 없는 것은 아니다.(그냥 작은 Class를 만들면 그만이다;;)

        • 그럼에도 불구하고 구조체를 써야하는 이유는 분명 존재할 것이다.
    • new 연산자를 사용하지 않고도 인스턴스화할 수 있다.

      • 하지만 **생성자**를 호출하지 않으므로, 선언된 맴버를 따로 초기화 해야하는 불편함이 존재한다.
        public struct Song
        {
            public int lengthInSeconds, yearRecorded;
      
            // 생성자
            public Song(int p1, int p2)
            {
                lengthInSeconds = p1;
                yearRecorded = p2;
            }
        }
      
        class TestSongNoNew
        {
            static void Main()
            {
                // new 연산자 없이 인스턴스를 생성한다.
                // Stack 메모리에 객체가 할당된다.
                // 생성자를 호출하지 않는다.
                Song song1;
      
                // 속성 초기화
                song1.lengthInSeconds = 213;
                song1.yearRecorded = 1994;
      
                // Display results:
                Console.WriteLine("lengthInSeconds = {0}, yearRecorded = {1}", song1.lengthInSeconds, song1.yearRecorded);
      
            }
        }
      
        /* Output:
            lengthInSeconds = 213, yearRecorded = 1994
        */
      
    • 구조체구조체 또는 클래스에서 상속될 수 없으며, 클래스의 기본 클래스가 될 수 없다.

      • 클래스와 달리, 상속 구조를 구현할 수 없다.
    • 클래스와 같이 **인터페이스**를 구현할 수 있다.

    • 구조체는 Stack 영역에 할당되는 값 타입이고, 클래스는 Heap 영역에 할당되는 참조 타입이다.

      • Stack 영역(정적 할당)이란?

      • Stack 영역은 컴파일 시점에서 크기가 결정되는 요소들이 저장되는 메모리 영역이다.

        • 함수 내부 지역 변수매개 변수Stack 영역에 할당되며, 함수 종료 시 소멸된다.
      • Stack 영역은 LIFO(Last In First out)라는 자료구조 가진다.

        • 즉 마지막에 넣은(Push) 데이터가 가장 먼저 나가는것을(Pop) 의미한다.

          • Stack Push 순서:

            • A > B > C > D > E
          • Stack Pop 순서:

            • E > D > C > B > A
        • 그럼 JS Stack 은?

          • JS 는 동적 언어 이므로 컴파일 시점이 아닌 런타임 시점에서 메모리가 할당(allocation)된다.

            • C, C++, C#(정적 언어)
          • JS 원시 타입(string, number, boolean, undefined, null)은 고정된 크기를 가지며, Stack 메모리 영역에 할당된다.

          • JS 의 모든 실행 컨텍스트ECStack(일종의 Call Stack) 내부로 할당된다.

            • 아래와 같은 방법으로 ECStack Count 를 셀 수 있다.

                function getStackCount(){
              
                    var i = 0;
              
                    try{
                        (function _stack(){
              
                            ++i && arguments.callee();
                        })();
                    }
                    catch(e){
              
                        console.log(e.message);
              
                        return i;
                    }
                }
              
                // get call stack count
                console.log('call stack count: ' + getStackCount());
              
                /* output
              
                 Maximum call stack size exceeded
              
                 // 15,745 번 함수 호출 후 Stack overflow 가 발생했다.
                 call stack count: 15745
              
                 */
              
      • Heap 영역(동적 할당)이란?

        • Heap 영역은 런타임 시점에서 크기가 결정되는 요소들이 저장되는 메모리 영역이다.

          • Object 또는 Array Data Type 등을 가진다.
        • Heap 영역은 런타임 시점에서 메모리를 **가변적**으로 **동적** 할당반환(GC를 통해) 시키는 구조를 가진다.

        • GC할당Heap 메모리 영역의 반환(de-allocated(수거))을 담당한다.

          • GC(Garbage Collection)란?

            • 자동 메모리 관리의 한 형태이며, 프로세스 수행 중, 더 이상 필요가 없어진 객체가 점유하는 메모리에 대해 반환 작업을 수행한다.

            • 프로그래머가 직접 메모리를 반환할 수는 없다.(반드시 GC를 통해 반환된다)

            • 단 GC에게 메모리 반환 조건을 만들어 줄 수는 있다.

              
                var obj = {x: 1, y: 2};
              
                // GC에게 메모리 반환 조건을 만들어 준다.
                obj = null;
              
              
      • JS Heap 메모리 영역 테스트 해보기!!

        • V8 Handle scope Diagram

        • 아래 코드는 Heap 메모리 영역에 객체할당하는 예제이다.

          // global execution context
        
          // 전역 실행 컨텍스트에서 함수 객체를 하나 선언한다.
        
          // 선언된 함수 객체는 Heap 메모리 영역에 할당된다.
          function globalA(){
              this.x = 1;
              this.y = 2;
          }
        
          function init(){
        
              // _global 전역 변수에 생성된 인스턴스를 할당한다.
              // 이때 전역 변수(참조 변수)는 Heap 메모리 영역에 할당된 객체를 참조하게된다.
        
              _global = new globalA();
          }
        
          init();
        
        • 전역 실행 컨텍스트에서 선언된 globalA 함수 객체의 Heap 메모리 영역.

        • _global 전역 변수가 참조하는 객체Heap 메모리 영역.

        • Chrome Inspector를 통해 할당된 Heap 메모리 영역을 살펴볼 수 있다.

        • 만약 함수 지역 변수객체를 할당했다면, 그 객체(Heap 영역)의 메모리 반환 시점은 언제일까?

          • 결론: 지역 변수에 할당된 객체 메모리 반환 시점은 **함수 종료 시점**이 된다.

            • 테스트 1: 전역 변수와 함수 지역 변수에 동일한 객체를 할당해본 후 Profile 을 통해 Heap 영역을 관찰해본다.

                // global execution context
              
                // 전역 실행 컨텍스트에서 함수 객체를 하나 선언한다.
              
                // 선언된 함수 객체는 Heap 메모리 영역에 할당된다.
                function globalA(){
                    this.x = 1;
                    this.y = 2;
                }
              
                function init(){
              
                    // _district 지역 변수에 생성된 인스턴스를 할당한다.
                    // 이때 지역 변수(참조 변수)는 Heap 메모리 영역에 할당된 객체를 참조하게된다.
              
                    // 단 지역 변수에 할당된 객체의 "메모리 반환 시점"은, 함수 종료 시점(함수 실행 컨텍스트 종료 시점)이 된다.
              
                    var _district = new globalA();
                }
              
                init();
              
            • 지역 변수에 할당된 객체Heap 메모리 영역에서 반환되어 보이지 않는다.

    3. JS 로 Struct 구현해보기

    • 위에서 나열한 구조체특징클래스본질적으로 다른 부분인, **데이터 생성**에 대한 부분을 JS 를 통해 구현해 보았다.

      • 구조체: 타입(Stack 메모리 영역에 할당되며, 값이 복사되어 할당된다)

      • 클래스: 참조 타입(Heap 메모리 영역에 할당되며, 참조 값이 할당된다)

    • 구조체의 특징 중 극히 일부를 구현한것이며, 타 언어에서 구현된 구조체라 할 수는 없다.

    
    function Struct(val){
    
    	if (
    		typeof val === 'string' ||
    		typeof val === 'number' ||
    		typeof val === 'boolean' ||
    		val === null ||
    		val === undefined
    	){
    		console.error('primitive type value: ' + val);
    		return val;
    	}
    
    	var _copy = function(val){
    
    		var ret = null;
    		var constructor = val.constructor;
    
    		if (constructor === Function){
    
    			// 함수(상황에 따라 생성자 함수가 될 수도 있다) 객체 내부 this 값을 apply 함수를 통해 초기화 시키고, 그 결과를 반환하는 함수를 생성한다.
    			ret = function $F(){
    
    				// this === global or $F of instance object
    				return val.apply(this, arguments);
    			};
    
    			// 전달된 함수 객체의 prototype(객체 원형)을 할당한다.
    			ret.prototype = val.prototype;
    		}
    		else if (constructor === String || constructor === Number || constructor === Boolean) {
    			ret = new constructor(val.valueOf());
    		}
    		else if (constructor === Date){
    			ret = new constructor(val.getTime());
    		}
    		else if (constructor === Object || constructor === Array){
    
    			ret = new constructor();
    
    			for (var n in val){
    
    				if (val.hasOwnProperty(n)){
    					ret[n] = val[n];
    				}
    			}
    		}
    
    		return ret;
    
    	}(val) || val;
    
    
    	return _copy;
    }
    
    	// 5 가지의 원시 타입 값 할당
    Struct('test'); // test
    Struct(1); // 1
    Struct(true); // true
    Struct(undefined); // undefined
    Struct(null); // null
    
    /*
    	모든 경우(모든 타입)에 전달된 원본 값(객체)이 아닌 복사 된 값을 반환한다.
    */
    
    /*
     함수 객체 선언
     */
    var $$ = function() {
    
    	var $$ = function(){
    		return new _$$();
    	};
    
    	var _$$ = function(){
    		this.x = 1;
    	}
    	// 나를통해 생성될 객체 원형에 새로운 객체를 할당한다.
    	_$$.prototype = {
    		getX: function() {
    			return this.x;
    		}
    	};
    
    	$$.staticMethod = function(){};
    
    	return $$;
    }();
    
    /*
     다른 유형의 함수 객체 선언
     */
    function Plus(x, y){
    
    	x = x || 0;
    	y = y || 0;
    
    	return parseFloat(x + y);
    }
    
    var originalFn = $$;
    var structFn = Struct($$);
    
    console.dir(originalFn); // function
    console.dir(structFn); // function
    
    console.dir(originalFn()); // object
    console.dir(structFn()); // object
    
    var originalFn1 = Plus;
    var structFn1 = Struct(Plus);
    
    console.dir(originalFn1); // function
    console.dir(structFn1); // function
    
    console.dir(originalFn1(1, 3)); // 4
    console.dir(structFn1(2, 3)); // 5
    
    var structStr = Struct(new String('test'));
    var structNum = Struct(new Number(1));
    
    console.dir(structStr); // string object
    console.dir(structNum); // number object
    
    var structObject = Struct({x: 1, y: 2});
    var structArray = Struct([1, 2, 3]);
    
    console.dir(structObject); // object
    console.dir(structArray); // array
    
    var structBoolean = Struct(new Boolean(false));
    console.dir(structBoolean); // boolean object
    
    
    var structDate = Struct(new Date());
    console.dir(structDate); // date object
    

    4. 정리하며

    • 그럼 언제 사용하면될까?

      • 개인적인 생각으로는 이럴때가 아닌가 싶다?

        • 클래스로 만들기에는 너무 간단한 구조인 경우.

          • 사실 이 경우, 반드시 구조 만의 문제는 아닐 수 있다.

            • 만약 모든 경우에 클래스를 사용한다면, 위에서 언급한 봐와같이 객체Heap 영역에 할당될 것이며, 결국 GC 는 그 메모리 반환을 위해 쓸때 없는 리소스를 낭비하게 될 것이다.
        • 상속 구조를 만들 필요가 없는 경우.

      • 하지만, 이 두 가지 특징 모두 각 언어(C/C++/C# 등 구조체를 가진 모든 언어)가 가지는 특성에 따라 충분히 변할 수 있는 부분이므로 모든 언어에 적용된다고 말할 수는 없다.

        • 간단히 말해, 특정 언어구조체를 어떤 방식으로 구현하느냐에 따라, 사용 범위도 크게 달라질 수 있다는 말과 같다.

    5. 참고 URL

    Read more


  • designPattern 

    1. 정의

    • 가장 큰 범위의 인터페이스 정의는 사물 간 또는 사물과 인간 간(User Interface)의 의사소통을 위한 중간 매개체다. 즉 서로간의 의사소통을 위해 정해진 약속이 존재하며, 이를 표준 규격 이라 말할 수 있는것이다.

    2. 의미

    • 인터페이스(DB Interface):

      • 구현 클래스가 공통적으로 가져야할 필수 특성(connect, disconnect 등)들을 선언함으로써, 해당 구현 클래스들에게 사용자(User Class)와의 의사 소통 방침(표준 규격)을 알리는데 목적이 있다.

        • 인터페이스 내부에 선언된 모든 특성은 구현 클래스를 통해 반드시 구현되어야 한다.
    • 구현 클래스(MSSQL, ORACLE API Class 등):

      • 사용자(User Class)와의 의사소통을 위해 인터페이스 내부에 선언된 모든 특성들을 충실히 구현한다.

        • 이때 사용자 입장에서는 특정 구현 클래스 명세 없이도, 인터페이스 명세에 따라 제공받은 모든 API 를 사용할 수 있다는 장점이 존재한다.

          • 정확히 말하자면, 특정 API 의 모든 기능이 아닌 인터페이스를 구현한 기능에 한정된다.

    3. 상황에 따른 의미

    • 만약 운영중인 어플리케이션의 DB 플랫폼이 MSSQL 에서 PostgreSQL 로 변경된다고 가정해보자.(오랜 기간 운영중인 어플리케이션의 경우 이런저런 이유로 이와 같은 상황이 충분히 올 수 있다)

      이때 위 다이어그과 같은 인터페이스 설계가 되어있는 상태라면, PostgreSQL 규격에 따라, 구현 클래스만 작성하면 최소 비용으로 기능을 확장할 수 있을 것이다.

      • 사용자 입장에서보면, 인터페이스를 통해 구현된, 공통적인 기능 만큼은 기존 코드를 재사용할 수 있다는 큰 장점까지 갖게 되는 것이다.(어플리케이션 유지보수 비용 최소화)

    4. JS 로 인터페이스 구현해보기

    인터페이스 구현은 아래 C# 인터페이스 가이드에 맞춰 구현되었으며, JS 코드에 대한 자세한 설명은 따로 하지 않겠다.(만약 JS 에 대해 어느정도 알고 있는 상황이라면, 코드를 분석해보길 권장한다)

    • 아래는 구현에 대한 간략한 설명이다.

      • 인터페이스 선언을 위한 인터페이스 함수 객체를 구현한다.

        • 인터페이스 [이벤트] 선언을 위해 Observer 기능을 구현한다.

        • C# 이벤트 선언

      • 인터페이스를 상속받은 구현 클래스를 검증(상속받은 인터페이스를 모두 구현했는지에 대한 검증)하는 함수 객체(Interface.ensureImplement)를 구현한다.

      • 위 다이어그램과 같이 인터페이스 및 각 클래스(구현 클래스)들을 구현한다.

    • C# 인터페이스(인터페이스 정의는 각 언어마다 조금씩 차이를 보인다(모든 언어에 동일하게 구현되어있지 않다))

    5. 정리하며

    • 인터페이스는 개발 중인 어플케이션 규모 및 설계에 따라 적용 범위가 얼마든지 달라질 수 있다.(또는 사용이 불필요할 수 도 있다(간혹 팀내에서의 관행적인 적용이 있다는 말을 들은적이 있는데 이건 정말 아닌듯 하다;;;))

      • 너무 당연한 말일 수 도 있지만, 인터페이스와 같은 모든 디자인 패턴은 적재 적소에 활용해야만 득이되는 것이다.
    • 위에서 언급한 내용과 다른 적용 사례에 대해(인터페이스를 구현하는 상황이 또 모가 있을까?) 한번쯤 심도있게 생각해보길 바란다

    6. 참고 URL

    Read more