들어가기 앞서.

 분산버전 롼리시스템은 다른사람과의 협업을 염두에두고 만들어진 툴입니다. 결국 원격저장소와 로컬 저장소 사이를 얼마나 효율적으로 관리하느냐가 관건입니다. Git에서는 원격저장소와 소통하기 위한 기능을 제공하고있습니다.



GIT 명령어를 알아봅시다.


git clone:   원격 저장소의 모든 내용을 로컬저장소에 복사합니다.

git remote: 로컬저장소를 특정 원격 저장소와 연결합니다.

git push:    로컬 저장소의 내용을 보내거나 로컬 저장소의 변경사항을 원격 저장소로 보냅니다.

git fetch:    로컬저장소와 원격 저장소의 변경사항이 다를때 이를 대조하고 git merge 명령어와 함께 최신 데이터를 반영하거나 충돌문제등을 해결합니다.

git pull:      git remote 명령을 통해 서로 연결된 원격저장소의 최신내용을 로컬 저장소로 가져오면서 병합합니다. gir push와 반대 성격의 명령입니다. 




Git Clone: 원격저장소의 내용을 로컬 저장소로 가져옵니다.


클론을 정의하자면 다음과 같습니다.

1. 내가 생성한 원격 저장소를 내컴퓨터와 연결해서 데이터를 복사하는 작업.

2. 포크한 원격 저장소를 내 컴퓨터와 연결해서 데이터를 복사하는 작업.


앞선 포스팅에서 만들었던 http 주소를 가져옵니다.


그리고 앞서 블로그에서 설치했던 GIT BASH를 실행 해주십다. 

$ mkdir git_remote

$ cd git_remote

그리고 clone 명령을 해서 땡겨옵니다.

$ git clone https://github.com/pjh2174/myTistoryExam.git


$ cd myTistoryExam

$ git status


status 명령을 치면, 현재 아무것도 커밋되어있지않고, clean한 디렉토리임을 알 수 있습니다.



Git remote: 로컬저장소와 원격저장소를 연결합니다.

앞부분에선 cloen을 통해 생성했던 원격저장소를 로컬저장소로 파일을 복사했습니다. 하지만 뭔가 모순된점이 있을 수 도 있는데요.

만약 유저가 이미 로컬에서 작업했던 내용이 있는 상태였다면, 원격저장소에있는걸 clone할필요없이 바로 연결해서 올려야하는 절차가 필요할 수도있습니니다. 그렇기때문에 일단 깃허브에서 원격 저장소를 임의로 생성해봅니다.

$ git remote add origin https://github.com/pjh2174/myRemoteExam.git

저는 이전블로그에 hello.py 가있는 브랜치로 접근하여 위의 명령어를 쳤습니다.

원격 저장소와 연결되었는지 호가인하려면 아래와 같이 명령을 치도록합니다.


$ git remote -v



git push: 로컬 작업 내역을 원격 저장소에 올립니다.


이제 원격저장소와 로컬 저장소가 연결되었습니다. 이젠 원격저장소에 자신의 결과물을 업로드 해야할 차례입니다. git push라는 명령어를 사용하면 수월하게 진행하실 수 있습니다.


$git push


음 거절당하게 될겁니다. 어느 원격 저장소로 발행할 것인지, 로컬의 어느 브랜치에 푸시할 것인지 명시하지 않았기 때문입니다.


$git push origin --all 


명령어를 자세히 분석해보겠습니다.

$ git push 원격저장소별칭 로컬브랜치이름 

위에서 실행한 -all은 origin 저장소에 로컬의 모든 브랜치를 푸시하는겁니다. git은 원격 저장소에 로컬 저장소의 브랜치와 같은 이름의 브랜치가 있다면 해당 브랜치를 변경하고 없다면 새브랜치를 원격 저장소에 만듭니다. 단 주의할점은, 같은 이름의 브랜치가 있는데 서로의 내역이 다르다면 푸시를 거부하게 됩니다. 


아무튼 명령을 수행하면 깃허브 로그인창이뜨게되고, 아이디와 비번을 입력합니다. 성공적으로 업로드가 수행되어집니다.

성공적으로 올라온것을 확인하실 수 있습니다.



원격저장소에 파일을 하나 추가한후 다시 내용을 푸시해보도록하겠습니다.


$ cat >> README.md

remote repository of myRemoteExam

$ git add README.md

$ git commit -m "remote repository add a README.md files"


자 다시한번 push해보겠습니다. 원격저장소 별칭(origin)과 master 브랜치에 push push 해보겠습니다.

