'분류 전체보기'에 해당되는 글 80건

  1. 2013.12.26 unit test spring restTemplate by 파이팅야
  2. 2013.11.26 lombok by 파이팅야
  3. 2012.09.26 RestTemplate 으로 첨부 파일내용 보내기 by 파이팅야 8
  4. 2012.07.09 jQuery.validate와 spring validate 연동 by 파이팅야 2
  5. 2012.03.22 '웹 개발자를 위한 Spring 3.0 프로그래밍'을 읽고... by 파이팅야 3
  6. 2012.03.19 spring mvc 3 date by 파이팅야 4
  7. 2012.03.18 spring mvc 3 code 관리 (enum) by 파이팅야 4
  8. 2012.03.08 spring mvc 3 error log by 파이팅야 4
  9. 2012.02.24 JIRA 와 Redmine 비교 by 파이팅야 3
  10. 2011.07.07 HTML5 웹표준컴퍼런스2011 다녀와서 by 파이팅야 3

This blog demonstrates how to setup and test webservice client codes written in Spring RestTemplate.

Obtaining the jar

I am using Spring 3.1 here and have to include the jar file from the spring-mvc-test project into the classpath. The project has since been incorporated into Spring 3.2. See the Spring documentation for details. The codes here are tested against Spring 3.1 and spring-mvc-test. The APIs may be somewhat different in Spring 3.2.

Restful webservice client

My webservice client communicates with the server via XML so I setup RestTemplate with JaxB marshaller and unmarshaller as follows:

    <bean id="exampleRestClient" >
        <property name="template" ref="exampleRestTemplate"></property>
        <property name="baseUrl" value="<server url>"></property>
    </bean>

    <!-- RESTful template and related beans -->
    <bean id="exampleRestTemplate">
        <property name="messageConverters">
            <list>
                   <ref bean="marshallingHttpMessageConverter"/>
            </list>
        </property>
    </bean>

    <bean id="marshallingHttpMessageConverter">
      <property name="marshaller" ref="jaxb2Marshaller" />
      <property name="unmarshaller" ref="jaxb2Marshaller" />
    </bean>

    <bean id="jaxb2Marshaller">
        <property name="contextPath">
                <value>com.blog.resttemplatetest.jaxb</value>
        </property>
        <property name="schema" value="classpath:myschema.xsd"/>
    </bean>

The webservice client implements the normal CRUD functions by delegating to its RestTemplate bean. I will focus on the put function here and demonstrate how to write unit test for it in next section.

@Component(“exampleRestClient”)
public class ExampleRestClient {

private RestTemplate template;
private String baseUrl; // base url

public ExampleResponse add(final ExampleData resource, final ExampleResponse response) {
return put(baseUrl, resource, response);
}

private ExampleResponse put(final String url, final ExampleData resource, final ExampleResponse response) {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
final HttpEntity<ExampleData> requestEntity = new HttpEntity<ExampleData>(resource, headers);
final ResponseEntity<ExampleResponse> reponseEntity = template.exchange(url, HttpMethod.PUT, requestEntity,
response.getClass());
return reponseEntity.getBody();
}

// getters and setters

Note that instead of using the put() method in RestTemplate, I use its exchange() method in order to get the return response from the web service, as the RestTemplate#put method does not return anything. Both input and output classes ExampleData and ExampleResponse are JAXB objects generated via xjc from the example XML schemamyschema.xsd.

Writing unit test for webservice client

spring-mvc-test provides a mock server class MockRestServiceServer to support client testing. For example, to test the put add method in our ExampleRestClient:

...
import static org.springframework.test.web.client.RequestMatchers.method;
import static org.springframework.test.web.client.RequestMatchers.requestTo;
import static org.springframework.test.web.client.ResponseCreators.withSuccess;
...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:example-test.xml" })
public class StorefrontRestClientTest {
    private MockRestServiceServer mockServer;

    @Autowired
    private ExampleRestClient service;

    @Before
    public void setUp() {
        mockServer = MockRestServiceServer.createServer(service.getTemplate()); // (1)
    }

    private String loadXmlFile(final String filename) throws IOException {
        // load xml file into String ...
    }

    @Test
    public void testAddReturnCorrectResponse() throws Exception {

        final ObjectFactory factory = new ObjectFactory();
        final String responseXml = loadXmlFile("/test/expectedResponse.xml");

        mockServer.expect(requestTo(service.getBaseUrl()))
                  .andExpect(method(HttpMethod.PUT))
                  .andRespond(withSuccess(responseXml, MediaType.APPLICATION_XML)); // (2)

        final ExampleData exampleData= factory.createExampleData();
        final ExampleResponse response = (ExampleResponse) service.add(exampleData, new ExampleResponse());
        assertEquals(200, response.getStatus()); // (3)
        assertEquals(12345, response.getID()); // (3)
        mockServer.verify(); // (4)
    }
    ...
// file: expectedResponse.xml
<?xml version="1.0" encoding="UTF-8"?>
<tns:exampleResponse xmlns:tns="http://" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.example.com/example example.xsd ">
  <tns:status>200</tns:status>
  <tns:ID>12345</tns:ID>
</tns:exampleResponse>

Note:

  1. Instantiate mock server with the RestTemplate to be tested
  2. This sets the expectations on the request and mocks the response to be returned, in this case the Xml string stored in file expectedResponse.xml (above).  Note the static imports required at the top of the codes forrequestToaddExpect and andResponse.
  3. Junit assertions as would normally included.
  4. Call verify() method to ensure that expectations are being met.

imrt static 매번하기 귀찮으면
eclipse Window>Preferences>Java>Editor>Content Assist>Favorites 에 다음의 내용 추가하면 됨
org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
org.springframework.test.web.client.match.MockRestRequestMatchers.*;
org.springframework.test.web.client.response.MockRestResponseCreators.*;
org.junit.Assert.*;

참고 url



Posted by 파이팅야
,

lombok

풀그림 2013. 11. 26. 20:00


Lombok features overview

val
Finally! Hassle-free final local variables.
@NonNull
or: How I learned to stop worrying and love the NullPointerException.
@Cleanup
Automatic resource management: Call your close() methods safely with no hassle.
@Getter / @Setter
Never write public int getFoo() {return foo;} again.
@ToString
No need to start a debugger to see your fields: Just let lombok generate a toString for you!
@EqualsAndHashCode
Equality made easy: Generates hashCode and equals implementations from the fields of your object.
@NoArgsConstructor@RequiredArgsConstructor and @AllArgsConstructor
Constructors made to order: Generates constructors that take no arguments, one argument per final / non-null field, or one argument for every field.
@Data
All together now: A shortcut for @ToString@EqualsAndHashCode@Getter on all fields, and @Setter on all non-final fields, and@RequiredArgsConstructor!
@Value
Immutable classes made very easy.
@SneakyThrows
To boldly throw checked exceptions where no one has thrown them before!
@Synchronized
synchronized done right: Don't expose your locks.
@Getter(lazy=true)
Laziness is a virtue!
@Log
Captain's Log, stardate 24435.7: "What was that line again?"
@Delegate
Don't lose your composition.
experimental features
Here be dragons: Extra features which aren't quite ready for prime time yet.



References

1.     Lombok site : http://projectlombok.org/

2.     Lombok features : http://projectlombok.org/features/index.html

3.     Lombok annotion 설명 : http://jnb.ociweb.com/jnb/jnbJan2010.html

4.     Project Lombok - http://projectlombok.org

5.     Lombok API Documentation - http://projectlombok.org/api/index.html

6.     Project Lombok Issues List - http://code.google.com/p/projectlombok/issues/list

7.     Use Lombok via Maven - http://projectlombok.org/mavenrepo/index.html

8.     Project Lombok Google Group - http://groups.google.com/group/project-lombok

9.     Reviewing Project Lombok or the Right Way to Write a Library - http://www.cforcoding.com/2009/11/reviewing-project-lombok-or-right-way.html

10.   Morbok: Extensions for Lombok - http://code.google.com/p/morbok

11.   Using Project Lombok with JDeveloper - http://kingsfleet.blogspot.com/2009/09/project-lombok-interesting-bean.html


Posted by 파이팅야
,

이렇게 쓸 필요는 없을 것 같지만... 막 짜본 내용

ㅇ. servlet-context.xml (FormHttpMessageConverter 추가 한다.)

