구글의 Reverse Geocoding도 있지만, 한국 정서에 맞는 알맞은 주소를 제공하는 네이버 API 서비스를 활용해보았다.
우선 네이버의 ncloud.com에서 Application > map 서비스를 사용신청하고 필요한 준비를 진행한다.
각각의 프로젝트에 맞는 설정이라 부차적 설명은 생략하기로 하고
iOS 코드를 확인해 보도록 하자. 소스는 Objective-C 로 작성되었다.
# NaverMapReverseGeocode.h
//
// NaverMapReverseGeocode.h
//
// Created by James Hong on 2021/08/02.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "AFNetworking.h"
NS_ASSUME_NONNULL_BEGIN
#define GEOCODER_COUNTRYCODE 0
#define GEOCODER_POSTALCODE 1
#define GEOCODER_COUNTRYNAME 2
#define GEOCODER_ADMINAREA 3
#define GEOCODER_SUBADMINAREA 4
#define GEOCODER_LOCALITY 5
#define GEOCODER_SUBLOCALITY 6
#define GEOCODER_THROUTHFARE 7
#define GEOCODER_FEATURENAME 8
#define GEOCODER_ADDRESS 9
#define GEOCODER_FULLADDRESS 10
#define UPDATE_ADDRESS 1
#define UPDATE_LATLNG 2
#define UPDATE_ADDRESS_ERROR 3
#define NaverOpenAPIURLWithQuery @"https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords=%f,%f&orders=legalcode,admcode&output=json"
#define NaverOpenAPIURL @"https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc"
#define addParameterKeyValues @[@"request", @"coordsToaddr", @"coords", @"", @"orders", @"legalcode,admcode", @"output", @"json"]
#define addHeaderKeyValues @[@"X-NCP-APIGW-API-KEY-ID", @"*발급된값*", @"X-NCP-APIGW-API-KEY", @"*발급된값*", @"Content-Type", @"application/json; charset=utf-8"]
@interface NaverMapReverseGeocode : NSObject
typedef void (^NaverResponseHandler)(id result, NSError *error);
@property (nonatomic, assign) double lat;
@property (nonatomic, assign) double lon;
@property (nonatomic, assign, nullable) NaverResponseHandler responseHandler;
@property (nonatomic, strong) AFHTTPSessionManager *afHTTPSessionManager;
+ (instancetype)sharedManager;
- (void)requestAddress:(double) lat lon:(double) lon callback: (NaverResponseHandler) callback;
@end
NS_ASSUME_NONNULL_END
# NaverMapReverseGeocode.m
//
// NaverMapReverseGeocode.m
//
// Created by James Hong on 2021/08/02.
//
#import "NaverMapReverseGeocode.h"
@implementation NaverMapReverseGeocode
+ (instancetype)sharedManager {
static id sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (void)requestAddress:(double) lat lon:(double) lon callback: (NaverResponseHandler) callback {
NSString *latLngString = [NSString stringWithFormat:@"%f,%f", lon, lat];
NSDictionary *dic = @{addParameterKeyValues[0]: addParameterKeyValues[1],
addParameterKeyValues[2]: latLngString,
addParameterKeyValues[4]: addParameterKeyValues[5],
addParameterKeyValues[6]: addParameterKeyValues[7]
};
_lat = lat;
_lon = lon;
NSLog(@"*** latLngString: %@", latLngString);
[self requestApi:dic callback:callback];
}
- (NSError *) getErrorFromException:(NSException *) exception {
NSMutableDictionary * info = [NSMutableDictionary dictionary];
[info setValue:exception.name forKey:@"ExceptionName"];
[info setValue:exception.reason forKey:@"ExceptionReason"];
[info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
[info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
[info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];
NSError *error = [[NSError alloc] initWithDomain:[self getAppVersion] code:100 userInfo:info];
return error;
}
- (NSString *) getValue:(NSDictionary *)dic key:(NSString *)key {
NSObject *obj = [dic valueForKey:key];
return [NSString stringWithFormat:@"%@", obj];
}
- (void) parseAddress:(NSDictionary *)dic callback:(id) callback error:(NSError *)error {
NaverResponseHandler handler = (NaverResponseHandler)callback;
NSMutableDictionary *address = [[NSMutableDictionary alloc] initWithCapacity:GEOCODER_FULLADDRESS+1];
@try {
if(!error) {
if([dic valueForKey:@"status"] != nil) {
NSDictionary *status = [dic valueForKey:@"status"];
NSDictionary *resultsDic = [dic valueForKey:@"results"];
NSString *code = [self getValue:status key:@"code"];
NSString *message = [self getValue:status key:@"message"];
NSString *name = [self getValue:status key:@"name"];
NSLog(@"*** parseAddress: code:%@, message:%@, name:%@", code, message, name);
if([@"0" isEqualToString:code] && resultsDic != nil) {
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:resultsDic options:NSJSONWritingPrettyPrinted error:&error];
NSArray *results = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableContainers error:&error];
if(!error) {
for(NSDictionary *row in results) {
//NSLog(@"*** parseAddress: row:%@", row);
if([@"admcode" isEqualToString:[self getJsonObjectValue:row keys:@[@"name"]]]) {
NSString *country = [self getJsonObjectValue:row keys:@[@"region", @"area0", @"name"]];
NSString *province = [self getJsonObjectValue:row keys:@[@"region", @"area1", @"name"]];
NSString *provinceAlias = [self getJsonObjectValue:row keys:@[@"region", @"area1", @"alias"]];
NSString *city = [self getJsonObjectValue:row keys:@[@"region", @"area2", @"name"]];
NSString *dong = [self getJsonObjectValue:row keys:@[@"region", @"area3", @"name"]];
[address setValue:country forKey:[@(GEOCODER_COUNTRYCODE) stringValue]];
[address setValue:province forKey:[@(GEOCODER_ADMINAREA) stringValue]];
[address setValue:city forKey:[@(GEOCODER_LOCALITY) stringValue]];
[address setValue:dong forKey:[@(GEOCODER_SUBLOCALITY) stringValue]];
[address setValue:provinceAlias forKey:[@(GEOCODER_FEATURENAME) stringValue]];
}
}
} else {
NSLog(@"*** parseAddress: %@", error.description);
}
} else if([@"3" isEqualToString:code]) {
NSLog(@"*** MESSAGE: %@", message);
}
}
}
handler(address, error);
} @catch(NSException *e) {
NSLog(@"*** NSException: %@", e.description);
handler(address, [self getErrorFromException:e]);
}
}
- (NSString *) getJsonObjectValue:(NSDictionary *) obj keys:(NSArray *)keys {
NSString *result;
@try {
for(NSString *key in keys) {
//NSLog(@"*** getJsonObjectValue - key: %@", key);
if([key isEqualToString:[keys lastObject]]) {
id value = [obj valueForKey:key];
if([value isKindOfClass:[NSString class]]) {
result = (NSString *)value;
} else if([value isKindOfClass:[NSNumber class]]) {
result = [NSString stringWithFormat:@"%d", [value intValue]];
} else {
NSLog(@"*** getJsonObjectValue - value: %@", value);
}
} else {
obj = [obj valueForKey:key];
}
}
} @catch(NSException *e) {
NSLog(@"*** Exception: %@", e.description);
}
return result;
}
- (void) requestApi:(NSDictionary*)parameters callback:(id) callback {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:NaverOpenAPIURLWithQuery, _lon, _lat]];
self.afHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url]; // Base URL here, not entire URL
self.afHTTPSessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
NSDictionary *headers = @{
addHeaderKeyValues[0]: addHeaderKeyValues[1],
addHeaderKeyValues[2]: addHeaderKeyValues[3],
addHeaderKeyValues[4]: addHeaderKeyValues[5]
};
__block NaverMapReverseGeocode *weakSelf = self;
NSLog(@"*** url: %@", url.absoluteString);
[self.afHTTPSessionManager GET:url.absoluteString
parameters:headers
headers:nil
progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSDictionary *responseData = [[NSDictionary alloc] initWithDictionary:responseObject];
//NSLog(@"*** responseData : %@", responseData);
[weakSelf parseAddress:responseData callback:callback error:nil];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"failure %@",error.localizedDescription);
[weakSelf parseAddress:nil callback:callback error:error];
}];
}
@end
동작구조는 간단하다, NaverMapReverseGeocode를 sharedManager로 호출하여 WGS84 위,경도 좌표값과 함께 requestAddress를 콜백과 함께 호출하고, 콜백블럭에 필요한 후처리 코드를 작성하면 끝난다.
콜백에 리턴되는 결과는 NSDictionary로, 필요한 키의 값을 불러 조합해서 활용하면 된다.
# 아래는 사용 예시이다.
...
[[NaverMapReverseGeocode sharedManager] requestAddress:[SharedData.currentLat doubleValue] lon:[SharedData.currentLon doubleValue] callback:^(id _Nonnull result, NSError * _Nonnull error) {
//
NSDictionary *addrs = (NSDictionary *)result;
if(addrs != nil && [addrs.allKeys count] > 0) {
NSMutableString *sb = [[NSMutableString alloc] init];
[sb appendString:(![Common isEmpty:[addrs valueForKey:[@(GEOCODER_ADMINAREA) stringValue]]] ? [addrs valueForKey:[@(GEOCODER_ADMINAREA) stringValue]] : @"")];
for(NSString *key in @[[@(GEOCODER_LOCALITY) stringValue], [@(GEOCODER_SUBLOCALITY) stringValue]]) {
if(![Common isEmpty:[addrs valueForKey:key]]) {
[sb appendString:@" "];
[sb appendString:[addrs valueForKey:key]];
}
}
SharedData.currentAddr = [NSString stringWithUTF8String:sb.UTF8String];
NSLog(@"*** new Address: %@", SharedData.currentAddr);
} else {
NSLog(@"*** NaverMapReverseGeocode addrs: %@", addrs);
NSLog(@"*** NaverMapReverseGeocode ERROR: %@", error.description);
}
}];
...
'개발관련 정보 > 아이폰' 카테고리의 다른 글
XCODE 16.3에서 WEB 리소스 파일 관리 (0) | 2025.05.26 |
---|