기본 콘텐츠로 건너뛰기

iOS: iPhone용 계산기 프로그램 만들어보기

이 내용은 Stanford 대학에서 제공한 "Developing Apps for iOS"의 강의 내용을 참고하여 작성한 것임을 밝힌다.

2012/2/22 - Xcode 4.3 (iOS 5.0) 기준으로 업데이트!

Stanford의 강의 내용이 좋지만 영어의 장벽때문에 좌절하고 있을 개발자 분들에게 약간이나마 도움이 되었으면 하는 마음과 내 스스로 정리한다는 차원에서 해당 강좌를 듣고 내용을 정리해보고자 한다. 이 강의는 iTunes University에서 "Developing Apps for iOS"로 직접 검색하여 "Building a simple calculator"라는 제목의 동영상을 통해 볼 수 있다.

원래 이 Stanford 강의의 목적은 기본적인 iOS 프로그램 개발과 MVC 구조에 대한 이해를 돕는 것이다. 아직 기초 단계의 강의라 메모리 해지 등에 대해서는 신경쓰지 않고 진행한다.

1. Xcode에서 새로운 프로젝트 생성
Xcode를 구동한 후 iOS의 Single view application으로 Calc라는 이름의 새로운 프로젝트를 만들자. (Storyboards는 사용하지 않는다.)

2. MVC에서 M에 해당하는 Model 소스를 추가
좌측의 Calc 폴더에서 context menu를 뛰우고 New File...을 선택한다. New File 다이얼로그에서 Objectiv-C class를 선택하고 이름을 CalcBrain이라고 하자. CalcBrain.h와 CalcBrain.m 파일이 생성된다. (.m 파일은 .c 로 생각하면 된다.) CalcBrain은 계산기 구현에 필요한 model을 담당할 파일들이다.

3. CalcBrain.h에 instance variable과 instance method를 선언
계산기 model에 필요한 데이터와 동작을 header 파일에 선언한다.
#import <Foundation/Foundation.h>

@interface CalcBrain : NSObject {
 double operand;
 NSString *waitingOperation;
 double waitingOperand;
}

- (void)setOperand:(double)anOperand;
- (double)performOperation:(NSString *)operation;

@end

4. CalcBrain.m에 instance method 구현
#import "CalcBrain.h"

@implementation CalcBrain

- (void)setOperand:(double)anOperand
{
 operand = anOperand;
}

- (void)performWaitingOperation
{
 if ([@"+" isEqual:waitingOperation]) {
  operand = waitingOperand + operand;
 } else if ([@"-" isEqual:waitingOperation]) {
  operand = waitingOperand - operand;
 } else if ([@"*" isEqual:waitingOperation]) {
  operand = waitingOperand * operand;
 } else if ([@"/" isEqual:waitingOperation]) {
  if (operand != 0) {
   operand = waitingOperand / operand;
  }
 }
}

- (double)performOperation:(NSString *)operation
{
 if ([operation isEqual:@"sqrt"]) {
  operand = sqrt(operand);
 } else {
  [self performWaitingOperation];
  waitingOperation = operation;
  waitingOperand = operand;
 }
 return operand;
}

@end

위 구현 코드에서 좀 햇갈릴 수 있는 부분은 performWaitingOperation이다. performOperation 메쏘드는 인자로 +, -, *, /, =, sqrt와 값은 연산기호를 입력받는다. 이 때 전에 기다리고 있는 연산이 있다면 이를 수행하기 위해 performWaitingOperation을 호출하고 이번에 받은 연산자는 현재 operand 값과 함께 다음번에 계산하기 위해 각각 waitingOperation, waitingOperand에 보관된다. 좀 더 이해를 돕기 위해 3 + 4 = 를 사용자가 순서대로 입력했다고 가정하고 어떤 순서로 수행되는지 설명해보겠다.
  • setOperand: 3
  • performOperation: +
  • performWaitingOperation: 하지만 기존에 기다리던 연산이 없다. 그냥 리턴 됨.
  • waitingOpeation = +
  • waitingOperand = 3
  • setOperand: 4
  • performOperation: =
  • performWaitingOperation: 기존에 저장해둔 + 연산을 수행. 즉, 3 + 4의 값이 operand에 결과 값으로 저장 됨.
  • waitingOperation이 = 으로 변경 됨. 다음 번 performWaitingOperation이 수행되면 = 는연산자로서 아무런 의미가 없으므로 그냥 리턴 됨.
위 처리 방법을 이해하였으면 계산기 구현을 위한 Model 구현은 끝났다. 다음은 MVC에서 V에 해당하는 View를 구성해보도록 하자.

