|| 과 | 의 연산자 차이


[ 예제 4-14 ] 의 || 연산자를 | 연산자로 바꾸어서 컴파일한 후 프로그램을 실행해 보십시오.





num1 이 0 보다 크거나 num2 가 0 보다 큽니다.

num1 = 1

num2 = 1


부호 연산자

[ 예제 4-3 ] 의 4 행에 있는 - 부호 연산자를 + 부호 연산자로 바꾸고 프로그램을 컴파일해 보십시오. 어떤 일이 일어납니까?





다음과 같은 컴파일 에러가 발생한다.


Type mismatch: cannot convert from int to short


1. 다음의 프로그램은 잘못된 프로그램입니다. 어떤 부분이 잘못되었는지 찾고, 잘못된 이유를 설명하십시오.





byte 는 1 바이트 짜리 타입이라 [ -128, 127 ] 의 범위를 표현한다. 그런데 for 문 안에서 cnt 가 127 이 넘어 가게 된다. byte 가 아니라 int 를 사용해야만 한다.





2. 다음의 리터럴들이 각각 어떤 타입인지 괄호 안에 쓰십시오.


a) "도" (    )

b) 100 (    )

c) 0x01ABf (    )

d) 12e2 (    )

e) false (    )

f) '\123' (    )



a) "도" ( string )

b) 100 ( int )

c) 0x01ABf ( float )

d) 12e2 ( int )

e) false ( boolean )

f) '\123' ( char )




3. 다음 중 잘못된 리터럴을 고르고 어디가 잘못되었는지 설명하십시오.


""   0.0    '\r\n'    .5e100f    0xABCDEFABCDEF    0x12.5e2    "단가:\10000"    TRUE    1e-100f




'\r\n' : 2 개의 문자가 들어 있는 문자열이므로 "\r\n" 이라고 해야 한다.

.5e100f : float 범위를 넘어 선다. f 를 빼야 한다.

0xABCDEFABCDEF : 4 바이트가 넘어가므로 int 에 넣을 수 없다. 만약 long 에 넣고자 한다면 0xABCDEFABCDEFL 이라고 해야 한다.

0x12.5e2 : 16 진수이므로 e 를 쓰면 안 된다. 0x12.5p2 라고 해야 한다.

"단가:\10000" : \ 를 문자열 안에 쓰면 escape sequence 가 된다. 그러므로 "단가:\\10000" 이라고 해야 한다.

TRUE : true 라고 해야 한다.

1e-100f : float 범위를 넘어 선다. f 를 빼야 한다.


Unicode 문자 출력하기


char 타입도 정수 타입이기 때문에 char 타입의 변수에 Unicode 코드갑승ㄹ 정수로 직접 대입할 수도 있고, char 타입의 변수를 for 문의 카운트 변수로 사용할 수도 있습니다. 이 성질을 잘 이용하면 다음과 같은 for 문을 만들어서 a 부터 z 까지의 영문 알파벳을 출력할 수 있습니다.



위의 for 문을 고쳐서 12593 ~ 12686 에 해당하는 코드값을 갖는 Unicode 문자들을 출력하는 프로그램을 작성해 보십시오. 어떤 문자들이 출력됩니까? 출력된 문자들이 Unicode 의 해당 문자들과 일치하는지 Unicode 홈페이지를 방문하여 확인해 보십시오.





http://www.unicode.org/charts/ 에 가면 "hangul compatibility jamo" 라는 pdf 를 찾을 수 있다. 그 pdf 에 보면 각 문자 아래에 숫자가 적혀 있는데 16진수 값이다. 즉 'ㄱ' 밑에 3131 은 0x3131 을 의미한다. 그리고 그것은 10 진값으로는 12593 이다. 아래 결과는 제대로 출력된 것이라 볼 수 있다.


12593 0x3131

12594 0x3132

12595 0x3133

12596 0x3134

12597 0x3135

12598 0x3136

12599 0x3137

12600 0x3138

12601 0x3139

12602 0x313a

12603 0x313b

12604 0x313c

12605 0x313d

12606 0x313e

12607 0x313f

12608 0x3140

12609 0x3141

12610 0x3142

12611 0x3143

12612 0x3144

12613 0x3145

12614 0x3146

12615 0x3147

12616 0x3148

12617 0x3149

12618 0x314a

12619 0x314b

12620 0x314c

12621 0x314d

12622 0x314e

12623 0x314f

12624 0x3150

12625 0x3151

12626 0x3152

12627 0x3153

12628 0x3154

12629 0x3155

12630 0x3156

12631 0x3157

12632 0x3158

12633 0x3159

12634 0x315a

12635 0x315b

12636 0x315c

12637 0x315d

12638 0x315e

12639 0x315f

12640 0x3160

12641 0x3161

12642 0x3162

12643 0x3163

12644 0x3164

12645 0x3165

12646 0x3166

12647 0x3167

12648 0x3168

12649 0x3169

12650 0x316a

12651 0x316b

12652 0x316c

12653 0x316d

12654 0x316e

12655 0x316f

12656 0x3170

12657 0x3171

12658 0x3172

12659 0x3173

12660 0x3174

12661 0x3175

12662 0x3176

12663 0x3177

12664 0x3178

12665 0x3179

12666 0x317a

12667 0x317b

12668 0x317c

12669 0x317d

12670 0x317e

12671 0x317f

12672 0x3180

12673 0x3181

12674 0x3182

12675 0x3183

12676 0x3184

12677 0x3185

12678 0x3186

12679 0x3187

12680 0x3188

12681 0x3189

12682 0x318a

12683 0x318b

12684 0x318c

12685 0x318d

12686 0x318e



부동소수점수 내부 표현의 확인


JDK 라이브러리에 있는 Double.doubleToRawLongBits 라는 메서드는 dobule 타입의 값을 그와 똑같은 비트 패턴을 갖는 long 타입의 값으로 만드는 메서드입니다. 이 메서드의 사용 방법은 다음과 같습니다.



그리고 JDK 라이브러리의 Long.toBinaryString 이라는 메서드는 long 타입의 정수 내부 표현을 문자열로 만들어서 리턴하는 메서드입니다. 이 메서드의 사용법은 다음과 같습니다.



이 두 메서드를 이용하여 12.735 와 -12.735 의 내부 표현을 문자열로 출력하는 프로그램을 작성하십시오. 그리고 그 결과를 [ 그림 3-17 ]의 비트 패턴과 비교해 보십시오.





다음과 같은 결과가 나왔다.


12.375 : 100000000101000110000000000000000000000000000000000000000000000

-12.375 : 1100000000101000110000000000000000000000000000000000000000000000


12.375 는 앞에 0 이 빠진 표현을 보여 주고 있기 때문에 사실은 다음과 같은 결과가 나왔다고 봐야 한다.


12.375 : 0100000000101000110000000000000000000000000000000000000000000000

-12.375 : 1100000000101000110000000000000000000000000000000000000000000000


결과를 보면 -128 의 경우 MSB( most significant bit, 부호 비트 )가 1 인 것을 알 수 있다. 그리고 정수 표현과는 다르게 소수 표현은 보수 표현을 사용하지 않고 있음을 알 수 있다.


2진 소수


3.14 를 위 그림에서와 같은 방법으로 2진 소수로 바꾸어 보십시오. 이 문제는 컴퓨터로 푸는 것이 아니라 종이에 직접 써서 풀어야 합니다.




다음과 같이 종이에 풀었다.



값은 다음과 같다.


11.00100011110 ...2




그런데 사실 프로그램으로도 이를 재현하는 것이 가능하다. 다음과 같이 코드를 작성해서 답을 검증해 보았다.



결과는 다음과 같이 나왔다. 위의 계산과 동일함을 알 수 있다.


11.0010001111010111


정수 내부 표현의 확인


JDK 라이브러리에 있는 Integer.toBinaryString 이라는 메서드는 int 타입의 내부 표현을 문자열로 만들어서 리턴하는 메서드인데, 다음과 같은 방법으로 사용할 수 있습니다.



이 메서드를 이용해서 -37 의 내부 표현을 출력하는 프로그램을 작성해 보십시오. 그리고 -5 의 내부 표현을 출력하는 프로그램도 작성해 보십시오. 37 과 5 의 2의 보수에 해당하는 비트 패턴이 출력되는 것을 확인할 수 있습니까?





결과는 다음과 같다.


37 : 100101

-37 : 11111111111111111111111111011011

5 : 101

-5 : 11111111111111111111111111111011


1. 다음의 프로그램에는 잘못된 부분이 있습니다. 어떤 부분이 잘못되었는지 찾고, 잘못된 이유를 설명하십시오.





이 프로그램에서는 max 라는 지역변수를 if 블락 바깥쪽에서 접근하고 있다. max 를 if 블락 바깥으로 빼든가 System.out.println 을 블락 안 쪽에서 호출해야만 한다.





2. 다음의 프로그램을 실행하면 어떤 결과가 출력될까요?



이 프로그램의 switch 문이 "키위"라고 출력하도록 만들려면 어떻게 해야 할까요?




프로그램은 'K' 를 출력한다. "키위"를 출력하게 하려면 'K' 를 비교하는 case 문을 추가해야 한다.





3. 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 순으로 정수를 출력하는 프로그램으로 만들려고 합니다. a, b, c 위치에 알맞은 식을 채워 넣으십시오.








4. JDK 5.0 의 향상된 for 문을 이용하여 arr 배열 항목들의 총합을 계산하여 출력하려고 합니다. a, b 의 위치에 뭐라고 써야 할까요?








5. "Hello, Java" 의 Java 대신에 명령형 파라미터로 입력받은 값을 출력하는 프로그램을 만들려고 합니다. 예를 들어 Tom 이라는 값을 입력하면 "Hello, Tom" 이라고 출력애야 합니다.


1) 다음 프로그램의 a 위치에 뭐라고 써야 할까요?



2) 이 프로그램이 "Hello, World" 라고 출력하게 하려면 명령행에 뭐라고 입력해야 할까요?




1)
 


2)


java HelloEverybody World


단 파라미터가 아무것도 없을 경우에 java.lang.ArrayIndexOutOfBoundsException 예외가 발생할 수 있다.


익셉션 처리 문제


Section05 의 Excercise 2-5 에서 작성했던 프로그램은 명령행 파라미터로 정수가 아닌 값을 넘겨주면 익셉션을 발생합니다. 그 익셉션을 잡아서 한글로 된 에러 메시지를 출력하는 프로그램을 작성하십시오.




다음과 같이 코드를 작성한다.



이를 컴파일하고 명령줄에서 다음과 같이 실행한다.


java Hello 1 2 3 four 5 six 7 8


그러면 아래와 같은 결과가 나온다.


1

2

3

four

four 는 정수가 아닙니다

5

six

six 는 정수가 아닙니다

7

8

26


여기에서 java.lang.NumberFormatException 을 사용한 이유는 Integer.parseInt 메서드가 그 예외를 발생시키기 때문이다. Eclipse 같은 도구에서 parseInt 메서드에 커서를 위치시킨 다음에 Javadoc 라는 탭을 보면 다음과 같이 설명이 되어 있는 것을 볼 수 있다.


Throws:

    NumberFormatException - if the string does not contain a parsable integer.


메서드 호출문의 연습


1장에서 설치한 JDK 안에는 JDK 라이브러리라는 자바 모듈이 포함되어 있는데, 그 안에는 여러 가지 유용한 기능의 클래스와 메서드들이 있습니다. 그 중에 있는 Integer.parseInt 라는 메서드는 문자열을 정수로 바꾸는 메서드입니다. 이 메서드의 사용법은 다음과 같습니다.



이렇게 메서드를 호출하면 이 메서드는 "12" 라는 문자열을 12 라는 정수로 만들어서 리턴합니다. 이 메서드를 이용해서 명령행 파라미터로 받은 정수를 모두 더하여 합을 출력하는 프로그램을 작성하십시오.




다음과 같이 코드를 작성한다.



이를 컴파일하고 명령줄에서 다음과 같이 실행한다.


java Hello 1 2 3 4 5 6 7 8


그러면 아래와 같은 결과가 나온다.


1

2

3

4

5

6

7

8

36


break 문의 연습


다음과 같은 배열이 있습니다.



이 배열에서 -1 이 나오기 전까지의 항목 값들을 순서대로 출력하십시오.





435

88

67

Done.


향상된 for 문


다음의 프로그램에 있는 두 개의 for 문을 모두 향상된 for 문으로 고칠 수 있을까요?



고칠 수 없는 for 문이 있다면 왜 그런지 이유를 설명해 보십시오.




두 번째 for 문만 향상된 for 문으로 고칠 수 있다. 왜냐하면 향상된 for 문에서의 변수는 배열 내의 "값"을 의미하고 있기 때문이다. 배열 요소의 값에 10 을 곱하고 싶으면 그것의 인덱스를 얻어 올 수 있어야 하는데, 그것의 인덱스를 받아 올 수 있는 방법이 없다.



결과는 다음과 같다.


100

200

300

400

500

Done.




만약 아래와 같이 코드를 작성해 버린다면, 컴파일에는 문제가 없지만 결과가 틀리게 나온다. 첫 번째 향상된 for 문에서의 value 는 단지 복사본이며 원본 배열의 내용에 영향을 미치지 못한다.



Ecplise 같은 데서 빌드하면 다음과 같은 warning 을 볼 수 있다.


The value of the local variable value is not used UpdateArrayExample.java /UpdateArrayExample/src line 7 Java Problem


결과는 다음과 같다.


10

20

30

40

50

Done.


for 문의 연습


다음과 같은 배열이 있습니다.



이 배열의 3 번째 항목부터 7 번째 항목까지의 값을 모두 더해서 출력하는 프로그램을 for 문을 이용해서 작성하십시오.





결과는 다음과 같다.


50


while 문과 do-while 문의 비교


앞에서 작성한 [예제 2-30]과 [예제2-32]는 똑같은 결과를 출력합니다. 그러면 다음 두 프로그램도 똑같은 결과를 출력할까요?






다른 결과를 출력한다. 첫 번째는 조건을 만족하지 못해서 "Done." 만 출력한다.


그런데 두 번째는 조건을 만족하지는 못하지만 일단 do 블락을 실행하므로 "10" 과 "Done." 이 출력된다.


else if 절과 switch 문


앞에서 작성했던 [ 예제 2-26 ]은 else if 절을 사용해서 정수의 범위를 알아 내는 프로그램이었습니다. 그와 똑같은 기능을 하는 프로그램을 switch 문을 이용해서 작성할 수 있을까요? 이 경우에 switch 문과 else if 절을 갖는 if 문 중에서 어느 것을 사용하는 게 더 적절한지 생각해 보십시오.





switch 문을 사용하려면 비교가 가능한 정수 형태의 카테고리 값이어야만 한다. 비교하고자 하는 값을 카테고리로 나눌 수만 있다면 어떻게든 switch 문으로 표현할 수 있을 것이다.



코드를 보면 알겠지만, 좀 삽질이다. 범위를 가지고 있는 값에 대해서는 if 문을 사용하는 것이 좋다.

switch 문을 if 문으로


[ 예제 2-27 ] 과 똑같은 기능을 하는 프로그램을 switch 문을 대신 "else if" 절을 갖는 if 문을 이용해서 작성하십시오.





default 는 else 와 같다. 그러므로 다음과 같이 변경할 수 있다.



2차원 배열의 항목 수





위 코드의 2 차원 배열은 3 X 4 의 크기를 가진다. 그러므로 table 자체에다가 length 를 적용하면 3 이 나오고, table[ 0 ] 에다가 length 를 적용하면 4 가 나온다. 아래와 같이 출력된다.


3

4




좀 더 진보된 예제를 들어 보겠다. 배열 선언을 [] 로 했다는 것은 배열의 요소 개수가 가변적인 크기를 가질 수 있음을 의미한다. 아래와 같이 코드를 작성한다면 모든 행의 열 개수를 다르게 만들 수 있다.



이 경우 출력은 다음과 같다.


3

4

7

8


유별난 final 변수


final 변수에는 복합 대입 연산자, 증가 연산자, 감소 연산자를 사용할 수 없습니다. 왜 그럴까요?




복합 대입 연산자, 증가 연산자, 감소 연산자는 어떤 변수의 값을 변경하는 것이다. final 이라는 키워드가 붙게 되면 그 변수는 변경해서는 안 되기 때문에  그러한 연산자를 사용할 수 없다.

변수 선언의 위치


다음의 프로그램은 잘못된 프로그램입니다. 하지만 명령문 하나만 위치를 바꾸면 올바르게 작동할 수 있습니다. 그 명령문을 찾아서 위치를 바꾸십시오.





if 문 안에 abs 지역변수가 선언되어 있는데 이는 if 로 묶인 블락 안에서만 유효하다. 그러므로 11 라인에서 abs 를 접근하려고 할 때 식별자를 찾을 수가 없다. 그래서 해당 지역변수 선언을 if 문 바깥으로 옮겨야 한다.



1. 이 장에서 배운 내용을 생각하면서 다음의 괄호를 채워 봅시다.


