관리 메뉴

새로운 시작, GuyV's lIfe sTyle.

닷넷 게시판 만들기 Part 51 본문

ⓟrogramming/asp.net 게시판

닷넷 게시판 만들기 Part 51

가이브 2011.06.27 13:34

 

2011/06/22 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 49
2011/06/24 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 50

1. 닷넷 개발환경 준비, 테스트
2. 닷넷 알아보기 [7/7]
3. asp.net 컨트롤 [10/10]
4. 데이터베이스(DB) [7/7]
5. 닷넷 게시판을 만들어보기 전에.. [4/4]
6. 게시판 만들기 <1> [18/18]
7. 게시판 만들기 <2> [3/..]



지난 시간에 이어서 포토게시판을 계속 만져보자.

그 전에, 버그를 조금 잡고 가도록 한다.

먼저, 수정기능에서 페이지가 넘어가지 않는 문제가 있는데, 수정해보자.
조회페이지인 board_view.aspx 에서 수정버튼을 클릭하면 페이지도 넘겨줘야한다. 그리고 검색중이라면 그 값도 계속 유지해줄 필요가 있는지도 생각해보고, 필요하다면 함께 넘겨주기로 하자.


 // 수정버튼
 void btnModify_Click(object sender, EventArgs e)
 {
  Response.Redirect(String.Format("board_write.aspx?c={0}&n={1}&page={2}&stype={3}&svalue={4}",
           CATEGORY_ID, BOARD_ID, NOW_PAGE, Request["stype"], Request["svalue"]));
 }




그리고 글수정시에 사용하는 board_write.aspx 에서도 수정 후 페이지 이동시에 값들을 받아서 넘겨줘야겠다.

    // 이후 글보기로 바로 이동
    Response.Redirect(
       "board_view.aspx?c=" + Request["c"] + "&n=" + Request["n"] + "&page=" + Request["page"]
       + "&stype=" + Request["stype"] + "&svalue=" + Request["svalue"]
       );




리스트(board_list.aspx)의 검색 기능에서도 버그를 발견했다.
처음 검색시에 버튼을 누르면 PostBack 이 된다. 그리고 board_list.aspx 에 GET 방식으로 값을 실어서 다시 페이지를 열어주는데, (서버컨트롤의 단점이다. 의도하지 않게 board_list.aspx 는 이 때 두 번의 로딩이 일어나게 된다) 이 때 검색종류와 검색어를 각각 stype, svalue 이름의 변수를 정했었고, 이 값이 있다면 검색으로 간주해서 값을 유지하게 했었다.

이렇게 검색모드에서 검색어를 넣고 다시 검색해도 이전의 값들이 유지되는 버그가 있다. 이는 PostBack 문제이다.
검색결과 상태는 IsPostBack 이 아니지만 검색버튼을 누른 후에 잠깐 IsPostBack 이 true 가 되고, 이 때 stype/svalue 두 개의 값이 존재하기 때문에 사용자가 입력한 현재의 값이 덮어씌워진다. 그러므로 다음처럼 수정해서 간단하게 해결하자.

  if (IsSearch)
  {
   // 검색일 때 호출
   btnCancel.Visible = true;
   
   if (!IsPostBack)
   {
    lstType.SelectedValue = STYPE;
    txtSearch.Text = SVALUE;
   }
  }




PostBack 이 되는 시점일 때는 강제할당을 하지 않게 하면 재검색시에는 값 유지를 하지 않으면 되겠다.
asp.net 은 서버컨트롤이라는 녀석을 많이 사용하기 때문에 오류가 나지는 않지만 값들이 의도하지 않게 바뀌지 않거나 이상하다면 1차적으로 PostBack 을 의심할 필요가 있다. 우리는 지금 일부러 서버컨트롤만 이용해서 만들고 있지만, 되도록 사용을 지양하는 것이 좋다. (하드웨어가 워낙 발전해서 옛날 말이긴 하지만 중대형 웹 사이트는 네트워크 부하도 무시못한다.)

이 외에도 게시판에는 알고도 넘어간 버그, 필자도 모르는 버그 등이 많이 숨어져 있을 것이다. ^^


