출처 : http://hochulshin.com/permutation-composition-summary/

 

 

순열, 중복 순열, 조합, 중복 조합에 대한 이해를 해보자.

 

1. 개요

순열, 중복 순열, 조합, 중복 조합를 비교해 보자.

1.1 순열

  • 중복없이 n개 중에서 r개를 뽑아 순서를 정해 나열하는 경우를 말한다.
  • 예를 들어, 다섯 개의 문자 a, b, c, d, e를 일렬로 배열해야 하는 것을 생각해보자.
a, b, c, d, e
a, b, c, e, d
a, b, e, c, d
...

1.2 중복 순열:

  • n개 중에서 r개를 뽑아 순서를 정해 나열하는 경우, 같은 것(종류)을 다시 뽑을 수 있는 것을 말한다.
  • 예를 들어, 숫자 1,2,3을 이용해 만들수 있는 3자리 정수를 모두 구해야 한다고 생각해 보자.
111
112
113
121
122
...

1.3 조합

  • 중복없이 n개 중에서 r개를 순서에 상관없이 뽑는 것을 말한다.
  • 예를 들어 시간이 서로 다른 4 과목 A, B, C, D중 2개 만을 선택해서 수강 신청 하는 경우를 생각해보자.
AB
AC
AD
BC
BD
CD

1.4 중복 조합

  • n개 중에서 r개를 같은 것(종류)를 뽑을 수 있으며 뽑히는 순서에 상관없이 선택하는 것을 말한다.
  • 예를 들어 1인당 2개의 표를 가지고 4개의 연예인(A, B, C, D)에게 선호도 투표를 하는 경우를 생각해보자. 이때 2개의 표를 같은 인물에게 2표를 모두 행사할 수 있다면.
AA
AB
AC
AD
BB
BC
BD
..

2. 순열

2.1 개요

세개의 문자 a,b,c 중 2개를 택해 일렬로 배열하는 방법의 수는 첫 번째 자리의 문자를 택하는 가짓수(3)와 남은 문자 중에서 두번째 자리에 놓을 가짓수(2)를 곱한 것과 같다. 이처럼 서로 다른 n가지에서 r가지를 택하는 순열의 모든 경우의 수는 nPr로 표시할 수 있다.

nPr = n!/(n-r)!, nP0는 1, nPn은 1(모두 선택하거나, 아무것도 선택하지 않을 경우의 수는 1)

2.2 구현

nPr을 이루는 각 경우를 뽑아내기 위한 구현의 예제는 다음과 같다.

  • 문제: 1, 2, 3, 4 숫자 4개가 주어졌을때 이 중 3개의 숫자를 뽑아 한번씩만 사용해서 만들 수 있는 모든 숫자를 출력하라.
#include <stdio.h>

int T[10]; //nPr을 이루는 각각의 경우를 저장
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

void swap(int *i, int *j){
    int temp = *i;
    *i = *j;
    *j = temp;
}
/*T[]에서 q개 출력*/
void process(int q){
    for(int i = q-1; i>= 0; i--){
            printf("%d ", T[i]);
    }
    printf("\n");
}
 /*data[]에서 앞에서부터 n개의 숫자 중 r개를 선택해서 순열을 출력하는 함수. q는 출력 시 출력 갯수 지정*/
void Perm(int n, int r, int q){
    if(r == 0){
        process(q);
        return;
    }
    for(int i = n-1; i>=0; i--){
        swap(&data[i], &data[n-1]); //n-1을 모든 index와 swap해서 다양한 순서를 만든다.
        T[r-1] = data[n-1];		  //T의 뒤에서부터 결과값 저장	
        Perm(n-1, r-1, q);		  //다음  index로 진행 	
        swap(&data[i], &data[n-1]);
    }
}
int main(void){
    Perm(4, 3, 3); 
    return 0;
}

3. 중복 순열

3.1 개요

서로 다른 n개의 중복을 허용하여 r개를 택하여 나열하는 방법을 n개에서 r개를 택하는 중복 순열이라고 한다. 중복 순열의 모든 경우의 수는 아래와 같다.

중복 순열 nㅠr = n의 r제곱승

3.2 구현

중복 순열의 각 경우를 출력하는 코드를 순열 구현 코드와 비교하면서 확인해보자. 1, 2, 3, 4 숫자 4개가 주어지고 이 중 3개를 뽑는데 같은 숫자가 반복되어도 가능하다.

#include <stdio.h>

int T[10]; //nPr을 이루는 각각의 경우를 저장
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

void swap(int *i, int *j){
    int temp = *i;
    *i = *j;
    *j = temp;
}

void process(int q){
    for(int i = q-1; i>= 0; i--){
            printf("%d ", T[i]);
    }
    printf("\n");
}
 /*data[]에서 앞에서부터 n개의 숫자 중 r개를 (중복) 선택해서 순열을 출력하는 함수. q는 출력 시 출력 갯수 지정*/
void PI(int n, int r, int q){
    if(r == 0){
        process(q);
        return;
    }
    for(int i = n-1; i>=0; i--){
        swap(&data[i], &data[n-1]);
        T[r-1] = data[n-1];
        PI(n, r-1, q);	//유일하게 다른 부분. 1가지를 선택한 후 선택할 수 있는 종류를 줄이지 않음.(n)
        swap(&data[i], &data[n-1]);
    }
}

int main(void){
    PI(4, 3, 3);
    return 0;
}

4.조합

4.1 개요

서로 다른 n개에서 순서를 생각하지 않고 r개(0<=r<=n)를 택하는 것을 조합이라고 한다. n개 중 r개를 선택하는 모든 조합의 경우의 수는 다음과 같다.

nCr = nPr을 r!로 나눈 것 = n!/(r!*(n-r)!), nC0과 nCn은 1이다.

nCr은 다음과 같은 관계를 가진다.

nCr = n-1Cr-1 + n-1Cr

참고로, 서로 다른 n개의 물건을 p개, q개, r개의 3개의 그룹으로 나누는 방법은 다음과 같다.

nCp * n-pCq * rCr

예를 들어, 서로 다른 종류의 꽃 15송이를 다섯 송이씩 세묶음으로 나누는 방법의 수는 15C10 * 10C5 * 5C5이다.

4.2 구현

조합을 한 각 경우를 출력하는 것을 생각해보자. nCr = n-1Cr-1 + n-1Cr, nC0 = 1 관계를 이용한다. 1, 2, 3, 4 숫자 4개가 주어졌을때 순서 상관없이 3개씩 묶어 그룹을 만들 때 그 멤버를 출력한다.

#include <stdio.h>

int T[10]; //nPr을 이루는 각각의 경우를 저장
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

void process(int q){
    for(int i = q-1; i>= 0; i--){
            printf("%d ", T[i]);
    }
    printf("\n");
}
/*data[]에서 앞에서부터 n개의 숫자 중 r개를 선택해서 조합을 출력하는 함수. q는 출력 시 출력 갯수 지정*/
void Comb(int n, int r, int q){
    if(r == 0){
        process(q);
        return;
    }else if(n<r){
        return;
    }
    else {  //loop이 아님
        T[r-1] = data[n-1];
        Comb(n-1, r-1, q);  //n-1Cr-1: 현재 아이템을 선택한 경우
        Comb(n-1, r, q);    //n-1Cr: 현재 아이템을 선택하지 않은 경우
    }
}

int main(void){
    Comb(4, 3, 3);
    return 0;
}

5. 중복 조합

5.1 개요

서로 다른 n개에서 순서를 생각하지 않고 중복을 허용하여 r개(0<=r<=n)를 택하는 것을 중복 조합이라고 한다. 예를 들어, 숫자 1, 2에서 중복을 허용하여 3개를 선택하는 조합은 (1,1,1)(1,1,2)(1,2,2)(2,2,2)이다. 중복 조합의 모든 경우의 수는 다음과 같이 표현된다.

nHr = n+r-1Cr

nHr은 다음과 같은 관계를 가진다.

nHr = nHr-1 + n-1Hr

5.2 구현

여기서 중복 조합의 구현은 생략한다. 스스로 구현해 보자!

'알고리즘' 카테고리의 다른 글

DFS,BFS  (0) 2015.07.09
XOR  (0) 2015.06.12
KMP 알고리즘 (문자열 패턴 검색)  (0) 2015.05.29
[알고리즘] 퀵 정렬(Quick Sort)  (0) 2014.12.01
블로그 이미지

kuku_dass

,

git 사용법과 소스 관리

SCM 2015. 11. 9. 16:15

출처 : http://bcho.tistory.com/773


소스 코드 관리와 git

조대협 (bwcho75@지메일)

소프트웨어 개발에 있어서, 소스코드의 관리는 중요한 포인트 중의 하나이다. 다양한 버전과 변경 관리, 협업을 위해서는 소스코드를 저장 및 관리할 수 있는 시스템이 필요하고, 이를 VCS (Version Control System) 또는 SCM (Source Code Management) System이라고 한다. 소스코드 관리 시스템의 주요 기능은 다음과 같다.

