2012-09-26

OOM killer 라는게 있다는건 처음 알았네.
http://sylphong.egloos.com/523835

며칠동안 이유없이 tomcat이 죽더니... dmesg로 확인해 볼 것. 혹은 OOM killer 로 더 알아볼 것.

2012-09-25

Spring MVC Controller 의 REST scaffold

spring roo 가 제공하는 것을 약간 수정하여 나만의 scaffold로...
* roo는 update 경로가 좀 다르고
* list에서 spring data의 Pageable을 사용함.

http://java.dzone.com/articles/restful-standard-resolved 도 참고하자.


@RequestMapping
public String list(@PageableDefaults(pageNumber = 0, value = Config.PAGE_SIZE) Pageable pageable, Model model) {
}

@RequestMapping(method = RequestMethod.POST)
public String create(@Valid MyObj obj, BindingResult bindingResult, Model model, HttpServletRequest httpServletRequest) {
}

@RequestMapping(params = "form")
public String createForm(Model model) {
}

@RequestMapping(value = "/{obj}", method = RequestMethod.PUT)
public String update(@Valid @ModelAttribute("obj") MyObj obj, BindingResult bindingResult, Model model, HttpServletRequest httpServletRequest) {
}

@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public View remove(@PathVariable Long id) {
}

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView show(@PathVariable Long id, Model model) throws Exception {
}

2012-09-22


roo의 service는 aggregate repository 같다. domainTypes에 entity만 지정해주면 그에 해당하는 repository들을 모두 엮어 준다.  그렇다면 하나의 entity가 여러 service에 지정될 수도 있다. 하지만 이건 좋지 않은 것 같다. 서로 역할이 달라야 할 service들이 하나의 entity에 대한 operation들을 중복해서 가지게 되니까. 각 service가 처리하는 entity들은 관련 영역별로 구분하여 모아주는 것이 좋지 않을까. 물론 service라는 것이 지정된 entity만 처리할 수는 없을 것이다. 그럴 때는 domainTypes에 해당 entity를 지정하는 것보다는 그 entity의 repository를 DI 받도록 하자.

AspectJ가 주입된 메소드, 필드를 못찾는다 싶을 때에는 Markers -> Java Problems 를 보자. 여기에는 진짜 문제만 나타난다. 한번은 이클립스에 아주 많은 에러가 있는 듯 표시하지만 Java Problems에는 단 2개의 문제만 있었다. 그 2개의 문제만 해결했더니 모든 에러가 사라졌음.

2012-09-15

모든 entity에 생각없이 @RooEquals를 붙이고 있는데, 이게 구현이 HashCodeBuilder를 사용하면서 모든 field 에 대해 hashCode를 구하다 보니 서로 hashCode를 구하려고 하는 바람에 StackOverflow가 남.

mac에서 iso를 usb로 옮기기. 부팅 가능하게.

우선 아래 명령으로 usb 의 device명을 알아낸다.

$ sudo diskutil list

나의 경우는 /dev/disk4 였다.

usb를 꽂고 diskutil 로 erase 를 해준다. ms-dos (FAT)로.

그런 다음 partition을 unmount하자. 이 때 usb는 diskutil에 여전히 표시되고 있다.

이제 터미널에서 다음을 입력한다.

sudo dd if=myfile.iso of=/dev/disk4 bs=8192

음... 부팅안되는구나. 미안. ㅋ

2012-09-12

roo 를 적용하면서 오늘 하루 종일 기존 repository (가내 수공업 repository)를 active record 로 모두 바꾸고 있는데, 웹 서핑 하다 보니 다시 spring jpa 기반 repository로 바꾸고 싶다. active record 는 작은 프로젝트, repository 는 큰 프로젝트에 알맞다고...(?)

2012-09-11

2012-09-07

어제 내가 관리하는 서버가 ssh 접속이 안된다고 카페24에서 전화가 왔다. 덜컥... 후덜덜... 아직 정식으로 사용하는 시스템이 아니긴 해도, 백업 설정도 안되어 있는데...

또, 내 주제에 시스템 보안을 완벽히 할 자신도 없다는 생각도 든다. 

이럴 바에야 클라우드로 가는게 좋겠다는 생각이 드는데... 클라우드로 가도 시스템 보안은 마찬가지인가? 그렇다면 paas로 가야 하나?