  <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
    <property name="messageConverters">
    <list>
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
    <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
    <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
    </list>
    </property>
    </bean>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

        <property name="maxUploadSize">  

            <value>104857600</value>  

        </property>  

     </bean>

ㅇ. view 내용

<spring:url value="/test/upload" var="addUrl"/>

<form id="inquiryForm" action="${addUrl}" method="POST" enctype="multipart/form-data" >

<input type="file" name="upload"/>  

<input type="submit" id='test'>

</form>


ㅇ. 호출 하는 쪽 Controller (Base64로 file 정보를 encoding한다.)

@RequestMapping(value="/upload", method=RequestMethod.POST)

public void index(@RequestBody MultipartFile upload) throws IOException {

String url = mwDefaultUrl + "/inquiry/testUpload" ;

String encodedString = Base64Helper.encodeToString(upload.getBytes(), false);

formData.add("upload", encodedString);

formData.add("fileName", upload.getOriginalFilename()); 

                HttpHeaders requestHeaders = new HttpHeaders();  

    requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(formData, requestHeaders);  

ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); 

System.out.println("in web response.getBody[" + response.getBody() + "]");

}


ㅇ. 받는 쪽 Controller(Base64로 decoding한다.)

    @RequestMapping(value="/testUpload" , method=RequestMethod.POST )

public @ResponseBody String index(@RequestParam("upload") String encodedString, String fileName, HttpServletRequest httpRequest) {

    byte[] upload = Base64Helper.decode(encodedString);

OutputStream out = null;

try {

out = new FileOutputStream("d:\\data\\" + fileName);

out.write(upload);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

if (out != null)

try {

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

           return "hoho test upload";

    }



Posted by 파이팅야
,


다음과 같은 jQuery.validate를 사용하는 jQuery plug-in을 만들고

;(function($){  

$.fn.ajaxHandler = function(instanceSettings) {

$.fn.ajaxHandler.defaultSettings = {

validator : null,

action : null,

method : null,

data : null,

dataType : "json",

cache : false,

eventName : "submit",

// function

beforeSendFunction : null,

successFunction : null,

errorFunction : null,

completeFunction : null,

};

var settings = $.extend({}, $.fn.ajaxHandler.defaultSettings, instanceSettings || {});


var ajaxThis = $(this);

//jQuery validation을 통과한 데이터를 이용해서 Spring에 ajax 호출을 시도한다.

$(this).off(settings.eventName);

$(this).on(settings.eventName, function() {

$.ajax({

url : (settings.action == null) ? $(this).attr("action") : settings.action,

type : (settings.method == null) ? $(this).attr("method") : settings.method,

data : (settings.data == null) ? $(this).serialize() : settings.dataType,

dataType : settings.dataType,

cache : settings.cache,

// ajax와 validation을 동시에 사용하기 위해서 사용한다.

beforeSend : function() {

var beforeSendResult = true;

if (settings.beforeSendFunction != null)

beforeSendResult = settings.beforeSendFunction();

  

var validationResult = true;

if (ajaxThis.get(0).tagName == "FORM" && settings.validator != null) {

validationResult = ajaxThis.valid();

if (!validationResult)

settings.validator.focusInvalid();

}

return (validationResult && beforeSendResult) ? true : false;

},

complete : function (request) {

if (settings.completeFunction != null)

settings.completeFunction(request);

},

success : function (jsonResult, textStatus, httpRequest) {

if (settings.successFunction != null)

settings.successFunction(jsonResult, textStatus, httpRequest);

},

error : function (httpRequest, ajaxOptions, thrownError) {

if (settings.errorFunction != null)

settings.errorFunction(httpRequest, ajaxOptions, thrownError);

if (400 == httpRequest.status && 

settings.validator != null &&

ajaxThis.get(0).tagName == "FORM") {

validationError(httpRequest, settings.validator);

} else {

$.processErrorForAjax(httpRequest);

}

}

});

 

// browser가 form을 submit하는 것을 막기 위해서 false를 리턴한다.

// true를 리턴할 경우 Spring이 반환하는 jsonResult 데이터가 화면에 표시된다.

return false;

});

//Validation Error 공통으로 처리하는 함수

//Spring에서 전달한 jsonData를 <filed, message>형태로 생성한후

//jQuery Validation을 통해서 Error message를 출력한다.

function validationError(request, validator) {

if (null == request)

return;

if (400 == request.status) {

var requestResult = $.parseJSON(request.responseText).result;

var errors = new Array();

$.each(requestResult, function() {

errors[this.field] = this.message;

});

validator.showErrors(errors);

validator.focusInvalid();

}

};

};

})(jQuery);


$(document).ready(function () {

$.validator.setDefaults({

errorElement: "span",

errorPlacement: function(error, element) {

var field = element.attr('id');

error.attr('id', field + '.errors');

error.insertAfter(element);

}

});

});


Controller에서는 다음과 같이 호출하고

private JsonResult getValidationErrorResult(BindingResult result, JsonResult jsonResult) {

    jsonResult.setCode(HttpServletResponse.SC_BAD_REQUEST);

    jsonResult.setMessage("Validation Error");

    

    List<ValidationError> errors = new ArrayList<ValidationError>(); 

    for (FieldError fieldError : result.getFieldErrors()) {            

        String message = this.messageSource.getMessage(

                fieldError.getCode(), fieldError.getArguments(), Locale.getDefault());

        errors.add(new ValidationError(fieldError.getField(), fieldError.getCode(), message));

    }

    jsonResult.setResult(errors);

    

    return jsonResult;

}

@RequestMapping(value="/save", method=RequestMethod.POST)

public @ResponseBody JsonResult getFaxSearchList(HttpServletResponse httpResponse, 

@Validated({Default.class}) @ModelAttribute FaxHistory faxHistory, BindingResult result) { 

JsonResult response = new JsonResult();


if (result.hasErrors()) {

      response = getValidationErrorResult(result, response);

       httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);

      return response;

}

// restTemplate로 DB Insert 처리

// Business Process

return response;

}



다음과 같이 호출하면 jQuery.validate의 에러처리와 spring의 에러처리하는 부분을 일관되게 하며, ajax 호출하는 부분도 일괄되게 할수 있지 않을지? 소스는 좀더 하면서 가다들어야 할듯...

// ajaxHandler 연동

$("#frmFaxHistory").ajaxHandler({

validator : faxHistoryValidator,

successFunction: function(jsonResult, textStatus, httpRequest) {

alert(' success Function 사용자 정의 호출 시');

}//,

// errorFunction : function(httpRequest, ajaxOptions, thrownError){

// alert(' error Function 사용자 정의 호출시');

// }

});


Posted by 파이팅야
,

 

개책개인적으로 참고 되었으면 하는 내용... 에구 붙여넣기 하니 들여쓰기가 이상해 지넹...

C.    
P64) 스프링컨테이너가 DI를 해줘서 OracleArticleDao articleDao = new OracleArticleDao(); 와 같은 구문이 필요없게 되고 해당 구문이 class에 들어가 있으면 의존성이 강해지게 된다.

D.    P70) lookup method injection

