이 문제는 유명한 퍼즐인 스도쿠를 구현하면 된다.

스도쿠가 풀리는 입력만 주어지기 때문에 퍼즐을 완성하는 알고리즘만 잘 구현한다면 꽤 쉽게 풀 수 있다.

 

실제 스도쿠 퍼즐을 사람이 풀기 위해선 다양한 전략, 기법이 필요하지만 컴퓨터가 풀기 위해선 가능한 숫자들을 다 try & error 하는 식으로 해도 빠르게 풀 수 있기 때문에, 백트래킹 + 주먹구구식 대입으로 구현하면 될 것 같다.

 

문제는 효율적인 백트랙킹을 위해 데이터를 어떤식으로 저장해야 하는가이다.

 

각 빈칸에 1~9 의 숫자가 들어갈 수 있는지 확인을 위해, row(열), col(행), box(정사각형) bool 타입 배열에 이미 들어간 숫자를 저장하였고, 각 빈칸에 들어갈 수 있는 모든 숫자 후보들을 다 저장하기 위해 2차원 벡터를 이용하였다.

vector< vector< pair< pair<int,int> ,int> > > v;

안쪽 pair<int,int>      : y,x 좌표

바깥쪽 pair의 second : 가능한 숫자 후보

 

이렇게하면 안쪽 벡터는 같은 빈 칸의 가능한 모든 숫자 후보를 모두  저장할 수 있다.

데이터만 잘 저장해놓으면 재귀함수를 이용해 쉽게 백트래킹을 구현하여 풀 수 있다.

 

 

 

소스코드

#include<stdio.h>
#include<vector>
using namespace std;

int board[10][10];
bool cols[10][10], rows[10][10], box[10][10];
vector< vector< pair< pair<int,int> ,int> > > v;

bool chk(int y,int x, int num){
    if(rows[y][num] || cols[x][num] || box[3*(y/3) + (x/3)][num]){
        return false;
    }
    return true;
}

bool solve(int p){
    if(p == v.size()){
        return true;
    }

    for(int i=0; i<v[p].size(); i++){
        int y = v[p][i].first.first, x = v[p][i].first.second, num = v[p][i].second;
        if(chk(y,x,num)){
            rows[y][num] = cols[x][num] = box[3*(y/3)+(x/3)][num] = true;
            board[y][x] = num;
            
            if(solve(p+1)){
                return true;
            }
            
            rows[y][num] = cols[x][num] = box[3*(y/3)+(x/3)][num] = false;
            board[y][x] = 0;
        }
    }
    return false;
}
int main(){
    
    for(int i=0;i<9;i++){
        for(int j=0;j<9; j++){
            scanf("%d", &board[i][j]);
            if(board[i][j] != 0){
                rows[i][board[i][j]] = cols[j][board[i][j]] = box[3*(i/3) + (j/3)][board[i][j]] = true;
            }
        }
    }
    for(int i=0;i<9;i++){
        for(int j=0;j<9; j++){
            if(board[i][j] == 0){
                vector< pair< pair<int,int>, int> > tmp;
                for(int num=1; num<=9; num++){
                    if(chk(i,j, num)){
                        tmp.push_back(make_pair( make_pair(i,j), num));
                    }
                }
                v.push_back(tmp);
            }
        }
    }

    solve(0);

    for(int i=0;i<9;i++){
        for(int j=0;j<9;j++){
            printf("%d ",  board[i][j]);
        }
        printf("\n");
    }

    return 0;
}

 

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 12865 평범한 배낭  (0) 2020.08.28
BOJ 15649~15650 N과 M  (0) 2020.08.20
BOJ 1194 달이 차오른다, 가자.  (0) 2020.08.11
BOJ 6087 레이저통신  (0) 2020.08.09
BOJ 11505 구간 곱 구하기  (0) 2020.08.06

이 문제는 BFS 를 이용해 푸는 미로 문제의 응용버전이라고 볼 수 있다.

이 문제만의 특징은 'A'~'F' 의 문이 있고, 문을 열기 위해선 각 알파벳에 맞는 소문자 'a'~'f' 열쇠를 가지고 있으면 통과할 수 있다.

 

다행히 문과 열쇠의 종류가 6개 밖에 안되기 때문에, 비트마스크를 이용해 저장할 수 있고

주의할 점은 지나온 길을 다시 가지 않기 위한 visited 변수는 키의 보유 상황에 따라 갈 수 있는 길이 다르기 때문에 이를 고려해야한다.

 

 

 

 

 소스코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
 
