Objective-C로 iOS 앱 개발 시작하기
iOS 운영체제에서 동작하는 애플리케이션을 개발하기 위해서는 Objective-C 언어의 학습이 필요합니다. 이 글에서는 Objective-C 언어를 사용하여 iOS 앱 개발을 시작하는 방법에 대해 살펴보겠습니다.
프로젝트 설정하기
Objective-C로 iOS 앱을 개발하기 위해서는 Xcode라는 IDE(Integrated Development Environment)를 사용해야 합니다. Xcode를 실행시키고, 새로운 프로젝트를 생성합니다.
프로젝트 유형 선택하기
Xcode에서는 프로젝트를 생성할 때, iOS 앱을 개발하기 위한 여러 가지 유형의 프로젝트를 제공합니다. 예를 들어, Single View Application, Tabbed Application, Game 등이 있습니다.
이 중에서 개발하고자 하는 앱의 유형에 맞추어 프로젝트 유형을 선택합니다. 선택한 프로젝트 유형에 따라 Xcode가 자동으로 기본적인 구조를 생성해줍니다.
프로젝트 정보 입력하기
프로젝트를 생성할 때, 프로젝트 정보를 입력해야 합니다. 이 정보는 개발자 이름, 회사 이름, 프로젝트 이름, Bundle Identifier 등을 포함하고 있습니다.
프로젝트 이름은 해당 앱의 이름을 입력하면 됩니다. Bundle Identifier는 앱의 고유 식별자로, 일반적으로 역순 도메인 네임을 사용합니다.
파일 구조 살펴보기
프로젝트를 생성하면, Xcode는 일부 파일과 폴더를 자동으로 생성합니다. 이 파일과 폴더는 iOS 앱 개발에 필요한 기본적인 구조를 제공합니다.
- AppDelegate.h/.m : 앱의 진입점이 되는 AppDelegate 클래스 파일입니다.
- ViewController.h/.m : 화면을 구성하는 ViewController 클래스 파일입니다.
- Main.storyboard : 화면 구성을 위한 Storyboard 파일입니다.
- Assets.xcassets : 앱에서 사용하는 이미지, 아이콘, 컬러 등의 리소스 파일을 담는 폴더입니다.
- LaunchScreen.storyboard : 앱이 실행될 때 보이는 로딩 화면을 구성하는 Storyboard 파일입니다.
기본 구조 이해하기
iOS 앱을 개발할 때, 앱의 구조를 이해하는 것이 중요합니다. 이를 위해서는 iOS 앱의 생명주기와 이벤트 처리 방식을 이해해야 합니다.
iOS 앱 생명주기
iOS 앱은 실행되면 다음과 같은 생명주기를 거칩니다.
- Not Running : 앱이 실행되지 않은 상태입니다.
- Inactive : 앱이 실행 중이지만, 아직 이벤트를 받을 수 없는 상태입니다.
- Active : 앱이 실행 중이며, 이벤트를 받을 수 있는 상태입니다.
- Background : 앱이 백그라운드에서 실행 중인 상태입니다.
- Suspended : 앱이 백그라운드에서 실행 중이지만, 메모리가 부족한 경우에는 종료될 수 있는 상태입니다.
앱이 실행되면, AppDelegate 클래스의 application:didFinishLaunchingWithOptions: 메소드가 호출됩니다. 이 메소드에서는 앱 초기화 작업을 수행하고, UIWindow 객체를 생성하여 화면을 구성합니다.
이벤트 처리 방식
iOS 앱은 이벤트 기반(event-driven)으로 동작합니다. 즉, 앱이 실행 중인 동안 사용자의 입력, 시스템 이벤트 등이 발생하면, 해당 이벤트에 대한 처리를 수행합니다.
이벤트 처리는 다음과 같은 순서로 이루어집니다.
- 이벤트 발생 : 사용자의 입력, 시스템 이벤트 등이 발생합니다.
- 이벤트 수집 : UIApplication 객체가 이벤트를 수집합니다.
- 이벤트 전달 : 이벤트가 화면 구성 요소(예: 버튼, 레이블)에 전달됩니다.
- 이벤트 처리 : 화면 구성 요소는 이벤트를 처리합니다.
ViewController 클래스
iOS 앱에서는 화면을 구성하는 데에 ViewController 클래스를 사용합니다. ViewController 클래스는 사용자 인터페이스와 관련된 로직을 담당합니다.
ViewController 클래스는 UIViewController 클래스를 상속받아 구현합니다. 이 클래스에서는 viewDidLoad() 메소드를 오버라이드하여, 화면이 로드될 때 필요한 초기화 작업을 수행합니다.
// ViewController.m 파일
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
@end
Storyboard 파일
iOS 앱에서는 Storyboard 파일을 사용하여 화면을 구성합니다. Storyboard 파일은 화면 구성 요소(예: 버튼, 레이블)와 ViewController 클래스를 연결하는 역할을 합니다.
Storyboard 파일에서는 다음과 같은 작업을 수행할 수 있습니다.
- 화면 구성 요소 추가하기
- ViewController 클래스와 연결하기
- 화면 전환 구성하기
- Segue(세그) 추가하기
Segue
Segue(세그)는 화면 전환을 위한 기능입니다. Segue를 추가하면, 화면 전환 시에 ViewController 클래스 간의 연결을 자동으로 처리할 수 있습니다.
Segue를 추가하려면, Storyboard 파일에서 Ctrl 키를 누른 상태에서 화면 구성 요소(예: 버튼, 테이블 셀)를 클릭하고, 드래그하여 다른 ViewController 클래스와 연결합니다.
// ViewController.m 파일에서 Segue를 사용하는 예제
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"ShowDetail"]) {
DetailViewController *detailViewController = [segue destinationViewController];
// DetailViewController에 데이터 전달하기
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *selectedTitle = self.titles[indexPath.row];
NSString *selectedSubtitle = self.subtitles[indexPath.row];
detailViewController.titleString = selectedTitle;
detailViewController.subtitleString = selectedSubtitle;
}
}
Objective-C 언어 기초 살펴보기
Objective-C 언어는 C 언어를 기반으로 하고 있으며, 객체 지향 프로그래밍(Object-Oriented Programming)을 지원합니다. 이번 섹션에서는 Objective-C 언어의 기초적인 문법과 객체 지향 프로그래밍에 대해 살펴보겠습니다.
클래스와 객체
Objective-C 언어에서는 모든 것이 객체입니다. 객체는 클래스(Class)를 통해 생성됩니다. 클래스는 객체를 생성하기 위한 일종의 틀이며, 객체의 속성과 메소드를 정의합니다.
// 클래스 선언 예제
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *myString;
- (void)myMethod;
@end
위의 예제에서는 MyClass라는 이름의 클래스를 선언하였습니다. 이 클래스는 NSObject 클래스를 상속받으며, myString이라는 문자열 속성과 myMethod라는 메소드를 가지고 있습니다.
프로퍼티(Property)
Objective-C 언어에서는 클래스의 속성을 프로퍼티(Property)라는 용어로 표현합니다. 프로퍼티는 해당 클래스의 객체가 가지는 속성을 나타내며, 객체 외부에서 접근할 수 있도록 getter와 setter 메소드를 자동으로 생성합니다.
// 프로퍼티 선언 예제
@property (nonatomic, strong) NSString *myString;
위의 예제에서는 myString이라는 이름의 문자열 프로퍼티를 선언하였습니다. 이 프로퍼티는 strong 타입의 속성으로, 객체가 메모리에서 해제될 때 같이 해제되지 않도록 보호합니다.
메소드(Method)
Objective-C 언어에서는 클래스에서 정의한 동작을 메소드(Method)라는 용어로 표현합니다. 메소드는 클래스의 객체가 수행할 수 있는 동작을 나타내며, 객체가 메시지를 수신하면 해당 메소드가 실행됩니다.
// 메소드 선언 예제
- (void)myMethod;
위의 예제에서는 myMethod라는 이름의 메소드를 선언하였습니다. 이 메소드는 반환값이 없는(void) 메소드로, 객체가 수행할 동작을 정의합니다.
객체 생성하기
Objective-C 언어에서는 클래스를 사용하여 객체를 생성할 수 있습니다. 객체를 생성할 때에는 alloc 메소드를 사용하여 메모리를 할당하고, init 메소드를 사용하여 초기화합니다.
// 객체 생성 예제
MyClass *myClass = [[MyClass alloc] init];
위의 예제에서는 MyClass 클래스의 객체를 생성하였습니다. 이 객체는 myClass라는 이름의 포인터 변수에 저장됩니다.
메시지(Message)와 선택자(Selector)
Objective-C 언어에서는 객체 간의 통신을 메시지(Message)를 통해 수행합니다. 객체에 메시지를 보내면, 해당 객체는 해당 메시지를 처리하는 메소드를 실행합니다.
메시지는 다음과 같은 형태로 작성됩니다.
[myObject myMessage];
위의 예제에서는 myObject라는 객체에 myMessage라는 이름의 메시지를 보내는 것입니다. 이 메시지를 처리하는 메소드가 myObject 객체 내부에 존재한다면, 해당 메소드가 실행됩니다.
메시지를 처리하는 메소드를 선택자(Selector)라고도 부릅니다. 선택자는 메소드의 이름을 나타내며, Objective-C 언어에서는 @selector() 구문을 사용하여 선택자를 생성합니다.
// 선택자 생성 예제
SEL mySelector = @selector(myMethod);
위의 예제에서는 myMethod라는 선택자를 생성하여 mySelector라는 이름의 변수에 저장하였습니다.
상속(Inheritance)
Objective-C 언어에서는 상속(Inheritance)을 지원합니다. 상속은 이미 정의된 클래스를 기반으로 새로운 클래스를 생성하는 기능입니다. 상속을 사용하면, 기존 클래스에서 정의된 속성과 메소드를 새로운 클래스에서 그대로 사용할 수 있습니다.
// 상속 예제
@interface MySubClass : MyClass
@end
위의 예제에서는 MyClass 클래스를 상속받아 MySubClass라는 클래스를 생성하였습니다. MySubClass 클래스는 MyClass 클래스에서 정의한 속성과 메소드를 그대로 사용할 수 있습니다.
다형성(Polymorphism)
Objective-C 언어에서는 다형성(Polymorphism)을 지원합니다. 다형성은 같은 타입의 객체가 다른 방식으로 동작할 수 있도록 하는 기능입니다.
// 다형성 예제
@interface MyParentClass : NSObject
- (void)myMethod;
@end
@interface MyChildClass : MyParentClass
- (void)myMethod;
@end
@implementation MyParentClass
- (void)myMethod {
NSLog(@"Parent Class");
}
@end
@implementation MyChildClass
- (void)myMethod {
NSLog(@"Child Class");
}
@end
위의 예제에서는 MyParentClass와 MyChildClass라는 두 개의 클래스를 생성하였습니다. 이 두 클래스는 모두 myMethod라는 메소드를 가지고 있습니다.
MyParentClass에서는 myMethod 메소드를 구현하여 "Parent Class"라는 문자열을 출력하도록 하였습니다. MyChildClass에서는 myMethod 메소드를 오버라이드하여 "Child Class"라는 문자열을 출력하도록 하였습니다.
이제 다음과 같은 코드를 실행하면 다음과 같은 결과가 출력됩니다.
MyParentClass *parentClass = [[MyParentClass alloc] init];
[parentClass myMethod]; // "Parent Class"
MyChildClass *childClass = [[MyChildClass alloc] init];
[childClass myMethod]; // "Child Class"
MyParentClass *polymorphism = [[MyChildClass alloc] init];
[polymorphism myMethod]; // "Child Class"
위의 예제에서는 MyParentClass와 MyChildClass 클래스를 사용하여 다형성을 구현하였습니다. 마지막 줄에서는 MyParentClass 타입의 변수에 MyChildClass 객체를 할당하여 myMethod 메소드를 호출하였습니다. 이 때, 결과는 "Child Class"가 출력됩니다.