$ git push origin master


역시 깃허브에 반영된것을 확인하실 수 있습니다. 추가된모습을 보실수 있으세요.


혹은 README.md 파일을 다른 브랜치에도 추가하고 싶다면, 로컬저장소에서 git push origin master 명령을 실행하기전에

로컬 저장소를 병합한 후, git push origin -all 형태로 명령을 실행하셔도 됩니다.

.



git fetch와 git pull: 원격저장소와 로컬 저장소의 간격 메꿉니다.


원격 저장소의 commit내역들을 Local 저장소로 가져와 병합하는 방법은 git fetch와 git pull  크게 두가지가 있습니다.

git pull 명령은 원격 저장소의 정보를 가져오면 자동으로 로컬 브랜치에 병합까지 수행하는 것입니다. 자동으로 병합되기때문에 어떤 점들이 변했는지 알아보기가 힘들 수 있습니다. 그러므로 git fetch를 사용하길 권고합니다.


git fetch와 git pull을 이용해보기위해 실습순서를 아래와 같이 진행할 것입니다.


1. 원격저장소, github에서 파일 수정합니다.

2. 로컬저장소에서도 같은파일 수정합니다.

3. push 시도와 실패합니다.

4. fetch 실시합니다.

5. 병합시도합니다.

6. 푸시 재시도를 할것입니다.

7. 원격저장소에서 제대로되었는지 확인할 것입니다.



github로 가서, hello.py 파일을 변경합니다. (원격저장소에서 commit 한다는 의미입니다.)



자이제 로컬저장소에서도 hello.py를 수정해봅시다.

그리고 아래와 같이 로컬에서 커밋해버립니다.

$ git commit -a -m "hello.py modified in local"


그리고 push를 원격저장소에 해봅니다.

$ git push origin master       master 로컬 브랜치의 내용을 origin 원격저장소에 push하겠다.


하지만 오류를 내면서 git pull 명령을 힌트로 알려줍니다만 별로 유용하지않습니다.


우리는 git fetch를 사용할것입니다.


$ git fetch

$ git status

뭐 큰이상 없는것같습니다.


$ git branch -a 

위의 명령을 치면 현재 어떤 브랜치가 있는지 알 수 있습니다. 모든 로컬저장소와 원격 저장소의 브랜치정보를 볼 수 있습니다.


$ git merge origin/master

위의 명령을 치면, 충돌이 발생했다고 출력합니다.


이제 충돌을 해결해봅시다.

git diff 브랜치이름 명령을 이용해봅시다. git diff는 로컬저장소의 브랜치와 원격 브랜치 저장소 사이에 어떤 차이점이 있는지 미리 알아보는 명령입니다.

만약 git pull명령을 이용했다면 페치와 병합을 자동으로 수행해버려서 어떤 변경사항이있는지 알기가 매우어려워집니다.

그리고 이제 로컬에서 원하는 방향으로 다시 수정해봅시다. conflict을 해결하는 방향으로요!.


그리고 다음 명령과 같이 커밋합니다.

$ git commit -a -m "conflict resolved github"
$ git push origin master


이제 제대로 병합된것을 확인하실수있습니다.


또한, insights -> graphs -> Network를 누르시면, 지금까지 작업과정을 확인하실 수 있습니다.



만약 위의 과정에서  git pull origin master 이런식으로 명령어를 쳤다면, 변경내용을 가져오고 자동으로 병합해버립니다. 그러므로 그상태에서 git commit 과 git merge명령을 실행해도 이미 작업할 것이 없다는 메세지를 확인하실 수 있습니다.



추천드리자면, fetch를 통해 원격저장소의 커밋을 가져오고 로컬저장소에서 이를 확인한 다음 수동으로 병합하는 방법을 가장 추천드립니다.




Android application Test 방법


크게 2가지가 있습니다. 안드로이트 테스트(Instrumentation Test)와 Local PC의 JVM을 활용하는 새로운 개념의 Local Unit Test 입니다. 


참고) 로컬 유닛 테스트는 로컬 JVM을 활용하기 때문에 타깃 디바이스와 연결피 필요없습니다. 그리고 테스트 코드의 전체 실행속도가 향상되는 효과가 있습니다. 안드로이드 테스트에서는 APK를 생성하여 타깃 디바이스에 설치하고 실행하는 과정에서 시간 소모가 많은 편입니다.