5.IBOutlet과 IBAction 선언
Objective-C에서는 MVC 구조에서 View와 Controller간의 통신을 위해 outlet과 target/action을 사용한다. outlet은 Controller가 View에 있는 UI 객체를 가리키는 참조이고 이를 통해 UI 객체를 다룰 수 있다. target/action은 반대로 View에서 어떤 UI 객체가 사용자에 의해 이벤트가 발생한 경우 이를 Controller에게 알려주는 방법이다. outlet과는 달리 View에서 Controller 방향으로는 참조가 존재하지 않고 View가 Target인 Controller에 Action에 해당하는 메시지를 전달하는 방식이다.

Outlet, Target/Action을 구현하기 위해 우리 예제의 Controller에 해당하는 ViewController.h에 아래와 같이 코드를 작성한다.
#import <UIKit/UIKit.h>
#import "CalcBrain.h"

@interface ViewController : UIViewController {
 IBOutlet UILabel *display;
 CalcBrain *brain;
 BOOL middleOfNumberTyping;
}

- (IBAction)digitPressed:(UIButton *)sender;
- (IBAction)operationPressed:(UIButton *)sender;

@end
IBOutlet UILabel *display; 코드 라인이 View에 있는 UILabel 객체를 다루기 위해 사용하는 참조이다. 맨 앞에 붙은 IBOutlet은 void를 #define 한 것으로 Interface Builder에서 소스 코드를 분석하기 위해 사용하는 일종의 메타데이터라고 이해하자.

(IBAction)digitPressed와 (IBAction)operationPressed는 View에서 사용자에 의해 숫자 버튼 혹은 연산자 버튼이 눌리면 각각 Action에 대한 Target이 될 메쏘드이다. 역시 앞에 (IBAction)이라는 것은 void에 해당하며 Interface Builder를 위한 소스 코드 분석용 메타데이터로 이해하자.

6. Interface Builder에서 View 구성
좌측 화면의 Resources에서 ViewController.xib을 클릭하여 Interface Builder를 실행한다. 아래 순서대로 화면을 구성하자.
  • Library에서 Label을 선택하여 View 윈도우의 상단에 적절히 배치한다. (계산 결과 긴 문자열이 표시 될 수 있도록 라벨의 폭을 넓게 잡아주어야 한다.)
  • 배치한 Label의 Attributes를 수정하여 폰트 크기를 늘리고 text의 layout을 우측 정렬로 바꾼다.
  • Label에 대한 outlet을 설정하기 위해 File's Owner를 선택하고 control 키와 왼쪽 마우스 버튼을 누른 상태에서 마우스를 끌어 Label까지 이동 후 버튼을 논다. 그러면 컨텍스트 메뉴가 나오면서 outlet으로 어떤 변수를 선택할지 물어본다. 이 때 display를 선택하자. 이제 View에 있는 Label 객체는 Controller의 display 멤버에 의해 참조할 수 있게 되었다.
  • Round Rect Button을 Library에서 선택하여 View 윈도우에 배치한다.
  • 이 버튼을 선택한 후 control 키와 왼쪽 마우스 버튼을 누른 상태에서 마우스를 끌어 File's Owner 아이콘위로 이동 후 마우스 버튼을 띈다. 
  • 그러면 File's Owner 아이콘 위로 digitPressed, operationPressed라는 context 메뉴가 나타날 것이다. 여기서 digitPressed를 선택한다. 여기서 File's Owner은 ViewController이다. digitPressed와 operationPressed는 IBAction으로 앞에서 선언해두었기 때문에 Interface Builder가 이를 인식하여 메뉴에 보여주는 것이다.
지금까지 설명한 과정은 숫자입력을 위한 버튼을 Controller의 action target에 적절히 연결하는 과정이다. View 화면은 대충 아래와 같은 모양이 되어 있을 것이다.

  • 숫자입력 버튼에 대한 target/action 설정을 그대로 활용하기 위해 이미 추가한 버튼을 선택하고 Cmd-C로 복사하고 Cmd-V로 9개의 버튼을 더 추가한다. 그런 후 적절히 숫자 값을 지정해준다.
  • Operation을 나타내는 +, -, *, /, sqrt, = 에 해당하는 버튼을 추가해보자. 다시 새로운 Round Rect Button을 하나 View에 배치한다. 
  • 역시 이 버튼을 선택하고 control 키와 왼쪽 마우스 버튼을 누른채로 File's Owner로 마우스를 끌고 가서 버튼을 논다. 이번에는 메뉴에서 operationPressed를 선택한다.
  • Operation에 해당하는 모든 버튼을 추가하기 위해 이 버튼에 대해서도 Cmd-C, Cmd-V를 수행하여 5개의 버튼을 더 추가 배치한다.
  • View를 아래와 같이 정리해보자.