협업을 위한 코드 공유 - 여러 사람들이 협업을 할 경우, 코드를 각 개발자와, 팀간에 공유할 수 있어야 하며, 

접근 제한 -  사용자의 권한 등급에 따라 접근을 제한해야 한다.

다양한 버전(형상) 관리 - 소프트웨어 개발 버전 또는 릴리즈 (브랜치) 마다, 다른 코드를 저장할 수 있어야 한다. 예를 들어 릴리즈된 버전이나 마이너 버전에 대한 코드 관리, 패치 코드 관리 등이 그 예가 될 수 있다. 또한 다양한 브렌치중 두개 이상의 브렌치를 하나의 코드로 합칠(merge)할 수 있어야 한다.

특정 시점 추적 – 태깅 설명

변경 추적 마지막으로, 각 코드에 대한 변경을 추적할 수 있어야 한다. 누가? 언제? 어떤 이유로 코드를 어떻게 변경을 했는지를 추적하여 문제 발생시 원인 분석을 할 수 있어야 한다.

1)   Branch

먼저 소스코드 관리에 대한 개념 이해를 위해서는 Branch의 개념 이해가 필요하다. 소프트웨어 개발을 할 때, 하나의 저장소에 소스코드를 저장해서 개발하고, 개발자간의 공유를 해서 협업을 진행한다.

 개발을 진행하다가 특정 목적에 의해서 별도의 작업에 의해, 예를 들어 영문으로 된 버전을 중국어나 한국어로 제공하기 위해서는 기존 개발 소스코드의 복사본을 만들어서,중국어 개발용으로 하나 사용하고, 한국어 개발용으로 하나 사용 한다. 이렇게 새롭게 만들어진 소스코드의 복사본 (중국어버전, 한국어 버전)을 브랜치라고 하고, 원래 소프트웨어를 개발하던 소스코드 저장소를 메인 브랜치(또는 trunk version, head version)이라고 한다.

이렇게 코드의 원본에서 용도와 목적에 따라서 새로운 복사본을 만들어 나가기 때문에,메인 코드에서 복사본을 나뭇가지 (Branch)라고 하고, 이 모양이 마치 나무와 같은 구조가 되기 때문에, Source Code Tree또는 Branch Tree라고 한다.

2)    Check Out / Commit & merge

소스코드 저장소를 만들었으면 해야 할 일이 코드를 저장하거나 내려 받는 일이다.

코드를 내려 받는 행위를 Check Out이라고 하고, 작성된 코드를 저장소에 업로드 하는 행위를 Commit이라고 한다.

VCS의 종류에 따라서 Check Out된 코드의 경우, 다른 사람이 편집을 할 수 없고read만 할 수 있도록 lock을 걸 수 도 있다. (VCS 종류에 따라 다르다). 이 경우 다른 사람이 내가 편집하고 있는 코드를 편집할 수 없기 때문에, 코드 작성에는 문제가 없지만 반대로 다른 개발자는 lock이 풀리기 까지 기다려야 하기 때문에, 협업에 문제가 발생할 수 있다. 그래서 lock을 걸지 않고 코드를 동시에 2인 이상이 코드를 다운받아 편집할 수 있는 방법을 제공하는데, 이 경우 코드를 Commit을 할 경우, 한 파일을 다운 받아서 각자 편집을 하고 저장을 시도할 경우, 코드상 Conflict(충돌)이 발생한다.

 즉 개발자 A,B가 소스 코드 버전 1을 Check Out 받았다고 하자, 개발자 A가 소스코드 2번 라인에 "printf('hello world')" 라는 코드를 추가하고 Commit을 해서 코드 버전이 2가 되었다고 했을 때, B도 역시 2번 라인에 "printf('hello developer')" 라는 코드를 추가하고 Commit을 하려고 하면, A와 B가 편집한 내용이 충돌이 생긴다. 둘다 1번 버전의 코드를 가지고 수정을 하였으나, 공교롭게 개발자 A,B 가 같은 파일을 수정하였기 때문에 VCS 입장에서는 B 개발자가 수정한 내용을 반영하게 되면A개발자가 반영한 내용이 없어져 버리기 때문에 문제가 될 수 있다.

이런 경우 VCS에서 B가 Commit하려는 코드가 현재 버전인 2 버전에 의해서 편집 된것이 아니라는 것 을 알려주고, 개발자 B에게 선택을 하도록 하는데, 2 버전 코드와 다른 내용을 병합 (merge)하게 하거나, 3버전의 코드를 Overwrite하여 2버전의 코드 변경 내용을 무시할 수 있도록 한다.

대부분의 경우에는 당연히 다른 개발자의 변경 내용을 무시할 수 없기 때문에, merge를 선택하게 되고, merge는 개발자B가 2번 버전과의 로직의 차이를 일일이 확인하면서 수동으로 merge를 하도록 한다.

3)   Update

Update의 개념은 개발자가 소스 코드를 Check Out 받아 놨을 때, 현재 작업 버전이 오래되거나, 또는 다른 개발자가 Commit을 먼저 해서, VCS의 소스코드 버전이 올라갔을 때, VCS와 Local의 코드를 동기화 시키기 위해서 사용한다.

위에서 설명한 Commit시 발생하는 Conflict(충돌) 시나리오와 마찬가지로, 내가 변경한 코드와 다른 개발자에 의해서 변경된 코드가 Conflict이 발생할 수 있으며, 앞에서 설명한 방법과 같은 방법으로 수동으로 코드의 변경 부분( 차이 나는 부분)을 병합하도록 한다.

4)   Tagging

다음으로 Tagging이라는 개념을 알아보자, Tagging 이란, 코드를 개발 중에, 특정 시점의 이미지에 표시를 해놓는 것을 의미한다. 예를 들어, 매일 소스코드에 대한Tagging을 달아놓으면, 개발 중에 문제가 생겼을 경우에, 특정 날짜의 소스 코드로 다시 돌아갈 수 가 있다.

Tagging은 주로, Build 시마다 하는 경우가 많은데 (이를 빌드 태그라고 함). 통상 적으로 빌드시 에러가 날 경우에 다른 개발자들이 빌드 에러로 인하여 개발을 못하는 경우가 생길 수 있기 때문에, 이런 경우에는 이전 Build시 태그해놓은 버전으로 소스코드를 돌려서 다시 개발을 수행하고, 문제가 해결 되면, 새로운 코드를 다시Commit 하는 방식으로 개발을 진행한다.

5)    Release Branch

다음으로 Branch중 Release Branch에 대한 설명이다.

패키지 소프트웨어 개발 프로세스를 생각해보자, 예를 들어, 서버 제품을 개발하여,출시를 했다고 하자. 현재 개발 중인 메인 Branch 에서 해당 시점에 릴리즈를 했다.릴리즈한 서버의 버전은 6.1이다. 메인 branch 로는 계속해서 신제품 개발을 이어나가고 7.0 개발을 진행 중이었다.

이때, 6.1을 사용하던 고객에게서 버그 수정 요청이 온경우 어떻게 해야 할까?

6.1 코드에 일부 코드만 수정하여 패치를 발급하면 되지만, 메인 branch의 경우 이미 7.0 버전 개발을 위해서 코드 개발이 많이 진행되었기 때문에 6.1 에 대한 코드는 바뀌어서 찾을 수 가 없고 7.0 버전 역시 개발이 완료되지 않아서 수정이 불가능하다.

이런 시나리오를 방지하기 위해서 패키지 형태의 소프트웨어 개발은 각 릴리즈 마다release branch를 발급하는 것이 좋다.

우리가 사용하는 Windows 도, Windows XP,7등으로 release를 하지만 Service Pack이나 필수 패치등을 발급하는 것은 각 release 별 branch를 발급한 뒤에, branch 별로 패치나 service pack것과 같은 원리이다.

이런 release branch는 웹서비스 (face book 등)의 경우, 하나의 코드가 하나의 서비스에만 배포가 되기 때문에, 별도의 release branch를 유지하는 것 보다는release tagging을 한 후에, 이슈가 있을 경우 이슈를 fix한 최신 버전을 다음release 때 배포 하는 방식등을 주로 사용하기 때문에, release branch의 효용성이 패키지 소프트웨어에 비해서 떨어진다.

6)   QA branch

다음으로 사용할 수 있는 branch중 하나는 QA branch 이다. production 으로 (상품화 하기 위해) 개발팀에서 source code를 freezing 하고 QA 팀에 해당 제품을 넘겨서 테스트를 의뢰할 경우, QA팀에서 계속해서 버그 수정 요청을 해오게 된다. 이 때, 버그를 main branch에서 계속해서 수정을 하게 되면, main 개발에 많은 수정이 가해지기 때문에 개발이 어려워질 수 있다. (merge도 많이 발생하고, 번거로움) 그리고, main branch에서는 개발이 계속 진행되기 때문에, feature 변경이나 기타 수정이 있을 경우 QA에 의해서 reporting된 버그가 제대로 재현이 되지 않는 경우가 있을 수 있다. 그래서 QA에 넘기기 전에 QA branch를 따고, 버그에 대한 fix를 이QA branch에서 수행 및 반영 한다.

