개발과삶2008/12/15 23:12
지난 글에서 AppDelegate가 비즈니스 로직을 담당하고 View단의 클래스는 모두 AppDelegate를 통해서 접근 가능한 패턴을 설명해드렸습니다.
이번에는 지난 글에 보충하여 View 클래스를 어떻게 관리하고 접근하는 지에 대해 설명하고자 합니다.

대부분의 View는 IB로 만든 xib형태의 파일과 이를 참조하여 초기화하는 View Controller로 구성되어 있습니다. 이들 View Controller는 한 번 초기화한 이후로 재활용하는 경우가 많아서 객체의 멤버 변수로 저장하고 재사용하게 되는데 이들 변수의 위치가 고민스러웠습니다.

어디에든 이들 View Controller를 접근할 수 있다면 많이 편하리라 생각이 들어서 View만 전담하는 클래스를 작성하는 패턴을 생각해봤습니다.
ViewFactory라는 클래스는 AppDelegate의 applicationDidFinishLaunching 정도에서 초기화되고 AppDelegate를 통하여 접근하도록 하여 어디에서나 ViewFactory를 이용하여 View Controller를 초기화하고 재사용할 수 있도록 합니다.

대략 아래와 같은 형태가 될 것입니다.

AppDelegate.h
@interface AppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    id viewFactory;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) id viewFactory;

@end

AppDelegate.m
@interface AppDelegate()
- (void)initializeObjects;
@end


@implementation AppDelegate

@synthesize window;
@synthesize viewFactory;

- (void)applicationDidFinishLaunching:(UIApplication *)application {   
    [window makeKeyAndVisible];
    [self initializeObjects];
}

- (void)dealloc {
    [viewFactory release];
    [window release];
    [super dealloc];
}

- (void)initializeObjects {
    viewFactory = [[ViewFactory alloc] init];
}


ViewFactory 클래스는 해당 App에 사용되는 대부분의 View Controller 및 아이콘 등을 초기화하고 필요할 때마다 가져다 쓸 수 있도록 하게 됩니다.

여기서 최초 진입점은 AppDelegate이고 AppDelegate에서 얻어온 ViewFactory 인스턴스를 통하여 원하는 View Controller의 인스턴스를 가져오게 됩니다. 그리고, 가급적이면 명시적인 클래스를 사용하지 않고 id를 사용하여 코딩시 import 문제를 줄일 수 있도록 합니다. 단, 성능 문제가 있으므로 적절하게 조절하여 사용하시면 되겠습니다.

개발하면서 정리하는 과정이라 불완전할 수 있습니다. iPhone 개발자 여러분들의 피드백도 기다리겠습니다.
저작자 표시 비영리 변경 금지
Posted by 종이비행기
개발과삶2008/11/13 23:48
iPhone project diagram

iPhone project 패턴 도식


제목은 거창하게 시작하긴 했지만 내용은 모두들 알고 계시는 것일 수 있습니다. 저의 개인적인 개발 경험(뭐, 길지는 않습니다^^)을 통해 얻게된 지식을 정리하는 정도로 많이 부족할 수 있으니 눈을 버리고 시간을 버리기 전에 미리 양해를 구햡니다.

iPhone은 Mac OSX용 애플리케이션을 개발용 XCode에 iPhone SDK를 이용하는 방식으로 개발을 하게 되어있습니다. 결국, Objective-C 언어를 바탕으로 Cocoa라는 UI 관련 프레임워크를 iPhone에 맞게 변형시킨 Cocoa Touch라는 프레임워크를 제공하게 됩니다. 이는 기존의 Cocoa를 대부분 계승한다는 의미로 개발 패턴도 기존과 동일하다는 점을 보여준다 하겠습니다.

최근 수년간 웹개발에서 논의되어 왔고 이제는 어느정도 정착단계에 들어간 MVC 패턴은 Mac 및 윈도우즈 애플리케이션에서는 이미 많이 사용되어 왔었습니다. 별로 새로울 것도 없는 개발 패턴이기는 하지만 여전히 이를 잘 지키기는 쉽지가 않습니다.

iPhone 개발을 시작한지는 얼마되지 않았지만 개발하면서 나름대로 iPhone에 맞게 개발 패턴을 만들어두고 그에 맞춰서 개발하는 것이 생산성 향상에 도움이 될 듯 하여 글로서 정리해보려 합니다. 아직 경험 부족으로 부족한 부분이 있겠지만 개인적인 용도로 사용하고자 정리하는 것이라 또 한 번 양해를 우선 구합니다.