a) 명령문만 가지고는 온전한 자바 프로그램이 될 수 없습니다. 자바 프로그램의 되기 위해서는 먼저 ( 클래스class )를 만들고, 그 안에 ( main 메서드method )를 만들고, 그 안에 ( 명령문statement )을 써 넣어야 합니다.


b) java.exe 명령을 실행할 때 클래스 이름을 지정하면 그 클래스의 ( main ) 메서드로부터 자바 프로그램의 실행이 시작됩니다.




2. 자바 프로그램을 실행하려면 반드시 <이것>이 필요합니다. <이것>은 자바 클래스 파일을 다른 종류의 플랫폼에서도 실행할 수 있도록 만들어 줍니다. 자바의 가상 기계어를 하드웨어와 운영체제에 맞는 진짜 기계어로 바꾸어 주는 역할을 하는 <이것>은 무엇일까요?


자바 가상 기계Java Virtual Machine.




3. 메모장으로 HelloJava.java 프로그램을 입력해서 저장하고 컴팡리했는데 다음과 같은 에러가 발생했습니다. 에러 메시지를 보고 다음 그림의 프로그램 어느 부분이 잘 못 되었는지 찾아 보십시오.



E:\work\chap1>javac HelloJava.java

HelloJava.java:5: '}' expected

^

1 error

E:\work\chap1>


HelloJava 클래스에 닫는 괄호가 없음.

ndk-build tool


android 명령을 사용해서 project 를 생성하고 나면, 그것을 컴파일해 줄 필요가 있다. 이 컴파일 작업은 ndk-build 를 통해서 이루어 진다. 그런데 책에서는 ndk-build 에 대해서 자세하게 다루고 있지 않다. 그런데 안타깝게도 안드로이드 개발자 사이트에서도 자세한 내용을 다루고 있지 않다. 하지만 $ANDROID_NDK/docs/NDK-BUILD.html 을 열어 보면 해당 도구에 대한 설명을 볼 수 있다.


이 문서는 "ndk-build 란?" 장을 제외하고는 NDK-BUILD.html 의 내용을 번역한 것이다.


ndk-build 란?


일단 ndk-build 라는 것의 개념부터 살펴 보도록 하자. ndk-build 에 대해서 --version 이라는 매개변수를 주고 실행하면 다음과 같은 결과를 볼 수 있다.


GNU Make 3.81

Copyright (C) 2006  Free Software Foundation, Inc.

This is free software; see the source for copying conditions.

There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


위의 결과를 보면 알 수 있겠지만, ndk-build 라는 것은 GNU Make 에 대한 wrapper 이다. 여기에서 이제 또 다른 의문이 생길 것이다. 그럼 make 란 무엇인가?


make 라는 것은 Visual Studio 같은 IDE 가 가지고 있는 빌드 시스템과 유사하다고 생각하면 된다. 예를 들어 우리는 Visual Studio 를 사용해서 솔루션과 프로젝트를 만들고 거기에 소스 파일, 헤더 파일, 리소스 파일 등을 추가한다. 그 다음에 솔루션이나 프로젝트의 속성창을 열어 필요한 옵션들을 설정한다. 그리고 나서 빌드 버튼을 누르게 된다. 그러면 지정된 옵션에 따라서 컴파일과 링크가 수행된다. 이 때 "최소 다시 빌드" 옵션을 설정하게 되면, 변경된 파일을 검색해서 관련 object 파일만을 컴파일하고 링크하게 된다. make 가 바로 이러한 작업을 해 주는 도구이다. 


물론 make 가 하는 일이 단순하게 그런 것만 있는 것은 아니다. 의존성을 설정한다던가 batch 명령과 유사한 작업을 한다던가 여러 가지 작업을 하게 된다. make 는 여러 가지 구현이 존재한다. 그 중에서 가장 많이 사용하는 것이 GNU Make 이다. 자세한 내용은 "GNU Make 강좌" 를 참조하기 바란다.  GNU Make 의 매뉴얼은 "GNU Make Manual" 에서 찾아 볼 수 있다.


어쨌든 ndk-build 는 GNU Make 의 wrapper 이며, 우리는 이것을 프로젝트를 설정하고 빌드하는 것을 도와 주는 도구 정도로 생각하면 되겠다.


용례.


안드로이드 NDK r4 는 새로운 작은 쉡 스크립트인 'ndk-build' 를 소개했는데, 이는 머신 코드를 빌드하는 것을 단순화하기 위한 것이다.


이 스크립트는 NDK 의 최상위 디렉토리에 존재하며, 응용프로그램 프로젝트 디렉토리나 그것의 모든 하위 디렉토리들에서 명령줄에서 실행된다. 예를 들어 :



여기에서 $NDK 는 NDK 설치 경로이다. 당신은 그것을 매번 입력하는 것을 피하기 위해서 PATH 에다가 $NDK 를 추가하거나 alias 를 만들 수 있다.


옵션.


'ndk-build' 에 대한 모든 매개 변수는 NDK 빌드 스크립트를 실행하는 기저에 깔린 GNU Make 명령에 직접적으로 전달된다. 자주 쓰는 용례는 다음과 같다 :


  • ndk-build : 머신 코드를 다시 빌드한다.
  • ndk-build clean : 생성된 바이너리를 모두 지운다.
  • ndk-build NDK_DEBUG=1 : 디버깅할 수 있는 네이티브 코드를 생성한다.
  • ndk-build V=1 : 빌드를 시작하고, 빌드 명령들을 보여 준다.
  • ndk-build -B : 완전한 다시 빌드를 강제한다.
  • ndk-build -B V=1 : 완전한 다시 빌드를 강제하고, 빌드 명령들을 보여 준다.
  • ndk-build NDK_LOG=1 : 내부 NDK 로그 메시지들을 보여 준다( NDK 자체를 디버깅하는데 사용된다 ).
  • ndk-build NDK_DEBUG=1 : 디버깅 가능한 빌드를 강제한다( 아래 참조 ).
  • ndk-build NDK_DEBUG=0 : 릴리스 빌드를 강제한다( 아래 참조 ).
  • ndk-build NDK_HOST_32BIT=1 : 항상 32 비트 툴체인을 사용한다( 아래 참조 ).
  • ndk-build NDK_APPLICATION_MK=<file> : 다시 빌드하는데, NDK_APPLICATION_MK 명령줄 변수에 의해 지정된 특정 Application.mk 를 사용한다.
  • ndk-build -C <project> : <project> 에 위치한 프로젝트 경로를 위해 네이티브 코드를 빌드한다. 터미널에서 'cd' 를 입력하고 싶지 않을 때 유용하다.


디버그 빌드 대 릴리스 빌드.


NDK r5 에서 ndk-build 는 릴리스 빌드와 디버그 빌드 사이를 더 쉽게 전환할 수 있도록 변경되었다. 이는 NDK_DEBUG 변수를 사용해서 수행된다.


예를 들어 :


$NDK/ndk-build NDK_DEBUG=1 => 강제로 디버그 바이너리를 생성한다.

$NDK/ndk-build NDK_DEBUG=0 => 강제로 릴리스 바이너리를 생성한다.


NDK_DEBUG 를 지정하면, ndk-build 는 그것의 기본 동작을 유지하는데, 그것은 AndroidMenifest.xml 에 <application> 엘리먼트가 존재하면  android:debuggable="true" 로 설정되어 있는지를 확인한다.


중요 : 만약 당신이 SDK r8 ( 혹은 그 이상 )의 빌드 툴을 사용한다면, 당신은 AndroidManifest.xml 파일을 전혀 손댈 필요가 없다!

왜냐하면 ( "ant debug" 나 ADT 플러그인의 관련 옵션을 사용해 ) 디버그 패키지를 빌드하게 되면, 그 도구가 자동으로 NDK_DEBUG=1 을 사용해 생성된 네이티브 디버그 파일을 선택할 것이기 때문이다.


그리고 편리하게도 NDK 에 의해 생성된 릴리스 오브젝트 파일과 디버그 오브젝트 파일은 서로 다른 디렉토리에 저장된다( 예를 들어 obj/local/<abi>/objsobj/local/<abi>obj-debug ). 이는 두 모드를 전환할 때 ( 심지어 당신이 한 두개의 소스 파일들만을 수정했을 때 ), 모든 소스들이 다시 빌드되는 것을 막아 준다.


64 비트 툴체인과 32 비트 툴체인.


일부 툴체인들은 64 비트 버전과 32 비트 버전을 둘 다 가지고 있다. 예를 들어 $NDK/toolchain/<name>/prebuilt$NDK/prebuilt 는 "linux-x86" 과 "linux-x86-64" 폴더를, 만약 OS 가 그것을 지원한다면 32 비트 모드 리눅스 툴과 64 비트 모드 리눅스 툴을 위해 모두 포함하고 있다.당신은 환경 변수나 ndk-build 명령줄에서  NDK_HOST_32BIT=1 을 사용하여 32 비트 툴체인을 사용하는 것을 강제할 수 있다.


Note that 64-bit tools utilize host resources better( faster, handle larger programs, etc ) and they should function identically to their 32-bit counterparts. le. 64-bit toolchains still generate 32-bit binaries for Android.


요구 사항.


'ndk-build' 나 NDK 를 범용적으로 사용하기 위해서는 GNU Make 3.81 을 필요로 한다. 빌드 스크립트들은 적절하지 못한 Make 톨을 사용하고 있는지를 확인할 것이며 에러 메시지를 출력하며 불평할 것이다.


만약 GNU Make 3.81 이 설치되어 있지만 기본 'make' 명령에 의해 실행될 수 없는 상태라면, GNUMAKE 를 환경 변수에 정의해서 'ndk-build' 를 실행하기 전에 그것을 가리키도록 하라. 예를 들어 :


GNUMAKE=/usr/local/bin/gmake ndk-build


혹은 그 변경을 더 영구적으로 만들라 :


export GNUMAKE=/usr/local/bin/gmake ndk-build


당신의 쉘과 GNU Make 3.81 설치 위치를 맞춰라.


당신은 다음과 같은 환경 변수들을 사용해서 $NDK/prebuilt/<OS>/bin 의 다른 호스트 prebuilt 툴들을 덮어 쓸 수도 있다.


NDK_HOST_AWK=<path-to-awk>

NDK_HOST_ECHO=<path-to-echo>

NDK_HOST_CMP=<path-to-cmp>


내부 구현.


'ndk-build' 자체는 GNU Make 에 대한 작은 wrapper 이며, 그것의 목적은 올바른 NDK 빌드 스크립트를 쉽게 실행하는 것이다. 이는 다음과 동일하다 :


$GNUMAKE -f $NDK/build/core/build-local.mk [parameters]


여기에서 '$GNUMAKE' 는 GNU Make 3.81 이상을 가리키며, '$NDK' 는 당신의 NDK 설치 디렉토리를 가리킨다.


다른 쉘 스크립트들( 혹은 당신만의 Makefile 들 )로부터 NDK 빌드 스크립트를 실행하고자 한다면 이에 대한 지식을 사용하라.


책에 보면 android 툴을 사용해서 프로젝트를 생성하는 것을 볼 수 있다. 예를 들면 다음과 같은 명령을 사용한다.



책에서는 그냥 저렇게 하면 프로젝트가 생성된다고만 말하고 실제 그것이 어떤 의미를 가지고 있는지 혹은 어떤 동작을 하는지에 대해서 설명하지 않는다. 그래서 "그런가 보다"하고 넘어 갔었는데, 역시 근원을 파고들고자 하는 욕구가 사라지지 않아서 정리해 본다. 다행히도 Android Developer 사이트에 관련 문서가 있어서 번역해 본다.


원문 : Manaing Projects from the Command Line

주의 : 번역이 개판이므로 이상하면 원문을 참조하십시오.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

주의 : 가독성을 높이기 위해서 잘 알려진 용어나 발음이 비슷한 용어는 한글로 표기합니다.




명령줄에서 프로젝트 관리하기


android 툴은 세 종류의 프로젝트들을 모두 생성하기 위한 명령들을 제공한다. 안드로이드 프로젝트는 프로젝트를 설치를 위한 .apk 파일로 빌드하는데 필요한 파일들과 리소스들을 포함한다.


  • 안드로이드 프로젝트는 프로젝트를 설치를 위한 .apk 파일로 빌드하는데 필요한 파일들과 리소스들을 포함한다. 당신은 최종적으로 장치에 설치하고자 하는 모든 응용프로그램을 위해 안드로이드 프로젝트를 생성할 필요가 있다.
  • 당신은 안드로이드 프로젝트를 라이브러리 프로젝트로서 설계할 수도 있는데, 이는 그것에 의졶하는 다른 프로젝트들과 공유되는 것을 허용한다. 안드로이드 프로젝트가 라이브러리 프로젝트로 설계되면, 그것은 장치에 설치될 수 없다.
  • 테스트 프로젝트들은 JUnit 기능을 확장해 안드로이드에 특화된 기능들을 포함한다. 테스트 프로젝트를 생성하는 것과 관련한 더 많은 정보를 원한다면, Testing from other IDEs 를 참조하라.


안드로이드 프로젝트 생성하기기



안드로이드 프로젝트를 생성하려면, 당신은 android 툴을 사용해야만 한다. 당신이 android 로 새로운 프로젝트를 생성할 때, 그것은 일부 기본 파일들, stub 파일들, 설정 파일들, 빌드 파일을 가진 프로젝트 디렉토리를 생성할 것이다.


새로운 안드로이드 프로젝트를 생성하려면, 명령줄을 열고, SDK 의 tools/ 디렉토리로 가서, 다음을 실행하라 :


android create project \

--target <target_ID> \

--name <your_project_name> \

--path path/to/your/project \

--activity <your_activity_name> \

--package <your_package_namespace>


  • target 은 응용프로그램을 위한 "빌드 타겟" 이다. 그것은 당신이 응용프로그램을 빌드하려고 하는 ( Google APIs 와 같은 모든 애드온을 포함하는 ) 안드로이드 플래폼 라이브러리와 관련이 있다. 이용 가능한 타겟들과 그것들의 관련 아이디들을 보려면, 다음을 실행하라 : android list targets.
  • name 은 프로젝트의 이름이다. 이것은 선택 사항이다. 만약 이름이 제공되면, 이 이름은 응용프로그램을 빌드할 때 .apk 파일 이름으로 사용될 것이다.
  • path 는 프로젝트 디렉토리의 위치이다. 만약 디렉토리가 존재하지 않으면, 자동으로 생성될 것이다.
  • activity 는 기본 Activity 클래스의 이름이다. 이 클래스 파일은 <path_to_your_project>/src/<your_package_namespace_path>/ 내부에 생성될 것이다. 당신이 name 을 지정하지 않으면 그것은 .apk 파일 이름으로 사용될 것이다.
  • package 는 프로젝트를 위한 패키지 이름인데, 자바 프로그래밍 언어에서의 패키지를 위한 규칙과 동일하다.


여기 예제가 있다 :


android create project \

--target 1 \

--name MyAndroidApp \

--path ./MyAndroidAppProject \

--activity MyAndroidAppActivity \

--package com.example.myandroid


프로젝트를 생성하고 나면, 당신은 개발을 시작할 준비가 된 것이다. 원하는 곳에 프로젝트 폴더를 옮길 수는 있지만, 응용프로그램을 에뮬레이터에 보내기 위해서는  Android Debug Bridge ( adb ) - SDK 의 platform-tools/ 디렉토리에 있음 - 를 사용해야만 한다는 것을 염두에 두기 바란다( 이에 대해서는 나중에 언급한다 ). 그래서 당신은 프로젝트 솔루션과 paltform-tools/ 폴더에 접근할 필요가 있다.

Tip : platform-tools/tools/ 를 PATH 환경 변수에 추가하라.

Caution : 당신은 SDK 디렉토리의 위치를 움직여서는 안 된다. 왜냐하면 이는 local.properties 에 위치한 SDK 위치 속성을 깨 버리기 때문이다. 만약 당신이 SDK 위치를 옮기고자 한다면, android update project  명령을 사용하라. 더 많은 정보를 원한다면 "프로젝트 갱신하기" 를 참조하라.


프로젝트 갱신하기



만약 이전 버전의 안드로이드 SDK 에서 만든 프로젝트를 업그레이드하고 싶거나 현존하는 코드로부터 새로운 프로젝트를 생성하고 싶다면, android update project 명령을 사용해 프로젝트를 새로운 개발 환경으로 갱신하라. 당신은 이 명령을 ( --target 옵션과 함께 ) 사용해서 현존하는 프로젝트의 빌드 타겟을 변경하거나 ( --name 옵션과 함께 ) 프로젝트 이름을 변경할 수 있다. android 툴은 분실되거나 갱신될 필요가 있는 ( 이전 섹션에서 열거되었던 ) 모든 파일들과 폴더들을 안드로이드 프로젝트가 필요한 만큼 생성하게 될 것이다.


현존하는 안드로이드 프로젝트를 갱신하려면, 명령줄을 열고 SDK 의 tools/ 디렉토리로 이동하라. 그리고 다음을 실행하라 :


android update project --name <project_name> --target <target_ID>

