◎위챗 : speedseoul
https://10apps.tistory.com/146?category=431104
지난 3월에 애플에서 예고한 것처럼 UDID 정책이 강화되면서 UDID API를 사용하 수 없게 되었다. 이 정책을 신규 앱을 등록할 때는 물론이고 기존 앱을 업그레이드 할때도 적용이 되기 때문에 UDID를 사용하는 앱은 앱 스토어 등록이 거부된다. UDID 조회 API는 iOS 5.0 부터 사용이 Deprecated 되었다. 하지만 지금까지는 사용에 별문제 없었지만 이제는 상황이 바뀌었다. 이것을 지난 2011년 애플의 사생활 보호를 위한 조치의 일환이다. 즉, 이용자의 개인 정보에 해당하는 정보를 수집할 수 없도록 하는 조치이다. 아이폰의 앱들이 주소록이나 일정 정보들은 무단으로 사용하는 경우가 많았는데 이에 대한 애플이 대책의 일환으로 iOS 5부터 적용이 됐다. UDID로 일종의 개인정보로 분류한 것이다.
이로 인해서 기존의 UDID를 사용하던 앱들은 비상이 났다. UDID는 폰을 구분할 수 있는 유일한 값으로 많이 사용되어서 어떤 서비스를 할때 특정 사용자의 특정 기기만 서비스를 허용하기 위해서 자주 사용된다. B2C보다는 B2B 앱들이 많이 쓰게 되는 B2B의 경우 보안을 위해서 사용자를 구분해서 특정 사용자에게 허용할 수 있는 기능이 들어가기 때문이다.
UDID 대안으로 거론되는 것들에 대해서 알아보고 그에 따른 장단점을 알아 볼것이다.
UDID는 폰을 구별하기 위한 키값으로 사용을 한다. 따라서 이를 대신한 유일한 값을 찾으면 된다. 그 유일 값을 구하기 위한 방법으로 기존 UDID 대용으로 사용할 수 있는 방법은 5가지 정도 있다.
이에 대해서 자세히 알아보자.
iOS 5.0에서 UDID를 조회하는 API를 Deprecated 시키면서 애플에서 내놓은 대안으로 앱별로 CFUUIDCreate를 이용해서 UUID를 생성해서 사용하라는 것이다. UUID는 국제 표준으로 고유성을 완벽하게 보장할 수는 없지만 실제 사용상에서 중복될 가능성이 거의 없다. ( 위키피디아의 범용 고유 식별자 참조 )
여기서는 UUID를 생성하고 이후에 이것을 Keychain에 저장해서 다른 앱에서도 UUID를 공유해서 사용할 수 있도록 했다.
다음은 UUID를 생성하고 Keychain을 사용해서 생성된 값을 저장하는 샘플코드이다.
- (NSString*) udidUsingCFUUID
{
// initialize keychaing item for saving UUID.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID"
accessGroup:nil];
NSString *uuid = [wrapper objectForKey:kSecAttrAccount];
if( uuid == nil || uuid.length == 0)
{
// if there is not UUID in keychain, make UUID and save it.
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
uuid = [NSString stringWithString:(NSString *) uuidStringRef];
CFRelease(uuidStringRef);
// save UUID in keychain
[wrapper setObject:uuid forKey:kSecAttrAccount];
}
return uuid;
}
UDID는 iOS의 기기를 구별해는 값이기 때문에 많이 사용되었다. 그리고 이 때문에 사라지게 되었다. 이와 비슷하게 기기별로 다른 고유의 값이 또 있다. Wifi 모듈의 맥주소이다. 맥은 기기가 만들어질때 할당이 되는 값으로 구제적인 기구로 부터 번호를 할당받기 때문에 이 값을 이용할 수 있다.
- (NSString*) udidUsingMacAddress
{
// can use up to iOS 6.0 and before
// mac address from system in iOS 7 and later will be
// '02:00:00:00:00:00
NSString *macaddr = [self getMacAddress];
// hash |macaddr| using SHA1 to make udid which is 40 long.
const char *ptr = [macaddr UTF8String];
unsigned char buf[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(ptr, strlen(ptr), buf);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x",buf[i]];
NSString *uuid = [NSString stringWithString:output];
return uuid;
}
위 소스를 보면 맥주소를 조회한 이후에 맥을 그냥 사용하지 않고 SHA1 해쉬를 사용해서 UDID와 동일한 길이의 문자열을 만들었다.
맥은 그동안 UDID 대용으로 많이 사용되었는데 최근에 발표된 iOS7에서는 이부분을 의식해서 인지 진째 맥주소를 구할 수 없도록 만들었다. iOS7에서 맥주소를 구하면 02:00:00:00:00:00 값으로 폰과 상관 없이 일정한 값이 반환된다.
따라서 맥을 사용하는 방법은 iOS7부터는 사용할 수 없게 되었다. 필자가 개인적으로 Bluetooth는 되지 않을까하고 찾아보았지만 Bluetooth의 맥 주소를 구할 수 있는 API가 없어 찾지 못했다. 대신 Wifi의 맥주소로 유추하는 법이 있는데 Wifi의 맥주소를 구할 수 없으니 소용이 없다.
UDID 대용으로 등장한 오픈소스들이 많은데 Appsfire의 OpenUDID가 그중 하나이다. 사용 방법은 다음과 같이 간단하다.
#import "OpenUDID.h" // OpenUDID
- (NSString*) udidUsingAppsfire
{
NSString* openUDID = [OpenUDID value];
return openUDID;
}
OpenUDID 소스를 Github로 부터 받아서 소스를 설치하고 위와 같이 호출하면 내부적으로 UDID를 생성해서 리턴하게 된다. OpenUDID는 1번 방법과 다른 방법으로 값을 저장한다. Keychain을 사용하지 않고 파일과 UIPasteboard를 사용했다. UDID 저장을 위한 UIPasteboard를 만들어 저장한다. UIPasteboard는 Copy&Paste를 사용하기위한 것으로 공간 정보를 공유하는 방법으로 사용된다. 따라서 UIPasteboard의 이름만 공유할 수 있다면 앱들간에 같은 값을 취할 수 있다.
이 프로젝트는 최근 iOS6이후에 더이상 update를 하지 않겠다고 선언을 했다. OpenUDID를 사용하지 말고 앞으로 보게된 advertisingIdentifier 를 사용하도록 권장하고 있다.
iOS 5에서는 UDID를 쓰지 말라고만 하고는 아무런 대안도 내놓지 않던 애플이 iOS6 이후부터는 UDID대신에 사용할 수 공식 API를 공개했다.
UIDevice의 identifierForVendor는 이름에서도 알 수 있드시 값은 제작사에서 많은 앱은 같은 UUID값을 가질 수 있다. 같은 제작사의 앱들이 같은 iOS 기기에 설치된다면 이 앱들은 같은 값을 받게 됬다. 하지만 이 값을 항상 동일하지는 않은데 가령 기기에서 같은 제작사의 앱들이 모두 삭제된 이후에 설치되면 이 값을 변경된다. 즉, 제작사의 한 앱은 깔려 있어야 값이 유지된다는 말이다.
반면, ASIdentifierManager의 advertisingIdentifier는 같은 폰의 앱들은 모두 동일한 값을 가지게 된다. 기존의 UDID와 동일하니 한가지 OS가 다시 Reset되는 경우에는 값이 변경될 수 있다. 그리고 기기의 설정에서 광고제한을 하면 사용에 제약을 받는다. 따라서 이 경우 앱 개발사가 이 값을 마음대로 써도 되는지는 현재로는 알 수 없다.
사용 방법은 두가지 모두 간단한다.
- (NSString*) udidUsingiOS6UIDevice
{
// can use iOS 6.0 and later
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
- (NSString*) udidUsingiOS6ASIdentifier
{
// can use iOS 6.0 and later
ASIdentifierManager *manager = [ASIdentifierManager sharedManager];
return [[manager advertisingIdentifier] UUIDString];
}
ASIdentifier를 사용하기 위해서는 AdSupport.framework를 추가해주어야 한다.
지금까지 여러가지 대안들을 보았다. 대안들이라고 했지만 사실 지금까지의 애플의 행보를 보아서는 첫번째 방법과 같이 UUID를 앱별로 만들어 사용하거나 공식으로 지원하는 API를 사용하는 방향으로 가야 할 것이다.