오랫만이네요. 이건 이주일 전부터 써야지하고 생각했던건데 너무 오랫동안 묵혀뒀어요.
실버라이트 2에서 가장 많이 달라진 것중 하나는 바로 프로젝트에서의 리소스 파일의 관리와 URI 접근 규칙이죠.

※이 글에서 사용된 내용을 포함하는 샘플프로젝트는 다음의 파일을 다운로드 하세요.

SilverlightResourceFiles.zip

실버라이트 리소스와 URI 테스트 프로젝트


리소스 파일이란 공통으로 사용되는 XAML, XML, 이미지, 동영상, 음악 등 컴파일되지 않는 모든 '비실행(non-executable)' 파일을 말해요. 실버라이트 애플리케이션이 이러한 리소스 파일을 요청할 때에는 URI(Uniform Resource Identifier)라고 하는 형식으로 리소스 파일의 위치를 식별하게 되죠.

실버라이트에서 다루는 리소스 파일은 크게 다음과 같은 네가지로 분류할 수 있어요. 자세한 설명은 각 토픽에서 다루도록 하죠.

  • 리소스(Resource)
  • 컨텐트(Content)
  • Site of origin
  • 절대 URI
※노트
이 글에서 모든 비실행 데이터 파일을 의미하는 리소스 파일과 리소스 파일의 종류를 말하는 리소스를 일반적인 폰트와 이탤릭체로 구분합니다. 이 둘의 의미를 혼동하지 않도록 주의하여 읽어주세요.

이 글에서는 실버라이트에서의 리소스 파일의 종류와 정의 그리고 각각의 URI를 표현하는 방법에 대해 설명합니다. 가능하다면 MSDN의 Resource Files도 함께 참고하세요. 사실 이 글의 핵심은 MSDN의 글과 일치하고 약간의 설명만 추가한거에요.

리소스 파일

리소스 파일은 애플리케이션 어셈블리나 라이브러리 어셈블리에 포함(embedded)되는 비실행 데이터 파일을 말해요. 리소스 파일은 어셈블리에 포함되기 때문에 반드시 프로젝트에 포함되어야 하며 비주얼 스튜디오에서는 다음과 같이 프로젝트에 추가된 파일의 Build Action을 Resource로 설정하여 해당 파일을 리소스 파일로 지정할 수 있어요.

비주얼 스튜디오는 비실행 파일이 추가될 경우  기본적으로 Build Action을 Resource로 설정하게 되죠. 만약 해당 파일이 어셈블리에 포함되는게 아니라 다음에 설명할 다른 리소스 형식으로 설정하고 싶다면 반드시 Build Action을 수정해야 해요.

이렇게 리소스로 추가된 리소스 파일은 어셈블리 내에 포함되므로 같은 어셈블리 내에 있는 코드에서는 별다른 접근자 없이 곧바로 리소스 파일의 상대 URI(relative URI)를 사용하여 접근할 수 있어요. 예를 들어 위의 그림처럼 EmbeddedInApplicationAssembly.png 파일이 프로젝트의 루트에 들어있다면 이 그림 파일을 보여주기 위한 Image 오브젝트는 XAML과 코드 비하인드에서 각각 다음과 같이 표현할 수 있죠.

[XAML]
<Image Source="EmbeddedInApplicationAssembly.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Uri("EmbeddedInApplicationAssembly.png", UriKind.Relative));

한가지 주의할 점은 리소스 파일은 상대 URI로 표현되므로 만약 이 리소스 파일을 요청하는 코드의 위치가 리소스 파일의 위치와 다를 경우 제일 앞에 슬래시로 시작하는 정확한 상대경로를 적어주는 것이 좋죠. 또 한가지 잊지 말아야 할 것은 실버라이트의 URI 표현에는 상위 경로를 의미하는 '../'이나 현재 경로를 의미하는 './'을 사용할 수 없다는 점이에요. 다음은 슬래시로 시작하는 정확한 상대경로로 URI를 표현하는 방법을 보여줘요.

[XAML]
<Image Source="/EmbeddedInApplicationAssembly.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Uri("/EmbeddedInApplicationAssembly.png", UriKind.Relative));

여러가지 면을 고려할 때 리소스 파일은 리소스를 관리하기 위한 하나의 폴더에 저장하고 리소스 파일의 URI를 지정할 때에는 반드시 슬래시로 시작하는 정확한 상대경로로 표현하는 것이 실수를 줄일 수 있는 가장 좋은 방법이라고 할 수 있어요.

※노트
현재, MSDN에는 앞에서와 같이 /로 시작하는 리소스 파일에 대한 상대 URI를 지정할 수 있다고 나와있으나 실제로 코드를 만들어보면 런타임 예외를 유발하죠. 앞의 설명에서 '../'이나 ./'을 사용할 수 없다는 것도 맞고 정확한 표현을 위해서 완전한 상대 URI를 써야한다는 설명도 맞지만 불행히도(?) /로 시작하는 단순한 형태의 상대 URI를 리소스 파일에 사용할 수 없으므로 다음에서 설명하는 명시적인 방법을 통한 상대 URI를 사용할 것을 권장합니다.

