2012-10-29

오늘 아침에는 서준이와 치과에 다녀왔다. 늘 그렇듯, 아이와 병원에 가는 것은 힘든 일이다. 예전에도 눈병이 났을 때에, 의사 선생님이 무지막지하게 눈알을 뒤집는데, 피가 주륵주륵 나는 것이 차마 볼 수가 없었다. 그냥 짠하고 눈물이 날 것만 같았다. 오늘은 앞니 두 개가 신경이 죽은 것 같다는 진단을 받았고, 그대로 두면 다음 치아에도 영향을 줄 수 있어 할 수 없이 치료를 하게 되었다. 결정을 하면서도 이게 잘 하는 일인지 쉽게 판단할 수 없었다. 신경치료라니. 신경이 죽으면 이빨이 검게 변한다고. 어쩌면 그냥 두어도 다음 치아가 날 때까지 버틸 수도 있지 않을까. 물어보니 다음 치아는 초등학교 들어갈 때 쯤 난다고 한다. 그렇다면 그 때까지 완벽하게 관리를 해야 하는데, 또 물어보니 양치질만으로 문제가 안 생긴다는 보장이 없다고.. 염증이라는 것이 몸의 전반적인 상태와 관련이 있으니.

일단 엑스레이를 찍었고, 치료할 때에는 마취를 한다. (아이에게 엑스레이와 마취가 어떤 영향이 있을지도 모르는 답답한 부모들이다 ㅠㅠ). 좀 진정을 한 후, 문제의 이빨의 앞면에 구멍을 낸다. 그런 다음 그 구멍을 통해 치아 속의 위부분까지 긁어 올라간다. 나중에 보면 치아가 겉 표면만 남고 안에는 텅 빈 모습이 된다. 그런 다음 내부를 채운다. 채우는 물질이 무엇인지는 잘 모르겠다. 서준이가 아파하면서 움직이니까, 그물 같은 담요로 몸을 덮고 잠궈버린다. 이런...

암튼 힘든 아침이었다. 그래도 수술 시간이 그리 길지 않아 다행이라고 해야 하나. 사실 예전에 왔을 때에도 치료를 해야 한다고 의사선생님이 말씀하셨는데, 좀 과잉진료인 것 같아 하지 않았었다. 오늘은 집사람이 왜 그냥 한다 했을까. 암튼 조금은 애매한 판단이었다고 생각은 드는데, 그래도 하고 나니 말끔한 앞니가 되어서 보기에는 좋긴 하다.

2012-10-26

http://www.youtube.com/watch?v=F9f0692r_dg&feature=em-uploademail-new

세바시에서 영어공부에 대해 얘기하는데, 아이에게 영어를 직접 가르치라고. 꽤 괜찮은 방법 같다.

2012-10-25

angular form validation 좋은 방법 없나?

제대로 하려면 일관된 방법이 있을 것 같기는 한데, 아직 어떻게 해야 할지 아이디어가 안잡히다보니 일단 이정도로만 처리하자. 더 좋은 방법이 있으면 누가 좀 알려주세요.

브라우저는 required, pattern 과 같은 것으로 기본적인 폼 validation 처리를 할 수 있다.
input이 이 조건이 충족되지 않으면 submit을 하지 않는다.

angular는 여기에 더해서 ng-minlength, ng-maxlength, ng-pattern 과 같은 것들을 제공한다. 그러나 문제는 form submit을 막아주지는 않는다. form submit을 못하게 하려면 어떻게 하나?

action속성을 제거하고 ng-submit을 사용하는 방법이 있다.

$scope.mainFormSubmit = function() {
    if (!$scope.mainForm.$valid) {
        $log.log("form is invalid");
    } else {
        $log.log("form is ok");
        // set action and submit using jQuery ?
    }
};

하지만 action 속성을 줄 수 없으니 갑갑하다.

또 하나의 방법은 submit 타입의 버튼을 없애고 button이나 anchor로 바꾸는 것을 생각해 볼 수 있다. ng-click handler에서 submit하면 된다. 이러면 action 속성을 줄 수 있다.

보너스로 아래와 같이 메시지로 피드백을 줄 수도 있겠다.

.alert.alert-error(ng-show="!mainForm.$valid") 입력값이 올바르지 않습니다.

2012-10-24

gpt partition

외장하드 하나를 집 컴퓨터에서 포맷을 한 다음 집사람 회사 컴퓨터에 꽂았더니 디스크가 보이지 않더란다. 디스크 관리자에서 gpt partition으로 뜨면서 포맷도 할 수가 없네.

다음 순서로 작업한다.

  • 커맨드라인에서 diskpart 실행
  • list disk
  • select disk x (x is number)
  • clean
이제 디스크관리자에서 포맷을 할 수 있다.

2012-10-22

조산소 다녀 왔다. 이제 일주일 정도만 지나면 둘째가 언제든지 나올 수 있다고... 아직 많이 남았다고 생각했는데, 어느새 시간이 이렇게 흘러 코앞으로 닥쳐왔구나. 별로 준비한 것도 없는데 말이야. 머 사실 특별한 준비를 해야 한다는 생각은 없지만 그래도 아내는 이것저것 생각이 많다. 미리미리 준비할 것도 많을테지. 나만 생각없는 남편으로 지내고 있다.  나로서야 이름을 어떻게 짓나 하는 것 뿐. 그나저나 앞으로 먹고 살 일이 걱정이긴 하네. 이젠 벌이도 시원찮고. ㅎㅎ

2012-10-19

스터디 책도 읽어야 하고, 내 기술서적도 읽어야 하고, 철학 서적도 읽어야 하고, 가끔 소설책도 읽어야 하고... 하악...

2012-10-12

owncloud를 설치해봤다.

쉽게 설치된다. 패키지를 받아서 (php로 작성되어 있다) /var/www 밑에 풀면 된다.

  • 몇가지 디렉토리의 소유를 www-data:www-data 로 바꾸고.
  • .htaccess를 사용하므로 000-default에서 AllowOverride 를 All로 바꾸고
  • a2enmod rewrite, a2enmod headers
  • service apache2 restart 하면 끝.
전용 클라이언트도 제공하고 좋아 보이더라. finder에서 접속하라고 WEBDAV 연결도 제공한다. 킹왕짱이다. 웹으로 연결하려면 http://server/owncloud 로 연결하면 되고. UI도 깔끔하더라.

그런데 이 새끼 이거. 
파일을 올릴 수가 없네. 안되네. webdav로 복사해도 느려 터져서 되는지 안되는지도 모르겠고, 전용 클라이언트가 지정한 폴더에 파일을 넣어도 싱크하겠다는 반응이 없어. 웹브라우저로 올리라고? 그렇게는 못하지. 

머... 찾아보면 우회하는 방법도 있겠지만... 내가 그리 한가하지 않은 관계로 오늘은 여기까지.

2012-10-10

spring roo를 쓰면서 결국 aspectj를 쓰게 된 셈인데, 이게 은근 불편한 점이 하나 있네. 에러가 하나라도 있으면 aspectj로 주입된 필드와 연관된 모든 라인을 에러가 있다고 표시를 하는구나. 내가 만든 에러가 어딘지 찾기가 무척 힘듦.
entity 클래스들이 연관 클래스와 상호작용하는 메소드들을 가지는 것이 좋은가? 아니면 이런 작업들은 Service에서 하는 것이 좋은가?

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 !