잊지 않겠습니다.

gradle 정리 - single project

Java 2013. 10. 25. 23:54
ava project에 대한 build tool의 최종 완성본

build tool의 역사

1. no build tool
2. ant
3. maven
4. gradle

Gradle을 이용한 java sample project의 개발

gradle setupBuild 를 이용한 초기 project build.gradle 파일 생성
기본적으로 maven way와 동일한 file path를 갖고 있다. 

src/main/java
src/main/resources
src/text/java
src/test/resources

1. targetCompatibility, sourceCompatibility의 설정
: 사용된 java version을 설정한다. 기본값은 1.5다

2. repositories의 설정
: repository를 설정한다. repository는 Maven에서 사용되던 것과 동일하며 Local PC에 maven이 설치된 경우, LocalPC에 대한 maven repository를 먼저 찾아보도록 설정하는 것이 가능하다. 
maven 설정은 다음과 같이 구성한다. 

repositories {
    mavenLocal()              //Local PC maven repository
    mavenCentral()           //mavenrepository site를 참조   
}

3. dependencies의 설정
: dependency를 설정한다. dependency의 경우에는 maven에 비하여 간결한 format을 지원한다.

 dependencies {

     compile 'org.slf4j:slf4j-api:1.7.5'
     testCompile "junit:junit:4.11"
     providedCompile 'javax.servlet:servlet-api:3.1'
     providedRuntime 'webcontainer:logging:1.0'
}


* compile : compile시에 필요한 dependency를 설정한다.

* testCompile : test 시에 필아한 dependency를 설정한다. 

* providedCompile : compile시에는 필요하지만, 배포시에는 제외될 dependency를 설정한다. (war plugin이 설정된 경우에만 사용 가능하다)

* providedRuntime : runtime시에만 필요하고, 실행환경에서 제공되는 dependency를 설정한다. (war plugin이 설정된 경우에만 사용 가능하다)

 

4. gradle build sequence

* compileJava : java compile

* processResources : resources로 지정된 file 처리 - jar를 만들기 위해 build folder로 copy등의 절차를 취한다

* classes : classes directory를 구성한다. compileJava를 통해 compile된 class객체와 resource들을 취합한다.

* jar : 모인 파일들을 이용해서 jar를 구성한다.

* compileTestJava : test code를 구성한다.

* processTestResources : test resources를 구성한다.

* testClasses : 앞 두과정을 통해서 모여진 파일들을 처리한다.

* test : testClass를 실행해서 JUnit/TextNG 결과를 도출시킨다.


Posted by Y2K
,

* 사내 강의용으로 사용한 자료를 Blog에 공유합니다. Spring을 이용한 Web 개발에 대한 전반적인 내용에 대해서 다루고 있습니다.



WebApplication은 HTTP Protocol로 동작하는 네트워크 프로그래밍의 일종입니다. 

이 정의가 조금은 의문스러워보이실지 모르겠지만, 저희는 지금 네트워크 프로그래밍을 하고 있는것입니다. 다만 이 네트워크를 처리해주는 것이 Servlet Container가 됩니다. 네트워크 프로그래밍은 매우 어려운 작업이지만, 지금 우리가 할 수 있는 가장 큰 이유는 Servlet Container가 이 일을 처리해주고 있기 때문입니다. 이는 매우 큰 의미를 갖습니다. 더이상 개발자들은 이 어려운 네트워크 문제를 다루지 않고, 처리하고자 하는 BL에만 집중할 수 있다는 점이, 오늘날의 성공적인 web application 환경을 만들어주게 되었습니다.  네트워크 처리 부분만을 의미하는 것으로 web application server 라는 표현을 사용하기도 합니다. 

위에서 보시는것처럼 web application server는 web으로 동작하는 application만을 의미하게 됩니다. 또한 Java EE(java enterprise edition)의 명세 및 구현된 web application 기술에 대한 구현체가 servlet container라고 할 수 있습니다. 이에 따라 servlet container는 다음과 같은 정의를 내릴 수 있습니다. 

servlet container = web application server + Java EE web application 기술 구현체

가장 널리, 그리고 무료로 사용될 수 있는 servlet container는 다음과 같습니다. 

tomcat : 가장 오랫동안 servlet 기술 명세에 대한 reference 구현체였으며, 가장 잘 알려진 servlet container입니다. 
jetty : 실험적 시도를 가장 과감하게 도입하는 것으로 유명합니다. 또한 속도가 가장 빠르고 가벼운 servlet container로 유명합니다.
grizzly : 현 servlet 기술 명세에 대한 reference인 glassfish의 servlet framework입니다. 제한적 무료라서 그런지, tomcat에 비해서 대중성이 조금 떨어지고 있다는 느낌이 들긴 합니다. 


HTTP Protocol

HTTP는 다른 Protocol과는 조금 다른 특징을 가지게 됩니다. 일반적으로 network protocol은 size나 해석의 문제에 의하여 압축되고, 특정 문자로 해석되도록 데이터를 만드는 것이 일반적입니다. 주소번지 1번지부터 12번지까지는 특정 어떤값을 이용하는 식으로요. 그렇지만 HTTP Protocol은 String을 그대로 사용하고 있습니다.  만약에 daum 사이트에 갔을 때, http는 어떤 통신을 하는지 한번 간단하게 알아보도록 하겠습니다. 