QA가 모두 완료되고 나면, 이 QA branch에 있는 변경 내용을 다시 main branch로merge하여, bug 수정 내용을 반영하도록 한다.

7)    소스 코드 브랜치 관리 전략

앞서 살펴본 바와 같이 사실 branch는 main code (branch)의 복사본이다. 용도에 따라서 얼마든지 만들 수 있고, 필요에 따라 merge를 할 수 도 있다. 그러나 패키지냐, 웹서비스냐와 같은 소프트트웨어의 종류와, 팀의 크기와 구조 , 릴리즈 정책등에 따라서 어떠한 branch를 언제 사용할지가 다르기 때문에, 개발하는 소프트웨어의 형상에 따라서 알맞은 branch 전략을 결정해야 한다.

또한 VCS 제품에 따라, 몇몇 제품의 경우에는 이미 자사 제품 특성에 맞는 브렌치 구조에 대한 레퍼런스를 제공하거나, 또는 성격에 따라 맞는 레퍼런스 브렌치 모델이 많이 공개되어 있다.

또 다른 방법으로는 오픈소스에서 사용하고 있는 소스 코드의 tree 구조를 레퍼런스 해보는 것도 좋은 방법이다.

다음은 몇 가지 유용한 브렌치 구조에 대해서 소개하도록 한다.

오픈소스 소스 코드 브렌치 관리 전략

다음은 요즘 많이 사용하는 VCS 솔루션의 하나인 git를 이용한 branch 전략이다.


[1]

이 브렌치 구조는 이미 git flow라는 이름으로, git를 사용하는 사람들에게는 널리 알려져 있는 브렌치 모델이다. 이 구조는 오픈소스와 같이 대규모의 분산된 개발 조직이 서비스나 오픈소스 제품을 만드는 구조에 매우 적합니다.

※ http://nvie.com/posts/a-successful-git-branching-model/

그리고, 이 구조대로, git의 저장소를 정의하고, 쉽게 사용할 수 있도록 git flow라는 오픈소스 도구를 제공한다. https://github.com/nvie/gitflow

개념을 살펴 보면, main branch에서는 개발을 진행하지 않고, 별도의 develop branch를 만들어서 여기서 개발을 진행한다. 기능 (feature) 별로 feature branch를 별도로 만들어서 개발을 진행하고 feature 개발이 완료되면, develop branch로 merge를 한다.

master와 develop branch의 관계는 재미있는 개념이 있는데, 바로 code review이다. 개발자가 코드 수정을 하였을 경우, Jenkins 등의 빌드 시스템에 통합되서 빌드되고, 테스트되어야 동작 여부를 확인할 수 있기 때문에, 어딘가 코드를 공유할 수 있는 장소가 필요하다. 즉 개발이 완료된 부분은 먼저 develop branch에 저장되서 컴파일 및 테스트를 끝내고, code review를 위해서 다른 개발자와 reivew를 하고 승인이 되면 그 때master branch로 반영이 되는 것이다.

release 시기가 되면, 별도로 release branch를 만든 후에, release에 필요한 각종configuration file 정리, 기타 메뉴얼이나 문서등을 합쳐서 release하고, release가 된 버전은 main branch에 반영한 후 tagging을 한다.

오픈소스 프로젝트에 보면, 소스 코드가 날짜나 버전 별로 open 이 되어 있는 형태를 본 기억이 있을 것이다. 이렇게 main branch는 외부 개발자 공개를 위해서 유지하고, 특별한 release가 있을때에만 반영하여, 항상 main branch에는 문제 없는 버전이 존재하도록 한다.

또한 혹시 main branch를 통해서 공개된 버전이 문제가 있을 경우에는 별도로 Hot fix (버그 수정용) branch를 만들어서 버그를 수정하고, 테스트를 끝낸 후에, 버그 수정 내용을 main branch에 반영하고 또한 함께, develop branch에도 반영한다.

 일반적인 소프트웨어 브렌치 관리 전략

다음은 git 이전에 많이 유행하였던, Subversion (이하 svn) 사용시 많이 사용하는 브렌치 구조이다. SVN에서는 크게 아래와 같이 세가지 브렌치를 메인으로 하여 코드를 저장하는 구조를 사용한다.



trunk 또는 head 라고도 하는데, 이 브렌치는 현재 개발이 진행중인 코드를 저장한다.

개발을 진행중에, 빌드나 또는 특정 날짜를 기준으로 tag를 따놓는데, 향후 빌드 실패나, 특정 날짜의 개발 내용으로 돌아가기 위해서 사용하며 이는 tags라는 브렌치 아래 들어간다. tag 명은 위의 그림에서와 같이 날짜를 사용할수 도 있으며, 또는 빌드 시스템의 빌드 #를 사용하기도 한다.

마지막으로 release 브렌치이다. Branches는 릴리즈시마다, 그 때의 소스코드 형상을 저장해놓는 구조이다.

이 브렌치 전략은 단일팀에서, 서비스나 제품을 릴리즈 주기를 통해서 릴리즈 하는 구조에 적절하며, 버그 수정이나 기능 개선은 다음 버전(release)에 포함 시키는 형태일 경우 적절하다. (별도의 패치를 위한 브렌치를 관리하는 전략이 없기 때문에)

8)   분산 형상 관리 시스템 Git

그러면 근래들어 많이 사용되고 있는 VCS 시스템인 Git에 대해서 알아보도록 하자.

Git는 기본적으로 분산 소스코드 관리 시스템이다. 모든 개발자가 중앙 저장소에 붙어서 작업을 하는 것이 아니라. 소스 코드가 여러개의 서버에 다른 복제본으로 존재할 수 있다. 먼저 이 분산 저장소의 개념부터 이해 해보도록 하자

중앙 집중형 저장소 (Centralized Version Control System)

중앙 집중형 저장소는, 코드가 저장 서버 단 한군데만 저장된다. 개발자가, 코드를 받아서 수정하고 저장하면, 그 내용이 바로 중앙 저장소에 반영된다 (당연 하지만). 즉 서버에는 항상 마스터 버전(최신 버전)의 소스코드가 저장되어 있다.

  그리고, 서버가 다운 되거나, 네트워크에 접속할 수 없다면 당연히 코드를 commit하거나 최신 코드를 내려받을 수 없다.



분산형 저장소 (Distributed Version Control System)

분산 저장소는 말 그대로, 소스코드가 하나의 중앙 서버가 아니라, 여러개의 서버나 여러개의 개발자 PC에 저장될 수 있으며, 각각이 소스 코드 저장소 (source repository)가 된다. 각 저장소에 저장되는 소스 코드는 같은 버전의 코드가 아니라 제각기 다른 브렌치 코드가 저장된다. 즉 서버 A에는 branch A,B,C 버전이, 서버 B에는 branch A,C,D버전과 같이 다른 브렌치 버전을 저장할 수 있다. 즉 각 저장소에 브렌치 버전이 모두 틀리고, 소스 코드를 access해서 가지고 오는 장소도 모두 다르기 때문에, 시스템 자체에서는 마스터 버전 (최신 버전이 항상 어느 곳에 저장되어 있는가)의 개념이 없다. 예를 들어 설명하자. 아래 그림과 같이 Developer Terry는 Server A에서 코드를 내려 받아서, 데이타 베이스 관련 모듈을 개발하고 있고, Michelle은 Server B에서 UI 관련 모듈을 개발하고 있다. 각자는 개발을 진행하면서, 수시로 각자 Server A와 Server B에 Commit을 하고 있다고 가정하자. Server A는 전체 시스템에서 데이타 베이스 모듈 부분은 가장 최시일테고, Server B에는 UI 모듈의 가장 최신 버전의 코드가 들어가 있을 것이다.

대신 각 모듈의 개발이 끝나면, Server A와 Server B의 코드를 merge하여, 개발 내용을 병합(합칠 수)할 수 있다.

즉 전체 시스템의 최신 소스 코드가 명시적으로 어느 한곳에 저장되어 있지 않는 구조이다.



이러한 형태로, 코드의 여러 버전을 여러 저장소에 분산해서 저장할 수 있기 때문에, 몇가지 장점이 올 수 가 있는데, 팀 단위나 기능 단위로 저장소를 분리해서 개발하거나 릴리즈 버전 단위로 저장소를 분리해서 개발할 수 있는 등 소스코드 버전관리에 많은 유연성을 가지고 있다.

또한 중앙 저장소의 개념이 없기 때문에, 특정 VCS 시스템이 장애가 나더라도, 내가 사용하고 있는 VCS만 문제가 없다면 개발을 계속할 수 있고,앞에서 언급한바와 같이 개발자의 Local PC에 VCS를 설치하여 네트워크 연결이 없는 상태에도 개발을 지속할 수 있다.