이제 다시 돌아와서..
현재 포토게시판은 이미지파일을 업로드 하면 리스트에서 작게 표시가 되고 있다. 업로드한 이미지 파일을 그대로 보여주는데, 다음 그림을 보자.





귀여운 코알라 이미지에 오른쪽 버튼 - 속성을 보니, <img>태그 속성에 width 를 80 으로 준다고 해서 이미지 자체의 사이즈는 줄여지지 않는 당연한 현상(브라우저는 무조건 전송받은 후 처리)이 일어나는 것을 알 수 있다. 대충 계산해서 리스트가 12개니까 약 10메가 조금 안되는 전송이 일어나고 있는 것이다. 이는 매우 비효율적이며, 속도도 느릴 것이다. 게다가 만약 '디카'로 찍은 사진을 그대로 올린다면...?

그래서, 게시판 글쓰기 기능에 두가지를 추가해보려고 한다.
기능 한개는 지금처럼 이렇게 업로드되는 이미지를 따로 작게 줄인 그림파일로 만드는 진짜 섬네일 파일을 만드는 것이고, 다른 하나는 이미지(파일) 업로드를 할 때 용량 제한을 거는 것이다.

용량 제한은 여러분들이 쉽게 구현할 수 있다. FileUpload 컨트롤 MSDN에서 멤버들을 쭉 살펴보면 사용자가 컨트롤에 넣어서 전송받은 파일의 용량을 알 수 있는 FileUpload.PostedFile.ContentLength 라는 속성이 있다. 이는 Int32(int) 형이며 바이트(byte)단위이다. 1024바이트가 1킬로바이트(KB), 1024 킬로바이트가 1메가바이트(MB), 1024메가바이트가 1기가바이트(GB), 1024기가바이트가 1테라바이트(TB)로 올라가는데, ContentLength 를 잘 나눠주면 이쁘게 aaa.jpg (3.2 MB), bbb.jpg (382 KB) 같이 DB 테이블에 컬럼을 따로 주고 파일명처럼 따로 저장해주면 되겠다. 혹은 꼭 DB에 넣지 않더라도 물리적으로 존재하는 파일을 파일 관련 클래스인 FileInfo(클릭시 MSDN으로 이동)로 읽어와서 용량을 그때 그때 가져오는 방법도 있겠다. 구현 방법은 생각해보고 생각한 대로 시도해보자. 숙제(?)이니 꼭 해보시기 바란다.


그럼, 필자와 함께 진짜(?) 섬네일을 만들어보자. 당장 생각해보기에, 이미지 파일을 작게 줄여주는 방법을 알아야겠다. 이미지에 관련된 네임스페이스를 찾아보도록 하자.

http://msdn.microsoft.com/ko-kr/library/ms229335(v=VS.80).aspx

오랜만에 MSDN을 다시 살펴보려니 신나지 않는가?




좌측에 머리아픈 네임스페이스 목록을 보니 Drawing 이라고 있다. 그리기! 그렇다. 닷넷(asp.net 에 국한된 것이 아니다)에서는 이렇게 그리기 관련 네임스페이스가 존재한다. 그리고 이미지는 따로 System.Drawing.Imaging 이라는 네임스페이스가 존재하는 것 같다.




System.Drawing.Imaging 네임스페이스를 클릭해보니 위와 같이 나온다. 중요한 내용은 GDI+ 이미징 기능을 제공한다는 말인데, 기본 그래픽 기능은 System.Drawing 에서 제공한단다. 하단에 노란 박스로 '주의'라고 되어 있는 녀석이 있다. 한번 읽어보자. 대놓고 성능이 저하되고 오류가 발생하는 등 예기치 않은 문제가 생길 수 있다고 한다. 그러면 왜 제공을 하는거지? 우리 같은 초보는 겁이 덜컥 날 수 있겠다.

GDI+ 는 Graphic Device Interface 의 약자이다. +가 붙은건 뭐 버전업 같은 이유로 붙었나 보다.
일단은, 어느 고수가 써놓은 GDI+ 에 대해 좀 읽어보도록 하자.