먼저 request는 다음과 같이 발생됩니다.

GET / HTTP/1.1
Host: www.naver.com:8000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: UTF-8,*;q=0.5
Cookie: JSESSIONID=1q5bdgf2p3b22nz1n133l3qd

모든 HTTP Protocol은 기본적으로 CR + LF에 의하여 한개의 항목이 끝나는 것을 지정합니다. 그리고 데이터는 {Name}: {Value} 형태로 구성이 됩니다. 이 형태는 매우 중요합니다. 공백과 :의 위치까지 정확하게 잡혀있는 Protocol format이 됩니다. 만약에 우리가 HTTP Protocol을 직접 만들어서 보내주고 싶다면 이와 같은 형태로 데이터를 구성해서 보내주면 됩니다. 

지금까지 보신 위 코드가 HTTP Header 가 됩니다.  개발시에 자주 이야기가 나오던 HTTP Header에 특정 데이터를 넣어서 보낸다던지, 특정 데이터를 얻어오는 일들이 모두 위의 Text로 구성이 됩니다. 

다음은 response입니다. 

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 07 May 2013 01:24:54 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
P3P: CP="CAO DSP CURa ADMa TAIa PSAa OUR LAW STP PHY ONL UNI PUR FIN COM NAV INT DEM STA PRE"
Vary: Accept-Encoding,User-Agent
Content-Encoding: gzip

404


보시면 request와 비슷한 내용이 return되는 것을 알수 있는데, 아래 부분에 약간 다른 내용이 있습니다. 이 부분이 Body입니다. Body는 Header다음에 CR + LF + CR + LF 후에 나오는 text 영역이 됩니다. 우리가 만드는 HTML 부분이 되는 것이 일반적입니다. 

HTTP Protocol의 request type

request의 첫줄에 나오는 GET / HTTP/1.1에 주목할 필요가 있습니다. 이 부분은 지금까지 사용되는 부분과 조금 다릅니다. 이 부분을 http method라고 불리우는 영역입니다. HTTP method에 따라서 url의 parameter를 server에서 달리 해석하게 됩니다. 

1. GET
우리가 가장 자주 보는 형태입니다. parameter는 ?로 시작되고, =로 key와 value가 설정이 됩니다. 그리고 다른 key가 있는 경우에는 &로 연결이 됩니다. 따라서 형태는 다음과 같이 구성이 될 수 있습니다. 


가장 주로 사용되는 형태입니다. HTTP Protocol의 정의상으로는 GET는 정보의 조회시에 사용하는 형태입니다. <a> tag나 browser의 url로 접근하는 경우에는 모두 GET으로 동작하게 됩니다. 

2. POST
역시 자주보이는 형태입니다. parameter의 구성방법역시 GET과 동일합니다. 다만 parameter를 보내는 방법에 큰 차이를 가지고 있습니다. GET에서 parameter를 이용해서 구성을 했지만, POST는 body에 GET에서 보내줬던 paramter를 넣어서 보내주게 됩니다. 이 방법은 사용자에게 parameter를 노출시키지 않는 장점을 가지고 있고, GET에서 불가능한 대용량 대이터를 보내는 것이 가능합니다. 그리고 file upload 시에도 POST 방식을 사용하게 됩니다. HTTP protocol에서는 기본적으로 update의 의미를 가지고 있습니다. 

위 GET/POST는 가장 많이 사용되고 있는 HTTP method입니다. 그리고 Browser에서 기본적으로 지원을 하고 있습니다. 그렇지만, 아래 나오는 method들은 구형 browser에서는 지원되지 않고 있습니다. 그리고 GET/POST 이외에는 parameter를 보내지 못합니다. 이 부분은 RFC 문서에 정의된 내용으로 HTTP 1.1 이상의 버젼에서는 추후 지원할 수도 있습니다.  

3. DELETE
조금은 생소한 method입니다. HTTP protocol에서는 이름 그대로 delete의 의미를 가지고 있습니다. 

4. PUT
Create/Insert의 의미를 갖는 method 입니다. 

5. TRACE
보낸 request를 그대로 다시 보내주길 원하는 method입니다. echo server나 server의 상태가 원활한지를 알아보는 방법으로 사용됩니다.  이 method는 매우 심각한 문제를 가지고 있습니다. XST(Cross-Site Tracing)이라는 악의적 공격방법을 이용해서 사용자의 request에 들어있는 인증정보를 빼돌릴때 사용이 됩니다. 그래서 TRACE를 지원하는 web server들은 모두 심각한 오류를 가지는 것으로 보고되며, 모든 web server는 TRACE method를 무효할 것을 권장하고 있습니다. 

6. OPTIONS
Web Server에서 HTTP Method중 어떤 것들을 지원하는지 알아보는데 사용됩니다. 

7. CONNECT
Proxy에서 사용됩니다. Http TLS(Transport Layer Security) Tunnelling을 요청할 때 사용됩니다. CONNECT로 보내지는 method는 그 서버를 통해서 다른 서버에 접속하게 되는 것을 요청하게 됩니다. 

8. HEAD
GET과 동일한 정보를 return합니다. 다만 차이를 갖는 것이 GET은 Body에 데이터를 넣어서 보내지지만, HEAD는 Header에 Message-Body에 body를 모두 넣어서 보내게 됩니다. HEAD method는 browser에서 cache를 사용할 때 주로 이용합니다. 