리소스 파일의 또 한가지 특징은 해당 리소스가 다른 어셈블리에 있어도 그것을 참조할 수 있다는 점인데요, 예를 들어 다음과 같이 다른 프로젝트(즉, 다른 라이브러리 어셈블리)에 있는 리소스가 있다고 생각해 보죠.

이렇게 다른 어셈블리에 있는 리소스 파일은 다음과 같이 요청할 수 있어요.

[XAML]
<Image Source="/SilverlightResourceLibrary;component/EmbeddedInLibraryAssembly.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Uri("/SilverlightResourceLibrary;component/EmbeddedInLibraryAssembly.png", UriKind.Relative));

/SilverlightResourceLibrary;component/ 부분은 어셈블리의 위치를 명시적으로 표시하는 방법인데요, 위와 같이 다른 어셈블리를 표현할 수도 있지만 자기 자신의 어셈블리를 명시적으로 표현할 수도 있죠. 예를 들어 같은 어셈블리에 있더라도 명시적으로 정확히 리소스 파일을 가져오기 위해 /SilverlightResourceFiles;component/...와 같이 표현할 수 있어요.

앞에서 설명한 것처럼 리소스 파일은 어셈블리에 포함되는데요, 이 사실은 .NET Reflector와 같은 어셈블리 해커를 통해 쉽게 확인할 수 있어요.


컨텐트 파일

컨텐트 파일은 배포되는 패키지에 포함되는(in-package) 비실행 데이터 파일을 말해요. 즉,  XAP 패키지 파일(.XAP)내에 파일의 형태로 포함되는 형태의 리소스를 말하죠. 컨텐트 파일은 비주얼 스튜디오에서 다음과 같이 Build Action을 Content로 설정하여 지정할 수 있어요.

컨텐트 파일도 패키지 내에 포함되기 위해 반드시 프로젝트에 추가되어 있어야 하며 따라서 항상 그 파일이 존재한다고 가정할 수 있어요. 또한 패키지를 기준으로 항상 어셈블리와 같은 경로에 있기 때문에 단순히 상대 URI만으로 해당 리소스를 요청할 수 있죠. 다음은 XAML과 C# 코드로 컨텐트 파일을 요청하는 Image 오브젝트를 보여줘요.

[XAML]
<Image Source="/IncludedInApplicationPackage.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Uri("/IncludedInApplicationPackage.png", UriKind.Relative));

잊지 마세요, 컨텐트 파일은 코드가 들어있는 어셈블리가 위치한 경로와 컨텐트 파일의 경로가 서로 다를 수 있기 때문에 반드시 위와 같이 슬래시로 시작하는 정확한 상대 URI로 표현해야 해요.

컨텐트 파일은 다음과 같이 애플리케이션 패키지(.XAP)를 압축해제 해 보면 확인할 수 있어요.


Site of Origin

Site of Origin은 딱히 한글로 번역할만한 단어를 찾지 못했는데요, 풀어서 얘기하자면 실버라이트 애플리케이션 패키지를 다운로드한 원래 사이트(같은 도메인에 있는 서버)를 말해요. 즉, Site of Origin 파일은 원래 사이트에 있는 비실행 데이터 파일을 말하죠. Site of Origin 파일은 다음과 같이 비주얼 스튜디오에서 Build Action을 None으로 설정하면 돼요.

또한 Copy to Output Directory 속성을 Copy Always로 설정하면 출력 경로에 해당 파일을 항상 복사하게 되므로 배포가 조금 더 용이해지죠.

※노트
하지만 위와 같이 Copy Always를 설정하더라도 실버라이트와 연결된 웹 사이트나 웹 애플리케이션 프로젝트의 출력 경로에는 해당 파일이 복사되지 않으므로 반드시 다음과 같이 Site of Origin 파일들을 실제 웹 프로젝트의 출력 경로에 복사해줘야 합니다. 이 기능은 다음 베타때 꼭 지원을 해줬으면 하네요.

중요한 것은 Site of Origin 파일은 어셈블리나 패키지에 포함되지 않고 필요할 때마다 다운로드(On-Demand)되므로 실제로 서버에 파일이 존재하지 않을 가능성이 있다는 점이에요. 따라서 Site of Origin 파일을 사용하는 코드는 적절한 이벤트 핸들링을 통해 로드에 실패했을 때 처리를 수행해야 하죠. 그러나 이 글에서는 논의의 범위를 벗어나므로 에러를 처리하는 방법에 대해서는 소개하지 않고 다음에 추가로 포스팅하도록 하죠. Site of Origin 파일은 XAML과 C# 코드에서 다음과 같이 표현할 수 있어요.

[XAML]
<Image Source="/OnDemandAtSiteOfOrigin.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Uri("/OnDemandAtSiteOfOrigin.png", UriKind.Relative));

