Tag Archives: Framework

XCode 6 Framework 만들기

xcode6_framework_01

XCode 6버전에 들어서면서 손쉽게 만들기 어려웠던 Cocoa Touch Framework 프로젝트를 만들 수 있게 되었습니다. 이 프로젝트의 경우 Target이 Framework가 되며 실제로 빌드를 할 경우 XCode DerivedData 디렉토리안에 빌드된 Framework가 생성됩니다. 이렇게 빌드된 Framework를 당신이 원하는 프로젝트에 드래그&드롭을 하게 되면 간단하게 Framework가 추가되며 안에 있는 기능들을 호출할 수 있게 됩니다.

Framework 만들기

지금부터 간단한 Framework를 제작하고 예제앱에 추가하여 사용하는 방법을 구현해 보도록 하겠습니다. 지금부터 진행할 예제는 하나의 Workspace안에 두개의 프로젝트(Framework, App)로 구현할 것인데 Workspace 없이 별개의 단일 프로젝트를 각각 구현하셔도 괜찮습니다.

xcode6_framework_02

File > New > Workspace를 선택하여 개발을 할 워크스페이스를 하나 만들어줍니다. 저는 HelloFrameworkExample로 만들어보겠습니다.

xcode6_framework_03

그럼 완전 아무것도 없는 빈 IDE가 등장합니다. 다시 File > New > Project 메뉴를 선택하여 프로젝트를 추가해줍니다.

xcode6_framework_04 xcode6_framework_05

스크린샷과 같이 Cocoa Touch Framework를 선택하고 Next를 눌러 진행합니다. 다음은 프로젝트의 기본 설정을 하게 되는데 적당히 본인의 환경에 맞춰주시면 됩니다. 언어의 경우 Swift, Objective-C 둘중에 하나를 선택할 수 있는데 저는 일단 Objective-C로 진행을 하도록 하겠습니다.xcode6_framework_06

마지막에 소스가 저장될 디렉토리를 설정하는데 하단에 Add to에 현재 추가할 Workspace를 선택해주어야 작업할 워크스페이스에 추가됩니다. Git Source Control은 예제 프로젝트이므로 사용하지 않을 생각입니다.xcode6_framework_07

생성된 Cocoa Touch Framework의 디렉토리 구조도는 위와 같습니다. Framework Import시에 가장 기본으로 불려질 HelloFramework.h와 프로젝트 설정이 담겨있는 Info.plist와 테스트 코드들이 담겨있습니다.

소스파일이 아닌 프로젝트 자체를 선택했을때 오른쪽에 뜨는 프로젝트 설정에 주목할 부분은 Deployment Target 설정입니다. Framework가 갖는 특성이 다양한 환경에서 실행되어야 하는 라이브러리라고 볼 때 더 많은 환경에서 구동되도록 구현하는것은 당연해 보입니다. Deployment Target의 경우 실행 가능한 가장 낮은 버전을 설정하게 되는데요. 현재 시점에서 선택할 수 있는 합리적인 버전은 5.1.1이지 않나 생각합니다.

2015년 2월부터 Apple에서는 앱스토어에 등록하는 모든 앱이 64비트를 지원할것을 강제화 했습니다. Framework가 32비트와 64비트를 동시에 지원할 수 있는 가장낮은 Deployment Target은 5.1.1입니다. 실제로 더 낮은 버전을 설정하면 다음과 같은 경고 메시지를 볼 수 있습니다.

xcode6_framework_08

위의 Deployment Target 설정과 연계하여 Build Setting에 대해서도 확인해 봅시다.

xcode6_framework_09

여기서 주목해야 하는 설정은 Architectures와 Valid Architectures 설정입니다. 보면 32비트 아키텍쳐인 armv7/armv7s와 64비트 아키텍쳐인 arm64를 모두 지원하는것을 알 수 있습니다. 그 다음으로 확인해야 하는 부분은 Build Active Architecture Only 설정입니다. 기본 설정은 Debug에 한해 Yes로 되어있습니다만 이렇게 되어있을 경우 Simulator 환경에서는 i386으로만 빌드가 됩니다. 하지만 당신의 환경이 x86_64 아키텍쳐 환경을 필요로 할 수 있으므로 No로 바꾸어 가능한 모든 환경에서 동작하도록 빌드를 합시다.

xcode6_framework_10

이제 간단한 예제 API를 만들어봅시다. File > New > File 메뉴를 선택하거나 마우스 우클릭 후 Add File을 선택하여 Cocoa Touch Class를 추가합니다.

xcode6_framework_11

