Android의 debugging module인 ADB에 대한 기본적인 소개와 command에 대해서 정리한 내용입니다. 일반적으로 Eclipse에서 UI 조작만을 통해 사용하곤 하지만 command line에서 control 할 경우 더 많은 기능들을 활용할 수 있습니다.
원문 : http://developer.android.com/guide/developing/tools/adb.html
Android의 debugging module인 ADB에 대한 기본적인 소개와 command에 대해서 정리한 내용입니다. 일반적으로 Eclipse에서 UI 조작만을 통해 사용하곤 하지만 command line에서 control 할 경우 더 많은 기능들을 활용할 수 있습니다.
원문 : http://developer.android.com/guide/developing/tools/adb.html
아래의 페이지를 번역해서 작성했습니다.(급하게...) 몇 문장 누락시키기도 하고 합쳐서 기술하기도 했습니다. 내용에도 있습니다만 Google C++ Testing Framework은 xUnit architecture 기반의 unit test tool로 다양한 플랫폼을 지원하고 사용이 어렵지 않습니다.
원문 : http://code.google.com/p/googletest/wiki/GoogleTestPrimer
Title은 이렇게 적었지만 사실 뾰족한 수가 없다. 오늘 우연히, 또 오랜만에 테스팅에 대한 책을 읽다보니 문득 얼마전까지 고민하던 문제들이 떠올라서 계속 생각해볼까 한다. 책에서 언급된 내용을 대충 요약하면 아래와 같다.
1. Regression test 는 반복적으로 수행해야 하는 test 기법이다.
2. 매번 반복하는데 공수가 많이 들어가므로 가능한 자동화 하는게 좋다.
3. 자동화를 하면 편해질 것 같지만 나름의 고통이 따른다.
4. 그 나름의 고통이란 testcase maintenance 를 의미한다.
5. Regression test 를 수행하는 이유가 소스코드의 수정에 따른 side effect 때문인데, 코드가 수정되면 testcase 도 수정되어야 하기 때문이다.
먼저 Regression test 부터 정리해보면
어떤 개발자가 특정 application을 구성하는 Module A, B, C 를 구현했다고 치자. 처음에 component test 혹은 integration test를 진행해서 Module A에서만 defect이 검출되었고 테스터는 테스트 매니저에게, 매니저는 개발팀과 유관된 타 부서에도 reporting을 했다. 개발자는 test report를 받아들고는 '아! A의 이런 부분이 잘못되었군. 요렇게 수정하면 되겠다' 는 생각을 하고 수정을 한다. 그리고는 이제는 문제가 없을테니 테스터가 더 이상 괴롭히는 일이 없을거라 기대한다. 물론 맞게 수정되었다면 A에 대한 테스트는 통과할 것이다. 하지만 A, B, C는 application을 구성하는 하부 module이기 때문에 유기적으로 연관이 있을 수 밖에 없고, 때문에 A의 코드 수정사항이 다른 B, C에 영향을 끼칠 가능성을 배제할 수 없다. 보통 얘기하는 side effect는 바로 이런 상황에서 발생한다.
때문에 A의 수정사항 때문에 다른 부분에 문제가 생겼는지를 확인하고자 전 module에 대해서 테스트를 진행해야만 하는게 일반적이고 이러한 테스트를 우리는 regression test라 부른다.
테스트를 진행하다보면, side effect 같은게 많지 않을거라 생각하지만 경험상 의외로 많이 발생한다. 개발자들은 regression test의 결과를 받고는 바로 항변하는데, '난 이 부분의 코드를 수정하지도 않았는데, 왜 defect이 발생한거죠? 테스트가 잘못된거 아닌가요?' 이런 반응이 일반적이다.
이런 일이야 비일비재하니 뒤로 넘기고, regression test에 대해서 다시 집중을 해보면 코드 수정사항에 대해 매번 전 부분에 걸쳐 테스트를 재수행해야 하기 때문에 일을 덜기 위해서는 자동화가 필수적이라고 할 수 있다. 그리고 자동화를 하더라도 필연적으로 마주치게 되는게 바로 Testcase maintenance이다. 사실 경험상 자동화 환경을 구축하는 일 자체는 그렇게 어렵지 않다. 왜냐하면 '무에서 유를 창조'하는 작업이기도 하기 때문에 나름 재미도 느낄 수 있고, 보람도 있다. 때문에 실제로 어렵더라도 덜 어렵게 느껴진다.
하지만 매 수정사항에 대해 Testcase를 유지보수 하는 일은 정말 지루한 작업이다. 절대적인 시간과 노력을 들여야 하는 작업임에도 '표시'가 나질 않고, 이미 했던 것이기 때문에 흥미도 떨어지는게 일반적이다.
어떻게 하면 maintenance에 소요되는 노력을 줄일 수 있을까?
또는 어떻게 해야지 조금이라도 덜 힘들고 지루한 느낌이 덜 들게 만들 수 있을까?
항상 고민이 되는 부분이다.
SW Testing 부분에 몸을 담고 있는 대부분의 사람들도 마찬가지겠지만...
가도 가도 끝이 없는 테스트...
올라간 듯 보여도 다시 보면 제자리.
쉽지 않다.
Android 연구회 과제로 notepad에 몇 가지 기능 추가하기로 했었는데, 내가 맡은 부분은 완료했다. 입력된 메모를 email로 전송하는 간단한 기능인데 책도 보고 여기저기 뒤지면서 android 에서 제공하는 기본적이고 중요한 내용은 어느 정도 익힐 수 있었다. 사실 제공되는 notepad sample 자체가 가지는 몇가지 문제점 때문에 그것부터 개선한 후에 가려고 했으나(예전에 포스팅 했던), 내 능력의 미숙함과 더불어 게으름 때문에 신규 기능만 추가하는 선에서 마무리 하기로 했다.
1. User account
내가 참조했던 어떤 블로그에서 android 가 아직 자체적으로 email 관련 라이브러리를 제공하지 않는다고 봤는데, 예전 버전에서 안되었던건지 그래서 현재는 제공되는 것인지는 확인을 해봐야 하지만 일단 해당 라이브러리를 import 해서 사용했고 덕분에 Gmail 계정 사용자만 이용 가능한 상태. 어쨌든 공부의 목적으로 시작한거니 문제없는 기능구현이 우선이었다. User account 를 입력할 수 있는 activity 를 별도로 구현했으며 sample 의 NoteList activity 에서 intent로 넘겨받은 계정 정보를 처리. SharedPreferences 로 한 번 입력한 내용은 activity 나 application 이 종료되더라도 계정정보를 다시 입력하는 일이 없도록 작성했다.
2. Recipients
User account 정보를 입력한 이후에는 수신인 email 주소를 입력하는 activity를 실행하게 되는데, 필수 항목이므로 한글자도 입력하지 않으면 OK 버튼을 비활성화 상태로 두어 진행 불가능하게 구현했다. (User account 도 마찬가지) 여기까지 필요한 정보를 입력하고 OK 버튼을 누르면 이메일이 전송된다.
3. 결론
Notepad sample의 소스를 들여다보면 application 작성에 필요한 거의 대부분의 내용을 담고 있다는 걸 알 수 있다. Activity 간 data를 주고받는 Intent 의 사용은 물론이고, database, ContentProvider 를 모두 사용해서 구현되어 있기 때문에 이런 필수적인 API 들의 사용방법을 익히기에 딱 적당한 내용인 듯 하다. 추가로 배운 내용들은 아래에 간단히 요약.
Popup dialog
popup 형태의 activity 를 만들기 위한 방법은 여러가지가 있을 수 있는데 가장 간단한 방법은 일단 popup 형태로 만들고자 하는 activity 를 일반적인 activity 처럼 구현한 이후에 AndroidManifest.xml 파일의 해당 activity의 속성에 아래의 항목을 추가하면 끝.
android:theme="@android:style/Theme.Dialog"
Activity 는 호출순서의 가장 마지막에서 시작할 수 있도록 작성할 것.
sample 이 제공하는 것 이외에 별도로 작성한 activity 가 두 개인데 (User account와 recipient 정보 입력하는), 시나리오상 두 개가 연속으로 불려야 할 때도 있고, 따로 불려야 하는 경우도 있다. 두 개의 activity가 연속으로 시작해야 하는 경우 (User account 입력 후 바로 수신인 메일 주소를 입력하는 시나리오)를 위해 처음엔 startActivityForResult 를 연속으로 호출하게 작성했었다. 내 생각엔 하나의 activity 가 활성화되면 입력이 끝나야 main activity 로 넘어가서 다시 두 번째 activity 가 시작될거라고 생각했기 때문이었는데, 실제로 구현해보니 main activity는 두 개의 activity 를 모두 호출하고 그 아래에 있던 디버깅용 구문들까지 모두 순서대로 호출해버리는 사태가 발생했다. 이리저리 머리를 굴려서 적당히 분리해 문제가 없도록 개선은 했는데, 이런 경험하면서 가능한 startActivity나 startActivityForResult 호출한 이후엔 다른 메소드가 호출되는 일이 없도록 작성하는게 좋겠다는 생각을 하게 되었음.
Hint / Toast
EditText 에 data 를 입력하지 않은 상태에서 예시를 보여주는 hint 의 사용법과 Activity 에 짧은 메세지를 보여주기에 적당한 Toast 의 존재를 알았다. 그 중 Toast 는 간단히 구현결과 확인하는데도 아주 좋음.
내가 가진 책이 너무나 좋은 관계로 책 전반을 구글 혹은 출판사에서 제공하는 샘플 을 사용해 설명하고 있지만 해당 샘플을 어떻게 import 하는지에 대한 설명이 없다. IDE를 이클립스로 사용하고 있기 떄문에 제공되는 샘플에 프로젝트 파일만 들어있다면 클릭 몇 번으로 import는 마칠 수 있지만 유감스럽게도 구글에서 제공하는 샘플에는 프로젝트 파일이 들어있지 않다. .classpath도 물론 없다. 프로젝트 생성에 관련된 파일들만 제외하고 샘플 자체에 대한 파일들만 들어있는 셈이다. 처음엔 프로젝트를 새로 만들면서 manifest 파일이나 각종 소스파일과 리소스들을 그냥 복사, 붙여넣기 등을 통해서 해결하려고 해봤는데 그나마 건드려야 하는게 너무 많아서 짜증을 내고 있던 찰나에 이런 생각에 이르게 되었다. '구글에서 과연 이렇게 하라고 해둔걸까?' 역시 그렇지 않다. 제대로 꼼꼼하게 살펴보지 않아서 삽질만 한 셈. 구글에서 제공하는 샘플들을 사용하는 방법은 아래와 같다.
1. 프로젝트 생성
기본적으로 프로젝트 생성을 해보면 위와 같은 pop-up 을 만날 수 있다. 여기서 default로 설정된 항목들을 그대로 두고 필수항목인 프로젝트 이름이나 activity name 등을 채운 후에 Next를 해버리면, 나와 같은 삽질을 해야만 한다. 그래서 천천히 살펴보면 이런 걸 볼 수 있다. Create project from existing sample.
2. Sample 추가
Create project from existing sample 을 선택하면, 바로 위에 선택되어 있던 라디오버튼 항목들은 비활성화가 되고, Build Target 까지 적당히 원하는걸로 선택해주면 해당 platform 아래에 있는 sample 들을 선택할 수 있게 되어있다. (실제로 platform 별로 sample 디렉토리가 별도로 존재한다.) 그래서 맘에 드는 sample 을 골라주기만 하면 끝. 물론 프로젝트 이름 등의 필수항목들은 본인이 직접 채워줘야...
회사 업무 이외에 연구회 활동을 하고 있다. 다뤄본 사람이 조금 있어서 진입장벽이 나름 낮았던 WM은 이후 프로젝트에 별로 유용하지 않을 듯 하여 안드로이드로 옮겨타게 되어서 짬짬이 준비하고 있다. 덕분에 iphone 에 대한 공부는 좀 더 미뤄질 예정. 시간관계상 갑작스럽게 새로운 무언가를 만들기는 힘들듯 해서 제공되는 sample 중 하나인 notepad 를 응용하기로 했는데, 이리저리 돌려보니 notepad sample 자체의 기능이 조금 부족한 감이 없지 않아 개선작업부터 먼저 할 생각을 하게 되었다. 간단히 메뉴나 작은 기능만 추가하면서 익힐 예정. 아래는 기본 기능에서 개선이 필요한 부분들.
1. 메모 저장 기능
notepad 실행 후 menu 버튼을 눌러 새 메모를 추가하게 되면 간 단히 글 입력이 가능하지만, 별도의 저장버튼이 존재하지 않는다. 단지 글 입력 후 되돌아가기 버튼을 누르면 저장이 되는 형태. 별도의 저장 기능이 있는 것 보다 편할 수도 있지만 사람 헷갈린다. 그러므로 저장 메뉴가 필요함. (Delete는 있는데 왜 Save는 없는걸까?)
2. Title과 본문 분리
저장 아닌 저장을 마치고 note list로 빠져나가서 확인해보면 처음에 입력했던 모든 내용이 title로 등록되어 있다. 물론 별도로 title을 수정하면 title만 변경되고 실제 내용엔 변화가 없다. 이걸 보면 입력한 내용과 동일한 data를 title에도 복사하고 저장은 별도로 하고 있음을 유추해 볼 수 있다. 개선이 필요하다. 새 메모 입력시 Title과 본문을 분리해 두어야 하고, 본문 입력시 제한적인 화면크기를 생각해서 title은 숨겨야 한다. 물론 저장시 숨겨두었던 title은 다시 꺼내서 보여주어야 함.
3. Menu list에서의 Delete 기능
Note list에서 메모 하나를 선택 후 메뉴 버튼을 눌러보면 Add Note, Edit Note, Edit Title 세 가지의 메뉴만 나타난다. 물론 메모를 선택해 롱키를 누르면 context menu에 Delete가 보이지만 별도의 메뉴로 존재해야 한다. Edit이 있는데 Delete가 없다는건 말이 안되니까.
책을 보고나서 정리한다는게 생각처럼 쉽지 않다. 특히 예제가 많거나 할 때는, 가장 좋은 방법이 직접 해보는거라 다 넣고 싶지만 너무 길어지게 되어서 고민된다. 그래서 이후로는 그냥 팁이나 문법적인 내용만 간략하게 정리할 생각이다. 어차피 대부분의 Objective-C 책들이 C 문법을 어느정도 알고 있다는걸 가정하고 쓰여졌으니까.
1. Indirection
책에선 간접적인 방법에 의한 참조를 indirection이라고 표현하고 있다. 코드에 직접 값을 입력하거나 하는 방법이 아닌 포인터 등을 이용해 접근하는 방법을 의미한다. 예를 들어 for 문을 돌리는데 원하는 반복횟수를 직접 숫자로 입력하는게 가장 간편한 방법이 되겠지만 이런 방법을 아무 생각없이 사용하게 되면 이후의 유지보수나 확장성 측면에서는 별로 좋지 않다. 그래서 함수 상단에 반복횟수를 저장하는 변수를 하나(예를 들어 흔히쓰는 count나 cnt 등으로) 두고 그 변수를 for문이나 함수 내의 다른 곳에서 사용하게 되면 수정할 필요가 있을 때, for 문이나 함수 전체를 훑어보고 수정할 필요 없이 저장된 변수의 값만 하나 살짝 바꿔주면 된다. 책에선 이런 식의 접근 방법을 indirection 이라하고 객체지향의 의미를 설명하게 전에 언급해 두었다. 실제 예는 아래에.
위의 그림은 word.txt 파일에서 문자열을 한줄씩 읽어 문자열과 함께 문자열의 길이를 출력해 주는 예이다. 이걸 indirection의 의미를 이용해 어떻게 바꿀 수 있을까? 파일명을 코드에 넣지 않고 인자로 입력받는 방법을 쓴다면 여기서 좀 더 나아간 코드가 될 것 같다는 느낌이 든다. 물론 위의 예제 자체도 indirection을 약간 사용한 내용이다. 파일에서 읽어오는 것이 아니라 문자열을 직접 코드 안에 넣었다면 유지보수나 확장이 더 힘든 코드가 될 것이다.(물론 때에 따라 코드 안에 직접 문자열을 넣어두는게 좋을 때도 있지만...) 어찌되었든 위의 코드를 조금 더 개선해보면 아래와 같이 된다.
main 함수 상단만 변경했다. 먼저 인자의 갯수를 판단해서 파일 이름을 넣어두었는지 판단한 후에 해당 인자를 파일 이름으로 해서 fopen을 하는 내용으로 변경되었는데, 책과 거의 비슷하게 만들어 보니 오류가 있다. 해당 파일이 없을 경우를 고려해서 파일이 없을 땐 아무 작업도 하지 않고 종료되게 해야 하는데 위의 예제는 fopen에 실패해도 그냥 fgets를 호출해 파일포인터를 사용하게 된다. 분명 잘못된 파일이면 run time error 가 발생할게 뻔하다. 하지만 어차피 indirection 을 설명하기 위한거니까 이해해 주시길. 이렇게 변경하면 최초의 예제보다는 좀 더 간편한 코드가 만들어진다.
2. XCode에서 argument 설정하기
두번째 예제를 만들어보면서 XCode에서 argument는 어떻게 설정하는지 알게 되었다. 바로 다음의 그림 참조.
코드가 보이는 편집기 말고 XCode 화면을 보면 왼쪽에 Executables가 보이고 화살표를 눌러 하위 내용을 보면 코드가 들어있는 m 파일이 빌드된 바이너리가 보인다. 그 바이너리를 더블클릭하면 아래와 같은 창이 열리면서 다양한 설정메뉴들이 나타난다.
상위의 탭에서 두번째에 위치한 Arguments를 클릭하면 처음엔 비어있는데 가운데쯤에 있는 + 버튼을 누른 후에 인자로 넣고 싶은 내용을 추가해주면 끝난다. 나의 경우엔 문자열이 들어있는 텍스트 파일이 빌드된 실행파일보다 두 단계 상위 디렉토리에 있었기 때문에 상대경로로 지정해 두었다. 여기까지 입력한 후에 콘솔창을 띄우고 Run 해보면 끝. 물론 txt 파일이 정상이어야 하고 인자로 입력한 위치/파일명과 동일한 파일로 이미 만들어져 있어야...^^
Indirection에 이어 OOP에 대한 부분을 공부했다. Objective C 자체가 객체지향 때문에 C에서 small talk를 차용했다고 알려져 있기 때문에 얼마나 효율적인가가 궁금하긴 했다. 일단 C++ 과 비교했을 때 비슷하다는 느낌도 들었지만 가독성 면에서는 더 낫다는 생각이다. 또 java와 비슷하게 interface와 implementation 부분을 구분하기 좋다는 것도 장점이지 않을까 싶다. 내용 정리하면서 사용했던 예제는 보통 C++이나 java에서 OOP 설명에 많이 사용하는 도형 예제이다. (Circle, Rectangle, Triangle 을 처음엔 구조체로 작성했다가 클래스로 바꾸고, 나중엔 상속 개념을 도입하면서 추상화 클래스인 Shape 라는 걸 만들면서 refactoring 하는 그런...)
1. 용어
다른 언어에서는 볼 수 없었던 조금은 생소한 용어를 아래에 따로 정리했다. 사실 메세지의 경우엔 Objectvie C 코드에서 가장 특이하다고 생각했던 문법에 관련된 내용으로 이 부분만 책에서 봤는 데도 Objective C 예제 코드들을 이해하기가 쉬웠다.
Message : Objective C에서 객체가 수행하는 액션.
Method dispatcher : Objective C의 코드에서 메세지가 보내지면 method dispatcher 가 메소드를 찾는다. 가장 먼저 해당 클래스에서 찾고, 없으면 상위 클래스로 올라가면서 찾는 방식을 취하며 찾아도 없으면 오류를 내보낸다.
2. Message
아래의 그림을 살펴보면,
이상한 부분이 하나 눈에 띈다.
[shape draw];
이게 뭔지 도대체 알 수 없었다. 배열도 아니고...Objective C 샘플들을 보면 대괄호로 묶인 저런 문장들이 자주 보인다. 메세지를 보낸다는 의미로 해석하면 된다고 하는데, 책 내용 그대로 보면 shape 라는 객체에 draw라는 메세지를 보낸다는 의미라고 한다. 근데 왠지 뭔가 뒤죽박죽 이상해서 내 나름대로 편하게 해석하기로 했다. shape에게 draw 라는 명령을 내리는 거라고 생각하면 훨씬 편한 듯 하다. 뭐 그게 그거일 수도 있다...ㅎㅎ
다시 정리하면 shape라는 객체가 존재하는데 그 객체는 멤버함수로 draw를 가지고 있어야 하고 그 draw를 호출하는 내용이라고 볼 수 있다. 그런데 그건 그렇다고 하더라도 또 이상한게 보인다. "id". 객체를 가리키는 포인터로 이해하면 쉽다. 난잡하게 생긴 포인터 대신 깔끔하게 id 라는 타입을 사용하는 듯 하다. 그러니까 [shape draw]의 shape가 id 타입의 변수이지 만 어차피 객체를 가리키기 때문에 shape가 가리키고 있는 객체에게 draw 를 지시하는 내용.
헷갈릴 수 있으니 또 다른 예문을 보자.
첫번째 예제와 같은 프로젝트에 사용한 main 함수의 내용이다. 첫번째 예제와 비슷한 부분이 몇 군데 보인다. 일단 재미있는 부분은 shapes[0] = [Circle new]; 문장인데 처음에 다루었던 내용을 보면 같다. 단지 Circle 이라는 클래스에 new라는 메세지를 보내고 있을 뿐이다. 한마디로 Circle 객체를 새로 만들라는 의미.
두번째로, 비슷하지만 좀 이상한 부분이 보인다.
[shape[0] setBounds: rect0];
shape[0]는 객체를 가리키는 id 배열 중 하나이니 어차피 객체를 가리키는 거라 생각하면 되지만 뒷 부분의 콜론 좌우는 좀 어색하게 느껴진다. 알고보니 간단했는데, setBounds는 메세지. 그러니까 멤버함수인거고 콜론 이후의 rect0는 setBounds에 전달하고 싶은 parameter. 끝. 이상한거 없음.
3. Interface / Implementation
첫 부분에 언급했던 것처럼 Objective C에서는 interface 와 implementation이 구분하기 좋게 문법적으로 구성되어 있다. 아래는 상속개념까지 적용해서 refactoring 까지 마친 상태의 도형의 최상위 클래스 Shape이다.
클래스를 interface와 implementation으로 구분해 놓은 내용이고, @interface로 시작해 @end로 끝맺음을 하는 형태를 갖추어야 한다. Shape 옆의 콜론은 상속을 받는 의미로 해석하면 편할 듯 하고 extends 정도로 해석하면 되지 않을까? (나만의 생각) 그러니까 Shape는 NSObject (C++에서 Object 클래스 같은 최상위 클래스)를 상속받고 그 아래의 중괄호로 싼 내용은 멤버변수라고 생각하면 된다. 특이한 부분은 '-' 로 시작하는 3줄. 직관적으로 멤버함수를 의미한다는게 느껴진다. (void) 는 리턴타입을 의미하며, 위에 있는 main 함수 예문을 참고해서 보면 setFillColor 라는 method는 ShapeColor 타입의 fillColor를 인자로 받는 method 라는 걸 명시해주고 있다.
구현부인 implementation도 @로 시작해 @end로 끝맺음한다. Interface 부분과 동일한 형태로 method를 나열해 중괄호로 묶어 구현내용을 넣어두었다. 마지막에 있는 draw는 Shape를 상속받을 Circle, Rectangle 등의 sub class에서 overriding 해서 쓸 거라 빈 내용으로 두었다.
4. Inheritance
상속이다. OOP에서 가장 중요한 개념 중 하나. 역시나 예제 위주로 보는게 편하다. 아래의 그림.
Shape를 상속받는 Circle과 Rectangle 클래스의 interface와 implementation 부분이다. 대충보면 대부분 이해가 되는데 하나 이상한게 interface 안에 내용이 없다는 사실. 두 클래스 모두 Shape의 멤버변수와 멤버함수 이외에 추가할게 없는 경우엔 저렇게 빈 공간으로 두어도 무방하다. implementation 부분을 보면 Shape의 draw를 overriding 해서 쓰려고 재정의해 두었다. 두 클래스의 draw의 차이점은 NSLog에 들어가는 circle과 rect 문자열의 차이. ㅎㅎ
초반에 용어정리해 두었던 method dispatcher를 생각해보면, method dispatcher는 내가 사용한 예제에서 draw를 호출할 때 각 클래스(Circle, Rectangle)에 있는 draw를 먼저 호출하려 할거고 만약 재정의되어 있지 않다면 상위의 Shape로 올라가 draw를 찾아서 있다면 Shape의 draw를 호출하게 만든다.
특이한 문법 때문에 Objective C 책을 한 권 샀다. 시간날 때 마다 공부해서 정리할 생각. 책은 Apress에서 발간한 원서를 BJ 퍼블릭이란 곳에서 번역한 출판한 "(아이폰과 맥 OS X 개발을 위한) Objective C" 이다. (원래 서명은 굵은 꺽쇠(?)로 감싸줘야 한다고 배웠었는데, 맥북이란게 한글 친화적이지 못한 부분이 조금 있어서 어쩔 수 없이 큰 따옴표를...)
1. Hello World
누구나 언어를 처음 배우면 처음 해보는게 Hello World 출력하는 프로그램이다. 왜냐하면, 1.간단하고, 2.사용할 IDE의 기본을 후딱 맛보기 좋으며, 3.여러가지 이유로 각종 책들이 젤 첫 예제로 Hello World를 사용하기 때문이다. 아무것도 모르는 우리들이야 어쩔 수 있나 무작정 따라할 수 밖에.
IDE로는 OS X 설치된 맥만 있으면 무료로 다운받을 수 있는 Xcode. 무조건 실행해보면, Visual Studio나 Eclipse 등의 IDE들 처럼 프로젝트를 새로 만들거냐 라는 물음의 메뉴가 최상위. 기타 등등은 아래에 배치되어 있다. 새 프로젝트 만들겠다고 선택한 후
위 그림과 같은 화면이 나오면 왼쪽 메뉴에서 Application, 오른쪽에서 Command Line Tool, 중앙의 Type을 Foundation으로 선택한뒤 Choose 버튼을 꾹 누른다. 그리고 적당한 프로젝트 이름과 위치를 지정해주면 끝. Xcode가 뚝딱뚝딱 뭔가 처리하더니 아래와 비슷한 화면이 보이게 된다.
난 HelloWorld 라고 프로젝트 이름을 지정했더니 HelloWorld.m 파일이 보인다. 그게 Objective-C 소스가 들어갈 파일이다. 나머지 파일에 대해선 천천히 공부할 예정. 책에서 언젠간 언급이 되겠지. (난 시간이 없다. ㅎㅎ 언급이 안되더라도 언젠간 알아야 할 시점이 올거다. 아마도...) 더블클릭해서 원래 Xcode가 만들어준 소스를 지우고 아래처럼 입력해본다.
C와는 다르게 #import 라는게 있지만, 생김새보니 딱 #include 역할이다. stdio.h 대신 Foundation.h 가 쓰인다고 생각하면 될 듯 하다. 참고로 Foundation.h 파일의 위치는 /System/Library/Frameworks/Foundation.framework/Headers/ 이다. 분명 나중에 해당 헤더들을 한번쯤은 열어볼 필요가 생길 것 같다. C처럼 생각한다면...
헤더파일 import를 제외하고는 어색한 부분이 딱 하나 보인다.
NSLog(@"Hello World");
NS는 스티브 잡스가 애플에서 쫓겨났을 때 만들었던 NeXT 라는 회사에서 만든 NextSTEP의 약자라고 하는데, 저 앞부분만 제외하고 보면 Log니까 콘솔에 글자를 찍어주는 함수일거란 느낌이 든다. 근데 @는 뭘까? @는 뒤에 오는 문자열이 NSString 형태라는 알려주는 기호라고 한다. 그러니까 NSLog는 C의 printf와 비슷한데, printf에서처럼 일반 문자열을 사용하는게 아니라 NSString type을 사용하기 때문에 @를 넣어준다고 생각해주면 쉽다. printf 보다 편리한 점은, 뿌려주고 싶은 문자열 마지막에 개행문자(\n, \r\n)를 넣지 않아도 된다는 점. 빌드해보면 아래와 같은 결과를 얻을 수 있다. (Build + Run 은 Command + R이고, 이전에 콘솔 창을 먼저 열어줘야 하는데 단축키는 Command + Shift + R)
굵은 글씨로 Hello World 라고 떡하니 찍혀있다. 어찌보면 당연한건데 항상 처음하는건 신기하고 기쁘다.
2. %@, BOOL
좀 더 예제를 다뤄보기로 하자. 역시 전부 다 지우고 이리저리 입력해 보았다. 아래의 그림.
그냥 단순히 숫자 비교하는건데, 속도를 위해 main 함수를 보면 아래와 같은 구문이 있다.
NSLog(@"%d and %d are same, right? %@", 3, 5, boolToString(areSame(3, 5)));
printf 처럼 형식지정자가 비슷하다. int type 받을거니까 %d 사용했는데, %@ 라는 것도 있다. 직관적으로 NSString type을 받을 것임을 느낄 수 있다. 간단하다. 특이한 것 하나는 YES, NO 같은게 있다는건데 이건 뭘까? 바로 BOOL type 이다.
C에서도 bool type 이 있고 true, false 가 있으니 마찬가지라고 생각하면 된다. 보통 C/C++ 로 개발하다 보면, 편의성이나 type들이 꼬이는걸 막기 위해 개발자들이 별도로 사용하기 위해 type을 재정의하는 경우가 있는데 Objective C도 마찬가지인 듯 보인다. 그냥 쓰기 편하게 unsigned char를 재정의 한 것이고 각각 1을 YES, 0을 NO 라는 키워드로 정의해 두었다. (1바이트 이상의 값을 BOOL 변수에 대입하면 하위 바이트 값만 저장되므로 유의)
며칠전 자기 전에 30분 정도 본 내용인데 정리하려니 시간이 배는 더 걸리는 것 같다. 하지만 이래야 복습이 된다. 간단한 것도 잘 익혀둬야 나중에 삽질하지 않으니 기본은 철저히 익히는게 좋은 듯. 계속 정리할 거다. 꼭...
참! 결과 화면. ㅎㅎ 결과를 봐야 재미 있는게 개발인데...빠져선 안된다.