ii.        Singleton bean들은 spring container가 처음 한번만 bean 객체를 생성할 때 해당 singleton bean들의 property나 생성자에 DI를 한다. 그러므로 singleton bean에서 non-singleton bean DI할 수가 없다.(non-singleton bean끼리는 호출시점에 2개의 객체가 생성되어 서로 DI하면 되고, non-singleton에서 singleton을 참조하는 경우는 호출 시점에 non-singleton을 생성하고 초기에 이미 생성되어 있는 singleton DI하면 되므로 문제되지 않는다.)

1.   Application 이나 Business용 서버는 main 함수에서 applicationContext context에 접근할 때,

2.   WebApplication web.xml에서 <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>를 등록해서 root web application context 생성하고 <servlet><servlet-class>org.springframework.web.servlet.DispatcherServlet></servlet-class>를 등록해서 servlet web application context를 생성한 시점에 spring container singleton bean객체들이 생성된다. Root web application

iii.        Singleton bean A non-singleton bean B를 사용한다고 할 때, container singleton bean A를 단지 container가 한번만 생성할 것이고, 따라서 property도 역시 한번만 설정될 것이다. Container bean B가 필요한 매 순간 새로운 객체를 생성하여(특정 method를 호출해서) bean A에게 제공해야 하는 경우 사용 함. 책의 예제에서는 getCommandFactory() method를 호출할 때마다 새로운 CommandFactoryImpl 객체가 생성되어 Processor.process()를 사용할 수 있다.

iv.        http://www.egovframe.org/wiki/doku.php?id=egovframework:rte:fdl:ioc_container:dependencies

v.        http://www.javajigi.net/display/SFL/Method+Injection

E.     Lazily-instantiated beans(spring container에서 바로 bean 생성하지 않고 호출시 생성)

vi.        <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>

F.     p85) 자동설정은 사용하지 않는 것이 명확할 듯 싶습니다.

G.     p101) InitializingBean이나 DisposableBean과 같은 Spring 프레임웤 소스에 종속성이 있는 것을 사용하기 보다는 init-method destroy-method 속성을 이용하여 initializingBean DisposableBean을 대체 할 때 다음과 같은 장점이 있습니다. (http://whiteship.tistory.com/552)

vii.        초기화 메소드와 소멸자 메소드 이름을 마음대로 정할 수 있습니다.

viii.        Spring 프레임웤 종속성이 생기지 않습니다.

ix.        모든 bean들 에게 공통의 이름을 사용하고 싶다면 <beans /> 태그에서 default-init-method default-destroy-method 속성을 사용하면 됩니다.

x.        <beans /> 태그에 있는 defaut-xxx-mehot 속성을 <bean /> 태그에 있는 xxx-method oveeriding 합니다.

H.    P105) PropertyPlaceholderConfigurer @Configuration @Bean으로 정의하면 안된다. (http://toby.epril.com/?p=964)

xi.        class내에서 사용할 때는 @Value("${database.username}") private String username 과 같은 형식으로 사용하면 된다.

xii.        System property os명을 다음과 같이 가져올 수도 있음 @Value("#{systemProperties['os.name']}")

 

I.      P109) eclipse plug-in ResourceBundle Editor을 사용해서 properties 파일들을 수정하면 편함

xiii.        ApplicationContextAware를 상속받아서 사용하면 spring에 종속적으로 되므로 MessageSourceAccessor 를 사용해서 종속성을 줄여주는 것이 좋다. (http://masterofcoding.tistory.com/category/PROGRAMES/SPRING)

xiv.        ReloadableResourceBundleMessageSource를 사용하면 Application을 재시작하지 않아도 되고 변경된 내용을 참조하고 한글문제도 없다고 하나MessageSourceAccessor에서 읽는 문제가 있다고 좀더 테스트 해봐야 같습니다. Wmc java framework 그대로 사용함. (http://www.javajigi.net/pages/viewpage.action?pageId=2468)

A.     P 205) dispatcher servlet url-pattern '/'로 한 경우 javascript, css등의 static resource에 대해서 servlet이 처리 할 수 있도록 <mvc:resource>를 사용해야 한다. (http://blog.naver.com/PostView.nhn?blogId=kkkkang45&logNo=70092718296), <mvc:view-controller path="/" view-name="welcome"/> 것도 있음

B.     P 208) HTTP Header Accept라는 Header 값을 browser가 고정해서 전달해서 내가 원하는 midiatype으로 반환 받을 수 없으므로…, URL 확장자(http://localhost/test.json), request 'format' parameter, request accept header, defaultContentType 순으로 mediatype을 구하고 해당 mediatype에 맞는 viewResolver viewResolvers defaultViews에 정의되어 있는 값과 비교해서 적당한 것을 찾아서 해당 View를 사용하게 됩니다. ContentNegotiationViewResolver를 주로 사용합니다.

i.        http://dev.anyframejava.org/docs/anyframe/plugin/restweb/1.0.1/reference/html/ch03.html

ii.        http://stove99.tistory.com/19

C.     P 216) forceEncdoing property 값을 true로 해야 response 값도 encoding 됩니다.(http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/filter/CharacterEncodingFilter.html) 이 설정을 하면 form POST 방식으로 전송되는 것에 대해서 encoding이 되고, form GET방식으로 전송되는 것도 encoding하기 위해서는 apache server.xml의 내용을 수정해야 합니다. (http://springmvc.egloos.com/513986)

D.    P 259) return "account/creationForm"의 처리는 .net postback과 같이 생각하면 되면 입력했던 값들을 그대로 가지고 있다. 성공인 경우에는 redirect하는 것이 일반적임

 

E.     P 269) ASP.NET MVC와 같이 client validation 체크는 지원되지 않는다. Bean Validation을 이용할려면 http://www.slideshare.net/kingori/spring-3-jsr-303내용 참고 하고 error메시지를 properties 파일과 연동할려면 파일명을 'ValidationMessage_ko.properies'와 똑같이 만들고(물론 국가별로 '_xx' 는 추가해서 넣고) annotation명에 대한 validation 실패 에러메세지를 같이 작성한다.('예제로 쉽게 배우는 스프링 프레임워크 3.0' 책의 p436~440, p447 내용 참고하세요… 책은 제 자리에 있습니다.)

iv.        Javax.validation.constraints.Min.message={value}이상인 값을 입력해 주세요.

v.        Org.hibernate.validator.constraints.NotEmpty.message=입력해 주세요

F.     P 272) 여러 파일 첨부기능을 확인 할 때 아래의 코드를 참고하고, 파일 확장자는 'StringUtils.getFilenameExtension("testFolder/testjco.txt")'로 구할 수 있음

vi.        List<MultipartFile> multipartFileList = multipartHttpServletRequest.getFiles("file");

vii.             multipartFileList.isEmpty() ß 파일 첨부 안 해도 true값임

viii.         