Person이라는 클래스를 추가해보겠습니다. IDE에 새로운 두개의 파일(.h/.m)이 생성된것을 확인할 수 있습니다. 다음과 같이 간단한 데모 코딩을 해보겠습니다.

#import <Foundation/Foundation.h>

@interface Person : NSObject

- (instancetype)initWithName:(NSString *)name age:(int)age;
- (void)info;

@end
#import "Person.h"

@interface Person ()

@property(strong, nonatomic) NSString *name;
@property(nonatomic) int age;

@end

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(int)age
{
    if(self = [super init]) {
        self.name = name;
        self.age = age;
    }
    return self;
}

- (void)info
{
    NSLog(@"The person's name is %@ and his age is %d", self.name, self.age);
}

@end

위의 코드에 대한 내용은 굳이 설명하지 않아도 괜찮을 것이라 생각하고 패스하겠습니다^^ 하지만 초기화후에 info를 호출하여 NSLog로 값들을 출력한다는것만 기억해두도록 합시다.

xcode6_framework_12이렇게 추가된 클래스가 Framework 외부의 프로젝트에서 접근가능해야 할 경우 위와 같은 설정이 필요합니다. 클래스의 헤더파일을 선택하면 (위의 예시는 Person.h) 오른편에 Target Membership란이 보이는데 Framework와 Test가 보입니다. Framework 오른편에 Visibility 설정을 할 수 있습니다. Public으로 바꿔주어야 이 Framework를 사용하는 다른 프로젝트들이 자유롭게 이 클래스를 사용 할 수 있습니다. 기본적으로 Project로 되어있는데 Project의 경우 해당 프로젝트 이내(여기서는 Framework 자기 자신)에서만 접근이 가능하며 Private는 프로덕트에는 포함이 되지만 사용되기 원하지 않는 개발단계의 클래스들을 지정할 때 사용합니다.

마지막으로 HelloFramework.h 헤더파일에 추가한 클래스를 Import 선언해 줍니다. 여기에 선언하는 모든 클래스는 이 Framework를 Import하였다면 자유롭게 사용할 수 있게 됩니다. Framework 내부에서만 사용될 클래스는 여기에 선언하지 않도록 합니다.

#import <UIKit/UIKit.h>

//! Project version number for HelloFramework.
FOUNDATION_EXPORT double HelloFrameworkVersionNumber;

//! Project version string for HelloFramework.
FOUNDATION_EXPORT const unsigned char HelloFrameworkVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <HelloFramework/PublicHeader.h>

// 외부에서 자유롭게 접근할 클래스의 헤더들을 다음처럼 선언해 줍니다.
#import <HelloFramework/Person.h>

이것으로 예제 Framework를 만들어 보았습니다. 이제 한번 빌드를 해보도록 하겠습니다. Product > Build 메뉴를 선택합니다. 특별히 잘못한 것이 없다면 오류 없이 빌드가 성공할 것입니다.

xcode6_framework_13

처음에 보면 Products 이하의 파일들은 빨간색으로 표시되고 있습니다. 파일이 없는(Missing) 상태임을 뜻합니다. 빌드를 하면 생길것이라 생각되어 빌드를 해도 변화가 없는것을 확인할 수 있습니다. Products 이하의 파일들은 실제 환경인 Device 환경에서 빌드된 파일들을 표시합니다. 처음에는 기본적으로 Simulator가 선택되어있어 Simulator 빌드가 수행되므로 여기에 표기가 되지 않습니다.

상단의 Build Scheme 설정 옆에 iOS Device / Simulator를 선택할 수 있는 부분이 있습니다. 여기서 iOS Device를 선택합니다. 이후에 다시 Build를 수행하면 Products 이하의 바이너리들이 검정색으로 바뀝니다. 빌드가 성공했군요.xcode6_framework_14

빌드된 .framework파일을 선택하고 오른쪽의 Inspector를 보면 Full Path가 표시됩니다. 마지막에 화살표 아이콘이 있는데 저 아이콘을 누르면 해당 디렉토리로 바로 이동하여 Framework 파일의 위치를 보여줍니다. 이창을 띄워둔 상태로 예제앱을 만들어보도록 하겠습니다.

예제앱 만들기

이번에는 만들어둔 Framework를 활용한 간단한 예제앱을 만들어보도록 하겠습니다. File > New > Project를 선택하여 새로운 프로젝트를 생성합니다. 프로젝트 이름은 HelloApp으로 하겠습니다.

xcode6_framework_15 xcode6_framework_16