그리고 소스코드가 중앙 서버만이 아니라 여러 서버와 PC에 분산 되서 저장되기 때문에 서버 장애로 저장소가 손상된다고 해도, 다른 서버나 다른PC에서 소스코드와 History들을 모두 저장하고 있기 때문에, 중앙 서버 방식에 비해서 복구가 쉽다.

Git

DVCS는 Mercurial, Bazzar등 여러가지 제품이 있으나, 근래에는 git 가 가장 많이 사용되고 있다. Linux Kernel 프로젝트나, 안드로이드, Gnome, Ruby on Rails등이 Git를 사용하는 대표적인 프로젝트이다.

Git는 기존의 VCS에 비해서 설치가 쉽고, 속도가 매우 빠르다. 그리고, branch와 merge가 매우 빠르고 사용이 쉽다. 특히 merge의 경우 누가? 언제? 무엇을 어떤 부분을 merge했는지 까지 상세하게 추적이 가능하기 때문에, 오픈 소스와 같이 대규모 개발자가 동시에 개발을 진행하는 환경에서는 매우 유용하다. 오픈소스 개발자들이 network가 연결되어 있지 않은 상황에서도 언제 어디서나 개발을 할 수 있다. 그리고, 자기만의 개발branch로 개발을 하다가 main branch로 merge도 쉽다. 아마 이런 장점이 git를 major VCS로 만든게 아닌가 싶다.

 

그러면 간단하게, git의 간략한 기본 사용법을 알아보자.



1)     Repository 생성 (init과 clone) - git에서 init 명령은 local에 새롭게 저장소를 만드는 명령이다. 이를 통해서 새롭게 git저장소를 만들 수 있다.
만약에 원격에 있는 서버의 저장소를 복제해서 로컬에 만들려면
git clone 사용자명@서버주소:"저장소 경로"
명령어를 사용하면 된다.

2)     파일 추가
다음으로 파일을 추가해보자. 위의 예제에서 echo 명령을 사용하여, init.html과, index.html 파일을 생성하였다. 그리고git에 이 파일이 git에 commit이 될 예정이라고 mark를 해놓는다. 위의 예제에서는 index.html 파일만 먼저 commit을 하도록 한다.
git에서는 add의 개념을 이해하려면 먼저 staging area라는 개념을 이해해야 한다.



작업 디렉토리 (working dir)에서 작업을 한것은 내 local pc에만 반영된 내용이다. 이 내용을 저장소로 올리기 전에, git는staging(git에서는 index라는 이름으로 사용한다.) 이라는 개념을 제공한다. 소스 코드를 저장소에 최종 반영하기 전에 두 단계를 거치는 two-phase commit을 사용한다.

staging이라는 개념은 작업 디렉토리에서 작업한 내용을 반영하기 전에, 최종으로 확인하는 중간 단계 정도로 생각하면 된다. 작업 디렉토리에서 작업한 내용 중 commit할 내용을 미리 "add" 명령어를 통해서 stage에 반영한 후에, stage에 있는 내용을 commit전에, 저장소내의 코드와 비교(diff)하면서, commit을 할 수 있다.

작업 영역과 (working directory)와 stage영역간의 비교는 
"git diff" 명령어를 통해서 가능하고  stage와 master 버전에 저장된 코드의 변경 사항은"git diff --cached"  (또는 git diff --staged) 명령을 이용해서 비교가 가능하다.

작업 디렉토리에서 바뀐 내용을 몽땅 한꺼번에 commit하는 것이 아니라, feature별이나 특정 그룹 (기능이나 FIX별 또는 모듈별)staging에 이동 시킨후, 하나하나 검증하면서 그룹별로 commit이 가능하다.

3)     저장소에 반영 (commit)
add
를 통해서, 변경본 반영 리스트를 작성하고, diff등을 통해서 확인이 끝나면, 이를 저장소에 반영해야 한다. 반영은commit 명령어를 사용하며, commit에 대한 comment (변경 내용)을 같이 넣는다.
git commit -m "변경 내용 description"

4)     변경 내용을 원격 저장소에 반영 (push)
앞 단계 까지 끝났으면, 소스코드의 변경 내용은 내 local pc에 있는 git 로컬 저장소에 반영되었다. git는 앞서도 설명했지만 분산저장소이기 때문에, commit을 한다고 해서, 서버에 코드가 저장되지 않는다. 기본적으로 commit은 로컬 저장소에 반영이 되지만, 서버의 원격 저장소에 코드를 반영하려면 별도의 반영 작업이 필요하다. 반영은 push 명령어를 사용한다.다음과 같이 push 명령을 사용하여, 원격 서버에 반영한다.



git push origin "브렌치명"
예) git push origin master (master branch로 push하는 명령)
※단 이 경우는 처음에 저장소를 만들때, git clone을 통해서 원격 저장소로 부터 코드를 읽어와서 로컬 저장소를 만들었을 경우이다.

만약에 원격 저장소로 부터 clone을 해서 만든 경우가 아닐때, 원격 저장소로 code를 밀어 놓고자 한다면, 원격 저장소를 정의해줘야 한다. 원격 저장소를 정의 하는 방법은
git remote add "원격저장소명" "원격저장소주소"
예) git remote add zipkin https://github.com/twitter/zipkin.git 
zipkin이라는 이름으로 https://github.com/twitter/zipkin.git URL에 있는 원격 저장소를 등록하였다. 여기에 push를 하려면, git push zipkin master (zipkin 원격 저장소 master branch에 push)

분산 저장 VCS 답게, 원격 저장소는 하나가 아닌 여러 개를 git remote add 명령을 통해서 추가 할 수 있고, 등록되어 있는 원격 저장소는 git remote -v 명령을 통해서 조회해볼 수 있다.

5)     브렌치 관리
소스코드 브렌치에 대한 개념은 앞에서 설명하였기 때문에 별도로 설명하지 않고, 명령어 사용법만 설명한다. 현재 코드에서 브렌치를 생성하려면

git branch "브렌치명" 을 사용한다..
예) git branch bugFix
bugFix라는 이름으로, 현재 코드에서 branch 만들기

그리고 현재 작업중인 브렌치를 이동하려면
git checkout "브렌치명" 을 사용한다.
예) git checkout master
master 브렌치로 이동

6)   merge

merge는 다른 브렌치의 내용을 현재 작업 중인 브렌치로 합쳐 오는 작업이다.
예를 들어 내가 master 브렌치에서 작업을 하고 있을때, 예전에 버그 수정을 위해 만들었던 bugFix라는 브렌치의 내용을 현재 브렌치에 반영하고 싶을 경우, master 브렌치에서 "git merge bugFix" 라는 명령을 사용하면 bugFix 브렌치의 변경 내용을 master 브렌치에 반영하게 된다.

이 때, 서로 변경 내용이 다르거나, 같은 코드 라인을 수정하였을 경우 충돌 (conflict)이 발생하는데, 충돌이 발생한 경우,직접 충돌 부분을 수정한 후, git add를 통해서 수정한 파일을 넣고, git commit을 통해서 최종 반영한다.

7)   원격 저장소의 변경 내용을 읽어오기 (pull & fetch)

반대로, 원격 저장소에서 다른 사람들이 작업했던 내용을 내 로컬저장소로 가지고 오려면 pull 과 fetch 라는 두 가지 방법이 있다.

먼저 fetch ("git fetch")의 경우, 원격 저장소의 업데이트 된 내용을 별도의 브렌치로 읽어 온다. 실제 내가 로컬에서 작업중인 로컬 저장소에는 반영되지 않는다. 반영을 하려면 원격 저장소에서 읽어온 내용을 merge를 통해서 내 작업 영역에 반영해야 한다 이해를 돕기 위해서 아래 그림을 보자.



원격 저장소의 v1 버전에서 clone을 받아서 로컬 저장소에서 개발을 시작하였다. 로컬에서 여러번의 commit을 통해서, v4버전까지 개발을 진행하였다.

그 상태에서, 원격 저장소의 변경 내용을 업데이트 하기 위해서 fetch를 하면, 원래 clone을 하였던 원격 저장소의 브렌치(master)의 최신 코드를 로컬로 복사해서 origin/master라는 이름의 브렌치에 업데이트를 한다. 내 작업 영역은 여전히 v4이고, 원격 저장소의 변경 내용은 반영되지 않았다.

이를 반영하려면 "git merge origin/master"를 해주면 merge를 통해서 내 작업 영역에 반영된다. (v5 버전상태)

pull ("git pull")은 한마디로, fetch + merge다. pull 명령어 하나로 자동으로 fetch와 merge를 한꺼번에 해주는 명령어 이다. 아래 그림을 보자, 처음에 원격 저장소에서 v1 버전을 clone으로 내려 받아서 개발을 하다가, v4 버전에서 원격 저장소의 코드를 pull 해주면, 원격 저장소의 clone을 한 원본 저장소와, 로컬의 저장소의 현재 브렌치를 merge하여 새로운 버전으로 만들어 준다.