--path <path_to_your_project>


  • target 은 응용프로그램을 위한 "빌드 타겟" 이다. 그것은 당신이 응용프로그램을 빌드하려고 하는 ( Google APIs 와 같은 모든 애드온을 포함하는 ) 안드로이드 플래폼 라이브러리와 관련이 있다. 이용 가능한 타겟들과 그것들의 관련 아이디들을 보려면, 다음을 실행하라 : android list targets.
  • path 는 프로젝트 디렉토리의 위치이다. 만약 디렉토리가 존재하지 않으면, 자동으로 생성될 것이다.
  • name 은 프로젝트의 이름이다. 이것은 선택 사항이다. 만약 이름이 제공되면, 이 이름은 응용프로그램을 빌드할 때 .apk 파일 이름으로 사용될 것이다.


여기 예제가 있다 :


android update project --name MyApp --target 2 --path ./MyAppProject


라이브러리 프로젝트 설정하기



라이브러리 프로젝트는 표준 안드로이드 프로젝트이다. 그래서 당신은 새로운 응용프로그램 프로젝트를 생성하는 것과 같은 방식으로 새로운 라이브러리 프로젝트를 생성할 수 있다. 특히, 당신은 필요한 파일들과 폴더들을 가진 새로운 라이브러리를 생성하기 위해 android 툴을 사용할 수 있다.


새로운 라이브러리 프로젝트를 생성하려면, <sdk>/tools/ 디렉토리로 이동하고 다음 명령을 사용하라 :


android create lib-project --name <your_project_name> \

--target <target_ID> \

--path path/to/your/project \

--package <your_library_package_namespace>


create lib-project 명령은 그 프로젝트가 라이브러리임을 빌드 시스템에 알려주는 프리셋 속성을 포함하는 표준 프로젝트 구조를 생성한다. 그것은 프로젝트의 project.properties 파일에다가 다음 라인을 추가함으로써 수행된다 :


android.library=true


명령이 종료되면, 라이브러리 프로젝트가 생성되고 당신은 아래 섹션들에서 설명하는 것처럼 소스 코드와 리소스들을 그 프로젝트 안으로 옮기기 시작할 수 있다.


만약 당신이 현존하는 응용프로그램 프로젝트를 라이브러리 프로젝트로 변환해서 다른 응용프로그램들이 그것을 사용할 수 있도록 하고 싶다면, 당신은 adnroid-library=true 속성을 응용프로그램의 project.properties 파일에 추가함으로써 그 작업을 수행할 수 있다.


매니페스트 파일 생성하기


라이브러리 프로젝트의 매니페스트 파일은 표준 안드로이드 응용프로그램처럼 반드시 그것이 포함하고 있는 공유 요소들을 모두 선언해야만 한다. 더 많은 정보를 원한다면, AndroidManifest.xml 을 위한 문서를 참조하라.


예를 들어, TicTacToeLib 예제 라이브러리 프로젝트는 GameActivity 라는 Activity 를 선언한다 :


<manifest>

  ...

  <application>

    ...

    <activity android:name="GameActivity" />

    ...

  </application>

</manifest>


라이브러리 프로젝트 갱신하기


만약 당신이 라이브러리 프로젝트의 빌드 속성들( 빌드 타겟, 위치 )을 갱신하고자 한다면, 다음 명령을 사용하라 :


android update lib-project \

--target <target_ID> \

--path path/to/your/project


라이브러리 프로젝트 참조하기



만약 당신이 응용프로그램을 개발하고 있고 공유 코드와 공유 리소스들을 라이브러리 프로젝트로부터 가져 오기를 원한다면, 당신은 응용프로그램 프로젝트의 빌드 속성들에 라이브러리 프로젝트에 대한 참조를 추가함으로써 이를 쉽게 수행할 수 있다.


라이브러리 프로젝트에 대한 참조를 추가하려면, <sdk>/tools/ 디렉토리로 가서 다음 명령을 사용하라 :


android update project \

--target <target_ID> \

--path path/to/your/project

--library path/to/library_projectA


이 명령은 응용프로그램의 빌드 속성들을 갱신해서 라이브러리 프로젝트에 대한 참조를 포함하도록 한다. 특히, 그것은 android.library.reference.n 속성을 프로젝트의 project.properties 파일에 추가한다. 예를 들면 :


android.library.reference.1=pat/to/library_projectA


여러 개의 라이브러리들에 대한 참조를 추가하고 있다면, 당신은 그것의 상대적인 우선순위를 ( 그리고 merge 순서를 ) 설정할 수 있는데, 이는 project.properties 파일을 수작업으로 편집하고 각 참조들의 .n 인덱스를 적절하게 변경함으로써 이루어 진다. 예를 들어, 이러한 참조들을 가정해 보자 :


android.library.reference.1=path/to/library_projectA

android.library.reference.2=path/to/library_projectB

android.library.reference.3=path/to/library_projectC


당신은 library_projectC 에 가장 높은 우선순위를 부여하기 위해 다음과 같이 참조들을 재정렬할 수 있다 :



android.library.reference.2=path/to/library_projectA

android.library.reference.3=path/to/library_projectB

android.library.reference.1=path/to/library_projectC


레퍼런스들에서 .n 인덱스는 "1" 로 시작하며 "구멍" 없이 하나씩 증가한다는 것에 주의하라. 빠진 부분이 생긴 다음의 인덱스에서 나타나는 참조들은 무시된다.


빌드시에, 그 라이브러리들은 응용프로그램에 동시에 merge 되는데, 가장 낮은 우선순위에서 시작해 가장 높은 우선순위로 merge 된다. 라이브러리는 스스로는 다른 라이브러리를 참조할 수 없으며, 빌드시에 라이브러리들은 응용프로그램과 merge 되기 전에 다른 것들과 merge 되지 않는다는 것에 주의하라.


매니페스트 파일에서 라이브러리 요소 선언하기


응용프로그램 프로젝트의 매니페스트 파일에서, 당신은 응용프로그램이 라이브러리 프로젝트로부터 들여 오게 되는 응용프로그램이 사용하게 될 모든 요소들에 대한 선언을 추가해야만 한다. 예를 들어, 당신은 모든 <activity>, <service>, <receiver>, <provider> 등을 선언해야만 한다. 그리고 <permission>, <uses-library> 및 그와 유사한 요소들도 선언해야만 한다.


선언들은 라이브러리 요소들의 fully-qualified 이름들을 사용해 적절한 위치의 라이브러리 요소들을 참조한다.


예를 들어, TicTacToeMain 예제 응용프로그램은 다음과 같이 GameActivity 라는 라이브러리 Activity 를 선언한다 :


<manifest>

  ...

  <application>

    ...

    <activity android:name="com.example.android.tictactoe.library.GameActivity" />

    ...

  </application>

</manifest>


매니페스트 파일에 대한 더 많은 정보를 원한다면, AndroidManifest.xml 을 위한 문서를 참조하라.


의존성 있는 응용프로그램 빌드하기


하나 이상의 라이브러리 프로젝트에 의존하는 응용프로그램 프로젝트를 빌드하기 위해, 당신은 Build and Running 에 기술된 것처럼 표준 Ant 빌드 명령들과 컴파일 모드들을 사용할 수 있다. 그 툴들은 의존성 있는 응용프로그램 프로젝트를 컴파일하는 작업의 일부로서 응용프로그램에 의해 참조되는 모든 라이브러리들을 컴파일하고 merge 한다. 다른 명령들이나 단계들이 필요하지 않다.


Ubuntu 12.10 에서 NDK 를 설치할 때, 압축 관리자로 그냥 압축을 푸는 경우에 ndk-build 를 실행하면 다음과 같은 에러를 만날 수 있다.



이 문제 때문에 한참 고생을 했는데, 압축이 잘 못 풀려서 arm-linux-androideabi-gcc 가 생성되지 않기 때문에 발생하는 문제이다. 파일탐색기에서 "여기에 풀기"를 통해 압축을 풀면 이상하게 압축이 제대로 풀리지 않는 문제가 있다.


그래서 다음과 같이 직접 압축을 풀었더니 arm-linux-androideabi-gcc 가 제대로 생성되었다.



파일탐색기에서 푸는 것이 왜 제대로 안 풀리는 지는 모르겠다. 한 번 알아 볼 필요가 있을 것 같다.


AskUbuntu 에다가 질문을 올려 두었다. 어떤 답이 나올지 궁금하다. 리눅스 초보자라 이것 저것 모르는게 많아 골치 아프다; Why does a "Extract Here" context menu work incorrectly?


주의 : 공부하면서 정리한 내용이라 오류가 있을 수 있습니다.

주의 : 가독성을 높이기 위해서 잘 알려진 용어나 발음이 비슷한 용어는 한글로 표기합니다.




Android NDK 개발 환경 : Cygwin


왜 Cygwin 이 필요한가?



개발자라면 자신이 개발하고 있는 환경에 대해서 제대로 이해해야 할 필요가 있다고 생각한다. NDK 를 사용해서 안드로이드 앱을 개발하기 전에 개발환경에 대해서 대강이라도 이해해 보고자 공부하면서 정리해 본다.


안드로이드는 기본적으로 리눅스 커널을 기반으로 올라 가 있는 가상머신이기 때문에 NDK 는 리눅스 개발환경과 관련이 있다. 그러므로 윈도우즈 환경에서 안드로이드 NDK 프로그래밍을 하기 위해서는 시그윈이 필수이다. 아래 그림은 안드로이드의 시스템 아키텍처를 설명하는 것으로서, 인터넷에 가장 많이 퍼져 있는 것을 주워 왔다.



NDK 로 개발을 한다는 것이 의미하는 바가 무엇인지 생각해 볼 필요가 있다. 많이들 알고 있는 사실이겠지만, 어떤 프로그램이 어떤 머신에서 실행되기 위해서는 해당 머신에 맞는 실행가능한 파일( executable )을 만들어야만 한다. 그것이 소위 우리가 알고 있는 '컴파일'과 '링크'라는 작업이다. 이 컴파일 작업과 링크 작업을 합쳐서 '빌드'라고 표현한다.


우리가 열심히 코딩한 라인들은 어셈블리어로 변환되고, 이 어셈블리어들은 다시 기계어로 변환된다. 그리고 CPU 는 이 기계어를 마이크로 명령어로 나누어서 프로그램을 실행하게 된다. 그런데 이 CPU 가 어떠한 명령어 집합 구조( Instruction Set Architecture, ISA )를 제공하느냐에 따라서 다른 기계어를 만들어야 하기 때문에, 프로그램을 빌드할 때는 해당 CPU 에 맞는 구성을 만들어야 한다. 예를 들어 CPU 가 x86 ISA 를 제공한다면 x86 에 맞는 빌드 구성을 해야 하고, CPU 가 x86/64 를 제공한다면 거기에 맞는 빌드 구성을 만들어야 한다.


자 우리가 NDK 를 통해 만든 바이너리를 실행할 플래폼의 환경은 어떠한가? 일반적으로 현재 안드로이드 OS 가 돌아 가는 환경은 ARM 코어를 사용하는 리눅스 환경이다. 이는 어떠한 사실을 내포할까?


    • 우리가 C/C++ 응용프로그램을 개발할 때 CreateFile() 과 같은 Win32 API 를 사용할 수 없다.

    • Visual Studio 를 이용할 수 없다. --> VC++ 컴파일러를 사용할 수 없다.


사실 Visual Studio 의 컴파일러를 GCC( GNU Compiler Collection ) C++ 컴파일러로 연결해 놓고 개발할 수는 있을 것이다. 하지만 기본 환경 자체가 Win32 서브시스템과 x86 혹은 x86-64 아키텍처를 위한 개발에 최적화되어 있으므로 참 어려운 일이 될 것이다. 구글링을 해 보면 여러 가지 자료들이 나오기는 하는데 얼마나 편한 지는 잘 모르겠다. 어쨌든 우리는 리눅스 OS 를 기반으로 ARM ISA 에 맞춰서 빌드해야만 하는 상황이고, 윈도우즈 환경에서 이를 가능하게 만들 수 있는 방법을 찾아야만 한다. 


이때 우리에게 한 줄기 빛을 비춰주는 존재가 있으니, 그것이 바로 시그윈이다.


Cygwin 이란 무엇인가?



일단 시그윈 공식 사이트의 설명을 인용해 보도록 하겠다. 출처 : http://cygwin.com/index.html


시그윈은 :

  • 윈도우즈를 위해 리눅스처럼 느껴지는 환경을 제공하는 도구들의 집합이다.

  • 많은 리눅스 API 기능을 제공하는 리눅스 API 레이어로서 동작하는 DLL( cygwin1.dll )이다.


그리고 다음과 같이 착각하지 말라고 경고하고 있다.


시그윈은  :

  • 윈도우즈 상에서 네이티브 리눅스 앱들을 실행하는 방법이 아니다. 당신이 윈도우즈 상에서 프로그램을 실행하기 원한다면, 소스로부터 앱들을 리빌드해야만 한다.
  • signal, pty 등과 같은 유닉스 기능들을 인지하는 네이티브 윈도우즈 앱들을 마술처럼 만들어 주는 방법이 아니다. 다시 말하지만, 당신이 시그윈 기능의 이점을 취하기 원한다면, 소스로부터 앱들을 리빌드할 필요가 있다.


앞에서도 언급했지만 특정 CPU 아키텍처에 대해 리눅스용으로 빌드된 프로그램은 윈도우즈에서 실행될 수 없다. 당연히 윈도우즈 환경에 맞는 구성으로 다시 빌드해야만 한다. 시그윈은 크로스 플래폼 개발을 가능하게 해주는 환경일 뿐이지, 에뮬레이터가 아니라는 것을 명심해야만 한다.


Cygwin 의 작동 원리.



그렇다면 시그윈은 어떻게 해서 리눅스와 같은 개발 환경을 지원하게 되는 것일까?


여러분은 Visual Sutdio 를 통해 프로젝트 설정을 하다가 '서브시스템' 이라는 항목을 본 적이 있을 것이다. 윈도우즈는 여러 개의 서브시스템을 가지고 있는데, 우리가 주로 사용하는 것이 Win32 서브시스템이다. 아래 그림은 Windows NT 의 시스템 아키텍처에서의 POSIX 서브시스템을 보여 주고 있다.


윈도우즈 NT 시스템 아키텍처( 출처 : 위키피디아 )


이 POSIX 라는 것은 Portable Operating System Interface 의 약자로 IEEE 가 운영체제 간의 호환성을 유지하기 위해서 제정한 표준이다.


POSIX( 대충 '파직스'라고 발음됨 )는 "Portable Operating System Interface" 의 머리글자들이며, 운영체제 간의 호환성을 유지하기 위해서 IEEE 에 의해 제정된 표준 패밀리이다. POSIX 는 다양한 유닉스 및 다른 운영 체제들 간의 소프트웨어 호환성을 위해 응용프로그램 인터페이스( API )와 명령줄 쉘, 그리고 유틸리티 인터페이스들을 정의한다.


중략...


유닉스 같은 운영 체제 환경을 위한 POSIX 명세는 원래 코어 프로그래밍 인터페이스를 위한 단일 문서로 구성되어 있었지만, 결국에는 19 개의 분리된 문서로 늘어 났다( 예를 들면 POSIX1, POSIX2, 등 ). 표준화된 사용자 명령 줄과 스크립팅 인터페이스들은 Korn Shell 에 기반했다. awk, echo, ed 등을 포함하는 많은 사용자 수준 프로그램, 서비스, 유틸리티들도 표준화되었으며, 기본적인 I/O( 파일, 터미널, 네트웤 )을 포함하는 프로그램 수준 서비스들도 포함한다. POSIX 는 표준 스레딩 라이브러리 API 도 정의하는데, 이는 현대의 거의 대부분의 OS 에 의해서 지원된다. 요새는 대부분의 POSIX 파트들이 하나의 IEEE Std 1003. 1-2008 표준에 포하모디었으며, 이는 POSIX 1-2008 로 알려져 있다.


하략...


- 출처 : 위키피디아, POSIX


OS/2 라는 것은 MS 와 IBM 이 초기에 제작한 운영체제로 요새는 별로 의미가 없으므로 관심있는 분들은 위키피디아의 OS/2 라는 글을 따로 읽어 보기 바란다.


POSIX 를 지원하는 OS 리스트는 다음 링크의 'POSIX-oriented operating systems' 절에서 찾아 볼 수 있다 : 위키피디아, POSIXGNU/Linux 같은 경우에는 POSIX 에 대해 '거의 대부분의 호환성' 을 가지고 있다. 시그윈은 POSIX 에 대해 '많은 호환성' 을 가지고 있다고 표현되고 있다. 즉 안드로이드 시스템의 기반이 되는 리눅스가 POSIX 를 거의 완벽하게 지원하고 있기 때문에, 우리는 윈도우즈 상에서 시그윈을 통해 거기에 맞는 환경을 구성할 수 있는 것이다.


위에서 윈도우즈의 POSIX 서브시스템에서 언급을 했는데 여기에서 윈도우즈의 POSIX 를 언급한 것은 그냥 예를 든 것 뿐이다. 시그윈이 윈도우즈의 POSIX 서브시스템의 레이어라는 이야기는 아니다. 왜 그럴까? 윈도우즈의 POSIX 서브시스템은 POSIX 표준을 제대로 지키고 있지 않기 때문이다. 