간단히 Framework가 동작하는것을 확인하기만 할것이기에 무엇을 선택해도 상관없지만 Single View Application으로 만들어보겠습니다.

xcode6_framework_17

마찬가지로 Add to 설정에서 기존에 만들어둔 Workspace를 재활용 할 수 있도록 설정해 줍니다. 이제 HelloApp도 XCode에 추가되어 작업할 수 있게 되었습니다.

xcode6_framework_18

아까 열어두었던 창의 HelloFramework.framework 파일을 드래그하여 추가해줍니다. 여기서 복사를 하여도 되고 Framework가 계속 변화가 이루어지는 상황이라면 Reference만 추가해놓고 Framework의 업데이트가 바로 적용되도록 할 수도 있습니다. 이제 Framework를 Import하여 사용해 보도록 하겠습니다. 첫번째 뷰인 ViewController.m에 추가해보겠습니다.

#import "ViewController.h"
#import <HelloFramework/HelloFramework.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // Framework의 Person 클래스를 사용
    Person *person = [[Person alloc] initWithName:@"Hello" age:19];
    [person info];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Person클래스의 info메소드를 호출하게 되면 NSLog를 통해 이름과 나이를 출력하게 만들어져있는것을 이전에 확인하였습니다. 정말로 제대로 호출이 되는지 실행을 해보겠습니다.

xcode6_framework_19

실행전에 Build Scheme과 적당한 실행 디바이스가 선택되어있는지 확인하고 빌드하시기 바랍니다. 저는 일단 심뮬레이터에서 구동해보겠습니다.

xcode6_framework_20

원하는 값이 정상적으로 출력이 되었군요^^

Spring .NET을 소개합니다.

사용자 삽입 이미지

 

 

 

 

 

자바를 하시는 분들은 다들 아실만한 스프링 프레임워크가 있습니다.  C#을 하다 보니깐 스프링 프레임워크의 DI(Dependency Injection)이나 AOP(Aspect Oriented Programming)가 그리워 지더군요.

그래서 찾아보니 C#에서 Spring .NET이라는 프레임워크가 존재 하더군요. 확실히 자바로 만들어진 스프링에 비하면 좀 어설픈 느낌이 있지만 그래도 정말 멋지게 구현되었습니다.

그래서 한번 간단하게 정리해 보겠습니다. Spring .NET은 [이곳] 에서 다운 받으실 수 있습니다. 혹은 소스포지의 주소는 [이곳]입니다.

Spring .NET이란?

  • 객체의 라이프사이클을 관리하기 위해 의존성 주입(Dependency Injection)을 사용하는 경량(Lightweight) 컨테이너
  • 엔터프라이즈급 .NET 어플리케이션 개발을 위한 어플리케이션 프레임워크

Core

Spring .NET Framework의 Core에는 IoC(Inversion of Control) Container와 Base Functionality가 포함되어있습니다. 간단한 예제로 다음과 같은 의존성 부패(Dependency Rot) 문제가 있는 소스가 있습니다.

Public class Human
{
    public Money money;
    public Human() { this.money = GET_MONEY_OBJECT(); }
}

Public class Container
{
    public Human Born()
    {
        return new Human();
    }
}

위의 예제에서는 Human은 Money 인스턴스를 생성하는 방법에 종속됩니다. 다른 말로 Money객체의 생성 여부가 Human에게 달려있다는 것이죠. Container 클래스 제작자는 Money가 Human안에서 생성되는지도 혹은 어떤식으로 사용되는지도 모를 수 있습니다. 그렇다면 DI(Dependency Injection)을 통한 제어 역전(IOC Container)을 사용하는 예제를 보여드리겠습니다.

Public class Human
{
    protected Money money;
    public Money pMoney { set { this.money = value; }} 
}

Public class IoCContainer
{
    public Human Born()
    {
        Human human = new Human();
        Money money = new Money();
        human.pMoney = money;
        return human;
    }
}

정확하게 Money 인스턴스는 Born 메서드에서 생성되어 Human에 Set 됩니다. 이로서 Spring에서 말하는 느슨한 결합이 구현됩니다. 다음에서 이를 실제 적용한 예제를 만들어 보도록 합시다.

using System;
using System.Collections.Generic;
using System.Text;

Namespace HelloApp
{
    class Hello
    {
        public string sayHello(string name)
        {
            return “안녕하세요? “ + name + “씨!”;
        }
    }
}
<?xml version=“1.0” encoding=“utf-8” ?>
<configuration>
    <spring>
        <objects xmlns=“http://www.springframework.net”>
            <object id=“MyHello” type=“HelloApp.Hello”/>
        </objects>
    </spring>
