지난 달 애플이 자사의 새로운 신제품을 발표했다. Apple Special Event January 2010 행사에서 스티브잡스는 직접 자사의 아이패드(iPad)라는 신제품을 들고 나와 키노트 연설을 했다. 언제나 그렇듯이 잡스만의 간결한 키노트와 사람들의 호기심을 자극할만한 시연은 많은 이야깃거리를 제공해 줬다. 더욱이 한국 시간으로 새벽 3시에 시작되는 발표를 보기 위해 많은 사람들이 잠을 설치기도 했다.
<표 1> 아이패드 기능과 용량별 가격
생긴 모양과 기능은 아이팟 터치·아이폰 단말의 스크린을 9.7인치 LED 화면으로 키워놓았다고 생각하면 이해가 잘 된다. 앱스토어를 통해 애플리케이션을 다운로드 받을 수 있고, 전자책 등을 구입할 수도 있다고 한다. 최근 많은 이슈가 되고 있는 e-book 시장에 새로운 강자가 등장했다고 할 수 있겠다.
<화면 1> 아이패드
아이패드 제품과 함께 프로그램 개발을 위한 아이폰 SDK 3.2 베타도 같이 발표됐다. 필자가 실제로 설치해 보니 Xcode로 기존의 아이폰 애플리케이션을 개발해 온 개발자라면 문제없이 개발할 수 있을 것 같았다.
이번 호에서는 그동안 사용해 왔던 IB(Interface Builder)라는 UI 디자인 툴을 이용하지 않고 직접 프로그램을 만드는 방법을 알아볼 것이다. 사실 IB는 화면을 디자인해 주는 것 이외에도 내부적으로는 각각의 Object들에 대해 필요한 Delegate나 View 템플릿 등을 제공해 준다. 하지만 다수의 개발자들은 불필요하다고 생각되는 템플릿 코드(dummy code)나 세밀하게 메모리를 관리해야 할 경우 IB에서 생산하는 것을 거추장스러워 하기도 한다. 그래서 처음부터 다시 만드는 수고를 마다하지 않는다. MS 윈도우 프로그램으로 생각하면 MFC와 같이 비주얼 스튜디오에서 제공해 주는 라이브러리 함수 등을 마다하고 WIN32 API를 직접 사용하는 것을 선호하는 개발자가 많다는 것에 비유될 수 있다.
결과는 툴을 사용하든 코드로 직접 구현하든 같다. 필자는 귀찮은 UI 작업 등이나 빠른 결과물을 얻을 수 있다는 측면에서 IB를 사용하는 것도 그리 나쁘지는 않은 것 같다. 하지만 게임이나 대용량의 데이터를 사용하는 일에는 적합하지 않을 수도 있다.
이제부터 IB 없이 코드로 애플리케이션을 구현하는 방법에 대해 알아보자.
IB 없이 만드는 View와 Object들
가장 먼저 할 일은 새로운 View를 ‘Windows-based Appli cation’으로 만드는 것이다. 불필요한 코드가 가장 적은 템플릿이기 때문이다. 프로젝트의 이름은 ‘withoutIB’로 작성하겠다.
<화면 2> Windows-based Application으로 View 생성
<화면 3>은 최초로 보여지는 View이다. 여기서 우리는 IB를 사용하지 않을 것이므로, ‘Resources-iPhone | MainWindow. xib’를 삭제한다. 삭제할 때는 <화면 4>와 같이 3개의 버튼 중 ‘Also Move to Trash’를 선택해야 프로젝트에서 완전히 지워지고 해당 파일도 쓰레기통으로 이동된다.
<화면 3> 최초로 보여지는 View
<화면 4> Resources-iPhone MainWindows.xlb 삭제
이제부터는 코드를 직접 구현해야 하는데, IB 없이 폼과 각종 Object들을 만들려면 다음과 같은 원리를 염두에 둬야 한다.
1. 최상위 View(버튼, 이미지 등 모든 것을 View라고 부르게 된다)를 하나 만든다(보통 Windows-Based 템플릿으로 만든 윈도우 View를 최상위 View로 사용한다).
2. 초기화 한 최상위 View 위에 원하는 Object를 배치한다.
3. 원하는 이벤트와 동작들을 추가해 코딩한다.
4. Delegat 함수에 적절한 동작을 추가한다.
5. 빌드&디버깅에 들어간다.
이제 IB를 사용하지 않고 프로젝트를 만들고자 할 때 필요한 준비가 끝났다. 코드를 하나하나 살펴보면서 진행하자.
withoutIBAppDelegate.h와 withoutIBAppDelegate.m 파일의 내용을 살펴보자. 이 두 파일은 주로 내부적으로 동작하는 애플리케이션 간의 동작을 제어한다고 생각하면 된다. 그리고 화면에 버튼 등을 실제로 보여주기 위한 소스 코드는 별도로 추가하겠다. 각 코드는 간단한 주석과 함께 살펴보자.
<리스트 1> withoutIBAppDelegate.h #import <UIKit/UIKit.h> @class withoutIBViewController.; // ViewController를 사용하기 위해 클래스 이름을 추가한다. @interface withoutIBAppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; withoutIBViewController *viewController; } // withoutIBAppDelegate 클래스를 만든다. 애플리케이션을 대표하기 위해 <UIApplicationDelegate>를 상속한다. @property (nonatomic, retain) IBOutlet UIWindow *window; // 앞으로 윈도우 View에서 이벤트 및 함수 처리를 할 것이다. @property (nonatomic, retain) withoutIBViewController *viewController; @end |
<리스트 2> withoutIBAppDelegate.m #import "withoutIBAppDelegate.h""withoutIBViewController.h" @implementation withoutIBAppDelegate @synthesize window; @synthesize viewController; (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; viewController = [[withoutIBViewController alloc] init]; // Override point for customization after app launch [window addSubview:viewController.view]; [window makeKeyAndVisible]; return YES } (void)dealloc { //소멸자에 해당되는 함수로 리소스를 정리해 주는 역할을 한다. [viewController release]; [window release]; [super dealloc]; } |
또 실행을 위해 main.c 소스를 <리스트 3>과 같이 수정해야 한다. UIApplicationMain 함수의 네 번째 인자로 withoutView Delegate를 추가한다.
<리스트 3> main.c int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, @"withoutViewDelegate"); [pool release]; return retVal; } |
이제 화면에 보여질 Object들을 담을 withoutIBViewCon troller를 만들 차례이다. <화면 5>와 같이 ‘UIViewController subclass’ 템플릿을 선택한다. IB를 사용하지 않을 예정이므로 Options에서 ‘With XIB for user interface’ 체크박스에 체크하지 않는다.
<화면 5> UIViewController subclass 템플릿 선택
새로 만들어진 파일에는 IB를 이용하지 않기 때문에 별다른 내용이 없다. 이 곳에 개발에 필요한 Object들을 수동으로 만들면 된다. withoutIBViewController.m 파일에 Delegate 함수들이 주석 처리돼서 템플릿 형태로 만들어진다.
<리스트 4> withoutIBViewController.h #import <UIKit/UIKit.h> @interface withoutIBViewController : UIViewController { } @end |
<리스트 5> withoutIBViewConroller.m #import "withoutIBViewController.h" @implementation withoutIBViewController // Implement loadView to create a view hierarchy programmatically, without using a nib. (void)loadView { // 이곳에 처음 View가 로드될 때 필요한 함수들을 작성한다. } / *Implement viewDidLoad to do additional setup after loading the view, typically from a nib. (void)viewDidLoad { [super viewDidLoad];z } /*Override to allow orientations other than the default portrait orientation. (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } (void)dealloc { [super dealloc]; } @end |
버튼 추가하기
먼저 버튼을 놓아둘 배경이 될 UIView를 loadView 함수에서 만든다. loadView 함수는 IB를 사용하지 않을 때(nib 파일을 사용하지 않을 경우) 불려지는 함수이다.
<리스트 6> loadView - (void)loadView { // Create the main view UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; self.view = contentView; [contentView release]; ... |
다음으로 버튼을 만드는 코드를 살펴보자.
<리스트 7> 버튼 생성 UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // 버튼 타입을 결정한다. button.frame = CGRectMake(160.0f, 0.0f, 160.0f, 160.0f); //버튼이 그려질 위치를 표시한다. button.center = CGPointMake(160.0f, 240.0f); //버튼의 가운데 지점을 표시한다. [button setTitle:@"버튼테스트" forState:UIControlStateNormal]; // 버튼의 타이틀을 표시한다. [button addTarget:self action:@selector(showinfo) forControlEvents:UIControlEventTouchUpInside]; // 버튼을 터치했을 경우 동작하는 이벤트를 showinfo라는 이름으로 사용하기로 했다. button.hidden = NO; // 버튼이 로딩될 때 보여지도록 한다. button.alpha = 1.0f; // 버튼의 투명도를 결정한다. 1.0~0.1까지 정할 수 있다. [self.view addSubview:button]; // 메인 View에 버튼을 붙인다. |
이것을 컴파일하고 실행해 보면 <화면 6>과 같은 버튼이 생성될 것이다.
<화면 6> 버튼 생성
앞에서 버튼이 눌려졌을 경우 동작하는 함수 이름을 showinfo로 만들기로 했다. 이곳에 여러 가지 동작을 넣을 수 있는데, 필자는 AlertView를 사용해 보기로 했다. <리스트 8>과 같이 함수를 만들어 둔다.
<리스트 8> AlertView 생성 -(void) showinfo { UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"IB없이 만들기" message:@"Alert View 입니다."; delegate:self cancelButtonTitle:@"취소" otherButtonTitles:@"확인",nil [alert show]; [alert release]; } |
실행한 결과는 <화면 6>의 오른쪽 이미지와 같다.
네비게이션 바 만들기
네비게이션 바(navigation Bar)는 화면(view)의 최상위에 위치한 형태다. 일반적으로 거의 모든 아이폰 프로그램에서 사용되는 것을 볼 수 있다. UIView는 버튼을 만들 때 사용한 것을 재활용한다.
네비게이션 바는 여러 View들을 서로 전환하면서 사용하고자 할 때 주로 사용된다. 이 때문에 몇 가지 형태의 스타일 버튼이 같이 쓰이게 되는데, 이번 강좌에서는 모양을 확인하는 차원에서 형태만 보자.
먼저 네비게이션 바를 그린다. 다른 Object들과 유사한 방식으로 다음과 같이 initWithFram으로 표시될 좌표를 설정한다.
UINavigationBar *navibar = [[UINavigationBar alloc] initWithFrame: CGRectMake(0.0, 0.0, 320.0, 40.0)];
이렇게 만든 네비게이션 바에 필요한 타이틀이나 버튼을 붙여보자. 여러 가지 방식으로 원하는 아이템을 붙일 수 있다. 다음은 타이틀을 붙일 경우의 코드이다.
UINavigationItem *naviItem = [[UINavigationItem alloc] initWithTitle:@"네비게이션바 타이틀"];
[navibar pushNavigationItem:naviItem];
이 코드는 단순히 타이틀만을 표시해 본 것이고, 타이틀 좌·우에 버튼을 붙일 경우는 다음과 같은 방식으로 코드를 naviItem 아래에 붙이면 된다.
[navibar showLeftButton:@"오른쪽" withStyle: 0
rightButton:@"왼쪽" withStyle: 0];
일반적인 형태로 네비게이션 바에 방향 버튼만을 추가할 경우는 다음과 같이 하면 된다.
[navibar showButtonsWithLeftTitle:@"Back" rightTitle:nil
leftBack: YES ];
// 왼쪽에 방향표시 버튼을 붙일 경우
<화면 7> 타이블 좌우에 버튼 생성
텍스트 박스 만들기
다음으로는 사용자의 입력을 받아서 처리하는 텍스트 박스를 만들어보자. 다른 Object 들과 마찬가지로 좌표를 생성하고 해당되는 View를 추가하면 된다.
<리스트 9> 텍스트 박스 생성 // 텍스트 박스를 생성하는 예제 UITextField *txtbox= [[UITextField alloc] initWithFrame:CGRectMake(10.0, 10.0, 300.0, 30.0)]; [txtbox setBorderStyle:UITextBorderStyleRoundedRect]; [self.view addSubview:txtbox]; |
<화면8> 텍스트 박스 생성
텍스트 박스에서 자주 사용되는 프로퍼티를 살펴보자. 다음과 같은 항목들이 있다.
@property(nonatomic,copy) NSString *text;
@property(nonatomic,retain) UIColor *textColor;
@property(nonatomic,retain) UIFont *font;
@property(nonatomic) UITextAlignment textAlignment;
@property(nonatomic) UITextBorderStyle borderStyle;
@property(nonatomic,copy) NSString *placeholder;
@property(nonatomic) BOOL clearsOnBeginEditing;
@property(nonatomic) BOOL adjustsFontSizeToFitWidth
@property(nonatomic) CGFloat minimumFontSize;
@property(nonatomic,assign) id<UITextFieldDelegate> delegate;
@property(nonatomic,retain) UIImage *background;
@property(nonatomic,retain) UIImage *disabledBackground;
@property(nonatomic,readonly,getter=isEditing) BOOL editing;
다음과 같은 방법으로 사용하면 된다.
1. 기본적으로 표시되는 텍스트를 표현할 때
[txtbox setText:@ “기본적으로 표시되는 텍스트”];
2. 텍스트의 색상을 빨간색으로 변경할 때
[txtbox setTextColor:[UIColor redColor]];
3. 텍스트의 폰트를 다른 폰트로 변경할 때
[txtbox setFont:[UIFont fontWithName:@“DBLCDTempBlack” size:24.0]];
4. 텍스트를 가운데 정렬하고 자할 때
[txtbox setTextAlignment:UITextAlignmentCenter];
이와 같은 방법으로 여러 가지 항목들을 조절할 수 있다.
라벨 만들기
라벨도 아이폰 UI에서 가장 많이 사용되는 것 중 하나일 것이다. 텍스트 박스와 유사하게 다음과 같은 코드로 만들 수 있다.
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10.0,60.0, 300.0, 30.0)]; [label setText:@"라벨입니다."]; [self.view addSubview:label];
<화면 9> 라벨 생성
<주요 프로퍼티>
@property(nonatomic,copy) NSString *text;
@property(nonatomic,retain) UIFont *font;
@property(nonatomic,retain) UIColor *testColor;
@property(nonatomic,retain) UIColor *shadowColor;
@property(nonatomic) CGSize shadowOffset;
@property(nonatomic) UITextAlignment textAlignment;
@property(nonatomic) UILineBreakMode lindBreakMode;
이미지 View 만들기
자주 쓰이는 기능 중의 하나인 파일로부터 이미지를 로드하는 방법을 알아보자.
이미지를 로드하기 위해서는 UIImage라는 Object를 사용한다. 이 과정을 설명하기 위해 glass_numbers.png라는 이미지 파일을 프로젝트에 추가했다. 다음과 같은 코드가 사용된다.
UIImageView *imgload = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imgload initWithImage:[UIImage imageNamed: @"glass_numbers_0.png"]];
self.view addSubview:imgload];
<화면 10> 이미지 View 생성
<주요 프로퍼티>
@property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled;
@property(nonatomic) NSInteger tag;
@property(nonatomic,readonly,retain) CALayer *layer;
@property(nonatomic) CGRect frame;
@property(nonatomic) CGRect bounds;
@property(nonatomic) CGPoint center;
@property(nonatomic) CGAffineTransform transform;
@property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled;
@property(nonatomic,getter=isExclusiveTouch) BOOL exclusiveTouch;
<사용 예>
1. 이미지를 화면의 가운데로 오게 할 때
[imgload setCenter:CGPointMake(160.0, 240.0)];
2. 새로운 이미지를 불러올 때
[imgload setImage:[UIImage imageNamed:@"glass_numbers_0 .png"]];
3. URL을 입력해 이미지를 불러오고자 할 때
① http 프로토콜을 사용해 이미지를 불러올 때는 다음과 같이 먼저 이미지가 포함된 URL을 만들고 ImageWithData라는 프로퍼티를 사용해 해당 이미지를 가져오게 된다.
NSURL *aTempURL = [NSURL
URLWithString:@"https://mail.google.com/mail/help/images /logo2.gif"]; NSData *aTempData = [NSData dataWithContentsOf URL:aTempURL];
// 이렇게 하면 aTempData에 주소의 이미지 데이터가 들어간다 .
[imgload setImage:[UIImage imageWithData:aTempData]];
② 이미지를 회전시킬 때는 setTransform이라는 프로퍼티를 사용하는데, 원하는 방향과 좌표를 다음과 같은 방식으로 지정해 주면 이미지를 회전할 수 있다.
[imgload setTransform:CGAffineTransformMake(10.0, 5.0, 0.0, 5.0, 10.0, 10.0)];
<화면 11> 이미지 회전
WebView 만들기
이름 그대로 웹 브라우저와 같은 역할을 하는 Object이다. URL을 그대로 가져와서 사용하려면 loadRequest라는 함수를 사용한다. 그리고 URLWithString이라는 애트리뷰트를 통해 URL을 입력한다. 다음은 google.com에 접속하는 경우의 예이다.
UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]]]; [self.view addSubview:webView];
<화면 12> WebView 생성
<주요 프로퍼티>
@property(nonatomic,readonly,retain) NSURLRequest *request;
@property(nonatomic,readonly,getter=canGoBack) BOOL canGoBack;
@property(nonatomic,readonly,getter=canGoForward) BOOL canGoForward;
@property(nonatomic,readonly,getter=isLoading) BOOL loading;
@property(nonatomic) BOOL scalesPageToFit;
<사용 예>
1. 웹 페이지 로딩 애니메이션 보이기
아이폰에서 사파리 브라우저로 웹 페이지를 로딩할 때 페이지가 로드되는 중에 상단에 로딩 애니메이션이 동작하는 것을 볼 수 있다. 이것은 다음과 같이 networkActivityIndicatorVisible이라는 애트리뷰트를 사용하면 된다.
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
<화면 13> 웹 페이지 로딩 애니메이션 생성
이 때 애니메이션을 종료하려면 UIWebView의 Delegate를 사용해야 한다. Delegate는 일종의 콜백 함수 역할을 하는 Obj-C의 컨트롤러이다. 따라서 콜백 함수처럼 해당 Object에 전달되는 이벤트나 메시지 등을 모니터링하고 있다가 반응을 하게 된다.
이번 예에서는 Delegate 함수를 사용했다.
-(void)webViewDidFinishLoad:(UIWebView *)webView;
withoutIBViewController.h에서 다음과 같은 Delegate 컨트롤러를 확인한다. <UIWebViewDelegate>가 있는지 보자.
#import <UIKit/UIKit.h>
@interface withoutIBViewController : UIViewController <UIWebViewDelegate> { }
@end
다음으로 실제로 사용될 곳에 Delegate 함수를 사용하겠다고 알려준다. WebView의 경우는 다음과 같이 쓰면 된다.
webView.delegate = self
이제 WebView에서 발생되는 메시지를 기다렸다가 Delegate가 해당되는 동작을 하면 된다. 다음과 같이 하면 google.com 페이지가 열리는 동안 로딩 애니메이션을 볼 수 있다.
<리스트 10> WebView 생성 UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]]]; UIApplication* app = [UIApplication sharedApplication]; app.networkActivityIndicatorVisible = YES; //로딩애니메이션이 동작한다 . webView.delegate = self; [self.view addSubview:webView]; |
웹 페이지가 모두 로딩되면 WebView Delegate를 사용해 다음과 같이 애니메이션을 중단해 준다.
-(void)webViewDidFinishLoad:(UIWebView *)webView {UIApplication sharedApplication].networkActivityIndicatorVisible = NO; }
MapView만들기
MapView는 구글 맵을 활용해서 지도를 볼 수 있는 Object이다. <화면 14>와 같이 지도를 핀 과 함께 표시할 때 사용한다. 지도를 표시하기 위해서 MapKit를 import하고 MapKit. framework을 포함하도록 한다 .
#import <MapKit/MapKit.h>
MapView는 MK로 시작되는 클래스를 사용해서 클래스 포인터를 만든다.
MKMapView *mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
<화면 14> MapView 생성
<주요 프로퍼티>
@property (nonatomic) MKMapType mapType;
@property (nonatomic) MKCoordinateRegion region;
@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
@property(nonatomic,getter=isZoomEnabled) BOOL zoomEnabled;
@property(nonatomic,getter=isScrollEnabled) BOOL scrollEnabled;
@property (nonatomic) BOOL showsUserLocation;
@property (nonatomic,readonly) MKUserLocation *userLocation;
@property (nonatomic,readonly,getter=isUserLocationVisible) BOOL userLocationVisible;
@property (nonatomic, readonly) NSArray *annotations;
@property (nonatomic, readonly) CGRect annotationVisibleRect;
<사용 예>
1. 지도를 보여줄 때
먼저 withoutIBViewController.h에 MapKit을 포함하고, MapKit.framework을 가져온 후 Delegate 컨트롤러를 추가한다. 2개 이상의 Delegate 컨트롤러를 추가할 때는 다음과 같이 ‘,’로 구분한다.
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface withoutIBViewController : UIViewController <UIWebViewDelegate, MKMapViewDelegate> {
}
@end
withoutIBViewController.m 파일에 MapView를 생성한다. 다른 Object를 추가할 때와 유사하게 쓰면 된다.
MKMapView *mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
2. 사용자 위치를 표시할 때
mapView.showsUserLocation = YES
3. 지도의 확대·축소가 가능하도록 할 때
mapView.zoomEnabled = YES
4,지도에 스크롤이 가능하도록 할 때
mapView.scrollEnabled = YES
5, 화면에 표시할 지도의 형태를 정의할 때
mapType이라는 프로퍼티를 쓰는데, 종류는 3가지다.
mapView.mapType = MKMapTypeStandard
지도의 타입은 다음과 같다.
enum{
MKMapTypeStandard = 0,
MKMapTypeSatellite,
MKMapTypeHybrid
};
typedef NSUInteger MKMapType;<화면 15>는 새틀라이트(Satellite) 타입과 하이브리드 타입으로 지도를 표시해 본 것이다 .
Delegate를 사용하기 위해서 다음의 코드를 추가한다.
mapView.delegate = self;
이번 호에서는 IB를 사용하지 않고 코드만으로 각종 View와 Object들을 만들어 봤다. IB를 사용하지 않고 UI를 구성하는 과정을 소개해서 그런지 그림이 별로 없는 강좌가 된 듯 하다. 다음 호 부터는 애플 앱스토어에서 판매중인 애플리케이션에서 구현된 프로그램 기법을 분석해 보고 실제 구현도 해보자.
'PlayPhone' 카테고리의 다른 글
모토로라 안드로이드폰 '모토로이' 사용기(?) (1) | 2010.03.17 |
---|---|
사진을 블링블링하게 하는 마술 'More Noel' (0) | 2010.03.17 |
윈도우 폰 7 , 스마트폰 시장에 도전장을 던지다 (0) | 2010.03.16 |
통합LGT, 스마트폰/일반폰 넘나드는 올해 전략 (1) | 2010.03.16 |
윈도우폰 7을 가장 빨리 만나는 방법 (0) | 2010.03.16 |