Gradle을 활용화여 안드로이드 테스트 코드와 로컬 유닛테스트코드를 실행하는 방법을 알아보겠습니다.




로컬 유닛 테스트

app module의 build.gradle을 보시겠습니다. dpendencies부분에 아래와 같이 testCompile이 웬만하면 디폴트로 지정되어있으실겁니다.


dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
}

그리고 ExampleUnitTest 클래스를 보시면 아래와 같이 코드가 미리작성되어있는것을 확인하실 수 있습니다. Junit4에서는 테스트코드에 @Test라는 애너테이션을 부착합니다.

public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

로컬유닛 테스트 코드를 실행하는 방법은 간단합니다. 패키지명을 우크릭하고 "Run Tests in 프로젝트명" 클릭하면 Run창에 테스트 결과가 출력됩니다. 

해당 unitTest 클래스 오른쪽 마우스로누르고, run "ExampleUnitTest" 선택시 정상적으로 테스트됩니다.


로컬유닛테스트를 실행할때 호출되는 중요한 Gradle Task는 다음과 같습니다.


:app:mockableAndroidJar UP-TO-DATE

:app:compileDebugUnitTestSources UP-TO-DATE


1. mock(app:mockableAndroidJar ) 테스크는 안드로이드의 android.jar 파일을 mock 인터페이스로 컴파일합니다. 로컬 유닛 테스트가 동작하려면 안드로이드 코드에 대한 mock 인터페이스가 필요합니다. mock 인터페이스 호출 시, 가짜객체를 주입하는 경우도 있습니다. 가짜 객체(mock object)를 이용한 테스트 기법은 구글문서를 참고하십시오.


mock 테스트 실행결과는 /build/generated/mockable-android-26.jar(26은 버전명) 입니다. 


2. complieDebugUnitTestSources 태스크입니다. 태스크 이름자체대로 디버그 모드로 로컬 유닛 테스트 코드를 컴파일합니다. 릴리즈 모드일때는 릴리즈 이름에 맞게 테스크가 실행되는것을 확인하실 수 있습니다. 



또한, 콘솔에서도 테스트코드를 실행하실 수 있습니다. HTML로 결과가 출력되는데요.  프로젝트 폴더에서 cmd창을 연 후,


> gradlew :app:testDebug 

와같이 치신후 엔터를 누르고, app/build/reports/tests/debug 폴더에 테스트 결과를 확인하실 수 있습니다.




안드로이드 테스트

안드로이드 테스트코드는 Activity, Fragment, ButtonView, TextView 등 UI컴포넌트와, 유틸리티클래스와 같은 Java클래스 모두 테스트할 수 있습니다. 다만 에뮬레이터나, 타깃 디바이스에서 실행해야하는 제약이 있습니다 .

저는 MainActivityTest라는 클래스를 만들고 아래와 같이 코드작성 후, 실시해보겠습니다.

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
public MainActivityTest() {
super(MainActivity.class);
}


public void testHelloLabel(){
Activity ac = getActivity();

}

app 모듈의경우 /app/src/androidTest/java 폴더에 위치하고있습니다. 실행하는 방법은 로컬 유닛테스트와 유사합니다.

오른쪽마우스 누르고, run "MainActivityTest" 를 실행합니다.


:app:compileDebugAndroidTestSources

이번에 실행된 gradle test는 unitTest가 아닌, AndroidTest로 바뀐것을 확인하실 수 있습니다.


콘솔에서도 역시 확인하실 수 있습니다.

gradlew :app:connectedAndroidTest + 엔터 치시면됩니다.gradle 콘솔창에 뜨듯이 진행후, 완료구문을 확인하실 수 있습니다.



.


Espresso 연동하기

Espresso는 Android Testing Support Library에 편입되었습니다. 강력한 테스팅 도구이죠. 연동하는 방법을 살펴보시겠습니다.


app모듈의 build.gradle의 내용 추가입니다.


defaultconfig{


  testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}


그다음 denependencies블록에 추가할 내용입니다.


androidTestCompile 'com.android.support.test:runner:0.4.1'

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'


compile 'com.android.support:appcompat-v7:23.0.1'




로컬 유닛 테스트의 제약사항

로컬 유닛테스트 같은 경우 andoir.util.Log 클래스에 대한, mock 인터페이스가 제공되지안항 오류를 낼때가있습니다.

이를 피하려면  모듈 build.gradle에 다음 내용을 추가해야합니다.


android{

    

   testOptions{

unitTests.returnDefaultValues = true

   }


}

