ASP.NET2010.02.05 17:06

원문 : http://haacked.com/archive/2010/02/04/aspnetmvc2-rc2.aspx

오늘 오전에 ASP.NET MVC 2 RC 2 가 발표되었습니다.

가장 눈에 띄는점은 유효성 체크 입니다.

ASP.NET MVC 가 처음 나왔을때 제가 느낀 불편한 점이 몇가지 있었는데요, 그 중에 유효성 체크도 있었습니다.
하지만, 이번 RC2 에서 가장 크게 바뀐 부분이고, 지금껏 많은 개발자들이 이부분에 대한 피드백을 주어서 반영된것 같습니다.

기존에는 유효성 검사를 하기위해서는 HttpVerbs.Post 동사 부분에서 If 문으로 처리를 하였죠?
다음처럼 말이죠...
'
' POST: /Product/Create

<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult
    ' Validation logic
    If productToCreate.Name.Trim().Length = 0 Then
	    ModelState.AddModelError("Name", "Name is required.")
    End If
    If productToCreate.Description.Trim().Length = 0 Then
	    ModelState.AddModelError("Description", "Description is required.")
    End If
    If productToCreate.UnitsInStock < 0 Then
	    ModelState.AddModelError("UnitsInStock", "Units in stock cannot be less than zero.")
    End If
    If (Not ModelState.IsValid) Then
	    Return View()
    End If

    ' Database logic
    Try
	    _entities.AddToProductSet(productToCreate)
	    _entities.SaveChanges()
	    Return RedirectToAction("Index")
    Catch
	    Return View()
    End Try
End Function

ref : http://www.asp.net/learn/mvc/tutorial-36-vb.aspx

하지만, 이번 RC2 에서는 이 부분이 단순하게 다음처럼 바뀌어버립니다.
'
' POST: /Product/Create

<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult
    ' Validation logic
    If ModelState.IsValid Then
	    Return View()
    End If

    ' Database logic
    Try
	    _entities.AddToProductSet(productToCreate)
	    _entities.SaveChanges()
	    Return RedirectToAction("Index")
    Catch
	    Return View()
    End Try
End Function


윗부분에 필드별 조건문은 모두 없어졌습니다.

핵심은 Models 영역의 <Required()> 어노테이션 선언 덕분입니다.
Input Validation vs. Model Validation in ASP.NET MVC

이제 좀 쓸만하겠는데요?

하지만 아직 URL Rewrite, Routing 부분은 좀더 개선이 필요해 보입니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.10.20 15:47

MVC 패턴에서 AJAX을 쓰기가 꽤 까다롭습니다.


위 그림과 같이 페이지 요청을 하면 라우팅 테이블(또는 룰)을 거쳐 Controller->Model->View 의 흐름을 거치게 됩니다.

기존 WebForm 에서는 간단히 처리가 되는데, MVC 모델에서는 간단하지가 않죠.

일전에 자바 프로젝트를 하면서 MVC 2 모델에 Spring 위에서 AJAX을 구현해야 하는 경우가 생겼습니다. 이때는 Return Type이 XML 문서로써, AJAX 요청에 대해서 MultiActionController 를 상속받아서 처리한 경우가 있었습니다. 이건 Spring 프레임웍에서 지원하는거라 별 어려움없이 xml 설정과 액션 우회 방식으로 처리 되어 실제 흐름은 위 그림과 비교 해 보면 실존하는 페이지 View 가 없고, Document 객체를 메모리에 Writing 시키는 방식으로 처리 했습니다.

제가 MS 제품을 좋아 하는 이유가 쓰기 편하게 설계가 되어 있다는 점 입니다. 마침 Walkthrough: Adding ASP.NET AJAX Scripting to an MVC Project 문서를 발견하여 간단히 따라 할수 있도록 정리 해 봤습니다.

  • 준비물
    • Microsoft Visual Studio 2008
    • ASP.NET MVC 2 - Download

1. 프로젝트 시작하기


위 두가지가 설치되었으면 Visual Studio 를 실행하고, 그림과 같이 프로젝트 템플릿을 ASP.NET MVC 2 Web Application 을 선택하고, 프로젝트명을 MVCAjaxApplication 이라고 입력한 후 확인을 누릅니다.



2. ASP.NET AJAX 스크립트 라이브러리를 스크립트 참조

솔루션 탐색기에서 /Views/Shared/Site.Master 를 선택하고, 소스보기를 한후 아래 코드를 head 에 추가합니다.



3. HomeController 클래스에 액션 메서드 추가