위 method를 이용한 결과는 HTTP result라는 숫자로 표시가 되는데요. 이는 response의 첫줄에 나오는 숫자가 Return에 대한 결과입니다. 우리가 자주 보는 HTTP result의 결과가 바로 이것입니다. 

1. 2xx - Success
# 200 : OK
# 201 : Created - POST나 PUT에 의해서 새로운 Resource가 생성되었음을 나타내는 result code입니다.
# 202 : Accepted - async http request가 들어왔을 때, 그 request가 수용되었음을 나타냅니다. 
# 203 : Partial Information - return되는 정보는 cached된 것이거나, 내부 정보임을 의미합니다. 
# 204 : No Response - Response가 정상적으로 처리되었지만, Output의 내용이 없음을 의미합니다. 주로 Body가 비어있는 경우에 204를 return 합니다.

2. 3xx - Redirect
# 301 : Moved - 요청된 URL의 페이지가 다른 곳으로 이동되었음을 나타냅니다.
# 302 : Found - 요청된 URL의 페이지에서 다른 곳으로 이동하는 것을 원하는 것을 나타냅니다. 301과 비슷하지만, 302의 경우에는 Form을 POST로 넘겼을 때, 그 결과에 대한 Accept의 의미로 사용됩니다.
# 303 : Method - Found와 같은 의미로 사용되지만, Found는 URL로 이동됨을 나타냅니다. Method는 Body에 있는 Document를 이용해서 표시하는 것을 의미하게 됩니다.
# 304 : Not modified - 변경된 상황이 없기 때문에 Cache에 있는 내용을 이용해서 표시하라는 것을 의미합니다.

3. 4xx - Client Error
# 400 : Bad Request - HTTP Protocol에 어긋난 request가 입력되었습니다.
# 401 : Unauthorized - 인증되지 않은 HTTP Request가 들어왔습니다.
# 402 : Payment Required - Http Head에 있는 정보를 변경해서 다시 보내주는 것을 요청할때 사용됩니다.
# 403 : Forbidden - 인증되었지만, 권한이 없음을 나타냅니다.
# 404 : Not Found - Resource가 없는 URL에 요청되었음을 나타냅니다. 우리가 Spring으로 web을 개발할때, 처음에 자주 볼 수 있는 에러입니다. Controller 뿐 아니라 View File이 없는 경우에도 404 Not Found가 표시됨을 유의해주시길 바랍니다. 

4. 5xx - Server Error
# 500 : Internal Error - 서버 내부의 에러입니다. java나 .net에서 exception이 발생했을 때, 이것을 처리하지 않을때 나오는 에러입니다.
# 501 : Not Implemented - 아직 구현되지 않은 URL을 호출했습니다. 이는 404와는 다른 에러입니다. 404의 경우에는 아애 없는 URL이 호출된 상황이고, 501의 경우에는 구현되지 않았지만, URL resource는 존재할 때 사용됩니다.
# 502 : Timeout - RFC에 의해서 정의된 에러는 아닙니다. 그렇지만 몇몇 WebServer들은 구현되어있는 에러코드로, Server의 BL로직이 너무 오래 걸릴때, 에러로 return 해줍니다. 


웹의 구조적 아키텍쳐

apache httpd를 시작한 Roy Fielding은 web은 주요 제약점에 의해서 확장성이 좌우되는것을 알게 되고, 그에 대한 구조적 스타일을 다음과 같이 정의했습니다. 

1. Client / Server
웹은 client/server 기반 system으로 client/server 규약의 핵심은 관심의 분리입니다. 웹의 일괄된 인터페이스를 따른다는 가정하에, Client와 Server는 각자의 언어 및 기술을 이용해서 독립적으로 구현되고 배포가 가능하게 됩니다. 

2. Uniform interface
웹을 구성하는 Client/Server/Network 간의 interface는 일관성에 기반하고 있습니다. 이러한 구조가 붕괴가 되는 경우, 현 웹 커뮤니케이션체계는 붕괴가 되어버립니다. 
이 인터페이스는 다음과 같은 정의들을 보일 수 있습니다. 

# 리소스 식별 : URI로 구분될 수 있는 resource는 unique하기 때문에, 고유 식별자로 사용될 수 있습니다.
# 표현을 통한 Resource 처리 : HTML, JSON과 같은 web resource는 표현 방법이 정해져있지 않습니다. HTML을 그냥 text로 보여줘도 되고, web browser에서 보이듯이 rendering해서 표시를 해도 괜찮다는 뜻입니다. 이는 Document-View 구조와 같이 Resource에 대한 처리는 Client에게 맡긴다는 정의로도 볼 수 있습니다.
# 자기 서술적 메세지 : HTTP protocol의 header는 자신에 대한 서술적 메세지를 포함합니다. 그리고, 요청에 대한 처리는 전적으로 요청에 대한 응답자에서 결정하게 됩니다. 
# Application 상태 엔진으로서의 Hyper-media : resource의 상태표현은 resource의 link를 포함합니다. 따라서 모든 resource는 실타래처럼 연결되기 때문에 사용자들은 정보와 application을 직접적인 방식으로 훝어보는 것이 가능하게 됩니다. 이는 HTML의 표준적인 특징중 하나입니다.