7. Controller 코드 구현
마지막으로 Controller의 코드를 구현해주자. ViewController.m 파일을 아래와 같이 구현한다.

@implementation ViewController

- (CalcBrain *)brain
{
 if (!brain) {
  brain = [[CalcBrain alloc] init];
 }
 return brain;
}

- (IBAction)digitPressed:(UIButton *)sender
{
 NSString *digit = [[sender titleLabel] text];
 if (middleOfNumberTyping) {
  [display setText:[[display text] stringByAppendingString:digit]];
 } else {
  [display setText:digit];
  middleOfNumberTyping = YES;
 }
}

- (IBAction)operationPressed:(UIButton *)sender
{
 if (middleOfNumberTyping) {
  [[self brain] setOperand:[[display text] doubleValue]];
  middleOfNumberTyping = NO;
 }
 NSString *operation = [[sender titleLabel] text];
 double result = [[self brain] performOperation:operation];
 [display setText:[NSString stringWithFormat:@"%g", result]];
}

@end
Controller 코드는 View에서 발생한 이벤트에 대하여 Model에 적절한 값을 전달하거나 혹은 필요한 logic을 수행하도록 하고 그 결과를 다시 View에 반영하는 역할을 한다.

8. 실행
자 이제 Run을 선택하여 실행해보자. 혹시, 실행에 문제가 있다면 댓글로 알려주기 바란다.

댓글

  1. 3번 확인 좀 부탁드려요. 전부다 따라했는데 에러가 12개가 나왔는데요 (ㅠㅠ) 3번이 calcbrain.h 헤더파일 해주라고 하셨는데 calcbrain.h가 아니고CalcViewController.h 같습니다. 빨리 확인 좀 해주세요 .. 하구싶어용~

    답글삭제
  2. 에고 3번 과정에 CalcBrain.h 소스가 아닌 다른 소스를 복사해 두었네요. 죄송합니다. 수정 하였으니 한번 확인해 보세요.

    답글삭제
  3. 스탠포드 강의를 옮겨놓으셨네요~

    답글삭제
  4. 5번과정의 CalcBrain *brain; 에서

    Expected specifier-qualifier-list before 'CalcBrain' 에러나는데..

    어떻게 된건가요 ㅠㅠ
    도와주세요!

    답글삭제
  5. 5번 과정의 소스 코드에 #import "ClacBrain.h" 를 추가하면 해결 될 겁니다. 소스 코드 예제에서 빼먹었었네요.

    답글삭제
  6. self.window.rootViewController = self.viewController;

    이게 뭐죠

    실행시키면 스레드 에러뜨는거같은데

    답글삭제
  7. 안되자나요! 테스트하고 올린건가요?
    에러나자나요!
    테스트좀 하고 올리지!

    답글삭제
    답글
    1. Xcode가 버전업 되면서 기존 내용과 달라진 부분이 있어 에러가 발생했군요. 4.3 버전 기준으로 수정해 놓았습니다.

      삭제
    2. 졸라 싸가지없네; 버전업때문에 안되는걸...

      주인장님 힘내십쇼 잘보고갑니다

      삭제
  8. double result = [[self brain] performOperation:operation];
    부분의 performOperation -> performOperand로 바꾸시면 잘 실행됩니다

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

Wireless: HotSpot 2.0 이란?

스마트폰 사용자가 HotSpot 2.0을 지원하는 Wi-Fi 망을 사용하는 경우라면 기존 Wi-Fi 망과 달리 이동통신 망에서 Wi-Fi 망으로의 네트워크 연결 전환이 자연스럽게 이루어진다. 예를 들면, 3G 네트워크를 이용하여 영화를 보고 있다가 HotSpot 2.0 네트워크에 연결이 가능하게 되면 영화 시청 중단 없이 Wi-Fi 망으로 자연스럽게 네트워크 연결이 이동하여 3G 망의 부하도 줄이고 사용자의 네트워크 비용도 절약할 수 있다. 시스코에서 제공한 White Paper 를 참고.