ix.        for(MultipartFile multipartFile : multipartFileList) {

x.            multipartFile.isEmpty() ß 파일 첨부했는지 확인 가능 함

G.     P 287) SimpleMappingExceptionResolver를 사용해서 특정 Exception일 때 특정 view를 호출하도록 하는 것 많이 사용함, spring에서 발생하는 에러와 jsp에서 발생하는 에러 처리 관련(http://cranix.net/184http://cranix.net/186)

2.     기타

기타 참고 내용
H.   
Spring mvc ajax - http://maxheapsize.com/2010/07/20/spring-3-mvc-ajax-and-jquery-magic-or-better-simplicity/

I.      Spring mvc log4j 관련 - http://www.mkyong.com/spring-mvc/spring-mvc-log4j-integration-example/

J.      view에서 상대경로 참고할 때 - http://www.sivalabs.in/2011/07/context-root-relative-urls-using.html

K.     Spring mvc 3.1 에서 변경된 것 - http://blog.springsource.org/2011/06/13/spring-3-1-m2-spring-mvc-enhancements-2/

L.     jstl 관련 - http://sunfuture.springnote.com/pages/3514717

M.    myBatis 관련 - http://kaludin.egloos.com/2717395, http://www.mybatis.org/spring/sample.html

Posted by 파이팅야
,

spring mvc 3 date

Spring 2012. 3. 19. 19:56
화면에 특정 날짜형식으로 입력 받고, Controller에서 입력받은 값을 특정 날짜 형식으로 Model에 Binding하고 입력사항 검사도 할려면 다음과 같이 하면 될것 같다.

spring context에 'config.properties' file을 추가하고
<bean id="propertyPlaceHolder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8"/> 
</bean>

'config.properties'에 다음과 같이 date format pattern을 넣고
date.format=yyyy-MM-dd
datetime.format=yyyy-MM-dd HH:mm:ss

joda-time-2.1.jar 파일을 다운받고 다음과 같이 model에 @DateTimeFormat(parrent="${date.format}")를 입력해서 property파일과 연동 한 후
public class DateModel {
private String name;
@DateTimeFormat(pattern="${date.format}")
private Date registerDate;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getRegisterDate() {
return registerDate;
}
public void setRegisterDate(Date registerDate) {
this.registerDate = registerDate;
}
@Override
public String toString() {
return "DateModel [name=" + name + ", registerDate=" + registerDate
+ "]";
}
}
 
jsp파일에서 다음과 같이 입력 받거나 출력하면 된다.
<form:input path="registerDate"/> 

<spring:eval expression="dateModel.registerDate" />  


@DateTimeFormat를 사용하지 않은 Model의 날짜 형식을 다르게 해서 출력하고 property file의 내용을 참고 할려면... spring tag가 없는것 같아서 다음과 같이 해야 할것 같다..
Controller에 다음의 코드를 넣고

@Value("${date.format}")
private String dateFormat;

@ModelAttribute("dateFormat")
public String dateFormat() {
return this.dateFormat;
}

jsp파일에서 다음과 같이 사용한다.(깔금한 방법은 아니다. spring tag로 지원이 되어야 할것 같다. 아니면 다른 방법이 있는지... 찾아 봐야 할듯...)
<fmt:formatDate pattern="${dateFormat}" value="${communication.registerDate}"/> 


이렇게 하면 config.properties안의 날짜형식 pattern 값으로 날짜 형식을 관리 할 수 있을듯...
jQuery의 datepicker를 사용하는 부분은 다음과 같은 .js파일을 만들고 datepicker사용하는 부분에서 include해서 사용하고 국가별로 변경해서 사용하면 될듯...
jQuery(function ($) {
    $.datepicker.regional['ko'] = {
        monthNames: ['. 01', '. 02', '. 03', '. 04', '. 05', '. 06', '. 07', '. 08', '. 09', '. 10', '. 11', '. 12'],
        dayNamesMin: ['일', '월', '화', '수', '목', '금', '토'],
        showMonthAfterYear: true,
        dateFormat: 'yy-mm-dd',
        showOn: 'both',
        buttonImage: 'http://static.xxx.co.kr/calendar.gif',
        buttonImageOnly: true,
        changeYear: true
        //        yearSuffix: '',
        //        firstDay: 0,
        //        weekHeader: 'Wk',
        //        closeText: '닫기',
        //        prevText: '이전달',
        //        nextText: '다음달',
        //        currentText: '오늘',
    };


    $.datepicker.setDefaults($.datepicker.regional['ko']);
});
 
Posted by 파이팅야
,
code 관리를 enum으로 할 때 해당 코드들을 웹 화면에 comboBox, radiobutton, checkbox 등등으로 보여줄때 다국어 지원이 안된다. (<form:select> 안에 resourceBundle과 연동되어 처리되는 attribute가 있었으면 좋을련만...) 그럴때 다음과 같이 하면 어떨지... 간단하게 만들어봄.... refactoring은 더 해야 하지만...(DB에는 short type의 column에 숫자 코드값만 들어가는 경우임, DB의 table에 코드성 데이터가 있더라도 동적으로 추가 가능한 기능(페이지)가 없다면 enum으로 관리 할 수도 있을것 같다.


spring context에

<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:/message/validation</value>
<value>classpath:/message/label</value>
</list>
</property>
       <property name="defaultEncoding" value="UTF-8" />
       <property name="cacheSeconds">
           <value>60</value>
       </property>
</bean>

label_ko.properties 에 다음과 같이 WriterTypeCode에 대한 label명을 넣고
label.WriterTypeCode.CUSTOMER = \uACE0\uAC1D
label.WriterTypeCode.MANAGER  = \uAD00\uB9AC\uC790
label.WriterTypeCode.SYSTEM   = \uC2DC\uC2A4\uD15C

Code interface를 만들고
public interface ICode {
short getCode();
}


사용할 Code enum을 만들고
public enum WriterTypeCode implements ICode {
CUSTOMER((short)1),
MANAGER((short)2),
SYSTEM((short)3);
private short code;
private String codeLabel;
private WriterTypeCode(short code) {
this.code = code;
}
@Override
public short getCode() {
return code;
}
public void setCode(short code) {
this.code = code;
}
public String getCodeLabel() {
return codeLabel;
}
public void setCodeLabel(String codeLabel) {
this.codeLabel = codeLabel;
}
}

controller에서 modelAttribute를 만들고
@Controller
@RequestMapping("/code/*")
public class CodeEnumController {

@Autowired
private MessageSource messageSource;

@ModelAttribute("writerTypeCodes")
public EnumSet<WriterTypeCode> writerTypeCodes() {
EnumSet<WriterTypeCode> writerTypeCodes = EnumSet.allOf(WriterTypeCode.class);
String message = null;
for(WriterTypeCode writerTypeCode : writerTypeCodes) {
message = this.messageSource.getMessage(
"label.WriterTypeCode." + writerTypeCode.name(), null, Locale.getDefault());
writerTypeCode.setCodeLabel(message);
}
return writerTypeCodes;
}

@RequestMapping(value="/code", method=RequestMethod.GET)
public ModelAndView code() {
CodeGroup codeGroup = new CodeGroup();
codeGroup.setName("testName");
codeGroup.setOrder(1);
codeGroup.setWriterTypeCode(WriterTypeCode.MANAGER);
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;   



jsp에서 보여준다.
<form:select path="writerTypeCode" items="${writerTypeCodes}" itemLabel="codeLabel" itemValue="code"></form:select> 

<spring:message code="label.WriterTypeCode.${communication.writerTypeCode}" 
var="writerTypeCodeValue" />
writerTypeCode [${writerTypeCodeValue}]


post 방식으로 Model에 binding될때는 Controller에 다음의 코드를 넣고
@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(WriterTypeCode.class, new CodePropertyEditor(WriterTypeCode.class));
}

@RequestMapping(value="/code", method=RequestMethod.POST)
public ModelAndView codePost(@ModelAttribute CodeGroup codeGroup ) {
System.out.println("################## codeGroupResult[" + codeGroup + "]");
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;   
}

CodePropertyEditor 를 만든다.
public class CodePropertyEditor extends PropertyEditorSupport {
@SuppressWarnings("rawtypes")
Class clazz; 

@SuppressWarnings("rawtypes")
public CodePropertyEditor(Class clazz) {
this.clazz = clazz;
}

@Override
public void setAsText(String text) throws IllegalArgumentException {
if (!StringUtils.hasLength(text)) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!text.matches("\\d+")) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!ICode.class.isAssignableFrom(this.clazz)) {
super.setValue(null);
throw new IllegalArgumentException();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
EnumSet typeCodes = EnumSet.allOf(this.clazz);
int code = -1;
int parameterCode = Integer.valueOf(text);
for(Object typeCode : typeCodes) {
code = ((ICode)typeCode).getCode(); 
if (code == parameterCode) {
super.setValue(typeCode);
break ;
}
}
}

여기까지 하면 웹에서의 처리는 끝나고 DB쪽에 binding하기 위해서는 myBatis인 경우 다음과 같이 TypeHandler를 만들어서 연동 하면 된다.  다음과 같이 ACodeTypeHandler를 만들고 
public abstract class ACodeTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
//** log와 validation 체크 해야 함
private EnumSet<E> enumSet;

public ACodeTypeHandler(Class<E> enumClass){
if(!ICode.class.isAssignableFrom(enumClass))
throw new IllegalArgumentException(); // 메세지 추가하기
this.enumSet = EnumSet.allOf(enumClass);
}
@Override
public E getResult(ResultSet resultSet, String columnName)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnName));
}