솔루션 탐색기에서 /Controllers/HomeController.vb 를 선택 하고, 다음과 같이 액션 메서드를 추가 해 줍니다.
    Public Function GetStatus() As String
        Return "Status OK at " + DateTime.Now.ToLongTimeString()
    End Function

    Public Function UpdateForm(ByVal textBox1 As String) As String
        If textBox1 <> "Enter text" Then
            Return "You entered: """ + textBox1.ToString() + """ at " + DateTime.Now.ToLongTimeString()
        End If

        Return [String].Empty
    End Function


4. Index 페이지 재 정의

지금까지 한 작업들을 보여주는 View 페이지(/Views/Home/Index.aspx)에서 <asp:Content> 부분을 다음과 같이 수정합니다.


5. 테스트

지금까지 작업한 내용을 확인하기 위해 "실행" 을 해 봅니다.
그러면 다음과 같이 잘 나옵니다.(혹시 index.aspx 에 포커스를 두고 실행하면 라우팅 오류가 날수 있으므로, 컴파일 후 Default.aspx 페이지에서 실행 하시기 바랍니다.)


이렇게 간단하게 ASP.NET MVC 에서 AJAX을 구현 할수 있습니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
Windows Azure2009.07.30 20:29
플랫폼을 Windows Azure 로 정하고 나서 급 관심이 높아 졌습니다.
Azure 위에서 데이터는 어떻게 처리가 되고, MVC 와 잘~ 붙일수 있을까? 라는 의문이 들어 자료를 찾던중 보물과도 같은 아티클을 찾게 되어 간단히 소개 하려 합니다.

Access Your Data On Premise Or In The Cloud With ADO.NET Data Services - Elisa Flasko

원문이고, 내용이 많기 때문에 간략하게 정리 해 봤는데요, 주관이 많이 섞여있으니 정확한 내용은 위 링크를 참조 하세요.

최근에 몇년동안 웹서비스에 대한 관심이 많아졌습니다. 이 웹서비스의 핵심은 HTTP 통신과 XML 기반의 데이터 전달이죠.
훌륭한 기술임에도 불구하고, 데이터 전달 속도가 느려 전체적인 퍼포먼스가 떨어진다는 비평가 들도 많았습니다. 저도 그중 한 사람이니깐요.

하지만, 이렇게 데이터를 XML 기반으로 구조화 되게 표현하면서 점점 데이터 중심적인 RESTful 한 인터페이스를 가지게 되었습니다. 이러한 기술 발전이 클라우드 컴퓨팅의 초석을 마련하게 되지 않았나 생각합니다.


제가 준비하고 있는 "한글 형태소 분석기 With ASP.NET MVC, SPRING.NET, iBATIS.NET ON VS2010 에서 어쩌면 iBATIS.NET 이 빠지게 될지도 모르겠습니다.

왜냐면 Elisa Flasko 이 양반의 아티클에서는 간단한 블로그 서비스를 Windows Azure + Azure Table + ASP.NET MVC 로 구현하고 있습니다. 그 중에 Data Service 부분인데, 어떻게 구현이 되었냐면...

우선, 미니 블로그의 형태를 ASP.NET MVC 패턴으로 설계가 되었고, 모델이 되는 녀석을 POCO(Plain Old CLR Objects) 로 만들었답니다. 이양반의 POCO는 이전에 제가 VO 라고 설명했던 엔티티 클래스와 비슷하다고 보시면 됩니다.

이 POCO를 RESTful 하게 표현하기 위해서 WCF Service 를 만들었고, 프로젝트에 웹참조를 시켰습니다.(대단한 발상인것 같네요)

이게 미니 블로그 데이터 서비스를 위한 POCO 입니다. 그러면 이녀석을 WCF Service 로 만들었다면 원하는 데이터를 어떻게 가져왔을까요? 이를 위해 LINQ 를 사용했네요... 이양반 기술을 아주 적합하게 잘 사용한것 같네요..
public ActionResult Index(string channel, string author)
    {
        microblogEntities svc = new microblogEntities(new 
        Uri("http://localhost:50396/MicroData.svc"));
        ViewData["channels"] = svc.Channels;

        int y = DateTime.Now.Year;
        int mm = DateTime.Now.Month;
        int d = DateTime.Now.Day;

        var q = from m in svc.Messages.Expand("Author").Expand("Channel")
                where m.Channel.ChannelName == channel && (m.DatePosted.Year == y && 
                m.DatePosted.Month == mm && m.DatePosted.Day == d)
                select m;

        if (!string.IsNullOrEmpty(author))
        {
            q = from m in q
                where m.Author.UserName == author
                select m;
        }

        ViewData["msg"] = q;

        return View();
    }


이 코드 보고 깜딱 놀랐습니다. DB 커넥션이라는 개념을 없애버리고, RESTful 한 데이터를 LINQ 를 이용해서 Expand() 메서드를 통해 원하는 데이터를 한번에 가져오는 방식!!!

그리고 가져온 데이터를 ASP.NET MVC 의 ViewData 에 할당하여 View 페이지로 전달을 합니다.

그러면, MVC 에서 M / VC 를 나눌수가 있게 됩니다. 물론 Windows Azure 를 이용하지 않고 이 방식을 적용 할 수가 있게 되죠. 그런데, 왜 Azure 를 사용했을까요? Windows Azure는 HTTP 를 통한 RESTful 한 스타일로 Blobs, Tables, Queues를 지원 합니다.

세가지 방식 중에서 Elisa Flasko 라는 양반은 Tables 를 사용하여 구현을 했는데, 엔티티 계층구조를 저장할 수 있는데, 테이블은 정해진 스키마를 가지지 않고 property를 통해 다양한 데이터타입을 가질수 있습니다. 이 테이블을 CRUD 하기 위해서 LINQ 를 사용할 수 있습니다.

또한, 이러한 RESTful 한 데이터 표현에 대해서 JAVA 어플리케이션으로도 이 클라우드 스토리지에 접근이 가능하게 되는것이죠. 대형 포털의 서비스를 보면, 페이지에 iframe 을 덕지덕지 밖아 넣었습니다.

우리가 Open Social 과 같은 서비스를 개발 하기 위해서 더이상 iframe 따위로 눈가리고 아옹하지 않아도 되었습니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.06.01 10:22
처음에 언급한것과 같이 테스트 해 보면서, 연재 하다 보니 오랜시간이 걸렸습니다. 지난 강좌에서 말씀드린 수정, 삭제를 구현하는부분에서 애매한 부분이 생겼는데, 억지로 라우팅을 변경하다보니 헤깔리는 부분이 많았습니다. 결론적으로, ASP.NET MVC에서는 라우팅 테이블이라는것이 존재하므로, 보통 웹 개발을 할때, 하나의 ASPX를 글쓰기, 수정하기 기능을 같이 넣는데, 이렇게 하는것은 좋지 않다는 결론을 내었습니다. 또한 validate 가 그리 녹녹하지는 않네요 ^^;

자 그럼 마지막 연재를 끝내보도록 하겠습니다.
구현해야 할 내용이 수정, 삭제 이고, 수정은 글쓰기의 view,  삭제는 view없이 이루어집니다.
그러면 글 읽기 화면에 수정, 삭제 기능을 추가해야 하므로, Detail.aspx 를 다음과 같이 수정합니다.
상단에 자바스크립트로, 1차적인 유효성 체크를 하고, 실제 post 액션이 발생했을때 맞는 패스워드 인지 또다시 체크를 하게 구현 하였습니다. post액션 발생시켜서 유효성 체크를 하는것이 구쓰리님의 ASP.NET MVC 책에 나오는 정석입니다.

상단에 Html.ValidationSummary("You must specify a password") 라는 메서드는 컨트롤러단에서 유효성 체크를 해서 ModelState.AddModelError() 메서드를 통해 값을 넣게 되면 나타나는 것인데, 랜더링이 되면 레이블 형태를 취합니다.

하단에 보면 약간 지저분해 보이는 코드가 있습니다. Html.Hidden(), Html.ValidationMessage() 메서드가 있는데, Html.Hidden() 메서드는 억지로 MVC 라우팅을 무시하고, POST 액션을 취하기 위한 방법인데, 앞으로는 수정 화면을 따로 View를 가져가도록 코드를 짜는것이 맞을것 같습니다. 그리고, Html.ValidationMessage()는 좀전에 얘기한 ModelState.AddModelError("pwd", "message") 라고 컨트롤러에서 오류값을 넣으면 Html.ValidationMessage("pwd")로 반환 받을 수 있습니다.

자... 그러면, 여기서 Modify, Delete 컨트롤러를 구현해야 합니다. BoardController.vb에 다음과 같이 구현하도록 합니다.


억지로 구현하다보니 컨트롤러가 좀 지저분해 졌습니다.
우선 HttpVerbs.Post 동작을 하는 Modify 메서드를 보면, ASPX에서 Hidden필드로 seq, password 필드로 pwd를 가져오기 위해 FormCollection을 받아옵니다.

우선 password가 유효한지 검사해서 유효하지 않으면 DetailController에 오류를 담아서 보내고, 유효하다면, 해당 seq로 게시물이 있는지 찾아서 게시물을 VO에 셋팅 합니다.

여기서 자세히 보면

이 구문이 write컨트롤러와 modify컨트롤러를 같이 쓰게될때 들어가게 되는 지저분한 코드 입니다.

만약 복잡한 화면에 코드가 많이 들어가야할 어쩔 수 없는 경우라면 이런 방법으로 하는것도 그리 나쁘진 않을것이지만, 유지보수 차원에서 그리 추천하는 코드는 아닙니다.

Delete 메서드를 보면 Modify 와 비슷합니다. 단지 Delete는 View가 없기 때문에 아래처럼 심플하게 구현되어 집니다.


자... 그러면, Modify, Delete 메서드에서 새롭게 추가된 Facade 객체의 메서드는 다음과 같이 3가지가 추가 되었습니다.
Me.sbFacade.getValidPwd(formValues)
Me.sbFacade.updateBoard(boardDetail, formValues)
Me.sbFacade.deleteBoard(id)

그러면, 이번에는 지금까지 했던 코딩 방식과는 다르게 반대로 코딩을 해 보겠습니다. 목적지 부터 해 봅니다.
(이렇게 하는것이 더 편하더라구요 ^^;)

Board.xml 을 다음과 같이 마지막에 3가지 statements를 추가 하도록 합니다.


그리고, 인터페이스 BoardListDAO.vb 를 다음처럼 코드를 추가 합니다.



SqlMapBoardListDAO.vb 클래스를 다음처럼 코드 추가 합니다.



getValidPwd 메서드는 FormCollection 을 받아서 해당게시물의 작성자 비밀번호가 맞는지 값을 Integer 타입으로 돌려주는것입니다. 맞으면 1, 틀리면 0

updateBoard 서브프로시져는 가져온 보드에 폼값을 받아서 업데이트 처리 해 줍니다. update이므로 반환값이 없습니다. 더욱 정교하게 설계를 한다면 반환값을 True, False으로 업데이트 성공유무를 오류메시지와 함께 표현해 주는것도 좋은 방법입니다.

deleteBoard 서브프로시져 마찬가지로 삭제 하고 받을 필요가 없습니다. 향상된 UI 에서는 "xx개 행에 적용되었습니다." 라는 메시지를 뿌려주곤 하는데, 이때도 마찬가지로 affectedRows 값을 받아와서 표현해 줄 수 있습니다.

아... 참고로, VB.NET 에서는 인터페이스에 메서드를 추가 하면, 구현하는 클래스는 Implements 인터페이스 부분에 파란 밑줄이 표시되는데, 이 파란 밑줄 젤 끝에서 엔터를 치면 자동으로 구현받는 메서드가 생성이 됩니다.

이제는 SpringBoardFacade.vb 인터페이스에 메서드를 추가할 차례입니다. 다음처럼 추가 합니다.


마지막으로 SpringBoardImpl.vb 클래스에서 인터페이스에 선언된 메서드를 구현 해야 합니다. 다음처럼 코드를 추가 하도록 하겠습니다.


 
여기까지가 ASP.NET MVC + SPRING.NET + iBATIS.NET 모두 합쳐서 만들어본 간단한 게시판의 하나의 LifeCycle 입니다. 제 생각에 Log On, About 클릭 하면 오류가 날건데요, BoardController 를 참조하여 해당 컨트롤러의 contructor injection을 objects.xml 에 구현해 주어도 됩니다. ^^;

원래는 이런거 안해줘도 잘 돌아가는데 왜 해주냐고요? 
음... 오픈소스 프레임웍이 모든걸 다 해주지는 못합니다. 훌륭한 닷넷 프레임웍 을 도울뿐이죠... 그러기 위해서는 약간의 수고가 따르는것 같네요.

단, iBATIS.NET은 정말 훌륭한것 같습니다. 서비스를 운영중에 어떤 쿼리가 변경되어야 한다고 할때 예전에 RDO 가 뜰때 sp기반의 어플리케이션을 작성할때 쿼리가 수정이 될때 sp만 수정하면 되었습니다. 하지만, 모두 sp로 때려넣다 보니, DB부하가 커지게 되죠.. 그러면 간단한 부분은 inline 쿼리로 처리 하게 되는데, inline쿼리로 구현했던 부분이 수정이 된다고 하면?
재배포 해야 합니다. 

하지만, iBATIS.NET을 붙이면 해당 xml 파일의 쿼리만 수정해 주면, Dynamic Loading 을 합니다. 트랜잭션관리 또한 우수합니다. SqlMapxxxxxDAO.vb 에서 메서드에서 Mapper.Instance().QueryForObject("blabla", vo) 부분 기억나실꺼에요..
이 매퍼를 통한 쿼리 호출시에 같은 메서드 안에서는 트랜잭션 처리가 됩니다.
예로, 첫번째 줄에 삭제, 두번째줄에 업데이트, 세번째 줄에 조회 라고 한다면, 하나라도 오류가 생기면 자동으로 롤백 처리가 됩니다.

오픈소스 프레임웍이 이처럼 좋긴 하나 모든걸 해결해 주지는 못합니다.

실생활에 비유를 하자면 제 생각엔 오픈소스 프레임웍은 망치 이고, 사람은 닷넷 프레임웍이라고 생각합니다.
사람은 손으로 컴퓨터도 다루고, 밥도 먹고, 운전도 할 수 있습니다. 하지만 특정한 도구 없이는 못을 박기에는 힘듭니다. 쉽게 해 주는것이 망치 이죠.. 단 벌레 한마리 잡는데 망치를 사용하는것이 맞는것일까요?

망치 매니아 라면 그러셔도 좋습니다.!! 단 유리에 붙어 있는 벌레를 잡으러 망치를 들다간 큰일 나겠죠? :)

지금까지 한 소스코드를 첨부 합니다. :)


 
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.27 13:10
간단한 게시판 하나 만드는데 연재가 길어지고 있습니다. 셋팅과 부연설명등이 잇따라 나오면서 이렇게 되어버렸습니다. ㅡㅡ;
이번 포스트는 글 읽기 입니다. 처음 부터 열심히 따라 하셨다면 쉽게 구현 할수 있을것 입니다.
전체적인 패러다임은 글쓰기 화면과 바슷합니다.

우선 목차 페이지 부터 시작하겠습니다.
글읽기를 위해서 목차의 제목에 링크를 달아야 합니다.

그림과 같이 링크를 달려고 하는데, 어떤 부분을 고쳐야 하는지 한번 봅시다.

처음에 셋팅할때 보았던, Global.asax 의 라우팅을 보면 {controller}/{action}/{id} 라고 해석이 되어집니다.
즉, 예전에는 Detail.aspx?seq=4 라는 표현이 /Board/Detail/4 라고 표현이 됩니다.

그러면 리스트 페이지에서 title을 나타내는 부분에 링크를 걸어주면 됩니다.

기존코드에서 약간 변경이 되었습니다. 기존에 비해 a 태그와 Html.Encode 메서드가 추가 되었습니다. a 메서드의 링크값은 위에서 설명한것과 같이 표현 방식이고, Html.Encode() 메서드는 헬퍼 메서드 입니다. 데이터를 입력하고, 웹페이지에서 다시 보여줄때, encode를 해 주는것이 매우 중요합니다. 왜냐면 악의를 품고 javascript injection 공격에 대한 대비책이 될수 있으니깐요.. 해커들이 개인정보를 빼낼때 주로 인젝션을 사용하는데, 이때 encode 시키면 결과값을 쉽게 알아볼수 없기 때문에 텍스트 데이터에 대해 display 할때는 가급적 Html.Encode() 메서드를 사용하기를 권장합니다.

그럼 상세 내용 보는 페이지(view)를 Detail.aspx 라고 한다면 BoardController 에서 라우팅 메서드를 하나 만들어야 합니다.
기존 코드와 함께 추가된 Detail() 메서드를 추가해 봅시다. BoardController.vb

여기서 주의해야 할 부분은 받는 파라메타의 변수값은 무조건!! id 라야 합니다. 지금은 이유를 모르겠지만, 내부적인 매핑이 별도로 이루어지는 모양입니다.

이렇게 했으면, boardDetail = Me.sbFacade.getBoardDetail(id) 부분에 파란줄이 생길것입니다. 파사드에 메서드가 없기 때문이죠.. 그럼 파사드에 추가해 보도록 하겠습니다.

우선 SpringBoardFacade.vb 인터페이스에 getBoardDetail() 메서드를 추가 해 봅시다.


그리고, SpringBoardImpl.vb 클래스에 구현을 합니다.


마찬가지로, BoardListDAO.vb 인터페이스에도 추가해 줍니다.


그다음은????? SqlMapBoardListDAO.vb 클래스에 구현합니다.


실제 구현 부를 보면 정말 심플합니다.
Mapper.Instance().Update("updateReadCount", seq) 는 현재 게시물의 조회수 1 증가 시키는 명령입니다. update 문이겠죠? 그리고 다음줄에 getBoardDetail 은 게시물의 내용을 가져오는 부분입니다. 이게 딸랑 두줄만에 끝나죠 ^^*

그럼 이제 Board.xml 에서 쿼리를 추가해 줘야 합니다.


간단히 쿼리 두개가 statement id 값을 가지며 추가가 되었습니다.
그러면 다시 컨트롤러(BoardController)로 돌아와 보면 View가 빠져있는걸 알수 있습니다.
그럼 Detail.aspx 라는 뷰를 추가 해 봅시다. 코드 하이라이트가 asp.net 코드는 인식이 안되므로 캡춰 했습니다. 참고로 Write.aspx 와 비슷하니 참조해서 코딩 하시면 됩니다. (이런 무책임함... ㅡㅡ; 죄송합니다. )

여기까지 코딩 했으면 이제 실행해 봅시다. 두번째 글을 클릭하면 다음과 같이 글 보기 화면이 잘 나올것 입니다.

짠~ 근데 날짜 형식도 이상하고.. 많이 허접해 보입니다. 어쩔수 없습니다. 최소한의 디자인으로 이정도면 훌륭하다고 판단이 됩니다. 죄송합니다. ㅠㅠ

이렇게 글 보기 화면까지 훌륭하게 잘 만들어 봤습니다.
남은건 수정, 삭제 입니다. 요것 까지 해서 하나의 게시판 Life Cycle 를 만들어 본 후에 대망의 제 2탄!!!

지금 생각하는건, 똑같은 게시판이지만, AJAX와 jQuery 까지 붙이고.. 욕심이 생기는 부분은 아예 View를 silverlight 로 만들어 버릴까? 하는 생각도 있습니다. 의견을 주시면 적극 반영하여, 2탄 강좌에서는 태요님과 같이 깔끔한 연재가 되도록 할께요 ^^;

앗... 혹시나 하는 생각에 hoons.kr 에서 찾아보니 훈쓰님이 다음과 같은 답변을 달았네요...


참고 해야 겠습니다.

그럼 좋은 하루 되세요~
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.24 20:30
이번에는 글 쓰기 페이지를 개발할 차례입니다. 물론 유효성 체크나 기타 유용한 기능은 게시판 개발의 한 라이프 사이클이 지난 후에 일명 "고도화 작업" 을 하면서 더 깊이 다뤄 보고, 지금 이 게시판은 오픈소스 프레임웍과 ASP.NET MVC 와의 유연성과 확정성을 알아보기 위한 테스트 게시판입니다. 똑같이 따라했는데 오류가 나신다고요? 죄송합니다. 중간중간 빠뜨린게 많나봅니다. ㅡㅡ; 중간중간에 올리는 소스를 참고 하시어 빠진부분을 체크 해 보아요~

자... 서두가 길었는데, Write 페이지를 만들기 위해서 이전에 했던 부분을 약간 수정해야 하는 부분이 있는데 그것부터 하고 넘어 가겠습니다.

지난시간에 했던, List.aspx 에서 버튼 링크를 잘못 넣었습니다. 아래와 같이 onclick 부분을 수정해 주세요 ^^;
<input type="button" value="글쓰기" onclick="location.href='/Board/Write'" />

그리고, /Views/Board/Write.aspx 를 추가 합니다. 그리고 캡춰된 이미지와 같이 코딩합니다.

이렇게 코딩 하고 나면 이처럼 허접한 화면이 됩니다. ^^;

지금 우리에게 중요한건 디자인이 아닙니다. ㅡㅡ; 죄송합니다. ㅠㅠ

흠... 그리고 저장버튼을 눌렀을때 HomeController의 Write 액션을 취합니다. 물론 이때 Http의 Post 동사를 취하게 됩니다.(보통 사람들이 이렇게 어렵게 말 하는데, 그냥 어트리뷰트 추가해 주면되니 걱정 하지 마시기 바랍니다.)

자... 그럼 이제 어떻게 받는지 확인해 보죠.
BoardController.vb 를 다음과 같이 Write메서드를 오버로드 해서 하나 더 만들어 봅니다.


이렇게 만들면 Me.sbFacade.setBoard(formValues) 요기 파란죽이 쫙~ 갑니다. SpringBoardFacade에 없기 때문이죠.
그럼 이번에는 파사드에 추가 해 줍니다. SpringBoardFacade.vb


그리고, SpringBoardImpl.vb 클래스도 메서드 구현해 줍니다.


잘 보시면 파라메타로 FormCollection 이란걸 가지고 다닙니다. 보통은 컨트롤러에서 받아서 VO에 셋팅하고 VO를 가지고 다니지만, 완전한 로직 분리를 위해 폼컬렉션 자체를 가지고 다니기로 했습니다.

역시나 파란줄이 쭉쭉 갑니다~ DAO 에도 메서드를 선언해 줘야 하죠. 우선 BoardListDAO 인터페이스를 다음처럼 메서드 하나 추가 해 줍니다.


마찬가지로 구현을 해야 하니 SqlMapBoardListDAO.vb 클래스를 다음과 같이 변경합니다.


여기서 눈여겨 보셔야 할 부분이 getNewThread 메서드와 FormCollection 에서 폼 컬렉션을 VO 에 셋팅 하는 부분입니다.
우선 getNewThread 메서드는 태요님의 강좌에서 thread를 마지막 thread 번호보다 1000을 크게 하는것인데, 이것을 태요님은 프로시져로 처리 하셨지만, 저는 우선 select 해서 insert 하기로 하고, select를 해 보는것입니다. 중요한게 아니니 패쓰~

자...  여기까지 했다면 마지막으로 쿼리를 짜야 합니다. Board.xml 파일에 우리가 위에서 호출하는 getNewThread와 setBoard 를 추가해 봅시다.


여기까지 했으면 다 된것입니다.

그럼 실행 해 볼까요? 오류없이 잘 뜰것입니다. 글쓰기 화면에서 다음처럼 입력해 보겠습니다.

그리고 저장 버튼을 팍~ 누르는 순간..... 다음과 같은 화면이 잘 나옵니다.~

여기까지 해서 목차화면, 글쓰기 화면 까지 만들어 봤습니다.

다음강좌에는 글 읽기, 글 수정 부분을 하겠습니다.
저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.22 10:10
이번 강좌의 목표는 list 페이지에 출력 해 보는것입니다. 물론 페이징이나 링크 등이 안먹지만 리스트 화면으로 출력 해 보겠습니다. 지난 강좌에 이어 설정파일 수정 부터 하겠습니다.

우선 objects.xml 을 다음과 같이 변경합니다.


마찬가지로 BoardController도 Facade 패턴을 적용하기 위해 생성자 주입을 시킵니다.
다시한번 왜 Facade를 사용하는지 간단히 설명드리자면 Facade 패턴은 복잡한 서브 시스템에 통일된 인터페이스를 두어 복잡한 API를 단순화 시켜 줍니다. 각각의 모듈을 서브 시스템 단위로 쪼개어 구성하는 것은 시스템의 복잡도를 낮춰 주지만 반대로는 각 서브시스템간의 결합도가 증가 하게 됩니다. 이러한 서브시스템 간의 의존도를 낮추고 서브 시스템의 사용자 입장에서 사용하기 편한 인터페이스를 제공하고자 하는것이 Facade 객체 라 합니다. 
Facade객체는 실생활에 따져봤을때 호텔의 인포메이션 센터와 같다고 할수 있습니다. 각 요청을 한곳으로 집중시키는 방식을 말 합니다. MVC 모델에서 요청은 항상 Controller가 받는데, 이 요청을 받을때 Facade 가 중앙 집중식으로 관리 해 주기 때문에 MVC 모델의 패턴에 적합하다고 판단이 되어 채택하게 되었습니다. ^^*

그럼 이제 BoardController 클래스를 다음과 같이 코딩합니다.


여기서도 마찬가지로 Facade 를 통해 게시물 리스트를 가져옵니다. 아주 간격하지 않나요? ^^;

그럼 이 쿼리는 Board.xml 에서 처리 되기 때문에 다음과 같이 변경합니다.


잘 보시면, resultMap과 select 가 1:1 로 매핑됩니다. 사실 이 부분이 좀 애매한 부분인데, 자바 버전에서는  하나만 선언 해 놔도 문제가 없었지만, iBATIS.NET에서는 select하는 필드 수 보다 resultMap에 선언된 property 가 많으니깐 IndexOutOfBoundsException 이 나더라구요.. 왜 그런지는 못찾았습니다. 하다보면 다른곳에서 또 나타나면 그때 다시 개선 방안을 모색해야 할듯 합니다. ㅡㅡ;
아.. 참고로, iBATIS의 가장 단점은 디버깅이 어렵습니다. 엉뚱한 오류가 나는경우가 허다합니다. 그러니 조심조심 해야하죠.
디비가 Oracle 일때 쿼리문의 마지막에 세미콜론(;)이 찍혀있으면 정말 황당한 오류가 나죠.. 참고 하시기 바랍니다. =ㅁ=;

이제 View를 만들어야 합니다. BoardController 에서 보내는 것이니, /Views/Board/List.aspx 를 만듭니다.
제가 쓰는 코드 하이라이트 헬퍼가 asp.net을 지원하지 않아 aspx 코드는 매번 이렇게 캡춰를 받아서 올리네요 ㅡㅡ;

여기서 중요한건, SpringBoard.Models.Vo를 Import 해야지만 m.Title 과 같이 인텔리센스가 동작하고, TBoard 를 사용할수 있습니다. 코드 그대로 간결합니다. ㅡㅡ;

여기까지 하고, 실행 해 봅시다. 실행하여 Board 메뉴를 클릭하면 다음과 같이 리스트가 잘 출력이 됩니다.

처음에 하나의 row만 넣었으니 몇개 더 넣어보고 새로고침 해 봅시다.

INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (2000, 0, 'admin', 'pwd', '두번 째 글입니다', 1, '본문 내용입니다')


INSERT INTO ThreadBoard (thread, depth, writer, pwd, title, mode, content)
Values (3000, 0, 'admin', 'pwd', '세번 째 글입니다', 1, '본문 내용입니다')

이렇게 데이터를 넣고 새로고침 해 봅시다. 다음과 같이 잘 나오나요?

잘 따라 오셨는데 잘 안되시는 분들을 위해 친절하게 지금까지의 소스를 첨부 합니다. 단, sqlMap.config 파일에서 connectinString 을 자신의 디비환경에 맞게 입력 해 주세요.
connectionString="Data Source=(local);Initial Catalog=디비명;User ID=아이디;Password=패스워드"


그럼 다음에는 글쓰기 화면 Write 부분을 해 봅시다.











저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.20 12:36
지난 포스팅에서 ASP.NET MVC + SPRING.NET + iBATIS.NET의 세팅을 모두 끝내고, 테스트 까지 성공 하였다. 이번에는 게시판 리스트 페이지를 만들어 본다.

우선은 HomeController 대신에 BoardController쪽으로 라우팅 되도록 Board컨트롤러를 추가 시키고, 처음부터 리스트 페이지로 이동해도 되지만, 기존에 존재하는 Master페이지에서 Home, About 메뉴에다가 Board 메뉴를 추가 시키고, Board메뉴를 선택했을때 BoardController로 가도록 한다.

위 화면 처럼, Home, About, Board 가 되도록 Board 아이템을 하나 추가한다.
그러기 위해서는 Views/Shared/Site.Master 페이지에 메뉴 항목을 다음과 같이 Board 컨트롤러를 추가한다.
Html.ActionLink 의 파라메타는 linkText, ActionName, ControllerName 으로 구성된다. 즉, 파라메타 상으로, BoardController 에 List 메서드 쪽으로 보낸다는 말이니깐, 컨트롤러와 메서드를 만들어 보자.
우선 컨트롤러 폴더에서 마우스 오른쪽 클릭 하고, 추가->Controller 를 선택하면 Add Controller 다이얼로그가 뜨는데, 선택된 부분을  Board로 바꾸어 최종적으로 이름이 BoardController가 되도록 하고, Add버튼을 클릭한다.



여기서 곧장 Controller 를 수정할 것이 아니라, 처음 부터 계속 말 했던 Facade를 태우기 위해 우선 DAO단에 코드를 추가 하도록 한다. 게시판 리스트 화면이므로, getBoardList로 하도록 하자.

SqlMapBoardListDAO.vb 에 getBoardList를 추가한다.


코드를 보면 굉장힌 간결한걸 알수 있다. 쿼리는 iBATIS.NET 을 통하여 Board.xml에서 구현된다. ^^

이 클래스의 인터페이스인 BoardListDAO에도 다음과 같이 인터페이스(메서드)를 추가한다.


그리고 Facade의 구현 클래스인 SpringBoardImpl.vb의 코드는 다음과 같이 추가 되어진다.


코드에서 보는것과 같이 간결하게 이루어 진다. 이 모든것들이 SPRING.NET 과 iBATIS.NET 프레임웍 덕분이다.
하지만, 단점처럼 보이는것이 왜 자꾸 Facade를 통하려고, 비슷한 코드를 반복적으로 넣는지... 이것은 Spring in Action 이라는 책과 Core J2EE Pattern 이라는 책에서 이 방식이 좋다고 한다. ㅡㅡ; 약간 황당하게 들릴수는 있으나, 실제로 이렇게 개발을 해 보았더니 아주 확장성이 좋았다. Facade 패턴에 관한것은 나중에 다시 언급하기로 하겠다.

그러면 이제 SpringBoardFacade.vb 인터페이스에도 getBoardList 를 선언한다.


이렇게 추가하고 나면 코드단의 작업은 모두 끝났다.
다음작업은? 설정파일들을 수정하는 작업이 남았다.

설정파일 수정, 리스트 페이지 디자인, Board.xml 의 getBoardList statement 는 다음 포스트에서...

아침에 출근해서 한시간동안 하려고 하니 너무 시간이 부족한것 같다.. 일이 계속 밀리네 ㅡㅡ;


저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.17 11:11
오픈소스 프레임웍의 단점인 셋팅을 하는데만 벌써 5번째 게시물이 되었다.
힘들게 하나하나 셋팅하고 자료 찾는 작업이 반복적으로 이루어진다. 하지만, 셋팅이 끝나고 페이지가 정상적으로 뜰때 그 성취감은 정말 뿌듯~ 하다. :)

이전 게시물에서 Models 폴더 아래에 Business Logic을 위한 구조를 잡았다. 차근차근 하나씩 해 보자.
우선 첫 게시물에서 만든 ThreadBoard 와 매핑시킬 Entity Class 흔히 VO(Value Object) 라 한다.


참고로, 폴더 구조대로 Namespace를 잡아 두었다.
TBoard.vb 의 코드는 다음과 같다.


간단한 VO 하나 코딩하는데, 시간이 꽤 걸렸다. Visual Studio 에서는 아직은 Entity Class 에 대한 지원이 약간 부족한듯 하다. 참고로 자바 개발툴인 Eclipse와 IntelliJ 에서는 위 코드와 같이 Properity 를 한꺼번에 쉽게 코드 생성이 가능하다. 이 부분을 흔히 getter and setter 설정이라고 한다. 혹시나 MS 관계자분이 이 글을 봤을때 제안을 할수 있도록 IntelliJ 에서 지원하는 getter and setter 팝업메뉴를 첨부 하겠다. 이렇게 한번에 되면 좋을것 같은데 VS2010 에서는 지원 하려나? ^^;


암튼 이게 중요한게 아니고, 빨리 세팅을 마무리 해 보자.

이 TBoard 클래스는 테이블과 매핑이 될 부분이다. 어플리케이션이 커지고 복잡해 지면, 이런 VO 가 많아진다. 거의 테이블과 1:1 정도 되는것 같다.

다음으로는 Board.xml 파일을 만들어 보자 코드는 다음과 같다.


여기서 중요한건, resultMap 인데, property는 앞서 코딩한 SpringBoard.Models.Vo.TBoard 의 private 변수명과 매칭이 되어야 하고, column은 테이블 필드명과 같아야 한다.

다음으로 BoardListDAO 인터페이스를 다음과 같이 코딩한다.


셋팅을 위한 테스트 메서드를 선언하는 부분이다. 이제 구현부 SqlMapBoardListDAO 클래스는 다음과 같이 코딩한다.


이게 iBATIS의 파워이다. Mapper.Instance().QueryForList() 메서드를 이용하여, Board.xml의 getList 라는 statement 를 호출하고, 결과값을 그대로 반환한다.

원래 이 부분에서 iBATIS가 없을때는 커넥션, 예외처리, 디비 닫는 부분 까지 모두 여기서 처리를 했었다. 하지만, iBATIS의 도움을 받아서 이렇게 한줄로 끝난다. 또한, 운영중 요구사항 변경이 쿼리 수정 정도의 요구 일때는 Board.xml 파일 하나만 수정하면 곧바로 적용이 되어버린다. 훌륭하다~

자~ 그러면, 비즈니스 로직 부분을 처리 했는데, 어떻게 호출을 할까?
앞서 설명한 Front Controller 패턴으로 처리 하기 위해 Facade 클래스를 만들어 두었다. 거기에 등록 해야 한다.

이전에 만들어둔 SpringBoardFacade 인터페이스의 Test 메서드를 이렇게 바꾼다.


리턴타입이 기존에 String 타입이였던것을 IList 로 바꾸었다. 그리고, SpringBoardImpl 클래스에 setter injection 을 위한 프라퍼티 설정과 Test() 메서드 변경을 위해 다음과 같이 코드를 수정하자.


좀 특이한 부분이 생겼다. boardListDAO property 인데, 이건, 이전에 만들어둔 objects.xml 에서 injection 을 위한 통로가 된다. 그러면 당연히 objects.xml 도 다음과 같이 수정하자.


이제 마지막으로 HomeController 클래스를 수정해야 한다.
참고로, 클래스에 Dependency Injection 을 위해서는 생성자에 inject, property 에 inject, 인터페이스에 inject 이렇게 세가지 방법이 있다.

HomeController 클래스에는 생성자 인젝션으로 처리가 되어있다. constructor-arg 이 부분이 생성자 인젝션, 그리고, property name="boardListDAO" ... 이 부분이 setter injection(property injection) 이다.
HomeController 클래스는 다음과 같이 변경되었다.


이제 View 에 표현해 보자.
Index.aspx 페이지는 다음처럼 두줄이 추가 되었다.(코드 하이라이트에서 asp.net 코드는 처리 못하는군 ㅡㅡ;)



실행을 해 보면 다음과 같은 화면이 정상적으로 잘 뜬다.

이렇게 해서 ASP.NET MVC + SPRING.NET + iBATIS.NET 의 구성 셋팅이 모두 끝났다.

ASP.NET MVC 의 흐름을 그대로 따르면서, SPRING.NET의 DI를 이용해 결합도를 낮추었고, iBATIS.NET의 매핑으로 데이터베이스에서 쉽게 데이터를 가져왔다.

친절하게도 지금까지의 소스는 아래 링크!!! 하지만 동작이 안될것이다. SqlMap.config 의 connectionString을 수정해야 한다.

ex) Data Source=(local);Initial Catalog=디비명;User ID=아이디;Password=패스워드

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower
ASP.NET2009.04.16 13:55
이번에는 iBATIS.NET 을 붙이는 작업을 한다.
이것도 마찬가지로 시간이 꽤 걸렸다. 자료가 없지는 않지만, 그래도 하나의 프로젝트 이니깐, 설계 부분이 탄탄해야 참고 하는 사람들이 활용폭이 높아질것 같아서, 폴더 트리 부터 네임스페이스 까지 꼼꼼하게 제대로 된 위치로 맞추다 보니 많이 늦어졌다.

iBATIS.NET 은 DataMapper 부분을 사용할건데, 다운받은 파일을 압축을 풀어보면 IBatisNet.DataMapper-bin-1.6.1\IBatisNet.DataMapper-bin-1.6.1\binaries-2.0 이 위치에 관련 파일이 있을것이다.

여기서 우선 Visual Studio 의 인텔리센스 기능을 위해 xsd 파일 세가지(provider.xsd, SqlMap.xsd, SqlMapConfig.xsd)를
Visual Studio 2008 버전일때 C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas 위치에 복사해 둔다.

그리고 참조추가는 IBatisNet.DataMapper.dll, IBatisNet.Common.Logging.Log4Net.dll 을 추가 해 둔다.
iBATIS 도 SPRING.NET 과 마찬가지로 로그를 뱉어내기때문에 로그 관련 Logging dll도 같이 추가 한다.

web.config 위치에 아래 링크로 걸려있는 providers.config를 넣자.
 
그리고나서, 같은 위치에 sqlMap.config 파일을 생성해서 다음과 같이 코드를 넣어준다.


connectionString은 자신의 환경에 맞게 첫번째 게시물의 ThreadBoard 테이블이 생성된 곳을 지정하면 된다.
18번줄의 <sqlMap resource="Models/iBATIS/Maps/Board.xml" /> 부분은 iBATIS.NET 이 참조할 테이블과 쿼리가 설정된 파일의 위치이다.

이렇게 config 파일을 설정을 했으면 어플리케이션 폴더 구조를 다시한번 살펴보자.
우선 Business Logic 은 Models 안에서 처리 하기로 해서 그 안에서 비즈니스 로직이 처리가 된다. 또한, 비즈니스 로직의 처리를 iBATIS가 DB와 통신을 하기 때문에 그 안에 iBATIS 라는 폴더를 생성해 두고 거기서 작업을 한다.

Models 폴더만 캡춰를 해 봤다. 이렇게 폴더와 클래스, 인터페이스를 생성 하자.

폴더는 다음처럼 Models 안에 하위 폴더를 만들어 두고, 그 안에 있는 파일을 설명 하면.

Board.xml 파일을 쿼리가 저장되어 있는 파일이다.
BoardListDAO 는 SqlMapBoardListDAO 클래스의 인터페이스 이다.


TBoard 클래스는 테이블과 매핑되는 Value Object 이다.
쿼리 결과를 TBoard에 매핑하여 IList 에 담는 방식이 된다.

소스 내용은 다음 게시물에서...

출근해서 계속 자료찾고 포스팅 하고 있으니.. 눈치주는 사람은 없지만.. 일이 밀려서 일좀 해야겠어요 ㅋㅋ




저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by dotnetpower

티스토리 툴바