시그윈은 윈도우즈의 POSIX 와는 개별적으로 동작하는 개발환경이다. 시그윈 환경, 시그윈 서브시스템, 시그윈 플래폼 등의 용어를 사용해도 무방하지 않을까 싶다. 시그윈이 윈도우즈의 POSIX 서브시스템의 레이어로서 동작하지 않는 이유는 다음 글을 보면 좀 더 이해가 가지 않을까 싶다 : The Sad History of the Microsoft POSIX Subsystem. 아래에 일부만 인용해 보았다. 요약하면 윈도우즈 POSIX 가 허접(?)이란 것과 XP 부터는 POSIX 서브시스템을 지원하지 않는다는 것이다. 그 뒤의 이야기가 쭉 나오는데 다 읽어 보지는 못했다.


윈도우즈 NT 가 처음 개발되고 있었을 때, 목표 중의 하나는 커널을 프로그래밍 인터페이스와 분리하는 것이었다. NT 는 원래 OS/2 를 성공시키기 위한 것이었지만, 마이크로소프트는 윈도우즈 3.x 응용프로그램들을 실행하기 위한 호환성을 포함시키고, 방위산업 시장에 판매하기 위한 1980 년대 DoD( 국방성 ) Orange Book 과 FIPS 명세들을 만족시키기 원했다. 결과적으로 윈도우즈 NT 는 복잡한 자유재량에 의한 접근 제어 기능을 가진 멀티유저 플래폼으로서 개발되었으며, 하이브리드 마이크로커널 3 유저랜드[각주:1] 환경들로서 구현되었다 :


  • Windows( Win32 )
  • OS/2
  • POSIX


마이크로소프트는 Win32 문제를 가지고 IBM 과 다퉜으며, NT 프로젝트는 OS/2 와 갈라섰다. 팀의 초점은 Win32 로 이동했다. 그래서 Win32 API 를 호스트하는 Client Server Runtime Subsystem( CSRSS ) 은 의무적이 되었으며, OS/2 와 POSIX 서브시스템이 실제로 완료되지 않았음에도 불구하고 그것들은 윈도우즈 NT 부터 윈도우즈 2000 까지 5 개의 버전에 포함되었다. OS/2 서브시스템은 단지 OS/2 1.0 명령줄 프로그램들만을 실행할 수 있으며, presentation manager 에 대한 지원이 존재하지 않았다. POSIX 서브시스템은 POSIX.1 명세를 지원했지만, 모든 종류의 쉘이나 유닉스 같은 환경을 제공하지 않았다. Win32 가 윈도우즈 95 의 형태에서 성공하면서, OS/2 와 POSIX 서브시스템의 개발은 중단되었다. 그것들은 윈도우즈 XP 와 윈도우즈 서버 2003 에서 완전히 죽고 사라졌다.


반면에 Softway Systems 는 1996 년 쯤에 OpenNT 라고 불리는 유닉스-윈도우즈 NT 간 포팅 시스템을 개발했다. OpenNT 는 NT POSIX 서브시스템 상에서 빌드되었지만, 그것에 살을 붙여 이용할 수 있는 유닉스 환경을 제공했다. 이는 유닉스 시스템이 매우 비싸진 시점이었다. Softway 는 OpenNT 를 사용해서 US Federal agency 들을 위한 여러 유닉스 응용프로그램들을 윈도우즈 NT 로 변경했다. 1998 년에 OpenNT 는 Interix 라고 이름을 바꿨다. Softway Systems 는, 마이크로소프트 POSIX 서브시스템이 지원하지 않았던 시스템 호출을 구현하고, 풍부한 libc,  파일 시스템에 대한 single-rooted view, 그리고 실용적인 gcc 를 개발하기 위해, 최종적으로 NT POSIX 서브시스템에 대한 완전한 대체물을 만들었다.


하략...


위키피디아에 의하면 Windows Server 2003 R2 부터는 사실은 Interix 의 변형인 SUA( Subsystem for UNIX-based Applications )를 통해서 POSIX 를 지원하고 있다고 한다. 이것은 SFU( Windows Services for UNIX )라고 불리기도 한다. Windows Vista 및 Windows 7 의 Enterprise 에디션과 Ultimate 에디션도 SUA 를 포함하고 있다고 한다. 자세한 내용은 Windows Services for UNIX 를 참조하라.


결론.



우리가 윈도우즈 환경에서 리눅스 ARM 용 프로그램을 빌드하기 위해서는, 그에 맞는 바이너리를 생성해 줄 수 있는 환경( 컴파일러, API 등 )을 구성할 필요가 있다. 윈도우즈에서 이를 가능하게 해 주는 것이 POSIX 기반의 시그윈 개발 환경이다. 


우리는  시그윈 환경을 이용해 리눅스 ARM 용 프로그램을 빌드하고, 그것을 안드로이드 장치로 복사하고 설치하게 된다. 이것이 윈도우즈와 리눅스 간의  크로스 플래폼 개발이다. 안드로이드 SDK 를 사용하게 되면 자바 바이트 코드( 플래폼에 독립적인 중간 코드 )를 생성하기 때문에 빌드 환경이 중요하지 않지만, 안드로이드 NDK 는 실제 리눅스 API 를 이용하고 기계어로 컴파일하기 때문에 반드시 올바른 환경에서 개발해야만 한다.


이제 시그윈이라는 것의 정체에 대해 어느 정도 이해할 수 있게 되었을 것이라 생각한다. 그리고 시그윈을 통해 뭔가를 빌드한다는 것의 의미에 대해서 이해할 수 있게 되었을 것이라 생각한다.


다음에 시간이 나면 POSIX 에 대해서 좀 더 구체적으로 다뤄 보도록 하겠다.


  1. 사용자의 응용프로그램들이 운영체제에 대한 위험 부담 없이 실행될 수 있는 커널 외부의 개념 공간 - 출처 : 윅셔너리 [본문으로]

 개발자라면 자신이 사용하고 있는 도구들에 대한 개념을 어느 정도는 이해하고 있어야만 합니다. 그런 의미에서 ADB( Android Debug Bridge )에 대한 글을 번역해 보았습니다.


원문 : Android Debug Bridge.

주의 : 번역이 개판이므로 이상하면 반드시 원문을 참조하세요.

주의 : 가독성을 높이기 위해서 발음이 비슷하거나 잘 알려진 용어는 한글로 표기합니다.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.




Android Debug Bridge


Android Debug Bridge( adb )는 다용도의 명령줄 도구인데, 당신이 에뮬레이터 인스턴스나 안드로이드용 장치와 통신할 수 있도록 해 준다. 이는 클라이언트-서버 프로그램으로, 세 가지 요소를 포함한다.


  • 클라이언트. 이는 당신의 개발 머신 상에서 동작한다. 당신은 adb 명령을 날림으로써 쉘shell로부터 클라이언트를 실행할 수 있다. ADT 플러그인과 DDMS 와 같은 다른 안드로이드 도구들도 adb 클라이언트들을 생성한다.
  • 서버. 이는 당신의 개발 장치 상에서 백그라운드 프로세스로서 동작한다. 이 서버는 클라이언트와 에뮬레이터나 장치상에서 실행중인 adb daemon 사이의 통신을 관리한다.
  • Daemon. 이는 각각의 에뮬레이터나 장치 상에서 백그라운드 프로세스로서 동작한다.


당신은 adb 도구를 <sdk>/paltform-tools/ 에서 찾을 수 있다.


당신이 adb 클라이언트를 실행할 때, 그 클라이언트는 먼저 이미 실행중인 adb 서버 프로세스가 존재하는지를 검사한다. 만약 존재하지 않으면, 그것은 서버 프로세스를 실행한다. 서버가 시작될 때, 그것은 로컬 TCP 포트 5037 에 연결되며, adb 클라이언트들로부터 보내지는 명령들을 받는다 - 모든 adb 클라이언트들은 5037 포트를 사용해서 adb 서버와 통신한다.


그리고 나서 서버는 모든 실행중인 에뮬레이터/장치 인스턴스들에 대한 연결을 설정한다. 그것은 에뮬레이터/장치들에 의해 사용되는 5555 에서 5585 사이의 홀수 번호 포트들을 스캐닝함으로써 에뮬레이터/장치 인스턴스들을 배치한다. 서버가 adb daemon 을 찾으면, 그것은 그 포트에 연결을 설정한다. 각 에뮬레이터/장치 인스턴스들은 연속된 포트의 쌍을 요청한다는데 주의하라 - 콘솔 연결을 위해 짝수 번호 포트를 사용하고, adb 연결을 위해 홀수 번호 포트를 사용한다. 예를 들어 :


에뮬레이터 1, 콘솔 : 5554

에뮬레이터 1, adb : 5555

에뮬레이터 2, 콘솔 : 5556

에뮬레이터 2, adb : 5557

이런 식이다...


위에서 보이듯이 5555 포트에 연결된 에뮬레이터 인스턴스는 5554 포트에서 응답을 받는 콘솔의 인스턴스와 동일하다.


일단 서버가 모든 에뮬레이터 인스턴스들에 대한 연결을 설정하고 나면, 당신은 adb 명령을 사용해서 그러한 인스턴스들에 접근할 수 있다. 서버는 에뮬레이터/장치 인스턴스들에 대한 연결을 관리하고 다중의 adb 클라이언트들로부터의 명령을 다루기 때문에, 당신은 모든 클라이언트들( 혹은 스크립트 )로부터 모든 메율레이터/장치 인스턴스들을 제어할 수 있다.


Syntax



당신은 개발 머신의 명령줄이나 스크립트에서 adb 명령을 제출issue할 수 있다. 용례는 다음과 같다 :



만약 단 하나의 에뮬레이터가 실행되고 있거나 단 하나의 장치만이 연결되어 있다면, adb 명령은 기본적으로 그 장치로 보내 진다. 만약 다중의 에뮬레이터가 실행되고 있거나 다중의 장치가 연결되어 있다면, 당신은 -d, -e, -s 옵션을 사용해서 그 명령이 향해야만 하는 대상 장치를 지정해야할 필요가 있다.


Commands



아래 표는 지원되는 adb 명령들을 모두 열거하고, 그것들의 의미 및 용례를 설명한다.


표1. 이용 가능한 adb 명령들.

 Category  Command  Description  Comments

 Target

 Device

 -d

 adb 명령이 연결된 하나의 USB 장치로만 향함.

 두 개 이상의 USB 장치가 연결되어 있다면 에러를 반환함.

 -e

 adb 명령이 실행중인 단 하나의 에뮬레이터 인스턴스로만 향함.

 두 개 이상의 에뮬레이터 인스턴스가 실행중이면 에러를 반환함.

 -s <serialNumber>

 adb 명령이 특정 에뮬레이터/장치 인스턴스로 향함. adb 에 할당된 ( "emulator-5556" 과 같은 ) 시리얼 넘버에 의해서 지정됨.

 Directing Commands to a Specific Emulator/Device instance 를 참조하라.

 General  devices

 연결된 모든 에뮬레이터/장치 인스턴스들의 리스트를 출력함.

 더 많은 정보를 원한다면 Querying for Emulator/Devices Instances 를 참조하라.

 help  지원되는 adb 명령들의 리스트를 출력함.  
 version  adb 버전 번호를 출력함.  
 Debug

 logcat [option] [filter-specs]

 화면에 로그 데이터를 출력함.

 

 bugreport

 버그 리포팅을 위한 목적으로 dumpsys, dumpstate, logcat 데이터를 화면에 출력함.

 
 jdwp

 주어진 장치에서 이용 가능한 JDWP 프로세스들의 리스트를 출력함.

 당신은 forward jdwp:<pid> 포트 포워딩 명세를 사용해서 지정한 JDWP 프로세스에 연결할 수 있다. 예를 들어 :

 adb foward tcp:8000 jdwp:472

 jdb -attach localhost:8000

 Data

 install <path-to-apk>

 안드로이트 응용프로그램을 에뮬레이터/장치에 밀어 넣음( .apk 파일의 전체 경로를 지정 ).

 
 pull <remote> <local>

 지정된 파일을 에뮬레이터/장치 인스턴스로부터 당신의 개발 컴퓨터로 복사함.

 

 push <local> <remote>

 지정되 파일을 당신의 개발 컴퓨터로부터 에뮬레이터/장치 인스턴스로 복사함.  
 Port and
 Networking

 forward <local> <remote>

 소켓 연결들을 지정된 로컬 포트로부터 에뮬레이터/장치 인스턴스의 지정된 리모트 포트로 포워딩.

 포트 명세는 다음과 같은 정책을 사용할 수 있음 :
 tcp:<portnum>

 local:<UNIX domain socket name>

 dev:<character device name>

 jdwp:<pid> 

 ppp <tty> [param]...

 USB 에 PPP 를 실행함.

  • <tty> - PPP 스트림을 위한 tty. 예를 들어 dev:/dev/omap_csmi_ttyl.
  • [param]... - 0 개 이상의 PPP/PPPD 옵션들. defaultroute, local, notty 등.

 당신은 자동적으로 PPP 연결을 시작해서는 안된다는 것에 주의하라.

 
 Scripting  get-serialno

 adb 인스턴스 시리얼 넘버 문자열을 출력.

 더 많은 정보를 원한다면 Querying for Emulator/Device Instances 를 참조하라.
 get-state

 에뮬레이터/장치 인스턴스의 adb 상태를 출력.

 

 wait-for-device

 장치가 연결될 때까지 실행을 중지 - 즉 인스턴스 상태가 device 일 때까지.

 당신은 이 명령을 어떤 adb 명령들 앞에 배치할 수 있다. 그 명령들은 다른 명령들이 제출되기 전에 에뮬레이터/장치 인스턴스가 연결될 때까지 기다려야 하는 명령들이다. 아래 예가 있다.

 이 명령은 전체 시스템이 완전히 부팅되기 전까지 adb 를 기다리게 하지는 않는다. 그러한 이유로, 당신은 그것을 완전히 시스템이 부팅되기를 요구하는 명령들 앞에다가 배치하면 안 된다. 그러한 예로, install 은 안드로이드 패키지 매니저를 요구하는데, 이는 시스템이 완전히 부팅된 후에만 이용 가능하다. 다음과 같은 명령은

에뮬레이터나 장치 인스턴스가 adb 서버에 연결되자 마자 install 명령을 제출하지만, 안드로이드 시스템은 완전히 부팅되어 있기 전이다. 그래서 그것은 에러를 만들 것이다.

 Server  start-server

 adb 서버 프로세스가 실행중인지 검사하고, 실행중이 아니면 시작함.

 
 kill-server

 adb 서버 프로세스를 종료함.

 
 Shell  shell

 리모트 쉘을 대상 에뮬레이터/장치 인스턴스에서 시작함.

 더 많은 정보를 원하면 Issuing Shell Commands 를 참조하라.

 shell [shellCommand]

 쉘 명령을 대상 에뮬레이터/장치 인스턴스에 제출하고, 리모드 쉘을 나감.


Querying for Emulator/Device Instances



adb 명령을 제출하기 전에, 어떤 에뮬레이터/장치 인스턴스들이 adb 서버에 연결되는지 아는 것이 도움이 된다. 당신은 연결된 에뮬레이터/장치들의 리스트를 devices 명령을 사용해서 생성할 수 있다.



응답으로, adb 는 각 인스턴스에 대한 상태 정보를 출력한다.


  • 시리얼 넘버 - 에뮬레이터/장치 인스턴스를 유일하게 식별하기 위해서 그것의 콘솔 포트 넘버를 사용해 adb 에 의해 생성된 문자열이다. 그 시리얼 넘버의 포맷은 <type>-<consolePort>이다. 여기에 시리얼 넘버의 예가 있다 : enumerator-5554.
  • 상태 - 그 인스턴스의 연결 상태는 다음 중 하나일 것이다.
    • offline - 그 인스턴스가 adb 에 연결되지 않았거나 응답하지 않고 있음.
    • device - 그 인스턴스가 이제 adb 서버에 연결됨. 이 상태는 안드로이드 시스템이 완전히 부팅되거나 실행중임을 내포하지 않으며 선택적이다. 왜냐하면 그 인스턴스는 시스템이 부팅중일 때 adb 에 연결되기 때문이다. 그러나 부팅이 되고 나면, 에뮬레이터/장치 인스턴스에 대한 일반적인 실행가능한 상태이다.
    • no device - 어떠한 에뮬레이터/장치도 연결되지 않음.


각 인스턴스를 위한 출력은 다음과 같은 형식이다 :



여기 devices 명령과 그것의 출력을 보여 주는 예가 있다.



Directing Commands to a Specific Emulator/Device Instance



만약 다중의 에뮬레이터/장치 인스턴스들이 실행중이라면, 당신은 반드시 adb 명령들을 제출할 때 대상 인스턴스를 지정해야만 한다. 이를 위해서, 명령들에서 -s 옵션을 사용하라. -s 옵션의 용례는 다음과 같다.