http://ko.wikipedia.org/wiki/%EA%B7%B8%EB%9E%98%ED%94%BD_%EC%9E%A5%EC%B9%98_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4

쉽게 풀이를 해드린다면, 이 GDI 라는 것은 이름처럼 Device, 그러니까 하드웨어를 건드린다. 윈도우 운영체제에서 여러가지 하드웨어 사용자가 직접 사용할 수 있게 해주는데, 우리 모니터에 나오는 녀석이 그래픽카드를 통해 출력되는 영상이다. 자세히는 모르겠으나 하드웨어에 접근하기 때문에 "주의"라는 말이 붙은 것이다. 그리고 닷넷 2.0 당시에는 모두 없었던 기술이지만, 닷넷 3.0이 나오고 나서 프로그래밍 방식이 지금 이 GDI+ 에서 WPF 라는 쪽으로 넘어오게 되었다. WPF에서는 그림을 그릴 때 GDI+를 사용하지 않는다. (사용할 수도 있지만) 물론 아직까지 많은 프로그램은 GDI+를 이용한다.

한번 더 정리를 하면, 이 GDI+는 닷넷에서 직접 관리하지 못한다. 닷넷 코드는 Managed Code 라고 부르는데, 직역하면 '관리되는 코드'라 할 수 있고, 이는 닷넷 프레임워크에서 자체적으로 관리한다. 네트워크, USB등 하드웨어 제어를 하기 위해서 프로그래밍을 작성시 잘못되더라도 닷넷에서 모두 관리되기 때문에 하드웨어에서 발생하는 저급(Low Level) 수준의 오류가 발생하지 않고(예를 들어서 블루스크린 같이 뻗어버리는) 닷넷 자체가 죽어버리면 그만인 것이다.

설명이 좀 길어졌는데, 이 GDI+는 관리되는 코드로 작성하지만 실제로는 닷넷에서 관리하지 못하는 녀석으로 분리되어 있다고 생각하자. Unmanaged Code 를 건들기 때문이다. Native Code 라고도 불리는 닷넷 이전의 프로그래밍 방식의 DLL(닷넷의 DLL과 확장자는 같으나 전혀 다른)을 말한다. 그렇기 때문에 메모리 유수가 발생할 수 있다. 닷넷 자체에서 메모리를 잡고 있다가 자동으로 거두는 가베지 컬렉터(GC)라는 기능에서 벗어나기 때문이다. 그러나 이런 것들을 사용한다면 저급 수준의 프로그래밍을 개발할 수 있다. 자세한 내용은 이 블로그에서 고수님의 설명을 좀 더 읽어보시기 바란다.


다시 우리 프로그램으로 돌아가자. 
섬네일 이미지 처리를 하기 전에, 선행학습이 필요하다. 빈 aspx 페이지를 만들도록 하자. Page_Load() 메서드도 만들어준다. 당연히 네임스페이스 참조도 필요하다.




준비가 끝났으니 간단한 예제를 하나 만들어보자.
우리가 만들고 있는 게시판 포토게시판에서 최근 자료를 하나 가져와서 그 이미지에다가 제목, 작성자를 그림 위에 그려준 후 출력하는 것을 만들어보자.

asp.net 에서 GDI+를 사용하기 위해서는 System.Drawing.Graphics 라는 클래스를 이용해야 한다.





기본적으로 이렇게 시작할 수 있다. 주석을 달아놓았으니 읽어보시면 될 것 같다.
DB로부터 1개의 레코드를 읽어와서 file_attach 컬럼에 있는 이미지 파일명으로 /upload 폴더에 있는 이미지를 직접 불러온다. 그리고 이를 Bitmap 이라는 클래스를 이용해서 가져온다. 이미지 객체를 하나 생성했다고 보면 되겠다.

Bitmap 클래스에 대한 자세한 정보는 MSDN에서 보도록 하자.
http://msdn.microsoft.com/ko-kr/library/system.drawing.bitmap_members(v=VS.80).aspx


