이야~, 이것보다 더 간단할 순 없겠네요. 여기서 저는 업로드된 파일을 받아들여 그것들을 media 디렉터리로 이동시키기 위해 PHP의 move_uploaded_file 함수를 사용하였습니다. 확실히 이상적인 애플리케이션은 충돌하는 파일명을 확인해야 되겠지만 이것은 단순히 테스트용 애플리케이션이라 단순하게 만들겠습니다.
저는 이 페이지를 Grid 클래스에 기반하여 작성하였습니다. Grid 클래스는 수많은 XAML 레이아웃 관리자중 하나입니다. 그것은 HTML의 테이블(table)과 유사합니다. 이 경우 저는 3개의 행을 정의할 것입니다. 첫 번째 것은 썸네일이 위치할 곳입니다. 가장 큰 두 번째 행은 이미지 미리보기가 위치할 곳입니다. 그리고 세 번째 행은 자그마한 상태 표시줄을 가질 것입니다. Grid.RowDefinitions 태그를 살펴보시면 여러분은 이러한 3개의 행들과 그것의 상대 너비들이 배치되어 있는 것을 보실 수 있습니다.
를 이용하여 정의될 것입니다. XAML에서는 컨테이너를 정의한 다음 특수한 속성, 이 경우에는 컨테이너 내의 요소가 필요한 곳을 지정하기 위해 Grid.Row와 Grid.Column을 사용합니다.
여기에 양념을 곁들이기 위해 마우스를 위에다 올려 놓으면 이미지 썸네일 주위에 글로우(glow) 효과를 주는 코드를 파일의 상단에 추가할 것입니다. 이 코드가 표 4에 나타나 있습니다. 리스트 4. 마우스오버 글로우 정의 이것은 XAML 버전의 인라인 CSS 스타일시트입니다. 이 경우 저는 이미지 타입과 IsMouseOver 속성이 true로 설정될 때 걸리는 트리거를 적용시킨 스타일을 정의합니다. BitmapEffect 속성이 변경되면 바깥쪽 글로우 효과가 추가됩니다. 바깥쪽 글로우 이미지에는 앤티-알리아싱 글로우 시각효과를 추가합니다. 여러분은 글로우의 색상, 글로우의 크기, 그리고 그 밖의 것들을 지정할 수 있습니다. XAML은 이미지 뿐만 아니라 거의 모든 객체에 적용시킬 수 있는 비트맵 효과들의 복주머니를 제공합니다. 여러분이 Adobe 포토샵과 그것의 이펙트 패키지에 익숙하다면 여러분은 XAML 툴킷에서 제공해주는 것과의 유사점을 발견하실 수 있을 겁니다. 그것들을 설명하기 위해 저는 표 5에서 보여지는 것과 같이 업로드 버튼에 배경을 선형 그라디언트 채움(linear gradient fill)을 가지도록 변경하는 자그마한 시각적인 표시효과를 줄 것입니다. 리스트 5. 업로드 버튼의 외관 바꾸기 제가 버튼의 배경 속성을 변경한 것에 주목해 주십시오. XAML의 모든 UI 요소들은 여러분이 개별 객체 수준에서든 요소의 클래스를 지정하든 표에 나타나 있는 방식으로 변경할 수 있는 XAML 요소들로 구성됩니다. 예를 들어 모든 버튼-혹은 특정 클래스의 모든 버튼-에 새로운 배경, 버튼의 이름에 사용되는 새로운 글꼴, 버튼의 내용을 위치시키기 위한 새로운 템플릿, 기타 등등의 것들이 주어질 수 있습니다. 버튼의 모든 상호작용성은 유지하면서 말입니다. 그러므로 예를 들어, 아이콘 버튼을 만드는 일은 XAML에서는 일도 아닙니다. 백엔드와 연동하기 이제 모든 인터페이스들을 한데 묶어놓으면 사용자 인터페이스 뒷단에 있는 Uploader.xaml.cs 클래스에 대한 작업을 시작할 수 있습니다. 가장 먼저 해야 할 일은 각각의 이미지에 관한 정보들을 가지고 있을 클래스를 작성하는 것입니다. 저는 그 클래스를 ImageInfo라고 부를 것이며 표 6에 나타나 있습니다. 리스트 6. ImageInfo private 클래스 using System; using System.IO; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Navigation; using System.Windows.Media.Imaging; using System.Collections; using System.Configuration; namespace UploaderProject { public class ImageInfo { private BitmapImage m_Image = null; public BitmapImage Image { get { return m_Image; } } private string m_sPath = null; public string Path { get { return m_sPath; } } private ImageInfo() { m_Image = null; m_sPath = ""; } public ImageInfo( string sPath, BitmapImage image ) { m_sPath = sPath; m_Image = image; } }이 클래스는 사실 전용 파일을 가질 만큼 중요하지는 않기 때문에 저는 그냥 Uploader 인터페이스 코드의 정의 파일과 동일한 위치에 박아놓을 겁니다. 백엔드의 작업을 완료하기 위해 저는 인터페이스 자체에 대한 코드를 표 7에 나타나 있는 것과 같이 Uploader 클래스로 작성합니다. 리스트 7. 인터페이스 이면의 Uploader 클래스 public partial class Uploader { private Hashtable m_ImageLookup = new Hashtable(); private WebClient m_Client = new WebClient(); private ArrayList m_PendingFiles = new ArrayList(); private ImageInfo m_CurrentlySelected = null; private string m_sCurrentDownload = ""; public Uploader() { m_Client.DownloadFileCompleted += new AsyncCompletedEventHandler( WebClient_DownloadFileCompleted ); m_Client.DownloadProgressChanged += new DownloadProgressChangedEventHandler( WebClient_DownloadProgressChanged ); } void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { PercentDone.Text = String.Format("{0} - {1}% done", m_sCurrentDownload, e.ProgressPercentage); } void WebClient_DownloadFileCompleted(object sender,AsyncCompletedEventArgs e) { PercentDone.Text = ""; StartUpload(); } void StartUpload() { string sPath = (string)m_PendingFiles[0]; m_sCurrentDownload = Path.GetFileName(sPath); m_PendingFiles.RemoveAt(0); string sUploadPage = ConfigurationSettings.AppSettings.Get("UploadPage"); if ( m_Client.IsBusy == false ) m_Client.UploadFileAsync(new Uri(sUploadPage), "POST", sPath, null); } protected override void OnInitialized(EventArgs e) { string[] sFiles = Directory.GetFiles( Directory.GetCurrentDirectory() ); foreach( string sFile in sFiles ) AddImageButton( sFile ); } void Upload_Click(object sender, RoutedEventArgs e) { if (m_CurrentlySelected != null) { m_PendingFiles.Add(m_CurrentlySelected.Path); StartUpload(); Upload.Visibility = Visibility.Hidden; m_CurrentlySelected = null; } } protected void AddImageButton(string sPath) { if (Path.GetExtension(sPath).ToLower() != ".jpg") return; BitmapImage image = null; try { image = new BitmapImage(); image.BeginInit(); image.UriSource = new Uri("file://" + sPath); image.EndInit(); } catch { } if (image != null) { int nFixedHeight = 100; double dScaleRatio = (double)image.Height / (double)(nFixedHeight - 10); Canvas b = new Canvas(); b.Width = (int)(image.Width / dScaleRatio) + 5; b.Height = nFixedHeight; Image bi = new Image(); bi.Source = image; bi.Width = (int)(image.Width / dScaleRatio); bi.Height = nFixedHeight - 10; bi.Style = (Style)DocumentRoot.FindResource("TopImage"); bi.SetValue(Canvas.LeftProperty, 5.0); bi.SetValue(Canvas.TopProperty, 5.0); bi.MouseUp += new MouseButtonEventHandler(TopImage_MouseUp); b.Children.Add(bi); m_ImageLookup[bi] = new ImageInfo(sPath, image); ImageList.Children.Add(b); } } void TopImage_MouseUp(object sender, MouseButtonEventArgs e) { if (m_ImageLookup[sender] != null) { m_CurrentlySelected = (ImageInfo)m_ImageLookup[sender]; DisplayImage.Source = m_CurrentlySelected.Image; Upload.Visibility = Visibility.Visible; } } } }코드의 대부분은 AddImageButton 메소드에 들어있는데, 이 메소드는 이미지 썸네일을 화면에 추가합니다. 메소드가 하는 일은 동적으로 새로운 Canvas 객체를 생성한 다음 이미지 객체가 위치한 곳에서 그 객체를 인터페이스의 ImageList 요소에 추가하는 것입니다. 동적 HTML과 같이 새 인터페이스 항목을 화면에 추가하는 것은 시각 트리에 새로운 노드를 추가하는 것 만큼이나 쉽습니다. 이 예제의 다른 흥미로운 부분은 Upload 버튼이 클릭되면 호출되는 Upload_Click 메소드입니다. 이 메소드는 WebClient 객체를 이용하여 웹 서버로의 파일 전송을 시작합니다. 이 WebClient 객체는 스크립팅 가능한 웹 브라우저의 대용물인데, 여러분은 페이지의 내용을 가져오고, 폼을 포스트하며, 그리고 심지어 쿠키와 세션을 저장하는 데 이 객체를 사용할 수 있습니다. 그러므로 여러분이 어떤 인증에 관한 요구사항을 가지고 있다면 여러분은 여전히 최초 로그인시에 WebClient를 사용할 수 있으며 그 다음에 파일을 업로드 시킬 수 있습니다. WebClient를 이용하는 것의 가치는 Uploader에 어떠한 특별히 비정형화된 방법이 없다는 것입니다. 이 메커니즘은 웹 브라우저가 파일을 업로드 하는 것과 정확히 동일한 방법을 사용하므로 PHP 웹 애플리케이션이 이 메커니즘을 지원하도록 아무것도 변경할 필요가 없습니다 애플리케이션이 필요로 하는 한 가지는 이미지 업로드를 처리할 페이지의 URL 입니다. 이는 표 8에 나타나 있는 것처럼 애플리케이션에 대한 XML 환경설정 파일에 설정됩니다. 리스트 8. 환경설정 파일 제 경우에는 동일한 라우터상에 있는 제 윈도 박스 옆에 놓여져 있는 Mac OS X상에 PHP 애플리케이션이 작동되고 있으므로 주소를 직접 입력해 놓았습니다. 최종 산출물에서 여러분은 http://mycompany.com/upload.php과 같이 IP 주소에 대한 DNS를 통해 해석된 호스트명을 사용하게 될 것입니다. 시험해 보기 PHP 웹 사이트가 완료되고 업로더의 프론트엔드와 백엔드 코드가 작성되면 이제 애플리케이션을 구동시켜서 그것이 작동하는지 알아볼 차례입니다. 그래서 저는 F5를 눌러 디버거를 실행시킨 다음 그림 1-1과 같은 것을 보게 됩니다. 그림 1-1. 업로더의 초기화면 다음에 제가 아름다운 노란색 꽃을 선택하자 C# 코드는 그 이미지가 프레임의 가운데로 오도록 지정합니다. 이것이 그림 1-2에 나타나 있습니다. 그림 1-2. 이미지 선택 후의 화면 이 지점에서 재미있을 것 같은 것은 창의 크기를 리사이즈 한다는 것인데, 창은 늘어나는 반면 요소의 레이아웃은 그대로 유지된다는 것에 주목해 주십시오. 이는 XAML을 사용하는 이점들 중 부분에 불과하며 그리드 레이아웃 시스템의 경우 특히 그러합니다. 계속해서 저는 업로드 버튼을 누르고 업로드가 완료될 때까지 기다립니다. 그 다음, 저는 이 사이트의 index.php를 탐색하여 파일이 정말로 제 요청에 대해 업로드 되었는지 그림 1-3과 같이 살펴봅니다. 그림 1-3. 업로드된 이미지 보기 드디어 XAML과 WinFX SDK이 제공하는 매력적인 기능을 이용한 초보적인 수준의 이미지 업로더가 만들어졌습니다. 이는 또한 오픈소스 세계에 대한 윈도의 세계적인 활약을 보여주는 괜찮은 예시이기도 합니다. 결론 최근 윈도 비스타에 관한 많은 이야기들이 오고 가고 있습니다. 출시될 것인가? 언제 출시될 것인가? 무엇을 탑재할 것인가? 저는 비스타가 출시될 것이라고 생각하고 있으며 WPF(Windows Presentation Foundation, XAML을 포함하고 있음)에 대한 저의 경험상 윈도가 무엇보다 먼저 WPF를 탑재할 것이라 확신하고 있습니다. 왜냐하면 그것은 비스타의 핵심이기 때문이죠. 하지만 부차적으로는 제가 참여하고 있는 프로젝트를 보자면 그것은 견고하며 라이브러리 설계가 상당히 괜찮다고 생각됩니다. 그런데 한가지 주의사항을 알려드릴 텐데요, 그것은 기존의 참조물들을 조심하라는 것입니다. WinFX SDK는 지금껏 몇 가지 변화가 있었습니다. 클래스명이 바뀌었으며, 속성이 변경되었으며, 그리고 파일의 기록방식이 변경되었습니다. 여러분은 특정 태그나 클래스의 사용법에 대한 사항들에 대한 정보를 얻으려면 구글에서 검색하시면 보실 수 있으실 겁니다. 솔직히 말해서 마이크로소프트 웹 사이트에도 이전 버전의 SDK의 낡은 정보들이 있습니다. 그러므로 여러분은 이것을 이용해서 개발할 때나 복사해서 붙여넣기할 때 코드가 현재 버전의 API에서도 여전히 작동하는지를 주의깊게 살펴보아야 합니다. 거듭 말씀 드리지만 저는 WPF에 감명을 받았으며 엔지니어와 그래픽 디자이너가 WPF를 이용하여 그들의 인터페이스를 다음 수준으로 이끌어가는데 있어 상당한 잠재력이 있다고 생각합니다. Jack Herrington는 20년의 경험을 가진 수석 소프트웨어 엔지니어입니다. 그는 Code Generation in Action, Podcasting Hacks, PHP Hacks의 3권의 책을 펴낸 저자이며 30개 이상의 다양한 분야의 기술을 다룬 기사를 쓰기도 하였습니다. TAG :
이전 글 : 오픈라즐로(OpenLaszlo) 소개 다음 글 : 웹 서비스 구현에서의 관심의 분리 최신 콘텐츠 |