jade4j

java에서 jade를 쓰고 싶어서 scalate를 쓰게 되었는데, jade4j로 갈아 타야겠다. 복잡한 페이지는 컴파일하는 시간도 오래 걸리고. 메모리는 왜 일케 많이 잡아 먹는거야. app reload하면 2~400메가씩 점유량이 올라가니 tomcat메모리를 max 2G 줘도 몇번 코드 수정하고 나면 빌빌댄다.

반면 jade4j는 그냥 text를 parse하여 변환하는 것 같다. pegdown, ognl 같은거 쓰더라. 에러 났을 때 보여주는 ui는 play와 닮았던데 그대로 가져온 모양이다. 멋지다. 페이지 inheritance도 훨씬 잘되어 있고. mixin은 써본 적 없지만 잘만 하면 좋은 용처가 있을 것 같고.

슬슬 옮겨 가자.

--

ognl을 사용하므로 scala 문법의 if, for 등을 ognl 구문으로 바꾸어야 한다.

.getXXX 메소드 호출을 .xxx 와 같이 property 접근으로 바꾸어야 한다. sublime text에서 작업해야만 했다.
Find: \.get(.)
Replace: .\L\1

jade4j 에서는 BigDecimal 을 표시할 때, scale 대로 표시해준다. db에 설정된 scale대로 값을 가지므로 사실상 정수임에도 불구하고 .0000 과 같이 소수점이하 자리가 표시된다. ognl 은 객체의 메소드 호출을 지원하므로, 이런 류의 포맷팅을 위한 객체 및 메소드를 만든 다음, 이 객체를 페이지의 모델에 포함시켜, 페이지에서는 이 객체의 메소드를 호출함으로서 원하는 조작을 할 수 있다. 이 외에도 그 객체에 유틸리티 메소드를 정의해두면 좋을 것이다.

uri method를 f.uri로 바꾸기.

Find: =\{uri(.+)\}
Replace: =f.uri\1

ConversionService와 @ModelAttribute 를 이용한 Entity Update (수정) 처리

스프링 사용자 그룹에 질문을 던졌다.

ConversionService 를 이용한 id -> ModelAttribute 변환

결론은 허탈하게 해결되긴 했지만 그래도 나름 내부 구조를 이해하는데 도움이 되기도 했으니 다행이다.

하지만 나로서는 더 중요한 문제가 해결되었다. 수정폼의 submit을 매우 간단하게 처리할 수 있게 되었다.

예전에는 이렇게 했다. http://blog.jabberstory.net/2012/08/blog-post.html

하지만 이제 매우 간단해졌다. 그냥 uri 와 @ModelAttribute로 받고 저장하면 끝. controller를 분리할 필요도 없다.

@RequestMapping(value="/user/{user}", method=RequestMethod.PUT)
public String update(@ModelAttribute("user") User user) {
   UserRepository.save(user);
}

여기서 uri에 있는 "user"와 @ModelAttribute에 붙어 있는 "user"가 같아야 한다. 그리고 String -> User 변환을 위한 Converter를 등록해 두어야 한다. 이 converter는 db에서 주어진 user 문자열로 User를 찾는 작업을 한다.

예전에 했던 방식은 UserDto로 @ModelAttribute으로 받은 다음, 별도로 find한 User에 필드 복사를 일일이 해주었다. (bean의 속성을 복사하는 유틸리티 클래스를 이용하기도 했다)

이렇게 하다가 귀찮아서 merge()를 사용했는데, merge()도 위험하다. 수정 폼에 모든 필드가 없다면 @ModelAttribute으로 받은 dto에는 null 필드가 있을 것이고, 이게 db필드로 들어갈 위험성이 있기 때문이다.

이에 대한 해결책으로 토비 책에는 @SessionAttributes을 사용하라는 얘기가 나온다. 하지만 이것도 해결책은 될 수 없는데, 동시에 열려 있는 여러 User에 대한 수정폼을 처리할 수 없다. session에 저장된 user는 하나이기 때문이다. (3.1 이전에 출간되었기 때문에 어쩔 수 없지 않았나 생각한다)

하지만 ConversionService와 @ModelAttribute을 동시에 이용하면 위의 문제를 모두 해결할 수 있다.

success !