</configuration>
using System;
using System.Collections.Generic;
using system.Text;
using Spring.Context;
using Spring.Context.Support;

Namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            IapplicationContext ctx = ContextRegistry.GetContext();
            Hello hello = (Hello) ctx.GetObject(“MyHello”);
            string name = “홍길동”;
            string result = hello.sayHello(name);
            Console.WriteLine(result);
        }
    }
}

결과

사용자 삽입 이미지보시면 코드의 형태는 다를지 몰라도 자바의 그것과 매우 똑같습니다. XML에 객체들을 등록하고 컨텍스트에서 GetObject를 통해 존재하는 객체(Bean)을 가져오게 됩니다.

AOP

관점지향프로그래밍(Aspect Oriented Programming)이라는 것은 처음 접했을때는 매우 이해하기 난해한 개념이었습니다. 물론 지금도 제가 완벽히 이해하고 있는지도 모를 실정이지만, Spring에 적용시켜 보면 무언이구나 하는 생각이 들긴 하는군요.

사용자 삽입 이미지기존의 OOP는 객체간 위와 같은 느낌으로 얽혀 있습니다. 각종 include혹은 import를 사용하여 조립한다는 개념으로 서로 연결되죠. 이는 컴파일 순서에서 서로 얽히게 되며 강한 결합을 통해 서로 강하게 묶여있습니다. 그렇기 때문에 객체들의 추가/제거/교체/수정 등을 할 시에 많은 것을 고려해야 합니다. 위의 그림에서는 오른쪽 녹색 유틸리티 객체들의 내용이 수정, 추가되었거나 기능이 변경되었다면 그것을 사용하는 모든 왼쪽의 파란 메인 객체들의 소스또한 수정될지도 모릅니다.
사용자 삽입 이미지하지만 위의 그림의 느낌은 어떤가요? 유틸리티 객체들은 메인 객체의 배경이 되며 얼마든지 수정/교체가 가능합니다. 하지만 그때에 메인 객체들의 소스는 수정되지 않습니다.  이것이 관점지향프로그래밍의 개념입니다. 예제를 통해 어떻게 구현하는 것인지 알아보도록 하겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using AopAlliance.Intercept;

namespace HelloAop
{
    class ConsoleLoggingAroundAdvice : IMethodInterceptor {
        public object Invoke(IMethodInvocation invocation) {
            Console.WriteLine("수행전...");
            object retVal = invocation.Proceed();
            Console.WriteLine("수행후...");
            return retVal;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Text;

namespace HelloAop
{
    class ServiceCommand : ICommand
    {
        public object Execute(object arg)
        {
            Console.WriteLine("\n명령 수행 : {0}", arg);
            return null;
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.Text;
using Spring.Aop.Framework;

namespace HelloAop
{
    class Program
    {
        static void Main(string[] args) {
            ProxyFactory factory = 
                new ProxyFactory(new ServiceCommand());
            factory.AddAdvice(new ConsoleLoggingAroundAdvice());
            ICommand command = (ICommand)factory.GetProxy();
            command.Execute("이것은 인자입니다.");
        }
    }
}

ProxyFactory를 이용하여 ServiceCommand를 프록시로 선언합니다. 그후에는 AddAdvice를 이용하여 미리 만들어둔 어드바디스를 추가합니다. 이후에 프록시를 생성하여 명령을 실행하면 “수행전… → 명령수행 : 이것은 인자입니다 → 수행후“가 출력됩니다.

입력되는 인자나 반환된 결과값을 검사하거나 다른 루틴을 얼마든지 추가할 수 있습니다.  위의 과정을 통해 실제로 ServiceCommand 객체의 소스에는 손대지 않고 기능을 추가, 변경 할 수 있게 되었습니다.

사용자 삽입 이미지

위의 그림과 같이 실제로는 별개의 객체들을 프록시로 한데 묶은 다음에 프록시가 메서드 실행 요청을 대리 수령하고 어드바이스를 참고하여 실체 명령을 수행하게 됩니다.

Spring .NET을 도입할 시 얻을 수 있는 장점

  • 중급 이상의 프로젝트 수행 시 각각의 프로그래머 간 개발 스타일이 통일 될 수 있다.
  • 신입 개발자의 교육과정을 줄이고 Spring .NET을 할 줄 아는 개발자를 뽑아 바로 실무에 투입할 수 있다.
  • 각각의 모듈/컴포넌트별로 종속성이 제거 되므로 업그레이드, 추가, 제거가 용이하다.