보면 알겠지만, 당신은 adb 할당 시리얼 넘버를 사용해 명령을 위한 대상 인스턴스를 지정한다. 당신은 devices 명령을 사용해 실행중인 에뮬레이터/장치 인스턴스들의 시리얼 넘버를 획득할 수 있다. 예를 들어 :



다중 장치들이 이용가능 할 때, 만약 당신이에뮬레이터/장치 인스턴스를 지정하지 않고 명령을 제출한다면, adb 는 에러를 생성한다는 것에 주의하라.


당신이 이용 가능한 다중의 장치( 하드웨어 혹은 에뮬레이터 )를 가지고 있지만 단 하나만이 에뮬레이터라면, 단순히 -e 옵션을 사용해 에뮬레이터에다가 명령들을 보내라. 이와 유사하게 다중의 장치들이 있지만 단 하나의 하드웨어 장치만이 연결되었다면, -d 옵션을 사용해서 하드웨어 장치에 명령을 보내라.


Installing an Application



당신은 개발 컴퓨터로부터의 응용프로그램을 복사해 에뮬레이터/장치 인스턴스에 설치하기 위해서 adb 를 사용할 수 있다. 이를 위해, install 명령을 사용하라. 그 명령을 사용하면, 당신은 설치하기 원하는 .apk 파일의 경로를 지정해야만 한다.



에뮬레이터/장치 인스턴스 상에 설치하고자 하는 .apk 을 생성하는 방법에 대한 더 많은 정보를 원한다면, Building and Running 을 참조하라.


만약 당신이 Eclipse IDE 를 사용하고 있고 ADT 플러그인이 설치되어 있다면, 당신은 에뮬레이터/장치 상에 응용프로그램을 설치하기 위해 adb( 혹은 aapt )를 직접 사용할 필요가 없다는 것을 기억하라. 대신에 ADT 플러그인이 당신을 위해 응용프로그램의 패키징과 설치를 처리해 준다.


Forwarding Ports



당신은 forward 명령을 사용해서 임의의 포트 포워딩을 설정할 수 있다 - 특정 호스트 포트상에서 에뮬레이터/장치 상의 다른 포트로의 포워딩 요청. 여기 6100 호스트 포트에서 에뮬레이터/장치 포트 7100 으로의 포워딩을 설정하는 방법이 다음에 나와 있다.



당신은 다음과 같이 이름있는 추상 UNIX 도메인 소켓들로 포워딩을 설정하기 위해서 adb 를 사용할 수 있다.



Copying Files to or from an Emulator/Device instance



당신은 adb 의 pull 과 push 명령들을 사용해서 에뮬레이터/장치 인스턴스로부터 혹은 인스턴스로 파일을 복사할 수 있다. 특정 위치로 APK 파일을 복사만하는 install 명령과는 다르게, pull 과 push 명령은 에뮬레이터/장치 인스턴스의 모든 위치로 임의의 디렉토리나 파일들을  복사할 수 있게 해 준다.


에뮬레이터나 장치로부터 파일이나 디렉토리( 그리고 그것의 하위 디렉토리들 )를 복사하기 위해서는, 다음과 같은 명령을 사용한다.



파일이나 디렉토리( 그리고 그것의 하위 디렉토리들 )를 에뮬레이터나 장치로 복사하기 위해서는, 다음과 같은 명령을 사용한다.



명령에서 <local> 과 <remote> 는 당신의 개발 머신( local ) 과 에뮬레이터/장치 인스턴스( remote ) 상의 대상 파일/디렉토리에 대한 경로를 가리킨다. 예를 들어 :



Using activity manager( am )


adb 쉘에서, 당신은 activity manager( am ) 도구를 사용해 명령들을 제출하여 activity 를 시작하고, 프로세스를 강제로 종료시키고, intent 를 송출하고broardcast, 장치 스크린 속성들을 수정하는 등과 같은 다양한 시스템 동작들을 수행할 수 있다. 쉘에 있는 동안, 그 문맥은 다음과 같다 :



당신은 remote 쉘에 진입하지 않고도 adb 로부터 직접적으로 activity manager 명령을 제출할 수도 있다. 예를 들어 :



표2. 이용 가능한 activity manager 명령들.

 Command  Description 

 start [options] <INTENT>

 <INTENT> 에 의해 지정된 Activity 를 시작한다.

 Specification for <INTENT> arguments 를 참조하라.

 Option 들은 다음과 같다:

  • -D : 디버깅을 활성화.
  • -W : 론칭launch이 완료될 때까지 대기.
  • --start-profiler <FILE>: 프로파일러를 시작하고 <FILE>로 결과를 보냄.
  • -p <FILE> : --start-profiler 와 비슷하지만, 프로파일링은 앱이 idle 이 될 때 멈춤.
  • -R : <COUNT> 번 activity 론칭을 반복함. 각각의 반복에 앞서, 최상위 activity 는 종료될 것이다.
  • -S : 대상 앱을 activity 가 시작하기 전에 강제로 종료함.
  • --opengl-trace : OpenGL 함수들의 추적을 활성화.
  • --user <USER_ID>|current : 어떠한 사용자가 실행할 것인지를 지정; 만약 지정되지 않으면, 현재 사용자로서 실행됨.
 startservice [options] <INTENT>

 <INTENT> 에 의해 지정된 Service 를 시작한다.

 Specification for <INTENT> arguments 를 참조하라.

 옵션들은 다음과 같다 :

  • --user <USER_ID> | current : 어떠한 사용자로서 실행할 것인지를 지정; 만약 지정되지 않으면, 현재 사용자로서 실행됨.

 force-stop <PACKAGE>

 <PACKAGE>와 연관된 모든 것들을 강제로 종료한다( 앱의 패키지 이름 ).
 kill [options] <PACKAGE>

 <PACKAGE>와 연관된 모든 프로세스들을 종료한다. 이 명령은 종료하기 안전하고 다른 사용자 경험에 영향을 주지 않는 프로세스들만 죽인다.

 옵션들은 다음과 같다 :

  • --user <USER_ID>|all|current : 죽일 프로세스를 소유한 사용자를 지정한다; 만약 지정되지 않으면 모든 사용자이다.

 kill-all

 모든 백그라운드 프로세스들을 죽인다.

 broadcast [options] <INTENT>

 broadcast intent 를 제출한다.

 Specification for <INTENT> arguments 를 참조하라.

 옵션들은 다음과 같다 :

[--user <USER_ID>|all|current] : 어떤 사용자에게 보낼 것인지 지정한다; 만약 지정되지 않으면 모든 사용자에게 보낸다.

 instrument [options] <COMPONENT>

 Instrumentation 인스턴스를 사용해 모니터링을 시작한다. 일반적으로 대상 <COMPONENT> 는 <TEST_PACKAGE>/<RUNNER_CLASS> 형식이다.

 옵션은 다음과 같다 :

  • -r : raw result 들을 출력한다( 그렇지 않으면 <REPORT_KEY_STREAMRESULT> 를 디코딩한다 ). [-e perf true] 와 함께 사용해 성능 측정을 위한 raw 출력을 생성한다.
  • -e <NAME> <VALUE> : 인자 <NAME> 을 <VALUE>로 설정한다. 테스트 실행자를 위한 공용 형식은 -e <testrunner_flag> <value>[, <value>... ] 이다.
  • -p <FILE> : <FILE> 에 프로파일링 데이터를 작성한다.
  • -w : 반환하기 전에 instrumentation 이 끝나길 기다린다. test runner 들에게 요구된다.
  • --no-window-animation : 실행하는 동안 윈도우 애니메이션들을 끈다.
  • --user <USER_ID>|current : instrumentation 이 실행될 사용자를 지정한다; 만약 지정되지 않으면 현재 사용자이다.

 profile start <PROCESS> <FILE>

 <PROCESS> 상에서 프로파일러를 시작하는데, 그 결과를 <FILE>에 쓴다.

 profile stop <PROCESS>

 <PROCESS> 상의 프로파일러를 중지시킨다.
 dumpheap [options] <PROCESS> <FILE>

 <PROCESS> 의 힙을 덤프하는데, <FILE> 에 쓴다.

 옵션들은 다음과 같다 :

  • -user [<USER_ID>|current] : 프로세스 이름을 공급할 때, 덤프할 프로세스의 사용자를 지정한다; 만약 지정되지 않으면 현재 사용자이다.
  • -n : managed heap 대신에 native heap 을 덤프한다.
 set-debug-app [options] <PACKAGE>

 응용프로그램 <PACKAGE> 를 디버깅하기 위해 설정한다 :

 옵션들은 다음과 같다 :

  • -w : 디버거를 위해 응용프로그램이 시작되기를 기다린다.
  • --persistent : 이 값을 보존한다.
 clear-debug-app

 set-debug-app 를 사용해 디버깅중인 이전의 패키지를 클리어한다.

 monitor [options]

 crash 나 ANR 을 모니터링하기 시작한다.

 옵션들은 다음과 같다 :

  • --gdb : Start gdbserv on the given port at crash/ANR. 

 screen-compat [on|off ] <PACKAGE>

 <PACKAGE> 의 screen compativility 를 제어한다.

 display-size [reset|<WxH>]

 에뮬레이터/장치 디스플레이 사이즈를 덮어 쓴다. 이 명령은 당신의 앱을 다양한 스크린 사이즈를 넘나들며 테스트하는데 유용하다. 이는 큰 스크린을 가진 장치를 사용해 작은 스크린 해상도를 미믹함으로써 이루어 진다. 예를 들어 :

 am display-size 1280x800

 display-density <dpi>

 에뮬레이터/장치 디스플레이 밀도를 덮어 쓴다.이 명령은 높은 밀도의 스크린 환경 상에서 낮은 밀도의 스크린을 사용해 다양한 스크린 밀도를 넘나들며 당신의 앱을 테스트하는데 유용하다. 예를 들어 :

 am display-density 480

 to-uri <INTENT>

 주어진 intent 명세를 URI 로서 출력한다.

 Specification for <INTENT> arguments 를 참조하라.

 to-intent-uri <INTENT>

 주어진 intent 명세를 intent 로서 출력한다 : URI.

 Specification for <INTENT> arguments 를 참조하라. 


Specification for <INTENT> arguments


<INTENT> 인자를 취하는 activity manager 명령들을 위해, 당신은 다음과 같은 옵션들을 사용해 intent 를 지정할 수 있다 :


-a <ACTION>

"android.intent.action.VIEW" 와 같은 intent action 을 지정한다. 당신은 이를 한 번만 선언할 수 있다.

-d <DATA_URI>

"content://contacts/people/1" 와 같은 intent data URI 를 지정한다. 당신은 이를 한 번만 선언할 수 있다.

-t <MIME_TYPE>

"image/png" 과 같은 intent MIME 타입을 추가한다. 당신은 이를 한 번만 선언할 수 있다.

-c <CATEGORY>

"android.intent.category.APP_CONTACTS" 와 같은 intent 카테고리를 지정한다.

-n <COMPONENT>

"com.example.app/.ExampleActivity" 와 같은 명시적인 intent 를 생성하기 위한 패키지 이름 접두어를 사용해 컴포넌트 이름을 지정한다..

-f <FLAGS>

intent 에 setFlags() 에 의해 지원되는 플래그를 추가한다.

--esn <EXTRA_KEY>

null extra 를 추가한다. 이 옵션은 URI intent 들에 대해서는 지원되지 않는다.

-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE>

문자 데이터를 key-value 쌍으로서 추가한다.

--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE>

Boolean 데이터를 key-value 쌍으로서 추가한다.

--ei <EXTRA_KEY> <EXTRA_INT_VALUE>

정수 데이터를 key-value 쌍으로서 추가한다.

--el <EXTRA_KEY> <EXTRA_LONG_VALUE>

Long 데이터를 key-value 쌍으로서 추가한다.

--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE>

실수 데이터를 key-value 쌍으로서 추가한다.

--eu <EXTRA_KEY> <EXTRA_URI_VALUE>

URI 데이터를 key-value 쌍으로서 추가한다.

--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>

ComponentName 개체로서 변환되고 전달될 컴포넌트의 이름을 추가한다.

--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]

정수 배열을 추가한다.

--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]

Long 배열을 추가한다.

--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]

실수 배열을 추가한다.

--grant-read-uri-permission

FLAG_GRANT_READ_URI_PERMISSION 플래그를 포함시킨다.

--grant-write-uri-permission

FLAG_GRANT_WRITE_URI_PERMISSION 플래그를 포함시킨다.

--debug-log-resolution

FLAG_DEBUG_LOG_RESOLUTION 플래그를 포함시킨다.

--exclude-stopped-packages

FLAG_EXCLUDE_STOPPED_PACKAGES 플래그를 포함시킨다.

--include-stopped-packages

FLAG_INCLUDE_STOPPED_PACKAGES 플래그를 포함시킨다.

--activity-brought-to-front

FLAG_ACTIVITY_BROUGHT_TO_FRONT 플래그를 포함시킨다.

--activity-clear-top

FLAG_ACTIVITY_CLEAR_TOP 플래그를 포함시킨다.

--activity-clear-when-task-reset

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 플래그를 포함시킨다.

--activity-exclude-from-recents

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 플래그를 포함시킨다.

--activity-launched-from-history

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 플래그를 포함시킨다.

--activity-multiple-task

FLAG_ACTIVITY_MULTIPLE_TASK 플래그를 포함시킨다.

--activity-no-animation

FLAG_ACTIVITY_NO_ANIMATION 플래그를 포함시킨다.

--activity-no-history

FLAG_ACTIVITY_NO_HISTORY 플래그를 포함시킨다

--activity-no-user-action

FLAG_ACTIVITY_NO_USER_ACTION 플래그를 포함시킨다.

--activity-previous-is-top

FLAG_ACTIVITY_PREVIOUS_IS_TOP 플래그를 포함시킨다.

--activity-reorder-to-front

FLAG_ACTIVITY_REORDER_TO_FRONT 플래그를 포함시킨다.

--activity-reset-task-if-needed

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 플래그를 포함시킨다.

--activity-single-top

FLAG_ACTIVITY_SINGLE_TOP 플래그를 포함시킨다.

--activity-clear-task

FLAG_ACTIVITY_CLEAR_TASK 플래그를 포함시킨다.

--activity-task-on-home

FLAG_ACTIVITY_TASK_ON_HOME 플래그를 포함시킨다.

--receiver-registered-only

FLAG_RECEIVER_REGISTERED_ONLY 플래그를 포함시킨다.

--receiver-replace-pending

FLAG_RECEIVER_REPLACE_PENDING 플래그를 포함시킨다.

--selector

intent 데이터와 타입을 설정하기 위해서 -d 와 -t 옵션의 사용을 요구한다.

<URI> <COMPONENT> <PACKAGE>

위의 옵션들 중 하나에 의해 한정되지 않을 때, 당신은 직접적으로 URI, 패키지 이름, 컴포넌트 이름을 지정할 수 있다. 인자가 한정되지 않았을 때, 그 도구는 그것이 ":"( 콜론 )을 포함한다면 그 인자를 URI 로 가정한다; 그것이 "/"( 슬래쉬 )를 포함하면 인자를 컴포넌트 이름으로 가정한다; 그렇지 않으면 그것은 인자를 패키지 이름으로 가정한다.


Using package manager( pm )


adb 쉘 안에서, 당신은 package manager( pm ) 도구를 사용하는 명령들을 제출함으로써 장치에 설치된 응용프로그램 패키지들 상에서의 동작과 질의를 수행할 수 있다. 쉘에 있는 동안, 그 문맥은 다음과 같다 :



당신은 remote 쉘에 진입하지 않고도 adb 로부터 package manager 명령을 직접적으로 제출할 수 있다. 예를 들어 :