8)   태깅

git에서 태깅은 매우 간편하다.

git tag -a "태그명" -m "태그 설명"

예) git tag -a "build1109" -m "July.15 빌드 태그".

태깅한 버전으로 이동하려면, 브렌치 이동과 마찬가지로 checkout을 사용하면 된다.

git checkout "태그명"

만약 그 태그 버전으로 부터 무엇인가를 작업하려면, 해당 태그로 이동한 후에, git branch를 이용하여, 그 버전에서 부터branch를 따서 작업을 하거나 clone등을 해서 작업을 하는 것이 좋다. 그리고, 태그를 다른 사람과 공유하려면, 브렌치나 코드와 마찬가지로, 태그를 서버로 push해줘야 한다.

git push origin "태그명"

9)   Rebase

git의 rebase는 git만의 고유 기능이다. merge와 유사한 기능이지만, 코드 변경 History를 조금더 깔끔하게 정리해주는 기능이라고 생각하면 된다.

다음과 같은 브렌치가 있다고 하자. V3에서 브렌치를 생성하여 V4를 만들어내었고, master에서는 commit을 진행하여 V5로 진행되었다고 하자. 이 상태에서 V4의 변경 내용을 V5로 합치고자 한다.



일반적인 경우에는 merge를 한다. 아래와 같이 merge를 하는 경우에는 V6 버전이 새로 생기고, V4 branch와 내용과history가 모두 유지 된다.



반면 rebase 명령을 수행할 경우, merge와 마찬가지로, 코드를 master branch로 합치지만, 기존의 V4 branch는 없어지고, V4에 작업된 내용 (변경 History)는 마치 master branch의 변경 history인 것 처럼 하나로 합쳐진다.



유용한 git 관련 도구들

git의 경우, 요즘 널리 사용되는 만큼 지원되는 도구들도 많다. 여기서 몇몇 유용한 git 지원 도구들에 대해서 살펴보도록 하자.

1)   git-gui & gitk

이 두가지 툴을 gui 툴로, git를 설치하면 기본적으로 설치 된다. git-gui는 git에서 commit을 하는 것을 도와주고, gitk는git 저장소를 gui 기반으로 browsing할 수 있게 해준다.  

2)   gerrit

gerrit는 오픈소스 코드리뷰 도구로 (https://code.google.com/p/gerrit/) 에서 다운로드 바을 수 있으며, git를 사용시, 웹 기반으로 코드 리뷰를 가능하도록 해준다.

master branch와 dev branch를 갖는 상태에서, dev branch에서 개발을 한 후, dev branch로 push를 하면, jenkins등과 같은 자동 빌드 도구에서 컴파일 및 테스트를 수행한 후에, gerrit을 통해서 코드 리뷰 요청을 생성하고, 코드 리뷰어가 리뷰를 끝내면, 코드를 master로 push하는 시나리오를 만들 수 있다. UI는 좀 떨어지긴 하지만, 저렴한 비용으로 코드리뷰 도구를 사용할 수 있다.



3)   gitLab

git lab 역시 오픈 소스이다. 코드 리뷰뿐만 아니라, git 저장소에 대한 browsing, 프로젝트 관리, 이슈 관리등 다양한 기능을 제공한다.



지금까지 git에 대해서 가장 기본적인 기능만을 소개하였다. 이외에도 상당히 많은 기능이 있기 때문에, git의 경우에는 꼭 별도의 서적을 참고해 보기를 권장한다. 
※ Pragmatic Version Control Using Git, 저자 Travis Swicegood , ISBN-10: 1934356158
※ 참고할만한 글

Ÿ   git에 대해서 간략하게 설명해놓은 문서http://rogerdudler.github.io/git-guide/index.ko.html

Ÿ   git에 대해서 설치 없이 간단하게 배울 수 있는 온라인 튜토리얼 
http://pcottle.github.io/learnGitBranching/

사실 git를 사용해보면,그 속도도 빠르고 branch 생성이나 merge도 쉬워서 참 유연한 툴이구나 생각하게 된다. 반대로 생각해보면 유연하다는 것은 사용할 수 있는 용도가 많다는 것이고, 잘 모르면 어떤 방향으로 사용해야 할 지 정하기가 매우 애매하다는 이야기가 된다. svn과 같은 경우는 일반적으로 사용하는 branch 전략이 정해져 있어서 learning curve도 낮아서 쉽게 사용할 수 있지만, svn과 같은 centralized VCS에 익숙해져 있는 사람은 git를 접하면 개념을 잡는데 한참 시간이 소요되다가, 결국은 centralized VCS와 같은 형태로 사용하는 경우가 종종 있다.

 git가 되었건, svn이 되었건 간에, 툴의 사용법 보다는 소스코드의 버전 관리의 본질을 제대로 이해하고, 팀과 프로젝트 성격과 회사내의 릴리즈 정책에 맞는 VCS를 선택하고 그에 맞는 브렌치 전략을 제대로 수립하고 적용해 나가는 것이 더욱 더 중요하다.  특히 조직의 규모가 커지면 커질 수 록, 그리고 릴리즈하는 버전의 다양성이 많을 수 록, 효과적인 브렌치 구조의 정의는 개발 환경의 효율성에 아주 지대한 영향을 미치게 된다.

또한 이 글을 쓰는 내내 머릿속에 멤도는 생각은 수년전만 해도 개발자가 사실 이러한 VCS나 branch 전략에 대해서 크게 신경 쓸 필요가 없었다.일반적인 팀 단위 프로젝트에 들어가서 Check out, update, commit, merge 정도만 잘하면 되었으니까는, 그러나 앞서서 git에서 살펴보았듯이.이미 소스 코드 관리 전략은 오픈소스등의 영향을 많이 받아서 분산형이 대세다. 분산을 하다보니, 상대적으로 다른 개발자나 개발팀의 간섭을 덜 받으면서 별도의 브렌치에서 작업을 해서 코드 작업을 할 수 있다는 장점도 있지만, 그만큼 개발자가 이제는 코드 관리에 대한 깊숙한 이해를 가져야 한다는 것이다. 

블로그 이미지

kuku_dass

,

Click이라는 Event가 있다면, Event는 Eventhandler를 가져야 한다.

그 Event가 발생했을 때, 어떻게 처리될지 정의를 Handler가 가지고 있으니까.


결국


Click이라는 Event가 있다면, 우리는 Click += new EventHandler...

와 같이, Click이라는 Event에 그에 맞는 handler를 넣어 줄 것이다.


--------------------------------------------------------------------


EventHandler 란? - Event를 어떻게 다룰지 정의한 함수(메소드)를 가리키는 함수포인터.


(C언어) : 함수 포인터를 만들어주되, 함수 포인터 이름 자체를 하나의 Type으로

사용할 수 있도록 typedef 키워드를 함께 사용 한다.


typedef void (*EventHandler)(void)


EventHandler handler; // EventHandler 타입의 변수



--------------------------------------------------------------------

C#에서 EventHandler를 만들고, Event를 만들어서 사용해보자.



(1) 

C#에서는 아래와 같이 편리하게 Handler를 만들 수 있다.



delegate void EventHandler(void);


C언어의 코드와 비교해보면, 포인터 개념이 없고 Ref 개념이기 때문에,

'*' 는 사용하지 않고, typedef 를 써주는 대신 delegate만 사용해줬을 뿐이다.



(2) 

이제, EventHandler(함수, 메소드 포인터) 타입을 정의하였으니, 아래와 같이 

Handler를 사용할 수 있다.


delegate void EventHandler(void);


EventHandler handle = new EventHandler();



(3)

이제, Event를 만들어보자.


Event는 맨 위에서 말했던 것 처럼, event가 발생했을 때, 어떤 로직이 처리될지

그 로직을 정의해놓은 함수(메소드)를 바로 호출할 수 있도록, 그 로직을 포함하는

메소드를 가리키고 있는 함수포인터 값을 가지고 있어야 한다.


event = handler 메소드의 포인터 값.


즉, Event는 handler 값을 가질 수 있어야 함으로,

event자체의 타입은 EventHandler 타입이 될 것이다.



여기서, C#에서는 이 녀석이 Handler 자체를 말하려고 한 것인지, 아니면

Event 이고 값만 handler 를 갖으려고 하는 것인지 구분 할 수 있어야 한다.

event 키워드를 사용한다.




class Foo

{

 event EvenHandler Click;


 Operator+=(EventHandler ev)

  {

     this.Click = &ev;

  }...

};


이제 우리가 흔히 보던 Event 추가하는 코드를 작성 해보면,

-------------------------


void Mouse_Click(void){ cout << "Hello World" <<endl; }


Foo mouse;

mouse.Click += new EventHandler(Mouse_Click);



이제 위와 같이 해주면, 

Click 이라는 이벤트가 발생했을 때, Mouse_Click 메소드가 실행되고,

그 내의 로직이 실행 될 것이다.