@Override
public E getResult(ResultSet resultSet, int columnIndex)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnIndex));
}

@Override
public E getResult(CallableStatement callableStatement, int columnIndex)
throws SQLException {
if (callableStatement.wasNull())
return null;
return valueOf(callableStatement.getShort(columnIndex));
}

@Override
public void setParameter(PreparedStatement preparedStatement, int parameterIndex, 
E parameter, JdbcType jdbcType) throws SQLException {
     if (parameter == null)
     preparedStatement.setNull(parameterIndex, Type.SHORT);
     else
     preparedStatement.setShort(parameterIndex, ((ICode)parameter).getCode());
}
public E valueOf(short value) {
for(E e: this.enumSet){
if((((ICode)e)).getCode() == value)
return e;
}
return null;
}
}

WriterTypeCodeTypeHandler 를 만들고
public class WriterTypeCodeTypeHandler extends ACodeTypeHandler<WriterTypeCode> {
public WriterTypeCodeTypeHandler() {
super(WriterTypeCode.class);
}
}

mybatis config 파일에 다음의 내용을 넣으면 preparedStatement로 값셑팅하기 전과 ResultSet으로 결과값 Model에 binding 할때 자동으로 변환 된다.
<configuration>
<typeHandlers>
<typeHandler javaType="net.board.type.WriterTypeCode" handler="net.board.type.handler.WriterTypeCodeTypeHandler" />
</typeHandlers>
</configuration>


모든 Controller에서 사용할 수 있도록 AnnotationMethodHandlerAdapter.webBindingInitializer에 등록해도 되고... 이렇게 하면 enum으로 코드 관리가 되지 않을지... 

  --------------------------------------------------------------------------------------------------

mybatis와의 연동을 위해서는 ICode를 사용해야 하지만... 웹에서 comboBox나 listBox등에 enum의 내용을 출력하고 결과값을 해당 모델에 바인딩할때 ICode를 제거하고 spring:message와 연동해서 roof 돌면서 보여주고 '선택해 주세요'와 같은 것이 제일 상단에 보여지는 경우에는 결과값을 ''으로 해서 처리하면 '@ModelAttribute("writerTypeCodes")'의 소스코드와 initBinder()의 소스코드를 사용하지 않아도 된다. 예로 다음같이 view에서 enum값을 화면에 출력하고

<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:enumSelect path="modifyTypeCode" type="Header" ></t:enumSelect>


다음과 같은 tag 내용이면 modifyTypeCode용 @ModelAttribute가 필요없고 initBinder()에 소스추가도 필요없게 된다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>

<%@ attribute name="path" type="java.lang.Object" rtexprvalue="true" required="true" %>

<%@ attribute name="type" type="java.lang.String" rtexprvalue="true" required="false" %>

<spring:eval expression="'${nestedPath}'.substring(0, '${nestedPath}'.length() - 1)" var="basePath"/>

<spring:eval expression="${basePath}" var="baseBean"/>

<spring:eval expression="new org.springframework.beans.BeanWrapperImpl(baseBean)" var="beanWrapper"/>

<spring:eval expression="beanWrapper.getPropertyType('${path}')" var="pathClass"/>

<!-- Create a select option for each of the enum constants. -->

<form:select path="${path}">

    <c:if test="${type == 'Header'}">

            <form:option value=""><spring:message code="${pathClass.simpleName}.Header"/></form:option>

    </c:if>

    <c:if test="${type == 'All'}">

            <form:option value=""><spring:message code="All"/></form:option>

    </c:if>

    <c:forEach items="${pathClass.enumConstants}" var="enumConst">

        <!-- Value of option is the enum constant name, which spring knows

         how to bind using enum.valueOf(). -->

        <form:option value="${enumConst}">

        <!-- Label is pulled from propfile as simpleName.constant, e.g Color.RED -->    

        <spring:message code="${pathClass.simpleName}.${enumConst}"/>

        </form:option>

    </c:forEach>

</form:select>

Posted by 파이팅야
,

spring mvc 3 error log

Spring 2012. 3. 8. 17:44
에러에 대해서 급하게 정리 해봄

 web.xml에 http error와 web container error에 대해서 다음과 같이 기술하고
<!-- errorPage 설정 -->
   <error-page>
      <exception-type>java.lang.Exception</exception-type>
      <location>/WEB-INF/view/error/http/exception.jsp</location>
   </error-page>
   <error-page>
      <error-code>404</error-code>
      <location>/WEB-INF/view/error/http/404.jsp</location>
    </error-page>

spring mvc 설정에 다음과 같이 exception 발생 시 exception.jsp파일이 호출되게 한 후
<!--  Exception 처리 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">/error/exception/exception</prop>
</props>
</property>
</bean>
 

해당 jsp 파일에 다음의 내용을 추가해서 로그를 찍으면 좀더 유지보수에 좋지 않을지...
<%@ page import="org.apache.log4j.Logger" %>
<%
Logger logger = Logger.getLogger("spring exception");

// client의 cookie나 request Uri 정보등등을 로그에 남기기
String newLine = System.getProperty("line.separator");
StringBuffer sb = new StringBuffer()
.append("requestURL[").append(request.getRequestURL()).append("]").append(newLine)
.append("pathInfo[").append(request.getPathInfo()).append("],").append(newLine)
.append("method[").append(request.getMethod()).append("],").append(newLine)
.append("queryString[").append(request.getQueryString()).append("],").append(newLine)
.append("remoteHost[").append(request.getRemoteHost()).append("],").append(newLine)
.append("remoteAddr[").append(request.getRemoteAddr()).append("],").append(newLine)
.append("servletPath[").append(request.getServletPath()).append("],").append(newLine);
    // http header값도 로그에 추가