3. Layered System
우리가 구성하는 MVC와 같은 Layered System이 아닌 Browser-proxy-gateway-switch-web server 와같은 Layer가 구성되는 network 기반의 중간계층을 사용할 수 있는 구조적 특징을 갖습니다. network 기반의 중간계층의 경우에는 보안의 강화 또는 응답 캐싱, 부하를 분산하는 용도로 주로 사용됩니다. 

4. Cache
cache는 웹구조의 중요한 제약조건중 하나입니다. 캐시는 웹자체의 전체적인 비용을 줄일 수 있는 기술적 요건이며 server, network, client 모두에 위치가 가능하게 됩니다. 

5. Stateless
웹의 가장 큰 특징중 하나입니다. web server가 client의 상태를 직접 관리할 필요가 전혀 없다는 것입니다. 따라서 client는 server와 상호작용하는 관련 상황정보를 직접 관리를 해야지 됩니다. 이는 web server가 client와의 복잡한 연결을 위해 필요한 상태 관리를 전혀 하지 않는다는 점입니다. 이를 다른 말로 FF(fire and forget)이라고도 합니다. 이는 장점도 단점도 아닙니다. 비용상의 trade-off라고도 할 수 있습니다. 

6. Code-on-demand
웹은 주문형 코드를 많이 사용합니다. 이 제약조건은 script나 plugin과 같은 실행가능한 program을 일시적으로 program에 전송하여 client가 실행할 수 있도록 합니다. 그렇지만 이는 client와 server간의 강한 결합을 가지고 오게 되는데. 이는 web에 대한 확장성을 막아버릴 수 있기때문에 최대한 지양해야지 되는 구조라고 할 수 있습니다.

이러한 구조적 스타일에 기반한 웹의 확장은 2000년 Roy Fielding에 의해서 'Representational State Transfer'라고 이름을 붙여 발표하게 되었습니다. 그리고 이 논문의 제목은 지금 REST라는 용어로 더욱더 많이 사용되고 있습니다. 

REST는 어느날 갑자기 나온것이 아니라, 웹의 구조적인 특징을 이용하고, 그에 대한 안정화로서 나온 기술입니다. 어찌보면 기술이라기보다는 'Style'이라고 할 수 있는 방법입니다. REST에 충실한 구조를 RESTful 하다는 용어로 사용하고 있고, 이는 다음과 같은 특징을 가지고 있습니다. 

1. URI 식별자 설계
# '/'는 계층 관계를 나타내는데 사용됩니다.
# URI의 마지막 문자로 '/'는 사용하지 않습니다.
# '-'는 URI의 가독성을 높이는데 사용합니다.
# '_'은 URI에서 사용하지 않습니다.
# URI는 소문자로 구성을 합니다.
# 파일 확장자는 URI에 포함하지 않습니다. 파일 확장자는 URI를 통해 얻어질 수 있는 media-type에 따라 결정이 됩니다.

2. URI 디자인
# Document/Object의 이름은 단수를 이용한다 (ex: http://www.daum.net/leagures/teams/players/report)
# Collection이 표시되는 경우, 복수를 이용한다 (ex: http://www.daum.net/leagures/teams/players)
# 제어가 발생되는 URI는 동사나 동사구를 이용한다 (ex : http://www.daum.net/students/morgan/register)
# CRUD 기능을 나타내는 것은 URI에 사용하지 않는다. 
- 이는 REST API를 구성할 때 사용되는 규칙입니다. 
- 위에서 살펴본 GET/POST/DELETE/PUT을 이용한 URI를 구성해서 CRUD 기능을 만들어주는 것이 권장됩니다.

3. input parameter
# parameter의 경우에는 URI query라는 이름으로 사용됩니다. 
# URI query의 경우에는 검색기준으로 사용되는 것이 일반적입니다. 이는 HTTP GET method의 사용방법과 연관있습니다.

4. http method
# GET method는 리소스의 상태표현을 얻는데 사용됩니다.
# HEAD method는 응답에 대한 header만을 얻어올 때 사용합니다.
# PUT method는 리소스를 생성하거나 갱신하는데 사용합니다.
# POST method는 리소스를 생성하거나 갱신하는데 사용합니다. POST와 PUT는 일반적으로 같은 목적으로 사용됩니다. 다만 사람들끼리의 암시적인 약속으로 Create시에는 PUT을, Update에서는 POST를 사용하는 것이 일반적입니다.
# Control이 발생되는 URI는 반드시 POST에 의해서 실행됩니다.
# DELETE는 Resource를 삭제할 때 사용됩니다.
# OPTIONS는 resource의 사용 가능한 action method가 무엇인지를 알기위해서 구성됩니다.


Summary 

이번에는 HTTP에 대한 기본적인 이론에 대해서 알아봤습니다. HTTP는 우리가 만드는 모든 web application의 핵심기술입니다. 다만 기술의 구현은 servlet container 또는 web server에서 담당하지만, 그에 대한 동작을 알아보는 것은 개발시 디버그등에 매우 유리하게 만들 수 있습니다. 그리고 URI의 디자인은 URI 자체의 문서화 뿐 아니라 Google에서의 검색의 가장 큰 기준이 됩니다. 여기서 나온 내용들은 꼭 알아두시길 바랍니다.