--------------------------------------------------------------------



* 정리 *


1. c#에서는 event, eventHandler라는 개념이 있다.

   event : 특정 사건

   eventHandler : 특정 사건에 대한 로직.

   (특정 사건이 발생했을 때, 처리해야할 로직이 담긴 메소드의 포인터.)

   

2. EventHandler는 결국 함수 포인터이다.

   (typedef void (*EventHandler)(void)  == delegate void EventHandler(void) )


3. Event는 event 키워드를 사용하며, EventHandler타입으로 정의된다.

   event EventHandler Click;


4. event에 += 연산자 오버로딩 메소드를 통해, 핸들러를 추가해주면 된다.

'C,C++,C#' 카테고리의 다른 글

[c#] Serialization, Deserialization (직렬화, 역직렬화)  (0) 2016.09.06
블로그 이미지

kuku_dass

,

JSON 이란?

카테고리 없음 2015. 10. 7. 14:58

http://blog.naver.com/ksh81850/220296114304

http://cafe.naver.com/webappdev/450

http://cafe.naver.com/mcbugi/314254

블로그 이미지

kuku_dass

,


이벤트는 클래스내에 특정한 일(event)이 있어났음을 외부의 이벤트 가입자(subscriber)들에게 알려주는 기능을 한다. C#에서 이벤트는 event라는 키워드를 사용하여 표시하며, 클래스 내에서 일종의 필드처럼 정의된다.

이벤트에 가입하는 외부 가입자 측에서는 이벤트가 발생했을 때 어떤 명령들을 실행할 지를 지정해 주는데, 이를 이벤트 핸들러라 한다. 이벤트에 가입하기 위해서는 += 연산자를 사용하여 이벤트핸들러를 이벤트에 추가한다. 반대로 이벤트핸들러를 삭제하기 위해서는 -= 연산자를 사용한다. 하나의 이벤트에는 여러 개의 이벤트핸들러들을 추가할 수 있으며, 이벤트가 발생되면 추가된 이벤트핸들러들을 모두 차례로 호출한다.

다음 코드는 클래스(MyButton) 내에서 이벤트(Click)를 정의하고 이를 사용하는 예제이다.
(주: 고급문법의 C# delegate에서 C# event와 delegate와의 관계가 자세히 설명)
 

예제

// 클래스 내의 이벤트 정의
class MyButton
{
   public string Text;
   // 이벤트 정의
   public event EventHandler Click;

   public void MouseButtonDown()
   {
      if (this.Click != null)
      {
         // 이벤트핸들러들을 호출
         Click(this, EventArgs.Empty);
      }
   }
}

// 이벤트 사용
public void Run()
{
   MyButton btn = new MyButton();
   // Click 이벤트에 대한 이벤트핸들러로
   // btn_Click 이라는 메서드를 지정함
   btn.Click += new EventHandler(btn_Click);
   btn.Text = "Run";
   //....
}

void btn_Click(object sender, EventArgs e)
{
   MessageBox.Show("Button 클릭");
}



  • 이벤트 Click은 외부에서 엑세스할 수 있게 public 필드로 정의되어 있다.
  • MouseButtonDown() 메소드에서 이벤트를 외부로 보내기 전에 이벤트에 가입한 가입자들이 있는지 체크하기 위해 if (Click != null)을 사용한다.
  • 이벤트에 가입하거나 탈퇴하기 위해서는 += (subscribe) 또는 -= (unsubscribe)연산자를 사용한다.
  • 여기서 void btn_Click(object sender, EventArgs e) 메서드는 이벤트핸들러로 사용되고 있다.

출처 : http://www.csharpstudy.com/CSharp/CSharp-event.aspx


블로그 이미지

kuku_dass

,

디자인 패턴 중 WPF 많이 쓰이는 MVVM 패턴을 알기전에 MVC, MVP 패턴과의 차이점을 알 필요가 있다.

MVVM의 패턴과 유사한 패턴인 MVC, MVP 패턴과 차이점을 설명하기전에 공통적으로 쓰이는 Model과 View에 대해서 간략히 알도록 하자.

Model - 일종의 데이터(Data)라고 생각하면 된다. 데이터 이외에 데이터를 조작하는 간단한 로직이 추가 되기도 한다.

View - 디스플레이. 사용자에게 제공되어지는 UI Layer를 뜻한다. 보통 Application에서는 View는 CSS/HTML/XML/XAML 등으로 렌더링 된 화면을 가르킨다.

MVC, MVP, MVVM 패턴과 같은 프레임워크가 나오게 된 궁극적인 이유는?

한마디로 각 계층을 분리시킴으로써 코드의 재활용성을 높이고 불필요한 중복을 막기위한 이유이다.

Model과 VIew의 의존성을 어떻게 제어하느냐에 따라 각 패턴이 분류 된다.


1.MVC (Model - View - Controller)

모든 입력은 Controller 에서 처리된다. 입력이 Controller로 들어오면 Controller는 입력에 해당하는 Model을 조작(업데이트)하고, Model을 나타내어줄 View를 선택한다.

Controller는 View를 선택할수 있기 때문에 하나의 Controller가 여러개의 View를 선택하여 Model을 나타내어 줄 수 있다.

이때 Controller는 View를 선택만하고 업데이트를 시켜주지 않기때문에 View는 Model을 이용하여 업데이트 하게된다. 

Model을 직접 사용하거나 Model에서 View에게 Notify해주는 방법, View에서 Polling을 통해 Model의 변화를 알아채는 방법등이있다.

이와 같이, View는 Model을 이용하기 때문에 서로간의 의존성을 완벽히 피할 수 없다는 단점이 있고, 좋은 MVC 패턴이라 함은 View와 Model간의 의존성을 최대한 낮게한 패턴이 좋은 패턴이라 할 수 있다.

2. MVP(Model - View - Presenter)

MVC 패턴과 다르게 입력이 View에서 처리된다. Presenter는 View의 인스턴스를 갖고 있으며 View와 1대1 관계이고, 그에 해당하는 Model의 인스턴스 또한 갖고 있기때문에 View와 Model 사이에서 다리와 같은 역할을 한다. View에서 이벤트가 발생하면 Presenter에게 전달해주고 Presenter는 해당 이벤트에 따른 Model을 조작하고 그 결과를 바인딩을 통해 View에게 통보를 하여 View를 업데이트 시켜준다. MVC 패턴과는 다르게 Presenter를 통해 Model과 View를 완벽히 분리해 주기 때문에 View는 Model을 따로 알고 있지 않아도 된다는 장점이 있다. 단점으로는 View와 1대1 관계이기 때문에 View와의 의존성이 매우 강하다.

3. MVVM(Model - View - ViewModel)

ViewModel 뷰모델 말그대로 View를 나타내주기 위한 Model이라고 생각하면 된다. View보다는 Model과 유사하게 디자인 되며, View의 바인딩 될 때 가장 강력하다. 

MVP와 같이 View에서 입력이 처리된다.

MVVM 패턴의 가장 큰 장점이라 함은 Command와 Data Binding으로 MVP 패턴과 달리 View와의 의존성을 완벽히 분리 할 수 있다는 장점이 있다.

Command를 통하여 Behavior를 View의 특정한 ViewAction(Event)와 연결할 수 있으며, ViewModel의 속성과 특정 View의 속성을 Binding 시켜 줌으로써 ViewModel 속성이 변경 될때마다 View를 업데이트 시켜줄 수 있다.




출처 ; 

http://hackersstudy.tistory.com/71

블로그 이미지

kuku_dass

,

출처 : http://talsu.net/entry/NETC-Serialization-Deserialization
작성자 : 탈수

.NET(C#) Serialization , Deserialization

serialization 이란 개체를 저장하거나 전송할 수 있는 형태로 개체의 상태를 변환하는 프로세스 이다그리고 serialization과 반대로 다시 개체로 변환하는 것을 deserialization이라 한다.

serialization을 사용하는 가장 큰 이유는 개체의 상태를 저장소에 보존 했다가 나중에 똑같은 복사본을 다시 만들거나 한 응용프로그램 에서 다른 응용프로그램으로 개체를 전송하기 위해서 이다.

.NET Framework에서는 제공하는 serialize 방식은 크게 2가지로 나눌 수 있다.

l  이진 serialization : 형식 정확도를 유지 하므로 응용 프로그램의 여러 호출간에 개체 상태를 유지 하는데 유용하다개체를 스트림디스크메모리네트워크 등으로 serialize할 수 있다.

l  XML serialization public 속성과 필드만 serialize하며 형식 정확도를 유지하지 않는다데이터를 사용하는 응용프로그램을 제한하지 않고 데이터를 제공하거나 사용하려고 할 때 유용하다. XML은 공개 표준이기 때문에 웹을 통해 정보를 공유할 때 적합하고 SOAP도 마찬가지로 공개 표준이다.

형식 정확도를 유지한다는 말은 개체를 serialize을 하고 다시 deserialize했을 때 원본의 개체의 정확한 복제본이 만들어 진다는 것을 의미한다.

 

이진 serialization

serialize하고자 하는 개체의 public, private필드 와 클래스 이름을 모두 바이트의 스트림 (Binary)로 변환하는 방식이다그 후 개체가 deserialize되면 원본 개체의 정확한 복제본이 만들어진다.

이진 serialization에는 3가지 방법이 있다.

 

기본 serialization

serialize하고자 하는 class Serializable특성으로 표시 하는 것으로 해당 클래스의 모든 부분을 serialize한다.

 

    [Serializable]

    public class MyObject

    {

        public int n1 = 0;

        public int n2 = 0;

        public String str = null;

    }

 

MyObject라는 class를 Serializable 특성으로 표시하였다.

 

MyObject의 객체를 serialize 하여 파일로 저장하는 예

 

MyObject obj = new MyObject();

obj.n1 = 1;

obj.n2 = 24;

obj.str = "Some String";

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin"FileMode.Create, FileAccess.Write, FileShare.None);

formatter.Serialize(streamobj);

stream.Close();

 

순서를 요약해보면

1.      인스턴스 생성.

2.      BinaryFormatter를 만든다.

3.      파일로 저장하기 위한 FileStream을 만든다.

4.      Formatter에게 stream과 개체를 매개 변수로 제공하고 serialize를 지시한다.

5.      stream을 닫는다.

 

이번에는 반대로 serialize된 개체를 반대로 deserialize로 복원 하는 예

 

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin"FileMode.Open, FileAccess.Read, FileShare.Read);

MyObject obj = (MyObject)formatter.Deserialize(stream);

stream.Close();

 

1.      BinaryFormatter를 만든다.

2.      저장된 파일을 stream으로 오픈한다.

3.      Formatter에게 stream을 제공하고 Deserialize를 지시하고 개체를 받는다.

 

선택적 serialization

class에서 serialize하지 않아야 하는 필드가 있을 때 해당 멤버변수를 NonSerialized특성으로 표시해 줌으로써 해당 변수가 Serialize되는 것을 방지할 수 있다.

 

    [Serializable]

    public class MyObject

    {

        public int n1 = 0;

        [NonSerialized]

        public int n2 = 0;

        public String str = null;

    }

 

MyObject클래스의 n2 멤버는 더 이상 serialize되지 않는다.

NonSerialized 특성을 적용했을 때와 적용하지 않았을 때 각각 serialize, deserialize하여 비교해보면 n2멤버의 값이 다름을 확인해 볼 수 있다.

결국 NonSerialized로 표시되지 않은 모든 필드는 serialize된다.

 

Custom serialization

Custom serialization은 serialization을 직접 제어하는 방식이다. Custom serialization에는 2가지 방법이 있는데

첫번째로는 serialization 도중과 이후에 데이터를 수정하는데 사용되는 메서드에 다음 특성을 적용한다.

l  OnDeserializedAttribute

l  OnDeserializingAttribute

l  OnSerializedAttribute

l  OnSerializingAttribute

 

    // This is the object that will be serialized and deserialized.

    [Serializable()]

    public class TestSimpleObject

    {

        // This member is serialized and deserialized with no change.

        public int member1;

 

        // The value of this field is set and reset during and

        // after serialization.

        private string member2;

 

        // This field is not serialized. The OnDeserializedAttribute

        // is used to set the member value after serialization.

        [NonSerialized()]

        public string member3;

 

        // This field is set to null, but populated after deserialization.

        private string member4;

 

        // Constructor for the class.

        public TestSimpleObject()

        {

            member1 = 11;

            member2 = "Hello World!";

            member3 = "This is a nonserialized value";

            member4 = null;

        }

 

        public void Print()

        {

            Console.WriteLine("member1 = '{0}'", member1);

            Console.WriteLine("member2 = '{0}'", member2);

            Console.WriteLine("member3 = '{0}'", member3);

            Console.WriteLine("member4 = '{0}'", member4);

        }

 

        [OnSerializing()]

        internal void OnSerializingMethod(StreamingContext context)

        {

            member2 = "This value went into the data file during serialization.";

        }

 

        [OnSerialized()]

        internal void OnSerializedMethod(StreamingContext context)

        {

            member2 = "This value was reset after serialization.";

        }

 

        [OnDeserializing()]

        internal void OnDeserializingMethod(StreamingContext context)

        {

            member3 = "This value was set during deserialization";

        }

 

        [OnDeserialized()]

        internal void OnDeserializedMethod(StreamingContext context)

        {

            member4 = "This value was set after deserialization.";

        }

    }

 

각 특성을 적용한 메서드에 원하는 동작을 구현하면 된다.

 

두번째로는 해당 개체에 ISerializable인터페이스를 구현하면 된다.

ISerializable인터페이스 구현에는 개체가 deserialize될 때 사용되는 특별한 생성자 및 GetObjectData 메서드가 포함되어 있다.

 

    [Serializable]

    public class MyObject : ISerializable

    {

        public int n1;

        public int n2;

        public String str;

 

        public MyObject()

        {

        }

 

        protected MyObject(SerializationInfo info, StreamingContext context)

        {

            n1 = info.GetInt32("i");

            n2 = info.GetInt32("j");

            str = info.GetString("k");

        }

        [SecurityPermissionAttribute(SecurityAction.Demand,

        SerializationFormatter = true)]

 

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)

        {

            info.AddValue("i", n1);

            info.AddValue("j", n2);

            info.AddValue("k", str);

        }

    }

 

serialization시에는 구현된 GetObjectData메서드가 호출 되는데 이는 SerializationInfo를 직접 채워야 한다이름 및 값 쌍으로 serialize될 변수를 추가하며 모든 텍스트를 이름으로 사용할 수 있다. SerializationInfo에 추가되는 멤버 변수를 자유롭게 결정할 수 있다.

dserialization시에는 직접 SerializationInfo  StreamingContext를 받는 특수 생성자를 만들어야한다이것이 누락 된 상태에서 deserialize하려고 하면 예외를 trow한다.

 

XML  SOAP Serialization

XML serialization은 개체의 public 필드와 속성 또는 메서드의 매개변수와 반환 값을 XML스트림으로 serialize한다. XML serialization을 사용하면 저장이나 전송을 위해 직렬형식으로 변환되는 public 속성 및 필드가 있는 강력한 형식의 클래스가 만들어 진다.

XML serialization의 특징

l  XML은 공개 표준이기 때문에 XML 스트림은 플랫폼에 관계없이 필요에 따라 모든 응용 프로그램에서 처리될 수 있다.

l  XML serialization은 SOAP 사양과 일치하는 XML 스트림으로 개체를 serialize 하는 데 사용할 수도 있다.

l  개체를 serialize하거나 deserialize하기 위해서는 XmlSerializer클래스를 사용한다.

l  serialize된 데이터에는 데이터 자체와 클래스의 구조만 포함된다.

l  public 속성 및 필드만 serialize될 수 있다속성에는 public 접근자(get, set)가 있어야 한다. public이 아닌 데이터를 serialize해야 하는 경우 이진 serialization을 사용한다.

l  class에는 XmlSerializer에 의해 serialize될 기본 생성자가 있어야 한다.

l  메서드는 serialize될 수 없다.

 

개체 XML serialize

다음은 개체를 XmlSerializer를 이용하여 serialize한다.

 

    public class MyObject

    {

        public int n1 = 0;

        public int n2 = 0;

        private int n3 = 0;

        public String str = null;

 

        public MyObject()

        {

            n1 = 1;

            n2 = 2;

            n3 = 3;

            str = "Xml Serialization";

        }

    }

   

    class Program

    {

        static void Main(string[] args)

        {

            MyObject myObject = new MyObject();

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyObject));

            StreamWriter writer = new StreamWriter("myObject.xml");

            xmlSerializer.Serialize(writer, myObject);

            writer.Close();

        }

    }

 