java.util.Enumeration names = request.getHeaderNames();
    String headerName;
    while (names.hasMoreElements()) {
    headerName = (String)names.nextElement();
    sb.append(headerName).append("[").append(request.getHeader(headerName)).append("],").append(newLine);
    }
    sb.append("serverName[").append(request.getServerName()).append("]");
logger.error(sb.toString());
// Exception 정보도 로그에 추가
if (request.getAttribute("javax.servlet.error.exception") == null) {
logger.debug("request.getAttribute('javax.servlet.error.exception') is null");
return; // Return from the JSP servelet handler.
}
// javax.servlet.error.status_code: 에러 상태 코드를 말해 주는 정수이다.
// javax.servlet.error.exception_type: 에러가 생기게 된 예외 형을 지적해 주는 클래스 인스턴스이다. 
// javax.servlet.error.message: 예외 메시지를 말해주는 스트링이며, 예외 컨스트럭터로 보내어 진다.
// javax.servlet.error.exception: 실제 예외가 없어지면 버릴 수 있는 객체이다. 
// javax.servlet.error.request_uri: 문제를 일으킨 리소스의 URI를 말해주는 스트링이다.
Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
logger.error("spring exception", throwable);
%>

결과 화면
17:33:02,150 ERROR spring exception:101 - requestURL[http://localhost:8080/board2/WEB-INF/view/error/exception/exception.jsp]
pathInfo[null],
method[GET],
queryString[null],
remoteHost[0:0:0:0:0:0:0:1],
remoteAddr[0:0:0:0:0:0:0:1],
servletPath[/WEB-INF/view/error/exception/exception.jsp],
accept[*/*],
accept-language[ko-KR],
user-agent[Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E)],
accept-encoding[gzip, deflate],
host[localhost:8080],
connection[Keep-Alive],
cookie[JSESSIONID=4218FE934DC460160394497F33DEE71E],
serverName[localhost] 

 
/WEB-INF/view/error/http/exception.jsp 파일에는 다음의 부분만 추가 및 수정하고...
response.setStatus(javax.servlet.http.HttpServletResponse.SC_OK);
Logger logger = Logger.getLogger("http exception"); 

 
asp.net mvc 에서는 다음과 같이 global.asax.cs에 넣고 ...
         protected void Application_Error(Object sender, EventArgs e)
        {
            if (Config.RedirectCustomErrorPage)
            {
                var exception = Server.GetLastError();
                var httpException = exception as HttpException;

                Response.Clear();
                Server.ClearError();

                var routeData = new RouteData();
                routeData.Values["controller"] = "Errors";
                routeData.Values["action"] = "ServerError";
                routeData.Values["exception"] = exception;

                Response.StatusCode = 500;

                if (httpException != null)
                {
                    /* 
                     * 100 Series : Informational 
                     * 200 Series : Success 
                     * 300 Series : Redirection
                     * 400 Series : Client Error
                     * 500 Series : Server Error
                     */
                    Response.StatusCode = httpException.GetHttpCode();
                    if (400 <= Response.StatusCode && Response.StatusCode < 500)
                        routeData.Values["action"] = "HttpError"; //400대 에러가 들어온다
                    else
                        routeData.Values["action"] = "ServerError"; // 500대 에러가 들어온다
                }

                IController errorController = new ErrorController();
                var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
                errorController.Execute(rc);
            }
            else
            {
                System.Text.StringBuilder sbErrMsg = new System.Text.StringBuilder();
                sbErrMsg.Append("[URL : (");
                sbErrMsg.Append(Request.Url.ToString());
                sbErrMsg.Append(")][URLRefferrer : (");
                sbErrMsg.Append(Request.UrlReferrer.ToString());
                sbErrMsg.Append(")][PATH_INFO : (");
                sbErrMsg.Append(Request.ServerVariables["PATH_INFO"].ToString());
                sbErrMsg.Append(")][REQUEST_METHOD : (");
                sbErrMsg.Append(Request.ServerVariables["REQUEST_METHOD"].ToString());
                sbErrMsg.Append(")][QUERY_STRING : (");
                sbErrMsg.Append(Request.ServerVariables["QUERY_STRING"].ToString());
                sbErrMsg.Append(")][HTTP_COOKIE : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_COOKIE"].ToString());
                sbErrMsg.Append(")][UserHostName : (");
                sbErrMsg.Append(Request.UserHostName.ToString());
                sbErrMsg.Append(")][HTTP_USER_AGENT : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_USER_AGENT"].ToString());
                sbErrMsg.Append(")][HTTP_ACCEPT_LANGUAGE : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"].ToString());
                sbErrMsg.Append(")][SERVER_NAME : (");
                sbErrMsg.Append(Request.ServerVariables["SERVER_NAME"].ToString());
                sbErrMsg.Append(")]");

                Exception exErr = Server.GetLastError();
                Logger.GetInstance().Log.Error("(Global.Application_Error) Application Error, ["
                    + sbErrMsg.ToString() + "]", exErr);
            }
        }
Posted by 파이팅야
,

JIRA 와 Redmine 비교

기타 2012. 2. 24. 17:22

 

1.     JIRA Redmine 정보

A.     기본정보

시스템

만든 곳

/무료

개발언어

런칭년도

JIRA

Atlassian

유료

Java

2003

Redmine

Jean-Philippe Lang

무료(open source GPLv2)

Ruby on Rails

2006

B.      관련 상세내용

                     i.         JIRA (설명된 blog 내용)

1.      기본 내용 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

2.      User Guide 1 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

3.      User Guide 2 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

4.      Admin Guide 1 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

5.      Admin Guide 2 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

6.      Admin Guide 3 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

7.      Admin Guide 4 : http://blog.naver.com/hoi5man?Redirect=Log&logNo=60147341965

                    ii.         Redmine

1.      features : http://www.redmine.org/projects/redmine/wiki/Features

2.      Guide : http://www.redmine.org/projects/redmine/wiki/Guide

3.      Online demo : http://demo.redmine.org/

2.     JIRA Redmine 비교

A.     비교 내용

 

JIRA

Redmine

/무료

유료

무료(open source, GPLv2)

wiki, 문서관리

따로 문서관리 기능이 없다. Confluence  문서 관리하고 JIRA에서 링크 추가하는 형태로 사용하는 편이다.

Project wiki, 게시판, 문서관리 메뉴들이 있어 관리 할 수 있다.

검색

JQL(JIRA Query Language) SQL 쿼리문과 비슷하게 작성해서 검색할 수 있고 검색된 조건을 저장해서 나중에 쉽게 다시 검색할 수 있다.

15가지 검색 필드로 검색이 가능하다.

UI

기능이 많아서인지 UI가 한눈에 보이지 않는 부분이 있어서 처음에 접할 때는 쉽지 않은 부분이 있다. (관리기능이 주로 그러함) 단축키를 지원한다. 기존에 workflow 작성의 어려움을 개선하고자 Visual Workflow기능이 최근에 추가되었다.

UI가 직관적이고 쉽다. 단축키는 지원하지 않는다. Visual workflow 설정기능은 없고 간단하게 추가하는 기능이 있다.

Perfoce 연동

Atlassian's FishEye를 설치하면 연동 가능하다.

redmine 사이트에는 perforce 연동에 대한 내용은 없으나 블로그 내용으로는 연동 된다고 함.

editor

WYSWYG editor가 강력하다

기본적인 editor가 있다.

Sub Project 관리