MVC?
약자를 풀어보면 Model - View - Controller가 되겠습니다. 귀에 딱지가 앉을 정도로 많이 들으셨을 것입니다. iPhone 개발을 하면서 느끼는 것이지만 Model은 어느정도 명확하게 구분해서 개발을 하게 됩니다. 그러나, View와 Controller는 경계가 다소 모호하더군요. 여기서 말하는 뷰는 xib 또는 nib 확장자를 가진 IB(Interface Builder) 파일이 아닙니다. 그걸 지원하기 위한 또는 UI를 그리기 위한 .m 확장자를 가진 소스를 이야기하는 것입니다.

UITableView를 예로 들겠습니다. View 소스들은 보통 UIViewController를 상속해서 사용하게 됩니다. 여기에 UITableView를 얹었다(IB를 사용하든 직접 코딩을 하든)고 하겠습니다. 그러면, UITableViewDelegate, UITableViewDatasource 프로토콜을 구현하게 됩니다. 그러면, 테이블을 출력하기 위해 데이터를 어디에서 가져오게 될까요? Controller에서 데이터를 만들어서 Model 객체(들)을 테이블(View)로 전달해야하겠죠?
그런데, 여기서 문제가 생깁니다. 보통, UIViewController를 구현한 클래스(명칭이 애매해서 그냥 [View소스]라고 하겠습니다)에서 그 작업을 해버립니다. 왜냐하면, 현재 [View소스]에서 바로 접근할 수 있는 IBOutlet 변수들이 존재하고 IBAction으로 호출된 위치가 바로 [View소스]이기 때문입니다. 귀차니즘 또는 처음에는 별 기능없이 액션을 처리한다고 생각하게 되지만 개발이 진행되면서 그 코드량이 점점 많아지게 됩니다. 결국, [View소스]에 방대한(?) 비즈니스 로직이 생겨버리게 됩니다. 단적인 예이기는 하지만, 저의 경우는 이런 문제가 가끔 생기는 것을 보게 됩니다.

그래서 위와 같은 그림을 그리게 되었습니다. 그림을 설명하자면...

xib와 관련된 View 클래스가 하나씩 존재합니다.
하나의 MainWindow.xib는 하나의 MainViewCtrl을 갖게 됩니다. 물론, XCode에서 Window Based project로 생성하면 모든 delegate는 {PRJ NAME}AppDelegate에서 처리하게 됩니다. 그러나, 여기서부터 문제가 시작이 됩니다. 아, 우선 기본적으로 생성되는 소스를 보면 설명한 것처럼  AppDelegate 앞에 프로젝트명이 붙습니다. 이거 별로 보기가 좋지 않아서 리네이밍을 한후 IB에서 MainWindow.xib를 열어서 App Delegate 클래스명도 바꿔줍니다. 그러면 별 문제없이 단순한 AppDelegate 클래스명을 이용할 수 있습니다. AppDelegate *app = [[UIApplication sharedApplication] delegate];처럼 클래스명을 단순하게 하면 언제 어디서든 깔끔한 코딩으로 메소드를 호출할 수 있으니까요.
AppDelegate에서 문제가 생기는 이유는 여기에 비즈니스로직, UI 처리 액션, UI 관련 IBOutlet 변수 등등... 코딩의 대부분을 여기서 하는 불행한 사태가 발생할수도 있습니다. 그래서, 저는 AppDelegate에는 비즈니스로직만 담으려 노력하고 나머지는 MainViewCtrl이라는 클래스를 하나 더 만들었습니다. 여기에서 실행하면서 처음으로 접하게 되는 화면인 Root View(or Main View) UI를 처리하는 액션이나 IBOutlet 변수를 정의 및 실행하도록 하고 있습니다. 물론, 새로 만든 MainViewCtrl을 정상적으로 동작시키기 위해서는 UIApplication과 관련된 delegate 이외에는 모두 MainViewCtrl로 향하도록 선연결 작업을 해줘야 합니다(물론, IB에서). 또한, 대부분 Window 앞에 UIViewController를 하나 얹게 되는데 여기 클래스명을 MainViewCtrl로 바꾸는 것도 잊으시면 안되고, AppDelegate에서도 MainViewCtrl IBOutlet 변수를 하나 만드셔서 IB에서 MainViewCtrl과 연결을 해둬야 그 변수를 정상적으로 이용 가능하게 됩니다. (예를 들어가면서 설명하면 더 이해가 편하시겠지만 지금은 나름 정리하는 중이라 시간이 나질 않아서 죄송합니다.)