Apple M1 Mac Mini에서 이더리움 (Ethereum) 채굴하기

 돈을 벌 목적은 아니고 이더리움 기술에 대한 호기심에 직접 채굴(마이닝)에 나서 보기로 했다. 머신은 Apple M1 Mac Mini. 스팩을 살펴보니 8 Core GPU에 16GB 메모리를 공유하고 있어 가능은 해보인다. 큰 흐름은 다음과 같다. 채굴한 이더리움을 저장할 지갑을 만든다 만든 지갑의 정보를 잘 보관해둔다 (Secret Recovery Phrase, 지갑의 주소 값) Apple M1용 채굴 프로그램 설치 내 지갑 정보를 이용해서 채굴 프로그램 실행 일단, 채굴한 이더리움을 저장할 지갑(wallet)을 만들어야 한다.  크롬 브라우저 익스텐션 설치로 비교적 간단하게 지갑을 만들 수 있는  https://metamask.io/ 를 이용하기로 했다. 크롬 익스텐션을 설치 후 기존에 만든 지갑이 없으므로 "Create a Wallet"을 선택한다. 패스워드 입력하고 등등의 절차를 거치면 아래와 같은 Secret Recovery Phrase가 나온다. 이 값을 잘 보관해두기 바란다. 나중에 지갑을 복구할 때 필요한 값이다. 이 값이 유출되면 지갑에 모아둔 이더리움을 다 털릴 수 있으므로 안전한 곳에 보관한다. Confirm Your Secret Phrase에서 확인 과정을 거친다. 직접 입력하는 것이 아니라 단어 별 버튼을 일일이 클릭해서 확인해주어야 한다. (좀 번거롭지만 그만큼 Secret Recovery Phrase가 중요함을 인지시키기 위한 과정이다.) 이제 지갑은 준비 완료. 생성된 Account 화면에서 지갑의 주소갑을 얻을 수 있다.  Apple M1용 채굴 프로그램을 설치해보자. Ethminer M1 Github 프로젝트 에서 미리 컴파일된 바이너리를 다운로드 받는다. (Assets를 펼치고 ethminer-m1을 클릭해서 다운 받으면 된다) 원하는 폴더에 파일을 옮겨 놓고 Terminal에서 chmod +x로 실행가능하게 만든다. % mv ~/Downloads/ethminer-m1 .             %   % c

Java: Java for Game? Java가 Game 개발에 어울릴까?

Java가 기업용 서버 소프트웨어 개발에 활발하게 쓰이는 것과 달리 일반 응용프로그램 분야에서는 별로 대접을 받지 못하는 현실을 개선하려면 어떤 분야부터 손보면 좋을까? 로딩타임, 성능, 사용자 인터페이스 등 Java를 이용한 클라이언트 프로그램을 개발하지 않는 이유들은 개발자 별로 서로 다를 것이다. 하지만, 이런 단점에도 불구하고 점점 복잡해지는 소프트웨어를 더 쉽게 다양한 환경에서 동작하도록 만들기 위해서는 Java만큼 이미 성숙한 해결책도 없지 않은가? 클라이언트 개발을 활성화하기 위해 Java를 게임 개발에 활용할 수 있도록 지원하면 어떨까? 역시, 사용자가 직접 쓰는 응용프로그램 중에는 게임이 가장 시장이 큰 분야이니 말이다. 그렇다면, 현재 게임 개발에 Java가 어느 정도 사용되고 있고 미래에 더 활성화 될 가능성은 있을 것인가? 이런 의문점을 가지고 "Java + Game"에 대해 조사해보기로 했다. 1. Java로 개발한 게임들 우선, Java로 개발한 게임들에 대해 살펴보자. Oracle의  Java in Action 웹페이지 를 보면 Java를 이용한 3D MMORPG RuneScape 에 대한 설명이 있다. 가입자가 1억 3000만명이 넘는다고 하다. 실제 게임을 설치해서 실행해보니 WOW같은 화려한 그래픽에는 못미치치만 잘만든 Role Playing 게임이다 Puppy Games 에서 개발한  Revenge of the Titans 게임도 눈여겨 볼 만하다. 이 회사는 작고 손쉽게 즐길 수 있는 게임을 개발하는데 모든 게임을 Java로 만든다. Revenge of the Titans는 Starcraft와 같은 전략 게임으로 너무 머리쓰지 않고 즐길 수 있는 게임이며 그래픽도 신선하다. 개발이 진행 중인 것으로 보이는 Urban Galaxy 라는 게임도 재미있을 것 같다. SF 영화를 보면 자주 등장하는 미래의 빌딩 숲을 날아다니는 자동차로 전투도 치르고 무역도 하며 캐릭터를 키우는 게임으로