Posted by Y2K
,
지금까지 우리는 View를 살펴봤습니다. 지금까지 본 View들은 File upload만을 제외하고, 사용자의 입력이 Controller단으로 전달되는 것이 전혀 없는 View입니다. 
기본적으로 Web은 URL을 통해서, 그리고 POST의 경우에는 Body에 실린 parameter를 통해서 값의 전달이 이루어지게 됩니다. 그리고, 이 값은 Controller를 통해서 전달됩니다. 
이제 Controller의 다른 기능인 사용자 Action을 받는 기능에 대해서 알아보도록 하겠습니다.  

다시 한번 기억하는 것으로 Controller의 기능은 두가지입니다. 

1. Client에게 View를 전달하는 주체
2. Client에서 데이터를 전달받는 주체

먼저, 아주 기본적인 HTML입니다. (Tiles를 사용해서 head 부분은 제거 되었습니다.)

<form method="post" class="example-form">
    <fieldset>Book</fieldset>
    <label>책 제목</label>        
    <input type="text" name="title" placeholder = "책 제목을 입력해주세요."/>
    <label>작가 이름</label>
    <input type="text" name="author" placeholder = "작가 이름을 넣어주세요."/>
    <label>추가 설명</label>
    <input type="text" name="comment" placeholder = "추가 설명을 넣어주세요."/>
    <br/>
    <button type="submit" class="btn">추가</button>
</form>


그리고, 이 form의 데이터를 받는 Controller의 action 코드는 다음과 같이 작성될 수 있습니다. 

    @RequestMapping(value = "/book/add01", method = RequestMethod.POST)
    public String add(String title, String author, String comment) {
        Book book = new Book();
        book.setTitle(title);
        book.setComment(comment);
        book.setAuthor(author);
        bookService.add(book);

        return "redirect:add01";
    }

값의 전달방법은 html의 element의 name이 각각의 parameter의 input으로 들어가게 됩니다. 이 부분의 코드를 보다 더 명확히 해준다면 다음과 같이 작성될 수 있습니다.

    @RequestMapping(value = "/book/add01", method = RequestMethod.POST)
    public String add(@RequestParam(value = "title") String title,
            @RequestParam(value = "author") String author,
            @RequestParam(value = "comment") String comment) {
        Book book = new Book();
        book.setTitle(title);
        book.setComment(comment);
        book.setAuthor(author);
        bookService.add(book);

        return "redirect:add01";
    }

입력값이 되는 title, author, comment를 보다더 명확하게 전달하게 되는 것을 볼 수 있습니다. 그런데, 이와 같은 개발 방법은 많은 코딩양을 가지고 오게 됩니다. 그리고, OOP 개발자들은 이렇게 생각할 수 있습니다.

"객체를 전달할 방법은 없을까?" 

다음과 같은 코드로서 말이지요. 

    @RequestMapping(value = "/book/add02", method = RequestMethod.POST)
    public String add02(Book book) {
        bookService.add(book);
        return "redirect:add01";
    }

Controller code만을 고치고, 테스트 코드를 이용해서 한번 알아보도록 하겠습니다.  값의 return 정상적으로 Book 객체가 생성이 되었는지를 알아보기 위해서 Controller 코드와 Test code를 잠시 수정했습니다. redirect시에는 model을 전달할 수 없기 때문에 redirect 시에 임시로 값을 저장하는 RedirectAttribute를 이용, FlashMap에 값을 저장하는 것으로 Controller code를 변경하였습니다. 변경된 결과와 테스트 코드는 다음과 같습니다. 

//BookController
    @RequestMapping(value = "/book/add02", method = RequestMethod.POST)
    public String add02(Book book, final RedirectAttributes redirectAttribute) {
        bookService.add(book);
        redirectAttribute.addFlashAttribute("book", book);
        return "redirect:add01";
    }
//BookControllerTest
    @Test
    public void add02() throws Exception {
        String bookTitle = "Book Title";
        String bookAuthor = "Book Author";
        String bookComment = "Book Comment";

        MvcResult mvcResult = mvc.perform(post("/book/add02")
                .param("title", bookTitle)
                .param("author", bookAuthor)
                .param("comment", bookComment))
                .andExpect(status().isFound())
                .andExpect(flash().attributeExists("book"))
                .andDo(print())
                .andReturn();
        
        Book book = (Book) mvcResult.getFlashMap().get("book");
        assertThat(book.getTitle(), is(bookTitle));
        assertThat(book.getComment(), is(bookComment));
        assertThat(book.getAuthor(), is(bookAuthor));
        
        System.out.println(mvcResult);
    }

예상대로 객체를 그대로 넘겨서 받을 수 있는 것을 알 수 있습니다. form을 통해서 데이터가 전달되게 되면 다음과 같은 형식으로 데이터가 전달되게 됩니다. 

title=BookTitle&author=BookAuthor&comment=BookComment

