[GIS] 도로명 주소 UI 만들기 위한 SQL 문

국내에서 제공하는 공간데이터 중 행안부의 새주소 데이터를 이용해 도로명 주소 UI를 만들기 위한 SQL문을 정리해 보려 합니다. 행정구역은 서울시를 대상으로 하겠습니다. 도로명 주소 체계가 특정 지역에 대해 특이사항이 있는 것은 아니지만.. 일단 서울시의 경우 도로명 주소는 구를 선택하면 해당 구에 소속된 도로명들이 존재합니다.

서울시 하나의 구에 대한 도로명은 개수는 상당히 많아 선택된 구에 대한 도로명을 하나의 컴보박스 컨트롤을 통해 집어 넣으면 사용자가 선택하기 어렵습니다. 해서 다음의 UI처럼 선택된 구에 대한 도로명을 ㄱ,ㄴ,ㄷ 과 같은 첫자에 대한 자음으로 분류하는 방법을 사용합니다.

사용자 삽입 이미지
위의 그림에서 A Part에는 ㄱ,ㄴ,ㄷ과 같은 도로명에 대한 첫자의 자음의 리스트이고 B Part는 선택 자음에 대한 도로명의 리스트입니다. 행안부에서 제공하는 도로명 DB는 tl_sprd_manage입니다. 먼저 A Part를 구성하기 위한 SQL(MSSQL Server 기준) 문은 다음과 같습니다.

select distinct GetConsonant(SUBSTRING(RN,1,1)) 
from tl_sprd_manage 
where SIG_CD = ‘{SIG_CD} ' 
group by RN

{SIG_CD}는 서울시의 구에 대한 5자리의 행정코드 값입니다. 또한 위의 SQL 문에는 GetConsonat 함수가 사용되었습니다. 이 함수에 대한 정의는 다음과 같습니다.

create FUNCTION GetConsonant
(
  @STR AS NVARCHAR(100)
)
RETURNS NVARCHAR(100)
AS
BEGIN
  DECLARE @STR_N NVARCHAR(100)
  DECLARE @STR_S NVARCHAR(100)
  WHILE (LEN(@STR) > 0)
  BEGIN
    SET @STR_N = SUBSTRING(@STR, 1, 1)
    SET @STR = SUBSTRING(@STR, 2, LEN(@STR))
    SET @STR_S = ISNULL(@STR_S, '') + 
      (CASE WHEN @STR_N BETWEEN '가' AND '깋' THEN 'ㄱ'
          WHEN @STR_N BETWEEN '나' AND '닣' THEN 'ㄴ'
          WHEN @STR_N BETWEEN '다' AND '딯' THEN 'ㄷ'
          WHEN @STR_N BETWEEN '라' AND '맇' THEN 'ㄹ'
          WHEN @STR_N BETWEEN '마' AND '밓' THEN 'ㅁ'
          WHEN @STR_N BETWEEN '바' AND '빟' THEN 'ㅂ'
          WHEN @STR_N BETWEEN '사' AND '싷' THEN 'ㅅ'
          WHEN @STR_N BETWEEN '아' AND '잏' THEN 'ㅇ'
          WHEN @STR_N BETWEEN '자' AND '짛' THEN 'ㅈ'
          WHEN @STR_N BETWEEN '차' AND '칳' THEN 'ㅊ'
          WHEN @STR_N BETWEEN '카' AND '킿' THEN 'ㅋ'
          WHEN @STR_N BETWEEN '타' AND '팋' THEN 'ㅌ'
          WHEN @STR_N BETWEEN '파' AND '핗' THEN 'ㅍ'
          WHEN @STR_N BETWEEN '하' AND '힣' THEN 'ㅎ'
          ELSE @STR_N END)
       END
  RETURN @STR_S
END

이제 B Part에 대한 SQL문을 살펴보면 다음과 같습니다.

select RN, RN_CD from tl_sprd_manage where SIG_CD = ‘{SIG_CD}' 
and GetConsonant(SUBSTRING(RN,1,1))='ㄱ' 
group by RN, RN_CD order by RN

위의 SQL은 A Part에서 ‘ㄱ’을 선택했을때 ‘ㄱ’으로 시작하는 도로명의 리스트를 뽑아냅니다. 참고로 RN은 도로명이며 RN_CD는 도로명에 대한 코드값입니다.

C#의 스레드(Thread)에서 UI 컨트롤 사용하기

기본적으로 UI를 갖는 컨트롤은 메인 스레드가 아닌 다른 스레드에서 접근될때 충돌(Crash)이 발생합니다. 현재 C/S 기반의 맵엔진인 듀라맵을 서버단에서 사용하고 있으며, 서버에서 여러 개의 스레드에서 UI 컨트롤인 듀라맵에 접근할때 아래와 같은 충돌이 발생합니다.

사용자 삽입 이미지

이에 대한 해결 방안은 델리게이트(Delegate)와 UI 컨트롤을 담는 Form 클래스의 Invoke 매서드를 사용해 해결할 수 있습니다.

예를 들어서 지도 엔진인 듀라맵을 이용해 어떤 지점이 특정 폴리곤에 포함(Contain)하는지에 대한 기능을 수행하고자 할 때를 살펴 보겠습니다.

듀라맵을 이용해 다음과 같은 Contain이라는 새로운 사용자 정의 매서드를 만들었습니다. 이 Contain 매서드의 위치는 어디라도 상관이 없습니다. 중요한 것은, 바로 이 Contain이라는 매서드가 별도의 스레드를 통해 호출된다는 것이고 델리게이트 기법을 사용하지 않는다면 충돌이 발생합니다.

public bool Contain(double X, double Y, int FID)
{
    //UI 컨트롤 사용
}

별도의 스레드에서 문제 없이 위에서 언급한 Contain이라는  매서드를 호출하기 위해서는, Contain 매서드를 직접 호출하지 않고 델리게이트(대리자)를 사용하면 됩니다. 델리게이트를 다음처럼 Contain 매서드가 존재하는 같은 클래스에 선언합니다.

private delegate bool DelegateContain(double X, double Y, int FID);
private DelegateContain delegateContain;

이제 위의 델리게이트와 Contain 매서드를 연결하는 코드는 다음과 같습니다.

delegateContain = new DelegateContain(Contain);

이제 메인 스레드가 아닌 스레드에서 Contain 매서드의 기능을 활용하기 위해서는 Contain에 대한 델리게이트인 delegateContain를 사용하면 됩니다. 즉, 다음처럼…

bool bContain = (bool)form.Invoke(delegateContain, X, Y, FID);

스레드에서 사용하고자 하는 UI 컨트롤이 담긴 폼 객체의 Invoke 매서드를 사용해 delegateContain을 호출하며 Contain 매서드의 인자는 Invoke 매서드를 통해 전달하면 됩니다. Invoke는 가변 인자를 받으므로 인자의 제약이 덜합니다. 또한 Invoke의 반환값은 델리게이트 대상이 되는 함수의 반환값이며 object 타입이므로 Contain 매서드의 반환값으로 형변환하여 사용하면 됩니다.