한 가지 주의할 점은 Site of Origin에서의 '/'는 일반적인 웹과는 달리 웹 사이트의 루트 경로를 나타내지 않는다는 점이에요. 실버라이트 애플리케이션은 실버라이트 패키지가 배포되는 경로를 기준으로 완전히 독립적으로 작동하며 따라서 '/'는 실버라이트 패키지 즉, XAP파일이 있는 경로를 의미하게 돼요. 또한 앞에서 설명한 것처럼 '../'을 사용할 수 없기 때문에 실버라이트 애플리케이션이 상대 경로를 통해 웹 서버의 다른 리소스를 접근하는 것은 금지되게 되죠.


리소스(or 컨텐트) vs Site of Origin

Application Development Overview에서 다뤘듯이, 애플리케이션 패키지는 가능한 그 크기를 줄이는 것이 빠른 응답을 하는 애플리케이션을 만드는 가장 기본적인 방법이죠. 즉, 애플리케이션 패키지에 포함되는 리소스 파일과 컨텐트 파일은 그 성격과 크기, 종류에 따라 신중하게 선택하는게 좋아요. 각 파일의 선택은 대체로 다음과 같은 기준으로 정할 수 있을 거에요.

  • 리소스 파일
    모든 애플리케이션에 공통적으로 적용될 어셈블리에 포함되는 사용자 컨트롤 등의 표현에 없어서는 안될 에셋(Asset)성격의 이미지, XAML, 효과음 등의 작은 파일
  • 컨텐트 파일
    애플리케이션 마다 다르게 사용될 수도 있는 종류의 스킨, 설정 등의 작은 파일
  • Site of Origin 파일
    애플리케이션의 외형적인 이미지, XAML, 동영상, 음악 등 애플리케이션을 표현하기 위해 필요한 모든 종류의 파일

상대 URI로 표현되는 리소스들은 경우에 따라 애매하게 될 수 있어요. 예를 들어,

"RelativeURIResource.png"

이것만으로는 해당 파일이 리소스인지 Site of Origin인지 구분할 수 없겠죠. 또한,

"/RelativeURIResource.png"

이 경우는 해당 파일이 컨텐트인지 Site of Origin인지를 구분할 수가 없어요.
이런 경우 실버라이트 런타임은 다음의 과정을 통해 파일을 찾게 돼요.

  1. 만약 URI가 /어셈블리이름;component/ 처럼 명시적으로 지정된 경우 해당 어셈블리에서 리소스 파일을 확인하고 없을 경우 이미지 로드 실패
  2. 만약 제일 앞에 /가 없다면 가장 먼저 어셈블리에 해당 리소스 파일이 있는지 확인
  3. 다음으로 패키지가 위치한 Site of Origin에서 해당 파일을 요청하고 존재하지 않을 경우 이미지 로드 실패
  4. 만약 제일 앞에 /가 있다면 가장 먼저 Site of Origin에서 해당 파일을 요청
  5. 다음으로 XAP 패키지에서 컨텐트 파일을 확인하고 없을 경우 이미지 로드 실패
※노트
앞에서 설명한 것처럼 현재 실버라이트의 URI 표현은 뭔가 MSDN과 맞지 않는 부분이 있고 따라서 위의 검색 순서는 다음 베타 버전에서 변경될 가능성이 있으니 주의하세요.

절대 URI

절대 URI는 일종의 Site of Origin이지만 의미상 같은 도메인의 서버 뿐만 아니라 다른 도메인(크로스 도메인)의 리소스도 요청할 수 있다는 점에서 따로 분리했어요. 앞에서 살펴본 세가지 경우와는 달리 절대 URI는 말 그대로 지정된 절대적인 경로에서만 파일을 요청하게 되죠. 또한 절대 URI로 설정할 경우 요청하는 리소스의 종류에 따라 크로스 도메인을 사용할 수 있다는 점에서 앞의 세가지 경우와 다르다고 볼 수 있어요.

절대 URI는 다음과 같은 구성 요소로 이루어져 있죠.

  • 스키마
  • 호스트
  • 포트 번호
  • 경로

예를 들어 gongdosoft.com이라는 웹 서버에 있는 ghost.png라는 파일은 http://gongdosoft.com:80/ghost.png로 표현할 수 있고 각각,

http:// gongdosoft.com :80 /ghost.png
스키마 호스트 포트 번호 경로

로 구분할 수 있죠. 대체로 http-80과 같이 잘 알려진 스키마와 포트는 생략되는게 일반적이에요.

만약 스키마, 호스트, 포트 번호가 실버라이트 패키지의 위치와 다를 경우 접근 정책에 따라 제한을 받을 수 있으며 여기에 관해서는 URL Access Policy를 참고하세요.

절대 URI를 가진 리소스를 요청하는 방법은 다음과 같아요.

[XAML]
<Image Source="http://shiverlight.net/Images/AbsoluteURI.png" />

[C#]
Image img1 = new Image();
img1.Source = new BitmapImage(new Urihttp://shiverlight.net/Images/AbsoluteURI.png", UriKind.Absolute));

절대 URI도 Site of Origin과 같이 필요할 때 요청하는 리소스이기 때문에 리소스를 요청하는 시점에는 존재하지 않거나 요청에 실패하는 경우가 있을 수 있겠죠. 따라서 반드시 로드 실패에 대한 이벤트를 처리해야 한다는 것을 잊지 마세요.


참고


Posted by gongdo

Submit comment.