1.      serialize하고자 하는 인스턴스 생성

2.      XmlSerializer를 생성자에 serialize하고자 하는 인스턴스의 타입을 전달하여 생성

3.      기록할 stream생성

4.      XmlSerializer에게 stream과 개체를 전달하여 serialize지시

 

위의 결과 물인 myObject.xml에는 다음과 같은 내용이 있다.

 

<?xml version="1.0" encoding="utf-8"?>

<MyObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <n1>1</n1>

  <n2>2</n2>

  <str>Xml Serialization</str>

</MyObject>

 

private 멤버와 메서드의 정보는 기록 되지 않는 것을 확인할 수 있다.

 

개체 XML deserialize

이번에는 myObject.xml을 다시 deserialize해본다.

 

    class Program

    {

        static void Main(string[] args)

        {

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyObject));

            StreamReader reader = new StreamReader("myObject.xml");

            MyObject myObject = (MyObject) xmlSerializer.Deserialize(reader);

 

            Console.WriteLine(myObject.n1);

            Console.WriteLine(myObject.n2);

            Console.WriteLine(myObject.str);

        }

    }

 

1.      XmlSerializer를 생성자에 deserialize하고자 하는 인스턴스의 타입을 전달 하여 생성

2.      stream을 읽어 들이는 reader 생성