표3. 이용 가능한 package manager 명령들.

 Command  Description

 list packages [options] <FILTER>

 모든 패키지들을 출력하는데, 선택적으로 <FILTER> 에 있는 텍스트를 포함하는 패키지 이름을 가진 것들만 출력한다.

 옵션 :

  • -f : 그것들의 연관 파일들을 보여 준다.
  • -d : disabled 패키지들만을 보여 주는 필터.
  • -e : enabled 패키지들만을 보여 주는 필터.
  • -s : 시스템 패키지들만을 보여 주는 필터.
  • -3 : 서드 파티 패키지들만을 보여 주는 필터.
  • -i : 패키지들을 위한 인스톨러들을 보여 준다.
  • -u : uninstalled 패키지들도 포함한다.
  • --user <USER_ID> : 질의할 사용자 공간.

 list permission-groups

 모든 알려진 권한 그룹들을 출력한다.

 list permissions [options] <GROUP>

 모든 알려진 권한 그룹들을 출력하는데, 선택적으로 <GROUP> 에 있는 것들만 출력한다.

 옵션 :

  • -g : 그룹에 의해 조직함.
  • -f : 모든 정보를 출력함.
  • -s : 짧은 요약.
  • -d : 단지 위험한 권한들만을 열거함.
  • -u : 사용자가 보려고 하는 권한만을 열거함.
 list instrumentation

 모든 테스트 패키지를 열거함.

 옵션 : 

  • -f : 테스트 패키지를 위한 APK 파일을 열거함.
  • <TARGET_PACKAGE> : 이 앱만을 위한 테스트 패키지들을 열거함.

 list features

 시스템의 모든 기능을 출력함.

 list libraries  현재 장치에 의해 지원되는 모든 라이브러리들을 출력함.
 list users

 시스템상의 모든 사용자들을 출력함.

 path <PACKAGE>

 주어진 <PACKAGE>의 APK 경로를 출력함.

 install [options] <PATH>

 시스템에 ( <PATH>로 지정된 ) 패키지를 설치함.

 옵션 :

  • -l : forward lock 을 사용해 패키지를 설치.
  • -r : 데이터를 유지하면서 현존하는 앱을 재설치.
  • -t : 테스트 APK 들이 설치되도록 허용함.
  • -i <INSTALLER_PACKAGE_NAME > : 인스톨러 패키지 이름을 지정함.
  • -s : ( sd 카드와 같은  ) 공유 대용량 저장소에 패키지를 설치.
  • -f : 내부 시스템 메모리에 패키지를 설치.
  • -d : version code 다운그레이드를 허용.
 uninstall [options] <PACKAGE>

 시스템에서 패키지를 제거.

 옵션 :

  • -k : 데이터와 캐시 디렉토리들을 패키지가 제거된 후에도 남겨 둠.
 clear <PACKAGE>  패키지와 관련된 모든 데이터를 제거함.

 enable <PACKAGE_OR_COMPONENT>

 주어진 패키지나 컴포넌트를 이용 가능하게 함( "package/class" 로 작성됨).

 disable <PACKAGE_OR_COMPONENT>

 주어진 패키지나 컴포넌트를 이용 불가능하게 함( "package/class" 로 작성됨).

 disable-user [options] <PACKAGE_OR_COMPONENT>

 옵션 :

  • --user <USER_ID> : 이용 불가능하게 할 사용자.

 grant <PACKAGE_PERMISSION>

 응용프로그램에 대한 권한을 승인. 응용프로그램이 이미 선언한 선택적인 권한만이 승인될 수 있음.

 revoke <PACKAGE_PERMISSION>

 응용프로그램에 대한 권한에 대한 승인을 철회. 응용프로그램이 이미 선언한 선택적인 권한에 대해서만 승인이 철회될 수 있음.

 set-install-location <LOCATION>

 기본 설치 위치를 변경함. 위치 값들은 다음과 같다 :

  • 0 : Auto - 시스템이 최적의 위치를 결정함.
  • 1 : Internal - 내부 장치 저장소 상에 설치.
  • 2 : External - 외부 미디어 상에 설치.

 주의 : 이는 디버깅을 위한 용도이다; 이것은 응용프로그램이 깨지거나 다른 원하지 않는 행동을 하도록 만들 수 있다.

 get-install-location

 현재 설치 위치를 반환함. 반환값들을 다음과 같다 

  • 0 : Auto - 시스템이 최적의 위치를 결정함.
  • 1 : Internal - 내부 장치 저장소 상에 설치.
  • 2 : External - 외부 미디어 상에 설치.

 set-permission-enforced <PERMISSION> [true|false]

 주어진 권한이 강제되어야 하는지를 지정한다.

 trim-caches <DESIRED_FREE_SPACE>

 주어진 가용 공간에 도달하기 위해서 캐시 파일들을 손질한다.

 create-user <USER_NAME>

 주어진 <USER_NAME> 을 사용해 새로운 사용자를 생성하고, 그 사용자의 새로운 식별자를 출력함.

 remove-user <USER_ID>  주어진 <USER_ID>를 사용해 사용자를 제거하고, 그 사용자와 관련된 모든 데이터를 제거함.
 get-max-users

 장치에 의해 지원되는 최대 사용자 수를 출력함.


Examing sqlite3 databases from a remote shell


adb remote 쉘에서, 당신은 안드로이드 응용프로그램들에 의해서 생성된 SQLite 데이터베이스들을 관리하기 위해서 sqllite3 명령줄 프로그램을 사용할 수 있다. sqlite3 도구는 많은 유용한 명령들을 포함하고 있다; .dump 는 테이블의 내용을 출력하며, .schema 는 현존 테이블을 위한 SQL CREATE 상태를 출력한다. 이 도구는 SQLite 명령들을 실시간에 실행할 수 있는 기능을 제공하기도 한다.


sqlite3 를 사용하기 위해, 에뮬레이터 인스턴스 상의 remote 쉘로 들어간다. 그리고 나서 sqlite3 명령을 사용해 그 도구를 실행한다. sqlite3 를 실행할 때 당신은 선택적으로 당신이 살펴 보고자 하는 데이터베이스의 전체 경로를 지정할 수 있다. 에뮬레이터/장치 인스턴스들은 SQLite3 데이터베이스들을 /data/data/<package_name>/databases/ 폴더에다가 저장한다.


여기 예제가 있다 :



일단 sqlite3 를 실행하고 나면, 당신은 그 쉘에서 sqlite3 명령들을 제출할 수 있다. adb remote 쉘로 나가고 싶으면, exit 나 CTRL+D 를 사용한다.


UI/Application Exerciser Monkey


몽키는 당신의 에뮬레이터나 장치 상에서 실행되는 프로그램이며, 클릭, 터치, 제스쳐 등과 같은 사용자 이벤트들과 시스템 수준 이벤트들의 의사-랜덤 스트림들을 생성한다. 당신은 몽키를 사용해서 당신이 개발하고 있는 응용프로그램들에 대한 랜덤하지만 약간 반복적인 방식으로 스트레스 테스트를 할 수 있다.


몽키를 사용하는 가장 단순한 방법은 아래 명령을 사용하는 것인데, 그것은 당신의 응용프로그램을 실행하고 500 개의 의사-랜덤 이벤트들을 그것에 보낸다.



몽키를 위한 명령 옵션에 대한 더 많은 정보를 원한다면, 완전한 UI/Application Exerciser Monkey 문서화 페이지를 참조하라.


Other shell commands


이용 가능한 모든 쉘 프로그램들을 열거하기 위해서는 다음과 같은 명령을 사용하라 :



help 는 대부분의 명령들에 대해 이용할 수 있다.


표4 는 좀 더 공용적인 adb 쉘 명령들을 열거한다.


표4. 일부 다른 adb 쉘 명령들.

 Shell Command

 Description  Comments

 dumpsys

 스크린에 시스템 데이터를 덤프.

 Dalvik Debug Monitor Server( DDMS ) 도구는 당신이 사용하기에 더 쉬운 통합된 디버깅 환경을 제공한다.

 dumpstate  파일의 상태를 덤프.
 logcat [options]... [filter-spec]...  시스템 및 앱 로깅을 이용 가능하게 하고, 결과를 스크린에 출력.
 dmesg  커널 디버깅 메시지를 스크린에 출력.
 start

 에뮬레이터/장치 인스턴스를 시작( 재시작 ).

 
 stop  에뮬레이터/장치 인스턴스의 실행을 종료.  


Enabling locat logging



안드로이드 로깅 시스템은 시스템 디버그 출력을 수집하고 보기 위한 메커니즘을 제공한다. 다양한 응용프로그램 및 시스템의 일부로부터의 로그들은 일련의 circular buffer 들 안에 수집되는데, 그것들은 logcat 명령에 의해 보여지고 필터링될 수 있다.


당신은 logcat 명령을 사용해 시스템의 로그 버퍼들의 내용을 보고 따라갈 수 있다. 범용적인 용례는 다음과 같다 :



당신은 logcat 명령을 개발 컴퓨터나 에뮬레이터/장치 인스턴스의 remote adb 쉘에서 사용할 수 있다. 개발 컴퓨터에서 로그 출력을 보려면, 다음과 같이 한다 :



그리고 remote adb 쉘에서 로그 출력을 보려면 다음과 같이 한다 :



logcat 명령 옵션들과 filter 명세들에 대한 완전한 정보를 원한다면 Reading and Writing Logs 를 참조하라.


Stopping the adb server



어떤 경우에, 당신은 adb 서버 프로세스를 종료시키고, 그것을 다시 시작하기 원할 것이다. 예를 들어 adb 가 명령에 응답하지 않는다면, 당신은 그 서버를 종료시키고 재시작할 수 있으며, 그것은 그 문제를 해결할 것이다.


adb 서버를 중지하고자 한다면, kill-server 명령을 사용하라. 그리고 나서 당신은 다른 adb 명령을 제출함으로써 그 서버를 재시작할 수 있다.


원문 : http://developer.android.com/tools/publishing/app-signing.html


주의 : 번역이 개판이므로 원문을 참조하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

주의 : 가독성을 높이기 위해서 잘 알려진 용어나 발음이 비슷한 용어는 한글로 표기합니다.


Signing Your Applications( 응용프로그램 서명하기 )


안드로이드 시스템은 모든 설치된 응용프로그램들이 응용프로그램의 개발자에 의해 설정된held 개인 키private key 를 가진 인증서certificate 를 사용해 디지털 방식으로 서명될 것을 요구한다. 안드로이드 시스템은 그 인증서를 응용프로그램의 저작자를 식별하고 응용프로그램 사이에서 신뢰 관계를 설정하는데 사용한다. 그 인증서는 사용자가 설치할 수 있는 응용프로그램이 무엇인지를 제어하는데 사용되지는 않는다. 그 인증서는 인증 기관certificate authority에 의해서 서명될 필요는 없다: 안드로이드 응용프로그램을 위해서 자체 서명 인증서를 사용하는 것은 일반적이며 완전히 허용되어 있다.


안드로이드 응용프로그램에 대한 서명을 이해하기 위해서 몇 가지 중요한 점이 있다 :


  • 모든 응용프로그램은 반드시 서명되어야만 한다. 시스템은 서명되지 않은 응용프로그램을 에뮬레이터나 디바이스 상에 설치하지 않을 것이다.
  • 응용프로그램을 디버깅하기 위해서, 빌드 툴들은 당신의 응용프로그램을 특별한 디버그 키를 사용해서 서명하는데, 그것은 안드로이드 SDK 빌드 툴에 의해서 생성된다.
  • 당신의 응용프로그램을 최종 사용자들에게 릴리스할 준비가 되었을 때, 당신은 그것을 적절한 개인 키를 사용해 서명해야만 한다. 당신은 SDK 툴들에 의해서 생성된 디버그 키를 사용해 서명된 응용프로그램을 배포할 수 없다.
  • 당신의 응용프로그램을 서명하기 위해서 자체 서명된 인증서를 사용할 수 있다. 인증 기관은 필요치 않다.
  • 시스템은 서명자의 인증서의 유효 기간을 설치시에만 검사한다. 만약 응용프로그램의 서명자 인증서가 응용프로그램이 설치된 후에 만료되었다면, 응용프로그램은 정상적으로 계속 기능할 것이다.
  • 당신은 표준 툴 - Keytool 과 Jarsigner - 를 사용해서 키를 생성하고 응용프로그램 .apk 파일들을 서명할 수 있다.
  • 릴리스를 위해서 당신의 응용프로그램을 서명한 후에, 당신이 zipalign 툴을 사용해 최종 APK 패키지를 최적화할 것을 추천한다.


안드로이드 시스템은 적절히 서명되지 않은 응용프로그램을 설치하거나 실행하지 않을 것이다. 이는 안드로이드 시스템이 실제 디바이스에서 실행되든 에뮬레이터에서 실행되든 상관없이 적용된다. 그렇기 때문에, 당신은 반드시 에뮬레이터나 디바이스 상에서 그것을 실행하거나 디버깅하기 전에 응용프로그램에 대한 서명을 설정해야만 한다.


Signing Process( 서명 절차 )



안드로이드 빌드 절차는 응용프로그램을 빌드하기 위해서 사용하는 빌드 모드에 의존해 다른 서명을 한다. 두 가지 빌드 모드가 있다: 디버그 모드와 릴리스 모드. 당신은 응용프로그램을 개발하고 테스트하고 있는 동안에 디버그 모드를 사용한다. 당신은 사용자에게 직접적으로 배포할 수 있거나 Google Play 와 같은 응용프로그램 마켓에서 배포할 수 있는 릴리스 버전의 응용프로그램을 빌드하고자 할 때 릴리스 모드를 사용한다. 


디버그 모드에서 빌드할 때, 안드로이드 SDK 빌드 툴들은 ( JDK 에 포함된 ) Keytool 유틸리티를 사용하여 디버그 키를 생성한다. SDK 빌드 툴들은 디버그 키를 생성하기 때문에, 그것들은 디버그 키의 별명alias과 비밀번호를 알고 있다. 당신이 디버그 모드로 응용프로그램을 컴파일할 때마다, 빌드 툴들은 ( 역시 JDK 에 포함된 ) Jarsigner 유틸리티와 디버그 키를 사용해서 당신의 응용프로그램의 .apk 파일을 서명한다. 그 별명과 비밀번호가 SDK 빌드 툴들에 알려져 있기 때문에, 그 툴들은 당신에게 디버그 키의 별명과 패스워드를 컴파일할 때마다 묻는 작업을 할 필요가 없다.


당신이 릴리스 모드로 빌드할 때, 당신은 자신만의 개인 키를 사용해 당신의 응용프로그램을 서명한다. 만약 당신이 개인 키를 가지고 있지 않다면, 당신은 Keytool 유틸리티를 사용해서 키를 생성할 수 있다. 응용프로그램이 릴리스 모드에서 컴파일될 때, 빌드 툴들은 당신의 개인 키를 Jarsigner 유틸리티와 함께 사용해 당신의 응용프로그램의 .apk 파일을 서명한다. 당신이 사용하는 인증서와 개인키는 당신의 것이기 때문에, 당신은 keystore 와 key alias 를 위한 비밀번호를 제공해야만 한다.


당신이 Eclipse 를 ADT 플러그인과 함께 사용해 응용프로그램을 실행하거나 디버깅하고 있을 때는, 디버그 서명 절차가 자동적으로 수행된다. Ant 빌드 스크립트를 debug 옵션과 함께 사용할 때도, 디버그 서명은 자동적으로 수행된다. 당신은 Eclipse Export Wizard 를 사용하거나 Ant 빌드 스크립트를 수정하고 release 옵션으로 빌드함으로써 릴리스 서명 절차를 자동화할 수 있다.


Signing Strategies( 서명 전략들 )



어떤 관점에서의 응용프로그램 서명은 아마도 당신의 응용프로그램 개발에 접근하는 방식에 영향을 줄 수도 있다. 특히 당신이 여러 개의 응용프로그램을 릴리스할 계획을 가지고 있다면 더욱 그러하다.


일반적으로 모든 개발자들을 위해서 추천되는 전략은 당신의 응용프로그램의 기대 수명 전반에 있어서 같은 인증서를 사용해서 서명하는 것이다. 당신이 그렇게 해야 하는 몇 가지 이유가 있다 :


  • 응용프로그램 업그레이드 - 당신이 응용프로그램에 업데이트를 릴리스할 때, 만약 사용자가 새로운 업데이트를 끊김없이 사용하기를 원한다면, 당신은 계속해서 같은 인증서나 같은 인증서 집합을 사용해 업데이트에 서명하는 것을 원할 것이다. 시스템이 응용프로그램에 대한 업데이트를 설치할 때, 그것은 현존하는 버전의 인증서와 새로운 버전의 인증서를 비교한다. 만약 인증서 데이터와 순서가 정확하게 맞으면, 시스템은 업데이트를 허용한다. 만약 당신이   일치하는 인증서들을 사용하지 않고 새로운 버전을 서명하게 되면, 당신은 응용프로그램에 대해 새로운 패키지 이름을 할당할 필요가 있을 것이다 - 이 경우에, 사용자는 완전히 새로운 응용프로그램으로서 새로운 버전을 설치한다.
  • 응용프로그램 모듈화 - 안드로이드 시스템은 같은 인증서에 의해 서명된 응용프로그램들이 같은 프로세스에서 실행될 것을 요청받으면 그것을 허용한다. 그래서 시스템은 그것들을 단일 응용프로그램으로 취급한다. 이러한 방식으로 당신은 응용프로그램을 모듈 단위로 배치할 수 있으며, 사용자들은 필요하다면 각 모듈을 독립적으로 업데이트할 수 있다.
  • 권한을 통한 코드/데이터 공유 - 안드로이드 시스템은 서명 기반 승인 집행signature-based permissions enforcement을 제공한다. 그래서 응용프로그램은 특정 인증서를 사용해 서명된 다른 응용프로그램에 기능을 노출할 수 있다. 여러 개의 응용프로그램을 같은 인증서를 사용해 서명하고 서명 기반 승인 검사를 이용하면, 당신의 응용프로그램은 안전하게 코드와 데이터를 공유할 수 있다.


서명 전략을 결정하는 데 있어서 다른 중요하게 고려해야할 점은 당신의 응용프로그램을 서명하기 위해서 사용할 키의 유효 기한을 설정하는 방법이다.


