2015.12.28 23:54

※ NoSQL 이란? - 'No! SQL' 이 아니라, 【Not Only SQL】의 약어이다.


  1. 관계형 데이터베이스의 장점과 단점

    * 장점
        - 데이터의 일관성을 보장한다. (transaction, atomicity)
        - 정규화를 전제로 하고 있기 때문에 데이터의 중복 가능성이 상대적으로 적다.
        - SQL 언어를 이용하고, JOIN, LIKE 등 다양한 검색 조건을 이용해 원하는 데이터를 손쉽게 찾을 수 있다.
        - 축적된 경험과 수십년간 검증된 솔루션(제품, out of box)들이 있다.
        - 상대적으로 설치,사용 및 관리가 용이하다.

    * 단점
        - 단시간에 대량의 데이터 입력(수십 GB byte 이상)이 발생할 경우, 느리다. (상대적으로 그렇다)
        - 데이터 구조 혹은 스키마(schema)이 일정(혹은 정적)하고, 변동이 많을 경우, 유지관리가 어렵다.
        - 누적된 데이터 크기가 커질수록 수행 성능이 급격히 낮아진다.
        - 분산, 대용량 시스템을 구축하기 불가능한 것은 아니나, 상대적으로 어렵다.

    1. NoSQL과 RDBMS의 관계

      - NoSQL은 관계형 데이터베이스(RDBMS)를 대체하기 위한 것이 아니다.
        (관계형 데이터베이스가 엄격/정밀하다면, NoSQL은 유연하지만 데이터 구조가 정교하지 않다.)
      - 관계형 데이터베이스는 데이터의 일관성 및 신뢰성이 매우 중요한 비즈니스(금융, 전자상거래 등)에서는 필수적이나,
        대량의 데이터를 고속으로 처리하거나, 비정형 데이터를 처리하는데 적합하지 않다.
      - NoSQL은 대규모/분산 환경에 보다 적합한 형태로 설계되어 있다.
      - 신뢰성을 (약간이나마) 희생할 수 있다면, NoSQL은 보다 적은 하드웨어 비용으로 고성능/고가용성을 달성할 수 있다.
      - RDBMS는 SQL, 정규화 등 표준화된 언어와 기술 표준이 있으나, NoSQL은 제품(오픈 소스 등) 종류가 다양하고,
        각각의 기능/성능/특징이 매우 다양하기 때문에 용도에 맞는 솔루션을 선택하기가 까다롭다.
      - NoSQL은 발전(버전 업) 속도가 빠른 반면, 시장을 주도하는 솔루션 사업자가 명확하지 않다. (Do/Try It Yourself!)

      ▶ NoSQL과 RDBMS는 경쟁 관계가 아니라, 고유의 적합/적용 가능한 데이터/처리 영역이 있다. (일부 겹치는 부분도 있음) ◀

    2. NoSQL을 고려해야 하는 상황

      - 대량의 데이터를 수집/처리해야 할 경우
      - 데이터 구조 혹은 스키마가 일정하지 않을 것이라고 예상되는 경우
      - 보다 적은 비용으로 대용량 / 분산 처리를 하고 싶은 경우
      - 빠른 데이터 접근 속도를 보장 받고 싶은 경우.
      - 데이터의 무결성, 일관성을 최우선 조건으로 고려하지 않아도 되는 상황
      - 그리고, 가장 중요한 것은 직접 설치 및 검증을 수행할 수 있고 다양한 문제에 부딧치는 상황에 외부의 도움(기술지원 등) 없이 스스로 해결할 수 있는 역량과 의지를 가진 경우.

    3. NoSQL 분류[각주:1]

    휘발성 key-value
    스토어

    영속성 key-value 
    스토어

    문서형 데이터

    컬럼형 데이터

     memcached

    Flare 

    MongoDB

    Casandra 

     (Redis)

    (Redis) 

    CouchDB 

    HBase 

     

     

     

    HyperTable



    1. 빅데이터 시대를 위한 NoSQL 핵심 가이드 (로드북 출판사, Tatsuya Sasaki 저, 송은영 편역) 참조 [본문으로]
    Posted by 善 곽중선
    2015.05.09 13:43

    JBUG(JBoss User Group) 스터디 모임에서 "기업 통합 패턴 Enterprise Integration Patterns"을 번역하신 차정호 님의 강의를 듣는 귀한 기회를 얻었다.


     


    강의 초반에 '알고리즘의 시대에서 패턴의 시대'로 변모하는 것이 아닌가 라는 말씀을 하신다. 소프트웨어 개발에 있어서 알고리즘(algorithm)와 패턴(pattern)은 모두 중요하다고 생각한다. 어느 한 쪽이 더 중요하다 하기도 어렵고, 두 가지 기술의 적용 범위 혹은 특징이 상이하지 않은가 라는 생각에 강의를 들으면서 짧은 노트를 적어 봤다. 거듭 말하지만 두 가지 기술은 반대되는 개념은 아니다, 다만 몇 가지 면에서 차이를 비교해볼 수 있다.


    1.문제 풀이를 위한 공식(formula)인가? 아니면, 문제를 풀기 위한 행동 방식인가?


    "알고리즘"은 개별적인 '문제(사례)를 해결하기 위한 논리와 기법'이다. 즉, 알고리즘은 문제 자체를 해결하는데 쓰인다. 알고리즘은 그 자체가 가치를 지닌다. 완성된 논리이며, 코드로 씌어졌을 때 즉각적으로 유용하다. "패턴"은 빈번하거나, 다양한 문제들을 해결하는 과정에서 발견된 문제 풀이에 도움을 주는'유용한 사례들'이다. 패턴은 소프트웨어 개발자 간의 의사 소통을 돕고, 문제 해결을 위한 실마리가 된다. 패턴은 문제 자체를 해결해주지는 않는다. 패턴을 현실 세계에 비유하자면 레고 블록과 같다. 개별 레고 블럭(패턴)은 가치가 없다. 그것들을 연결했을 가치가 생겨난다.


    2. 상대적으로 좁거나 반대로 넓은 적용 범위


    대다수의 "알고리즘"은 특정 문제를 해결하기 위해 고안된 것이다. 다양한 문제가 있다면, 그만큼 많은 알고리즘이 만들어진다. 하나의 문제에 대해서도 복수의 알고리즘이 있을 수 있다. 반면에 "패턴"의 적용 범위는 넓다. 특정 문제 자체를 위한 해결책이 아니라, 본래 다양한 사례를 조사하고 그 과정에서 반복적으로 쓰이는 기법을 문장의 숙어(idiom)처럼 골라낸 것이다. 요약하면, 알고리즘은 좁은(narrow) 범위를 해결하는데 사용되고, 패턴은 넓은(wide) 범위의 문제들을 해결할 때 유용하다. (어느 한 쪽이 우월하다고 해석해서는 안된다.)


    3. 관습적이거나, 개성적이거나...


    "알고리즘"이나 "패턴"이나 모두 문제 해결을 위한 도구이다. 그러나, "패턴"은 보다 관습적이다. 누군가 패턴을 제안하거나, 발견할 수 있지만 그것이 과연 유용한 것인지는 많은 개발자들의 선호도(?) 라거나, 지지 여부에 따라 가치가 달라진다. 반면에 알고리즘은 알고리즘을 고안한 사람의 개성을 뚜렷히 드러낸다. 또한 그 성능과 가치는 (대게) 객관적으로 조사 가능하다. 각각의 패턴을 속도라거나, 자원 사용량 등으로 비교할 수는 없는 노릇이다.


    4. 논리적이거나, 직관적이거나..


    알고리즘은 수학적으로 증명 가능해야 한다. 주어진 입력에 대해 정확한 출력 값을 내놓는다는 증거(혹은 테스트)를 거친다. 하지만, 패턴은 나름 인간적이다. 증명을 거치지 않는다, 오히려 아름다운지 여부를 판단한다. 알고리즘 또한 아름다울 수 있다. 수학적 혹은 코드의 아름다움은 정교할 뿐만 아니라 간결한 형태를 통해 완벽함, 경이로움을 느끼는 것이리라. 하지만, 패턴에서 느끼는 아름다움은 정교함 보다는 단순함, 그리고 완벽함 보다는 복잡한 세상에서 찾아낸 보편성에 대한 감탄이라고 표현할 수 있다.

    Posted by 善 곽중선
    2015.04.20 23:57

    자바 필기 및 실기 문제

    다음 내용은 위에 나온 문제 중에서 필기 6번 문제와 해설입니다.


    [Q6] 아래 프로그램의 출력 결과를 적으시오.

     

    public class SetValues {

       

        public static void main(String[] argv) {

     

            String stringObj = "Hello";

            int intValue = 0;

            Float floatObj = new Float(1.0);

     

            setValues(stringObj, intValue, floatObj);

     

            System.out.println( stringObj + ", "

                           + intValue + ", " + floatObj );

     

        }

     

        private static void setValues(String strValue, int intValue, Float floatObj) {

            strValue.replace("H", "h");

            strValue += " World";

            intValue = 99;

            floatObj.valueOf((float) 2.0);

        }

    }

     

    위 문제는 call by value, call by reference의 차이를 식별할 줄 아는지를 묻는 것이라고 착각을 유도하는 문제이다. 시험 응시자가 자칫 핵심을 잘못 간파해서 엉뚱한데 집중하지는 않는지 여부와 자바에서 제공하는 기본 메소드들의 동작 방식을 제대로 이해했는가 여부를 파악하고자 출제된 것이다.


    아래 주석을 통해 객체가 어떻게 메소드 간에 전달되고, 어떻게 변화하는지를 설명하였다.
    (
    객체의 ID는 임의로 부여한 것이며, 실제 실행 중에는 달라질 수 있다.)

     

    public class SetValues {

       

        public static void main(String[] argv) {

     

            // stringObj (object id = 15)

            String stringObj = "Hello";

            // intValue (, 객체 아님)

            int intValue = 0;

            // floatObj (object id = 16)

            Float floatObj = new Float(1.0);

     

            setValues(stringObj, intValue, floatObj);

     

            System.out.println( stringObj + ", "

                           + intValue + ", " + floatObj );

         }

      

        // strValue (object id = 15), call by reference 방식으로 전달

        // intValue (, 객체 아님), call by value 방식으로 전달

        // floatObj (object id = 16), call by reference 방식으로 전달

        private static void setValues(String strValue, int intValue, Float floatObj) {

     

            // replace 메소드는 새로운 String 객체 (object id = 17) 만들어 반환하지만,

            // 변수에 반환 값이 할당되지 않으므로, 무시됨.

            strValue.replace("H", "h");

     

            // += (문자열 조합) 연산자를 실행하면, 새로운 객체(object id = 18) 생성되며,

            // strValue (지역변수) 참조하는 객체는 object id = 18 변경됨.

            strValue += " World";

           

            // intValue 변수는 기본형(primitive type)이므로, intValue 변수에 저장된 값이

            // 99 변경됨.

            intValue = 99;

     

            // Float 클래스의 valueOf() 메소드는 static 메소드이며,

            // 2.0 값을 지니는 새로운 Float 객체 (object id = 19) 생성해 반환함.

            // 반환된 객체는 어느 변수에도 할당되지 않기 때문에 무시됨.

            floatObj.valueOf((float) 2.0);

        }

    }

     

    결국 메인 함수로 복귀(return)하게 되면, setValues 함수 내에서 생성된 모든 객체는 무시되고, setValues 메소드 호출 이전 상태의 값들이 그대로 출력된다.

    Posted by 善 곽중선
    2015.04.18 16:02

    Original Link : The Magic of Strace (by Chad Fowler)


    Thanks to Chad Fowler! this document is korean translated version of "Chad Fowler's great writing"

    If you read this and got some insight, visit and read more posts at http://chadfowler.com/


    원 저자이신, 채드 파울러(Chad Fowler)님의 허락을 얻어 번역한 글입니다.


    내가 IT 경력을 쌓은지 얼마되지 않았을 때, 동료와 나는 며칠에 걸쳐 로터스 도미노 서버(Lotus Domino Server) 장애와 씨름하고 있는 팀을 지원하기 위해 멤피스(Memphis)에서 올란도(Orlando)까지 비행기를 타고 가야 했다. 올란도에 있는 팀은 장애를 해결하지 못하고 며칠째 아무 곳에도 가지 못하며 옴쭉달짝을 못하고 있는 상황이었다. 나는 그들이 나와 동료가 도움을 될거라 생각한다는 사실을 믿기 어려웠다. 우리는 로터스 도미노에 대해서는 아무 것도 모르는 상태였기 때문이다. 하지만, 나는 UNIX 를 열심히 공부했고 또 잘 다룰 줄 알았다. 아무래도 그들은 자포자기 상태였던 듯 하다.


    로터스 도미노는 소스가 공개되지 않는 블랙박스 형태의 "그룹웨어(groupware)" 서버였다. 그 때 문제가 정확히 무엇이었는지 기억 나지는 않지만, 데이터베이스의 파일들을 제대로 읽고 쓰지 못하는 문제를 해결했던 것 같고, 확실한 것은 로터스 기술팀의 고급(escalated) 기술지원으로도 문제를 해결하지 못했었다.


    그 경험은 내 경력을 거쳐 익힌 수많은 학습 중에 가장 깊이 있는 것 중에 하나였다. 내가 사용하는 다양한 유닉스 툴(tool) 중에서 지금까지도 가장 중요하게 여기는 strace.* 명령을 그 때 배웠다.


    근래에 들어서도 프로그래머이자 시스템 엔지니어로서 일하면서 거의 매일 strace 혹은 그와 유사한 명령들을 사용하고 있다. 이 글에서 왜 그리고 어떻게 strace 명령을 사용하는지 설명하고, 그리고 너무나도 훌륭하고 강력한 작은 도구를 활용하는 몇가지 팁(tip)을 보여주고자 한다.


    strace 는 무엇인가 (What is strace?)


    strace 는 특정 프로세스와 자식 프로세스의 시스템 호출(system call)과 프로세스에 전달되는 시그널(signal)을 추적할 수 있는 명령행 도구(command line tool)이다. strace 명령을 이용해 프로그램을 시작할 수도 있고, 실행 중인 프로그램의 시스템 호출을 추적하는데 사용할 수도 있다. 간단한 예제를 살펴보자. 다음과 같은 간단한 C 프로그램을 작성했다고 가정하자.


    [traceme.c]

    #include <stdio.h>
    void main() {
      printf("hi\n");
    }


    많은 기능을 담고 있지는 않다. 그냥 화면에 "Hi"라는 문자열을 프린트 할 뿐이다. 프로그램을 컴파일한 후에, strace 명령을 이용해 실행해보면 프로그램이 수행하는 시스템 호출을 모두 볼 수 있다.


    $ strace -s 2000 -f ./traceme

    execve("./g", ["./g"], [/* 25 vars */]) = 0

    brk(0)                                  = 0x1816000

    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

    mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2ffa263000

    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)

    open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

    fstat(3, {st_mode=S_IFREG|0644, st_size=35614, ...}) = 0

    mmap(NULL, 35614, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2ffa25a000

    close(3)                                = 0

    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

    open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

    ... (중략) ...

    write(1, "hi\n", 3hi

    )                     = 3

    exit_group(3)    


    프로그램을 실행하기 위해, strace 명령과 프로그램 명칭을 입력했다. (더불어, 프로그램 실행에 필요한 인자를 추가할 수 있다.) 위 예시에서는 '-s' 옵션(flag)를 추가해 strace가 출력하는 문자열의 최대 크기를 지정했다. 이렇게 하는 것이 함수들의 인자를 조회하는데 도움이 된다. 여기서는 임의로 2000을 지정했는데, 이 값은 우리가 실행하는 프로그램에서 보고자 하는 정보를 얻는데 충분하다. 기본값은 32인데, 내 경험으로는 프로그램을 추적하는 과정에서 필요한 정보를 캐내기에는 절대적으로 부족하다. 또한, '-f' 옵션을 지정했으며, 이는 strace로 하여금 우리가 실행한 프로세스의 자식 프로세스도 추적하라고 지시하는 것이다. 위 예제에서는 자식 프로세스가 없지만, strace를 사용할 때는 일반적으로 프로세스의 자식까지 추적하도록 설정하는 습관을 가지는 것이 프로그램의 실행 중에 벌어지는 모든 것을 알 수 있기에 권장한다.


    명령을 실행하고 나면, 좀 장황한 시스템 호출 기록들을 모두 보게 된다. 일부(거의 모두?)는 마치 횡설수설하는 것처럼 보인다. 들여다 봐도 대부분 이해할 수는 없겠지만, 그리 오래지 않아 쓸만한 정보들을 찾아낼 수 있을 것이다.


    추적(trace)의 첫번째 라인은 execve() 호출이다. 놀라울 것도 없이, execve() 이 수행하는 것은 프로그램을 실행하는 것이다. 프로그램의 경로와 인자(argument)의 배열, 그리고 프로그램 설정을 위한 환경 변수 목록을 받아들인다. (너무 데이터가 많기 때문에 출력 결과에서는 생략된 형태로 보인다.)


    마지막 2 라인은 알아볼 수 있는 또 다른 정보를 제공한다. 먼저, write() 호출은 C 프로그램에 포함된 "hi\n" 문자열을 포함하고 있다. write() 의 첫번째 인자는 문자열이 출력되는 대상 파일 디스크립터(file descriptor)이다. 여기서는 '1'이며, 프로세스의 표준 출력 스트림(standard output stream)을 의미한다. write 호출 이후 (알아보기 약간 어려울 수 있는데 실제 화면 출력과 strace 명령의 출력이 섞여서 보이기 때문이다), 프로그램은 exit_gorup() 을 호출한다. 이 함수는 exit() 와 비슷하게 동작하나, 프로세스의 모든 스레드(thread)를 종료한다. 


    execve() 호출과 write() 호출 사이에 존재하는 모든 호출들은 대체 무엇인가? 메인 프로그램이 시작하기 전에 모든 표준 C 라이브러리들을 호출하는 작업들이다. 이런 호출들은 기본적으로 런타임(runtime)을 초기화 하는 작업이다. 눈여겨 들여다 보면 몇가지 파일들을 점검하고, 파일들에 접근 가능한지 확인한 후 파일을 연다. 그리고, 그것들을 메모리 위치에 매핑(mapping)한 후에 닫는다.


    한가지 중요한 힌트 : 이러한 함수들 모두 문서화 되어 있으며, man 페이지를 이용해 읽어볼 수 있다. 만일, mmap()이 무엇을 하는 것인지 모른다면, "man mmap" 명령을 실행해 보면 도움말(정의/사용법/옵션 등)을 찾아볼 수 있다.


    위에 예시한 strace 출력 결과에 나오는 모든 함수를 찾아보고 공부하기 바란다. 책보다 쉽게 배울 수 있다!


    현실에서 실행 중인 프로세스를 추적하기 (Tracing a running, real-world process)


    내가 strace 같은 툴을 필요로 하는 순간은 이미 실행 중인 프로세스가 정상적으로 동작하지 않을 때이다. 일반적인 상황에서는 이러한 프로세스가 초기화 시스템(init system)에 의해 구동되기 때문에 문제해결을 하기가 여간 힘든 것이 아니다. 따라서, 로그를 분석하거나 모니터링 도구를 외부에서 구해야만 한다.


    strace 는 이미 구동 중인 프로세스의 내부 동작을 손쉽게 들여다 볼 수 있게끔 해준다. 운영 중인 루비 유니콘 프로세스 (루비 웹 서버)에 문제가 있고, 프로세스의 로그를 들여다 봐서는 도저히 쓸만한 정보를 얻을 수 없는 상태라고 가정해보자. strace 의 '-p' 옵션을 사용해 프로세스에 연결할 수 있다. 운영 중인 서버의 가동율이 높아서 꽤 많은 출력이 발생할 것이라고 예상되면 '-o' 옵션을 사용해 출력 결과를 로그 파일에 저장할 수 있다.


    $ sudo strace -o /tmp/strace.out -s 2000 -fp 12152 Process 12152 attached with 4 threads - interrupt to quit ^CProcess 12152 detached Process 12155 detached Process 12160 detached Process 3146 detached


    (strace 를 이용해 실행 중인 프로세스를 추적하니) 단 몇 초 만에 로그 파일의 크기는 9,000 라인을 넘어 버렸다. 아래에 어쩌면 흥미로울지도 모르는 로그 일부를 캡쳐(capture) 해두었다.


    3419 select(16, [14 15], NULL, [7 8], {29, 0}) = 1 (in [14], left {28, 987147}) 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 122710542}) = 0 3419 fcntl(14, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK) 3419 accept4(14, 0, NULL, SOCK_CLOEXEC) = -1 EAGAIN (Resource temporarily unavailable) 3419 getppid() = 25937 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 123156611}) = 0 3419 clock_gettime(CLOCK_MONOTONIC, {92751, 940365496}) = 0 3419 select(16, [14 15], NULL, [7 8], {29, 0}) = 1 (in [14], left {28, 972271}) 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 152520833}) = 0 3419 fcntl(14, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK) 3419 accept4(14, 0, NULL, SOCK_CLOEXEC) = -1 EAGAIN (Resource temporarily unavailable) 3419 getppid() = 25937 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 152849344}) = 0 3419 clock_gettime(CLOCK_MONOTONIC, {92751, 970066349}) = 0 3419 select(16, [14 15], NULL, [7 8], {29, 0}) = 1 (in [14], left {28, 958562}) 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 195372960}) = 0 3419 fcntl(14, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK) 3419 accept4(14, 0, NULL, SOCK_CLOEXEC) = -1 EAGAIN (Resource temporarily unavailable) 3419 getppid() = 25937 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 195710712}) = 0 3419 clock_gettime(CLOCK_MONOTONIC, {92752, 12883582}) = 0 3419 select(16, [14 15], NULL, [7 8], {29, 0}) = 1 (in [14], left {28, 956099}) 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 240006588}) = 0 3419 fcntl(14, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK) 3419 accept4(14, 0, NULL, SOCK_CLOEXEC) = 13 3419 fstat(13, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0 3419 recvfrom(13, "GET /a/check HTTP/1.0\r\nX-Forwarded-For: 127.0.0.1\r\nHost: localhost\r\nX-Request-Start: t=1390981158239000\r\nConnection: close\r\nUser-Agent: curl/7.29.0\r\nAccept: */*\r\n\r\n", 16384, MSG_DONTWAIT, NULL, NULL) = 167 3419 ppoll([{fd=13, events=POLLOUT}], 1, NULL, NULL, 8) = 1 ([{fd=13, revents=POLLOUT}]) 3419 write(13, "HTTP", 4) = 4 3419 ppoll([{fd=13, events=POLLOUT}], 1, NULL, NULL, 8) = 1 ([{fd=13, revents=POLLOUT}]) 3419 write(13, "/1.1 ", 5) = 5 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 241130051}) = 0 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 241273452}) = 0 3419 stat("/opt/app/tmp/cache/CE5/510/http%3A%2F%2Flocalhost%2Fa%2Fcheckh%3F", 0x7fffcb874db0) = -1 ENOENT (No such file or directory) 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 241762672}) = 0 3419 clock_gettime(CLOCK_REALTIME, {1390981158, 242012184}) = 0 3419 sendto(16, "Q\0\0\0\rSELECT 1\0", 14, MSG_NOSIGNAL, NULL, 0) = 14 3419 poll([{fd=16, events=POLLIN|POLLERR}], 1, 4294967295) = 1 ([{fd=16, revents=POLLIN}]) 3419 recvfrom(16, "T\0\0\0!\0\1?column?\0\0\0\0\0\0\0\0\0\0\27\0\4\377\377\377\377\0\0D\0\0\0\v\0\1\0\0\0\0011C\0\0\0\rSELECT 1\0Z\0\0\0\5I", 65536, 0, NULL, NULL) = 66 3419 rt_sigprocmask(SIG_SETMASK, ~[HUP INT QUIT KILL USR1 SEGV USR2 TERM CHLD STOP TTIN TTOU VTALRM WINCH RTMIN RT_1], NULL, 8) = 0


    모든 라인을 분석할 수는 없지만, 일단 첫번째 라인을 확인해보면, select() 호출이다. 프로그램은 select()를 이용해 시스템에 위치한 파일 디스크립터(file descriptor)의 변화(활동)를 감시한다. select() man 페이지에 의하면, 두번째 인자는 읽기 처리를 감시하기 위한 파일 디스크립터 리스트이다. select() 는 설정 가능한 시간만큼 블록(block)되며 파일 디스크립터의 활동을 주시하고, 활동이 감지된 파일의 갯수를 반환한다.


    일단 나머지 인자들에 대해서는 무시하고, select() 호출이 14 및 15번 파일 디스크립터의 읽기 처리를 감시하고 (추적 결과의 오른편을 주시할 것), '1' 값을 반환한다. strace는 몇가지 시스템 호출에 대한 정보를 추가로 제공하기 때문에 우리는 가공되지 않은(raw) 결과 뿐만 아니라, 어떤 파일 디스크립터(14)가 변화했는지를 파악할 수 있다. 가끔은 시스템 호출에 대한 가공되지 않은 정보가 필요한 경우가 있으며, 이럴 때는 "-e raw=select" 옵션을 strace 명령에 추가하면 된다. 이렇게 하면 strace 가 select() 호출에 대한 가공되지 않은 형태의 실행 결과를 출력한다.


    그렇다면, 14 및 15 파일 디스크립터는 무엇인가? 무언지 알 수 없다면, 앞서 확인한 단서는 무용지물이다. lsof 명령을 이용해 어떤 파일인지 확인해 보자.


    COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ruby 3419 app DEL REG 0,4 26473410 /dev/zero ruby 3419 app 0r CHR 1,3 0t0 14 /dev/null ruby 3419 app 1w REG 202,1 18 159903 /opt/app/log/unicorn-out.log ruby 3419 app 2w REG 202,1 621 159902 /opt/app/log/unicorn-err.log ruby 3419 app 3r FIFO 0,8 0t0 36967385 pipe ruby 3419 app 4w FIFO 0,8 0t0 36967385 pipe ruby 3419 app 5r CHR 1,3 0t0 14 /dev/null ruby 3419 app 6w CHR 1,3 0t0 14 /dev/null ruby 3419 app 7r FIFO 0,8 0t0 36967386 pipe ruby 3419 app 8w FIFO 0,8 0t0 36967386 pipe ruby 3419 app 9w FIFO 0,8 0t0 26472351 pipe ruby 3419 app 10u IPv4 36967388 0t0 TCP some-internal-host:60490->some-internal-host:6379 (ESTABLISHED) ruby 3419 app 11u unix 0xffff8800e8589a00 0t0 26473540 socket ruby 3419 app 12u IPv4 36967390 0t0 TCP some-internal-host:53691->some-internal-host:6379 (ESTABLISHED) ruby 3419 app 14u unix 0xffff8800e80a23c0 0t0 26473408 /opt/app/sockets/unicorn.socket ruby 3419 app 15u IPv4 26473409 0t0 TCP *:http-alt (LISTEN) ruby 3419 app 16u IPv4 36967393 0t0 TCP some-internal-host:49929->some-internal-host:postgresql (ESTABLISHED) ruby 3419 app 17u IPv4 36968294 0t0 UDP *:42720 ruby 3419 app 18u IPv4 36969521 0t0 TCP some-internal-host:43407->some-internal-host:mysql (ESTABLISHED) ruby 3419 app 19u IPv4 36969774 0t0 TCP some-internal-host:53698->some-internal-host:6379 (ESTABLISHED) ruby 3419 app 21u IPv4 36969842 0t0 TCP some-internal-host:33576->some-internal-host:mysql (ESTABLISHED) ruby 3419 app 22u IPv4 36969020 0t0 TCP some-internal-host:52827->some-internal-host:postgresql (ESTABLISHED) ruby 3419 app 23u IPv4 36969112 0t0 TCP some-internal-host:33209->178.236.7.109:https (ESTABLISHED) ruby 3419 app 24u IPv4 36969137 0t0 TCP some-internal-host:35439->some-internal-host:mysql (ESTABLISHED) ruby 3419 app 25u IPv4 36969148 0t0 TCP some-internal-host:49847->some-internal-host:mysql (ESTABLISHED) ruby 3419 app 26u IPv4 36970664 0t0 TCP some-internal-host:35217->some-internal-host:mysql (ESTABLISHED) ruby 3419 app 27u IPv4 54296608 0t0 TCP some-internal-host:48903->some-external-host.com:http (ESTABLISHED)


    "FD"라는 타이틀로 표시된 lsof 출력의 4번째 컬럼을 보면, 프로그램이 사용하는 파일 디스크립터라는 것을 알 수 있다. 그렇다! 14, 15 는 유니콘(Unicorn) 웹 서버가 listen 하고 있는 TCP 및 UNIX 소켓이다. 프로세스가 select() 를 이용해서 포트를 주시하고 있다는 말이 된다. 그리고, UNIX 소켓 (파일 디스크립터 14)를 통해서 통신이 발생하고 있다는 것 또한 알 수 있다.


    다음으로, 우리는 몇 개의 시스템 호출을 통해 UNIX 소켓을 통해 수신되는 통신을 받아들이려 시도하고 있고, EAGAIN이 반환되고 있다는 것을 확인할 수 있다. 이것은 멀티 프로세싱 서버(multi processing server)에서 정상적인 동작 방식이다. 프로세스는 수신되는 데이터를 대기(감시)하고, 읽기를 반복한다.


    결과적으로 accept4() 호출 이후에 파일 디스크립터(13)가 오류 없이 반환된다. 이제 요청(request)을 처리할 시간이다. 먼저 프로세스는 파일 디스크립터의 정보를 fstat() 호출을 이용해 확인한다. fstat()의 두번째 인자는 'stat' 구조체이며, fstat() 호출이 파일 상태 정보를 채워준다. 모드(S_IFSOCK)와 크기(일반적인 파일이 아니므로 0으로 보인다)를 확인할 수 있다. 짐작컨데 소켓 파일 디스크립터일 것이고, 프로세스는 recvform() 호출을 이용해 데이터를 읽어낸다.


    흥미로운 점에 대해 생각해보기 (Here’s where things get interesting)


    fstat() 처럼 recvform()의 첫번째 인자는 데이터를 읽기 위한 파일 디스크립터이며, 두번째 인자는 읽어들인 데이터를 담을 버퍼이다. 이것이 프로그램을 디버깅할 때 진정 흥미로운 포인트다 : 웹 서버 프로세스로 전송된 HTTP 요청 데이터 전체를 볼 수 있다! 가독성을 높인 다음 예시를 참고하기 바란다. :


    GET /a/check HTTP/1.0 X-Forwarded-For: 127.0.0.1 Host: localhost X-Request-Start: t=1390981158239000 Connection: close User-Agent: curl/7.29.0 Accept: */*


    충분히 제어할 수 없는 프로세스에 대한 문제해결(troubleshooting)을 시도할 경우, 위와 같은 정보는 아주 큰 도움이 된다. recvfrom() 호출의 응답 데이터는 수신한 데이터의 바이트 수이다 (167). 이제 응답(response)할 시간이다.


    프로세스는 먼저 ppoll을 이용해 시스템 측에 소켓에 쓸 수 있는 시점이 언제인지를 확인한다. ppoll()은 파일 디스크립터 목록을 받고, 이벤트를 설정한다. 여기서는 프로세스가 소켓이 블록 해지 되고(POLLOUT) 쓸 수 있는 시점에 통지(notified) 받기를 요청한다. 소켓에 쓸 수 있다고 통지되면, HTTP 응답 헤더를 write() 호출을 이용해 쓰기 시작한다.


    다음으로 유니콘 프로세스의 내부 라우팅(routing) 동작이 어떻게 동작하는지를 들여다 볼 수 있다. stat() 을 이용해 요청된 주소(URL 경로)를 위해 사용할 수 있는 물리적 파일이 파일 시스템 내에 있는지를 확인한다. stat()이 ENOENT를 반환하면, 해당 경로에 파일이 존재하지 않는다는 것을 의미한다. 파일이 존재하지 않는 것을 확인한 후 프로세스는 다음 단계의 작업을 수행한다. 일반적으로 정적인 파일 기반의 레일스 시스템 캐싱(caching on Rails systems)은 이와 같이 동작한다. 요청에 해당(대응)하는 정적 파일이 존재하는지를 확인하고, 없다면 코드를 실행한다.


    마지막으로 프로세스가 어떻게 동작하는지를 가볍게 흟어보자면, SQL 쿼리가 파일 디스크립터 16에 쓰였다는 것을 확인할 수 있다. lsof 출력 결과를 다시 보면, 파일 디스크립터 16은 또 다른 호스트의 postgresql 포트 (번호와 이름의 매핑은 /etc/services 파일에 설정되어 있음)로 연결된 TCP 연결이라는 것을 알 수 있다. sendto() 를 이용해 포맷된 SQL 쿼리를 postgresql 서버로 전송한다. 세번째 인자는 메시지의 길이이며, 네번째 인자는 플래그(flag) - 여기서는 MSG_NOSIGNAL - 인데 운영체제로 하여금 원격 연결 중에 오류가 발생하더라도 SIGPIPE 신호를 이용해 인터럽트(interrupt)하지 말 것을 요청하는 것이다.


    프로세스는 다음으로 poll() 함수를 이용해 읽기를 준비하거나, 소켓 오류를 처리하거나, 데이터를 읽을 수 있으면 recvfrom()를 이용해 postgresql 서버의 응답을 받아들인다.


    몇가지 세밀한 것들은 건너 뛰었지만, strace, lsof와 시스템 man 페이지를 조합해 프로그램의 보이지 않는 부분이 어떻게 동작하는지를 파악할 수 있다는 것을 이해했으리라 믿는다.


    정상이란? (What’s “normal”?)


    가끔은 프로세스의 개략적인(overview) 동작 방식만을 알고자 할 때도 있다. 90년대 후반에 "엔터프라이즈 자바"로 개발된 공급망관리시스템(supply chain management system) 제품의 장애를 해결할 때 이런 필요가 있었다. 잘 동작하다가도 특정 시간에 알 수 없는 상태에 빠지고 연결이 안되는 문제가 발생했었다. 우리는 소스 코드를 가지고 있지도 않았고, 기술지원 담당의 수준 낮은 답변에 지쳐 있었다. (결국 우리 스스로 모든 문제를 해결했다.) 그래서, 나는 "정상""적인 동작과 비정상적인 동작을 비교해보기로 결정했다.


    주기적으로 프로세스의 시스템 호출 기록을 샘플링(sampling)한 후에 정상적인 샘플과 시스템 장애가 발생했을 때의 샘플을 비교해 보았다. 그 당시의 정확한 결과를 기억하지는 못하지만, 이전에는 쓰지 않았던 트릭(trick)을 사용해서 해결해냈다. 최근까지도 항상 strace를 동작시키는 스크립트를 작성하고, 결과를 기록하며, 집계를 생성하고는 하고 있으며, 문제를 해결하기 위해 strace 로그를 활용한다.


    이제 유니콘 프로세스를 다시 살펴보자.


    sudo strace -c -p 1346 Process 1346 attached - interrupt to quit ^CProcess 1346 detached % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 97.06 0.062667 29 2132 select 1.23 0.000791 0 7035 clock_gettime 1.19 0.000767 2 316 poll 0.42 0.000269 0 2116 2105 accept4 0.05 0.000030 0 137 write 0.04 0.000024 0 173 close 0.03 0.000017 0 2184 fcntl 0.00 0.000000 0 350 36 read 0.00 0.000000 0 79 open 0.00 0.000000 0 351 14 stat 0.00 0.000000 0 93 fstat 0.00 0.000000 0 79 mmap 0.00 0.000000 0 79 munmap 0.00 0.000000 0 3 rt_sigaction 0.00 0.000000 0 54 rt_sigprocmask 0.00 0.000000 0 156 ioctl 0.00 0.000000 0 2 alarm 0.00 0.000000 0 83 socket 0.00 0.000000 0 86 2 connect 0.00 0.000000 0 218 sendto 0.00 0.000000 0 195 10 recvfrom 0.00 0.000000 0 3 recvmsg 0.00 0.000000 0 11 shutdown 0.00 0.000000 0 1 bind 0.00 0.000000 0 5 getsockname 0.00 0.000000 0 1 getpeername 0.00 0.000000 0 1 getsockopt 0.00 0.000000 0 2 clone 0.00 0.000000 0 156 gettimeofday 0.00 0.000000 0 11 getrlimit 0.00 0.000000 0 2094 getppid 0.00 0.000000 0 12 futex 0.00 0.000000 0 196 ppoll ------ ----------- ----------- --------- --------- ---------------- 100.00 0.064565 18414 2167 total


    strace 명령에 '-c' 옵션을 추가해 시스템 호출 횟수를 출력하도록 했다. '-c' 옵션을 사용해서 strace를 실행할 경우, 일정 시간만큼 실행하도록 한 후에, 프로세스에 인터럽트를 보내서 (ctrl-c)  집계 결과를 출력해야 한다. 출력 결과는 따로 설명이 필요없을 만큼 깔끔하다.


    나는 지금 오전 7시에 글을 쓰고 있고, 시스템은 업무 시간 중에 가동된다. 따라서, 지금 추적하는 유니콘 프로세스는 거의 동작하지 않는 상태이다(inactive). 위에 출력된 추적 결과를 보았다면, 놀랄만한 데이터가 없다는 것을 알 수 있을 것이다. 유니콘은 대부분의 시간을 select() 호출에 사용한다. 우리는 이제 select() 를 이용해 들어오는 연결을 기다린다는 것을 안다. 그러니, 프로세스는 대부분의 시간을 클라이언트(브라우저)의 요청을 대기하는데 사용한다. 이건 말이 된다.


    더불어 accept4() 의 에러 반환 값이 상대적으로 많다는 것을 볼 수 있다. 위에서 본 예제처럼, accept4() 는 일상적으로 EAGAIN 오류를 받고, 다시 연결을 기다리기 위해 select() 호출을 실행한다.


    남은 함수 목록들은 C 시스템 호출들을 다시 공부하는데 도움이 될만한 좋은 예제이다. 할일 목록(to-do list)에 등록하고 하루에 하나씩 읽어보고 이해할 수 있도록 하는 것이 좋다. 이렇게 하면, 다음에는 유니콘 프로세스가 부하가 발생하거나 문제가 생겨서 분석할 때, 좀 더 수월하게 시스템 호출들을 분석할 수 있을 것이다.


    어디가 느린거지? (Finding out what’s slow)


    시스템의 성능 문제가 발생했을 때 - 시스템이 느려진 상태일 때 - strace를 어떻게 활용할 수 있는지에 대한 이야기로 마무리하고자 한다. 최근들어 업무에서 트롤 같은 문제(gremlin-like problems)가 간혹 발생한 적이 있어서 strace 를 이용해 원인을 추적한 적이 있는데, 분산 시스템 상에서 서로 관련이 없는 컴포넌트들의 성능이 저하되는 것으로 보이는데 이유를 알 수가 없었다. 데이터베이스 중에서 하나가 느려지고, 일부 웹 서비스의 성능이 저하되었다. 결국은 sudo 까지도 느려지고 말았다.


    그것이 최종적인 단서였다. 우리는 sudo 명령을 strace로 추적했고, sudo 가 수행하는 각각의 시스템 호출 시간을 측정했다. 결국은 로그 명령문이 성능 저하의 원인이라는 것을 밝혀낸 것이다!  보아하나 syslog 서버를 제대로 확인하지 않고 잘못 설정해 놓았고, 그 서버로 로그를 전송하는 모든 서버가 이해할 수 없을만큼 느려지고 만 것이다.


    간단한 예제를 이용해서 프로세스의 성능 문제를 추적해보도록 하자. 다음과 같은 C 소스로 작성된 프로그램을 떠올려보고, 왜 실행하는데 2초 이상의 시간이 소모 되는지를 밝혀내 보고자 한다.


    #include <stdio.h> #include <unistd.h> void main() { printf("Hallo there!\n"); sleep(2); printf("Goodbye\n"); }


    코드를 흟어보면, 눈에 띄는 명백한 문제는 없다 (헛!), 그러니 답을 찾기 위해 strace 를 실행할 것이다. '-t' 옵션을 추가해 추적을 하는 동안 각각의 시스템 호출이 수행되는데 걸리는 시간을 측정하도록 한다. 아래에 추적 결과를 보자:


    $ strace -T ./time_me execve("./time_me", ["./time_me"], [/* 25 vars */]) = 0 <0.000307> brk(0) = 0x75f000 <0.000022> access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000027> mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bb589a000 <0.000021> access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000021> open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 <0.000023> fstat(4, {st_mode=S_IFREG|0644, st_size=35614, ...}) = 0 <0.000019> mmap(NULL, 35614, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7f3bb5891000 <0.000021> close(4) = 0 <0.000017> access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000021> open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 4 <0.000025> read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\37\2\0\0\0\0\0"..., 832) = 832 <0.000020> fstat(4, {st_mode=S_IFREG|0755, st_size=1852120, ...}) = 0 <0.000019> mmap(NULL, 3966008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 4, 0) = 0x7f3bb52b1000 <0.000022> mprotect(0x7f3bb5470000, 2093056, PROT_NONE) = 0 <0.000027> mmap(0x7f3bb566f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 4, 0x1be000) = 0x7f3bb566f000 <0.000027> mmap(0x7f3bb5675000, 17464, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f3bb5675000 <0.000025> close(4) = 0 <0.000019> mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bb5890000 <0.000021> mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bb588e000 <0.000021> arch_prctl(ARCH_SET_FS, 0x7f3bb588e740) = 0 <0.000018> mprotect(0x7f3bb566f000, 16384, PROT_READ) = 0 <0.002229> mprotect(0x600000, 4096, PROT_READ) = 0 <0.000027> mprotect(0x7f3bb589c000, 4096, PROT_READ) = 0 <0.000026> munmap(0x7f3bb5891000, 35614) = 0 <0.000036> fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000018> mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bb5899000 <0.000021> write(1, "Hallo there!\n", 13Hallo there! ) = 13 <0.000035> rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 <0.000018> rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0 <0.000019> rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 <0.000018> nanosleep({2, 0}, 0x7fffaa25c770) = 0 <2.000153> write(1, "Goodbye\n", 8Goodbye ) = 8 <0.000070> exit_group(8) = ?


    보시다시피, strace 출력 결과의 모든 시스템 호출에 마지막 항목에 실행 시간(초 단위)이 포함되어 있다. 한줄 한줄 따라가 보면, 대부분의 호출 수행 시간이 매우 짧은데 마지막에서 2초 이상의 시간을 소모하는 호출을 찾을 수 있다! 미스터리는 풀렸다. 우리의 프로그램 코드 중에서 nanosleep() 함수를 호출하면서, 2초라는 인자를 넘기는 코드가 있는 것 같다.


    실제로 추적할 때는 방대한 양의 추적 데이터를 파일에 저장한 후에 마지막 컬럼을 기준으로 정렬한 후, 문제가 되는 함수 호출부터 순서대로 확인하는 방식이 좋다.


    남은 이야기 (There’s more!)


    strace는 놀랍도록 많은 기능을 제공하는 툴(tool)이다. 그중에서 자주 사용하는 몇몇 옵션들만을 언급했을 뿐이며, 더 많은 옵션들이 있다. strace man 페이지를 찾아 더 많은 기능들을 살펴 보기 바란다. 그중에서 '-e' 옵션은 정규표현식을 입력받아 추적 결과를 필터링(filtering)하는 식으로 제어할 수 있기 때문에 꼭 살펴봐야 한다.


    이 글을 끝까지 읽는 동안 언급된 모든 함수들의 의미를 이해하지 못했을 수도 있다. 그러나, 너무 걱정하지 말기 바란다. 나조차도 다 아는 것은 아니다. 대부분의 지식을 오동작하는 프로그램을 추적하면서 익히고, man 페이지를 읽으면서 공부했다. 만일 UNIX/LINUX 환경에서 개발하고 있다면, 개발하고 있는 프로그램을 strace로 추적해보기 바란다. 시스템 수준에 어떤 일이 벌어지는지를 학습해라. man 페이지를 읽어라. 탐험해라(Explore). 즐거울 뿐만 아니라, 이해하게 되고 이전보다 일도 잘하게 될 것이다.


    추가 : 로터스 도미노 서버의 운명은? (UPDATE: And the fate of Lotus Domino server?)


    듣자하니, 이 글이 도미노 서버를 설치하는 과정에서 발생한 드라마 같은 이야기로 알려지고 있는 것 같다. 트위터 같은 사이트에서 모든 사람들이 내게 "세상에나, 도미노 서버는 결국 어떻게 된거죠?" 라고 묻는다. 그러니, 이제 이야기의 끝자락을 마무리 하고자 한다.


    어떻게든 시스템 호출을 추적하고 난 후에 도미노 서버를 고치고야 말았다. 유감스럽게도 그 당시에 정확히 어떻게 고쳐냈는지 기억하지 못하지만, 기적적으로 고쳐냈고 그 작업에 관련된 모든 사람들이 '엄청나게(huge)' 놀라워 했다는 것만은 확실하다.


    나와 동료는 출장을 위해 빌렸던 빨간색  포트 무스탕 컨버터블에 타기 위해 주차장으로 갔고, 음악을 듣기 위해 사두었던 단 한장의 CD를 꽂고 볼륨을 높였다. (Guns & Roses’ Appetite for Destruction, 파괴하고픈 욕망) 차를 몰고 무사히 공항에 도착했지만, 우리는 CD를 잊고 차에 두고 내리고야 말았다. 무사히 아틀란타를 거쳐 멤피스에 도착한 후에야 CD를 놓고 왔다는 사실을 깨달아 버린 것이다.


    누군가 우리 다음에 그 차를 빌린 사람은 우리만큼 글램 메탈(glam metal, hair metal) 음악을  즐겼기를 바란다.


    • 상용 유닉스에서는 strace 명령이 아니라, truss 라는 명령을 사용해야 합니다.
    • poor man's profiler 라는 글도 읽어보시면 좋습니다.


    Posted by 善 곽중선
    2015.04.17 20:15

    예전에 모 대기업 의뢰를 받아 "중급 자바 개발자 실력 검증"을 위해 출제한 문제들입니다.


    필기 문제 20문항과 실기 문제 1문항으로 이루어져 있으며, 

    자바 문법 / 객체지향 이해도 / 자바 프로그램의 동작 원리 이해 등을 묻는 질문으로 구성했습니다.


    [MS 워드 형식]


    JAVA 필기문제.docx


    JAVA 필기문제_답안.docx


    JAVA 실기문제.docx


    AddressBook_exam.zip


    [PDF 형식]


    JAVA 필기문제.pdf


    JAVA 필기문제_답안.pdf


    JAVA 실기문제.pdf


    Posted by 善 곽중선
    2015.04.04 11:05

    "Gregor Hohpe의 횡성수설"이라는 글 모음 중 하나를 번역했습니다. 소프트웨어 설계에 대한 훌륭한 통찰(insight)를 일상생활을 통해 얻을 수 있다는 깨달음을 주는 훌륭한 글입니다. 데이터베이스 관련 기술을 이야기 하고 있지만, 동기/비동기 처리 방식을 왜 구분하고, 어떻게 써야 하는지에 이해하는데 도움이 됩니다.


    원문 사이트 : Starbucks Does Not Use Two-Phase Commit 


    스타벅스는 2단계 커밋을 사용하지 않는다.


    Hotto Cocoa o Kudasai (ホットココアください, Hot Cocoa, please,  핫 코코아 주세요.)


    방금 2주간의 일본 출장을 마치고 돌아왔습니다. 출장 중에서 접한 익숙한 광경 중 하나는 신주쿠와 록본기 주변의 말도 안될만큼 많은 스타벅스 커피숖들이었습니다. 주문한 "핫 코코아"를 기다리면서 스타벅스가 어떻게 음료 주문을 처리하는지를 가만히 생각해보기로 했습니다. 스타벅스는 대부분의 다른 기업들처럼 주문 처리량을 극대화하는데 매우 큰 관심을 가지고 있습니다. 많은 주문은 많은 수익을 의미하죠. 결과적으로 그들은 비동기 처리 방식(asynchronous processing)을 사용합니다. 당신이 주문을 하면, 점원은 커피 한잔의 주문을 입력하고 대기열(queue)에 추가합니다. 대기열 혹은 큐(queue)는 문자 그대로 에스프레소 기계의 상단에 늘어서 쌓여 있는 빈 커피 컵들을 의미합니다. 이 대기열은 점원과 바리스타를 분리시키고 바리스타(barista)가 잠시 동안 숨을 돌리거나 반대로 매우 바쁜 경우에도 점원이 주문을 계속 받을 수 있도록 해줍니다. 이런 방식은 카페가 바쁜 시간에 다수의 바리스타들을 "경쟁적인 처리자(Competing Consumer)" 시나리오대로 움직이게끔 합니다.


    상관 관계 (Correlation)


    비동기 일처리 방식의 잇점을 선택함으로써 스타벅스는 비동기 방식이 근본적으로 가지고 있는 문제를 다룰 수 밖에 없게 됩니다. 예를 들자면, 상관관계입니다. 음료 주문은 2가지 이유 때문에 반드시 주문한 순서대로 처리해야 할 필요가 없습니다. 첫째, 바리스타들이 서로 다른 도구를 이용해 음료를 만든다는 점입니다. (드립 커피용 도구와 에스프레소 머신 등등) 또, 혼합 음료(blended drink)는 드립(drip) 커피 보다는 만드는데 시간이 더 걸립니다. 둘째, 바리스타들은 처리 시간을 최적화한 일괄(batch) 작업을 통해 한번에 여러가지 음료들을 만들 수 있습니다. 결과적으로 스타벅스는 상관관계라는 문제를 안고 있는 것입니다. 음료는 주문 순서에 상관없이 만들어지겠지만, 그것을 주문한 고객에게 정확히 전달 되어야 합니다. 스타벅스는 이러한 문제를 메시징 아키텍쳐(messaging architectures)에서 사용하는 상관관계 식별자(Correlation Identifier) 패턴으로 해결하고 있습니다. 대다수의 미국 스타벅스 매장에서는 명확한 상관관계 식별자인 고객의 이름을 컵에 쓰고, 커피가 완성되면 컵에 적힌 고객의 이름을 부릅니다. 다른 나라들에서는 음료의 종류를 이용해 상관 관계를 식별합니다.


    예외 처리 (Exception Handling)


    비동기 메시지 시나리오(asynchronous messaging scenario)에서 예외 처리는 어려운 문제입니다. 현실 세계에서 예외(문제)를 처리하는 최적의 사례를 살펴보려면 스타벅스가 문제를 해결하는 방식을 관찰해 보면 됩니다. 만일 당신이 마치 지갑을 깜박 잊고 있었다면, 스타벅스의 점원들은 어떻게 대응할까요? 이미 음료를 만들었다면 치워 버릴 것이고, 아직 만들기 전이라면 주문 목록(대기열 혹은 큐)에서 제외할 것입니다. 만일, 당신이 주문한 음료를 마음에 들어하지 않거나 주문하지 않은 음료를 내놓았을 경우에는 음료를 다시 만들어 낼 것입니다. 만일 커피 머신이 고장나서 주문한 커피를 제공할 수 없다면, 환불해 줄 것입니다. 각각의 시나리오는 제각기 다른 상황이지만, 일반적인 오류 처리 전략(error handling strategy)에 대해 설명하고 있습니다.


    가격 인하 혹은 취소 (Write-off) - 다양한 오류 처리 전략 중에서 가장 단순한 것입니다 : 아무 것도 하지 않거나, 이미 벌어진 일을 취소하는 것입니다. 썩 좋아 보이지는 않지만 실전 비즈니스에서는 그럭저럭 수용할만한 선택입니다. 손실이 작을 경우, 제대로 일처리하기 위해 오류 정정 시스템을 구축하는 것이 오히려 더 많은 비용을 지출하는 것일 수도 있습니다. 예를 들자면, 제가 컨설팅을 수행했던 여러 ISP(Internet Service Provider)사업자들은 과금(billing) 및 프로비저닝(provisioning) 싸이클(cycle)에서 발생하는 에러를 이런 방식으로 처리했습니다. 결과적으로, 고객은 비용을 청구받지 않고 서비스를 누리게 되는 것입니다. 이런 방식을 도입할지라도 수익 상의 손실은 사업을 유지하는데 있어 지장이 없을 만큼 작았습니다. 주기적으로, "무료(free)" 계정을 찾아 정리하기 위한 "조정 보고서"를 작성하는 작업을 수행하고, "무임승차"한 고객들을 처분합니다.


    재시도 (Retry) - ("트랜잭션"이라고 부르는) 큰 작업 그룹을 실행하는 중에 오류가 발생했을 때, 우리는 기본적으로 두 가지를 선택할 수 있습니다. 이미 수행된 작업을 취소하는 것 혹은 실패한 작업들을 재시도하는 것입니다. 재시도가 현실적으로 성공할 가능성이 있다면 그럴싸한 선택입니다. 예를 들어 비즈니스 규칙을 위반한 것이라면, 재시도는 성공할 수 없을 것입니다. 그렇지만, 외부 시스템이 일시적으로 응답하지 않는 상황이라면 재시도는 성공할 가능성이 있습니다. 

    단, 특별한 경우는 멱등 수신기(Idempotent Receiver : 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 가진 경우)입니다. 이러한 경우는 성공한 수신 처리 이후에 반복되는 메시지는 모두 무시되기 때문에, 간단히 모든 작업을 다시 시도할 수 있습니다.


    보상 처리(Compensating Action) - 마지막 옵션은 일관성 있는 상태로 시스템을 돌려놓기 위해 이미 수행된 작업을 취소하는 것입니다. 통화(금융) 시스템을 다루는 경우 이러한 "보상 처리""는 잘 작동하는데, 인출된 돈을 "얼마든지" 재입금할 수 있기 때문입니다.


    이러한 전략들은 분리된 준비와 실행 절차에 의해 동작하는 "2단계 커밋(2 phase commit)"과는 다릅니다. 스타벅스의 사례에 접목해보면, "2단계 커밋"은 점원(cashier)이 커피가 완성될 때까지 한 고객만을 바라보며 영수증과 돈을 테이블 위에 올려놓고 기다리는 것과 같습니다. 마지막으로 음료가 준비되면, 영수증과 돈으로 일거에 교환하는 것입니다. 이런 상황에서는 점원뿐만 아니라 고객까지도 "트랜잭션(거래)"이 끝날 때 까지 자리를 떠날 수 없게 됩니다. 이러한 "2단계 커밋" 방식을 적용하게 되면 일정 시간에 서비스 할 수 있는 고객 수는 극적으로 감소할 것이고, 스타벅스의 비즈니스를 확실하게 망하게 만들 것입니다. 이것은 2단계 커밋이 일상을 좀 더 단순하게 만들 수는 있지만, 메시지들의 자유로운 흐름을 방해하게 된다(그리고 확장성을 해친다)는 사실을 일깨워 주는 사례입니다. 왜냐하면, 비동기의 다중 작업 흐름 속에서 트랜잭션의 상태를 관리해야 하기 때문입니다.


    대화 (Conversations)


    커피숖 상호작용 사례는 간단하면서 일반적으로 대화 패턴(Conversations pattern)입니다.


    두 그룹(고객과 커피숖)간의 상호작용은 짧은 동기 상호작용(주문과 지불)들과  하나의 긴 비동기 상호작용 (음료 만들기와 받기)로 구성됩니다. 이러한 대화(혹은 작동방식)는 구매 시나리오에는 매우 일반적인 것입니다.  예를 들어, 아마존(Amazon) 인터넷 쇼핑몰에서 주문할 경우, 주문 번호 발행과 그에 따른 처리 절차(신용카드 지불 요청, 포장, 발송) 등은 짧은 동기적 상호작용이지만, 전체 흐름은 비동기적으로 처리됩니다. 추가적인 단계들이 처리될 때마다 (비동기적으로) 이메일을 통해 통보 받을 수 있습니다. 만일 어떤 것이라도 잘못 처리되는 것이 있다면 아마존은 보상(신용카드 결제를 취소하는 등)하거나, 재처리(분실된 물품을 다시 배송하는 등)를 해줄 것입니다.

     

    요약하면 실제 세계에서 벌어지는 일들은 대게 비동기적으로 동작한다는 것을 알 수 있습니다. 우리의 일상생활은 조화롭지만 비동기적인 상호작용(이메일을 읽고 쓰는 것, 커피를 사는 것 등등)들로 이루어져 있습니다. 이것이 의미하는 바는 비동기적인 메시지 아키텍쳐가 이러한 상호작용을 수행하는데 있어서 가장 자연스러운 방식이라는 것입니다. 또한 우리는 일상적인 생활 속에서 성공적인 메시징 솔루션을 설계할 수 있는 아이디어를 얻게 된 것입니다. 도모 아리가또 고자이마스. (Domo arigato gozaimasu! 감사합니다.)


    참고 : 호토 코코아를 검색하면 , 일본 애니메이션 "호토 코코아"가 나오는 군요.

    Posted by 善 곽중선
    2015.03.31 03:48

    Introduction to the Domain Name System


    Domain Name System (DNS)는 지속적으로 증가하는 인터넷 사용자를 수용하기 위해 설계되었다. DNS는 www.google.com 같은 명칭을 59.18.44.50 같은 IP 주소로 변환하여 (혹은 보다 확장된 IPv6 주소로 변환), 컴퓨터들이 상호 통신할 수 있게끔 하고, 월드 와이드 웹 같은 인터넷 어플리케이션을 사용할 수 있도록 해준다. DNS 의 역할은 마치 친구에게 전화를 하고자 할 때, 친구의 전화번호를 일일이 외울 필요가 없이 친구의 이름만으로 통화를 할 수 있게 해주는 것과 같다.


     DNS는 어떻게 동작하는가?

    DNS를 이해하기 위해서 자신의 컴퓨터에 로그인하고 있는 사용자 홍길동을 상상해보자. 홍길동은 웹 브라우저를 실행해 근무하고 있는 회사인 Acme.co의 웹 사이트를 조회할 수 있다. 회사 홈페이지를 조회하기 위해서 웹 브라우저에 http://www.acme.com을 입력할 것이다.


    1. 홍길동의 컴퓨터는 DNS 서버에 www.acme.com 의 IP 주소를 알려달라는 요청을 전송할 것이다.
    2. DNS 서버는 www.acme.com 의 IP 주소인 192.168.1.4를 찾기 위해 위해 자체 데이터베이스를 확인할 것이다.
    3. 서버는 확인한 IP 주소를 홍길동의 브라우저에 반환한다.
    4. 브라우저는 IP 주소를 이용해 웹 사이트의 위치를 파악한다.
    5. 브라우저는 홍길동의 컴퓨터 모니터에 웹 사이트를 출력한다.



    그림 : 도메인 명칭과 주소 (Domain Names and Addresses)


     도메인 (Domains)

    홍길동은 DNS 서버가 www.acme.com의 IP 주소를 주소를 알고 있기 때문에 회사 홈페이지에 접근할 수 있다. DNS 서버는 도메인 네임스페이스(domain namespace)를 검색해 회사 홈페이지 서버의 주소를 알아낸다. DNS 는 각각 이름이 부여된 도메인(domain)이라는 노드(node)로 구성된 트리 구조(tree structure)로 설계되어 있다. 트리의 최상위 노드는 DNS 루트 도메인(DNS root domain)이다. 그 아래에 .com, .edu,, .gov, .mil 같은 하위 도메인(sub domain)들이 존재한다.




    그림 : 도메인 명칭 시스템 계층도 (The Domain Name System Hierarchy)


    정규화된 도메인 이름(Fully qualified domain name : FQDN) 은 모든 네트워크 도메인 명칭이 루트까지 점(.)으로 연결된 문자열이다. FQDN 은 인터넷에 존재하는 모든 호스트(host)들을 구분하는 고유한 명칭(unique name)이다. 예제 도메인의 FQDN은 sample.com 이며, 부모 도메인은 .com 이고, 루트 도메인은 "." (dot)이다.


     Acme.co 회사의 주소 학습


    홍길동의 서버가 www.acme.com 의 IP 주소를 요청하는 과정을 좀 더 자세하게 살펴보자.



    그림 : DNS 계층도 검색 (DNS Hierarchical Name Search)


    1. 로컬 DNS 서버는 www.acme.com 도메인이 자체 데이터베이스에 등록되어 있는지를 확인한다. 만일 찾을 수 없다면, 해당 도메인을 관리하고 있지 않다는 것을 알게 된다.
    2. 로컬 DNS 서버는 루트 네임서버(root nameserver)에 최상위 레벨의 도메인 "."(dot)에 대한 정보를 요청한다.
    3. 루트 네임서버는 .com 도메인 및 하위 도메인을 관리하는 .com 네임서버에 요청하도록 지시한다.
    4. .com 네임서버는 acme.com이 자신의 하위 도메인임을 확인하고, acme.com 도메인을 관리하는 네임서버의 위치를 알려준다.
    5. 로컬 서버는 acme.com 네임서버에 www.acme.com 서버의 위치를 요청한다.
    6. acme.com 네임서버가 IP 주소 192.168.1.4를 알려준다.
    7. 로컬 서버는 최종적으로 검색한 IP 주소를 홍길동의 웹 브라우저에 반환한다.


     도메인 등록 (Establishing a Domain)

    Acme.co 는 홍길동이 접속할 수 있는 웹사이트를 운영하고 있는데, 이는 해당 도메인이 공인된 도메인 관리 회사(업체)에 등록되어 있기 때문이다. 또한 .com 서버의 데이터베이스에 도메인 명칭을 등록하고, IP 주소 범위를 규정하는 네트워크 번호(network number)를 요청했다. acme.com이 부여받은 네트워크 번호는 192.168.1.0이며, 이는 192.168.1.1 부터 192.168.1.255 까지를 포함한다. IP 주소는 옥텟(octet) 이라고 부르는 4개의 숫자로 구성되어 있고 각 숫자(번호)는 0부터 256까지 사용할 수 있다. 그러나, 0과 256는 브로드캐스트(broadcast) 및 네트워크 자체를 위해 예약되어 있으므로 호스트의 주소를 지정하는데 사용할 수 없다. 


     도메인과 존(zone)의 차이 

    도메인 네임스페이스는 DNS 트리의 특정(일부) 영역을 나타내는 존(zone)으로 구분되어 있다. 존은 특정 지점의 하위에 존재하는 모든 도메인을 포함한다. 


    각 존은 일반적으로 해당 존을 관할하는 하나 이상의 네임서버를 가지고 있다. 각 기관(혹은 기업)은 복수의 네임서버를 소유(및 운영)할 수 있지만 인터넷 클라이언트들을 루트 네임서버가 알고 있는 (혹은 루트 네임서버)에 등록된 네임서버에만 주소 검색을 요청할 수 있다. 루트 네임서버에 등록되지 않은 서버들은 각 기관의 내부에서 자체적인 용도로 사용된다.


    Acme.co 회사는 acme.com 이라는 도메인을 등록했다. 또한 acme.com, marketing.acme.com 및 finance.acme.com이라는 3개의 존을 구성했다. Acme.co 는 마케팅 및 재무 부서에 marketing.acme.com 및 finance.acme.com DNS 서버에 대한 관리 권한을 위임하였다. acme.com에 속한 누군가가 marketing.acme.com 내에 존재하는 호스트의 주소를 요청하면, acme.com은 주소 요청을 marketing.acme.com으로 유도할 것이다. Figure 13-4 acme.com은 3개의 존으로 구성되어 있으며, acme.com 존은 자체에 대한 관리만을 수행한다.



    그림 :  Acme.com With Delegated Subdomains


    Acme.co 는 하위 도메인(subdomain)들에 대한 관리 권한을 위임하지 않을 수도 있다. 이런 경우, acme.com 도메인은 marketing 및 finance 을 관리하는 존이 된다. acme.com 서버는 마케팅 및 재무 영역에 대한 주소 요청에 대해서도 응답한다.


    그림 : Acme.com Without Delegation


    존을 설정할 경우, 각 존에 네임서버를 필히 설정해야 한다. 각 존 마다 로컬 설정 데이터베이스를 가지고 있는 하나의 기본 네임서버(primary server)가 있어야 한다. 각 존은 기본 네임서버로부터 복사한 데이터를 가지고 있는 여러 개의 보조(secondary) 서버를 가질 수 있다. 다음 그림은 하나의 보조 서버를 구성한 사례이다.




    그림 : Primary and Secondary Servers for Zones


     네임서버

    DNS는 클라이언트/서버 모델을 기초로 만들어졌다. 네임서버는 DNS 데이터베이스를 저장하고 있으며, 네트워크를 통해 IP 주소를 요청하는 클라이언트에 해당 정보를 제공한다. 네임서버는 물리적 서버에서 실행되는 프로그램이며, 존 데이터(zone data)를 저장한다. 도메인의 관리자는 하나 이상의 존에 위치한 호스트들에 대한 정보를 표현하는 Resource Records (RR) 데이터베이스를 네임서버에 구성(입력)한다.


    그림 : Client/Server Name Resolution


    DNS 서버는 호스트 명칭을 주소로 변환하는 기능 혹은 명칭 식별 기능을 제공하며, 정규화된 도메인 명칭(FQDN)을 이용해 주소를 변환한다. 만일 로컬 네임서버가 변환을 요청 받은 명칭에 대한 데이터를 가지고 있지 않을 경우, 찾을 수 있을 때까지 다른 네임서버에 질의한다. 일반적으로 IP 주소 변환은 네임서버가 도메인 네임스페이스에 대한 질의 내역을 캐시(cache)에 담아두고 있기 때문에 매우 빠르게 수행된다.


    각각의 존은 필수적으로 기본 네임서버가 존 내의 호스트들에 대한 정보를 담고 있는 데이터베이스를 가지고 있으며, 복수의 보조 서버들은 기본 서버 데이터의 복사본을 저장하고 있다. 기본 서버의 정보를 이용해 보조 서버의 데이터를 최신 상태로 업데이트하는 작업을 존 전송(zone transfer)라고 한다.



    그림 : DNS Zone Transfer


    보조 네임서버가 기본 서버의 백업 역할을 수행할지라도, 두 가지 서버 모두 존에 대한 관리를 할 수 있다. 두 서버 모두 주소 변환 요청을 수행하면서 얻는 정보(RR) 뿐만 아니라, 존 데이터베이스의 호스트 정보를 활용한다. 클라이언트는 두 가지 종류의 서버에 모두 주소 변환을 요청할 수 있다.


    네크워크 관리 기관(업체, Network Registrar)의 DNS 네임서버에 자체 네임서버를 등록할 때, 존의 기본 서버, 보조, 혹은 캐시 기능만을 수행할 지 지정할 수 있다. 각 네임서버의 유형은 어떤 역할을 지정하느냐로 구분된다. 하나의 서버가 어떤 존의 기본 서버이면서 다른 존의 보조 서버가 될 수 있다. 기본 혹은 보조 서버의 역할만을 수행하거나, 존에 대한 관리 서버가 아니라 캐시(cache)를 이용해 주소 변환만을 수행하는 기능을 수행할 수도 있다.


    ▶ 참조 문서

      http://www.cisco.com/c/en/us/td/docs/net_mgmt/network_registrar/6-2/user/guide/Userguide/UG15_DNS.pdf


    Posted by 善 곽중선
    2015.03.26 23:50

    온라인 소스 코드 공유 사이트에 대해 지원 언어와 기능, 특징 등을 중심으로 소개한다. 소스 코드 공유 사이트는 다양하나, 소스의 문법을 강조해주는 기능 이외에 컴파일 및 실행 지원, SNS를 통한 소스 링크 공유, 선택적 공유, 회원 가입을 통한 히스토리 관리 등 세부적인 기능 면에서 차이가 있으므로 취향에 따라 선택해서 사용하면 된다. 또한 각 사이트가 지원하는 언어 종류를 확인해 두는 것이 좋다.

    소스 코드 공유 사이트의 용도는 다음과 같다.


    • 인터넷에서 발견한 쓸만한 코드 저장 해두기... (책갈피, 갈무리)
    • 공용 PC에서 작업하는데 컴파일러, IDE가 없을 경우, 간단한 코딩 및 테스트.
    • 내가 짠 소스를 친구에게 보내거나 받아볼 때, 코드 리뷰하기 위해 공유할 때...
    • 4. 블로그 등에 소스 붙여넣기 하는데 이쁘게 보이도록 문법 강조를 하거나, SNS에 물어보고 싶을 때...

    ▶ pastebin.com

      : 지원 언어 - 238가지.

       기능 및 특징 - 페이스북 계정 연동. 심플한 사용자 인터페이스. 광고 있음.


     codepad.org

      : 지원 언어 - C, C++, D, Haskell, Lua, OCaml, PHP, Perl, Plain Text, Python, Ruby, Scheme, Tcl

       기능 및 특징 - 온라인 컴파일러/인터프리터 그리고 간편한 협업 도구이다, 소스를 코드패드에 붙여넣고 실행하면, 짧은 URL (short URL)을 얻을 수 있고, 링크를 채팅이나 이메일을 통해 공유할 수 있다. 비밀/공개 링크 선택 가능. 광고 없음.


     gist.github.com

     : 지원 언어 - 대략 많다.

       기능 및 특징 - Git 저장소와 연동되는 소스 코드 공유 서비스. GitHub 계정 보유자에게 유용함. 비밀/공개 링크 선택 가능. 광고 없음.

     ideone.com

     : 지원 언어 - 60가지 프로그래밍 언어(어셈블리에서 클로져까지..)

      기능 및 특징 - 온라인 코드 컴파일/디버깅 툴. 회원 가입은 페이스북 연동 방식이며, 원하는 친구와 제한적 공유 가능하며 SNS 를 통한 공유가 간편함. 광고 있음.


     dpaste.com

     : 지원 언어 - Python. Framework: Django. Syntax coloring: Pygments. Base 32 conversion for IDs: basewhat. Hashing for IDs: MurmurHash. WSGI server: Gunicorn. Reverse proxy web server: nginx. Database: SQLite. Administration tooling: Fabric. Process control: Supervisor. Version control: Mercurial. Project hosting: Bitbucket. Static hosting: Amazon S3. Application hosting: JohnCompanies FreeBSD VPS. 

     기능 및 특징 - 단순히 문법 강조(syntax highlighting) 및 소스 공유 기능만 제공심플한 사용자 인터페이스와 간편한 사용법. 광고 없음.


     colorscripter.com

     : 지원 언어 - Action Script, ASP, C, C#, CSS, HaXe, HTML, Java, JavaScript, JSP, Objective C, Perl, PHP, Python, Ruby, SQL, Swift, Visual Basic.
      기능 및 특징 - 한국(한글) 사이트. 다양한 언어를 하이라이트하여 소스 코드를 더 보기좋게 블로그 등에 업로드 할 수 있다. Color Scripter는 네이버 블로그등 <script> 태그가 지원되지 않아 Syntax HighLighter를 사용할 수 없는 곳에서도 사용할 수 있으며, 간단한 설정만으로 자신이 원하는 방식으로 소스코드를 하이라이팅 할 수 있다. Adobe Air로 PC 버전을 다운로드 받아 설치할 수 있다.


    ▶ joel.franusic.com/Online-REPs-and-REPLs

     : 소스 코드 공유 사이트를 
    프로그래밍 언어 별로 분류해 놓은 즐겨찾기 모음 사이트.

    Posted by 善 곽중선
    2015.03.26 21:46

    Git, GitHub 초보자를 학습 사이트 및 자료 모음. (제보해주시면 추가합니다.)



    Posted by 善 곽중선
    2015.03.25 18:01

    ▶ 시스템 엔지니어(SE : System Engineer)라는 역할은 무엇인가?


    IT 분야에서는 다양한 역할을 수행하는 사람들이 공존한다. 일반적으로 IT를 대표하는 직종은 프로그래머이나, 그외에도 다양한 역할을 수행하는 사람들이 함께 일하고 있다. 그중에서 시스템 엔지니어는 프로그래머가 아니나 프로그래머와 가장 밀접한 관계를 맺고 일하는 사람들이다. 이들은 프로그래머가 개발한 소프트웨어(혹은 서비스)가 동작할 수 있도록 기반 시스템(base system) 혹은 인프라 구조(infra structure)를 설계 및 구축하고 운영하며 늘 정상적으로 동작하는지 감시하는데다가 끊임없이 보수한다. 예기치 못한 장애로 인해 어느 한 부분에 장애가 발생하면 문제를 파악하고 고치기도 하며 - 때로는 하드웨어 뿐만 아니라 소프트웨어 오류 또한 관리하고 수정한다 -, 노후한 부분에 대한 보수 및 장비 교체 등을 주도하기도 한다. 시스템이 최고의 성능을 발휘할 수 있도록 튜닝(tunning)하는 역할 또한 이들의 몫이다.


    프로그램을 개발하는 것도 아니고, 아름다운 그래픽을 만들어 내는 디자이너처럼 화려하지도 않고, 전체 팀을 총괄하는 프로젝트 매니저처럼 세련된 모습도 아니며 눈에 띄지 않고 조용히 자신의 역할을 수행하는 사람들이지만, 너른 바다를 항해하는 거대한 여객선 밑에서 묵묵히 엔진을 움직이는 사람들처럼 그들의 책임은 막중하다. 물에 떠 있기만 한 배는 아무 의미가 없지 않은가? 발전소가 멈추면 도시가 암흑에 빠지듯 시스템 엔지니어가 서버를 제대로 운영하지 않으면 인터넷에 연결된 소프트웨어 사용자들은 패닉에 빠지고, 모든 기업을 업무가 마비되고 마는것이다. 휴대폰 통화와 항공기 운항 등 거의 모든 문명을 지탱하고 있는 사람들이라 칭할 수 있다.


    ▶ 무엇을 배워야 하고, 어떤 일을 하는가?


    IT 시스템이라는 단어는 네트워크, 하드웨어 그리고 운영체제와 서버 소프트웨어를 포함하는 포괄적인 단어이다. 일부 보안 관련 기술에 대한 이해가 필요한 경우도 있기 때문에 시스템 엔지니어가 배워야할 지식은 실로 방대하다.


    시스템의 가장 하위 기술은 네트워크 기술이다. 인터넷 상에서 서버와 PC 그리고 모바일 기기들이 어떻게 서로 통신하는지, 인터넷 주소 체계와 프로그램 간에 데이터를 주고 받는 규약인 인터넷 프로토콜 등 네트워크 기반 원리를 우선 이해해야 한다. 이러한 이론적 배경 위에서 실제 데이터를 주고 받는 스위치, 라우터 등의 장비들을 설치하고, 정상적으로 동작하도록 적절한 설정을 수행하며, 정상적인 통신 유무를 판단하기 위한 관리 및 모니터링 기술들을 익힌다. 네트워크 분야는 그 자체로 매우 전문화 되어 있기 때문에 깊이 학습하면 네트워크 만을 전문적으로 설계, 구성하고, 또 관리하는 네트워크 설계 및 관리 엔지니어로 진출할 수 있다. 이러한 전문가들은 유/무선 통신사, 케이블 TV 사업자, 인터넷 망 사업자, 클라우드 서비스 등 거대 기업에 소속되어 거대한 네크워크의 일부를 설계하거나, 관리하는 업무를 맡는다. 인터넷 데이터 센터(IDC) 등에서 수백/수천 대의 서버 간의 네크워크를 통제하고 주시하는 업무를 수행하기도 한다.


    네트워크가 갖추어진 상태에서 필요한 것, 그 다음에 논할 주제는 하드웨어이다. 하드웨어는 소위 말하는 서버 컴퓨터를 떠올릴 수 있지만, 대용량 저장 장치, 백업 장치, 네트워크 스위치와 라우터, 그리고 방화벽 등 네트워크에 연결되거나 그 위에서 동작하는 다양한 요소들이 존재한다. 하드웨어를 다루는 업무를 담당하는 직군은 다양한 회사 혹은 기관에 소속되어, 그 나름의 역할을 담당하게 된다.


    가장 먼저 떠올릴 수 있는 역할은 하드웨어를 제작하고 판매하는 회사에 근무하는 엔지니어들이다. IT 분야의 하드웨어, 그중에서 소위 엔터프라이즈(enterprise) 영역이라고 부르는 기업 및 공공기관에서 사용하는 장비들은 개인이 사용하는 것들에 비해 성능, 용량, 가격 등에서 큰 차이가 있다. 이러한 장비들은 냉장고나 TV와는 다르다. 전원만 꽂는다고 동작하는 것도 아니고, 사용 매뉴얼만 읽어보고 쓸 수 있는 것도 아니다. 제품을 설치하고, 각종 설정을 하고 네트워크에 연결하고, 설치 후에 문제가 발생했을 사후 지원하는 기술지원 엔지니어가 필요한 것이다. 따라서, 대다수의 하드웨어 제조사들은 자신들이 판매하는 제품에 대한 전문 기술지원 팀을 운영하고 있다. 이러한 부류의 업체 중에서 우리가 쉽게 떠올릴 수 있는 기업들은 IBM, Oracle, HP 등이 있다. 다만, 안타깝게도 이들 회사의 한국 지사에서 근무하는 기술지원 엔지니어는 거의 없다고 보면 된다. 직접 하드웨어를 판매하기 보다는 벤더(vendor)라고 불리는 판매 대행사들이 직접 고객을 상대하기 때문에 기술지원 엔지니어들은 벤더 사에 소속된 경우가 많다.


    대규모의 하드웨어를 운영하는 공공(정부,공기업)/기업(금융,게임,온라인쇼핑,제조,포털 서비스등등)/기관(학교/재단), 그리고 서버 호스팅 업체 등에서 상주 근무하는 하드웨어 엔지니어들도 있다. 이들의 역할은 지속적으로 서버 및 네트워크 등 인프라 상태를 확인하고 관리하는 것이다. 착각하기 쉬운 점은 서버라는 것이 충분한 전원과 항온항습기를 통한 적정한 외부 환경만 갖추어지면 잘 동작할거라 믿는 것이다. 기계도 간혹 오동작을 하는데다, 지속적으로 다양한 작업을 처리하다 보면 데이터가 쌓이기에 시간이 지남에 따라 불필요한 정보를 삭제해야 하고, 주기적을 백업해야 한다. 노후 장비를 찾아서 교체해야 하며, 장비가 변경될 때마다 이런저런 설정을 계속 고쳐줘야 한다. 사용 중인 소프트웨어가 변경되면 그에 따른 다양한 조치는 말할 것도 없다. 대규모의 시스템을 운영하는 기업(예를 들어 포털/컨텐츠/게임/쇼핑 서비스 등)이 자체적으로 IT 인프라를 설계하고, 운영하는 조직을 가지고 있다면, 단지 서버를 관리하는 역할을 담당하는 엔지니어들 이외에 시스템을 설계하는 팀이 있기 마련인데, 이들이 하는 일에 대해서는 TA(Technical Architect)에 대해 이야기할 때 자세히 다루도록 하겠다.


    앞서 이야기한 IT 담당자들은 이미구축되어 있는 하드웨어와 소프트웨어를 운영하는 역할을 수행한다. 그러나, 사업 분야가 확장되거나, 법령이 바뀌거나, 모바일 등 새로운 서비스 채널을 만들어야 하거나, 업무 수행 방식이 대폭적으로 바뀌는 등의 새로운 비즈니스 요구가 발생했을 때, 그에 걸맞는 시스템을 재구축 하기에는 역부족이다. 한국 IT 업계에서는 이렇게 조직의 소프트웨어 및 하드웨어 전체를 새로 설계/구축하는 일을 차세대 프로젝트라고 부른다. 차세대 프로젝트 요구가 발생하거나, 일부 비지니스를 새로 시작해야 할 때 외부 용역 업체의 도움을 받게 되는데, 이럴 때 나타나 해결사 역할을 수행하는 기업들이 SI(System Integration, 한국에서만 사용하는 IT 용어이며 외국에서는 IT Outsourcing이라는 용어를 사용함) 회사들이다. SI 회사에서 소속되어 잠재 고객의 현재 시스템 구조를 진단하고, 새로운 요구사항에 맞추어 네트워크 및 하드웨어 설계를 진행하는 엔지니어들이 있다. 이들을 System Engineer 라고 부르기도 하고, 최근 들어서는 TA (Technical Architect)라고 부른다. (간혹 Architecture라고 잘못 지칭하는데 Architect가 역할/직책을 나타내는 정확한 표현이다.) 참고로, Architect는 TA(Tehchnical Architect), DA(Data Architect)., AA(Application Architect),. QA(Quality Architect) 등의 역할이 존재한다.


    SI 회사에서 TA의 역할은 기존에 구축된 시스템의 네트워크 및 하드웨어 설계와 세밀한 설정, 용량 등을 검토한 후, 새로운 비즈니스를 수행하고자 하는 고객의 요구에 맞추어 시스템 아키텍쳐(구조)를 재설계하는 것이다. 고객이 제시하는 예산이 허용하는 범위 내에서 가장 고급스러운(?) 장비를 선정해 납품할 수 있어야 하며, 기존 네트워크 구성의 문제점을 파악해 새로운 네트워크 구조를 제시한다. 새로운 장비가 몇 년 간 문제없이 동작할 수 있다는 것을 보증하기 위해 서버의 메모리, CPU 등의 용량을 계산하여 얼마나 많은 부하(load)를 감당할 수 있는지 계산하기도 한다. 고객 측과 계약이 이루어져 시스템 구축 프로젝트를 수행하게 되면, 하드웨어와 네트워크가 원활히 동작하도록 각종 설정을 수행하고, 운영체제를 설치 및 설정하며, 웹/웹 어플리케이션 서버 등의 소프트웨어를 설치한다. 서버가 일정 수준 이상의 부하를 감당할 수 있는지 확인하기 위해 성능 테스트(performance test)를 수행하고, 결과 보고서를 제출하는 것도 이들의 몫이다. SI 기업에 소속된 TA 들이 고객사의 시스템을 설계하는 서비스를 제공하는 반면에, 자체 서비스를 구축 및 운영하는 기업에 속한 이들은 소속 회사의 시스템을 설계하는 일을 한다는 점이 다르다.


    이제 다음에 논할 이야기는 소프트웨어에 관련된 것이다. 기업에서 사용하는 소프트웨어의 종류는 다양한다. 서버용 운영체제(유닉스,리눅스,윈도우), 웹 서버, 웹 어플리케이션 서버, APM(Application Performance Monitoring), 데이터베이스, BPM(Business Process Management), 그룹웨어(groupware), ERP(Enterprise Resource Planning), 이미지 처리 소프트웨어(image processing software), 문서관리시스템(Electronic Document Management System), 문서보안, EAI(Enterprise Application Integration), MCI (Multi Channel Interface), 그외에 산업 분야 별로 특화된 소프트웨어들을 나열하자면 수백/수천가지의 소프트웨어 제품들이 존재한다. 이러한 소프트웨어 역시 기업에서 사용하기 위해서는 설치, 환경설정, 문제해결 그외에 다양한 기술지원을 필요로 하고, 그에 기술지원 담당 엔지니어들이 있기 마련이다.


    SI 기업에서 고객사 소프트웨어 인프라를 검토하고, 새로운 아키텍쳐 설계를 담당하는 사람들이 AA(Application Architect)이다. AA는 고객사의 업무를 수행하는데 있어 가장 적합한 소프트웨어들을 선정하는 역할을 최우선적으로 수행한다. AA 역할을 수행하며 기업용 소프트웨어 구조를 설계하기 위해서는 다양한 소프트웨어 관련 이론들을 이해해야 하며, 어느 정도 하드웨어 지식 또한 갖추어야만 한다. 자료구조와 알고리즘에 대한 기초 지식을 이해해야 소프트웨어의 성능을 예측할 수 있고, 성능 문제 발생시 합리적인 판단을 수행할 수 있다. 운영체제를 설치할 수는 없더라도 운영체제의 다양한 명령어를 다룰 줄 알며, 각종 설정을 이해하고 있어야 한다. 운영체제를 모르면 최적의 성능을 이끌어 낼 수 없는데다, 소프트웨어/하드웨어들이 서로 충돌했을 때 원인 파악이 불가능하다. 네트워크 이론과 실전 지식을 갖추지 못할 경우, 다양한 소프트웨어가 통신하는 기업 환경을 설계할 수 없다. (네트워크 영역은 TA/AA가 함계 설계하고 구축한다.) 다양한 소프트웨어 제품들의 기능을 이해하고, 오픈소스들의 장단점을 파악하고 있어야 고객 환경에 최적의 구성을 제안할 수 있다.


    AA가 소프트웨어 아키텍쳐를 설계할 때 사용하는 소프트웨어들은 앞서 이야기한 다양한 기업에서 파는 제품일수도 있고, 오픈소스를 활용할 수도 있다. AA는 오픈 소스를 다룰 줄 알아야 하고 기능을 이해하며, 필요하면 수정할 수도 있어야 하기 때문에 TA와 달리 프로그래밍 역량을 필요로 한다. TA와 마찬가지로 자신에 설계하고 제시한 구조가 합리적이고 충분한 성능을 발휘할 수 있다는 것을 증명해야 하므로, TA 와 함께 성능 테스트를 수행하고 각종 소프트웨어의 설정을 수정하거나 필요하면 수정하는 작업을 통해 성능 최적화(performance optimization)을 실시한다.


    개략적으로 SE의 역할과 필요한 지식을 요약했다. 충분치 못한 부분은 추후에 보완할 생각이고, 다행히 아직은 밥은 먹고 산다.

    짤방(?) 원작자 - 김국현 블로그 

    Posted by 善 곽중선