만약에 Controller에서 객체를 받게 된다면, property의 이름을 이용해서 값을 자동으로 연결(Bind)하게 됩니다. 이를 annotation을 통해서 보다 더 명확하게 적어주면 다음과 같은 코드로 표현될 수 있습니다. 

    @RequestMapping(value = "/book/add02", method = RequestMethod.GET)
    public String add02(Model model) {
        Book book = new Book();
        model.addAttribute("book", book);
        return "book/add02";
    }

    @RequestMapping(value = "/book/add02", method = RequestMethod.POST)
    public String add02(@ModelAttribute Book book, final RedirectAttributes redirectAttribute) {
        bookService.add(book);
        redirectAttribute.addFlashAttribute("book", book);
        return "redirect:add01";
    }


이 부분은 View에서 Controller에서 값이 전달되는 방법입니다. Form을 통해서 뿐 아니라 javascript를 이용한 ajax call 역시 같은 방법으로 데이터가 전달이 되게 됩니다. 이제 jquery를 통해서 값의 전달을 해보도록 하겠습니다. 

먼저 간단히 ajax에 대해서 알아보도록 하겠습니다. 

Ajax는 Synchronous JavaScript And XML(비동기 자바 스크립트와 XML)의 약자로 서버와의 비동기 통신을 이용해 마치 데스크탑 애플리케이션을 사용하는 것과 같은 사용자와 애플리케이션간의 인터랙티브한 사용자 경험을 가능하게 하는 스크립트 언어입니다.

이전의 동기 통신에서는 웹 애플리케이션이 서버와의 인터랙션을 필요로 할 때에 매번 브라우저가 사용자와의 인터랙션을 멈추고, 서버로부터의 응답이 올때까지 기다려야 했습니다. 서버로부터의 응답이 오기 전까지 사용자는 아무것도 할 수 없었죠.
하지만 비동기 통신에서는 서버로부터의 응답을 기다릴 필요 없이 사용자는 계속해서 애플리케이션에서 원하는 작업을 할 수 있습니다.

Ajax를 이용하는 예로는 구글맵, 검색사이트 검색창에서의 검색어 제시, 네이버 실시간 검색 순위 등이 있습니다.


Ajax의 장점

Ajax의 주요 장점은 아래와 같습니다.

1) 페이지 이동없이 고속으로 화면 전환
: Ajax는 페이지의 이전 없이 필요한 부분의 데이터 송수신만을 자유롭게 행할 수 있으므로, 효율적이고 빠르게 페이지를 전환할 수 있습니다.

2) 서버의 처리를 기다리지 않고 비동기 요청이 가능
: 서버와의 통신시 사용자는 서버로부터의 응답을 기다리지 않고 계속해서 다음 작업을 이어갈 수 있습니다.

3) 서버에서 처리하는 부분을 클라이언트에서 분담 가능
: Ajax로는 최소의 데이터만을 브라우저에 전달하기 위해 서버에서 하는 작업 중 JavaScript에서 수행 가능한 일을
클라이언트에서 분담하는 것이 가능합니다.

4) 수신하는 데이터의 양을 줄일 수 있음
: 기존의 브라우저가 수신하는 데이터는 HTML이나 XHTML과 같은 마크업 언어로 받는 것이 일반적인데 반해, Ajax로는 수신하는 데이터가 HTML이나 XML에 한정되지 않고 최소한의 텍스트 데이터로도 수신이 가능하기 때문에 수신 데이터이 양을 줄일 수 있습니다.

5) 실시간 인터렉티브 성능이 증가
: (1) ~ (4) 까지의 장점을 이용해 Ajax에서는 데스크탑 애플리케이션과 유사한 실시간 인터랙티브 성능을 보여줄 수 있습니다.

Ajax의 단점

Ajax의 단점은 아래와 같습니다.

1) 크로스 브라우저화의 노하우가 필요
: Ajax는 JavaScript 이므로 브라우저에 따른 크로스 브라우저 처리가 필요합니다.

2) Ajax를 지원하지 않는 브라우저에 대한 대책 필요
: Ajax를 지원하지 않는 브라우저에서는 사용이 불가능하므로 이에 대한 대책이 필요합니다. (하지만 현재 Ajax를 지원하지 않는 브라우저는 거의 없다고 볼 수 있습니다.)

3) 보안에 대한 주의가 불가피
: 페이지 이동 없이 서버와 통신하기 때문에 전보다 더욱 신중한 보안상의 주의가 요구됩니다.

4) 현재의 처리 상황에 대한 정보가 필요
: 페이지 전환 없이 처리가 진행되므로 사용자가 처리가 완료되었는데도 이를 모를 수 있습니다. 따라서 처리 중을 나타내는 프로그레시브 바등을 사용하는 것이 요구됩니다.

5) 요청을 남발하면 역으로 서버 부하가 늘 수 있음
: Ajax의 장점은 서버 부하의 감소에 있지만 그 의도와 반대로 요청이 너무 빈번하게 일어나 서버의 부하를 늘려버릴 수 있습니다. 따라서 데이터 전송량, 요청 회수, 서버 부하 등에 대한 종합적인 판단과 튜닝이 필요합니다.

6) Socket open / close의 부하 시간 증가
: 브라우저에서 가장 큰 부하를 주는 http call을 open을 자주 시키게 됩니다. URL이동으로 인하여 한번에 모든 데이터를 들고오는 방식을 ajax로 여러번을 나눠서 받는 경우, socket의 open/close 시간이 http call 횟수만큼 증가하게 됩니다. 적정한 ajax call이 필요합니다.