만약 당신이 단일 응용프로그램을 위한 업그레이드를 지원할 계획을 가지고 있다면, 당신은 당신의 키가 그 응용프로그램의 기대 수명을 초과하는 유효 기한을 가지도록 보장해야만 한다. 25 년 이상의 유효 기한이 추천된다. 당신의 키의 유효 기한이 만료되면, 사용자들은 더 이상 계속해서 새로운 버전의 응용프로그램에 대한 업그레이드를 할 수 없을 것이다.

만약 당신이 여러 개의 분리된 응용프로그램들을 같은 키를 사용해서 서명한다면, 당신은 당신의 키가 모든 응용프로그램의 모든 버전에 대한 기대 수명을 초과하는 유효기한을 가지도록 보장해야만 한다. 이 때 그 응용프로그램들에는 미래에 추가할 수도 있는 의존성있는 응용프로그램들이 포함된다.

만약 당신이 응용프로그램을 Google Play 에서 배포할 계획을 가지고 있다면, 당신이 응용프로그램을 서명하는데 사용한 키는 반드시 2033 년 10 월 22 일 이후에 종료되는 유효 기한을 가져야만 한다. Google Play 는, 새로운 버전이 이용 가능할 때 사용자들이 계속해서 응용프로그램을 업그레이드할 수 있도록 보장하기 위해서, 이 요구사항을 강제한다.


당신의 응용프로그램을 설계할 때, 이러한 점을 유념하고 응용프로그램을 서명하기 위한 적절한 인증서를 확인하기 바란다.


Basic Setup for Signing( 서명을 위한 기본 설정 )



시작하기 전에, Keytool 유틸리티와 Jarsigner 유틸리티가 SDK 빌드 툴들에 대해 이용 가능한지를 확인하라. 두 툴은 모두 JDK 에서 이용할 수 있다. 대부분의 경우에, 당신은 JAVA_HOME 환경 변수를 설정함으로써 이러한 유틸리티들을 찾는 방법을 SDK 빌드 툴들에 알려줄 수 있다. 이를 통해 빌드 툴들은 적절한 JDK 를 참조하게 된다. 그렇지 않으면, 당신은 JDK 버전의 Keytool 과 Jarsigner 을 당신의 PATH 변수에다가 추가할 수 있다.


만약 Java 를 위한 GNU 컴파일러를 원래부터 포함하는 버전의 리눅스 상에서 개발중이라면, 시스템이 JDK 버전의 Keytool 을 사용하고 있는지 확인하라. 만약 Keytool 이 이미 PATH 에 존재한다면, 그것은 /usr/bin/keytool 의 symlink[각주:1] 를 가리키고 있을 것이다. 이 경우, symlink 대상을 확인해 그것이 JDK 의 Keytool 을 가리키는지를 확인하라.


Signing in Debug Mode( 디버그 모드에서 서명하기 )



안드로이드 빌드 툴들은 응용프로그램을 더 쉽게 개발하고 디버깅하게 하기 위해서 디버그 서명 모드를 제공한다. 그러면서도 여전히 APK 를 서명하기 위한 안드로이드 시스템 요구사항을 만족시킨다. 응용프로그램을 빌드하기 위해서 디버그 모드를 사용할 때, SDK 툴들은 Keytool 을 실행해서 자동적으로 디버그 키스토어와 키를 생성한다. 그리고 나서 이 디버그 키는 자동적으로 APK 를 서명하기 위해 사용되며, 그래서 당신은 자신의 키를 사용해 패키지를 서명할 필요가 없다.


SDK 툴들은 디버그 키스토어/키 를 미리 결정된 이름/비밀번호 를 사용해 생성한다.


  • 키스토어 이름 : "debug.keystore"
  • 키스토어 비밀번호 : "android"
  • 키 별명 : "androiddebugkey"
  • 키 비밀번호 : "android"
  • CN: "CN=Android Debug,0=Android,C=US"


필요하다면 디버그 키스토어/키 의 위치/이름 을 변경하거나 커스텀 디버그 키스토어/키 를 공급할 수 있다. 그러나 모든 커스텀 디버그 키스토어/키 는 반드시 ( 위에 언급된 것처럼 ) 기본 디버그 키와 같은 키스토어/키 이름과 비밀번호를 가져야 한다. ( 그렇게 하기 위해서는 Eclipse/ADT 에서, Windows > Preferences > Android > Build 로 이동하라. )


주의 : 당신은 디버그 인증으로 서명된 응용프로그램을 대중에게 릴리스할 수 없다.


Eclipse Users


당신이 Eclipse/ADT 에서 개발하고 있다면 ( 그리고 Basic Setup for Signing 에서 기술된 것처럼 Keytool 과 Jarsigner 를 설정했다면 ), 디버그 모드에서의 서명은 기본적으로 활성화된다. 당신이 응용프로그램을 실행하거나 디버그할 때, ADT 는 .apk 파일을 디버그 인증서를 사용해 서명하고, 패키지 상에 zipalign 을 실행하고, 그 다음에 선택된 에뮬레이터나 연결된 디바이스 상에 그것을 설치한다. 당신이 해야할 특별한 일은 없으며, 제공된 ADT 가 Keytool 에 대한 접근권을 가진다.


Ant Users


만약 .apk 파일을 빌드하기 위해 Ant 를 사용하고 있다면, ant 명령을 debug 옵션과 함께 사용함으로써 디버그 서명 모드가 활성화된다( 당신이 android 툴을 사용해서 생성한 build.xml 파일을 사용하고 있다고 가정한다 ). 당신이 and debug 를 실행해서 응용프로그램을 컴파일할 때, 빌드 스크립트는 키스토어/키 를 생성하며, 당신을 위해서 APK 에 서명한다. 그리고 나서 스크립트는 API 를 zipalign 툴을 사용해 정렬한다. 당신이 해야 할 일은 없다. 더 많은 정보를 원한다면 Building and Running Apps on the Command Line 을 읽어 보라.


Expiry of the Debug Certificate( 디버그 인증서 만료 )


디버그 모드에서 ( Eclipse/ADT 와 Ant 빌드의 기본 설정 ) 응용프로그램을 서명하기 위해서 사용된 자체 서명 인증서는 그것의 생성일로부터 1 년의 만료일을 가진다.


인증서가 만료되면, 당신은 빌드 에러를 만나게 될 것이다. Ant 빌드에서 에러는 다음과 같다 :



Eclipse/ADT 에서, 당신은 안드로이드 콘솔에서 비슷한 에러를 볼 수 있다.


이 문제를 해결하기 위해서는 단순하게 debug.keystore 파일을 제거하기만 하면 된다. AVD 들을 위한 기본 저장소 위치는 OS X 와 Linux 에서는 ~/.android/ 이고, Windows XP 에서는 C:\Documents and Settings\<user>\.android\ 이고, Windows Vista 와 Windows 7 에서는 C:\Users\<user>\.android\ 이다.


다음에 빌드를 하면, 빌드 툴들은 새로운 키스토어와 디버그 키를 생성할 것이다.


당신의 개발 머신이 non-Gregorian locale 을 사용하고 있다면, 빌드 툴들은 아마도 이미 만료된 디버그 인증서를 생성할지도 모른다. 그래서 당신은 응용프로그램을 컴파일하려고 시도할 때마다 에러를 만나게 될 것이다. 이를 우회하기 위한 정보는 문제 해결 토픽인 I can't compile my app because the build tools generated an expired debug certificate 를 참조하라.


Signing in Release Mode( 릴리스 모드에서 서명하기 )



당신의 응용프로그램을 다른 사용자에게 릴리스하기 위한 준비가 되었다면, 당신은 반드시 다음과 같은 일을 수행해야만 한다 :


  1. 적절한 개인 키 획득하라.
  2. 응용프로그램을 릴리스 모드로 컴파일하라.
  3. 당신의 개인 키를 사용해 응용프로그램에 서명하라.
  4. 최종 APK 패키지를 정렬하라.


만약 ADT 플러그인을 사용해 Eclipse 에서 개발중이라면, 컴파일, 서명, 정렬 프로시저를 수행하기 위해서 Export Wizard 를 사용할 수 있다. Export Wizard 는 심지어 당신이 새로운 키스토어와 개인 키를 그 과정에서 생성할 수 있도록 허용한다. 그래서 당신이 Eclipse 를 사용한다면, Compile and sign with Eclipse ADT 는 건너 뛰어도 된다.


1. Obtain a suitable private key( 적절한 개인 키 획득하기 )


응용프로그램에 서명을 하기 위한 준비에서, 당신은 먼저 서명할 적절한 개인키를 가지고 있다는 것을 확신해야만 한다. 적절한 개인 키는 다음과 같은 것이다 :


  • Is in your procession.
  • 응용프로그램과 동일시되기 위한 개인, 회사, 조직을 표현한다.
  • 응용프로그램이나 응용프로그램 모음suite의 기대 수명을 초과하는 유효 기한을 가진다. 25 년 이상의 유효 기한을 추천한다.
    만약 당신이 응용프로그램을 Google Play 에서 배포할 계획이라면, 2033 년 10 월 22 일 이후에 끝나는 유효기간이 요구된다. 당신은 유효 기한이 그 날짜 이전에 만료되는 키를 가지고 서명된 응용프로그램을 올릴 수 없다.
  • 안드로이드 SDK 툴들에 의해 생성된 디버그 키가 아니다.


그 키는 아마 자체 서명될 것이다. 만약 당신이 적절한 키를 가지고 있지 않다면, 반드시 Keytool 을 사용해서 생성해야만 한다. Basic Setup 에서 기술된 것처럼, 당신이 가진 Keytool 이 이용 가능한지 확인하라.


자체 서명된 키를 Keytool 을 사용해 생성하기 위해서는, keytool 명령을 사용하는데, 아래에 열거된 옵션들을 넘긴다( 필요하면 다른 것을 넘길 수 있다 ).


경고 : 개인키를 안전하게 보관하라. Keytool 을 실행하기 전에, 당신의 키를 안전하게 보관하는 방법과 그것이 당신과 사용자들에게 매우 중요한 이유에 대한 논의를 위해  Securing Your Private Key 를 반드시 읽어라. 특히, 당신이 키를 생성하고 있을 때, 당신은 키스토어와 키 모두를 위해 강한 비밀번호strong password를 선택해야만 한다.


 Keytool Option

 Description

 -genkey

 키 쌍을 생성한다( 공용 키, 개인 키 ) 
 -v

 verbose 출력을 활성화한다.

 -alias <alias_name>  키에 대한 별명. 8 개의 문자만이 사용된다. 
 -keysize <size>

 각각의 생성된 키의 크기( bits ). Keytool 은 1024 bits 의 기본 키 크기를 사용한다. 일반적으로 우리는 2048 bits 이상을 사용하는 것을 추천한다.

 -dname <name >

 누가 키를 생성했는지를 기술하는 구별 가능한 이름. 이 값은 자체 서명 인증서에서 발행인issuer과 제목 필드로 사용된다.

 커맨드 라인에서 이 옵션을 지정할 필요는 없다. 만약 제공되지 않으면, Jarsigner 는 당신에게 구별 가능한 이름 필드들( CN, OU 등 )을 입력하기 위해 당신에게 묻는다.

 -keypass <password>

 키에 대한 비밀번호.

 보안 예방책으로, 커맨드 라인에서 이 옵션을 포함하지 말라. 만약 제공되지 않으면, Keytool 이 비밀번호를 입력하기 위해 당신에게 묻는다. 이러한 방식으로, 당신의 비밀번호가 당신의 쉘 히스토리에 저장되지 않게 된다.

 -validity <valdays>

 키를 위한 일단위 유효 기한.

 주의 : 10000 이상의 값을 추천한다.

 -keystore <keystorename>.keystore

 개인 키를 포함하는 키스토어를 위한 이름.
 -storepass <password>

 키스토어를 위한 비밀번호.

 보안 예방책으로, 커맨드 라인에서 이 옵션을 포함하지 말라. 만약 제공되지 않는다면, Keytool 이 비밀번호를 입력하기 위해 당신에게 묻는다. 이러한 방식으로, 당신의 비밀번호가 쉘 히스토리에 저장되지 않게 된다.

 -keyalg <alg>

 키를 생성할 때 사용할 암호화 알고리즘. DSA 와 RSA 가 지원된다.


아래에 개인 키를 생성하는 keytool 명령의 예가 나와 있다 :



위의 예제 명령을 실행하면, keytool 은 키스토어와 키에 대한 비밀번호를 제공하고 키를 위한 구별 가능한 이름 필드들을 제공하기 위해 당신에게 묻는다. 그리고 나서 그것은 my-release-key.keystore 라는 파일로 키스토어를 생성한다. 그 키스토어와 키는 당신이 입력한 비밀번호로 보호된다. 그 키스토어는 단일 키와 10000 일 동안의 유효 기한을 포함한다. 별명은 당신이 나중에 응용프로그램을 서명할 때 이 키스토어를 참조하기 위해서 사용하게 될 이름이다.


Keytool 에 대한 더 많은 정보를 원한다면, http://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html 에 있는 문서를 참조하라.


2. Compile the application in release mode( 릴리스 모드에서 응용프로그램 컴파일하기 )


사용자에게 응용프로그램을 릴리스하기 위해서, 당신은 그것을 릴리스 모드로 컴파일해야만 한다. 릴리스 모드에서, 컴파일된 응용프로그램은 기본적으로 서명되지 않으며, 당신은 개인 키를 사용해 그것을 서명할 필요가 있을 것이다.


주의 : 당신은 서명되지 않은 응용프로그램이나 디버그 키로 서명된 응용프로그램을 릴리스할 수 없다.


With Eclipse


서명되지 않은 APK 를 Eclipse 에서 익스포트하기 위해서는, Package Explorer 에서 프로젝트를 오른쪽 클릭하고, Android Tools > Export Unsigned Application Package 를 선택하라. 그리고 나서 서명되지 않은 APK 를 위한 파일 위치를 지정하라. ( 그렇지 않으면, AndroidManifest.xml 파일을 Eclipse 에서 열어서, Manifest 탭을 선택하고, Export an unsigned APK 를 클릭하라. )


당신은 Export Wizard 를 사용해서 컴파일과 서명을 합칠 수 있다. Compiling and signing with Eclipse ADT 를 참조하라.


With Ant


당신이 Ant 를 사용하고 있다면, ant 명령을 release 옵션과 함께 사용해 릴리스 모드를 활성화할 수 있다. 예를 들어, 만약 당신이 build.xml 을 포함하고 있는 디렉토리에서 Ant 를 실행하고 있다면, 그 명령은 다음과 같을 것이다 :



기본적으로, 빌드 스크립트는 응용프로그램 APK 를 서명하지 않은 채로 컴파일한다. 프로젝트의 bin/ 에서의 출력 파일은 <your_project_name>-unsigned.apk 일 것이다. 응용프로그램 APK 는 여전히 서명되지 않았기 때문에, 당신은 반드시 수동적으로 개인 키를 사용해 서명하고 zipalign 을 사용해 정렬해야만 한다.


그런데 당신이 키스토어와 키 별명의 이름에 대한 경로를 프로젝트의 ant.properties 파일에다가 제공해 놓았다면, Ant 빌드 스크립트는 서명과 정렬을 수행할 수 있다. 이 정보를 제공하면, 빌드 스크립트는 ant release 를 수행할 때 당신의 키스토어와 별명 비밀번호를 입력받는 창을 띄운다. 그것은 패키지에 서명을 하고 그것을 정렬할 것이다. bin/ 의 최종 출력 파일은 <your_project_name>-release.apk 가 될 것이다. 이 단계들이 자동화되면, 당신은 3 과 5 의 수동 프로시저를 건너 뛸 수 있다. ant.properties 파일에 키스토어와 별명을 지정하는 방법에 대해서 배우고 싶다면, Building and Running Apps on the Command Line 을 참조하라.


3. Sign your application with your private key( 개인 키로 응용프로그램 서명하기 )


서명될 준비가 된 응용프로그램 패키지를 가지고 있을 때, 당신은 Jarsigner 툴을 사용해서 그것에 서명할 수 있다. Basic Setup 에서 설명했듯이, 당신의 머신에서 이용 가능한 Jarsigner 를 가지고 있는지 확인하라. 또한 개인키를 포함하는 키스토어가 이용 가능한지 확인하라.


응용프로그램에 서명하기 위해서, 당신은 Jarsigner 를 실행하는데, 응용프로그램의 APK 와 APK 를 서명하기 위한 개인 키를 포함하는 키스토어를 모두 참조하게 된다. 아래 표는 당신이 사용할 수 있는 옵션을 보여 준다.


 Jarsigner Option  Description 

 -keystore <keystore-name>.keystore

 개인 키를 포함하는 키스토어의 이름.
 -verbose

 verbose 출력을 가능하게 함.

 -sigalg  APK 를 서명하는데 사용하기 위한 서명 알고리즘의 이름. MD5withRSA 를 사용하라.
 -digestalg

 APK 의 엔트리들을 처리하는 과정에 사용하게 될 message digest algorithm. SHA1 을 사용하라.

 -storepass <password>

 키스토어를 위한 비밀번호.

 보안 예방책으로, 당신이 보안 컴퓨터에서 작업하고 있지 않은 한은 커맨드 라인에서 이 옵션을 포함하지 말라. 만약 제공되지 않으면, Jarsigner 가 비밀번호를 입력하기 위해 당신에게 묻는다. 이러한 방식으로, 당신의 비밀번호가 당신의 쉘 히스토리에 저장되지 않게 된다.

 -keypass <password>

 개인 키를 위한 비밀번호.

 보안 예방책으로, 당신이 보안 컴퓨터에서 작업하고 있지 않은 한은 커맨드 라인에서 이 옵션을 포함하지 말라. 만약 제공되지 않으면, Jarsigner 가 비밀번호를 입력하기 위해 당신에게 묻는다. 이러한 방식으로, 당신의 비밀번호가 당신의 쉘 히스토리에 저장되지 않게 된다.