그리고 실제 제어를 담당하는 Graphics 클래스를 이용해 잡아둔다. 이렇게 되면 첨부된 이미지 파일을 'g' 라는 이름으로 사용한다는 것이다. 이 Graphics 클래스는 많은 기능을 가지고 있다. 우리가 하려는 것은 이미지 위에 글을 써 넣는 것이다.


Graphics.DrawString() 메서드를 살펴보자.
http://msdn.microsoft.com/ko-kr/library/system.drawing.graphics.drawstring(v=VS.80).aspx

오버로드 된 메서드들 중에서 입맛에 맛는 것을 사용하면 되겠다. 첫번째 메서드를 이용하자.




그릴 문자열은 제목과 이름이니 총 2번을 사용하면 되고, 그리고 Font 클래스, Brush 클래스, PointF 구조체를 이용해서 어떤폰트를 써서 어떤색으로, 어디다가 그릴 것인지를 지정하면 된다.

 




위의 그림처럼 인수로 들어가는 클래스는 모두 생성자를 이용해서 초기화시켜서 바로 사용하면 되겠다.
맨 마지막 인수는 왼쪽 맨 위에서 'X위치, Y위치' 형식으로, 필자의 첨부파일 이미지는 X: 1024, Y: 768 이므로 이 범위에 들어갈 수 있게 지정하면 되겠다. (수정: 올린이의 위치를 50, 100 으로 지정해서 겹쳐짐을 방지하자)

이렇게 실행해보자. 무소식이 희소식이다. 아직 처리만 했고 출력 코드는 없기 때문이다.

출력은 두 가지 방식이 있다. 화면에 바로 보여줄 수 있고, 이렇게 'g'로 다시 그려진 Bitmap 인 'img'를 이미지 파일로 저장을 할 수도 있다.



