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.11 01:43

    "AlgoSpot DRAWRECT 문제 풀이"에 대한 마지막 코드 예제 입니다. 이번에는 절차지향 방식의 코드를 개선해 XOR 비트 연산자의 원리를 이용해 if 제어문 조차 사용하지 않는 short code를 소개하고자 합니다. XOR 비트 연산자 사용에 대한 아이디어는 이은택님이 제안해 주셨고, 이은택님의 샘플 코드 또한 참고해 보시면 좋겠습니다. XOR 연산자를 활용한 기법을 소개하는 이유는 컴퓨터 하부 구조의 동작 방식 (2진수 기반의 컴퓨터 원리 등)과 수학적 논리(혹은 공식)를 잘 이해할 경우, 더 할 나위 없는 성능을 발휘하는 가장 짧은 코드를 작성할 수 있다는 것을 보여 드리기 위함입니다.


    ▶ 배타적 논리합


    배타적 논리합 (exclusive or)은 수리 논리학에서 주어진 2개의 명제 가운데 1개만 참일 경우를 판단하는 논리 연산입니다. 약칭으로 XOR, EOR, EXOR라고도 씁니다. 연산자는 ⊻ (유니코드: U+22BB)입니다. 혼돈이 되지 않을 경우, XOR, xor ⊕ (유니코드: U+2295), +、≠ 라고도 씁니다. (위키백과 배타적 논리합 참조)


    배타적 논리합의 "진리표"는 아래와 같습니다.


     명제 P

    명제 Q

     P  Q

     참

     참

     거짓 

     참

     거짓 

     참 

     거짓

     참 

     참 

     거짓

     거짓

     거짓


    ▶ XOR (Exclusive OR) 비트 연산


    XOR 연산은 두 값의 각 자릿 수를 비교해, 값이 같으면 0, 다르면 1을 계산하는 연산입니다. XOR 연산은 비트 단위로 이루어집니다.


    0101

    XOR    0011

    -----------

           = 0110

     

    C or C++ 언어에서 XOR 비트 연산자는 ^ (caret) 문자를 사용합니다.


       x = y ^ z;


    ▶ XOR 비트 연산을 이용한 동일 값 제거


    XOR 비트 연산자를 활용하면, 여러 개의 값 중에서 동일한 값들을 제거할 수 있습니다. 예를 들어, 3개의 값이 존재하고 그 중에서 2개의 값이 동일하고 나머지 하나의 값이 다른 경우, XOR 비트 연산 2번을 통해 나머지 하나의 다른 값을 추출할 수 있습니다. 예를 들어, 십진수 12, 7, 7 등 3개의 값을 2번에 걸쳐 XOR 연산을 수행하는 과정과 그 결과는 다음과 같습니다. 연산 순서에 상관없이 하나의 다른 값이 얻게 되는 것을 확인할 수 있습니다.


    ▶ XOR 비트 연산을 short code 예시

    #include <stdio.h>
    #include <stdlib.h>
    
    /* x,y 좌표를 표현하는 점(point) 구조체를 정의한다. */ 
    typedef struct {
       int x;
       int y;
    } point;
     
    
    int main(int argc, char *argv[]) {
    	
    	// 테스트 케이스 반복 횟수 및 좌표 변수 생성 및 초기화 
    	int cnt = 0;
    	point p1 = {0, 0};
    	point p2 = {0, 0};
    	point p3 = {0, 0};
    	point p4 = {0, 0};
    	
    	// 테스트 케이스 반복 횟수 입력. 
    	int testCaseCount;
    	printf("Input test case number : ");
    	scanf("%d", &testCaseCount); 
    	
    	// 입력 받은 테스트 케이스 수만큼 반복 처리... 
    	for(cnt=0; cnt<testCaseCount; cnt++) {
    		
    		// ----- pt, p3, p3 등 3개의 좌표 값 입력 -----
    		printf("Input p1 (x, y) : ");
    		scanf("%d %d", &p1.x, &p1.y);
    		printf("p1.x = %d, p1.y = %d\n", p1.x, p1.y);
    		
    		printf("Input p2 (x, y) : ");
    		scanf("%d %d", &p2.x, &p2.y);
    		printf("p2.x = %d, p2.y = %d\n", p2.x, p2.y);
    		
    		printf("Input p3 (x, y) : ");
    		scanf("%d %d", &p3.x, &p3.y);
    		printf("p3.x = %d, p3.y = %d\n", p3.x, p3.y);
    		
    		// ----- 4번째 좌표 계산 및 출력 ----- 
    
    		// p4.x 좌표 계산 
    		p4.x = p1.x ^ p2.x ^ p3.x;
    		// p4.y 좌표 계산 
    		p4.y = p1.y ^ p2.y ^ p3.y;
    
    		// p4 좌표 출력 
    		printf("\nResult p4.x = %d, p4.y = %d", p4.x, p4.y );
    	}
    	
    	return 0;
    }


    Posted by 善 곽중선
    2015.05.10 03:39

    "AlgoSpot DRAWRECT 문제 풀이"에 대한 2가지 코드를 준비해 봤습니다. 그중에서 두번째로 객체지향적으로 문제를 풀어보는 코드입니다. 객체지향 프로그래밍의 목표는 컴퓨터가 최대한 효율적으로 문제를 해결할 수 있도록 코드를 작성하는 것이 아니라, 인간이 세상을 바라보는 시각에 근접하게끔 코드를 작성하는 것입니다. 따라서, 절차지향적 코드에 비해 코드의 분량이 좀 더 길어지거나, 상대적으로 비효율적으로 느껴질 수 있습니다. 그러나, 하드웨어의 성능 발전, 컴파일러 기술의 향상 등으로 인해 조금 더 길어진 코딩이라고 할지라도 전체적인 성능 상의 차이는 두드러지게 나타나지는 않는 편입니다. (참고 : 절차지향 코드 버전)


    더불어, 객체지향 기법을 적용하는 분야(혹은 도메인)에서는 기계 보다는 인간의 관점을 중시하는 경향이 있습니다. 객체지향 혹은 절차지향 중 어느 한 쪽이 우월한가를 따지는 것은 별 의미가 없습니다. 객체지향과 절차지향은 각각의 장점과 단점을 가지고 있고, 해결해야할 문제가 일상생활(실세계)의 문제에 가까울수록 객체지향 기법으로 설계하고 코딩했을 때 얻을 수 있는 이득이 많습니다. 이점을 염두에 두시고, 이어지는 설명과 구현 절차를 읽으셔야 합니다.  


    ▶ 객체지향 철학 그리고 5대 개념


    문제 풀이에 앞서 객체지향 철학 및 5대 개념을 먼저 읽어보시는 것을 권합니다.



    ▶ UML(Unified Model Language)


    객체지향으로 설계 및 구현할 때는 플로우 챠트가 아니라, 가급적 UML(Unified Modeling Language)을 이용해 설계합니다. (플로우 차트를 절대 사용하지 말아야 한다는 것은 아닙니다. 객체지향 코드 내에서도 논리적인 흐름이 발견되고 논리적인 흐름을 표현하는데는 플로우 차트가 유용합니다.) UML 에서는 다양한 다이어그램을 이용해 소프트웨어의 모델을 설계하는데, 그중에서 유스 케이스(Use Case), 클래스 다이어그램(Class Diagram) 그리고, 시퀀스 다이어그램(Sequence Diagram)을 가장 많이 사용하게 됩니다. "UML: 클래스 다이어그램과 소스코드 매핑" 문서를 읽어보시면 도움이 될 듯 합니다.


    UML 중에서 유스 케이스(Use Case)는 가장 직관적이면서 이해하기 쉽고, 가장 먼저 작성하게 됩니다. 사용자(actor)가 컴퓨터(소프트웨어 혹은 하드웨어)를 이용해 어떤 작업들을 수행하는가? 소프트웨어가 어떤 기능을 제공하는가? 라는 질문에 대해 가장 포괄적이고, 직관적으로 보여주는 다이어그램입니다. 아래 그림을 보시면, 논리적인 흐름이나 기술적인 정보는 아무것도 표현하지 않습니다. 객체지향 설계의 접근 방식의 모토(motto)는 바로 인간의 시선에서부터 시작하자는 것입니다.


    유스 케이스 다이어그램의 목적은 시스템 혹은 소프트웨어에 대한 요약(brief), 개요(outline or overview)를 제공하는 것입니다. 소프트웨어를 구현함에 있어서 가장 핵심적인 기능은 무엇인가? 좀 더 세세한 설계를 진행하기에 앞서 시스템을 최대한 간략히 묘사함으로써 개발자/기획자/설계자 등 프로젝트에 참여하는 모든 사람들이 공통의 목표를 명확히 공유할 수 있도록 하는 것이 목표입니다.


    DRAWRECT를 정의하는 유스 케이스는 매우 단순합니다. 3개의 좌표를 제시하고, 사각형의 4번째 좌표를 계산하는 것입니다. 유스 케이스 다이어그램으로 표현하면 아래와 같습니다.




    ▶ 객체 후보 정하기 


    절차지향 프로그램은 고유한 기능을 수행하는 함수(혹은 서브루틴)들을 프로그래머가 지정한 절차에 따라 연속적으로 실행시켜 결과를 얻는 방식입니다. 공장의 컨베이너 벨트처럼 잘 짜여진 흐름에 따라, 연쇄적으로 하나씩 작업들이 이어가는 것이죠. 따라서, 소프트웨어 개발자 혹은 설계자는 각각의 작업을 마친 후에 다음 작업이 무엇이 실행되어야 하고, 각 단계에서 가공된 데이터를 다음 단계의 함수로 어떻게 전달할 것인가? 동적인 흐름을 하나씩 따져가며 문제를 풀어야 합니다.


    반면에 객체지향 프로그래밍은 객체라는 형태의 부품들을 먼저 정의 혹은 추출합니다. 이것은 사람이 현실 세계를 이해하는 방식에서 비롯된 것입니다. 우리는 움직이는 자동차를 바라볼 때 자동차의 가속, 감속, 회전 등의 동작(behavior)과 자동차 자체의 형태, 무게, 차종 등의 상태 정보(status)를 구분해서 인식하지 않습니다. 그냥 자동차 자체는 동작과 상태를 함께 지니고 있다고 받아들입니다. 이러한 인간의 사고 방식을 따르는 객체지향 프로그램에서 객체라는 요소는 행위(behavior)와 상태(status)를 함께 가지고 있는 소프트웨어 부품(parts, component)입니다. 객체지향 설계에서는 문제를 푸는 절차에 앞서, 해결하고자 하는 문제 자체에 포함된 다양한 객체를 먼저 식별(identify or extract)하고, 각 객체들이 서로 어떻게 연결되고, 함께 상호 작용하는지를 파악합니다.


    DRAWRECT 문제에서 객체(혹은 클래스)로 구분지을 수 있는 후보군은 다음과 같습니다. 후보군이라고 말하는 것은 설계하는 사람의 관점에 따라 객체 혹은 클래스가 아니라고 볼 수도 있다는 말입니다. 정확히 어떤 방식으로 설계해야 하고 어떤 클래스들을 정의해야 한다는 엄격한 기준은 없습니다.


     객체(클래스) 후보

     행위 (behavior)

    상태(status) 

     뷰포인트

     입력 좌표 값의 정상 유무 검증

     최소, 최대 좌표 허용 범위 

     좌표

     X축, Y축 값 저장 및 조회

     X축 및 Y축 좌표 값

     사각형

     p1, p2, p3 등 3개의 좌표 저장

     p4 좌표 계산

     p1, p2, p3 등의 좌표 정보


     

    ▶ 객체 간의 정적 관계 설계 (클래스 다이어그램)


    객체 간의 정적인 관계는 클래스 다이어그램을 이용해 정의합니다. 관계(relation)를 통해 객체 혹은 클래스들이 서로 간에 어떤 의미(역할)을 가지는지를 나타내게 됩니다. 자동차의 부품들이 서로 아무런 상관없이 존재하거나, 독립적으로 동작하는 것이 거의 없는 것처럼 객체지향 프로그램에서도 프로그램의 부품에 해당하는 객체들에 각각의 역할과 의미가 부여되어야 합니다. 클래스 다이어그램을 통해서 각 클래스 간의 상속, 합성, 연관 등의 정적인 관계를 파악합니다. 이러한 관계 부여는 실제 코딩으로 이어지기 때문에 의미 없는 문서화 작업으로 치부해서는 안됩니다.


    - 사각형(Rectangle)은 좌표(Point)를 포함합니다. 이를 소유 관계(has-a relation)라고 합니다.

    - 뷰포인트(Viewpoint)는 좌표(Point)를 검증합니다. 이를 연관(association)이라 합니다.




    ▶ 객체 간의 상호 동작 설계 (시퀀스 다이어그램)


    객체 혹은 클래스들이 함께 동작하는 과정에서 어떤 기능을 호출하는지와 어떤 정보를 주고 받는지를 표현하는 것이 시퀀스 다이어그램입니다. 시퀀스 다이어그램은 기능의 흐름을 표현한다는 점에서 플로우 차트와 유사하나, 논리적인 판단(if 조건 등)을 기술하지 않는다는 점은 플로우 차트와 다릅니다. 앞서 말한 것처럼 객체지향 설계에서는 세밀한 논리적 흐름보다는 큰 얼개(구조)를 표현하는데 집중합니다.




    ▶ 객체지향 코드 작성


    위와 같은 설계를 바탕으로 구현된 코드는 아래와 같습니다.


    [Point.java]
    package algospot.exam.drawrect;
    
    /**
     * 점(point) 클래스.
     * 
     */
    public class Point {
    	// X 축 좌표.
    	private int xCoord;
    	// Y 축 좌표.
    	private int yCoord;
    	
    	/**
    	 * 생성자와 getter 메소드만을 제공하는 이유는 최초 값 설정 후에 잘못 변경되는 것을 막기 위함이다.
    	 * 
    	 * @param xCoord X 좌표
    	 * @param yCoord Y 좌표
    	 */
    	public Point(int xCoord, int yCoord) {
    		this.xCoord = xCoord;
    		this.yCoord = yCoord;
    	}
    	
    	public int getXCoord() {
    		return xCoord;
    	}
    	
    	public int getYCoord() {
    		return yCoord;
    	}
    	
    	public String toString() {
    		return String.format("(x = %d, y = %d)", xCoord, yCoord);
    	}
    }
    
    [Viewport.java]
    package algospot.exam.drawrect;
    
    public class Viewport {
    	private static final int MIN_COORDINATE = 1;
    	private static final int MAX_COORDINATE = 1000;
    
    	public static boolean validatePoint(Point point) {
    		return point.getXCoord() >= MIN_COORDINATE && point.getXCoord() <= MAX_COORDINATE &&
    				point.getYCoord() >= MIN_COORDINATE && point.getYCoord() <= MAX_COORDINATE;
    	}
    }
    
    [Rectangle.java]
    package algospot.exam.drawrect;
    
    /**
     * 직사각형 클래스
     * @author "Sunny Kwak"
     *
     */
    public class Rectangle {
    	private Point p1;
    	private Point p2;
    	private Point p3;
    	
    	public Rectangle(Point p1, Point p2, Point p3) {
    		this.p1 = p1;
    		this.p2 = p2;
    		this.p3 = p3;
    	}
    	
    	/**
    	 * 3개의 사각형 좌표 값을 이용해 마지막 좌표를 계산한다.
    	 * @return
    	 */
    	public Point calculateP4() {
    		int x = 0, y = 0;
    		
    		if(p1.getXCoord() == p2.getXCoord()) {
    			x = p3.getXCoord();
    		} else if(p1.getXCoord() == p3.getXCoord()) {
    			x = p2.getXCoord();
    		} else if(p2.getXCoord() == p3.getXCoord()) {
    			x = p1.getXCoord();
    		}
    		
    		if(p1.getYCoord() == p2.getYCoord()) {
    			y = p3.getYCoord();
    		} else if(p1.getYCoord() == p3.getYCoord()) {
    			y = p2.getYCoord();
    		} else if(p2.getYCoord() == p3.getYCoord()) {
    			y = p1.getYCoord();
    		}
    		
    		return new Point(x, y);
    	}
    }
    
    [AlgoSpotDrawRect.java]
    package algospot.exam.drawrect;
    
    import java.util.Scanner;
    
    /**
     * 직사각형의 3개 좌표를 입력 받아 4번째 좌표를 계산하는 프로그램.
     *  
     * @author "Sunny Kwak"
     */
    public class AlgoSpotDrawRect {
    
    	public static void main(String[] args) {
    
    		Scanner scanner = new Scanner(System.in);
    		
    		// 테스트 케이스 반복 횟수 입력.
    		System.out.print("Input test case number : ");
    		int loopCnt = scanner.nextInt();
    		
    		// 입력 받은 테스트 케이스 수만큼 반복 처리... 
    		for (int cnt = 0; cnt < loopCnt; cnt++) {
    
    			// pt, p3, p3 등 3개의 좌표 값 입력 
    			Point p1 = readPoint(scanner);
    			Point p2 = readPoint(scanner);
    			Point p3 = readPoint(scanner);
    
    			// 직사각형 객체 생성 및 4번째 좌표 계산 
    			Rectangle rectangle = new Rectangle(p1, p2, p3);
    			Point p4 = rectangle.calculateP4();
    			
    			// 정상적으로 계산된 경우, 4번째 좌표 출력 
    			System.out.printf("%d %d\n", p4.getXCoord(), p4.getYCoord());
    		}
    
    		scanner.close();
    	}
    
    	/*
    	 * x, y 좌표 입력 및 정상 좌표 유무 검사.
    	 * 정상 좌표인 경우, Point 객체를 생성한 후 반환.
    	 */
    	private static Point readPoint(Scanner scanner) {
    		System.out.print("Input p1 (x, y) : ");
    		int xCoord = scanner.nextInt();
    		int yCoord = scanner.nextInt();
    		Point point = new Point(xCoord, yCoord);
    
    		if (Viewport.validatePoint(point)) {
    			return point;
    		} else {
    			throw new IllegalArgumentException("Invalide coordinate : " + point);
    		}
    	}
    
    }
    
    


    Posted by 善 곽중선