├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis.yml ├── README.md ├── docs ├── part-01.md ├── part-02.md ├── part-03.md └── part-05.md ├── images ├── add-new-issue.gif ├── intellij-pr-3.png ├── intellij-pr-4.png ├── intellij-pr-5.png ├── intellij-pr-6.png ├── intellij-pr-7.png ├── intellij-pr-8.png ├── intellij-pr-9.png ├── issue-template-rg.gif └── wiki.gif ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── sample │ │ └── github │ │ ├── GithubApplication.java │ │ ├── model │ │ ├── Address.java │ │ └── Email.java │ │ └── user │ │ ├── User.java │ │ ├── UserFindService.java │ │ └── UserRepository.java └── resources │ └── application.yml └── test └── java └── com └── sample └── github ├── GithubApplicationTests.java └── user └── UserFindServiceTest.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## 증상 8 | * 9 | 10 | ## 시나리오 11 | 12 | ## screenshot 13 | 14 | ## response 15 | ```json 16 | ``` 17 | 18 | ## server log 19 | ``` 20 | ``` 21 | 22 | ## 실행 환경 23 | - OS: 24 | - Browser: 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 관련 내용 2 | 3 | 4 | ## 관련 링크 5 | * [JIRA - ](...) 6 | 7 | ## 적용 모듈 8 | * xxx-service 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## 관련 내용 2 | 3 | 4 | ## 관련 링크 5 | * [JIRA - ](...) 6 | 7 | ## 적용 모듈 8 | * xxx-service -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | 6 | env: 7 | global: 8 | - CODECOV_TOKEN = d116be9b-435a-42f3-a4c2-224be876d3f9 9 | 10 | install: true 11 | 12 | script: mvn install 13 | 14 | before_install: 15 | - mvn clean 16 | 17 | after_success: 18 | - mvn clean cobertura:cobertura coveralls:report 19 | - bash <(curl -s https://codecov.io/bash) 20 | 21 | branches: 22 | only: 23 | - master 24 | 25 | notifications: 26 | slack: cheese-dev:JXKSTVY4wHsMPbJi2uoKtyxs 27 | email: false -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/cheese10yun/github-project-management.svg?branch=master)](https://travis-ci.org/cheese10yun/github-project-management) 2 | [![Coverage Status](https://coveralls.io/repos/github/cheese10yun/github-project-management/badge.svg)](https://coveralls.io/github/cheese10yun/github-project-management) 3 | [![codecov](https://codecov.io/gh/cheese10yun/github-project-management/branch/master/graph/badge.svg)](https://codecov.io/gh/cheese10yun/github-project-management) 4 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fcheese10yun%2Fgithub-project-management&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) 5 | # 프로젝트 소개 6 | Github를 이용해서 Project Management 하는 방법 및 전체적인 프로세스에 대해서 정리했습니다. **issue 관리, 일정 관리, 코드리뷰, 버그 리포트 등 다양한 일들을 Github 하나에서 다 관리할 수 있고 어느 하나 부족하다고 생각하지 않습니다.** 이미 Remote Repository로 Github를 사용 중이시라면 적극 추천해 드립니다. 7 | 8 | 프로젝트는 지속해서 추가될 예정이라 상단의 Star, Watching 버튼을 클릭하시면 구독 알림 받을 수 있습니다. 9 | 10 | # 목차 11 | 12 | 13 | - [프로젝트 소개](#프로젝트-소개) 14 | - [목차](#목차) 15 | - [전체 플로우](#전체-플로우) 16 | - [Issue 발행](#issue-발행) 17 | - [Issue란?](#issue란) 18 | - [Issue Template](#issue-template) 19 | - [Issue Template 등록](#issue-template-등록) 20 | - [Issue Template 사용법](#issue-template-사용법) 21 | - [Issue Template 파일](#issue-template-파일) 22 | - [Issue 작업](#issue-작업) 23 | - [등록된 issue 살펴 보기](#등록된-issue-살펴-보기) 24 | - [Issue 연동](#issue-연동) 25 | - [Issue 기반 Branch 생성](#issue-기반-branch-생성) 26 | - [Pull Request[Code Review]](#pull-requestcode-review) 27 | - [Jetbrains Pull Request](#jetbrains-pull-request) 28 | - [GitHub Pull Request](#github-pull-request) 29 | - [Pull Request 작성법](#pull-request-작성법) 30 | - [Code Review](#code-review) 31 | - [IntelliJ 기반 Code Review](#intellij-기반-code-review) 32 | - [Code Review](#code-review-1) 33 | - [CI & Test Coverage](#ci--test-coverage) 34 | - [전체 플로우](#전체-플로우-1) 35 | - [Pull Request & Code Review](#pull-request--code-review) 36 | - [테스트 커버지리 표시](#테스트-커버지리-표시) 37 | - [Wiki](#wiki) 38 | - [Wiki 등록](#wiki-등록) 39 | - [GitHub 작성](#github-작성) 40 | - [Local 작성](#local-작성) 41 | - [ZenHub 사용법](#zenhub-사용법) 42 | - [기본 칸반보드](#기본-칸반보드) 43 | - [Milestone 적극 활용](#milestone-적극-활용) 44 | - [Epic 적극 활용](#epic-적극-활용) 45 | - [Issue 연결](#issue-연결) 46 | - [Filter 기능](#filter-기능) 47 | - [Github Action](#github-action) 48 | - [Github Action 만들기](#github-action-만들기) 49 | - [gradle.yml](#gradleyml) 50 | - [Action Workflows](#action-workflows) 51 | - [Badge](#badge) 52 | - [Schedule With Spring Batch](#schedule-with-spring-batch) 53 | - [Schedule Github action 생성](#schedule-github-action-생성) 54 | - [Batch Code](#batch-code) 55 | 56 | 57 | 58 | # 전체 플로우 59 | 1. Issue 발급 60 | 2. Issue 작업 61 | 3. Pull Request Code Review 진행 62 | 4. Issue 반영 63 | 64 | ## Issue 발행 65 | 66 | ### Issue란? 67 | 모든것이 이슈라고 볼 수 있습니다. 새로운 추가될 가능, 개선 해야할 가능, 버그 등등 모든것이 이슈라고 볼 수 있습니다. 모든 활동 내역에 대해서 이슈를 등록하고 그 이슈기반으로 작업을 진행하게 됩니다. 68 | 69 | 이슈를 등록할 때 자주 사용하는 템플릿을 등록해서 사용하는 방법이 효율적입니다. 이슈 템플릿을 등록하는 방법을 소개해드리겠습니다. 70 | 71 | ### Issue Template 72 | 73 | #### Issue Template 등록 74 | ![](https://github.com/cheese10yun/github/blob/master/images/issue-template-rg.gif?raw=true) 75 | 76 | 환경에 알맞는 Issue Template 생성을 합니다. 77 | 78 | 79 | #### Issue Template 사용법 80 | ![new_issue](https://github.com/cheese10yun/github/blob/master/images/add-new-issue.gif?raw=true) 81 | 82 | 위에서 등록한 Issue Template 기반으로 이슈를 생성할 수 있습니다. 83 | 84 | 85 | #### Issue Template 파일 86 | 87 | ``` 88 | . 89 | ├── .github 90 | │   └── ISSUE_TEMPLATE 91 | │   ├── bug_report.md 92 | │   └── feature_request.md 93 | ├── README.md 94 | ├── github.iml 95 | ├── images 96 | ├── mvnw 97 | ├── mvnw.cmd 98 | ├── pom.xml 99 | └── src 100 | ``` 101 | 위에서 등록된 Issue Template은 .github/ISSUE_TEMPLATE 디렉터리에 생성된 것을 확인할 수 있습니다. 102 | **각자의 맞는 환경에 따라서 Issue Template를 작성하시면 됩니다.** 저 같은 경우에는 Back-end를 주로 담당하기 때문에 bug template 에서 서버로그, response body 값을 등록했습니다. 103 | 104 | 같은 방법으로 Pull Request의 템플릿도 생성할 수 있습니다. 105 | 106 | ``` 107 | . 108 | ├── .github 109 | │   └── PULL_REQUEST_TEMPLATE.md 110 | ``` 111 | 112 | #### Issue Template 커스터마이징 113 | 114 | 아래의 경우는 `.github/ISSUE_TEMPLATE/custom.md` 의 예시입니다. 115 | 116 | GitHub에서 `---`와 `---` 사이의 정보를 읽어, 이슈 생성 시 선택할 수 있는 템플릿 메뉴를 제공합니다. 117 | 118 | ``` 119 | --- 120 | name: Hello Issue 121 | about: Custom Issue Template Test 122 | title: '' 123 | labels: '' 124 | assignees: '' 125 | 126 | --- 127 | 128 | 👋 Custom Issue Template Test 129 | ``` 130 | 131 | 따라서 로컬에서 코드를 직접 수정하시는 경우에는 상단의 `---`와 `---` 부분은 유지한 채, 정보와 템플릿 내용만 수정하시는 것을 추천합니다. 132 | 133 | ## Issue 작업 134 | 135 | ### 등록된 issue 살펴 보기 136 | ![등록된-이슈](https://i.imgur.com/2ciNoCd.png) 137 | 138 | * Assignees : 해당 작업의 담당자 139 | * Labels: 해당 작업의 성격 140 | * Milestone: 해당 작업이 속한 파트 141 | 142 | ![](https://i.imgur.com/DkniJHn.png) 143 | 144 | 다른 것들은 이해하기 쉬울 텐데 Milestone은 조금 생소할 수 있습니다. Milestone에 간단하게 설명해 드리면 이번 출시 버전이 1.0.0 일 경우 해당 버전이든 이슈(작업) 기능 강화, 새 기능추가, 버그 기타 등등 모든 이슈를 Version 1.0.0 Milestone이라는 항목에 추가하면 위 그림처럼 Version 1.0.0에 대한 전체적인 상황을 한눈에 볼 수가 있는 장점이 있습니다. 145 | 146 | ### Issue 연동 147 | ![intellij-task](https://i.imgur.com/FtO0Xme.png) 148 | 149 | 만약 Jetbrains의 IDE를 사용하고 계신다면 Task 연동을 통해서 Github와 연동하시는 것을 적극 권장해 드립니다. 150 | 151 | ### Issue 기반 Branch 생성 152 | ![issue-base-branch](https://i.imgur.com/R8aFoCL.png) 153 | 위에서 언급한 Jetbrains의 Task 연동을 하지 않아도 크게 상관없습니다. Task의 갖는 가장 큰 기능은 Github 이슈 기반으로 Branch를 생성을 쉽게 도와주는 것으로 생각합니다. **즉 Github에서 생성된 Issue 기반으로 Branch를 생성하는 것이 핵심입니다.** 154 | 155 | Github Issue는 각자의 유니크한 값인 Issue Number를 갖습니다. 또 그 Issue Number 기반으로 Branch를 이름을 갖게 하여 해당 Branch의 명확한 작업의 의도를 갖게 할 수 있습니다. 156 | 157 | Branch 네이밍을 통해서 해당 작업의 의도를 갖게 하는 것은 한계가 있습니다. 또 동료 개발자들이 정확히 무슨 작업을 하는지도 Branch 네이밍을 통해서 유추해내기도 어렵고, 해당 작업이 반영(머지)될 때 도 마찬가지입니다. 이러한 문제들을 Issue Number 기반으로 Branch를 생성(Issue Number Branch 네이밍에 추가)하면 아주 명확해집니다. 158 | 159 | ## Pull Request[Code Review] 160 | [issue-1](https://github.com/cheese10yun/github/issues/1)에 대한 풀리퀘스트를 통해서 코드리뷰를 진행해 보겠습니다. 161 | 162 | ### Jetbrains Pull Request 163 | ![intellij-pull-request](https://i.imgur.com/vkNR06g.png) 164 | 만약 Jetbrains IDE를 사용하신다면 위 방법 처럼 Pull Request를 하는 방법을 권장드립니다. 165 | 166 | ### GitHub Pull Request 167 | ![github-pull-request](https://i.imgur.com/6bBTJUV.png) 168 | 169 | Github Code 텝에서 `New Pull Request` 버튼을 클릭해서 Pull Request를 진행 합니다. 170 | 171 | ### Pull Request 작성법 172 | ![](https://i.imgur.com/3TnHt0c.png) 173 | 174 | * 왼쪽 위에 Reviewers 톱니바퀴 버튼을 클릭해서 리뷰어를 지정합니다. 175 | * resolved: #1(해당 Issue Number) 풀리퀘스트 요청하는 이유 즉 무슨 이슈에 대한 작업인지 명시합니다. 176 | 177 | `resolved` 키워드를 입력하면 해당 풀리퀘스트가 master Branch에 반영되면 자동으로 close 됩니다. 자동으로 close 되는 것이 싫으시다면 issue: #[해당 Issue Number]를 작성해주세요. 178 | 179 | 이렇게 Pull Request가 생성되면 새로운 Issue Number가 부여됩니다. **즉 Pull Request도 Issue입니다.** 180 | 181 | ![issue-pull-request-연결](https://i.imgur.com/skNmpeQ.png) 182 | 183 | **반드시 해당 풀리퀘스트가 무슨 이슈에 따른 요청인지 명시하시는 것을 권장합니다.** 그렇게 되면 위 그림처럼 해당 이슈에 #2[방금 요청한 풀리퀘스트]가 연결되어 해당 이슈가 무슨 코드로 인해서 진행됐는지 추적하기 좋습니다. 184 | 185 | ### Code Review 186 | 리뷰어가 요청받은 Pull Request로 가서 `Add your review` 버튼을 클릭합니다. 187 | 188 | ![리뷰진행](https://i.imgur.com/k11vL5w.png) 189 | 소스코드에 대한 질문 등 다양한 comment를 남기는 방식으로 pull request가 진행합니다. 190 | 191 | * Approve: 코드에 대한 의문점이 없다면 승인. 192 | * Comment: 간단한 피드백 제출 193 | * Request changes: 해당 코드에 문제가 있다고 판단되며 코드를 반드시 수정 요구 194 | 195 | 위 항목은 Comment로 Submit review를 진행했습니다. 196 | 197 | ![comment-표시](https://i.imgur.com/EHnVEjU.png) 198 | 위에서 작성한 comment가 해결됬었다면 `Merge pull request` 버튼을 눌러서 해당 pull request를 반영합니다. 반영이 완료되고 해당 branch가 더는 필요 없다고 판단되시면 `Delete branch` 버튼을 통해서 Remote에 있는 Branch를 삭제할 수 있습니다. 199 | 200 | **위에서 작성한 resolved: #1 키워드 덕분에 소스코드가 해당 Branch에 적용됐으니 자동으로 #1에 대한 이슈는 close 처리됩니다.** 201 | 202 | ### IntelliJ 기반 Code Review 203 | ## Code Review 204 | 205 | ![](images/intellij-pr-3.png) 206 | 207 | `Actions`에서 `View Pull Requests`을 통해서 현재 PR을 확인할 수 있습니다. 208 | 209 | 210 | ![](/images/intellij-pr-4.png) 211 | 212 | `state:open `을 통해서 현재 open 상태이 PR 리스트를 확인할 수 있고 우측에는 코드 리뷰 대상 파일이 있고 `User.java` 파일을 클릭해서 Diff를 확인할 수 있습니다. 213 | 214 | ![](/images/intellij-pr-5.png) 215 | 해당 코드에 대해서 코멘트를 추가하고 싶은 경우 `+` 버튼을 클릭해서 코멘트를 달 수 있습니다. 216 | 217 | ![](/images/intellij-pr-6.png) 218 | 219 | ![](/images/intellij-pr-7.png) 220 | 221 | 이렇게 추가한 코멘트는 Github, Intellij에서도 확인할 수 있으며 코멘트에 대한 코멘트도 추가할 수 있습니다. 222 | 223 | ![](/images/intellij-pr-8.png) 224 | 또 인텔리제이에서 머지도 가능합니다. `Merge` 버튼을 눌러서 머지를 진행합니다. [인텔리제이 PR & Code Review #24](https://github.com/cheese10yun/github-project-management/pull/24)는 실제 PR이 머지 된 것을 확인할 수 있습니다. 225 | 226 | ![](/images/intellij-pr-9.png) 227 | 위처럼 질의를 통해서 PR에 `status`, `author`, `assignee`을 기반으로 검색을 할 수 있습니다. 228 | 229 | # CI & Test Coverage 230 | ![](https://i.imgur.com/G5jo0Ty.png) 231 | 232 | [GitHub Marketplace](https://github.com/marketplace/category/continuous-integration) Public Repository를 이용하면 대부분 무료로 이용 가능합니다. **본 포스팅에서는 CI는 Travis CI, Test Coverage는 Coveralls를 이용해서 진행하겠습니다.** 233 | 234 | 전체적인 플로우를 설명하는 것이 목적 이리서 특정 툴에 대한 직접적인 사용법을 다루지는 않겠습니다. 언어의 특성 및 개인에 기호에 맞는 제품을 사용하시면 됩니다. 235 | 236 | ## 전체 플로우 237 | 1. Pull Request 요청 -> Code Review 진행 238 | 2. Code Review 완료 -> 특정 Branch에 반영 239 | 3. 특정 Branch 수정시 자동 CI Build 작업 진행 -> 테스트 코드 실행 240 | 4. 테스트 커버지리 표시 241 | 242 | ## Pull Request & Code Review 243 | ![](https://i.imgur.com/q6HmT7o.png) 244 | 245 | 별다른 설정을 하지 않았다면 Pull Request를 요청할 경우 Travis에서 자동으로 해당 요청한 코드 기반으로 Build 작업이 진행됩니다. Build가 실패했을 경우는 Pull Request 요청자는 코드를 수정해서 최소한 Build가 된 코드 기반으로 Code Review를 진행하게 해야 됩니다(Build도 안 되는 코드를 리뷰할 이유는 없을 거 같습니다.) 246 | 247 | 요청받은 Pull Request에 대해서 Code Review 작업을 진행하게 됩니다. Code Review가 완료되면 Merge pull request를 통해서 해당 작업(issue)을 반영합니다. 248 | 249 | ## 테스트 커버지리 표시 250 | ![](https://i.imgur.com/U1ROYeE.png) 251 | 252 | 위에서 Merge pull request를 통해서 해당 작업(issue)을 반영했다면 Travis가 Build 할 때 작성된 Test Code 기반으로 Coverage 정보를 위처럼 자동으로 코멘드를 추가해줍니다. 253 | 254 | 누군가가 테스트 코드를 작성하지 않았다면 `Change from base` 항목에서 - 표시가 됩니다. **이렇게 해당 작업마다 커버리지를 표시하는 것이 전체 커버리지를 높이고 그 값을 유지하는 좋은 방법이라고 생각합니다.** 255 | 256 | # Wiki 257 | GitHub에서는 기본적인 WIKI 기능을 제공해줍니다. 258 | 259 | ## Wiki 등록 260 | ![](https://github.com/cheese10yun/github/blob/master/images/wiki.gif?raw=true) 261 | 262 | ## GitHub 작성 263 | ![](https://i.imgur.com/t3LuQsm.png) 264 | GitHub에서 위키를 작성할 수 있습니다. 265 | 266 | ## Local 작성 267 | ![](https://i.imgur.com/Ff6Heeb.png) 268 | **위키를 로컬환경에서 작성해서 Push 할 수 있습니다. 이렇게 사용하시는 것을 추천 드립니다.** 269 | 270 | 271 | # ZenHub 사용법 272 | 273 | ![](https://i.imgur.com/F5c78MC.png) 274 | 275 | 수 많은 이슈들이 발급되면 그것을 한눈에 보는 것은 기존 UI에서는 어렵습니다. 이런 문제를 칸반보드 형식으로 해결해주는 것이 ZenHub 입니다. 276 | 277 | ![](https://cristinasantamarina.files.wordpress.com/2015/06/zenhub-task-board.png) 278 | Public Repository를 이용 중이라면 무료로 사용 가능합니다. Private Repository는 비용을 지급하셔야 합니다. 279 | 280 | 물론 GitHub에서 제공해주는 Project도 칸반보드를 제공해주어서 대안이 되지만 여러 레파지토리에 대한 표시, 애픽 기능 등 다양한 기능들을 제공해주기 때문에 개인적으로 ZenHub를 추천해 드립니다. 281 | 282 | **설치는 크롬 확장도구에서 다운로드 받으실 수 있습니다.** 283 | 284 | ## 기본 칸반보드 285 | ![](https://i.imgur.com/GvP6XPh.png) 286 | 287 | 기본적인 구조는 New Issue, Icebox, Backlog, In Progress, Review/QA, Done을 갖습니다. 각자 본인의 프로젝트와 성향에 맞게 사용하시면 됩니다. 288 | 289 | 저 같은 경우에는 생각나는 모든 것들을 New Issue 항목에 추가하고 우선순위가 낮고 당장 필요 없는 작업은 Icebox, 우선순위가 높은 작업은 Backlog 항목에 넣습니다. 진행 중인 작업은 In Progress 나머지 Review/QA는 Pull Request 항목, Issue가 Close 되면 자동으로 Close으로 가게 됩니다. 290 | 291 | **이처럼 모든 항목에 대해서 Issue로 관리하고 해당 Issue Number 기반으로 Branch가 생성되면서 코드리뷰, 반영 작업 사이클이 돌아가는 구조입니다.** 292 | 293 | ## Milestone 적극 활용 294 | ![](https://i.imgur.com/3lDuAxi.png) 295 | 296 | ZenHub 사용과 직접적인 기능은 아니지만, Milestone 여러 항목을 만들고 대부분 Icebox에 있는 issue가 아니라면 적절한 Milestone에 위치시키는 것을 권장합니다. 전체적인 작업에 진행 척도를 가시적으로 확인하기 좋은 점, 해당 Milestone(버전)에 추가되는 기능, 강화되는 기능, 수정된 버그 등을 직관적이로 표시 할 수 있습니다. 또 전체 Milestone 진척도를 알 수 있어 남은 시간도 산출하기 좋은 장점이 있습니다. 297 | 298 | ## Epic 적극 활용 299 | ![](https://i.imgur.com/dZhrATb.png) 300 | Epic은 Milestone과 비슷하게 이해하시면 됩니다. 큰 작업(Issue)이 있다면 그 작업(Issue)을 여러 Issue로 등록하고 한 묶음으로 관리하는 것입니다. 301 | 302 | Milestone의 장점처럼 작업에 대한 진척도를 가시적으로 표시하는 장점이 있습니다. 또 큰 작업물에 대한 작업은 여러 개발자가 나누어서 하다 보니 내가 그 기능을 작업하기 전에 선행적으로 진행되어야 하는 작업도 있을 수 있습니다. 이런 것들을 대한 피드백을 Epic을 이용하면 관리하기 편합니다. 303 | 304 | 305 | ![](https://i.imgur.com/nIMGciB.png) 306 | 해당 Issue를 Epic으로 등록 시킬 경우 Create an epic 버튼을 클릭하면 됩니다. 307 | 308 | ![](https://i.imgur.com/lN2uziy.png) 309 | Issue를 등록할 때 오른쪽 하단의 Epic에서 등록시킬 수 있습니다. 310 | 311 | ## Issue 연결 312 | 313 | ![](https://i.imgur.com/XYa3My3.png) 314 | Connect with an issue 기능을 통해서 이슈끼리 연결을 할 수 있습니다. **위처럼 Pull Request를 할 때 아주 유용하게 사용할 수 있습니다.** 315 | 316 | ![](https://i.imgur.com/DPrWlUd.png) 317 | Issue 13과 해당 이슈를 작업해서 Pull Request Issue 17 번이 ZenHub 보드에서 연결돼 있는 것을 확인 할 수 있습니다. **이처럼 연관된 작업을 묶어주는 장점이 있습니다.** 318 | 319 | ## Filter 기능 320 | 321 | ![](https://i.imgur.com/4pZEYsB.png) 322 | 323 | 다양한 Filter 기능을 제공합니다. Label, Assignee, Milestone, Repo 등등이 있습니다. 해당 기능은 직관적이니 324 | Repo를 제외하고 따로 설명을 진행하지는 않겠습니다. 325 | 326 | Repo는 여러 Repo를 한꺼번에 보여줄 수 있습니다. 가령 **Back-end, Front-end Repository가 각각 두 개 이고 그 Repo를 한 보드에 표시할 수 있습니다.** 실무 개발에서는 여러 Repo가 있기 때문에 이것을 한 보드에서 볼 수 있다는 것은 정말 좋은 Filter 기능이라고 생각합니다. 327 | 328 | 329 | > 해당 코드는 [Github](https://github.com/cheese10yun/github-action)에서 확인할 수 있습니다. 330 | 331 | # Github Action 332 | 333 | Github Action을 통해서 깃허브 자체적으로 CI & CD를 진행할 수 있습니다. Github에 대한 자세한 설명은 [공식홈페이지](https://github.com/features/actions)를 참고 해주세요. 본 포스팅에서는 Spring Boot & Gradle 환경에서 간단한 빌드를 다룰 예정입니다. 334 | 335 | 336 | ## Github Action 만들기 337 | 338 | Github Repository 상단에 `Actions`을 클릭 합니다. 339 | 340 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/github-action-1.png) 341 | 342 | Java With Gradle Action의 `Set up this workflow` 버튼을 클릭합니다. 343 | 344 | 345 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/github-action-2.png) 346 | 347 | `Java With Gradle Action`의 YML을 생성합니다. 348 | 349 | ### gradle.yml 350 | 351 | ```yml 352 | name: Java CI with Gradle 353 | 354 | on: 355 | push: 356 | branches: [ master ] 357 | pull_request: 358 | branches: [ master ] 359 | 360 | jobs: 361 | build: 362 | 363 | runs-on: ubuntu-latest 364 | 365 | steps: 366 | - uses: actions/checkout@v2 367 | - name: Set up JDK 1.8 368 | uses: actions/setup-java@v1 369 | with: 370 | java-version: 1.8 371 | - name: Grant execute permission for gradlew 372 | run: chmod +x gradlew 373 | - name: Build with Gradle 374 | run: ./gradlew build 375 | ``` 376 | 377 | `on.push`, `on.pull_request`을 보면 `master` branch에 `push`, `pull_request` 이벤트가 발생하는 경우 해당 `jobs`가 실행됩니다. `build`에서는 JDK 설정, Gradle 설정을 진행하고 최종적으로 `./gradlew build` 진행합니다. 378 | 379 | 380 | 381 | ### Action Workflows 382 | 383 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/github-action-3.png) 384 | 385 | `master`에 `push`, `pull_request` 이벤트가 발생할 경우 해당 Github Action이 동작하게 됩니다. 386 | 387 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/github-action-4.png) 388 | 389 | Event를 클릭하면 상세 Github Action에 대한 내용을 살펴볼 수 있습니다. 390 | 391 | ### Badge 392 | 393 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/github-action-5.png) 394 | 395 | 396 | 오른쪽 상단에 `Create status badge` 버튼을 클릭해서 Badge를 Markdown Copy를 진행할 수 있습니다. ![Java CI with Gradle](https://github.com/cheese10yun/github-action/workflows/Java%20CI%20with%20Gradle/badge.svg?branch=master) 397 | 398 | ## Schedule With Spring Batch 399 | 400 | Github Action은 `schedule` 기능을 제공하고 있습니다. Spring Batch를 이용하여 간단한 schedule Job을 작성해보겠습니다. 401 | 402 | ### Schedule Github action 생성 403 | ```yml 404 | # simple-job.yml 405 | name: Simple Job 406 | 407 | on: 408 | schedule: 409 | - cron: '*/5 * * * *' 410 | 411 | jobs: 412 | build: 413 | runs-on: ubuntu-latest 414 | steps: 415 | - uses: actions/checkout@v2 416 | - name: Set up JDK 1.8 417 | uses: actions/setup-java@v1 418 | with: 419 | java-version: 1.8 420 | - name: Grant execute permission for gradlew 421 | run: chmod +x gradlew 422 | - name: Build with Gradle 423 | run: ./gradlew build -x test 424 | - name: Commpany Save Job Execute 425 | run: java -jar -Dspring.batch.job.names=simpleJob ./build/libs/action-0.0.1-SNAPSHOT.jar 426 | ``` 427 | 위에서 생성한 `gradle.yml`을 기반으로 schedule Github Action을 위한 `simple-job.yml`을 생성합니다. `cron: '*/5 * * * *'` 해당 설정으로 5분마다 스케줄을 지정합니다. 428 | 429 | ### Batch Code 430 | 431 | ```kotlin 432 | @Configuration 433 | class SimpleJobConfig( 434 | private val jobBuilderFactory: JobBuilderFactory, 435 | private val stepBuilderFactory: StepBuilderFactory 436 | ) { 437 | 438 | @Bean 439 | fun simpleJob(): Job { 440 | return jobBuilderFactory.get("simpleJob") 441 | .incrementer(RunIdIncrementer()) 442 | .start(simpleStep()) 443 | .build() 444 | } 445 | 446 | private fun simpleStep(): Step { 447 | return stepBuilderFactory.get("simpleStep1") 448 | .tasklet { _, _ -> 449 | 450 | Unirest.post("https://hooks.slack.com/services/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") 451 | .header("Content-Type", "application/json") 452 | .body(""" 453 | { 454 | "text": "${LocalDateTime.now()}" 455 | } 456 | """.trimIndent()) 457 | .asString() 458 | 459 | RepeatStatus.FINISHED 460 | } 461 | .build() 462 | } 463 | } 464 | ``` 465 | Slack 으로 현재 시간을 보내는 메시지를 전송하는 Job입니다. 466 | 467 | ![](https://raw.githubusercontent.com/cheese10yun/github-action/master/images/simple-github.png) 468 | 469 | Simple Job Action에 대한 스케줄을 확인할 수 있습니다. 이처럼 schedule 기능을 이용하면 간단하게 Schedule Batch Job을 구성할 수 있습니다. 470 | -------------------------------------------------------------------------------- /docs/part-01.md: -------------------------------------------------------------------------------- 1 | # 프로젝트 소개 2 | Github를 이용해서 Project Management 하는 방법 및 전체적인 프로세스에 대해서 정리했습니다. **issue 관리, 일정 관리, 코드리뷰, 버그 리포트 등 다양한 일들을 Github 하나에서 다 관리할 수 있고 어느 하나 부족하다고 생각하지 않습니다.** 이미 Remote Repository로 Github를 사용 중 이리 사면 적극 추천해 드립니다. 프로젝트 전체 코드는 [GitHub](https://github.com/cheese10yun/github-project-management)에 공개 되어있습니다. 3 | 4 | # 목차 5 | * [Part1 - 이슈 발급 부터 코드리뷰까지](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part1-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EA%B8%89-%EB%B6%80%ED%84%B0-%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0%EA%B9%8C/) 6 | * [Part2 - CI & Test Coverage & Wiki](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part2-ci-test-coverage-wiki/) 7 | * Part3 - ZenHub 사용법 8 | 9 | 10 | # 전체 플로우 11 | 1. Isuee 발급 12 | 2. Issue 작업 13 | 3. Pull Request Coide Review 진행 14 | 4. Issue 반영 15 | 16 | # Issue 발행 17 | 18 | ## Issue란? 19 | 모든것이 이슈라고 볼 수 있습니다. 새로운 추가될 가능, 개선 해야할 가능, 버그 등등 모든것이 이슈라고 볼 수 있습니다. 모든 활동 내역에 대해서 이슈를 등록하고 그 이슈기반으로 작업을 진행하게 됩니다. 20 | 21 | 이슈를 등록할 때 자주 사용하는 템플릿을 등록해서 사용하는 방법이 효율적입니다. 이슈 템플릿을 등록하는 방법을 소개해드리겠습니다. 22 | 23 | ## Issue Template 24 | 25 | ### Issue Template 등록 26 | ![](https://github.com/cheese10yun/github/blob/master/images/issue-template-rg.gif?raw=true) 27 | 28 | 환경에 앎맞는 Issue Template 생성을 합니다. 29 | 30 | 31 | ### Issue Template 사용법 32 | ![new_issue](https://github.com/cheese10yun/github/blob/master/images/add-new-issue.gif?raw=true) 33 | 34 | 위에서 등록한 Issue Template 기반으로 이슈를 생성할 수 있습니다. 35 | 36 | 37 | ### Issue Template 파일 38 | 39 | ``` 40 | . 41 | ├── .github 42 | │   └── ISSUE_TEMPLATE 43 | │   ├── bug_report.md 44 | │   └── feature_request.md 45 | ├── README.md 46 | ├── github.iml 47 | ├── images 48 | ├── mvnw 49 | ├── mvnw.cmd 50 | ├── pom.xml 51 | └── src 52 | ``` 53 | 위에서 등록된 Issue Template은 .github/ISSUE_TEMPLATE 디렉터리에 생성된 것을 확인할 수 있습니다. 54 | **각자의 맞는 한경에 따라서 Issue Template를 작성하시면 됩니다.** 저 같은 경우에는 Back-end를 주로 담당하기 때문에 bug tempalte 에서 서버로그, response body 값을 등록했습니다. 55 | 56 | 57 | # Issue 작업 58 | 59 | ## 등록된 issue 살펴 보기 60 | ![등록된-이슈](https://i.imgur.com/2ciNoCd.png) 61 | 62 | * Assignees : 해당 작업의 담당자 63 | * Labels: 해당 작업의 성격 64 | * Milestone: 해당 작업이 속한 파트 65 | 66 | ![](https://i.imgur.com/DkniJHn.png) 67 | 68 | 다른 것들은 이해하기 쉬울 텐데 Milestone은 조금 생소할 수 있습니다. Milestone에 간단하게 설명해 드리면 이번 출시 버전이 1.0.0 일 경우 해당 버전이든 이슈(작업) 기능 강화, 새 기능추가, 버그 기타 등등 모든 이슈를 Version 1.0.0 Milestone이라는 항목에 추가하면 위 그림처럼 Version 1.0.0에 대한 전체적인 상황을 한눈에 볼 수가 있는 장점이 있습니다. 69 | 70 | ## Issue 연동 71 | ![intellij-task](https://i.imgur.com/FtO0Xme.png) 72 | 73 | 만약 Jetbrains의 IDE를 사용하고 계신다면 Task 연동을 통해서 Github와 연동하시는 것을 적극 권장해 드립니다. 74 | 75 | ## Issue 기반 Bracnh 생성 76 | ![issue-base-branch](https://i.imgur.com/R8aFoCL.png) 77 | 위에서 언급한 Jetbrains의 Task 연동을 하지 않아도 크게 상관없습니다. Task의 갖는 가장 큰 기능은 Github 이슈 기반으로 Branch를 생성을 쉽게 도와주는 것으로 생각합니다. **즉 Github에서 생성된 Issue 기반으로 Branch를 생성하는 것이 핵심입니다.** 78 | 79 | Github Issue는 각자의 유니크한 값인 Issue Number를 갖습니다. 또 그 Iusse Number 기반으로 Branch를 이름을 갖게 하여 해당 Branch의 명확한 작업의 의도를 갖게 할 수 있습니다. 80 | 81 | Branch 네이밍을 통해서 해당 작업의 의도를 갖게 하는 것은 한계가 있습니다. 또 동료 개발자들이 정확히 무슨 작업을 하는지도 Branch 네이밍을 통해서 유추해내기도 어렵고, 해당 작업이 반영(머지)될 때 도 마찬가지입니다. 이러한 문제들을 Issue Number 기반으로 Branch를 생성(Issue Number Branch 네이밍에 추가)하면 아주 명확해집니다. 82 | 83 | # Pull Request[Code Review] 84 | [issue-1](https://github.com/cheese10yun/github/issues/1)에 대한 풀리퀘스트를 통해서 코드리뷰를 진행해 보겠습니다. 85 | 86 | ## Jetbrains Pull Request 87 | ![intellij-pull-request](https://i.imgur.com/vkNR06g.png) 88 | 만약 Jetbrains IDE를 사용하신다면 위 방법 처럼 Pull Request를 하는 방법을 권장드립니다. 89 | 90 | ## GitHub Pull Request 91 | ![github-pull-request](https://i.imgur.com/6bBTJUV.png) 92 | 93 | Github Code 텝에서 `New Pull Request` 버튼을 클릭해서 Pull Request를 진행 합니다. 94 | 95 | ## Pull Request 작성법 96 | ![](https://i.imgur.com/3TnHt0c.png) 97 | 98 | * 왼쪽 위에 Reviewers 톱니바퀴 버튼을 클릭해서 리뷰어를 지정합니다. 99 | * resolved: #1(해당 Issue Number) 풀리퀘스트 요청하는 이유 즉 무슨 이슈에 대한 작업인지 명시합니다. 100 | 101 | `resolved` 키워드를 입력하면 해당 풀리퀘스트가 master Branch에 반영되면 자동으로 close 됩니다. 자동으로 close 되는 것이 싫으시다면 issue: #[해당 Issue Number]를 작성해주세요. 102 | 103 | 이렇게 Pull Request가 생성되면 새로운 Issue Number가 부여됩니다. **즉 Pull Request도 Issue입니다.** 104 | 105 | ![issue-pull-request-연결](https://i.imgur.com/skNmpeQ.png) 106 | 107 | **반드시 해당 풀리퀘스트가 무슨 이슈에 따른 요청인지 명시하시는 것을 권장합니다.** 그렇게 되면 위 그림처럼 해당 이슈에 #2[방금 요청한 풀리퀘스트]가 연결되어 해당 이슈가 무슨 코드로 인해서 진행됐는지 추적하기 좋습니다. 108 | 109 | ## Code Review 110 | 리뷰어가 요청받은 Pull Request로 가서 `Add your review` 버튼을 클릭합니다. 111 | 112 | ![리뷰진행](https://i.imgur.com/k11vL5w.png) 113 | 소스코드에 대한 질문 등 다양한 comment를 남기는 방식으로 pull reqeust가 진행합니다. 114 | 115 | * Approve: 코드에 대한 의문점이 없다면 승인 . 116 | * Comment: 간단한 피드백 제출 117 | * Request changes: 해당 코드에 문제가 있다고 판단되며 코드를 반드시 수정 요구 118 | 119 | 위 항목은 Comment로 Submit review를 진행했습니다. 120 | 121 | ![comment-표시](https://i.imgur.com/EHnVEjU.png) 122 | 위에서 작성한 comment가 해결됬었다면 `Merge pull request` 버튼을 눌러서 해당 pull request를 반영합니다. 반영이 완료되고 해당 branch가 더는 필요 없다고 판단되시면 `Delete branch` 버튼을 통해서 Remote에 있는 Branch를 삭제할 수 있습니다. 123 | 124 | **위에서 작성한 resolved: #1 키워드 덕분에 소스코드가 해당 Branch에 적용됐으니 자동으로 #1에 대한 이슈는 close 처리됩니다.** -------------------------------------------------------------------------------- /docs/part-02.md: -------------------------------------------------------------------------------- 1 | # 프로젝트 소개 2 | Github를 이용해서 Project Management 하는 방법 및 전체적인 프로세스에 대해서 정리했습니다. **issue 관리, 일정 관리, 코드리뷰, 버그 리포트 등 다양한 일들을 Github 하나에서 다 관리할 수 있고 어느 하나 부족하다고 생각하지 않습니다.** 이미 Remote Repository로 Github를 사용 중 이리 사면 적극 추천해 드립니다. 프로젝트 전체 코드는 [GitHub](https://github.com/cheese10yun/github-project-management)에 공개 되어있습니다. 3 | 4 | # 목차 5 | * [Part1 - 이슈 발급 부터 코드리뷰까지](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part1-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EA%B8%89-%EB%B6%80%ED%84%B0-%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0%EA%B9%8C/) 6 | * [Part2 - CI & Test Coverage & Wiki](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part2-ci-test-coverage-wiki/) 7 | * Part3 - ZenHub 사용법 8 | 9 | # CI & Test Coverage 10 | ![](https://i.imgur.com/G5jo0Ty.png) 11 | 12 | [GitHub Marketplace](https://github.com/marketplace/category/continuous-integration) Public Repository를 이용하면 대부분 무료로 이용 가능합니다. **본 포스팅에서는 CI는 Travis CI, Test Coverage는 Coveralls를 이용해서 진행하겠습니다.** 13 | 14 | 전체적인 플로우를 설명하는 것이 목적 이리서 특정 툴에 대한 직접적인 사용법을 다루지는 않겠습니다. 언어의 특성 및 개인에 기호에 맞는 제품을 사용하시면 됩니다. 15 | 16 | ## 전체 플로우 17 | 1. Pull Request 요청 -> Code Review 진행 18 | 2. Code Review 완료 -> 특정 Branch에 반영 19 | 3. 특정 Branch 수정시 자동 CI Build 작업 진행 -> 테스트 코드 실행 20 | 4. 테스트 커버지리 표시 21 | 22 | ## Pull Request & Code Review 23 | ![](https://i.imgur.com/q6HmT7o.png) 24 | 25 | 별다른 설정을 하지 않았다면 Pull Request를 요청할 경우 Travis에서 자동으로 해당 요청한 코드 기반으로 Build 작업이 진행됩니다. Build가 실패했을 경우는 Pull Request 요청자는 코드를 수정해서 최소한 Build가 된 코드 기반으로 Code Review를 진행하게 해야 됩니다(Build도 안 되는 코드를 리뷰할 이유는 없을 거 같습니다.) 26 | 27 | 요청받은 Pull Request에 대해서 Code Review 작업을 진행하게 됩니다. Code Review가 완료되면 Merge pull request를 통해서 해당 작업(issue)을 반영합니다. 28 | 29 | ## 테스트 커버지리 표시 30 | ![](https://i.imgur.com/U1ROYeE.png) 31 | 32 | 위에서 Merge pull request를 통해서 해당 작업(issue)을 반영했다면 Travis가 Build 할 때 작성된 Test Code 기반으로 Coverage 정보를 위처럼 자동으로 코멘드를 추가해줍니다. 33 | 34 | 누군가가 테스트 코드를 작성하지 않았다면 `Change from base` 항목에서 - 표시가 됩니다. **이렇게 해당 작업마다 커버리지를 표시하는 것이 전체 커버리지를 높이고 그 값을 유지하는 좋은 방법이라고 생각합니다.** 35 | 36 | # Wiki 37 | GitHub에서는 기본적인 WIKI 기능을 제공해줍니다. 38 | 39 | ## Wiki 등록 40 | ![](https://github.com/cheese10yun/github/blob/master/images/wiki.gif?raw=true) 41 | 42 | ## GitHub 작성 43 | ![](https://i.imgur.com/t3LuQsm.png) 44 | GitHub에서 위키를 작성할 수 있습니다. 45 | 46 | ## Local 작성 47 | ![](https://i.imgur.com/Ff6Heeb.png) 48 | **위키를 로컬환경에서 작성해서 Push 할 수 있습니다. 이렇게 사용하시는 것을 추천 드립니다.** -------------------------------------------------------------------------------- /docs/part-03.md: -------------------------------------------------------------------------------- 1 | # 프로젝트 소개 2 | Github를 이용해서 Project Management 하는 방법 및 전체적인 프로세스에 대해서 정리했습니다. **issue 관리, 일정 관리, 코드리뷰, 버그 리포트 등 다양한 일들을 Github 하나에서 다 관리할 수 있고 어느 하나 부족하다고 생각하지 않습니다.** 이미 Remote Repository로 Github를 사용 중 이리 사면 적극 추천해 드립니다. 프로젝트 전체 코드는 [GitHub](https://github.com/cheese10yun/github-project-management)에 공개 되어있습니다. 3 | 4 | # 목차 5 | * [Part1 - 이슈 발급 부터 코드리뷰까지](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part1-%EC%9D%B4%EC%8A%88-%EB%B0%9C%EA%B8%89-%EB%B6%80%ED%84%B0-%EC%BD%94%EB%93%9C%EB%A6%AC%EB%B7%B0%EA%B9%8C/) 6 | * [Part2 - CI & Test Coverage & Wiki](https://www.popit.kr/github%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-part2-ci-test-coverage-wiki/) 7 | * Part3 - ZenHub 사용법 8 | 9 | # ZenHub 사용법 10 | 11 | ![](https://i.imgur.com/F5c78MC.png) 12 | 13 | 수 많은 이슈들이 발급되면 그것을 한눈에 보는 것은 기존 UI에서는 어렵습니다. 이런 문제를 칸반보드 형식으로 해결해주는 것이 ZenHub 입니다. 14 | 15 | ![](https://cristinasantamarina.files.wordpress.com/2015/06/zenhub-task-board.png) 16 | Public Repository를 이용 중이라면 무료로 사용 가능합니다. Private Repository는 비용을 지급하셔 야합니다. 17 | 18 | 물론 GitHub에서 제공해주는 Proeject도 칸반보드를 제공해주어서 대안이 되지만 여러 레파지토리에 대한 표시, 애픽 기능 등 다양한 기능들을 제공해주기 때문에 개인적으로 ZenHub를 추천해 드립니다. 19 | 20 | **설치는 크롬 확장도구에서 다운로드 받으실 수 있습니다.** 21 | 22 | ## 기본 칸반보드 23 | ![](https://i.imgur.com/GvP6XPh.png) 24 | 25 | 기본적인 구조는 New Issue, Icebox, Backlog, In Progress, Review/QA, Done을 갖습니다. 각자 본인의 프로젝트와 성향에 맞게 사용하시면 됩니다. 26 | 27 | 저 같은 경우에는 생각나는 모든 것들을 New Issue 항목에 추가하고 우선순위가 낮고 당장 필요 없는 작업은 Icebox, 우선순위가 높은 작업은 Backlog 항목에 넣습니다. 진행 중인 작업은 In Progress 나머지 Review/QA는 Pull Request 항목, Issue가 Close 되면 자동으로 Close으로 가게 됩니다. 28 | 29 | **이처럼 모든 항목에 대해서 Issue로 관리하고 해당 Issue Number 기반으로 Branch가 생성되면서 코드리뷰, 반영 작업 사이클이 돌아가는 구조입니다.** 30 | 31 | ## Milestone 적극 활용 32 | ![](https://i.imgur.com/3lDuAxi.png) 33 | 34 | ZenHub 사용과 직접적인 기능은 아니지만, Milestone 여러 항목을 만들고 대부분 Icebox에 있는 issue가 아니라면 적절한 Milestone에 위치시키는 것을 권장합니다. 전체적인 작업에 진행 척도를 가시적으로 확인하기 좋은 점, 해당 Milestone(버전)에 추가되는 기능, 강화되는 기능, 수정된 버그 등을 직관적이로 표시 할 수 있습니다. 또 전체 Milestone 진척도를 알 수 있어 남은 시간도 산출하기 좋은 장점이 있습니다. 35 | 36 | ## Epic 적극 활용 37 | ![](https://i.imgur.com/dZhrATb.png) 38 | Epic은 Milestone과 비슷하게 이해하시면 됩니다. 큰 작업(Issue)이 있다면 그 작업(Issue)을 여러 Issue로 등록하고 한 묶음으로 관리하는 것입니다. 39 | 40 | Milestone의 장점처럼 작업에 대한 진척도를 가시적으로 표시하는 장점이 있습니다. 또 큰 작업물에 대한 작업은 여러 개발자가 나누어서 하다 보니 내가 그 기능을 작업하기 전에 선행적으로 진행되어야 하는 작업도 있을 수 있습니다. 이런 것들을 대한 피드백을 Epic을 이용하면 관리하기 편합니다. 41 | 42 | 43 | ![](https://i.imgur.com/nIMGciB.png) 44 | 해당 Issue를 Epic으로 등록 시킬 경우 Create an epic 버튼을 클릭하면 됩니다. 45 | 46 | ![](https://i.imgur.com/lN2uziy.png) 47 | Issue를 등록할 때 오른쪽 하단의 Epic에서 등록시킬 수 있습니다. 48 | 49 | ## Issue 연결 50 | 51 | ![](https://i.imgur.com/XYa3My3.png) 52 | Connect with an issue 기능을 통해서 이슈끼리 연결을 할 수 있습니다. **위처럼 Pull Request를 할 때 아주 유용하게 사용할 수 있습니다.** 53 | 54 | ![](https://i.imgur.com/DPrWlUd.png) 55 | Issue 13과 해당 이슈를 작업해서 Pull Request Issue 17 번이 ZenHub 보드에서 연결돼 있는 것을 확인 할 수 있습니다. **이처럼 연관된 작업을 묶어주는 장점이 있습니다.** 56 | 57 | ## Filter 기능 58 | 59 | ![](https://i.imgur.com/4pZEYsB.png) 60 | 61 | 다양한 Filter 기능을 제공합니다. Label, Assignee, Milestone, Repo 등등이 있습니다. 해당 기능은 직관적이니 62 | Repo를 제외하고 따로 설명을 진행하지는 않겠습니다. 63 | 64 | Repo는 여러 Repo를 한꺼번에 보여줄 수 있습니다. 가령 **Back-end, Front-end Repository가 각각 두 개 이고 그 Repo를 한 보드에 표시할 수 있습니다.** 실무 개발에서는 여러 Repo가 있기 때문에 이것을 한 보드에서 볼 수 있다는 것은 정말 좋은 Filter 기능이라고 생각합니다. 65 | -------------------------------------------------------------------------------- /docs/part-05.md: -------------------------------------------------------------------------------- 1 | 인텔리제이기반으로 Github 기반 Pull Request & Code Review를 진행할 수 있습니다. 바로 기능을 살펴보겠습니다. 인텔리제이 기반 Pull Request는 [Github로 프로젝트 관리하기 Part1](https://cheese10yun.github.io/github-proejct/#pull-requestcode-review-1)을 참고해 주세요 2 | 3 | ## Code Review 4 | 5 | ![](../images/intellij-pr-3.png) 6 | 7 | `Actions`에서 `View Pullrequests`을 통해서 현재 PR을 확인할 수 있습니다. 8 | 9 | 10 | ![](../images/intellij-pr-4.png) 11 | 12 | `state:open `을 통해서 현재 open 상태이 PR 리스트를 확인할 수 있고 우측에는 코드 리뷰 대상 파일이 있고 `User.java` 파일을 클릭해서 Diff를 확인할 수 있습니다. 13 | 14 | ![](../images/intellij-pr-5.png) 15 | 해당 코드에 대해서 코멘트를 추가하고 싶은 경우 `+` 버튼을 클릭해서 코멘트를 달 수 있습니다. 16 | 17 | ![](../images/intellij-pr-6.png) 18 | 19 | ![](../images/intellij-pr-7.png) 20 | 21 | 이렇게 추가한 코멘트는 Github, Intellij에서도 확인할 수 있으며 코멘트에 대한 코멘트도 추가할 수 있습니다. 22 | 23 | ![](../images/intellij-pr-8.png) 24 | 또 인텔리제이에서 머지도 가능합니다. `Merge` 버튼을 눌러서 머지를 진행합니다. [인텔리제이 PR & Code Review #24](https://github.com/cheese10yun/github-project-management/pull/24)는 실제 PR이 머지 된 것을 확인할 수 있습니다. 25 | 26 | ![](../images/intellij-pr-9.png) 27 | 위처럼 질이를 통해서 PR에 `status`, `author`, `assignee`을 기반으로 검색을 할 수 있습니다. 28 | -------------------------------------------------------------------------------- /images/add-new-issue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/add-new-issue.gif -------------------------------------------------------------------------------- /images/intellij-pr-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-3.png -------------------------------------------------------------------------------- /images/intellij-pr-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-4.png -------------------------------------------------------------------------------- /images/intellij-pr-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-5.png -------------------------------------------------------------------------------- /images/intellij-pr-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-6.png -------------------------------------------------------------------------------- /images/intellij-pr-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-7.png -------------------------------------------------------------------------------- /images/intellij-pr-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-8.png -------------------------------------------------------------------------------- /images/intellij-pr-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/intellij-pr-9.png -------------------------------------------------------------------------------- /images/issue-template-rg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/issue-template-rg.gif -------------------------------------------------------------------------------- /images/wiki.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheese10yun/github-project-management/71305ef3d8cd70b03958803ba5cfe7b8855849e5/images/wiki.gif -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.sample 7 | github 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | github-project-management 12 | Using Github 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.15.BUILD-SNAPSHOT 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | 38 | org.projectlombok 39 | lombok 40 | true 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | org.springframework.restdocs 49 | spring-restdocs-mockmvc 50 | test 51 | 52 | 53 | com.h2database 54 | h2 55 | runtime 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | org.eluder.coveralls 68 | coveralls-maven-plugin 69 | 70 | GooziNX1Xf09bAlRlDgFHagxht8YhtbtW 71 | 72 | 73 | 74 | 75 | org.codehaus.mojo 76 | cobertura-maven-plugin 77 | 78 | xml 79 | 256m 80 | 81 | true 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | spring-snapshots 92 | Spring Snapshots 93 | https://repo.spring.io/snapshot 94 | 95 | true 96 | 97 | 98 | 99 | spring-milestones 100 | Spring Milestones 101 | https://repo.spring.io/milestone 102 | 103 | false 104 | 105 | 106 | 107 | 108 | 109 | 110 | spring-snapshots 111 | Spring Snapshots 112 | https://repo.spring.io/snapshot 113 | 114 | true 115 | 116 | 117 | 118 | spring-milestones 119 | Spring Milestones 120 | https://repo.spring.io/milestone 121 | 122 | false 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/com/sample/github/GithubApplication.java: -------------------------------------------------------------------------------- 1 | package com.sample.github; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GithubApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GithubApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sample/github/model/Address.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.model; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.Column; 9 | import javax.persistence.Embeddable; 10 | 11 | @Embeddable 12 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 13 | @Getter 14 | public class Address { 15 | 16 | @Column(name = "street") 17 | private String street; 18 | 19 | @Column(name = "address") 20 | private String address; 21 | 22 | @Column(name = "zip_code") 23 | private String zipCode; 24 | 25 | @Builder 26 | public Address(String street, String address, String zipCode) { 27 | this.street = street; 28 | this.address = address; 29 | this.zipCode = zipCode; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sample/github/model/Email.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.model; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.Column; 9 | import javax.persistence.Embeddable; 10 | 11 | @Embeddable 12 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 13 | @Getter 14 | public class Email { 15 | 16 | @org.hibernate.validator.constraints.Email 17 | @Column(name = "email", nullable = false, unique = true, updatable = false) 18 | private String value; 19 | 20 | @Builder 21 | public Email(String value) { 22 | this.value = value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/sample/github/user/User.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.user; 2 | 3 | import com.sample.github.model.Address; 4 | import com.sample.github.model.Email; 5 | import lombok.AccessLevel; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | import javax.persistence.*; 11 | 12 | @Entity 13 | @Table(name = "user") 14 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 15 | @Getter 16 | public class User { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.AUTO) 20 | private long id; 21 | 22 | @Embedded 23 | private Email email; 24 | 25 | @Embedded 26 | private Address address; 27 | 28 | @Builder 29 | public User(Email email, Address address) { 30 | this.email = email; 31 | this.address = address; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/sample/github/user/UserFindService.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.user; 2 | 3 | import lombok.AllArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | @Service 8 | @Transactional 9 | @AllArgsConstructor 10 | public class UserFindService { 11 | 12 | private UserRepository userRepository; 13 | 14 | public User findById(long id) { 15 | final User user = userRepository.findOne(id); 16 | if (user == null) throw new RuntimeException(); 17 | return user; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sample/github/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.user; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface UserRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: local 4 | 5 | # Local 환경 6 | --- 7 | spring: 8 | profiles: local 9 | 10 | jpa: 11 | database: h2 12 | hibernate: 13 | ddl-auto: create-drop 14 | show-sql: true 15 | 16 | 17 | logging: 18 | level: 19 | ROOT: error -------------------------------------------------------------------------------- /src/test/java/com/sample/github/GithubApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sample.github; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class GithubApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | GithubApplication.main(new String[]{}); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/sample/github/user/UserFindServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sample.github.user; 2 | 3 | import com.sample.github.model.Address; 4 | import com.sample.github.model.Email; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.InjectMocks; 8 | import org.mockito.Mock; 9 | import org.mockito.runners.MockitoJUnitRunner; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.hamcrest.CoreMatchers.notNullValue; 13 | import static org.junit.Assert.assertThat; 14 | import static org.mockito.BDDMockito.given; 15 | import static org.mockito.Matchers.anyLong; 16 | 17 | @RunWith(MockitoJUnitRunner.class) 18 | public class UserFindServiceTest { 19 | 20 | @InjectMocks 21 | private UserFindService userFindService; 22 | 23 | @Mock 24 | private UserRepository userRepository; 25 | 26 | @Test 27 | public void findById_유저가있을경우_해당유저리턴() { 28 | //given 29 | final User user = buildUser(); 30 | given(userRepository.findOne(anyLong())).willReturn(user); 31 | 32 | //when 33 | final User findUser = userFindService.findById(anyLong()); 34 | 35 | //then 36 | assertThat(findUser.getId(), is(notNullValue())); 37 | assertThat(findUser.getEmail().getValue(), is(user.getEmail().getValue())); 38 | assertThat(findUser.getAddress().getAddress(), is(notNullValue())); 39 | assertThat(findUser.getAddress().getStreet(), is(notNullValue())); 40 | assertThat(findUser.getAddress().getZipCode(), is(notNullValue())); 41 | } 42 | 43 | @Test(expected = RuntimeException.class) 44 | public void findById_유저가존재하지않는경우() { 45 | //given 46 | given(userRepository.findOne(anyLong())).willReturn(null); 47 | 48 | //when 49 | userFindService.findById(anyLong()); 50 | } 51 | 52 | private User buildUser() { 53 | return User.builder() 54 | .email(Email.builder().value("yun@test.com").build()) 55 | .address(Address.builder() 56 | .address("address") 57 | .street("street") 58 | .zipCode("zip-code") 59 | .build()) 60 | .build(); 61 | } 62 | } --------------------------------------------------------------------------------