3.      XmlSerializer에게 stream을 전달하여 deserialize를 지시하고 리턴되는 Object를 복원하고자 하는 타입에 맞게 캐스팅 하여 받아 낸다.

 

개채를 SOAP 인코딩된 XML 스트림으로 Serialize하기

SOAP 메시지는 XML을 사용하여 생성되므로 XmlSerializer를 사용하여 클래스를 serialize하고 인코딩된 SOAP 메시지를 생성할 수 있다.

SOAP 인코딩된 XML serialize하기 위해서는 기존의 XML serialize에서 XmlSerializer생성자 매개 변수에 serialize 하고자 하는 인스턴스의 타입을 전달 했던 것과 달리 SoapReflectionImporter를 만들고 serialize된 클래스의 형식으로 ImportTypeMapping 메서드를 호출하여 XmlTypeMapping을 만든뒤 XmlSerializer생성자 매개 변수에 전달 한다.

 

    class Program

    {

        static void Main(string[] args)

        {

            MyObject myObject = new MyObject();

 

            XmlTypeMapping xmlTypeMapping =

                      new SoapReflectionImporter().ImportTypeMapping(typeof (MyObject));

 

            XmlSerializer xmlSerializer = new XmlSerializer(xmlTypeMapping);

            StreamWriter writer = new StreamWriter("myObject.xml");

            xmlSerializer.Serialize(writer, myObject);

            writer.Close();

        }

    }

 

기존의 XML serializer에서 XmlSerializer의 생성방식만 변경되었다.

 

이때 결과물 myObject.xml에는 다음과 같은 내용이 있다.

 

<?xml version="1.0" encoding="utf-8"?>

<MyObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1">

  <n1 xsi:type="xsd:int">1</n1>

  <n2 xsi:type="xsd:int">2</n2>

  <str xsi:type="xsd:string">Xml Serialization</str>

</MyObject>

 

SOAP형식으로 인코딩된 데이터가 있음을 확인할 수 있다.

블로그 이미지

kuku_dass

,

DFS,BFS

알고리즘 2015. 7. 9. 10:52

DFS : 깊이 우선탐색.

 백트래킹에 사용되며, 이진트리가 있다고 가정 하면,

 

root - L - R 순으로 방문한다.

 

BFS : 너비 우선 탐색.

그래프, 트리 등의 자료구조에 있어서, 현재 위치에서

연결된 노드들. 즉 방문가능한 노드들을 모두 방문한 후에, 다음 Depth로 진행한다.

 

  1

 2 3

45 67

이 있다고 하면,

DFS : 1 2 4 5 3 6 7

BFS : 1234567

 

'알고리즘' 카테고리의 다른 글

Algorithm - 순열과 조합  (0) 2016.08.29
XOR  (0) 2015.06.12
KMP 알고리즘 (문자열 패턴 검색)  (0) 2015.05.29
[알고리즘] 퀵 정렬(Quick Sort)  (0) 2014.12.01
블로그 이미지

kuku_dass

,

XOR

알고리즘 2015. 6. 12. 14:14

XOR

 

: x,y가 있을 때, 각 비트가 서로 다르면 1,  서로 같으면 0

 

x : 101

y : 011

------- XOR

     110

 

NOT(~), OR(|) , AND(&) 로 XOR 표현하려면?

 

- XOR = (x'y + xy')

하나씩 의미를 확인해보자.

 

x'y : x의 반대와 y의 AND. 즉 x'y의 결과 값은, x의 반대의 비트와 y 비트 중 똑같이 1인 곳의 값이 1이 된다.

x' : 010

y : 011

-------- AND

    010

즉 , 의미는 x,y 두 비트를 비교했을 때, 값이 서로 다면서, x의 비트가 0인곳의 위치가 , 결과적으로 1이 나오게 하는 것이다.

 

반대로 xy'은, x,y 원본의 비트가 서로 다른데, y의 비트값이 0인 위치가 1이 되도록 하는것이 xy'이다.

 

결론

 

XOR = (x'y + xy') 은

(x,y의 비트값이 서로 다른데, x쪽이 0인 위치 + x,y의 비트값이 서로 다른데, y쪽 비트가 0인 위치) 를 더해준 값이된다.

(어차피 둘다 1이여도, XOR는 0 이 되야 하기 떄문에,  x,y의 비트가 서로 다르면서 한쪽이 0인 부분만 찾아서 합침으로써, 결과를 얻는다)

'알고리즘' 카테고리의 다른 글

Algorithm - 순열과 조합  (0) 2016.08.29
DFS,BFS  (0) 2015.07.09
KMP 알고리즘 (문자열 패턴 검색)  (0) 2015.05.29
[알고리즘] 퀵 정렬(Quick Sort)  (0) 2014.12.01
블로그 이미지

kuku_dass

,

KMP


- 접두부, 접미부, 경계 세개의 개념이 중요.


Ex)

BAABABAAC 라는 패턴이 존재 할 때.

C라는 마지막글자에서 틀렸다면,

맞은 글자들을 대상으로, 위와같이 확인한다.


BAA  BA  BAA

접두부 경계 접미부


접두부와 접미부가 같으면서, 경계의 길이가 최소가 될때의

접두.미부의 길이를 최대 너비라 한다.



- 알고리즘의 요지

 : 100글자의 문자열 S, 10글자의 P가 있을 때,


S의 처음부터 하나씩 다 확인해볼필요가 없이,

확인할 필요가없는 부분은 점프를 해서, 성능향상을 얻는다.


1) S와 P를 한글자씩 비교해 나간다.

 1.1) 끝까지 모두 같으면 패턴을 찾은 것이다.

 1.2) 틀린 부분의 위치를 k라 할 때.

      1~k-1까지의 패턴값을 가지고, 경계를 찾는다.


2) 경계의 길이가 최소가 되는

   접두부,접미부를 구한 다음. 

   접두부를 접미부 시작위치로 옮긴 후 패턴을 다시 시작해서 비교 한다.(

(출처 : http://carstart.tistory.com/143)


일치한 글자의 수 n 0 1 2 3 4 5 6 7 8
문자 c B A A B A B A A  
경계의 길이가 최대가 되는 접두.미사의 길이 ( 접두 = 접미사의 길이) L -1 0 0 0 1 2 1 2 3
이동해야할 거리 S
(s = n-L)
1 1 2 3 3 3 5 5  

이동거리 테이블을 만들어야 한다. 위와같이..


우선, 맞는글자가 하나도 없는 경우의 L은 -1이다.: 공식 s=n-L에 맞추기 위해서는 정해져있다 값이.


위와 같이 처음에 테이블만 한번 구성 한 후,

패턴이 몇글자맞았는지에 따라 옆으로 S만큼 이동하면서 패턴이 다 맞는경우를 찾으면된다.

 

 

예제 문제)

 

집합 S는 N가지 문자열의 집합이다. Q개의 문자열이 주어질 때, 각 문자열의 부분 문자열이 하나라도 S에 속하는지 아닌지 판별하는 프로그램을 작성하라.

예제를 예로 들면 S= {"www","woo","jun"}일때, “myungwoo”의 부분 문자열 중 하나인 “woo”가에 속하므로 답은 ‘Y’이고, “hongjun”의 부분 문자열 중 하나인 “jun”이 에 속하므로 답은 ‘Y’이다. 하지만 “dooho”는 어떤 부분 문자열도 S에 속하지 않으므로 답은 ‘N’이다. 이 답을 이어 붙여서 “YYN”을 출력하면 된다.

 

입력

첫째 줄에는 테스트케이스의 개수 T가 주어지며 둘째 줄부터 각 테스트케이스가 주어진다.

각 테스트케이스의 첫째 줄에는 N(1 ≤ N ≤ 1,000)이 주어진다.

다음 N개의 줄에는 S에 속하는 길이 100 이하의 문자열들이 주어진다.

다음 줄에는 Q(1 ≤ Q ≤ 1,000)가 주어진다.

다음 Q개의 줄에는 답을 판별해야 하는 길이 10,000 이하의 문자열들이 주어진다.

입력으로주어지는 문자열은 모두 알파벳 소문자로 이루어져 있다.

 

출력

각 테스트케이스 마다 정답을 의미하는 Q자리의 문자열을 출력한다.

 

입력 예제

1

3

www

woo

jun

3

myungwoo

hongjun

dooho

출력 예제

YYN

 

main.cpp

input_test.txt

 

 

'알고리즘' 카테고리의 다른 글

Algorithm - 순열과 조합  (0) 2016.08.29
DFS,BFS  (0) 2015.07.09
XOR  (0) 2015.06.12
[알고리즘] 퀵 정렬(Quick Sort)  (0) 2014.12.01
블로그 이미지

kuku_dass

,