int n,m, sy,sx, dy[4= {-1,1,0,0}, dx[4= {0,0,-1,1};
char map[55][55];
int visited[55][55][66];
 
struct cell {
    int y, x, step, keys;    
    cell(int y, int x, int step, int keys) : y(y), x(x), step(step), keys(keys) { }
};
 
bool check_next_step(int y,int x, int keys){
    if (map[y][x] >= 'A' && map[y][x] <= 'F'){
        return keys & (1<<map[y][x] - 'A');
    } else {
        return map[y][x] != '#';
    }
}
 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%s",map[i]);
        for(int j=0;j<m;j++){
            if(map[i][j] == '0'){
                sy=i, sx=j;
            }
        }
    }
    queue < cell > q;
    q.push(cell(sy,sx, 00));
    visited[sy][sx][0= true;
    int ans = -1;
    while(!q.empty()){
        cell cur = q.front(); q.pop();        
        if(map[cur.y][cur.x] == '1') {
            ans = cur.step;
            break;
        }
        if (map[cur.y][cur.x] >='a' && map[cur.y][cur.x] <='f') {
            cur.keys |= (1<< (map[cur.y][cur.x] - 'a'));
        }        
        for(int i=0;i<4;i++){
            int ny = cur.y + dy[i], nx = cur.x + dx[i];
            if(nx>=0 && ny >=0 && nx < m && ny < n && !visited[ny][nx][cur.keys]){
                if(check_next_step(ny,nx, cur.keys)) {
                    visited[ny][nx][cur.keys] = true;
                    q.push(cell(ny, nx, cur.step+1, cur.keys));
                }
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
 
cs

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 15649~15650 N과 M  (0) 2020.08.20
BOJ 2580 스도쿠  (0) 2020.08.20
BOJ 6087 레이저통신  (0) 2020.08.09
BOJ 11505 구간 곱 구하기  (0) 2020.08.06
1344 축구  (0) 2018.06.01

 풀이 방법

 

이 문제는 두 지점 사이의 최소 거울 수를 구하는 문제로, 평소 두 지점의 최단거리를 구할 때 사용하는 BFS 를 응용해 "거리" 대신 "거울 수(방향 전환이 일어나는 수)" 를 사용하여 풀 수 있다고 생각했다.

 

단순 좌표 (y, x) 뿐만 아니라, 사용된 거울 수 및 이동해온 방향 정보가 필요하므로 새로운 구조체를 정의한다.

struct cell{
    int y, x, num, p_dir;
    cell(int y,int x,int num, int p_dir):y(y), x(x), num(num), p_dir(p_dir) {};
};

이동해온 방향(p_dir)을 저장하는 이유는 거울이 사용되는 지점이 방향이 전환되는 시점이기 때문에, 현재 좌표가 어느 방향에서 왔는지 확인하기 위해 필요하다.

 

이 구조체를 이용해 사용된 거울 수(num) 이 가장 작은 순으로 queue 에서 가져오기 위해 우선순위 큐를 사용한다.

 

 

 

 

 우선순위 큐의 Comparator 정의

내가 정의한 구조체에서 max-heap 으로 사용할지  min-heap 으로 사용하지 결정하기 위해 compartor를 정의해야 한다.

방법은 구사과 님의 블로그에 자세히 설명돼있다.

 

풀이에 사용된 방법은 구조체를 이용해 operator() 를 정의해 사용했다.

struct cmp{
    bool operator()(const struct cell& a, const struct cell& b){
        return a.num > b.num;
    }
};

// 중략

priority_queue< cell, vector<cell>, cmp > pq;

 

* 참고로 greater 는 작은 값을 먼저, less 는 큰 값을 먼저 리턴한다.

* a.num > b.num 는 greater 와 같고, a.num < b.num 은 less 와 같다.

 

이렇게 정의된 우선순위 큐를 이용하면, 시작지점에서 거울을 사용하지 않고 최대한 이동하여 탐색한 후,

거울  1개, 2개, k개 사용해서 이동할 수 있는 위치를 순차적으로 탐색할 수 있다.

 

 

 

 

 소스코드

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<queue>
  4.  
  5. using namespace std;
  6.  
  7. struct cell{
  8. int y, x, num, p_dir;
  9. cell(int y,int x,int num, int p_dir):y(y), x(x), num(num), p_dir(p_dir) {};
  10. };
  11. struct cmp{
  12. bool operator()(const struct cell& a, const struct cell& b){
  13. return a.num > b.num;
  14. }
  15. };
  16.  
  17. /*
  18. bool operator<(const struct cell a, const strcut cell b){
  19.   return a.num > b.num;
  20. }
  21. */
  22. int w,h, sy=-1,sx=-1, dy,dx;
  23. int dix[4] = {-1,1,0,0}, diy[4] = {0,0, -1, 1};
  24. char board[111][111];
  25. bool visited[111][111];
  26. bool dir_check(int a, int b){
  27. return (a<2 && b<2) || (a>=2 && b>=2) || a == -1;
  28. }
  29. int main(){
  30. scanf("%d%d",&w,&h);
  31.  
  32. for(int i=0;i<h;i++){
  33. scanf("%s", board[i]);
  34. for(int j=0;j<w;j++){
  35. if(board[i][j] == 'C'){
  36. if(sy != -1 && sx != -1){
  37. dy = i, dx = j;
  38. } else {
  39. sy = i, sx = j;
  40. }
  41. }
  42. }
  43. }
  44. priority_queue< cell, vector<cell>, cmp > pq;
  45. // priority_queue< cell, vector<cell>, greater<cell> > pq;
  46.  
  47. pq.push(cell(sy, sx, 0, -1));
  48. while(!pq.empty()){
  49. cell cur = pq.top();
  50. pq.pop();
  51. visited[cur.y][cur.x] = 1;
  52. if(cur.y == dy && cur.x == dx){
  53. printf("%d\n", cur.num);
  54. break;
  55. }
  56. for(int i=0;i<4;i++){
  57. int ny = cur.y + diy[i], nx = cur.x + dix[i];
  58. if(ny >=0 && nx >=0 && nx < w && ny < h) {
  59. if(!visited[ny][nx] && board[ny][nx] != '*'){
  60. // printf("\tnext : (%d, %d) num : %d, is_plus? : %d\n", ny,nx, cur.num, !dir_check(cur.p_dir, i));
  61. pq.push(cell(ny, nx, cur.num + (dir_check(cur.p_dir, i) ? 0 : 1), i));
  62. }
  63. }
  64. }
  65. }
  66. return 0;
  67. }

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 2580 스도쿠  (0) 2020.08.20
BOJ 1194 달이 차오른다, 가자.  (0) 2020.08.11
BOJ 11505 구간 곱 구하기  (0) 2020.08.06
1344 축구  (0) 2018.06.01
1351 무한 수열  (0) 2018.05.28

"구간 합" 유형 + 값 수정 쿼리 가 있는 문제는 "세그먼트 트리" 를 이용해 풀어야 한다.

구간 합은 보통 prefix sum(psum) 배열을 이용해 [a, b] 의 구간합을 psum[b] - psum[a-1] 로 풀 수 있다.

하지만 구간 값이 변경되는 "K 개의 수정 쿼리"가 있을 때는, 매 수정마다 N 크기의 배열을 다시 계산해야하기 때문에,

전체 복잡도가 O(K*N) 이 된다.

 

세그먼트 트리는 

     Leaf node      : 배열 값

     Internal node : 자식노드들의 구간 합

을 저장하여, 수정을 O(logN) 만에, 구간 합 계산 또한 O(logN) 번 만에 수행할 수 있는 자료구조이다.

 

자세한 내용은 백준님 블로그에 설명되어 있다.

 

 

이 문제는 값 수정 쿼리가 있는 구간 곱을 계산하는 문제로, 세그먼트 트리를 이용해야하며, 합 대신 곱 연산으로 변경해주면 된다.

 

#주의할 점

* 곱 연산이기 때문에 발생하는 오버플로우 문제가 있다.

* update 를 하는 방식은 다양하지만, 이전 값과의 차이(difference) 값을 해당 노드마다 곱해주는 방식으로는 WA 를 받는다.

  - difference 값을 계산하면서 발생하는 나눗셈 연산이 문제의 조건인 1000000007 MOD 연산에 영향을 줄 수 있을 것 같다.

  - 따라서 잎 노드의 변경으로부터 다시 계산하는 방식으로 업데이트를 수행한다.

 

 

소스코드

 

  1. #include<stdio.h>
  2. #include<vector>
  3. #define MOD(x) ((x)%1000000007)
  4. using namespace std;
  5. typedef unsigned long long ull;
  6. int n, m, k;
  7. ull arr[1000001];
  8.  
  9. ull make_seg_tree(vector< ull > &v, int node, int start, int end){
  10. if(start == end){
  11. return v[node] = arr[start];
  12. }
  13. return v[node] = MOD( make_seg_tree(v, node*2, start, (start+end)/2) *
  14. make_seg_tree(v, node*2+1, (start+end)/2 + 1, end));
  15. }
  16. ull update_seg_tree(vector<ull>&v, int node, int start, int end, int idx, ull val, bool was_zero) {
  17. if( idx < start || end < idx) return v[node];
  18.  
  19. if(start == end) {
  20. arr[idx] = val;
  21. return v[node] = val;
  22. } else {
  23. return v[node] = MOD(update_seg_tree(v, node*2, start, (start+end)/2, idx, val, was_zero) *
  24. update_seg_tree(v, node*2+1, (start+end)/2+1, end, idx, val, was_zero));
  25. }
  26. // v[node] = was_zero ? val : MOD((ull)(v[node] / arr[idx] * val)); // difference 를 이용한 수정
  27. }
  28.  
  29. ull get_mul(vector< ull >&v, int node, int start, int end, int left, int right) {
  30. if(right < start || end < left) return 1;
  31. if(start==end) return v[node];
  32. if(left <= start && end <= right){
  33. return v[node];
  34. }
  35. return MOD( get_mul(v, node*2, start, (start+end)/2, left, right) *
  36. get_mul(v, node*2+1, (start+end)/2+1, end, left, right));
  37. }
  38.  
  39. int main(){
  40. scanf("%d %d %d",&n,&m,&k);
  41. for(int i=1;i<=n;i++){
  42. scanf("%u",&arr[i]);
  43. }
  44. vector< ull > v(4*n);
  45. make_seg_tree(v, 1, 1, n);
  46.  
  47. int a, b,c;
  48. for(int i=0; i<m+k;i++){
  49. scanf("%d %d %d",&a, &b, &c);
  50. if(a == 1){
  51. //update
  52. bool was_zero = arr[b] == 0;
  53. ull val = (ull)c;
  54. update_seg_tree(v, 1, 1, n, b, val, was_zero);
  55. arr[b] = val;
  56. } else{
  57. //get mul
  58. printf("%u\n", get_mul(v, 1, 1, n, b, c));
  59. }
  60. }
  61.  
  62. return 0;
  63. }

 

 

 

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 1194 달이 차오른다, 가자.  (0) 2020.08.11
BOJ 6087 레이저통신  (0) 2020.08.09
1344 축구  (0) 2018.06.01
1351 무한 수열  (0) 2018.05.28
1670 정상 회담 2  (0) 2018.05.17

 

AWS CLI version 2 는 2020년 2월 10일에 Release 되었다.

 

Version 2 에 새로 추가된 일부 기능은 Version 1 로의 역호환을 지원하지 않을 수 있다.

따라서, Version 2 를 설치해 AWS 서비스들을 공부할 것이며,

앞으로 AWS study 카테고리에서는 데이터 엔지니어링 관련한 서비스들을 사용해보며, 공부한 내용을 정리할 계획이다.

정리할 내용은 주로 간단한 사용법 및 정리해놓으면 유용할 정보들이다.

 

 


CLI 2 설치 방법

공식 문서 : https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html

 

$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
$ sudo installer -pkg AWSCLIV2.pkg -target /

-target 으로 지정한 드라이브에 패키지가 설치되며, 파일들은 /usr/local/aws-cli 의 경로에 설치되며, /usr/local/bin 에 심볼릭 링크로 자동 생성된다. (위 폴더의 사용을 위해 sudo 권한이 필요)

 

* 디버그 파일 경로 : /var/log/install.log

 

설치 확인

 

 

# Version issue

* CLI version 1 과 version 2 가 모두 설치돼있을 경우, 환경변수 path 상에 앞 순서에 있는 버전이 동작하게 된다.

->  한 버전을 완전히 지운 후, 하나의 버전만 사용할 것을 권장함

 

* migration from version 1 to version 2 : http://docs.aws.amazon.com/cli/latest/userguide/cliv2-migration.html

 

 

 

IAM user 생성 및 AWS CLI Configuration 

CLI 를 통해 AWS 에 작업을 하기 위해 IAM (Identification and Access Management) 사용자가 필요하다. 

IAM 은 AWS 서비스를 이용하기 위해 필요한 권한을 부여할 수 있다.

 

1. AWS 사이트에서 [Services] - [Security, Identity, & Compliance] - [IAM] - [Users] 에서 사용자를 추가

 

 

2. 사용자 이름 및 액세스 유형 선택

 

사용자 이름을 정하고, CLI 를 이용하므로 액세스 유형은 프로그래밍 방식 액세스를 선택한다.

 

 

 

3. 권한 설정 선택

 

권한 설정 방법은 크게 세 가지

        1) 같은 권한을 공유하는 그룹에 새 사용자를 추가

        2) 기존 유저가 갖고 있는 권한을 복사

        3) 직접 권한 새로 부여

가 존재하며, 자세한 권한은 나중에 수정할 수 있으니 모든 액세서 권한을 갖는 관리자 권한으로 넣는다.

 

 

 

4. Tags 설정

 

생성할 유저에 대한 description 을 태그할 수 있다. (지금은 필요없으니 넘어간다)

 

 

 

5. AWS-CLI configuration

 

생성한 IAM 유저의 Access key ID 와 Secret access key 값을 이용해 터미널 상에서 설정하면 된다.

 

$ aws configure

 

 

Jenkins + Slack 연동


# Jenkins # Slack # 빌드 후 알림




 이번 포스트에서는 Jenkins와 Slack을 연동하여, Jenkins에서 빌드 결과를 Slack으로 알림 받을 수 있는 방법에 대해 정리한다.




Jenkins - Slack Notification Plugin 설치


 Jenkins의 플러그인 관리로 들어가 Slack Notification Plugin을 설치한다. 설치하고 재부팅없이 시작하기를 누르면 된다.












Slack- Jenkins CI App 설치


그리고 Slack에서도 Jenkins App을 설치해야 한다.








알림을 받을 채널을 선택하고, 






다음으로 진행하면, Slack에서 젠킨스 설정가이드 화면이 나타난다. Step3에 있는 Base URL과 Token을 저장하고 Save 버튼을 누른다.















Jenkins 설정


 Jenkins에서 알림을 보낼 프로젝트의 설정으로 들어가, 빌드 후 조치에서 Slack Notifications를 선택한다.








  언제 알림을 받을지 이벤트를 선택하고, Slack의 Jenkins app 설정에서 저장한 URL 과 token 값을 입력한다. 그리고 Team subdomain에는 xxxxx.slack.com 에 xxxxx에 해당하는 값을 입력하고, Project channel 란에는 알림을 받기 위해 설정한 채널을 입력하고 저장한다.









그리고 이제 push가 들어오면 빌드가 진행되고 완료되면 Jenkins는 Slack에 알림을 전송하는 것을 확인할 수 있다.








'Project Management > Jenkins' 카테고리의 다른 글

Jenkins + Github 연동 (hook)  (0) 2019.01.30

Jenkins + Github 연동


 # Jenkins # Github # push event # hook

 이번 포스트에서는 Jenkins와 Github을 연동하는 방법에 대해 다룬다. 연동했을 때의 장점은 Github의 commit, push, pull request 등의 이벤트가 발생할 때, 자동으로 테스팅과 함께 빌드를 하여 프로그램의 통합시간을 크게 단축시킬 수 있다.


프로젝트 준비

 
 만약 개인이 진행하고 있는 프로젝트가 없다면, 이 프로젝트(https://github.com/lis123kr/github.tutorial)를 fork 하여 사용할 수 있다. 젠킨스 설치는 생략한다. (centos 7 기준으로 yum install -y docker, docker pull jenkins 로 쉽게 설치가능)




Jenkins 설정

 
 먼저 Jenkins에서 새 프로젝트를 생성한다. Freestyle project로 생성한다.



아래 이미지와 같이 Github project의 주소를 복사하여 입력하고, 소스코드 관리 또한 [Git] - [git주소입력] 후, Credentials 설정을 위해 [add 버튼] - [jenkins]를 누른다.






Username에는 Github의 계정ID, Password에는 Github 계정 비밀번호를 입력하고 Add 버튼을 누른다.







[빌드 유발] 탭에서는 GitHub hook trigger for GITScm polling 을 누른다. Github의 hook trigger를 이용해 빌드를 유발시킬 것이다.





밑에 주의사항이 생길 수 있다. 지금은 github에서 아무 설정도 하지 않았기 때문에 연결을 하라고 하는 메시지이다. 지금은 무시하자.




위 프로젝트 준비에서 fork한 프로젝트는 Maven으로 빌드할 수 있다. 그리고 몇 가지 테스트가 준비되어있다. 각자 프로젝트 환경에 맞게 빌드를 선택하면 되며, 준비한 프로젝트를 사용하는 경우 Invoke top-level Maven targets을 누르고, Goals란에 test package를 입력하면 된다. 그리고 빌드 후 조치는 이후에 Slack과 연동해 알림을 해주는 방법에 대해 포스트할 계획이다.







그럼 이제 본격적으로 깃헙과 연결해보자. [Jenkins 관리] - [System 설정] 에 가면 Github 에 대한 설정이 있다. 이곳에 Github Server를 설정해야한다. 마찬가지로 Credentials을 위해 Add 버튼을 누르자.




 Kind를 Secret text로 변경하고, Secret을 입력해야한다. 이 Secret은 Github에서 생성해야한다. Github 홈페이지로 가보자. 












Github 설정 - Token 생성


[계정 설정(Personal Settings)] -  [Developer settings] 으로 이동하자.






 Personal access tokens 탭에서 token을 하나 생성해야한다. 즉, Jenkins 연결 시 인증을 위한 토큰이다. Generate new token 버튼을 누르자. 






 생성할 토큰에서 권한을 설정할 수 있다. 젠킨스가 필요한 권한인 repo와 admin:repo_hook을 체크하고 생성한다. 





생성하면 화면에 보이는 암호화된 문자열이 Secret에 들어갈 키이다.  복사하여 Jenkins 로 돌아가서 Secret에 입력하여 Add한 후, Test Connection을 해보자.






테스트가 성공했다.








  


Github 설정 - Webhook생성


 이제 인증을 위한 토큰을 생성해 Jenkins에서 인증까지 완료하였다. 이제 깃헙에서 특정 이벤트가 있을 때 hook을 할 수 있도록 webhook을 생성해보자. 

 젠킨스를 연동할 프로젝트로 돌아가 [Settings] - [Webhooks] 로 이동해 Add webhook을 눌러보자. Payload URL에는 요청을 보낼 URL 주소가 필요하다. 설치된 {젠킨스URL/github-webook/} 이 기본 설정이며 젠킨스에서 변경가능하다. 각자 URL을 입력한 후, Secret에는 이전에 발급받은 토큰을 입력한다. 그리고, 어떤 이벤트에 hook을 요청할 지 이벤트를 선택한다. 여기서는 push이벤트만 선택한다.








 생성되면 바로 요청되어 요청을 받는지 테스트가 되며 옆에 체크표시가 나타난다.

 















  


연동 테스트

 
 연동 테스트를 위해 push를 해보자.






  Jenkins에 생성했던 프로젝트로 가보면 빌드가 하나 생성되어 있음을 확인할 수 있다. 또한, 콘솔 출력을 통해 빌드 로그 또한 확인할 수 있다.







 master 브랜치에 있는 코드에는 Test 코드가 없다. Test 브랜치와 병합을 해 Push 해보자. 그러면 2개의 Test 까지 진행되는 것을 확인할 수 있을 것이다.












'Project Management > Jenkins' 카테고리의 다른 글

Jenkins + Slack 연동 - 빌드 후 알림 받기  (0) 2019.01.30

 Github + Slack 연동



 먼저 Slack 에서 Apps 의 +버튼을 통해 github을 검색하고  github을 설치한다.







 Slack의 어떤 채널에 연결을 할 지 선택할 수 있다.





설치가  완료되면  슬랙의 채널에서 /github signin 명령어를 통해 깃헙 계정에 로그인하여 연결한다.






로그인이 완료되면, Success! 라고 문구가 뜬다. 그리고,  특정 레퍼지토리랑 연결하기 위해,

/github subscribe [github ID] / [Repository명] 을 입력한다. 그러면 깃헙에 설치가 되어있지 않다고 설치하라는 문구가 뜬다.

설치해보자.






  팝업된 페이지의 밑으로 내려가보면 어떤 레퍼지토리에 액세스 권한을 줄지 선택할 수 있다. 연동할 레퍼지토리를 선택한다.





설치를 완료한 후, 다시 슬랙으로 돌아와 명령어를 다시 한 번 입력하면 해당 레퍼지토리를 github 봇이 구독한다.






깃헙 레퍼지토리에서 커밋, 푸시 등의 이벤트가 발생하면 Slack의 연동한 채널로 notification을 주게 된다.





 




Github 기초 사용법 - 3





목 차


   1. issue, labels, milestone
   2. project 활용







1. issue, labels, milestone


  issue  :  프로젝트를 진행하면서 발생하는 모든 이슈 (버그 발생, 개발, 풀 리퀘스트 등등)


 깃헙에서는 이슈기능을 통해 프로젝트에서 발생하는 모든 문제를 관리할 수 있도록 돕는다. 이슈는 아래 이미지와 같이 제목과 이슈에 대한 설명을 작성하여 생성할 수 있다.



  여러 이슈를 다음과 같이 만들어 볼 수 있다. 이해하기 쉽게 웹 어플리케이션을 개발할 때, HTML, CSS, JavaScript를 만드는 과정을 모두 이슈로 만들었다.





생성된 이슈는 리스트형태로 굉장히 관리하기 힘들어 보인다. 이슈가 굉장히 많아지면 어떤 이슈를 찾기가 점점 힘들어 질 것이다. 이러한 문제를 돕기위한 기능이 label 이다. 각 이슈에 대해 labeling을 통해 이슈를 검색할 수 있고, 이슈별 주제를 구분할 수 있다.


labels   :  각 이슈에 tag / label을 부여


 다음과 같이 Labels 탭(파란버튼) 에서 새로운 레이블을 만들 수 있고, 컬러 또한 지정할 수 있다.




 그리고, 이슈 상세 화면에서 오른쪽 사이드 메뉴의 Label을 통해 부여할 수 있다.




 또한, 해당 이슈에 대해 Assignees(책임자)를 부여할 수 있다.





  Assignees와 Labels를 각 이슈들에 부여한 후 목록페이지로 돌아오면 다음과 같이 깔끔하게 정리되어 있다. 이 label과 assignee를 통해 Filters 란에서 필터링하여 이슈를 검색할 수 있다.



 하지만, label과 assignees를 아무리 부여해도 무언가 부족하다. 각 기능별 서로 유사한 이슈들이 존재하며, 이 관련된 이슈들을 찾거나 해당 기능들이 얼마나 구현되었는지 파악하기 위해선 일일히 추적해야한다.


 이를 돕는 기능이 바로 MileStone기능이다. 마일스톤은 말그대로 이정표 역할을 하며, 이슈들을 그룹화한다.


Milestone   :  issue들의 그룹,  이정표로써 진행 상황을 표현


 마일스톤을 생성해보자. 




 이전에 Label을 부여한 것과 같이 이슈 상세페이지에서 마일스톤을 부여할 수 있다.




 마일스톤으로 3개의 이슈를 모두 묶을 수 있고, 만약 아래와 같이 하나의 이슈가 완료(close)되면 마일스톤의 진행상황이 업데이트 된다.

참고로 이슈페이지에서 @이름 을 통해 다른 사용자를 언급할 수 있고, # num 을 통해 커밋, 이슈 들을 참조해 소통을 원활하게 할 수 있다.






  이슈가 종료되면 아래 이미지와 같이 main page라는 마일스톤의 진행상황이 업데이트 되는 것을 볼 수 있다. 이 마일스톤 기능을 통해 연관된 이슈의 추적과 진행상황을 한 눈에 파악할 수 있다.












Project 활용


 issue, label, milestone으로 프로젝트를 진행하면서 발생하는 이슈들을 관리하는 법에 대해 알아보았다. 하지만, 아직도 이슈가 한눈에 들어오지 않는다. LIST 열거 방식의 한계가 있어, 프로젝트가 현재 진행중이고 목표하고 있는 것이 무엇인지 한 눈에 파악하고 싶다. 프로젝트의 칸반보드 기능을 이용하면, 이슈들을 좀 더 조직화할 수 있고,  우선순위에 따라 정렬하거나 보다 한 눈에 workflow를 볼 수 있다.

 Project 메뉴로 이동해 create a project 버튼을 눌러보자.




 프로젝트의 이름과 템플릿을 설정한다. 여기서는 "automated Kanban"을 선택해, 기본 템플릿이 있고 이슈를 알아서 어느정도 관리해주는 템플릿을 선택한다.





프로젝트가 생성되면 아래와 같이 3개의 열로 구성된 기본 템플릿이 메인 창에 나타나며, 오른쪽 사이트 바엔 이전에 생성했던 열려있는(open) 이슈 카드 목록이 보인다. 3개의 열은 " To do ", " In Progress ", " Done "으로 구성되어, 해야할 Task와 진행 중인 Task, 완료된 Task를 묶을 수 있다.(그냥 Trello 보드랑 똑같이 쓰면 된다...)      


  오른쪽의 이슈 카드는 드래그를 통해 이슈의 상태에 따라 옮기면 된다.






 직접 issue 카드를 생성할 수 있으며, 설정의 "Convert to issue" 통해 실제 issue로도 변환할 수 있다.






 
이전에는 Trello 보드를 이용해 이슈들을 관리했었는데, Github 내에서도 이 project 기능을 통해 별도의 트렐로와 같은 툴을 쓰지 않아도 프로젝트의 이슈들을 정리하고 관리할 수 있어 편리한 것 같다.






  Github 기초 사용법 - 2



  이번 포스트에서는 로컬 저장소에서 작업한 내용을 Github의 원격저장소로 push하는 등의 원격저장소 활용방법에 대해 다룬다. github의 기본 명령어 (git init & status & add & commit) 및 Branch 와 충돌관리에 대해 잘 모른다면 이전 포스트 [Project Management/Github] - Github 기초 사용법 - 1를 참고하세요.





목 차


   1. Github 레퍼지토리 생성
   2. 원격저장소 연결(git remote)
   3. git push (로컬 -> 원격)
   4. 원격저장소와 로컬저장소 충돌 문제 (git pull & fetch)
   5. fork/clone 와 pull request






1. Github 레퍼지토리 생성

 
 Github의 계정을 생성한 후, Repositories 메뉴의 New 버튼을 통해 레퍼지토리를 생성한다. 지금은 README 파일은 생성은 하지말자.



 생성하면 다음과 같이 레퍼지토리가 생성된다. 레퍼지토리의 URL을 복사하자. Git Bash에서 이 원격 저장소와 연동을 위해 이 원격 저장소의 주소가 필요하다.










2. 원격저장소 연결 (git remote)

 
 Git Bash 터미널로 돌아가 이전 포스트[Project Management/Github] - Github 기초 사용법 - 1에서 작업했던 환경에서  방금 생성한 원격 저장소를 연결해보자. 그리고 git remote -v 로 목록을 확인해보자. 통상적으로 origin이란 별명을 사용한다.

 git remote add [원격저장소 별명] [원격저장소 주소] : 원격저장소 추가
 git remote -v : 원격저장소 목록 확인

e.g. remote add origin https://github.com/lis123kr/github.tutorial.git



 아무 변경사항이 없어보인다. 하지만 추가한 원격 저장소를 통해 이제 로컬 환경의 작업내역을 원격으로 전송할 수 있다.






3. git push (로컬 -> 원격)


 git push [원격저장소 별명] [브랜치 명] : 원격저장소에 [브랜치 명]의 작업내용을 전달

 git push 명령어를 통해 원격저장소에 변경 내용을 발행해보자. git push origin --all 을 통해 모든 브랜치를 전송하였다. 그리고 github의 원격 저장소에도 업로드된 것을 확인할 수 있다.



 







4. 원격저장소와 로컬저장소 충돌 문제 (git pull & fetch)

 
 이번에는 원격 저장소와 로컬 저장소의 간격을 메우는 과정을 살펴보자. 새로운 기능을 로컬 저장소에서 구현했을 때는, commit하고 push하여 로컬 저장소에 업로드하면 되지만, 다른 팀원이 개발한 내역이 있어 원격 저장소의 작업 내역이 나의 로컬의 작업내역과 다를 때는 내 변경사항을 원격저장소에 업로드(Push)하면 충돌이 발생한다.

 이럴 때는 원격저장소의 변경사항과 나의 로컬저장소의 간격을 없애야한다. 즉, 원격저장소의 최신 변경사항으로 맞추어야 내 로컬 변경사항을 업로드할 수 있다. 이를 위한 명령어가 fetch와 pull이다. 

 git fetch : 원격 저장소의 변경 사항(commit) 들을 로컬저장소로 가져온다. (commit 내역을 가져온다)
 git pull   : 원격 저장소의 변경사항을 가져와 로컬에 병합한다.

 즉, git pull은 git fetch하고 git merge까지 해준다고 볼 수 있다. 그럼 이제 직접 해보자.

원격 저장소와 로컬 저장소의 간격을 만들기 위해 원격 저장소에서 내용을 수정해보자. github 저장소에서 C코드를 선택해 '펜' 버튼을 누르면 바로 수정가능하다. 주석 한줄을 추가한 후 밑에 commit 버튼을 통해 변경을 완료한다.

  




  이로써, 원격저장소에는 commit이 하나 더 생겼다. 내 로컬 환경과 간격이 생겼고, 로컬 환경에서 마찬가지로 주석을 작성해 push해보면 충돌이 발생한다.

 


 당연히 원격저장소와 로컬 저장소가 변경내역이 다르기 때문이다(원격과 로컬 각각 커밋이 하나씩 더 있다) . hint에는 git pull을 사용하라고 알려주지만 git fetch를 사용해보자. git fetch를 하면 아무 변화가 없어보인다. 파일도 로컬에서 변경한 내용 그대로이다. 하지만 git branch -a를 해보면 원격저장소의 임의의 브랜치(origin/master)가 생성되었다.

  


 git merge origin/master를 통해 원격저장소의 변경내역과 병합해보자. 변경내역에 충돌이 없다면 바로 병합이 되지만, 여기서 변경한 내역에는 충돌되는 부분이 있어 충돌이 발생했다. 충돌이 난 부분을 git diff를 통해 확인하고 해결하자.

 



 충돌을 해결한 후 원격저장소에 push하면 이제는 원격저장소와 로컬저장소가 같은 최신의 작업내역을 갖고있기 때문에 push 시 에러가 발생하지 않는다.




 지금까지의 변경내역을 그래프로 그려보면 다음과 같다. 각 점은 commit 내역을 의미하고 branch를 생성하고 병합한 내역과, 원격 저장소에서의 변경과 병합한 내역을 확인할 수 있다. 이 그래프는 Github 레퍼지토리에서 [Insights - Network]를 통해 확인할 수 있다.

   










5. fork/clone 와 pull request


 마지막으로 오픈소스의 발전 속도를 가속화하는 기능인 fork와 pull request에 대해 알아보자.

fork               : 다른 사용자의 원격저장소를 내 원격 저장소 복제
clone            : 원격 저장소를 로컬 저장소에 복제
pull request  :  저장소 간의 병합요청

  fork는 말 그대로 다른 사용자의 저장소를 내 저장소에 똑같이 복사하는 기능이다. pull request는 복사한 다른 사용자의 저장소에서 여러 기능 수정 및 추가를 한 후, 나의 변경 내역을 저장소 소유자(오리지널 저장소)에 병합요청을 하는 기능이다. 즉, 다른 사람의 소스를 내가 수정하고 변경요청을 통해 '기여'할 수 있는 기능이다.


 


 이를 직접 실습하기 위해선 또 하나의 계정이 필요하다. (저자는 두 개 있다)  다른 계정으로 지금까지 작업해온 저장소를 fork해보자. 레퍼지토리오른쪽 상단의 fork 버튼을 누르면 된다. fork 된 저장소의 원격 저장소 주소를 복사하여 git Bash에서 새로운 환경에 clone해보자. 




 
 clone한 저장소에서 git remote -v를 해보면 자동으로 복사했던 원격저장소(fork한)의 주소와 연결되어있다. 약간의 코드 수정을 하고 push까지 해보자. 
 


 
 Push까지 완료하면 fork 했던 원격 저장소를 보면 다음과 같이 커밋이 업데이트 되어있고, 기존 저장소와는 다르게 pull request가 있음을 확인할 수 있다. pull request를 해보자.




본 저장소와 fork 저장소의 변경사항을 보여주고 Create pull request 버튼을 통해 pull request를 생성할 수 있다. pull reqeust를 생성할 때, 보통 오픈소스 프로젝트에서는 template이 있어, 템플릿에 맞게 내용을 작성하여 병합요청을 해야한다.  





 다시 소유자의 계정으로 돌아가보자. pull reqeust 메뉴에 다른 사용자가 요청한 pull request가 생성되어있고, 내용을 확인할 수 있다.

 pull request를 받은 본 저장소의 소유자는 변경 내용을 확인하고 적합하다면 merge 버튼을 통해 병합할 수 있고, 또는 변경을 요청하며 병합을 거절할 수 있다.

 





 Merge를 하면 다른 유저가 변경한 코드 변경 내역을 원래의 저장소에 병합하게 된다. 다음 시간에는 위 이미지에서 오른쪽에 보이는 Reviewers 및 Labels, Project, Milestone에 대해 포스트할 계획이다.


+ Recent posts