Model 인스턴스 초기화는 AppDelegate를 이용합니다.
iPhone에는 SQLite라는 걸출한 데이터베이스를 내장하고 있습니다(구글 안드로이드로 그렇다죠? 빠르고 간편하고 오픈소스라 그런가 봅니다. 써본 결과로 sqlite 기본 라이브러리로 개발하기엔 불편한 점이 많고 FMDatabase라는 래퍼 라이브러리를 이용하면 JDBC와 유사한 방법으로 사용할 수도 있습니다. 일부 오버헤드는 있겠지만.).
저같이 Productivity나 Entertainment 관련 개발을 좋아하시는 분(게임을 제외한 대부분의 분야가 해당될 수도 있겠죠.)은 벌써 SQLite를 이용해서 개발해 보셨을 것입니다.
일반적으로 데이터베이스를 사용하기 위해서는 접속(Connection)이라는 과정을 거치게 됩니다. SQLite는 로컬 파일을 기반으로 입출력하기 때문에 네트워크 접속과 같은 문제로 접속 부하가 생기지는 않습니다만, 접속을 시도할 때마다 DB 파일을 열어서 스키마와 통계정보를 갖고 있는 딕셔너리를 메모리에 올리는 과정이 발생할 것입니다. 이과정에서 오버헤드가 분명 생기리라 봅니다. 그래서, Apple에서 제공하는 샘플(SQLiteBook)을 보시면 아시겠지만 DB 접속은 AppDelegate에서 1회만 발생합니다.
App이 초기화되는 AppDelegate의 init 메소드에서 접속을 맺고 dealloc에서 접속을 종료하는 형태로 App이 동작하는 동안에는 1회만 접속을 맺도록 하는 것이 좋겠습니다. 그러나, DB 동시 처리를 해야할 경우에는 고려를 해야합니다. iPhone은 동시에 1개의 App만 동작되도록 설계가 되어 있어서 지금 동작하는 App 내에서만 동시 처리가 없다면 문제가 없긴 합니다. 이렇게 1회 접속한 포인터(pointer)는 AppDelegate의 변수에 두고 이를 공유하는 형태로 사용하면 됩니다.
App을 개발할 때 모델 클래스에는 CRUD를 모두 처리할 수 있도록 구현하게 됩니다. CRUD를 처리하기 위해서는 DB 접속 포인터가 필요할 것이고 이걸 AppDelegate에서 지원하는 형태로 하면 편리하게 사용할 수 있습니다.
Factory Pattern처럼 newEmployee와 같은 메소드를 호출하면 DB접속 포인터가 주입된 새로운 모델 인스턴스가 반환되게 되면 DB 접속 포인터를 어떻게 주입해야 하는 가에 대한 고민은 모두 사라지게 될 것입니다. 물론, 데이터의 목록(모델 객체의 목록)을 요청할 경우에도 마찬가지로 AppDelegate를 활용해서 DB 포인터를 가진 상태로 반환하면 됩니다(물론, 반환은 NSArray, NSMutableArray에 담긴 모델 객체 정도겠지요.).

AppDelegate 1개만 사용하는가?
그건 아닙니다. 비즈니스 영역별로 분리해서 클래스를 만들고 이를 미리 초기화 하던지 필요할 때 초기화해서 쓰던지 간에 AppDelegate를 경유하도록하면 됩니다. 어떤 UIViewController 클래스든 AppDelegate *app = [[UIApplication sharedApplication] delegate]; 코드를 이용하여 AppDelegate를 접근할 수 있기 때문에 비즈니스 객체의 메소드 호출 관문으로 활용하면 될 듯 합니다.

참고로, 그림에는 없지만 Icons라는 group은 Resources의 서브 그룹으로 들어가 있어서 각종 Icons을 담아두는 용도로 사용이 됩니다.

현재 개발하고 있는 App은 이 패턴을 따라 개발하고 있습니다. 지속적인 수정 보완이 필요하기에 이 문서도 지속적으로 업데이트할 예정입니다. 부족한 글 끝까지 읽어주셔서 고맙습니다.


저작자 표시 비영리 변경 금지
Posted by 종이비행기