sub Project를 만들 수 없다. 대신 하나의 프로젝트에 여러 개의 sub task들을 만들 수 있도록 JIRA 5.0버전에서 지원할 예정이다.

Project tree 구조와 같이 sub Project를 계속 만들 수 있다.

issue 이동

하나의 issue를 다른 project로 옮길 수 있다. 여러 프로젝트들을 한번에 볼 수 있다.

A project에서 B project issue를 옮길 수는 없으나 A project sub project 사이에는 issue를 옮길 수 있다.

plug-in

JIRA가 업데이트 될 때마다 플러그인의 오류가 발생할 수 있으나 JIRA에서 플러그인의 유효성을 체크하는 기능이 있고 심각하지는 않다고 함.

플러그인을 만들어서 확장할 수 있으나 Redmine의 업그레이드가 잦아질 때 마다 관련 플러그인에서 오류가 발생하는 문제가 있다.

코드 리뷰

Crucible를 설치하면 JIRA에서 코드리뷰 기능을 추가할 수 있다고 함

관련 기능 없음

애자일 개발관련

GreenHopper를 설치하면 JIRA에서 애자일 프로젝트 관리 기능을 추가할 수 있다.

관련 기능 없음

4.     Reference

A.     Comparison of issue-tracking systems : http://en.wikipedia.org/wiki/Comparison_of_issue_tracking_systems

B.      Comparison of project management software : http://en.wikipedia.org/wiki/Comparison_of_project_management_software

Posted by 파이팅야
,

1.     표준기술 적용 운영 사례