7) 개발의 어려움
: 1)항목과 연관이 있는 이야기입니다. javascript는 브라우져에 따라 동작이 다르게 움직여집니다. javascript를 테스트하기 위해서는 지금 상태로는 browser를 반드시 실행시켜서 그 결과를 확인해야지 됩니다. 상당한 수작업이 들어가기 때문에 ajax를 통해서 일을 진행을 해야지 된다면, 그에 대한 pattern등을 명확히 지정해야지 됩니다. 필요없이 모든 것을 다 ajax로 하게 되면 개발 시간뿐 아니라 부하역시 무시할 수 없기 때문입니다.


ajax의 장/단점을 한번 알아봤습니다. 아무리 단점이 있다고 해도, 기술적으로 가장 큰 장점인 비동기적 요청과 서버의 부하분산을 위해서 반드시 ajax는 사용해야지 됩니다. Book을 추가하는 code를 한번 ajax로 구현해보도록 하겠습니다. (기본적으로 jquery를 이용하도록 하겠습니다.)

다음은 html과 javascript입니다. 기존과 다른 점은 값의 전달을 form의 submit이 아닌 button에 event를 bind시켜서 사용하고 있다는 점입니다. 

<script>
$(function() {
  $('#add-button').on('click', function() {
      var title = $('form input[name=title]').val();
      var author = $('form input[name=author]').val();
      var comment = $('form input[name=comment]').val();
      addBook(title, author, comment);
  });
});

function addBook(title, comment, author) {
    $.post("add03", 
            {
                "title" : title,
                "author" : author,
                "comment" : comment
            }, 
            function(jsonResult){
                alert(jsonResult);
            }, 'json')
            .done(function(jsonResult) {
                console.log(jsonResult);
            })
            .fail(function(jsonResult) {
                console.log(jsonResult);
            });
            
}
</script>

<form method="post" class="example-form">
    <fieldset>Book</fieldset>
    <label>책 제목</label>        
    <input type="text" name="title" placeholder = "책 제목을 입력해주세요."/>
    <label>작가 이름</label>
    <input type="text" name="author" placeholder = "작가 이름을 넣어주세요."/>
    <label>추가 설명</label>
    <input type="text" name="comment" placeholder = "추가 설명을 넣어주세요."/>
    <br/>
    <button id="add-button" type="button" class="btn">추가</button>
</form>