45번째 줄 처럼, Bitmap.Save(..) 메서드를 이용해서 저장한다.
( http://msdn.microsoft.com/ko-kr/library/system.drawing.image.save(v=VS.80).aspx )

처리된 이미지는 upload 폴더에 new_"파일명" 으로 다시 저장했다.





이렇게 처리되어 저장된 것을 볼 수 있다.
Bitmap 의 크기를 변경하려면 새로운 Bitmap 에 Graphics.FromImage() 로 'g'를 잡아주고, 여기에 DrawImage() 메서드를 이용하면 된다. 그리고 이것을 저장하면 끝이다.





80x60 크기로 저장하면 다음처럼 작게 나올것이다.






간단하게 Graphics 를 이용해서 이미지파일을 Bitmap 으로 불러와서 이를 재가공해서 저장까지 해보았다. 

필자는 언제나 asp.net 의 결과물은 무조건 <태그>라고 했었는데, 사실 꼭 그런것 만은 아니다. 결과물을 따로 지정하지 않으면 asp.net 의 aspx 파일은 무조건 HTML형식이다. 웹 내부적으로 응답할 때 웹서버에서 Content-Type 이라는 헤더를 같이 실어서 요청한 브라우저로 전송을 하는데, 브라우저에서는 이 헤더 정보에 따라서 HTML로 열기도 하고, 이미지를 그대로 보여주기도 하고, 다운로드 처리를 하기도 한다. aspx 페이지에서 Content-Type 헤더값을 변경해주면 aspx 는 이미지파일이 될 수가 있다. Response 클래스에 존재하는데, 속성명은 ContentType 이다. 



이렇게 Response.ContentType 의 기본값을 바꾸어주면 이미지로 되며, 이를 Save() 메서드를 Stream 인수를 Response.OutputStream 에 지정해주면 그대로 Bitmap 의 이미지가 출력으로 가게된다. 




이렇게 되면 웹브라우저에서는 이미지가 그대로 나오게 되며 gdi.aspx 파일은 이미지로 인식하게 된다. 그리고 파일의 크기가 폭삭 줄어서 매우 가벼운 섬네일 이미지가 되었다.




이렇듯, asp.net 에서는 동적인 이미지를 만들기 위해서 이 GDI+를 사용한다. 그리고 이미지를 리사이징하는, 우리가 해볼 섬네일 이미지를 만드는데도 사용을 할 수 있다. 좀 더 고급 프로그래밍을 구사할 수 있다면 이미지에 자유롭게 워터마크를 넣을 수도 있고, 이미지 합성, 각종 필터 효과(흑백이미지 같은..)도 가능한 것이다.


이제 이것을 우리 포토게시판에 구현해보자. 글쓰기 기능은 어디든 공통적으로 사용되니 'photo'카테고리일 때에만 섬네일 이미지를 하나 만들어주면 될 것 같다. 파일명은 저장할 때에 자동으로 앞에 'small_' 을 붙여서 게시판리스트에 적용하도록 하자.




board_write.aspx 에 사용되므로 위의 그림처럼 네임스페이스 참조를 해주고..



기존 파일 업로드에서 섬네일 만드는 기능을 추가하도록 하자. 
여기서 또 생각해봐야 할 문제는, 섬네일 사이즈를 동적으로 조절해줘야 한다는 것이다. 지금의 원본의 큰 이미지 사이즈는 소스상에서 img.Width 와 img.Height 로 읽어올 수 있는데, 이 크기가 만약 4:3 비율이 아니라면 달라져야한다. 이를 무시하면 사진이 찌그러질 것이다. ^^ 프로그래머는 늘 고민해야한다. 


이제 board_list.aspx 에서는 원본이 아닌 섬네일 이미지가 출력되면 될 것이다.



드디어 완성되었다.



기존에 올라간 파일들은 섬네일이 없어서 깨지겠지만 말이다. 저렇게 엑박 이미지를 대체하려면 파일을 체크해서 없다면 다른 이미지로 교체해주는 방법이 있겠다. 또는 우리는 다루지 않지만 javascript 의 onerror 속성을 이용해 클라이언트 측으로 돌려서 처리도 가능하겠다.





오늘 내용은 여기까지이다.




[추가내용]
GDI+의 글꼴은 웹서버에 설치된 트루타입폰트로 사용가능합니다. 예로.


[제어판-글꼴]에 설치된 글꼴을 더블클릭하면 위의 그림처럼 글꼴 이름이 있고, 이것들은 대부분 내부적으로 영문 글꼴명을 가지고 있습니다. (글림-Gulim, 돋움-Dotum 등) 

지금은 "맑은 고딕" (띄워쓰기 주의) 으로 지정하면 위의 폰트로 사용가능합니다. 참고하세요.

5 Comments
  • 프로필사진 후배 2011.06.27 18:17 신고 이 강좌의 가치를 닷넷 입문하려는 분들이 많이 알아야 할텐데 말이죠..ㅋ
  • 프로필사진 박승수 2011.08.29 17:23 신고 강좌 너무많은 도움이 되었습니다. 감사합니다.

    한가지 질문이 있어서 그런데 이미지에 글씨 넣을때 폰트를 지정해 줄때 고딕, 돋움말고 웹폰트나 기타 다른 폰트가 가능한지 테스트 해보니까 안먹는거 같아서요 ㅠㅠ
  • 프로필사진 가이브 2011.08.29 22:18 신고 안녕하세요. 도움이 되셨다니 다행입니다. ^^

    결론을 말씀드리면 웹서버가 설치된 윈도우의 폰트(제어판-글꼴)에 있는 것들이 사용가능합니다. 원하는 폰트를 웹서버에 설치해주세요.

    폰트 이름은 'Gulim, 굴림' 처럼 영어이름,한글이름 모두 가능하구요. 간혹 폰트명에 공백이 있을 수 있으니 유의하세요. 이와 관련해서 오류가 발생하면 자세한 오류는 표시되지 않습니다. 내용에도 있듯이 닷넷에서 벗어나기 때문이거든요.

    트루타입폰트(TTF)만 가능하구요. 웹폰트는 (제 지식으로는) 클라이언트 웹브라우저에서 CSS를 통해 폰트를 서버로부터 다운받아 적용하는 형식이기 때문에 적용될 수 없습니다.

    그럼 즐프하세요~
  • 프로필사진 박승수 2011.08.30 12:52 신고 띄어쓰기 문제였군요.. 감사합니다^^
  • 프로필사진 a 2012.01.12 13:29 신고 ㄳ합니다...
댓글쓰기 폼