A.      오픈뱅킹(우리은행, 국민은행, 기업은행)

                         i.         Why IE – 인터넷 뱅킹이나 쇼핑몰등에서 ActiveX를 사용해서 IE의 점유율을 줄일 수는 없었다.

                        ii.         한국 인터넷 뱅킹 현황 - Active-X, (공인)인증서 서버인증서, 개인인증서, 암호화 교신,  금융 기관이 주는 보안 프로그램 (http://openweb.or.kr/?page_id=1028)

                       iii.         오픈뱅킹 우리은행에서 2010.7 월 국내 최초 서비스, OTP 사용으로 ActiveX-모듈 사용 배제(OTP를 타사의 눈치가 보여 그냥 주기는 그래서 이벤트에 참가하면 무료 증정하도록 함, 하나의 OTP로 타 은행에서도 사용 가능), 10만명 인터넷 뱅킹 신규 가입, 2,500명 신규 계좌 개설, 여수신 잔액 100억원 가량

                       iv.         우리은행 오픈뱅킹의 의미 (http://openweb.or.kr/?p=3059)

1.      화려함을 위해 플래시를 사용하지 않고 깔끔한 페이지 레이아웃,

2.      플러그인으로 암호화 하지 않고 웹페이지를 경량으로 구축 후  웹브라우저를 사용한 SSL 암호화를 사용하여 기존 플러그인을 사용하는 은행들 사이트보다 빠르다

3.      불필요한 보안 프로그램 제거 효과(방화벽, 바이러스, 키보드 보안[마우스로 클릭하게 만듬])

                        v.         지원 가능 OS/브라우저 리눅스에서 오페라 및 크롬이 조회서비스만 가능하고 나머지 OS(윈도우, 매킨토시) 및 브라우져(IE, 파폭, 사파리, 오페라, 크폼, 우분투, 페도라)에서 모두 사용 가능

                       vi.         국민은행 플러그인 방식의 개인방화벽 사용, PC 지정하면 보안카드 사용가능 하도록 해서 OTP카드 의무화를 배제 했다.

                      vii.         기업은행 – nProtect netizen에 대해서 강제 설치 함

                     viii.         인터넷뱅킹 보안 규정 구현 방식

1.      공인 인증서 - ActiveX에서 플로그인 방식으로 변경

2.      개인 방화벽 설치 플러그인, AciveX

3.      키보드 보안 가상 키보드

4.      웹페이지 보안 – (브라우저에서 제공하는)SSL 표준 프로토콜로 감독기관 승인

B.      iOSHTML5 관련App

                         i.         HTML5 Rederence Guide

                        ii.         HTML5 PRO

C.      HTML5로구현된참고사이트

                         i.         Channy'sBlog(http://channy.creation.net/blog/776 )

                        ii.         차세대웹표준HTML5의탄생과미래(http://www.skyventure.co.kr/insight/web/view.asp?Num=17119&NSLT=Y)

                       iii.         HTML5 Demos and Examples (http://html5demos.com/)

                       iv.         HTML5 그놈이온다(http://www.iamcorean.net/187)

                        v.         HTML5 Gallery(http://html5gallery.com)

                       vi.         HTML5 and the Future of the Mobile Web (http://www.slideshare.net/wonsuk73/html5-and-the-future-of-the-mobile-web)

                      vii.         HTML5 examples(http://www.phpguru.org/html5-examples#html5.canvas.examples)

                     viii.         15 HTML5 Demos Showcasing Prowess of HTML5 Over Adobe Flash(http://www.techdrivein.com/2010/08/15-html5-demos-showcasing-prowess-of.html)

2.     HTML5 적용 사례 소개

A.      HTML5 왜 필요한가?

                         i.         HTML4 + Applet + Flash è HTML5 + Canvas + Video + APIs (HTML5의 기능으로 모두 커버가 된다. 장치 비종속적이다. 범용적 웹 어플리케이션 표준)

                        ii.         사용자

1.      Geolocation API로 장치의 지리적 위치 정보를 활용하는 향상된 사용자 경험 제공

2.      Web Workers API를 이용한 JavaScript 멀티 쓰레드 지원으로 빠른 웹 환경 제공

                       iii.         개발자

1.      효율적인 사이트 구성, 향상된 사용자 경험 제공

                       iv.         마크업 개발에서의 HTML5

1.      <div class=”header”> è <header> 로 코드 자체로서의 의미 강화

B.      개발 표준

                         i.         구조(HTML)와 표현(CSS)으로 변경 전 후

1.      변경 전 – (HTML + CSS)<td bgcolor=“yellow”>첫번째 셀</td>

2.      변경 후 – (HTML) <td>첫번째 셀</td>, (CSS) td{backgroundcolor:yellow}

                        ii.         무엇이 좋아졌나?

1.      호환성 – IE, 파폭, Safari, Chrome, Opera(W7, Mac)

2.      개발 프로세스 단축 디자인(CSS)과 개발(HTML)이 동시에 진행될 수 있다.

3.      UI관려 유지보수 비용 감소 디자인 관련 문제는 CSS만 수정함

4.      코드 용량 감소 – '(1)Table è (2)Div + CSS è HTML5' 형태의 변화로 코드량이 감소한다.

C.      적용 사례

                         i.         개인화 웹

1.      변경 및 폐지된 Element, Attribute 초점

A.      의미가 변경된 Element

                                                    i.         <b>, <cite>, <hr>, <i>, <menu>, <s>, <small>, <strong>

B.      HTML5에서 사용할 수 없는 Element

                                                    i.         <basefont>, <big>, <center>, <font>, <s>, <strike>, <tt >, <u>, <frame>, <framesets>, <noframes>, <acronym>, <applet>, <isindex>, <dir>

2.      구조(HTML)와 표현(CSS)의 구분 확실 - Background, bgColor, border의 속성 사용할 수 없고 CSS(표현)으로만 사용할 수 있다.

                        ii.         모바일

1.      <Input type='search'> 사용해서 iphone 우측 하단에 'Search'버튼 보이도록 함

3.     웹에서의 실시간 네트워크 커뮤니케이션

A.      Google docs를 같은 웹페이지를 2개의 창에 띄우고 한곳에 수정하면 다른 곳도 싱크 맞추어짐 ç HTTP is connectionless

B.      HTML4에서의 방법

                         i.         Polling

1.      서버에 주기적으로 물어보는 방법(Comet 아님)

2.      네트웤 자원 낭비하고 요청 주기에 따라 사용자는 딜레이를 체감함

                        ii.         Long Polling (http://www.uengine.org:8088/wiki/index.php/Comet_%EA%B5%AC%ED%98%84_%EA%B8%B0%EB%B2%95)

1.      처음에 한번 서버에 요청하고 이후 서버에서 변경이 있을 시 Client에 응답이 오는 구조

2.      구현에 따라 서버에 부하가 있다.

                       iii.         Streaming

1.      HTTP chunked(Response Data가 커서 나누어서 Client로 전달함)는 구조

2.      IE에서는 onreadystate 3번으로 날라오지 않아서 iframe을 사용함. 따라서 딸깍 소리 들림

C.      제약사항

                         i.         브라우져 마다 가능 가능한 최대 커넥션 수(IE 2개의 컨넥션만 만들 수 있다고 함)

D.     HTML5에서의 방법

                         i.         ServerSentEvent

1.      Server에서 보내는 정보만 받을 수 있다.

2.      Client간의 통신은 안된다.

3.      지원 하는 브라우져가 많지 않다.(IE 9 이상도 미 지원)

                        ii.         WebSocket

1.      양방향 통신 지원

E.      적용 사례

                         i.         마이피플 – flash socket 사용함, 추후 HTML5 WebSocket 으로 변경 예정

                        ii.         Naver 야구 9주기적으로 결과를 받는 내용이 많으므로 Long Polling 적용함.

4.     HTML5 2D, 3D, 벡터 그래픽

A.      기존에는 Photoshop, Flash, Silverlight, CSS 등으로 '차트, 컨트롤(버튼)'등을 만들었지만 이젠 HTML5에서 다 가능하다.]

B.      2D in Canvas

                         i.         사용 예 (사각형 그리기)

1.      <canvas id="c1" width="150" height="150">

2.      var canvas = document.getElementById('tutorial');

3.      if (canvas.getContext){  var ctx = canvas.getContext('2d'); ctx.fillRect(25,25,100,100); }

                        ii.         paper.js (http://paperjs.org/)

C.      2D in SVG

                         i.         사용 예

1.      <body>

2.      <svg width="400" height="300">

3.      <text x="100" y="100"

4.      font-size=”50px”

5.      fill=”rgba(0,0,255,1)”

6.      stroke=”red” onclick="something();">

7.      Hello World!

8.      </text>

9.      </svg>

10.   </body>

                        ii.         일러스트레이터로 그리고 다른 이름 저장해서 SVG 내용 복사 후 붙여 넣으면 됨

                       iii.         Raphael JS 를 사용하면 HTML5 지원 가능하면 SVG를 사용하고 IE에서는 VML를 자동으로 사용하도록 함 (http://raphaeljs.com/)

D.     WebGL (3D)

                         i.         고려사항 명암, 거리, 원근, 제질

                        ii.         OpenGL ES 2.0 , GLSL(OpenGL Shading Language) ç SpiderGL, GLGE, C3DL, SceneJS

                       iii.         사용 예(http://bodybrowser.googlelabs.com)

5.     웹앱 및 하이브리드앱 개발

A.      Native Apps (iOS, android SDK로 개발되는 것)

                         i.         장점 - High Performance, Full Device Resource, App Store, Gaming Machine

                        ii.         단점 개발자 비용 큼, 배우가 어렵다, App Store에 접수하기 어렵고 오래 걸린다. Adroid의 경우 삼성이 만든 것과 다른 곳에서 만든 것이 서로 틀리다. Device가 너무 많다. 버전 관리는 해야 한다.

                       iii.         웹 앱으로 개발 가능한 것들도 있다 잡지, 앵그리버드, 트위터 등등이렇게 할려면 HTML5로 좋다.

B.      HTML5

                         i.         장점 - Open Web Standard, Multi Device, Multi Platform, Realtime Update 지원

                        ii.         단점 – Device Resource(카메라와 같은 장치를 사용하지 못함), Accessibility(인터넷이 되어야 사용가능), confidence, Cross-domain(cross domain에 접근 안됨), performance 떨어짐

C.      사용빈도

                         i.         www.Html5test.com 에서 조사한 내용에 따르면 HTML5 mobile용 브라우져(safari, webkit, chrome)등에서 많이 사용한다.

D.     하이브리드 앱

                         i.         Device Resource 가 사용 가능하다(Camera, Contact, GPS)

                        ii.         앱에서 Browser의 접근에 대한 confidence은 믿을 수 있게 된다.

                       iii.         App Store에서 복잡한 게임성 카테고리를(30%) 제외한 다른 카테고리들(70%)은 모두 앱으로 개발 가능하다.

E.      적용 방법

                         i.         WebApps to native Apps

1.      PhoneGap, Appspresso(Eclipse IDE)

                        ii.         Build Native Apps with web Technology

1.      Titanium 여러 Native App으로 만들어 주지만 개발언어가 어렵다.

                       iii.         WebApp NativeApp을 각각 개발하고 합친다.

6.     리얼타임 서비스 개발을 위한 자바스크립트 기술

A.      Javascript 개발 구조 관련

                         i.         소스보기가 가능하고 복사해서 갔다 쓸 수 있고, Validation 용으로 사용할 수도 있지만, Ajax + DHTML 등을 이용해서 한 페이지에서 여러 가지 처리를 하는 응용프로그램(Application)을 만들 수 있다.

                        ii.         그러나 JavaScript에서는 개발 구조 Framework이 없어서 개발하기 어려웠다. 그래서 MVC MVVM(ModelView ViewModel) 등이 나왔다. 일반적으로 AjaxController를 호출해서 결과 JSON을 받아서 화면에 출력한다고 할 때 출력 값에 태그(Design)과 내용(Data)이 함께 있어 분업해서 작업하기 힘들어 진다. 이런 것을 중간에 편하게 템플릿 기능을 하는 knockout.js 가 지원해 준다. JSON HTML을 자동으로 연동하게 해 준다.

B.      Realtime Service

                         i.         SNS, facebook, twitter, google+,

                        ii.         Javascript 엔진인 'Google v8'을 사용해서 chrome을 만들었다고 한다. 이후 'google v8' 을 가지고 서버 프로그램이 가능한 node.js를 만들었다.

                       iii.         Node.js 장점 – NIO, HTTP lib 포함, module system, Javascript

                       iv.         WebSocket

                        v.         NowJS - Client에서 호출한 Method Server에서 바로 사용할 수 있게 된다. 프로토콜을 공유한다. (http://dev.paran.com/2011/05/17/nowjs-nodejs/)

1.      Client – now.sendData(“abcd”);

2.      Server – everyone.now.sendData = function(data){ … }

C.      Mobile Javascript

                         i.         jqueryMobile, Sencha Touch 등이 있지만 많은 것을 수용하느랴 무겁다. 따라서 사용용도에 따라서 사용할 수 있는 다양한 Micro javascript Framework(FormfactorJS, MinPubSub, Modernizr, JSON2, Objs, OORJa, $script.js, Artemia, Augment.js, domReady …)를 사용하는 것이 좋다.



Google Docs에 있는 첨부파일들 참고하기



Posted by 파이팅야
,