아래에 위의 예제에서 생성된 키스토어를 사용해 my_application.apk 라는 응용프로그램 패키지를 서명하기 위해 Jarsigner 를 사용하는 방법이 있다.



위의 예제를 실행하면, Jarsigner 는 키스토어와 키에 대한 비밀번호를 제공하기 위해 당신에게 묻는다. 그리고 나서 현재의 APK 를 수정하며, 그것은 APK 가 이제 서명되었음을 의미한다. 다른 키를 사용해 여러 번 APK 에 서명할 수 있다는 것에 주목하라.


주의 : JDK 7 부터, 기본 서명 알고리즘이 변경되었는데, APK 에 서명을 할 때 당신이 서명 알고리즘과 다이제스트 알고리즘을( -sigalg 와 -digestalg ) 지정하도록 요구하고 있다.


서명된 APK 를 검증하기 위해서, 당신은 다음과 같은 명령을 사용할 수 있다 :



만약 APK 가 적절하게 서명되었다면, Jarsigner 는 "jar verified" 를 출력한다. 만약 좀 더 세부적인 것을 우너한다면, 다음 명령들을 시도해 볼 수 있다 :



혹은



위에서 -certs 옵션을 가진 명령은 누가 키를 생성했는지를 기술하는 "CN=" 라인을 보여 줄 것이다.


주의 : 만약 당신이 "CN=Android Debug" 라는 것을 보게 된다면, 이것은 APK 가 안드로이드 SDK 에 의해 생성된 디버그 키로 서명되었음을 의미한다. 만약 응용프로그램을 릴리스하려는 의도라면, 당신은 반드시 디버그 키 대신에 개인 키를 사용해서 서명해야만 한다.


Jarsigner 에 대한 더 많은 정보를 원한다면, http://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html 의 문서를 참조하라.


4. Align the final APK package( 최종 APK 패키지 정렬하기 )


개인키로 서명된 APK 를 확보했으면, zipalign 을 파일에 대해 실행하라. 이 툴은 모든 비압축 데이터가 파일의 시작부분에 대해 상대적인 특별한 바이트에 정렬되는 것을 보장한다. 4 바이트 경계에서 정렬을 보장하는 것은 디바이스 상에서 설치될 때 성능 최적화를 제공한다. 정렬되어 있다면, 심지어 그것들이 정렬 제약을 가진 이진 데이터를 포함하더라도, 안드로이드 시스템은 모든 데이터를 패키지로부터 복사하는 대신에 파일을 mmap() 을 사용해서 읽어들일 수 있다. 이것의 효과는 응용프로그램을 실행함으로써 소비되는 RAM 의 양을 줄이는 것이다.


zipalign 툴은 안드로이드 SDK 와 함께 제공되며, tools/ 디렉토리 내부에 있다. 서명된 APK 를 정렬하려면, 다음을 실행하라 :



-v 플래그는 ( 선택적인 ) verbose 출력을 켠다. 4 는 바이트 정렬( 4 아닌 다른 값을 사용하지 말라 )이다. 첫 번째 인자는 당신의 서명된 .apk 파일( 입력 )이며, 두 번째 파일은 대상 .apk 파일( 출력 )이다. 만약 당신이 현존하는 APK 를 override 했다면, -f 플래그를 추가하라.


주의 : 당신의 입력 APK 는 zipalign 을 사용해 패키지를 최적화하기 전에 반드시 개인 키로 서명되어 있어야만 한다. 만약 zipalign 을 사용한 후에 서명을 하면, 그것은 정렬을 취소할 것이다.


더 많은 정보를 원한다면, zipalign 툴에 대해 읽어 보라.


Compile and sign with Eclipse ADT( Eclipse ADT 를 사용해 컴파일하고 서명하기 )


만약 Eclipse 를 ADT 플러그인과 함께 사용하고 있다면, Export Wizard 를 사용해서 서명된 APK 를 익스포트할 수 있다( 그리고 필요하면 키스토어도 새로 생성할 수 있다 ). Export Wizard 는 Keytool 과 Jarsigner 와의 모든 상호작용을 당신을 위해 해 준다. 이는 위에서 언급했던 컴파일, 서명, 정렬 등을 위한 수동 프로시저들을 수행하는 대신에 GUI 를 사용해 패키지에 서명할 수 있도록 해 준다. 일단 마법사가 당신의 패키지를 컴파일하고 서명하면, 그것은 zipalign 을 사용해 패키지 정렬도 수행한다. Export Wizard 는 Keytool 과 Jarsigner 를 모두 사용하기 때문에, 당신은 당신의 컴퓨터에서 그것에 접근할 수 있다는 것만 보장하면 된다; Basic Setup for Signing 에서 언급했듯이...


Eclipse 에서 서명된 APK 를 생성하기 위해서는 :


  1. Package Explorer 에서 프로젝트를 선택하고 File > Export 를 선택한다.
  2. Android 폴더를 열고, Export Android Application 을 선택하고, Next 를 클릭한다.
    Export Android Application 마법사가 시작되는데, 그것은 응용프로그램을 서명하는 절차로 안내해 줄 것이고, APK 를 서명하기 위한 개인 키를 선택하기 위한 단계도 포함할 것이다( 혹은 새로운 키스토어와 개인키를 생성할 수도 있다 ).
  3. Export Wizard 를 완료하면, 당신의 응용프로그램이 컴파일되고, 서명되고, 배포 준비가 될 것이다.


Securing Your Private Key( 개인키 보호하기 )



개인 키의 보안을 유지하는 것은 당신이나 사용자에게 있어서 매우 중요하다. 만약 누군가가 당신의 키를 사용하도록 허용하거나, 당신이 키스토어와 비밀번호를 서드 파티가 찾고 이용할 수 있는 보호되지 않은 위치에다가 남겨 두게 되면, 당신의 저작관과 사용자에 대한 신뢰가 위태롭게 된다.


서드 파티가 당신의 인지나 승인없이 당신의 키를 취할 수 있다면, 그 사람은 당신의 원본 응용프로그램들을 악의적으로 대체하고 그것들을 변질시키는 응용프로그램들을 서명하거나 배포할 수 있다. 또한 그러한 사람은 당신의 신분을 이용해 다른 응용프로그램이나 시스템 자체를 공격하거나 사용자의 데이터를 변질시키거나 훔치는 응용프로그램들을 서명하거나 배포할 수도 있다.


당신의 개발자로서의 평판은 그 키가 만료될 때까지 항상 적절하게 개인 키를 보호하느냐에 달려 있다. 여기 키 보안을 지키기 위한 몇 가지 팁들이 있다 :


  • 키스토어와 키에 대해서 강한 비밀번호를 선택하라.
  • Keytool 을 사용해 키를 생성할 때, -storepass 와 -keypass 옵션을 커맨드라인에서 제공하지 말라. 만약 그렇게 한다면, 당신의 비밀번호들이 쉘 히스토리에서 이용 가능하며, 그것은 당신의 컴퓨터에 로그인한 사용자라면 모두 접근할 수 있다.
  • 비슷하게, 응용프로그램에 Jarsigner 를 사용해서 서명할 때, -storepass 와 -keypass 옵션을 커맨드라인에서 제공하지 말라.
  • 누구에게도 개인 키를 주거나 빌려 주지 말라. 그리고 권한을 가지지 않은 사람들이 당신의 키스토어와 키의 비밀번호를 알 수 있도록 하지 말라.


일반적으로, 당신이 키를 생성하고 사용하고 저장할 때, 일반적인 예방책을 따른다면, 그것은 보호된 상태로 남아 있을 수 있을 것이다.


  1. 역주 : symbolic link [본문으로]

책의 71 페이지에서는 다음과 같은 명령어를 사용해서 APK 를 만들라고 하고 있다.



현재 1.8.4 버전의 ant 를 설치했는데, 이것은 구체적으로 빌드 타겟을 지정할 것을 요구한다.




그러므로 위에서 설명하고 있는 것처럼 아래와 같은 형식으로 호출해야 한다.



책의 70 페이지에서는 다음과 같이 project 를 설정하라고 이야기하고 있다.



필자는 윈도우 환경이기 때문에 cygwin 에서 다음과 같이 실행했다.



error 를 보면 알 수 있듯이 "-p" 옵션은 프로젝트가 있는 디렉토리의 경로를 지정할 것을 요구한다.


그러므로 현재 경로인 "./" 을 넣어서 해결했다.



책의 35 페이지에서는 ".bash_profile" 이라는 파일에 다음과 같은 식으로 스크립트를 추가할 것을 요구하고 있다.



환경변수를 가지고 올 때 MS_DOS 형식으로 인식하는 것을 막기 위함이라고 하는데, 저자가 설치한 것이 어떤 버전인지는 모르겠지만, 현재 설치한 버전(  1.7.17-1 )에서는 그런 문제가 자동으로 해결되어 있는 것 같다.


실제로 저런 스크립트를 추가하게 되면 오히려 제대로 동작하지 않는다.

원문 : How Do Those Funky Placeholders Work?


주의 : 허락받고 번역한 것이 아니므로 언제든지 내려갈 수 있습니다.

주의 : 번역이 개판이므로 이상하면 원문을 참조하세요.

주의 : 가독성을 높이기 위해서 발음이 비슷하거나 잘 알려진 용어는 한글로 표기합니다.


MPL 을 하다가 보면 가장 이해가 안 되는 것이 자리표 표현식( placeholder expression )이다. 아주 간단한 형태의 자리표 표현식같은 경우에는 그나마 이해가 가지만, 조금만 복잡해져도 정신이 안드로메다로 가 버린다.


"C++ Template Metaprogramming" 의 내용 중에 다음과 같은 코드가 있다. 이해가 가는가? 



나름 자리표 표현식에 대해서 이해했다고 생각했었는데, 필자도 쉽게 이해할 수가 없었다. 그래서 공부도 하는 겸해서 자리표 표현식과 관련한 글을 번역해 보기로 했다.




How Do Those Funky Placeholders Work?


요즘 C++ 표준 함수 binder 들은 정말로 사용하기 어렵다. Chris Gibson 은 Boost 의 대안의 비밀을 보여 준다. 그것은 더욱 낫고 마법같다.


새로운 코딩 기법에 대해서 배우고 그것을 작업하면서 사용하려고 할 때마다, 부서 전반에서 그것이 얼마나 빠르게 받아들여지는지 확인하는 것은 흥미롭다. 널리 전파되는 가장 빠른 것 중의 하나는 boost bind 라이브러리이다. 표준 파이브러리의 bind1st 나 bind2nd 적응자( adapter )들보다 훨씬 사용하기 수비고 훨씬 유연하다. 일단 재미있게 보이는 자리표( placeholder )를 이해하게 되면, bind 라이브러리는 사용하기에 우아하고 단순하다. 그리고 초기의 노력이 보상받게 된다. 당신의 코드는 깔끔해지며, 이해하기 쉬워지며, 빠르게 검토하기 쉬워지며, 빠르게 테스트할 수 있게 된다. 그것은 버그를 포함할 가능성이 더 적으며 아마도 더욱 효율적일 것이다.


내 경험상 평균적인 개발자들은, 물론 당신을 말하는 것은 아니고 나는 확실히 아니다, bind 라이브러리를 처음 사용할 때 아마도 너무 많거나 너무 적거나 잘못된 자리표를 사용할 것이다; 그리고 적어도 한 군데는 잘못된 위치에 그것들을 배치할 것이다. 이런 것들은 여러 페이지의 컴파일 에러들을 발생시킬 것이다. 그리고 나서 평균적인 개발자들은 그것이 너무 어렵다고 생각해서 템플릿은 악마의 조화라고 판단할 것이다. 그리고 그들은 매뉴얼을 읽는데 시간을 소비하지 않고, 그저 그것이 작동하기만을 원할 것이다. So they hand code a solution instead and decide to do it properly before review or release, which in all probability never happens.


그러므로 나는 bind 라이브러리가 직관적인지 그렇지 않은지 판단할 수 없다. 일단 이해하고 나면 직관적이다. but doesn't intuitive mean you don't need to get the hang of it? Can it be true that the more you use the bind library the more intuitive it gets?


일단 개발자들이 그것을 이해하고 나면 언제나 다음과 같이 질문한다. "그런데 그 멋진 자리표는 어떻게 작동하는 거죠?". 이상적인 세계에서는 당신은 라이브러리의 세부사항을 구현하는 것에 관심을 가지지 않을 것이며, 당신은 그냥 그것을 사용하면 될 것이다. 그러나 무대 뒤에서 무슨 일이 벌어지고 있는지 이해하는 것은 몇 가지 이유때문에 유용하다. 당신이 오타를 쳤을 때 평균적인 컴파일러가 제공하는 에러 메시지를 확인하면, 어떤 일들이 벌어지고 있는지 이해하는 데 큰 도움이 된다. 두 번째로 전문가에 의해 개발된 설계를 공부하는 것은 항상 유익하며 당신의 설계를 개선하는데 도움이 될 수 있다. 마지막으로 거의 대부분의 개발자들은 당연히 호기심이 많고, 자리표 구문을 마음에 들어 하면 보통 그것이 작동하는 방식에 대해 알기를 원한다.


이 기사에서는 bind 라이브러리의 자리표가 작동하는 방식에 대해 설명하려고 하는데, 매우 기본적인 bind 라이브러리를 만들 것이다. 그것은 그다지 대단하지 않을 것이지만, 당신의 궁금증을 만족시켜주기에는 충분할 것이라 생각한다. 예제들은 무엇보다도 명확성을 중시하며, 효율성, const 정확성, volatility 등과 같은 다른 이슈들은 고려되지 않는다. 함수 반환 형식들도 고려되지 않으며 void 라고 가정된다는 것에 주의하라.


A quick recap


당신이 widget 컬렉션을 가지고 있으며 당신은 각각에 대해 어떠한 동작을 수행하기를 원하고 있다고 가정하자; 당신은 단순히 다음과 같이 작성할 것이다 :



for_each 알고리즘은 단항 함수( unary function )를 세 번째 매개 변수로 취하는데, 그것은 범위 내의 각 반복자( iterator ) 당 한 번씩 호출되며, 참조해제된 반복자( 역주 : 반복자가 참조하고 있는 실제 값 )를 단항 함수의 단일 인자로 넘긴다( 아래를 보라 ). 우리의 대상 함수는 세 개의 매개 변수를 가지는데, 그것은 두 개의 문제를 가지고 있다 :


  • 단항 함수와 대상 함수의 매개 변수의 개수가 다르다.
  • 참조해제된 반복자는 대상 함수의 매개 변수 중의 어떤 것에라도 사상될 수 있다.



매개 변수의 개수가 다른 문제를 해결하는 것은 소프트웨어 공학의 근본 정리( Fundametal Theory of Software Engineering )을 적용하고 알고리즘과 대상 함수 간에 간접층( level of indirection )을 추가함으로써 수행된다. 간접층은 오브젝트의 형태를 취하며, 그것을 바인더( binder )라고 부르도록 하자. 바인더의 역할은 대상 함수의 인터페이스를 호출하는 알고리즘의 인터페이스를 받아들이는( adapt ) 것이다. 바인더는 for_each 에 의해 호출될 수 있다. 왜냐하면 그것은 단항 연산자 () 를 가지고 있기 때문이다. 그리고 바인더는 대상 함수를 호출할 수 있다. 왜냐하면 그것은 대상 함수에 대한 포인터와 두 개의 부가적인 인자를 생성자에서 공급받기 때문이다.


DoSomething 의 세 매개 변수들 중의 어떤 것이 widget 이어야 하는지 지정하는 것이 자리표가 오게 되는 곳이다. bind 라이브러리를 사용해서 우리는 이를 단순화할 수 있다 :



bind 함수는 네 개의 인자들을 받는다 : 대상 함수인 DoSomething 의 주소, Gadget 오브젝트, Gizmo 오브젝트, 자리표 _1. bind 에 대한 두 번째, 세 번째, 네 번째 인자의 순서는 대상 함수에 대한 인자의 순서와 같다는 데 주목하라. 자리표는 for_each 에 의해 단항 함수에 넘겨지는 인자의 위치를 지시한다. 만약 DoSomething 의 시그너쳐( signature )가 widget 을 두 번째 매개 변수로 기대했다면, 우리는 다음과 같이 작성해야할 것이다 :




















+ Recent posts