다음은 Controller 코드입니다.

    @RequestMapping(value = "/book/add03", method = RequestMethod.GET)
    public String add03(Model model) {
        Book book = new Book();
        model.addAttribute("book", book);
        return "book/add03";
    }

    @RequestMapping(value = "/book/add03", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> add03(@ModelAttribute Book book) {
        bookService.add(book);
        Map<String, String> bookDto = new HashMap<>();
        bookDto.put("title", book.getTitle());
        bookDto.put("author", book.getAuthor());
        bookDto.put("comment", book.getComment());

        return bookDto;
    }

기본적으로 json을 이용해서 book을 하나 insert 하게 되는 것을 볼 수 있습니다. 
지금까지 book을 추가하는 코드에 대해서 form을 이용하는 방법과 ajax를 이용하는 방법, 두가지 방법을 봤습니다. 하나 더 다른 내용을 보여드리겠습니다. 방금의 예시는 json을 이용해서 book에 대한 값을 다시 받아서 그 값을 이용해서 무언가 작업을 하는 것입니다. 만약에 paging을 해줘야지 되는 일이 발생한다면 이 것은 어떻게 처리를 해줘야지 될까요? 

2가지 방법이 있습니다. 

1. json을 만들어서 html 형식으로 문자열을 만든 다음에 특정 <div> section에 넣어주는 방법
2. Controller에서 html형식의 View를 return 시키고, 그 View를 특정 <div> section에 넣어주는 방법이 있습니다. 

이 두가지 방법에 대해서 알아보도록 하겠습니다. 

Ajax + JSON을 이용한 Paging

JSON을 이용해서 HTML을 만드는것은 HTML code 자체를 만드는 방법입니다. 먼저 Controller code를 살펴보도록 하겠습니다. 

    @RequestMapping(value = "book/list/index")
    public String getBookListIndex() {
        return "book/list/index";
    }

    @RequestMapping(value = "book/list/jsonpage", method = RequestMethod.POST)
    @ResponseBody
    public List<Book> getBookJsonList(@RequestParam(value = "pageIndex") int pageIndex,
            @RequestParam(value = "pageSize") int pageSize) {
        List<Book> books = bookService.listup(pageIndex, pageSize);
        return books;
    }

Controller는 2개의 method를 가지고 있습니다. 1개는 book list의 기본을 표시할 page이고, 2 번째 method는 book에 대한 json 값을 return 하는 method입니다. 이에 대한 HTML code를 살펴보도록 하겠습니다. 

<script type ="text/javascript">
$(function() {
    $('#btn-getbookList').on( 'click', function () {
        var pageIndex = parseInt($('input[name=pageIndex]' ).val());
        var pageSize = parseInt($('input[name=pageSize]' ).val());
        getBookList(pageIndex, pageSize);
    });
});

function getBookList(pageIndex, pageSize) {
    $.post("jsonpage", {
        pageIndex : pageIndex,
        pageSize : pageSize
    }, function(jsonResult) {
        var html = '<table class="table table-striped table-bordered table-hover">';
        for(var i = 0 ; i < jsonResult.length ; i++) {
            html += '<tr>';
            html += '<td>' + jsonResult[i].title + '</td>' ;
            html += '<td>' + jsonResult[i].author + '</td>' ;
            html += '<td>' + jsonResult[i].comment + '</td>' ;
            html += '</tr>';
        }
        html += '</table>';
        $( '#bookPage').html(html);       
    }, 'json');
}

</script>

<label>Page Index</label>
<input name ="pageIndex" type="number" />
<br/>
<label>Page Size</label>
<input name ="pageSize" type="number" value="3"/>
<br/>
<input type ="button" id="btn-getbookList" value="GetBookList"/>

<fieldset>
    <legend>Book List page </legend>
    <div id="bookPage" ></div>
</fieldset>

javascript를 이용해서 button에 click event를 bind시키고 있습니다. 그리고 getBookList function은 json을 통해서 얻어온 BookList의 숫자대로 loop를 돌아서 HTML code를 생성하는 것을 볼 수 있습니다. 최종적으로는 <table> 코드가 만들어져서 bookPagae라는 div tag에 추가 되는 형식으로 동작하게 됩니다. 


Ajax + HTML을 이용한 Paging

이 방법은 Controller에서 HTML View를 이용해서 보여지는 방법입니다. 서버단에서 View를 만들어주고, 그 View를 보여주는 방식으로 동작하게 됩니다. 이 View는 위 JSON으로 만들어지는 HTML과 완전히 동일한 HTML을 보내게 됩니다. 먼저 Controller 코드부터 살펴보도록 하겠습니다. 

    @RequestMapping( value = "book/list/index2" )
    public String getBookListIndex2() {
        return "book/list/index2" ;
    }

    @RequestMapping( value = "book/list/html" , method = RequestMethod.POST )
    public String getBookHtmlList( @RequestParam(value = "pageIndex") int pageIndex,
            @RequestParam(value = "pageSize") int pageSize ,
            Model model ) {
        List<Book > books = bookService. listup(pageIndex , pageSize);
        model.addAttribute ("books", books);
        return "book/list/html" ;
    }

Controller code는 매우 단순합니다. JSON으로 보내는것과 매우 유사합니다. 다만,  getBookHtmlList method에서 View name을 return 하는것만 다릅니다. book/list/html의 view code는 다음과 같이 구성됩니다.  view code는 바뀔 부분에 대한 HTML만을 보내게 됩니다. 

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ page language= "java" contentType ="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<table class ="table table-striped table-bordered table-hover">
<c:forEach var="book" items="${books } ">
    <tr>
        <td>${book.title }</td>
        <td>${book.author }</td>
        <td>${book.comment }</td>
    </tr>
</c:forEach>
</table>

위 javascript로 구성되는 html과 완전히 동일한 html을 만들것입니다. 이제 book/list/index2 view code를 살펴보도록 하겠습니다.

<script type="text/javascript">
$(function() {
    $('#btn-getbookList').on( 'click', function () {
        var pageIndex = parseInt($('input[name=pageIndex]' ).val());
        var pageSize = parseInt($('input[name=pageSize]' ).val());
        getBookList(pageIndex, pageSize);
    });
});

function getBookList(pageIndex, pageSize) {
    $.post("html", {
        pageIndex : pageIndex,
        pageSize : pageSize
    }, function(htmlResult) {
        $( '#bookPage').html(htmlResult);       
    });
}

</script >

<label>Page Index</label>
<input name ="pageIndex" type="number" />
<br/>
<label>Page Size</label>
<input name ="pageSize" type="number" value="3"/>
<br/>
<input type ="button" id="btn-getbookList" value="GetBookList"/>

<fieldset>
    <legend>Book List page </legend>
    <div id="bookPage" ></div>
</fieldset>

javascript에서 보내진 데이터를 그대로 div element에 집어넣은것 이외에는 차이가 없습니다. 


두 방법의 장,단점은 다음과 같습니다. 

방법장점단점
Ajax + JSON1. 데이터 전송량이 작다
2. client에서 html을 처리하기 때문에 server의 부하가 작다
1. javascript로 html을 만들어줘야지 되기 때문에 코드 작성에 어려움이 있다.
Ajax + HTML1. HTML + Server 코드이기 때문에 코드 작성이 간편하다

1. 데이터 전송량이 많다.
2. 서버에서 html rendering이 되기 때문에 server의 부하가 발생한다.


이 두 방법이 각각 장,단점을 가지고 있지만 어떤 방법이 더 우월하다고는 보기 힘듭니다. Ajax + HTML의 서버부하는 HTML cache를 통해서 충분히 해결이 될 수 있는 문제이고, javascript의 경우에도 구조화와 객체화를 통해서 충분히 쉬운 코드를 작성할 수 있습니다. 이것은 Style의 문제입니다. 그리고 개발에서 한가지 방법만을 정해두고 사용하기 보다는 주와 부를 나눠서 주는 Ajax + JSON으로 하고, javascript가 너무 복잡해지는 코드의 경우에는 Ajax + HTML로 구성하는것도 충분히 좋은 방법이라고 할 수 있습니다. 


Posted by Y2K
,