근본적으로 해결하려면 PowerMock과 같은 라이브러리를 활용하여 log 클래스의 static 메서드를 위한 가짜 객체를 주입하는 방법이 있습니다. PowerMock 관련 문서를 참조하시기 바랍니다.





들어가기 앞서

 Gradle 에서는 프로젝트를 단일 프로젝트나 멀티 프로젝트로 구성할 수 있습니다. 멀티프로젝트는 하위 폴더에 여러개의 Module을 추가할 수 있습니다. Android Studio 는 프로젝트를 생성하면 자동으로 멀티 프로젝트로 구성되고, 하위에는 App Module이 추가되어 있습니다. 



Android Library Module 만들기
 안드로이드 라이브러리 모듈은 com.android.libaray 플러그인이 필요하고, 그결과 AAR파일을 생성 할 수 있습니다. 
AAR파일은 JAR파일과 비슷해보일 수 있지만 다른점은, 하나의 앱처럼 안드로이드 화면을 포함할 수 있다는 점입니다. JAR 파일은 순수 로직만 포함할 수 있습니다. 하지만 AAR파일은 res폴더의 xml 이미지, 리소스등을 포함할 수 있기때문에 안드로이드 화면을 표현할수 있게 됩니다. 

다음은 안드로이드 라이브러리 모듈을 생성하는 과정입니다. 안드로이드 스튜디오에서 [File -> New -> New Module]을 선택합니다. 그 이후 안드로이드 라이브러리 모듈을 선택하고 이름은 MyLibModule이라고 하겠습니다.

그리고 build.gradle에 settings.gradle에 보시면 include된것을 확인하실 수 있습니다.
include ':app', ':mylibmodule'
또한 build->makd module 'mylibmodule' 을선택하면 aar파일이 생성되는것을 outputs 폴더에서 확인하실 수 있습니다.


Gradle 로컬 저장소 만들기에 앞서
 현업에서 상용프로젝트를 개발할때, 라이브러리를 gradle을 이용해서 참조해서 사용하는 경우가 있습니다. 그런데 jcenter나 maven center에 올려버리면 보안상 문제가 생길 우려가 엄청납니다. 그러므로 사내에 로컬 저장소를 만들어서 운영해야 할 것입니다.


Gradle 로컬 저장소 만드는 방법
 Gradle에서 로컬 저장소를 지정하는 방법을 배워보겠습니다. 
공동작업을 위해서는 프로젝트 폴더 하위에 로컬 저장소를 두는것이 일반적입니다. 먼저 프로젝트 홈폴더를 환경변수에 등록하겠습니다. 

ORG_GRADLE_PROJECT_HOME 환경변수는 gradle 스크립트에서 $PROJECT_HOME으로 참조할 수 있습니다.

다음과 같이 Project의 build.gradle을 변경합니다.

allprojects{
   reposotories{
     jcenter()
     mavne{
      url "file://($PROJECT_HOME)./myReposiroy"
     }
   }

}


참조할 수 있는 로컬 저장소가 생성되었습니다. URL은 위의 "file://($PROJECT_HOME)./myReposiroy" 와 같습니다.

그다음 mylibmoudle을 빌드한 결과를 로컬 저장소에 업로드할 수 있도록 uploadArchives 블록을 정의합니다.

mylibmoudle 모듈의 build.gradle 파일 아래쪽에 다음 내용을 추가합니다.


apply plugin: 'maven'

group ='com.exam.mylibmodule'

version = '1.0'


uploadArchives {

  repositories{

     mavenDeployer{

         repository( url: "file://($PROJECT_HOME)./myReposiroy")

      }

   }

 

}


업로드할 저장소의 URL과 그룹이름, 버전을 지정하면됩니다. group, version, repository url을 확인하실 수 있습니다.


uploadArchives 태스크를 실행해야합니다. 해당 프로젝트 폴더로 가서 gradlew :mylibmodule:uploadArchives 라고 치시면, 해당 태스크를 실행하고 .aar파일이 지정했던 repository에 생성되는것을 확인하실 수 있습니다. 




마지막으로 로컬저장소에 배포된 .aar파일을 참조하도록 하겠습니다.

app모듈에서 build.gradle을 아래와 같이 변경하면 되겠습니다.


dependencies{

  compile 'com.exam.mylibmoudle:mylibmodule:1.0@aar'

}


이제 app moudle에서 library module 의 액티비티를 직접 실행하실수있습니다. 라이브러리르 모듈을 참고하기 때문이죠.

이상입니다.




+ Recent posts