아주 조금씩 분위기가 바뀌고 있지만 야근을 열심히 하는 노동자의 미덕으로 보는 사회, 정년퇴직이 보장되지 않는

 

일자리 거기서도 높은 연봉을 받는 직업은 아주 소수의 사람들을 제외하고는 결국 돈이 아쉬워서 삶 = 일로

 

등가교환 해야하는 현실 혹은 삶의 질은 낮아지거나 겨우 살만큼의 돈을 받고 저녁있는 삶을 간신히 찾을수 있는

 

일자리 그나마도 안되서 일은 일대로 하고 급여는 적거나 불규칙한 일자리에서 지금의 20~30대들은 갈팡질팡하고

 

있는데 언제부터인가 YOLO(You Only Live Once) 라는 의미가 젊은층에 퍼지고 있다.

 

tvN 드라마 '혼술남녀' 중에서

 

좀 깊은 의미는 까짓거 한번 사는 인생 내가 원하는 대로 살아보자였을건데 언론이나 각종 미디어 매체, 이를 통한

 

마케팅을 펼치고 있는 기업광고등 최근 '혼밥', '혼술'등 사회적, 인간적, 경제적 스트레스를 뒤로 하고 혼자서 뭔가

 

자유로운 생활을 누리고 싶어하는 20, 30대 젊은층에게 하나의 트렌드로 자리 잡으려 하고 있는중으로 보인다.

 

나도 이 사회를 살아가는 평범한 30대 직장인으로서 이런 분위기에 대해 나쁘게 보진 않는다. 지금의 20~30대는

 

핵가족화가 이뤄지면서 2~4인 가족구성원으로서 살아오면서 부모님 세대와 다르게 본인의 개성, 감정 표출,

 

발언권을 대체적으로 충분히 가지고 살아온 세대이기 때문에 부모님 세대나 조금 더 세대차이가 있는 40대 중후반인

 

분들에게는 이런 문화가 생소하고 인생사에서 한번은 해야할 의무(결혼, 출산, 육아 등)를 회피하는 듯한 태도에 

 

거부감이 들겠지만 말이다.

 

 

(출처 : kybostory.co.kr)

 

그래서 때론 어른들의 삶, 경험의 기준에 맞춰 나름의 걱정 반, 오지랖 반으로 표현되면서 기성세대와 잦은 갈등,

 

마찰을 빚게되는게 요즘사람들에게는 스트레스로 다가오는것도 현실이다. 학창시절부터 대학은 어딜 갈거니?

 

졸업후엔 취업은 했니? 직장을 다니면 누구누구는 연봉이 얼마인 좋은 직장에 다녀서 좋은 여자, 남자와

 

얼마전 결혼했다더라 너는 언제쯤 할거니? 여기에 반박이라도 하면 너가 그러니까 못하는거다라는 잔소리를

 

듣기 쉽다. 여기서 끝이 아니라 많이 봤겠지만 결혼을 하고 나면 출산 얘기가 나오고 그나마 아이가 커나가면서

 

부모님들의 잔소리는 그래도 조금은 사그라 드는것으로 보인다. (끝은 아님 ;;)

 

이 와중에 갈등이 생기는 것중에 하나가 부모님 세대와 현세대의 사회전반적 시스템의 차이인데 부모님 세대는

 

우리는 너희 보다 어려운 시절을 겪고 살아왔다 너네가 밥을 굶어봤느냐, 요즘같이 먹을거 풍족하고 직장이

 

넘쳐나는 시대에 너희들은 배가 불렀다, 의지박약하다라는 표현으로 현 세대와의 갈등이 초래된다.

 

거의 대다수의 사람들은 자신이 해보지 못한 경험으로 상대방을 제대로 이해해보려 하지 않는다. 개인적으로

 

나는 부모님 세대의 역경과 고난을 이해하려고 노력하는 편이다. 부모님 세대의 모든이는 아니지만 많은분이  

 

독재정권, 끼니를 해결하지 못할정도의 가난, 가족구성원이 많아 학업에 대한 욕심이 있어도 성취할 수 없는

 

환경, 경직된 분위기의 저임금 일자리가 많은 시스템을 겪어오며 살아오신 분들이라고 생각한다.

 

얼마나 힘든 생활이었을지 현 세대 또한 경험해 보지 못했기때문에 당연히 이해하지 못할것이라고 생각든다.

 

하지만 모든 커뮤니케이션은 상호이해가 성립되야 이뤄진다. 현세대와 기성세대는 서로에 대한 개념적 양보와

 

이해없이는 영영 이런 대립적인 양상을 풀어나갈 수 없을것이다.

 

여기서 현세대의 어려움을 보자면 부모님 세대와는 다르게 겉으로 티가 나지 않는 고통을 겪고 있다. 우리세대는

 

겉으로만 보면 부모님 세대와는 비교를 불허할 정도로 잘 살고 있다.

 

 

 

70년대 서울의 모습

 

 

현재 서울의 모습

 

건물이 높고 화려하게 건축됐다고 모두가 잘살고 있다고 할 순 없으나 결국 이  모든것들이 우리 부모님 세대의

 

땀내나는 노동, 노력으로 이뤄진것은 사실이다. 부모님 세대와의 삶의 환경을 단순 비교했을때 우리는 먹고싶은것

 

있으면 벌어서 터치 몇번으로 배달시켜 먹고 살거리도 많고 구경할 것도 많고 인프라도 잘 구축되있다.

 

그런데도 우리는 불만을 토해낸다. 부모님 세대가 경험해보지 못해 이해할 수 없는 갈수록 복잡해지는 사회시스템

 

하에서의 노동, 여기서 살아남기 위해 남을 쓰러트려야만 내가 살 수 있는 현실, 현재 태어나는 아이들은 그보다

 

더 치열해질 미래를 위해 조기교육이니 영재교육이니 하며 초등학생이 놀지도 못하고 기계처럼 학원을 다녀야

 

하는 현 상황을 부모님들은 매체등을 통해서 간접적으로 이해한다고 하지만 본인의 경험이 아니기 때문에 이해하지

 

못하는 것이다. 부모님 세대에는 어렵지만 열심히 살고 아끼고 모으다보면 노후까진 생각하지 않았어도 어느정도의

 

삶을 누릴수 있는 고성장 시대였다. 아무것도 없으니 노력해서 점점 발전시켜 나올 수확물이 더 많았던 시대였다는

 

것이다. (여기서 97년 IMF사태는 제외하겠다. 혹시 IMF사태를 모르는 현재 20대는 IMF 사태만큼은 까서는 안된다.

 

극소수의 사람들을 제외하고 나라가 망할 위기였고 현재까지도 IMF사태이후로 절망적인 삶을 살아가는 분들이

 

많다. 참고 : https://namu.wiki/w/1997%EB%85%84%20%EC%99%B8%ED%99%98%20%EC%9C%84%EA%B8%B0)

 

 

 

이 시기에는 우리가 현재 재직중이라면 대기업 부심을 부릴만한 대기업들이 줄줄아 부도발생으로 해체되는 상황

 

이었다. 노동자들은 직장을 잃고 가계대출 위기는 지금과는 비교도 되지 않았으며 대학생들 또한 학비 부담을

 

피하기 위해 군대를 자원입대하는 형편이었으니까 말이다. 물론 이 상황에서 타이밍을 잘 잡은 사람들은 초저가에

 

주식 매수로 훗날 대박이 난 사람들도 일부분 있긴하다. (역시 인생은 한방인가 싶다..) 물론 잘못된 선택으로

 

더 심각한 나락으로 빠져든 분들도 엄청나다.

 

이런것들이 YOLO와 무슨 상관이 있느냐 싶겠지만 나는 지금 우리나라에서 돌고 있는 YOLO의 의미가 그까이꺼

 

얼마나 살고 얼마나 번다고 혼자 살면서 대충 나 혼자 만족스럽게 먹고 쓰고 마시자로 변질된 문화로 퍼지는것으로

 

느껴진다.

 

 

출처 : news.donga.com, 최상욱씨를 까려는 의도가 아님을 분명히 밝힙니다;;

 

베이비부머 시대의 은퇴, 장기적인 저성장 시대를 살아가는 가정이 있는 40대는 지금 허리띠를 졸라매고 있어서

 

기업들에게 YOLO같은건 어떻게든 끼워맞춰서 소비욕구를 끓어오르게 만드는 마케팅 활동을 펼치기 딱 좋은

 

소재이다. 10대는 아직 소비능력이 부족하므로 잠재적 소비자로 미뤄두고 20대는 알바나 직장생활등으로

 

소소하게 자신의 만족을 위해 뭔가를 구매할수 있는 소비층으로 특히나 매체의 영향을 더 민감하게 받아들이는

 

성향이 있기때문에 저렴하게 자기만족할 수 있을 만한 상품, 서비스를 홍보하기 딱 좋은 타겟이다.

 

 

 

거기에 TV프로그램으로 혼자 여유롭게 마음대로 살아가는듯한 모습을 보면서 대리만족까지 시켜주고 있다.

 

하지만 착각하지 말자. 대다수의 20~30대 직장인 (위의 최상욱 씨처럼 뭔가 일반 사람들보다 더 나은 수익원이

 

있거나 여튼 잘버는 분들은 패스!) 들은 현실을 직시해야 한다.

 

YOLO의 의미가 그냥 자기만족을 위해서 까이꺼 다 쓰고 즐기자 아니면 어짜피 먹고 살려고 돈 버는건데 뭐~ 라고

 

분위기에 휘둘려서 몽땅 써버리는 사람이 있다면 정신차려야 된다. 소소한 지속적인 소비도 어느정도는 조절할

 

필요가 있다.

 

흔히 백세시대라고들 한다. 과거와 달리 나날이 발전해가는 의학과학기술의 진보, 위생의 개선으로 점점 인간의

 

수명이 늘어나고 있다. 100세까지는 아니더라도 예전 세대보다 수명이 늘어나긴 했다는 것이다.

 

반면 우리가 실제 직장인으로 일할 수 있는 나이는 그렇게 길지 않다. 이 글을 보는 20대, 30대 초반의 직장인들은

 

이 또한 자신의 현실이 아니기 때문에 깊게 공감할 수 없을것이다.

 

'니가 능력이 부족해서 그런거지' , '나중에 노가다라도 뛰면 되지' 라는 식으로 생각할 수 있는데 첫번째 같은

 

주장을 펼치는 사람은 본인들이 소위 '꼰대'라고 욕하는 기성세대와 똑같은 행동을 하는거니까 노답이라 생각한다.

 

두번째의 경우 자신이 체력이 넘쳐나고 꾸준한 운동등으로 언제나 가동률이 넘쳐나는 사람들은 그나마 어느정도

 

삶을 챙길수는 있을거라 본다. 그러나 이것도 어느정도 나이에서는 한계가 온다. 내 지식이나 힘이 부족해서가

 

아니라 사회 시스템이 나이에서 필터링 하게 되어있기 때문이다. 그런다고 사회탓만 해서는 자신에게 돌아오는건

 

없다. 그저 나말고 누군가 어떻게든 이 사회를 바꿔주세요라고 하는 애어른같은 칭얼거림밖에 안될뿐인것이다.

 

물론 이런 상황에 속하지 않는 사람들도 분명 다수 존재한다. 하지만 그보다 평범한 사람들이 훨씬 많다.

 

나랑 내 주변 사람들은 안그러던데요? 같은 걸로 일반화 시킨다면 별수 없는것이다. 알아서 살고 나중에 나이들어서

 

경험해보면 알겠지라고 생각한다.

 

지금 우리의 상황을 보면 50대까지 별탈없이 직장생활을 하기가 매우 힘든분들이 많다. 그 대상이 내가 될 수도

 

있다는 생각은 항시 가져야한다. 왜 보험상품은 가입하면서 그렇게 소중한 자신에 대해서는 생각해 보는 공짜 보험을

 

들지 않는지 의문이 들때가 많다.

 

사람마다 가치관, 생각, 생활패턴이 모두 제각각이지만 물질만능주위 사회하에서 살아가는 우리에게 돈 앞에선

 

모두가 숙연해 져야한다고 생각한다.

 

따라서 현재 왜곡된 YOLO(다 써버려) 삶을 살고 있는 분들을 위해 생각해보자

 

굉장히 빡빡하게 잡아서 내가 당장 정직원, 비정규직을 막론하고 40살 즈음에 일자리에서 설곳이 없다고 가정해본다.

 

물가상승률이고 뭐고 다 뒤로 하고 100세까진 좀 오바인듯 하고 80세 까지 산다고 가정, 한달 생활비는 100만원으로

 

가정해도 4억8천만원이라는 돈이 필요하다. 여기서 어디가 크게 아프거나 잘못되면 훨씬 늘어날것이다.

 

언론등에서는 200~300만원까지 노후생활의 한달 생활비로 가정하는 경우가 많은데 그러면 결국 10억대의 자산을

 

축적해야 40년동안 별다른 경제활동 없이 살아갈 수 있다는 결론이 나온다. 어디까지나 단순계산으로만 해도 이런

 

결과가 도출된다.

 

물론 40대에 일자리를 잃었다고 해서 그 후의 생활을 아무런 경제활동 없이 조금 있는돈으로 먹고 산다는건

 

대다수의 사람들은 불가능 할것이다. 무슨일이라도 해야한다. 다만 일을 못하게 됐을때에 대비해서 미리 최대한

 

많은 저축을 해놓은 사람들이 점점 힘없고 설자리를 잃어가는 나이가 될수록 돈이 힘이고 내 자존심을 지킬수

 

있다고 생각한다. 예시로 든 5억원대의 돈까지는 없더라도 최대한 근접한 액수를 가질 수 있게 꾸준히 저축해야

 

노후까진 아니더라도 당장 어려운일이 생겼을때 든든한 버팀목이 되어줄 것이다. 반면 아무 준비도 없이 다 소비한

 

상태에서 실직, 자발적인 퇴사, 노후를 맞이한다면 누가 지적하지 않더라도 결국 모든것이 본인 손해로 돌아온다.

 

아파서 죽을것 같아도 먹고살아야 되기 때문에 일을 해야하고 사회구조상 나를 찾는 일터는 줄어드는데 어떻게든

 

먹고 살아야 되기 때문에 평생을 죽어라고 일해야 된다는 것이다.

 

 

게임 디아블로2 국민아이템 조던 링

 

난데없이 왠 게임 아이템이냐 싶겠지만 게임을 보면 우리 인생사와 비슷한 면이 많이 보인다. 마을에서 헐벗고 시작

 

해서 첫번째 관문을 헤쳐나가면서 어느정도 아이템을 갖췄다 싶으면 더 좋은 아이템을 구해야 하고 최종적으로는

 

최상급 아이템을 장착하고 보스(노후생활)를 물리쳐야 한다.

 

 

 

나이가 들고나서 직장생활을 하면서 어린시절 어른들에게 수도없이 들었던 '공부해라' 라는 말은 어찌보면 우리

 

사회의 씁쓸한 단면을 보여주는것 같다. 부모님 세대 기준으로 열심히 공부해서 고위관리직이나 대기업 입사로

 

경험과 좋은 인맥을 쌓으면서 부를 축적하기를 원하셨을것이다. 권하는 형태나 받아들이는 형태는 모두 

 

제각각이여도 몰라도 결국 중요한 기준점은 '돈' 이라는 것이다.

 

 

 

가끔 어떤분들은 일못하면 시골내려가서 자급자족 하던지 산에 들어가서 자연인이나 해야죠 라는 말을 하기도 한다.

 

굉장한 착각으로 시골은 도시보다 할게 없어서 자연스럽게 소비가 줄어들 뿐이지 공산품등의 물가는 오히려 도시

 

보다 비싸고 귀농,귀촌,자연인 어떤 길을 선택하든 토지매입이나 임대는 필수이며 결국 돈이 없으면 아무것도

 

할수없다. 특히나 귀농,귀촌등으로 시골생활을 하려면 건축이 가능한 토지를 매입하거나 적당한 집을 전,월세를

 

살아야 할건데 이 또한 공짜는 없다.

 

무분별하게 선동되다시피 YOLO가 왜곡되어 퍼지고 있는 현실이 안타까워서 장황하게 글을 썼는데 YOLO건 혼자

 

산다를 모두 떠나서 경제적 자립이 없이는 모든게 그마저도 별로 없는 내돈을 합법적으로 가져가려는 손길로

 

보고 아무리 조금 벌고 있어도 자기형편에 맞춰서 꾸준히 저축을 했으면 좋겠다. 우리가 저축만 열심히 하면 소비가

 

줄어서 경제가 어려워진다라는 소리가 있지만 이는 나무만 보는 소리라 생각된다. 소비가 줄면 기업이 고민하게 되고

 

이는 제품의 품질 향상으로 이어질 것이며 길게 보면 수출, 내수진작에 오히려 더 나은 환경을 제공할것이고 그렇게

 

기업이 발전하면 고용창출로 계속해서 순환되게 되는 결과로 이어질 것이다.

 

나와 동시대를 살아가는 20, 30대의 젊은이들이 각종매체에서 떠드는 소리에 휘말리지 않고 진정한 YOLO의 의미를

 

살려볼 수 있었으면 하는 바램이다. 한번사는인생이지 젊은삶만 사는 인생은 아니라는것을 말이다.

 

 

 

 

신고

3.x 버전대의 스프링 프레임워크가 나온지 얼마 안된 것 같은데 벌써 4.1버전대가 릴리즈 됐다. 그래도 3.x 버전대까지는

종종 설정도 살펴보고 이것저것 시도해봤는데 프로젝트 때문에 한참을 못보다가 최근에 시간이 되서 바뀐건 없나 살펴보니

꽤 많이 바꼇다.. 예전 2.x버전대의 설정지옥에 비하면 요즘에는 많이 편해지긴 했지만 아직도 잘 돌아가게 설정 하려면 시간

깨나 잡아먹는 일이긴 하다. 그리고 설정이 간편해진 대신 구조가 복잡해서 모르고 복붙만 해서 쓰다보면 돌아가긴 하지만

잠재적인 위험을 가지고 있는 코딩을 할 수 있는 경우도 이번에 느꼈다. 보통 스프링 메이저 버전이 업그레이드 되면 한번씩

둘러보느라 이번에 최신버전으로 설치하고 테스트 해봤는데 설정에서 꽤 막혔다. 이 글에선 막혔던 부분과 해결 방법에 대해서

정리 해보려고 한다.

1. MyBatis, iBatis

스프링 4.x버전대 부터 iBatis의 지원이 전면 중단 되었다. 이전에 iBatis를 사용하던 프로젝트를 하던 분들은 아래와 같은

설정을 사용하면서 스프링 연동 라이브러리였던 ibatis-spring-xxxx와 함께 iBatis라이브러리를 사용하셨을 건데 4.x버전

에서 사용할 수 없으므로 삭제 하고 MyBatis라이브러리와 mybatis-spring-xxx 라이브러리를 받아서 import해줘야 한다.

* Spring 3.x대의 iBatis 설정 예


    

* Spring 4.x대의 MyBatis 설정 예

    
    
        
        
        
    
            	

변경된 점만 보려는 것이기 때문에 MyBatis설정은 따로 정리해보겠다. Mapper라는 개념이 새롭게 생기긴 했는데

마이그레이션을 생각하고 설정을 해보니 Mapper를 우선은 굳이 사용하지 않고 기존 메소드에서 변경된 메소드로

변경해주고 typeAliases같은 경우 따로 관리를 하므로 따로 config하는 xml로 빼고 쿼리 사용 방식등을 손보면

그나마 마이그레이션 범위가 많이 줄어들지 않을까 싶다. 물론 프로젝트에서 잘돌아가고 있는 iBatis를 MyBatis로

변경할 확률은 거의 제로라고 보지만.. 어쨋든 iBatis개발이 중단되고 MyBatis로 바뀐지 꽤 됐는데 이번엔 아예

지원이 중단되서 좀 섭하긴 하다.

2. Ajax를 통한 ResponseBody, RequestBody JSON처리

이제까지 스프링을 사용하면서 개인적으로 박수 백번 쳐주는 기능이다. RequestBody의 경우는 일반적인 DB 쿼리시

파라미터 전송시에 그냥 좀 편하네 정도 였지만 SAP연동시 Table Structure 파라미터를 보낼때는 정말 편리하다고

생각된다. Ajax로 처리할때는 아래와 비슷하게 하면 된다.

- JSP

var param = {};
var paramList = [];

for(var i=0; i<=10; i++){
      paramList.push({testdata : i});
}
param["list"] = paramList;

$.ajax({
      url : "getTestData.do"
     ,async : false
     ,type : "POST"
     ,dataType : "json"
     ,data : JSON.stringify(param)
     ,contentType: "application/json; charset=utf-8"			
     ,success : function(data){
        alert(JSON.stringify(data));
     }
});

- JAVA(Spring Bean)

                
@Autowired
private ObjectMapper objectMapper;

@RequestMapping(value = "/getData", method = RequestMethod.POST)	
public @ResponseBody Map getTestData(@RequestBody String httpParam, Model model) throws IOException, SQLException{
         Map paramMap = new HashMap();
         paramMap = objectMapper.readValue(httpParam, new TypeReference(){});

         List list = testService.getData(paramMap); 
         Map resultMap = new HashMap();
         resultMap.put("testDataList", list);
	
          return resultMap;
}

근데 3.0, 3.1~4.X 버전대의 ObjectMapper 설정이 조금씩 달라진다. JSON 처리를 위해 Message Converter로 많이 사용되는

것중에 Jackson 라이브러리가 있는데 일단 핸들러가 변경되었다. 아래 설정을 비교해 보겠다.

- 스프링 3.0.x

                
	
	
		
			
				
				
			
		
		
	
	

 

- 스프링 3.1~4.X

                
		 
	
        
            
            
                
                    
                        text/html;charset=UTF-8
                        application/json;charset=UTF-8
                    
                
                        
            
            	            	
            
        
                                
    

- 스프링 3.1~4.X (mvc:annotation-driven 사용시)

                
     
     	
            
                
                    
                        text/html;charset=UTF-8
                        application/json;charset=UTF-8
                    
                
                        
           
            	            	
            			
     		
             

3.0버전대에서 사용되던 AnnotationMethodHandlerAdapter가 3.1버전대에서 deprecate됐다. 그래도 사용가능하긴 하다.

그리고 위 설정에서 MappingJackson2HttpMessageConverter가 있는데 이는 Jackson 라이브러리가 2.x 버전에서 생겼고

1.x 버전에서는 MappingJacksonHttpMessageConverter가 사용된다.

Spring 4.x에서는 MappingJacksonHttpMessageConverter 또한 지원하지 않기 때문에 Jackson 2.x 버전대로 변경을

권장한다. 또한 ObjectMapper의 패키지도 변경되었으므로 기존 1.x 버전대의 Jackson 라이브러리를 사용하고 있었다면

패키지 명도 변경해줘야 한다. 그리고 MappingJackson2HttpMessageConverter의 경우 JSP에서 Ajax로 보낼때

(jQuery 기준) contentType: "application/json; charset=utf-8" 와 같이 명시해주지 않으면 RequestBody에서 파싱에

실패했다는 메시지가 뜰 것이다. 설정때문에 인터넷을 찾다보면 AnnotationMethodHandlerAdapter를 사용한 설정예가

주를 이뤄서 꽤 많이 찾아 다녔던 것 같다. 설정 변경부분만 살펴봤는데 역시 변화에 유연한 스프링이긴 해도 버전이

올라가면서 코어 마이그레이션 하기는 쉽지 않은듯 하다. 여담으로 이전에 3.0 버전대에서 보통 ResponseBody를 처리

할때 AnnotationMethodHandlerAdapter를 사용하면서 스프링 Model(org.springframework.ui.Model)를 리턴하는 방식을

사용했는데 변경된 방식을 사용해보니 Resolver를 타고 RequestMapping에 선언된 View 페이지로 계속 리턴이 되서

의아했다. 여러 글을 보다보니 mvc-annotation-driven을 사용해서 설정하거나 3.2이상 버전에서 권장되는

RequestMappingHandlerAdapter를 사용했을때의 처리 방식이 달라져서 이다.  처리 요청을 받았을때 스프링에서 인스턴스

들을 특정 order로 등록하게 되는데 컨트롤러의 메소드에서 리턴되는 값이 있을경우 RequestMappingHandlerAdapter의

getDefaultReturnValueHandlers() 에서 핸들러들을 검색하다가 supportsReturnType() 메소드를 통해 먼저 발견되는 경우

true를 리턴하게 되고 스프링의 Model을 핸들링하는 ModelMethodProcessor보다 ResponseBody를 관리하는

RequestResponseBodyMethodProcessor보다 우선순위가 상위이기 때문에 먼저 처리가 된것이고 (ResponseBody의

경우 MessageConverter를 통해 결과가 처리되므로 컨트롤러 메소드에서 리턴되는 결과와는 조금 별개로 볼 수 있다.)

이미 View Resolver는 설정에 추가된 상태이기 때문에 처리가 된 것으로 보인다. 좀 복잡해서 확실하게 이해되진 않았고

확실히 맞는지는 모르겠지만 대강은 윤곽이 잡히는 것 같다. 근데 DefaultAnnotationHandlerMapping을 사용했을시

스프링 Model을 ResponseBody로 리턴해도 정상적으로 처리된것은 아직 이해가 되질 않는다. deprecate된데에 뭔가

이유가 있지 않을까? 여튼  스프링이 버전업 될때마다 느끼는 거지만 이런 이해안되는 부분을 찾다보면 Java개발 패턴도

보게 되고 여러모로 공부가 된다. 다음 프로젝트는 이상한 Wrapper클래스로 떡칠해서 스프링 기능도 제대로 못써먹는 

이놈도 저놈도 아닌  모 프레임워크(egov 아님 ;) 환경 말고 스프링 기능을 접해볼 수 있는 프로젝트로 가보고 싶다.

설정변경 부분 보다 다른말이 더 길어지긴 했는데 이제 설정 부분은 코어 버전 바뀔때가 아니어도 가끔 확인해봐야될

필요가 있다 생각됐다. 글을 보시는 분들도 참고가 되셨으면 한다.

-- Syntax Highliter 처음 써보는데 글쓰기 힘드네요. 긁어가기 쉽게 글 쓰는 분들 감사합니다. ㅠㅠ

 

신고

회사에서 조그마한 모바일 웹 개발 개선작업 프로젝트를 진행하고 있다. jQuery Mobile과 전자정부 프레임워크로 구성되어

있는데 사실 굉장히 작은 규모의 프로젝트라 전자정부 프레임워크 내부 API를 이것저것 사용할 일도 없고 사정상 지금 회사

에서 사용하는 노트북이 거의 10년전에 집에서 뒹굴던 노트북(싱글코어.. 램 2Gb)이라 이클립스가 무거우면 견디기가 어렵고

전자정부 프레임워크 개발환경은 왤케 무거운지.. validator 한번 돌아가면 노트북 팬이 터질듯이 돌아가고 손도 댈수 없을

정도여서 구조 리팩토링을 결심했다. 기존소스에서 전자정부 API를 사용하는 부분을 제거하고 Spring설정도 다시 잡고

하다보니 이틀이 꼬박.. 전자정부 프레임워크.. 참 좋긴한데 이번에는 제대로 신경질 나게 해주는 개발툴이 되버렸다.

물론 오래된 노트북 탓이 크긴하지만 ㅜ

기존소스에서 사용하던 jQuery Mobile이 훌륭한 디자인 테마와 템플릿을 제공하는건 높히 평가하지만 개인적으로 CSS가 너무

많아서 복잡한 편이고 제일 중요한건 이 프로젝트가 완전히 상업용으로 진행되는 프로젝트가 아니라서 파일럿 프로젝트보단

조금 부담있는 프로젝트이기 때문에 구조변경시에 디자인 문제가 맘에 걸렸다. 그래서 jQuery Mobile을 걷어내기로 하고

기존 HTML도 재사용 가능부분을 빼고 전부 새로 작성하기로 했다. 또 한가지 Value Object로 파라미터가 왔다갔다 하는걸

싫어해서 이 부분도 개선하려고 했지만 이것저것 다하면 눈에 보이는 결과물이 안나올 것 같아서 백엔드는 그대로 가기로 했다.

 

jQuery Mobile의 테마를 보면 위 사진과 같이 라운드 형태의 디자인이 많은데 요새 iOS도 그렇고 웹디자인 트렌드가 전반적

으로 플랫디자인으로 흘러가는 것 같아서 시간도 별로 없고 색하고 폰트만 잘 맞추면 플랫디자인 그까이꺼.. 생각하고 덤볐는데

역시..디자인하고는 거리가 먼 듯 하다. 플랫디자인 정말 어렵다 ㅜ

 

 

이렇게 플랫디자인을 한답시고 작업하다 보니 망할 콤보박스를 생각을 못했다. 셀렉트 박스를 그냥 넣으면 위와 같이 브라우저

or OS테마에 따라 제각각 다르게 보이기도 하고 우선 모양새가 영 좋지 않다. input 태그의 경우 보통 스타일링 하면

span{vertical-align:middle;padding:4px;border:2px solid #ccc;}

span input{border:0;}

과 비슷하게 input의 border를 지우고 감싸는 태그에 border를 주는것으로 스타일을 주면 되지만 불행하게도 우리가 흔히 쓰는

콤보박스 (Radio, Check, Select) 는 일반적인 방법으로는 스타일링 할수가 없다. 셀렉트 박스의 경우도 input과 마찬가지로

border를 0을 주면 전체적인 틀까지는 스타일링 할수 있지만 우측 Arrow는 어쩔수가 없다. 디자인만 보자면 셀렉트 박스를

UL, LI를 사용해서 새로 만들던지 기존 셀렉트 박스를 레이어처럼 감싸서 스타일링 하는 방법밖에 없다. 전자를 택하면

기존 로직도 다 변경해야 되니 귀찮기도 하고 시간도 오래 걸리고 두번째 방법도 만들자니 언제 만들고 있냐 싶어서

플러그인을 찾아봤다. 커스터마이징이 쉽고 일단 돌려봐서 기존 마크업 변경없이 최대한 빨리 예를 볼 수 있는걸로 찾아봤는데

jqTransform이라는걸 처음 찾았다.

 

위와 같이 예제에서는 꽤 괜찮아 보였는데 막상 적용해 보니 고치려면 손댈부분이 꽤 여러군데 있어서 다른 플러그인을 다시

찾아봤다. 대신 다른 유형의 콤보박스들도 한번에 디자인 적용이 되는걸 보니 괜찮아 보였다. 여튼.. 셀렉트 박스가 주목적이니

다시 찾아봤는데 그다지 맘에 드는 플러그인이 없었다. 그러던 중 이름도 거창한 Select Or Die라는 플러그인을 찾게됐다.

 

사용법은 간단하다 데모 페이지를 실행해보면 뭔지 알수 있다. 예를 들어 셀렉트 태그 전체를 스타일 적용한다면

$("select").selectOrDie();

라고 해주면 기본으로 아래와 같이 적용된다. 

스타일의 경우 selectordie.css를 고쳐서 사용하면 된다. 이 플러그인의 장점은 군더더기가 없어 보인다는 것이다. 보통

다른 플러그인의 경우 디자인을 덕지덕지 입혀놔서 처음 적용하고 나면 수정할 부분이 상당한데 SOD의 경우 CSS를 간단히

몇군데 수정하는 것으로 많은 부분이 해소 된다. 데모 페이지를 보면 다양한 옵션이 있어서 하나씩 적용해 보면 된다.

한가지 더 좋은점은 이벤트 발생후의 callback처리 등이 가능하다는 것이다. 다른 플러그인은 자세히 사용안해봐서 어떨지

모르겠지만 우선 대강 봤을때 이렇게 여러가지 옵션이 있는 플러그인은 없었다. 셀렉트 박스를 쓰다보면 Option태그를 동적

으로 셋팅해줄 경우가 생기는데 이 경우에는 option 태그를 append 한뒤 $("select").selectOrDie("update")와 같이

써주면 적용된다. 한가지 단점은 select 박스에 option태그가 있고 텍스트가 뭐라도 있어야 저 옵션이 적용됐었다. 글을

쓰다 보니 placeHolder옵션이 있어 어떻게 해볼수 있을것 같기도 하다. 이 플러그인은 위에서 썼듯 기존 마크업과 로직을

건드리지 않고 동적으로 select 박스의 겉을 감싸는 스타일링을 적용해주는 플러그인이다. 개인적으로 jQuery 플러그인

또한 많이 사용하는것을 좋아하진 않는 편인데 이유는 한번 피봐서 ;; 지금은 어느정도 버전이 변경되도 잘 돌아가는편인데

예전 jQuery 1.x 하위 버전때는 버전업된 jQuery에 맞춰지지 못해 오류가 나거나 오작동하는 경우가 많았다. 그래서

가능하면 플러그인에 의존하지 않으려고 했는데 이 플러그인의 경우 간만에 괜찮은걸 건진 것 같다. 아직 계속해서

개선이 이뤄지고 있는부분중에 Multiple셀렉트가 있어 보인다.

이 플러그인은 jQuery 1.8, 2.1에서 테스트 완료됐다고 한다. IE8, jQuery 2.1에서 지원하지 않는다고 하는데 어짜피

jQuery 2.x 대부터 IE8지원이 중단됐으니 상관없어 보인다. GitHub에서 받을수도 있지만 귀찮은 분들은 아래 첨부파일을

받아서 사용해 보시길..

-  다운로드

Select-or-Die-master.zip

 

 

 

신고

윈도우나 리눅스 설치시 보통 DVD나 CD ROM을 사용해서 설치를 합니다. 데스크탑이라면 보통은 설치를 해서 많이들

 

사용하실 텐데요. 노트북의 경우도 좀 덩치가 있는 것들은 DVD-RW를 내장하고 있는경우가 많지만 무게,크기 경량화를 위해

 

넷북이나 울트라씬 노트북의 경우 ODD를 제공하지 않는 경우가 대부분 입니다. 그래서 외장 ODD를 구입해서 사용하거나

 

고용량 USB에 윈도우 설치 DVD내용을 복사해서 부팅하는 방법을 선택해서 설치하는 경우가 많은데요. 이 과정들이 OS종류

 

마다 다를뿐더러 GRUB등을 MBR(Master Boot Record)에 심는 작업을 해야되는 번거로움을 거쳐야 될때가 많아서 저도

 

이제까지 ODD가 없으면 많이 시도해본적이 없습니다. 그러던 중에 구형넷북에 윈도우8을 설치하면 속도가 빠르다는 소식을

 

듣고 집에서 굴러다니는 넷북에 설치를 하려는데 제가 가지고 있는 메모리스틱은 4GB밖에 안되고 그나마도 다른 자료들이

 

들어있어서 사용할 수가 없고 SATA & IDE to USB 젠더가 있어서 ODD를 떼서 연결해서 설치할순 있지만 귀찮았습니다.

 

 

 

Serva구축 구성도

 

그래서 네트워크 설치를 시도해보게 됐는데 결론부터 말하자면 정말 편합니다. 요즘 PC들에는 대부분 네트워크 부팅 기능이

 

있기때문에 BIOS나 EFI에서 네트워크 부트만 설정하면 활용이 가능합니다. 이런행위를 가능하게 하려면 두가지 준비물이

 

필요합니다.

 

준비물 : 1. 윈도우 설치 DVD or ISO(ISO를 사용한다면 가상ODD 구축 프로그램 사용필수)

2. 윈도우 설치 DVD나 ISO를 공유할 서버용 PC

3. TFTPD를 사용하는 툴(Netboot), Serva

 

보통 인터넷에서 네트워크를 통한 윈도우 설치 방법을 보면 준비물 3에 해당하는 것중에 NetBoot를 통한 설치방법이 많이

 

발견됩니다만 이 툴은 윈도우XP를 설치할때는 간편하지만 상위버전 설치시에는 설정해줘야 할것도 많고 제대로 안될때가

 

많아서 추천하고 싶지 않습니다. 저 또한 많이 시행착오를 겪었구요. 찾던중 Serva라는 다용도 서버구축 툴이 있더군요.

 

HTTP,FTP기타등등의 서버를 간편하게 구축할수 있게 해주는 툴이었습니다. 여기서 목적은 윈도우 설치이므로 툴에 대한

 

자세한 설명은 생략하고 사용방법을 써보겠습니다. 우선 아래의 파일을 다운로드 하세요.

 

Serva_Non-Supporter_32_v2.1.4.zip

Serva_Non-Supporter_64_v2.1.4.zip

 

작업을 시작하기전 본 내용은 무선랜으로는 작업할 수 없고 유선랜으로만 가능하다는것을 알립니다. 그리고 내용은 공유기를

 

사용한 환경에서의 설정만을 다룹니다. 유동IP를 사용하는 경우도 설정 가능하지만 이 부분은 제외하겠습니다.

 

우선 위의 파일중 32나 64로 되있는것을 받습니다. 저의경우 64로 되있는 버전을 사용했는데 서버로 사용되는 PC의 OS가

 

64bit 일경우 사용하시면 됩니다.

 

1. 압축해제

  압축을 해제합니다. ex) D:\Serva , 그리고 따로 폴더를 하나 생성합니다. ex) D:\ServaRoot 이 D:\ServaRoot는 네트워크

  에서 공유될 윈도우 설치 파일들이 위치할 곳으로 매우 중요합니다. 핵심은 Serva 파일이 위치할 폴더와 윈도우 설치파일들이

  위치할 폴더를 따로 생성하는 것입니다.

 

 

 

2. Serva 환경설정

  Serva는 Donation 라이센스 프로그램입니다. 위 버전들은 Non Supporter 버전으로 기능제한이 있습니다만 아무 무리 없이

  활용가능합니다.

 

 

처음 실행하면 위와 같은 화면이 뜹니다. Settings를 눌러줍니다.

 

 

 

 

위와같이 TFTP 탭의 내용을 설정해 주는데 두번째 섹션의 'Bind TFTP to this address' 부분이 중요합니다. 보통 공유기를

 

사용한다면 192.168.0.1(공유기 IP)를 GateWay로 잡고 나머지 PC들이 공유하게 되는데 (ex)192.168.0.2 이 IP를 설정해 주는

 

것으로 시작 → 실행 → CMD 에서 IPCONFIG 명령을 통해 볼 수 있습니다. 여기서는 현재 Serva를 실행한 PC의 내부IP가

 

192.168.0.2 를 사용하는것으로 가정 하겠습니다. 매우 중요한 부분이므로 잘 따라하시면 됩니다.

 

이제 TFTP Server root Directory를 설정합니다. 앞서 Serva를 설치할때 만들었던 폴더 예와 같이 설정해 주시면 됩니다.

 

후에 이 폴더에 있는 윈도우 설치 파일을 공유하여 설치가 진행됩니다. 여기서는 D:\ServaRoot로 지정하겠습니다.

 

 

 

 

이제 DHCP서버를 설정해야 합니다. DHCP Server에 체크 하시고 Service Add-On에 체크하시고 위에서 TFTP탭을 설정했듯이

 

Bind DHCP to this address항목을 192.168.0.2로 설정합니다. 아래에 보시면 IP Poll 1st addr 항목이 있는데 이건 설치대상이

 

되는 PC가 사용할 IP주소 입니다. 공유기에서 따로 범위설정을 해서 막아놓은게 없다면 대강 아무 IP나 잡아주면 될겁니다.

 

여기선 이 IP를 192.168.0.3이라고 하겠습니다. 그리고 Pool size는 10으로 해주시고 Subnet Mask는 255.255.255.0으로 설정

 

합니다. 다 되셨으면 아래의 '확인' 버튼을 누르시면 Serva를 다시 실행하라는 메시지가 뜹니다. 다시 실행하시면 되며 재실행

 

후에 우선 Serva 프로그램을 종료해 놓습니다.

 

 

 

 

처음에 Serva를 설치할때 우리는 D:\(혹은 C:\) 에 Serva, ServaRoot라는 폴더를 두개 만들었습니다. 이번에 설정할 내용은

 

ISO나 DVD롬에 있는 윈도우 설치파일을 복사할 차례입니다. 위와같이 ex)D:\ServaRoot\WIA_WDS 폴더를 공유폴더로

 

지정합니다.

 

윈도우 7,8을 서버PC로 사용한다면 설정방법이 약간 다를수 있겠으나 결과는 동일합니다. 그리고 가장 중요한 것은 공유이름을

 

반드시 WIA_WDS_SHARE로 지정 하시기 바랍니다. 확인을 눌러서 완료하시고 D:\ServaRoot\WIA_WDS 이 폴더 내에

 

마음대로 폴더를 하나 만듭니다. ex)Windows7  그리고 그 폴더(D:\ServaRoot\WIA_WDS\Windows7) 에 윈도우 설치

 

DVD나 ISO에 있는 파일을 전부 복사 해서 넣어줍니다. 숨김파일이 있을수 있으므로 숨겨진 파일을 모두 볼수 있게 해서

 

복사 하시기 바랍니다. 네트워크 부팅시 Serva를 통해 위의 폴더의 내용을 인식하여 Windows를 설치 할 수 있습니다.

 

윈도우 XP나 2000의 경우 WIA_RIS 폴더, 리눅스의 경우 NWA_PXE 폴더에 위와 같은 작업을 수행해서 설치를

 

진행할 수 있습니다.

 

 

 

작업이 완료되면 위와 같은 구조가 될 것 입니다. 이제 Serva를 실행하고 설치대상인 PC를 네트워크 부팅 시킵니다.

 

BIOS나 EFI 구성에 따라 다르겠지만 통상 F12를 누르면 네트워크 부팅이 선택됩니다. 그러나 이미 OS가 설치된 상태라면

 

무시하고 넘어갈 수 있으므로 네트워크 장치를 부팅순서 최우선 순위로 두고 부팅하는것이 좋습니다

 

 

이제까지 작업이 제대로 됐다면 Serva에서 위와같이 Windows 부트 파일을 자동으로 알아차리고 물어봅니다. 이를 응용해서

 

위에서 설정한 ex)D:\ServaRoot\WIA_WDS\ 폴더에 여러가지 윈도우 버전에 따른 설치파일들을 위치시키면 여러개의

 

버전을 골라서 설치 할 수 있습니다.

 

 

 

위와같이 Serva의 부트로더로 부팅을 진행하게 됩니다.

 

 

 

부팅이 진행되면서 위와같이 서버PC의 사용자 ID와 비밀번호를 묻는 경우가 생깁니다. 앞에서 ServaRoot 폴더를 우리는 공유

 

폴더로 지정을 했었고 서버로 사용되는 PC의 윈도우에서 사용자 계정 암호가 걸려있으면 인증을 해야 공유폴더에 접근할 수

 

있기 때문에 묻는 것입니다. 서버 PC의 현재 로그인한 계정(ID)와 비밀번호를 입력하시면 됩니다. 어려우신 분들은

 

서버PC의 사용자 계정 암호를 삭제하고 접근해보셔도 될 것 같습니다. 아무튼 저렇게 ID와 PW를 입력한뒤 Connect를 누르고

 

인증이 완료되면 윈도우 설치 DVD를 넣고 작업한것과 동일한 셋업 화면을 보시게 됩니다. 이때부터는 설치작업을 진행하시면

 

되며 설치완료후 첫 재부팅시에 Serva를 종료하시면 됩니다.

 

※주의 : 위 이미지의 인증과정에서 ERROR : 0x37 Network resource is no longer available 이라는 메시지가

 

발견될 수 있는데 이는 네트워크 부팅 과정 이후의 설치 과정에서 설치대상PC의 Ethernet(LAN) 드라이버를 찾을 수 없어서

 

생기는 오류 입니다. 대개는 자동으로 알아 차리며 제 경우에도 Realtek 칩셋을 사용하는 Ehternet 드라이버를 자동으로

 

인식했습니다. VMWare는 이상하게 되질않네요.

 

여기까지 Serva를 이용한 네트워크 부팅을 통한 윈도우 설치 방법에 대해서 알아봤습니다. OS를 설치하려는데 DVD를 넣고

 

빼는것이 귀찮은 분들, 테스트 용도로 OS를 이것저것 설치해봐야 할 경우가 많을때 Serva는 위와같이 몇가지 설정만 거치면

 

셋업서버 용도로 사용할 수도 있는 매우 유용한 툴입니다. 저도 이번에 처음 사용해 봤는데 상당히 유용해서 집에 몇가지

 

윈도우 버전을 올려서 셋업서버로 구축해둔 상태입니다. 필요하신 분들은 참고하시기 바랍니다.

 

 

 

 

신고

요즘엔 보통 Tomcat 5~6버전 혹은 Tomcat 7 버전을 WAS로 사용하는 경우가 있을 겁니다. 제가 일하는 곳의

환경은 꽤 구형 시스템을 사용하고 있고 프로그램에서 DB 커넥션 시 JDBC로 바로 연결하고 있어 부하가 상당히 걸리는

편이라 개선을 해보고자 DBCP를 사용하여 커넥션을 얻어오도록 테스트를 해봤습니다. 요즘엔 각종 프레임워크에서

손쉽게 설정 가능 하지만 이곳 상황이 그렇지 못하기 때문에 좀 삽질을 했습니다.

먼저 DBCP를 사용하기 위해서는 자카르타 DBCP API인 Commons-dbcp-1.2.1.jar, Commons-collections-3.1.jar,

Commons-pool-1.2,jar 가 반드시 필요합니다.(꼭 버전이 맞아야 하는것은 아닙니다.)

그리고 중요한 것은 이 세개의 라이브러리가 톰캣 설치 디렉토리의 commons/lib 디렉토리에 위치해야 하며 어플리케이션이

위치하는 컨텍스트에 이 라이브러리들이 중복해서 등록됐을경우 오류를 발생 시킵니다.

1. Tomcat - Server.xml 수정

  - 톰캣이 설치된 디렉토리의 conf/server.xml을 수정합니다. 여기서 Resource의 name속성에 지정된 명칭은

  나중에 JNDI를 통해 가져올 이름으로 이후에 수정할 컨텍스트 내의 web.xml의 명칭과 동일 해야 합니다.

  혹은 전역적으로 설정하기 위해 <GlobalNamingResources> 내부에서 <Context>를 제외하고 아래 내용을 사용해도 됩니다.

<Context path="" docBase="ROOT" debug="0">
   <Resource name="jdbc/erpdb" auth="Container" type="javax.sql.DataSource"/>
   <ResourceParams name="jdbc/erpdb">
     <parameter>
          <name>username</name>
          <value>test</value>
     </parameter>
     <parameter>
          <name>password</name>
          <value>test</value>
     </parameter>
     <parameter>
          <name>driverClassName</name>
          <value>com.sybase.jdbc2.jdbc.SybDriver</value>
     </parameter>
     <parameter>
          <name>url</name>
          <value>jdbc:sybase:Tds:111.222.222.111:4444/erpdb?charset=eucksc</value>
     </parameter>
     <parameter>
          <name>removeAbandoned</name>
          <value>true</value>
     </parameter>
     <parameter>
          <name>removeAbandonedTimeout</name>
          <value>60</value>
     </parameter>
  </ResourceParams> 
 </Context>

설정후 이 부분을 보면 jdbc:sybase:Tds:111.222.222.111:4444/erpdb?charset=eucksc 뒤에 파라미터로 캐릭터셋을

지정해 줬습니다. 여기서는 DB의 캐릭터셋을 지정해 주면 됩니다. Sybase에서 혹 안되면 eucksc 대신 euc-kr 로 해보면 될 것

같습니다. 여기서 좀 삽질을 했는데 DB의 캐릭터셋이 euc-kr이라 위에서 설정시 <parameter>에 넣고 설정해 봤는데 아래와

같으 오류가 발생했습니다. JNDI를 통해서 커넥션을 가져오면서 나오는 에러였는데 위와 같이 설정해 줌으로 해결됐습니다.

org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (JZ004: DriverManager.getConnection(..., Properties)에 사용자 이름 속성이 없습니다.)

2. web.xml 수정(컨텍스트/WEB-INF/web.xml)

프로그램에서 JNDI를 통해 커넥션을 가져오기 위해 위에서 서버쪽의 server.xml에서 설정한 Resource name 과 동일하게

설정 해줍니다. 동일하지 않을경우 커넥션을 받을 수 없으므로 오류가 발생하게 됩니다.

 <resource-ref>
      <description>ERP DB</description>
      <res-ref-name>jdbc/erpdb</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
 </resource-ref>

2. 프로그램에서의 테스트

  Connection conn = null;
  InitialContext initContext = null;
  Context ctx = null;
  DataSource dataSource = null;

  try{
   initContext = new InitialContext();
   //ctx = (Context) initContext.lookup("java:comp/env");
   //dataSource = (DataSource) ctx.lookup("jdbc/erpdb");
   dataSource = (DataSource) initContext.lookup("java:comp/env/jdbc/erpdb");
  }catch(Exception e){
   System.out.println(e);
  }

  conn = dataSource.getConnection();

여기서 한가지 주의할 점은 경우에 따라 JDBC API도 톰캣 설치디렉토리의 common/lib 에만 위치 시키고 컨텍스트의

lib 디렉토리에서 중복될 경우 아래와 같은 오류가 발생할 수 있습니다.

org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.sybase.jdbc2.jdbc.SybDriver'

여기까지 설정후에 확인해보시면 아마 제대로 되지 않을까 싶습니다. 그나저나 기존 소스에 JDBC 커넥션 맺는 부분을

어떻게 효과적으로 바꿀지가 더 고민이 되는군요. 파일이 수만개가 넘고 거의 모든곳에서 커넥션을 맺는 행위를 서블릿에서

init() 호출시 하도록 되어 있습니다. 노가다의 끝판왕을 보게 될 듯 싶네요 ㅎㅎ. 어쨋든 톰캣 구 버전을 사용할때 위 내용을

참고하면 될 듯 싶습니다.

 

 

신고

보통 우리가 인터넷을 사용하면서 주소창에 흔히 볼수있는 주소 표현으로 다음과 같이..

 

http://xens.tistory.com/content/list.jsp?user=park&page=1 과 같은형태를 많이 보게 됩니다. 이를 RESTful 하게 표현하면

 

http://xens.tistory.com/content/list/user/park/page/1 과 같이 표현할 수 있게됩니다. 개념상으로는 단순히 이렇게 주소

 

표기를 위해서 뜬금없이 나타난 것은 아니고 'URI로 자원대상을 명시하고 Method로 자원에대한 행위를 정의한다'

 

를 베이스로 가지고 있는 아키텍쳐 이며 기본적으로 CRUD(Create, Read, Update, Delete)와 매치될수 있는 형태의 서비스

 

에서 쓰이고 있습니다. 저도 여기저기서 글만 봤지 최근에서야 공부겸 예제를 작성해 봤는데 글 제목에서 썼듯 URL Mapping

 

문제로 굉장한 삽질을 했습니다. 분명 시간이 지나면 잊어버릴게 뻔하니 간단히 정리를 해봅니다.

 

예제를 만들때는 Spring Framework 3.0.5와 Tomcat 7, jQuery를 사용했습니다. 이클립스에서 프로젝트 구조는 다음과

 

같다고 보시면 됩니다.

 

 

 

 

※JSP 호출 오류 해결방법

  REST형식으로 호출하면서 처음부터 진입을 가로막는것이 JSP 입니다. 처음 시작페이지가 content.jsp라고 하겠습니다.

 

 요즘에는 보통 서비스(URL)호출시 AJAX를 사용하여 콜백으로 온 데이터를 통해 결과를 출력하거나 하는 개발 방식을 많이

 

 사용하고 있는데 예를 들어서 content.jsp 에서 아래와 같이 호출한다고 가정합니다(처음 실행페이지가 content.jsp).

 

 $.ajax({

   ...

   url : "/person/parkdi"

   ...

 });

 

 그리고 스프링 컨트롤러는 다음과 같이 작성 했다고 가정합니다.

 

 @RequestMapping(value="/person/{id}", method=RequestMethod.GET)
 public ModelAndView getDataRestfulUrlPattern(@PathVariable("id") String id){...}

 

 이렇게 작성하고 바로 호출을 해보면 호출하는 content.jsp 자체가 에러가 납니다. 일단 이상현상을 보게된 후

 

 제일 먼저 봐야할것은 WAS의 web.xml 파일입니다. 우리는 Spring Framework를 사용했기 때문에 DispatcherServlet를

 

 다음과 비슷하게 매핑시켜줬을 겁니다.

 

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>ContextConfigLocation</param-name>
      <param-value>/WEB-INF/webmvc_config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

 

실행할때의 톰캣의 Console로그를 보시면 content.jsp파일도 DispatcherServlet에서 처리하려고 하는것을 볼수 있습니다.

 

그러나 위의 web.xml 설정과 같이 *.do에 대한 패턴을 매핑하도록 했기 때문에 content.jsp에 대한 매핑 URL을 찾을수가

 

없어서 에러가 발생하게 됩니다. 우선 문제에 대한 해결책은.. web.xml을 수정해야 합니다.

 

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>ContextConfigLocation</param-name>
      <param-value>/WEB-INF/webmvc_config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <servlet-mapping> 
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.gif</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.ico</url-pattern>
    <url-pattern>*.swf</url-pattern>
 </servlet-mapping>       

 

우선 문제에 대한 해결책은 위의 붉은 글씨처럼 Dispatcher서블릿의 url-pattern을 '/' 로 변경하고 deault servlet의 url-pattern

 

을 추가해줬습니다. 호출하는 페이지인 content.jsp에서는..

 

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

 $.ajax({

   ...

   url : "<c:url value='/person/parkdi' />"    //컨텍스트Path(spring_webmvc_restful_ex1/person/parkdi)

   ...

 });

 

컨트롤러에서는 위에 작성된 예와 같이 그대로 두고 호출하면 됩니다. 일단 문제는 해결됐지만 이유는 알아야 되겠죠.

 

※오류 원인

 

JSP는 WAS(톰캣 등)의 서블릿 컨테이너에서 처리를 하게 됩니다. 톰캣을 예로 보면 이클립스 프로젝트에서의 web.xml이 아닌

 

%CATALINA_HOME%(톰캣 설치 디렉토리)의 conf/web.xml을 열어보면

 

 <sevlet-mapping>

  <servlet-name>jsp</servlet-name>

  <url-pattern>*.jsp</url-pattern>

 </sevlet-mapping>

 <servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
       <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
     </init-param>
    <load-on-startup>3</load-on-startup>
  </servlet>

 

위와같이 org.apache.jasper.servlet.JspServlet 에서 jsp를 처리한다는 것을 볼 수 있습니다. JSP 인식오류 해결을 위해

 

위에서 추가했던 <servlet-name>default</servlet> 의 경우 서블릿 컨테이너의 DefaultServlet을 의미하는데 

 

url-pattern을 '/'로 정의한 경우에 디폴트 서블릿을 의미하며 이 디폴트 서블릿은 서블릿 매핑 URL에 걸리지 않는 요청을

 

처리한다고(톰캣 설정파일의 주석) 되어있습니다. 그래서 이제 DispatcherServlet의 url-pattern을 '/'로 설정함으로서

 

JSPServlet의 JSP처리와 DispatcherServlet의 처리를 분리 시켰습니다. 이렇게 분리 할경우 JSP와 스프링 컨트롤러 매핑은

 

잘 처리되지만 css, js등의 정적 컨텐츠들이 나오지 않는것을 볼수 있습니다. 이는 DispatcherServlet의 url-pattern을 '/'로

 

설정하면서 톰캣의 server.xml에 정의되있는 DefaultServlet의 url-pattern을 무시하게 되기 때문에 DefaultServlet이 작동하지

 

않게 되기 때문입니다. 따라서 호출시 정적 컨텐츠들도 DispatcherServlet이 처리하게 되니 표현되지 않게 됩니다.

 

<servlet-mapping> 
  <servlet-name>default</servlet-name>
  <url-pattern>*.js</url-pattern>
  <url-pattern>*.css</url-pattern>
  <url-pattern>*.jpg</url-pattern>
  <url-pattern>*.gif</url-pattern>
  <url-pattern>*.png</url-pattern>
  <url-pattern>*.ico</url-pattern>
  <url-pattern>*.swf</url-pattern>

</servlet-mapping> 

그래서 DefaultServlet이 처리할 url-pattern을 재정의 해준것이 윗 부분입니다. 사실 위와 같이 정의해줄경우 정적 컨텐츠에

 

대한 url-pattern을 전부다 정의해줘야 줘야하므로 지저분하기도 합니다. 그래서 스프링 3.0에서는 이 부분을

 

<mvc:default-servlet-handler /> 를 선언해줘서 해결을 한다고 합니다. 여기에는 주요 WAS의 DefaultServlet 명에

 

대한 내용을 처리하도록 되있다고 하네요. 저도 위의 DefaultServlet의 매핑을 재정의 해주기 전에 발견해서 적용해봤는데

 

xmlns:mvc="http://www.springframework.org/schema/mvc ,

xsi:schemaLocation=http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd

 

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:alwaysUseFullPath="true" />

 

위 내용이 분명히 선언되있는데도 아래와 같이

 

org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'mvc:default-servlet-handler'.

 

찾을수 없다고 계속 떠서 포기했습니다. 찾아봐도 위 내용의 XML확인이나 <mvc:annotation-driven /> 추가 여부만

 

확인하라는 답변밖에 없더군요.. 혹시 비슷한 경험 있으신 분은 답변 주시면 감사하겠습니다. 어쨋든 그냥 REST의 개념만

 

정리하고 CRUD에 해당하는 스프링의 RequestMethod(POST,GET,PUT,DELETE)를 좀 더 알아보려고 했던 것인데

 

하루종일 삽질만 한 것 같습니다;;

신고

1부에서는 HttpServletResonse의 getWriter()를 통해 마샬링한 데이터를 바로 출력하는 예제,

 

2부에서는 마샬링한 데이터를 OutputStream에 저장후 InputStream으로 변환하여 언마샬링 하는 예제를 작성해 봤습니다.

 

이번에는 마샬링 결과를 파일로 저장후 InputStreamReader를 통해 File을 읽어와서 언마샬링 하는 예제를 작성해 보겠습니다.

 

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

※Object를 마샬링 후 파일로 저장

import com.thoughtworks.xstream.XStream;

 

XStream xstream = new XStream();
final String xmlPath = "D:\\test.xml";
OutputStreamWriter osw =  

new OutputStreamWriter(new FileOutputStream(new File(xmlPath)), "utf-8");

 

UserBltnListCollections userBltnListCollections = new UserBltnListCollections();
userBltnListCollections.setUserBltnList(list);
xstream.processAnnotations(UserBltnListCollections.class);
xstream.toXML(userBltnListCollections, osw); //마샬링한 데이터를 파일로 저장

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

 - UserBltnListCollections Class

  import com.thoughtworks.xstream.annotations.XStreamAlias;
  import com.thoughtworks.xstream.annotations.XStreamImplicit;

 

  //루트 Element의 별칭을 지정해줌

  //주의점 : 당연한 결과겠지만 마샬링시 필드명에 Alias를 지정하여 필드명 변경등이

  //있을경우 언마샬링시에도 XStreamAlias를 사용해서 일치하게 설정해야 한다.
  @XStreamAlias(value="userBltnList")
  public class UserBltnListCollections {
 
  //하위 필드(Collection)에 대해 XStreamImplicit Annotation으로 지정함
  //(XAJB의 경우 @XmlElement(name="bltnList")로 지정함)

  //(UserBltnListVO클래스는 일반적인 getter/setter를 포함한 Value Object Class임)
  @XStreamAlias(value="bltnList")
  @XStreamImplicit(itemFieldName="bltnList")
  private List<UserBltnListVO> userBltnList = null;
  
  public List<UserBltnListVO> getUserBltnList() {
   if(userBltnList==null){
    userBltnList = new ArrayList<UserBltnListVO>();
  }
   return userBltnList;
  }

  public void setUserBltnList(List<UserBltnListVO> userBltnList) {
   this.userBltnList = userBltnList;
  }  
 }

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

- UserBltnListVO (ValueObject Class) 

 

import com.thoughtworks.xstream.annotations.XStreamOmitField

 

public class UserBltnListVO {
 
 //@XStreamAsAttribute로 지정시 속성으로 지정된다.
 //JAXB에서는 @XmlAttribute(name="xx")로 지정함 필드일 경우엔 마샬링시
 //JAXB와 마찬가지로 따로 지정할 필요는 없는것 같다.
 //JAXB는 마샬링시 모든 필드를 바인딩 하지만 XStream에서는
 //@XStreamOmitField가 지정된 필드는 제외시킨다. 
 
 @XStreamOmitField
 private String no = "";
 private String subject = "";
 private String writeDay = "";
 private String readNum = "";
 private String isSecret = "";
 private String name = "";

 ..

  getter/setter 생략

 ..

}

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

※Object를 마샬링후 데이터를 저장한 파일을 InputStreamReader로 읽어와서 언마샬링

InputStreamReader isr = new InputStreamReader(new FileInputStream(xmlPath),"utf-8");
UserBltnListCollections userBltnListCollectionsToUnMarshall = null;

//지정된 XStream 관련 Annotation을 처리한다.
xstream.processAnnotations(UserBltnListCollections.class);   
//마샬링된 파일을 InputStreamReader로 읽어와서 언마샬링 함
userBltnListCollectionsToUnMarshall = (UserBltnListCollections)xstream.fromXML(isr);   

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

JAXB에서 마샬링된 XML을 언마샬링 할때는 File클래스로 저장 후 바로 읽어와서 언마샬링 하도록 했으나 이번엔 방식은

 

비슷하나 Stream을 통해서 저장하는 방식으로 바꿔봤습니다. 예제에서는 작은 파일이기 때문에 어떤 방법으로 처리해도

 

IO에 무리가 되지 않으나 XML크기가 크고 빈번한 요청이 일어나는 Task라면 어떤 방식으로 처리할것인가를 고민해야 할

 

것으로 보입니다. 여기까지 XStream을 통한 마샬링, 언마샬링 방법에 대해서 알아봤습니다. 개념은 조금 다를지 몰라도

 

결과적으로 봐선 XStream을 사용하는것이 더 좋을것 같습니다. 좀더 유연하다고나 할까요? 시간이 되면 List<Map>을

 

언마샬링 하는 예제를 작성해 보려고 합니다. 이경우엔 따로 구현을 해야되는 것으로 알고 있습니다. JAXB 사용방법

 

예제를 작성할때도 잠깐 언급했지만 DomParser사용할때에 비해서 이렇게 비교가 안될정도로 편하게 쓸 수 있게 만들어준

 

개발자 분들에게 항상 존경과 감사함을 느낍니다.

신고

1부에서는 HttpServletResonse의 getWriter()를 통해 마샬링한 데이터를 바로 출력하는 예제를 작성해 봤습니다.

 

이번에는 OutputStream으로 마샬링한 데이터를 저장하고 이를 InputStream으로 변환하여 언마샬링 처리하는 방법을

 

예제로 작성해보겠습니다. JAXB를 사용한 예제도 비슷하게 HttpUrlConnection을 통해 가져온 InputStream을

 

언마샬링 하는 예제가 있으니 참고하시기 바랍니다.

 

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

※Object를 마샬링 후 OutputStream으로 저장한 후 InputStream으로 변환

  import com.thoughtworks.xstream.XStream;

 

  ByteArrayOutputStream ous = new ByteArrayOutputStream();
  ByteArrayInputStream ins = null;
  byte[] byteData= null;
  
  UserBltnListCollections userBltnListCollections = new UserBltnListCollections();
  userBltnListCollections.setUserBltnList(list); //list는 List Collection클래스임
  
  XStream xstream = new XStream();    

  //지정된 XStream 관련 Annotation을 처리한다.
  xstream.processAnnotations(UserBltnListCollections.class); 

  //UnMarshalling시 InputStream을 처리하기 위해 OutputStream으로 저장
  xstream.toXML(userBltnListCollections, ous); 
  byteData = ous.toByteArray();
  ins = new ByteArrayInputStream(byteData); //InputStream으로 변환 

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

 - UserBltnListCollections Class

  import com.thoughtworks.xstream.annotations.XStreamAlias;
  import com.thoughtworks.xstream.annotations.XStreamImplicit;

 

  //루트 Element의 별칭을 지정해줌

  //주의점 : 당연한 결과겠지만 마샬링시 필드명에 Alias를 지정하여 필드명 변경등이

  //있을경우 언마샬링시에도 XStreamAlias를 사용해서 일치하게 설정해야 한다.
  @XStreamAlias(value="userBltnList")
  public class UserBltnListCollections {
 
  //하위 필드(Collection)에 대해 XStreamImplicit Annotation으로 지정함
  //(XAJB의 경우 @XmlElement(name="bltnList")로 지정함)

  //(UserBltnListVO클래스는 일반적인 getter/setter를 포함한 Value Object Class임)
  @XStreamAlias(value="bltnList")
  @XStreamImplicit(itemFieldName="bltnList")
  private List<UserBltnListVO> userBltnList = null;
  
  public List<UserBltnListVO> getUserBltnList() {
   if(userBltnList==null){
    userBltnList = new ArrayList<UserBltnListVO>();
  }
   return userBltnList;
  }

  public void setUserBltnList(List<UserBltnListVO> userBltnList) {
   this.userBltnList = userBltnList;
  }  
 }

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

- UserBltnListVO (ValueObject Class) 

 

import com.thoughtworks.xstream.annotations.XStreamOmitField

 

public class UserBltnListVO {
 
 //@XStreamAsAttribute로 지정시 속성으로 지정된다.
 //JAXB에서는 @XmlAttribute(name="xx")로 지정함 필드일 경우엔 마샬링시
 //JAXB와 마찬가지로 따로 지정할 필요는 없는것 같다.
 //JAXB는 마샬링시 모든 필드를 바인딩 하지만 XStream에서는
 //@XStreamOmitField가 지정된 필드는 제외시킨다. 
 
 @XStreamOmitField
 private String no = "";
 private String subject = "";
 private String writeDay = "";
 private String readNum = "";
 private String isSecret = "";
 private String name = "";

 ..

  getter/setter 생략

 ..

}

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

 - InputStream을 언마샬링

 UserBltnListCollections userBltnListCollectionsToUnMarshall = null;  

 //지정된 XStream 관련 Annotation을 처리한다.
 xstream.processAnnotations(UserBltnListCollections.class);

 //InputStream을 읽어서 바인딩될 Object에 저장  
 userBltnListCollectionsToUnMarshall =  (UserBltnListCollections) xstream.fromXML(ins);

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

 

JAXB로 마샬링후 언마샬링 처리할때와 방식은 거의 비슷합니다. JAXB나 XStream둘 중 한가지라도 써보셨다면 결국

 

마샬링이나 언마샬링을 처리하는 부분의 약간의 차이, 어노테이션 이름 차이정도가 있으며 결국 맵핑을 얼마나 잘 시켜서

 

사용하느냐가 관건인 듯 싶습니다.

신고

XStream을 통한 마샬링, 언마샬링 처리에 대해서 알아보겠습니다. 최근 JAXB(Java Architecture for XML Binding)

 

API와 더불어 OXM(Object/Xml Mapping)에 많이 사용되고 있는 것 같네요. 스프링 프레임워크를 쓰시는 분들은

 

JAXB, XStream 모두 Spring OXM에 포함되어 있으므로 간단히 의존성 주입후 사용하시면 되겠습니다.

 

저는 따로 라이브러리를 받아서 사용 했습니다. 마샬링, 언마샬링 구현 방식은 JAXB와 거의 유사합니다만

 

가독성은 XStream이 더 간결했으며 대략적인 개념은 비슷한 라이브러리 들이지만 약간씩 차이가 있습니다.

 

개인적으로는 편의성만 보면 XStream이 쓰기 더 편하게 느껴집니다. 이제 간단한 예제부터 한번 보겠습니다.

 

대략적인 설명은 예제의 주석 부분을 보시면 이해 되실겁니다. 실습해보면서 나름 이해한 내용이라 잘못된 이해일수도

 

있습니다.

 

*JAXB를 통한 Marshalling, UnMarshalling 방법은 아래 링크들을 참고하시면 됩니다.

 

1부 : 파일을 통한 마샬링, 언마샬링 처리

2부 : HttpUrlConnection을 통해 얻어온 InputStream을 언마샬링

3부 : 마샬링된 데이터를 OutputStream에 저장하고 다시 InputStream으로 변환 후 언마샬링

 

 

※마샬링 후 출력

  import com.thoughtworks.xstream.XStream

 

  UserBltnListCollections userBltnListCollections = new UserBltnListCollections();
  userBltnListCollections.setUserBltnList(list);

 

  //마샬링시 xpp3 라이브러리의 종속성을 피하기 위해 DomDriver를 사용하도록 설정

  //할수 있다. 일부에선 속도저하가 심각하므로 꿈에서도 사용하지 말라는 소리가 있지만

  //그렇게 방대한 XML이 아니라면 상관 없을 듯 싶다.

  //ex) XStream xstream = new XStream(new DomDriver("utf-8"));


  XStream xstream = new XStream();

  //지정된 XStream관련 Annotation을 처리한다. 
  xstream.processAnnotations(UserBltnListCollections.class);
  xstream.toXML(userBltnListCollections, response.getWriter());   //출력

 

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

 - UserBltnListCollections Class

  import com.thoughtworks.xstream.annotations.XStreamAlias;
  import com.thoughtworks.xstream.annotations.XStreamImplicit;

 

  //루트 Element의 별칭을 지정해줌

  //주의점 : 당연한 결과겠지만 마샬링시 필드명에 Alias를 지정하여 필드명 변경등이

  //있을경우 언마샬링시에도 XStreamAlias를 사용해서 일치하게 설정해야 한다.
  @XStreamAlias(value="userBltnList")
  public class UserBltnListCollections {
 
  //하위 필드(Collection)에 대해 XStreamImplicit Annotation으로 지정함
  //(XAJB의 경우 @XmlElement(name="bltnList")로 지정함)
  @XStreamAlias(value="bltnList")
  @XStreamImplicit(itemFieldName="bltnList")
  private List<UserBltnListVO> userBltnList = null;
  
  public List<UserBltnListVO> getUserBltnList() {
   if(userBltnList==null){
    userBltnList = new ArrayList<UserBltnListVO>();
  }
   return userBltnList;
  }

  public void setUserBltnList(List<UserBltnListVO> userBltnList) {
   this.userBltnList = userBltnList;
  }  
 }

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

 

마샬링한 데이터를 곧바로 HttpServletResponse의 getWriter()를 통해 출력하는 예제를 작성해봤습니다. JAXB로 마샬링

 

할때와 결과는 동일하지만 가독성에 있어서는 XStream이 간결해 보입니다. 다음 예제에서는 OutputStream으로 저장한

 

마샬링 데이터를 InputStream으로 변환하여 출력하는 예제를 작성해 보겠습니다.

신고

1부에서는 파일을 통한 마샬링, 언마샬링 처리, 2부에서는 HttpUrlConnection을 통해 얻어온 InputStream을 언마샬링,

 

3부에서는 마샬링된 데이터를 OutputStream에 저장하고 다시 InputStream으로 변환하여 언마샬링 하는 예제를

 

작성해봤습니다. 마지막으로 가장 간단하게 HttpServletResponse 객체의 getWriter()를 통해 마샬링된 데이터를

 

바로 출력시키는 예제를 작성해 보겠습니다. 이번 예제는 마샬링 후 바로 출력하기 때문에 언마샬링을 거치지

 

않습니다.

 

-XML 원본 데이터 예시

 <userBltnList>
     <bltnList>
         <isSecret>Y</isSecret>
         <name>TEST</name>
         <no>TESTNO</no>
         <readNum>TESTREADNUM</readNum>
         <subject>Android GCM Push with Command Pattern</subject>
         <writeDay>2013-02-15 18:00:18.0</writeDay>
     </bltnList>
 </userBltnList>

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

 

※ Collection클래스를 마샬링

  import javax.xml.bind.JAXBContext;

  import javax.xml.bind.Marshaller;

 

  //list는 List Collection클래스

  UserBltnList userBltnList = new UserBltnList();
  userBltnList.setList(list);

 

  JAXBContext jaxbContext = null;
  Marshaller jaxbMarshaller = null;

  
  jaxbContext = JAXBContext.newInstance(UserBltnList.class);
  jaxbMarshaller = jaxbContext.createMarshaller();
  jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");  
  jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 
  jaxbMarshaller.marshal(userBltnList, response.getWriter()); 

 

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

- UserBltnList Class

 @XmlRootElement(name="userBltnList")
 @XmlAccessorType(XmlAccessType.FIELD)
 public class UserBltnList {
 
  //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
  //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.
  @XmlElement(name="bltnList")
  private List<UserBltnListVO> list = null;

  public List<UserBltnListVO> getList() {
   if(list==null){
    list = new ArrayList<UserBltnListVO>();
   }
   return list;
  } 
  public void setList(List<UserBltnListVO> list) {
   this.list = list;
  }
 }

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

- UserBltnListVO(Value Object Class)

public class UserBltnListVO {
 private String no = "";
 private String subject = "";
 private String writeDay = "";
 private String readNum = "";
 private String isSecret = "";
 private String name = "";

 ..

  getter/setter 생략

 ..

}

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

 

여기까지 4부에 걸쳐 JAXB(Java Architecture for XML Binding) API 사용방법에 대해 알아보았습니다. 예전에 DOMParser를

 

통해 삽질하던걸 생각해보면 정말 간편해 졌다는 생각이 듭니다. DOMParser를 사용하다 보면 XML이 복잡해지면 이놈이

 

저놈같고 저놈이 이놈같고 그랬었죠. 머리가 단순해서인지 계층구조는 저하고 좀 안맞는 것 같습니다.;; 지금 다니는 회사에선

 

사실 이런 데이터 변환 처리를 거의 할일이 없는데 알아두면 분명 유용할 듯 싶어 예제를 작성해봤습니다. 어딘가에서 유용하게

 

쓰였으면 좋겠군요. 소스는 그대로 붙혀서 사용하시면 당연히 실행이 안되실것이고 메소드 등은 환경에 맞춰 작성해서 참고

 

하여 사용하시면 될 듯 싶습니다.

신고

1부에서는 파일을 통한 마샬링, 언마샬링 처리, 2부에서는 HttpUrlConnection을 통해 얻어온 InputStream을 언마샬링

 

하는 방법에 대해 알아봤습니다. 이번엔 마샬링한 데이터를 OutputStream에 저장하고 InputStream으로 변환하여

 

언마샬링 처리 하는 예제를 작성해봤습니다. 일반적으로 이렇게 쓰이는 사례는 거의 없을듯 합니다.

 

-XML 원본 데이터 예시(UnMarshalling시 사용)

 <userBltnList>
     <bltnList>
         <isSecret>Y</isSecret>
         <name>TEST</name>
         <no>TESTNO</no>
         <readNum>TESTREADNUM</readNum>
         <subject>Android GCM Push with Command Pattern</subject>
         <writeDay>2013-02-15 18:00:18.0</writeDay>
     </bltnList>
 </userBltnList>

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

※ 마샬링한 데이터를 OutputStream으로 생성

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

 

JAXBContext jaxbContext = null;
Marshaller jaxbMarshaller = null;

 

//list는 List(Collection 클래스)

UserBltnList userBltnList = new UserBltnList();
userBltnList.setList(list);
  
ByteArrayOutputStream ous = new ByteArrayOutputStream();
ByteArrayInputStream xmlStream = null;
byte[] byteData = null;
   
jaxbContext = JAXBContext.newInstance(UserBltnList.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); 
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 
jaxbMarshaller.marshal(userBltnList, ous);  //마샬링된 데이터를 OutputStream에 저장 
byteData = ous.toByteArray(); //OutputStream을 Byte로 처리
xmlStream = new ByteArrayInputStream(byteData); //Byte를 InputStream으로 처리  

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

 

- UserBltnList Class

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

 

@XmlRootElement(name="userBltnList")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserBltnList {
 
 //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
 //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.
 @XmlElement(name="bltnList")
 private List<UserBltnListVO> list = null;

 public List<UserBltnListVO> getList() {
  if(list==null){
   list = new ArrayList<UserBltnListVO>();
  }
  return list;
 } 
 public void setList(List<UserBltnListVO> list) {
  this.list = list;
 }
}

 

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

- UserBltnListVO(Value Object Class)

public class UserBltnListVO {
 private String no = "";
 private String subject = "";
 private String writeDay = "";
 private String readNum = "";
 private String isSecret = "";
 private String name = "";

 ..

  getter/setter 생략

 ..

}

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

 

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

※ OuputStream에 저장된 마샬링된 데이터를 InputStream으로 변환하여 언마샬링 처리

UserBltnListUnMarshall userBltnListUnMarshall = null;

jaxbContext = JAXBContext.newInstance(UserBltnListUnMarshall.class);
userBltnListUnMarshall =

 (UserBltnListUnMarshall) jaxbContext.createUnmarshaller().unmarshal(xmlStream);

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

 

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

- UserBltnListUnMarshall Class

@XmlRootElement(name="userBltnList")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserBltnListUnMarshall {
 
 //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
 //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.
 @XmlElement(name="bltnList")
 private List<UserBltnListVOUnmarshall> bltnList = null;

 public List<UserBltnListVOUnmarshall> getList() {
  return bltnList;
 }
 public void setList(List<UserBltnListVOUnmarshall> bltnList) {
  this.bltnList = bltnList;
 }
}

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

 

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

 - UserBltnListVOUnmarshall(Value Object Class)

@XmlAccessorType(XmlAccessType.FIELD)
public class UserBltnListVOUnmarshall {
 
 //UserBltnListUnMarshall에서 루트 Element인 userBltnList를 가져오도록 하고
 //집합 Element인 bltnList를 컬렉션 형태로 가져오도록 했다. 이 bltnList의
 //Element를 XmlElement Annotation을 통해 가져오도록 한다.
 //(Marshalling때와 반대로 UnMarshalling때는 XmlElement나 XmlAttribute Annotation
 //으로 지정한 명을 원본 XML의 Element나 Attribute명을 읽어오게 된다.)
 
 @XmlElement(name="no")
 private String no = "";
 @XmlElement(name="subject")
 private String subject = "";
 @XmlElement(name="writeDay")
 private String writeDay = "";
 @XmlElement(name="readNum")
 private String readNum = "";
 @XmlElement(name="isSecret")
 private String isSecret = "";
 @XmlElement(name="name")
 private String name = "";

 ..

  getter/setter 생략

 ..

}

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

 

JAXB의 Unmashaller가 OutputStream을 알아서 처리해 줬다면 참 좋았겠지만 지원하지 않으므로.. 위와같은 방법으로 사용할

 

수 있음을 예제로 만들어 봤습니다. 다음 예제에서는 최종으로 HttpServletResponse객체의 getWriter()를 사용하여 마샬링된

 

데이터를 바로 출력할 수 있는 예제를 작성해 보겠습니다.

 

신고

JAXB를 이용한 File로 마샬링, 마샬링된 XML을 언마샬링 하는 방법에 대해 살펴보았습니다. 이번엔 HttpUrlConnection

 

을 통해 얻어온 InputStream으로 언마샬링 하는 예제를 써보겠습니다. 1부의 File을 통한 Marshalling, UnMarshalling

 

예제를 보시고 나면 금방 이해 되실 듯 싶습니다. 1부에서는 List형 데이터를 마샬링, 언마샬링 하는 방법을 다뤘고

 

이번에는 약간 변경하여 XML Element의 Attribute처리를 다뤄봤습니다.

 

-XML 원본 데이터 예시(UnMarshalling시 사용)

 <packing_list>
    <packing_data barcode_read_date="XXX" packing_no="XXX" reg_userid="XXX" complete_yn="Y"     matched_result="N" /> 

    <packing_data barcode_read_date="XXX" packing_no="XXX" reg_userid="XXX" complete_yn="Y"     matched_result="N" /> 
 </packing_list>

 

-------------------------------------------------------------------------------------------------※ HttpUrlConnection을 통해 XML을 InputStream으로 가져옴

 public InputStream getXmlStreamFromUrl(Map<String, Object> paramMap){
  String packingNo = (String)paramMap.get("packing_no");
  
  URL url = null;
  HttpURLConnection httpUrlConnection = null;
  InputStream ips = null;
  
  try {
   url = new URL("
http://testurl.com/test.xml");   
   URLConnection urlConnection = url.openConnection();
   httpUrlConnection = (HttpURLConnection) urlConnection;
   
   httpUrlConnection.setDoInput(true);
   httpUrlConnection.setDoOutput(true);
   httpUrlConnection.setRequestMethod("POST");
   httpUrlConnection.setUseCaches(false);
   httpUrlConnection.setDefaultUseCaches(false);
   httpUrlConnection.setRequestProperty("Content-Type", "text/xml");
   
   ips = httpUrlConnection.getInputStream();
   ips.close();
  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  return ips;
 }

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

 - HttpUrlConnection을 통해 가져온 XML(InputStream) 언마샬링

 //UnMarshalling을 위해 XML을 HttpURLConnection을 통해 가져온 InputStream을
 //가져온다.  

 import javax.xml.bind.JAXBContext;

 import javax.xml.bind.Marshaller;

 

 JAXBContext jaxbContext = null;


 InputStream xmlStream = getXmlStreamFromUrl(paramMap);   
 PackingList packingList = null;

 

  try {   
   jaxbContext = JAXBContext.newInstance(PackingList.class);
   packingList = (PackingList) jaxbContext.createUnmarshaller().unmarshal(xmlStream);  
  } catch (JAXBException e) {
   e.printStackTrace();
  }

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

- UnMarshalling을 위한 Java Class - PackingList

//UnMarshalling시에는 Marshalling과 반대로 XmlRootElement어노테이션에 지정한 이름에
//해당하는 데이터를 가져오게 된다.
@XmlRootElement(name="packing_list")
@XmlAccessorType(XmlAccessType.FIELD)
public class PackingList {
 //UnMarshalling시에는 Marshalling과 반대로 XmlElement어노테이션에 지정한 이름에 해당하는
 //데이터를 가져오게 된다.(Marshalling시에는 필드이름으로 지정하게 됨)
 
 //PackingListVO에서는 원본 XML과 같이 각 속성의 이름을 통해 값을 가져오도록 함.
 @XmlElement(name="packing_data")
 private List<PackingListVO> list = null;

 public void setPackingList(List<PackingListVO> packList){
  this.list = packList;
 }
 public List<PackingListVO> getPackingList(){
  return this.list;
 }
}

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

- PackingListVO Class - ValueObject Class(DTO)

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;

 

//XML로드시 필드명으로 읽어 들이겠다고 선언
@XmlAccessorType(XmlAccessType.FIELD)
public class PackingListVO {
 
 //XML에 정의된 속성명을 동해 값을 가져오게 된다. 
 @XmlAttribute(name="barcode_read_date")
 private String barcodeReadDate = "";
 @XmlAttribute(name="packing_no")
 private String packingNo = "";
 @XmlAttribute(name="reg_userid")
 private String regUserID = "";
 @XmlAttribute(name="complete_yn")
 private String completeYN = "";
 @XmlAttribute(name="matched_result")
 private String matchedResult = "";

 

 ..

  getter/setter 생략

 ..

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

HttpUrlConnection을 통해 언마샬링 처리는 실무에서 직접 적용 해본바로는 다른 프로그래밍 언어를 사용하는 타사와의

 

데이터 연동시에 유용하게 쓰였습니다. 사용하는 방법은 1부에서 파일로 XML마샬링 후 처리하는것과 비슷하게 크게

 

복잡한 부분은 없었습니다. 접속만 잘된다면 매우 유용하겠지요.

신고

흔히 데이터 처리 방법으로 과거부터 많이 XML을 SAX나 DOM API를 사용 하여 데이터를 가져오는 방식을 많이

 

사용 해왔습니다. 요즘은 프론트 엔드에서 AJAX를 사용한 화면전환없이 처리하는 방법을 많이 사용하면서

 

JSON(Java Script Object Notation)을 많이 사용하고 있지만 여전히 XML도 많이 사용되고 있습니다.

 

보통 스프링 프레임워크를 사용하시는 분들은 XStream을 사용하시는 것 같은데 JAXB에 비해 코드 처리가 간결해 보이긴

 

했습니다. 아직 XStream을 써보지 않은 관계로 JAXB를 통한 여러가지 XML Marshalling, UnMarshalling 처리에 대해

 

몇가지 써볼까 합니다.

 

1. JAXB(Java Architecture for XML Binding)

 

  이걸 뭐라고 읽어야 될지부터 좀 난감합니다. 전 그냥 '작스비' 라고 합니다. AJAX도 부르기 나름인것 같은데 어떤 분은

 

  에이잭스, 아약스, 아작스등..ㅎㅎ 아무튼 XML 스키마를 통해 Java 클래스 데이터를 Marshalling(직렬화), UnMarshalling

 

  (역직렬화) 작업을 좀 더 편리하게 하기 위해 만들어진 API라고 보시면 되겠습니다. Serialize와 Marshalling에는 개념의

 

  차이가 조금 있긴 합니다만 사전적 의미로 Marshalling은 '정렬하다'에 의미를 많이 두고 있는데 직렬화도 정렬된 데이터를

 

  바이트 스트림으로 전송가능하게 만든다는 의미를 두고 있으므로 차이는 있지만 비슷한 개념으로 생각하고 있습니다.

 

  어쨋든 이론은 훌륭해도 실제 써보기 전엔 의미에 불과하니 몇가지 처리방법에 대해 알아보겠습니다. 참고로 JDK 1.6버전

 

  이상에서는 별도의 라이브러리 없이 사용가능하다고 되있는데 저는 라이브러리를 다운받아서 테스트 해봤습니다.

 

1-1.  Marshalling (XML 파일 생성)

 

 처음 JAXB를 접하면서 구글링하면서 가장 많이 본 예제가 Java Object데이터를 마샬링하여 XML파일로 생성하는

 

 예제 였습니다. 실제 구현하다 보면 파일로 마샬링 하는것이 가장 심플하긴 합니다. 예를 들어보겠습니다. 자세한 소스코드는

 

생략하고 핵심 부분만 알아보겠습니다. 중간중간에 있는 주석을 보시면 이해되실 것 같습니다.

 

-XML 원본 데이터 예시(UnMarshalling시 사용)

 <userBltnList>
     <bltnList>
         <isSecret>Y</isSecret>
         <name>TEST</name>
         <no>TESTNO</no>
         <readNum>TESTREADNUM</readNum>
         <subject>Android GCM Push with Command Pattern</subject>
         <writeDay>2013-02-15 18:00:18.0</writeDay>
     </bltnList>
 </userBltnList>

 

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

 

※ 마샬링한 데이터를 파일(XML)로 생성

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

 

 JAXBContext jaxbContext = null;
 Marshaller jaxbMarshaller = null;

  UserBltnList userBltnList = new UserBltnList();
  userBltnList.setList(list);
  UserBltnListUnMarshall userBltnListUnMarshall = null;
  
  File file = new File("D:\\xmlresult.xml");
  jaxbContext = JAXBContext.newInstance(UserBltnList.class);
  jaxbMarshaller = jaxbContext.createMarshaller();  
  jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");  
  jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  jaxbMarshaller.marshal(userBltnList, file);

 

- 마샬링 대상 UserBltnList Class

 //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
 //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.

 //(XmlRootElement Annotation을 사용하여 UserBltnList클래스가 RootElement로

//  사용됨을 선언함)

 @XmlRootElement(name="userBltnList")
 @XmlAccessorType(XmlAccessType.FIELD)
 public class UserBltnList {
 
 //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
 //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.
 @XmlElement(name="bltnList")

 

 //Generic을 통해 Collection클래스에 UserBltnListVO(Value Object)가

 //저장됨을 지정함. 
 private List<UserBltnListVO> list = null;

   public List<UserBltnListVO> getList() {
     if(list==null){
       list = new ArrayList<UserBltnListVO>();
     }
     return list;
   } 
   public void setList(List<UserBltnListVO> list) {
     this.list = list;
   }
  }

 

UserBltnListVO Class - ValueObject Class(DTO)

public class UserBltnListVO {
 private String no = "";
 private String subject = "";
 private String writeDay = "";
 private String readNum = "";
 private String isSecret = "";
 private String name = "";
 ..

  getter/setter 생략

 .. 
}

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

※ 마샬링된 XML(파일)을 언마샬링

 UserBltnListUnMarshall userBltnListUnMarshall = null;

 jaxbContext = JAXBContext.newInstance(UserBltnListUnMarshall.class);
 userBltnListUnMarshall = (UserBltnListUnMarshall) jaxbContext.createUnmarshaller().unmarshal(file);

 

- 언마샬링 대상 UserBltnListUnMarshall Class

@XmlRootElement(name="userBltnList")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserBltnListUnMarshall {
 
 //Marshalling시에 Element의 이름을 정해준다. 반대로 UnMarshalling시에는
 //이 이름에 해당하는 Element속성이나 Element의 내용을 가져오게 된다.
 @XmlElement(name="bltnList")
 private List<UserBltnListVOUnmarshall> bltnList = null;

 public List<UserBltnListVOUnmarshall> getList() {
  return bltnList;
 }
 public void setList(List<UserBltnListVOUnmarshall> bltnList) {
  this.bltnList = bltnList;
 }
}

 

- 언마샬링 대상 UserBltnListUnMarshall Class의 Collection클래스 List의 VO

@XmlAccessorType(XmlAccessType.FIELD)
public class UserBltnListVOUnmarshall {
 
 //UserBltnListUnMarshall에서 루트 Element인 userBltnList를 가져오도록 하고
 //집합 Element인 bltnList를 컬렉션 형태로 가져오도록 했다. 이 bltnList의
 //Element를 XmlElement Annotation을 통해 가져오도록 한다.
 //(Marshalling때와 반대로 UnMarshalling때는 XmlElement나 XmlAttribute Annotation
 //으로 지정한 명을 원본 XML의 Element나 Attribute명을 읽어오게 된다.)
 @XmlElement(name="no")
 private String no = "";
 @XmlElement(name="subject")
 private String subject = "";
 @XmlElement(name="writeDay")
 private String writeDay = "";
 @XmlElement(name="readNum")
 private String readNum = "";
 @XmlElement(name="isSecret")
 private String isSecret = "";
 @XmlElement(name="name")
 private String name = "";

 ..

  getter/setter 생략

 ..

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

 

글로 쓰다보니 내용이 꽤 길지만 개념상으론 그렇게 복잡하진 않습니다. 다만 XML이 복잡할수록 대응되는 자바 클래스를 생성

 

해야하는것이 좀 복잡할 수 있겠습니다. 원래는 File외에 HttpURLConnection이나 InputStream, OutputStream,

 

HttpServletResponse의 getWriter()를 사용한 예도 한번에 쓰려고 했지만 복잡스러운것 같아서 4부에 걸쳐 작성하도록

 

하겠습니다.


신고

저는 4년차 개발자 입니다. 첫 직장을 3년여 있다가 떠나 제조업 전산실로 오게 된지 어느덧 반년이 훌쩍 넘었네요.

사실 전산실로 온것에는 여러가지 이유가 있었습니다만 첫번째로는 많은 개발자분들이 그렇듯 야근에 지쳤고 갑의 말도

안되는 주장과 억지에 지쳤었습니다. 물론 열의 한두개긴 했지만 좋은 갑업체도 있었습니다. 어짜피 수많은 개발자분들이

무리한 일정, 요구사항들을 많이 겪어보셨겠지만 제가 겪어본 갑 업체 담당자의 유형을 한 번 얘기해보려고 합니다.

1. 친절하고 말이 통하는 담당자

  갑업체 담당자 유형을 적겠다고 하면서 친절한 담당자가 먼저 나오다니 뭔 헛소리를 하려는 거냐 라고 생각하실 것 같습니다.

  전 직장에 있을때 SM업무를 맡고있을 당시 15~20개 정도의 업체를 담당하고 있었습니다. 이중에서 4~5개 업체는 친절하고

  괜찮은 담당자 분들이 있었습니다. 보통 SM업무를 하게되면 전화응대, 오류사항 검토 및 수정, 비교적 작은 규모의 추가개발

  을 진행하게 됩니다. 이때 보통 전화응대 차원에서 담당자들의 스타일을 접하게 되는데요. 이 친절한 담당자들이 있는 업체

  들의 공통점은 꼭 친절해서가 아니라 말이 통한다는 것이었습니다. 이분들이 개발적인 지식이 풍부해서가 아닙니다. 아무것도

  모르는 분도 분명 있었지요. 하지만 전후사정 얘기를 하고  이건 이래서 지금은 안되겠고 일단 급한건 미리 해놓겠다라고 하면

  수긍하고 일정을 조정하는 대책에 있어 말이 통했습니다.  물론 급한일의 경우가 한번에 동시에 터질경우도 있었지만

   오는말이 고우니 가는말도 곱듯 저도 반드시 해결을 해주곤 했죠. 물론 야근하거나 밤샜습니다.;; 그래도 이런업체들은

   어려운 일들을 마치고 나면 고되도 가장 보람 됐던 것 같습니다. 수고했다며 따로 식사를 대접받아본 적도 있구요. 보통 이런

   분들과 만나면 일 얘기는 한적이 없는 것 같습니다. 오히려 제가 처리가 늦어져 죄송한게 많다고 하고 온적이 많네요. 이렇게

   좋은 분들도 분명 있었습니다. 직접 업체나 기관명을 거론할 수는 없지만 제 기억속엔 항상 좋은 분들로 남아있을 것

   같습니다.

 2. 어설프게 아는 담당자(혹은 전직 개발자)

   아무것도 모르는 담당자와 함께 가장 힘든 유형이 아닐까 싶습니다. 자신만의 경험을 바탕으로 한편에서 주장하는 바를 인정

   하는법이 없습니다. 실제로 그런일이 있었는데 추가개발이 필요한 사항이 있었습니다. SM업무를 한다고 해서 개발 안하고

   유지보수만 하라는 법은 없지만 한 업체의 경우 제가 윗선에 보고 없이 몇건 임의로 개발을 해준 적이 있었습니다. 나름

   고객관리 차원에서 해준일들이었긴 했지요. 근데 그 추가개발건은 저 혼자서 하려면 밀려있는 일정과 맞물려 봤을때

   두달정도 걸리는 일이었습니다. 그래서 이래이래 얘길 해주고 지금은 안된다고 하니 갑자기 태도가 돌변해서 버럭 화를

   내더니 "이거 sql좀 몇개 넣고 jsp몇개 만들어서 하면 금방 하잖아요. 내가 해도 3일이면 하겠네." 라고 하더군요. 아마도

   모델1방식의 개발만 해보신.. 과거형 개발자의 포스가 느껴지는 말이었습니다. 여튼 그때 당시엔 그런게 이슈는 아니었고

    주 동작 모듈에도 영향을 미칠수 있는 개발이었기때문에 말했던 것이었지만 장황하게 개발론(?)에 대한 설명을 듣고 나서

    일단 전화를 끊었던것으로 기억합니다. 그 다음날에도 계속 닥달해대서 결국 저도 화가 치밀어서 "그럼 직접 만들어서 보여

    주시면 제가 앞으로 어떤 일정이나 업무지연에 있어서 아무 말 안하겠습니다." 하고 관련 쿼리와 상세설명, 소스패턴 흐름

    등을 알려줬습니다. 한 며칠 잠잠하더니 전화와서 살살 달래더군요. "에이 이거 보니까 어렵진 않겠는데 나도 해야될게

    있고.. 어쩌고 저쩌고.." 모르는척 넘어가긴 했지만 개발자의 아집 같은것이 느껴졌던 분이었습니다. 저 또한 가끔 그럴때가

    있을때가 있는데 이때의 경험을 통해 다른 개발자의 생각등을 이해해보고 항상 의식하려고 합니다. 모든 개발환경이

    동일할 순 없기때문이지요. 여튼 어설프게 아는 담당자들은 보통 자신이 아는 지식내의 생각을 꺾으려 하거나 반대입장이

    보이면 인정하지 않고 억지를 썼던 경험을 많이 겪었었습니다.

3. 여우같은 담당자

    한 업체에 여자 담당자가 있었습니다. 주제 시작에 앞서 절대 여자 개발자나 담당자들을 폄하하는것이 아니니 민감한 반응

    하지 않으셨으면 합니다. 이 담당자는 저랑 전화할때는 사근사근 얘기합니다. 그리고 알아들었다는 듯이 얘기합니다.

    전화통화를 마치고 한참있다보면 갑의 팀장이 씩씩대면서 전화가 옵니다. "당신 일을 아예 안할려고 작정을 했어?!!!"

    영문을 모르는 저는 일단 욕먹으니 화가 납니다. "아니 왜 화를 내세요? 제가 뭘 안해준다고 한적이 있습니까?"

    팀장이 말합니다. "아니 내가 누구누구한테 들어보니까 이것도 저것도 다 안된다면서요? 당신 윗사람 누굽니까 바꿔요!"

    이제 저는 여우가 지 욕안먹으려고 내가 설명한건 다 흘려버리고 "그냥 안된다고 을이 그랬져요 ㅜㅜ" 했다는 걸

    깨닫습니다. 일단 여우사냥은 둘째로 치고 팀장의 오해부터 풀어야해서 내가 이래저래 해서 안된다고 했다. 나머지

    내가 안해준게 있었느냐. 그 안된다고 한 건들도 좀더 후순위로 해서 나중에 해주겠다고 얘길했다. 오해가 있었던 것

    같다. 라고 하니 팀장이 듣고보니 제가 틱틱대면서 무조건 안된다 한게 아님을 깨닫습니다. "아 그랬었어요? 그럼

    말씀을 해주셨어야죠. 우리 같은 전산바닥에서 사는 사람들끼리 미안합니다." 저는 "네 저도 목소리 높아져서 죄송

    합니다." 하면서 훈훈히 마무리.. 그래도 팀장은 팔은 안으로 굽는다는 말대로 "누구누구 씨랑 얘기했던 내용은 지금

    통화한 선에서 정리해주셨음 좋겠네요. 저랑 얘기한걸로 푸십시다." 로 끝납니다. 저는 여우꼬리를 다 도끼로 잘라내고

    싶은 심정이지만 그냥 참습니다. 다음날.. 여우가 아무일 없었다는 듯 "이거좀 봐주시겠어요?" 어쩌고 하면서 전화가 

    옵니다. 또 왜 직급은 어디다 떼다 버렸는지 이해가 안됩니다. 대리인 저에게 언제부터 친했다는듯이 "누구씨?" 라고

    부릅니다. 대리 나부랭이지만 너랑 친구는 아니다. ㅡㅡ; 참고로 여우는 사원입니다. 회사마다 직급 정책이 다르다지만

    이 여우는 진짜 '사원' 이 었습니다. 암튼 이런 여우같은 담당자는 둘이 업무얘기할때는 조용하지만 팀장이나 그 윗선의

    후폭풍으로 영문을 모르고 박살난적이 많았던 기억이 있습니다. 이런 유형은 한번 탈이 나면 저까지 윗선의 타격을 입게

    되는 깊은 내상을 자주 남기는 유형이었습니다. 때릴수도 없고..ㅜ

 4. 귀찮아 하는 담당자 

   "무슨무슨 이름 프로그램을 우리가 쓰는데 연동좀 해주세요." 유지보수 게시판에 있던 내용입니다. 내용은 '냉무' 라고

   적혀 있습니다. 무슨 프로그램인지는 유명한 프로그램이라 알긴 하지만 뭐가 업무적으로 필요해서 뭘 연동해야되는지

   알수가 없습니다. 그래서 담당자에게 전화를 해보면 한숨 부터 쉬기 시작합니다. "하아.. 그래서 못해주신다는 거에요?"

   아니.. 뭘 원하는지 말을해요 이사람아..;; 이 사람은 아마 어릴적 공상영화를 많이 봤나봅니다. 말하지 않아도 다 알아

   서 해주는 공부기계같은 걸 원했나 봅니다. 어르고 달래서 요구사항을 도출해 냅니다. 안되면 직접 찾아도 가봅니다만

   이 사람은 일하기가 싫은건지 어쩐건지 몇시간 동안이고 잡아놓고 얘길 해댑니다. 지금 자기 일터 상황부터 시작해서

   집안 사정까지?.. 암튼 어찌어찌해서 요구사항을 듣고 옵니다. 만들다 보니 좀더 디테일한 내용이 필요해서 다시

   전화하거나 방문하면 난 몰라 개발하는 너가 알아서 해야되는거 아니냐 라는 반응입니다. 제가 요구한건 업무적인

   디테일 한 내용이지 이거 문자열 자르려면 뭐써야되요가 아닙니다. 여튼 이렇게 하다보니 점점 실랑이만 계속되고

   통화시간은 길어지고 결국 다른업체 일도 해결하다보면 야근이나 밤샘으로 때우게 됩니다. 일정이 점점 늘어지게 되고

   담당자가 자신의 윗선에서 한번 깨지고 오는날엔 큰소리가 납니다. 저는 저대로 그래서 요구사항을 분명히 하라고 하지

   않았냐 라고 말을 해보지만 소용없습니다. 다 내탓이오 하는게 오히려 속편해집니다.

5. 난 그냥 너가 싫어! (히스테리 형)

   위의 3번에 해당하는 여우담당자 처럼 이 경우에도 여자 담당자 였습니다. 몇번 일을 하다보니 이 업체.. 요구사항이

   너무 많습니다. 그래도 가능하면 이건 이래서 된다 안된다 얘기를 해줍니다. 담당자는 로봇같은 음성으로

   "그래도 해야됩니다. 아시겠지요" 하고 끊습니다. 힘없는 저는 또 밤샘과 야근으로 일을 처리 합니다. 근데 어느날

   갑자기 영업쪽 후배에게 얘길 듣습니다. "거기 갔다왔는데 담당자가 선배를 다른사람으로 바꿔달래요." "엥?

   뭔소리를 하는거냐? 내가 일을 안해주는게 아닌데?" 윗선에서도 이해를 못합니다. 유지보수 팀장이 영업과 함께

   업체를 찾아가서 담당자를 만나서 얘기해보니 비하인드 스토리가 나옵니다. 아마 제가 바쁜와중에 전화받을때

   인사를 대충하고 넘어간적이 있나봅니다. 이제까지 정황으로 봤을때 이 여자.. 내가 무슨 공주나 여왕처럼 취급해

   주길 원했나봅니다. 너무 어이가 없어서 벙찌지만 그냥 모른척 넘어가 주려고 하지만 사사건건 트집을 잡고

   안될때 마다 담당자 바꿔주세요 소리를 해댑니다. 이제 저도 한계치가 넘어서서 전화로 얘기합니다.

   "담당자 교체해드릴테니까요. 저는 이건 못하겠네요. 됏죠?" 바로 전화끊으니 나름 또 그쪽에서는 지내온 세월이

   많으니 제욕을 없는것 있는것 다 했나봅니다. 저도 여기서 지낸 시간이 있으니 당당합니다. "바꿔 줄께요. 이제

   오류 사항만 처리하겠습니다. 계약서에도 분명 그렇게 되있지요?" 하면서 전화를 끊습니다. 실제로 그 업체에선

   유지보수 통합지원 업체가 있어서 유지보수나 추가개발 관련사항을 무수하게 올리지만 전 그중에서 오류사항만

   골라서 처릴 해줍니다. 담당자는 혀를 깨물듯이 난리를 치지만 저는 거들떠 보지 않습니다. 이런 케이스는 참 뭐라고

   딱히 해결방법이 없었던 것 같습니다. 직접 본 사람도 아닌데 저정도면 직접보고 일했다면 참 뺨 한번 날라갔을지도

   모르겠다는 생각이 들었었네요.

6. 권위적인 담당자

   보통 무슨무슨 기관들에서 상하규율이 엄격해서 담당자 또한 권위적인 경우가 많았던 것 같습니다. 다른 개발자 분들도

   아마 많이 겪어보셨을 것 같습니다. 보통 이런경우 나는 갑이고 너는 을이야 이런 스멜을 풍길때가 많은데 그나마

   갑을 이면 괜찮은데.. 나는 갑이고 너는 병이야 상황에서는 정말 무슨 인도역사를 공부해서 카스트 제도를 우리나라에

   접목시키고 싶은 의지를 가지고 온사람들 처럼 인사한번에 민감해하고 내가 시키면 넌 다해야돼 ㅇㅋ? 라던지

   난 이제 퇴근할거니까 아랫것 주제에 야근해야지 라는 스타일이 있었습니다. 다행히도 많은 경우를 보진 않았지만

   이건 마지막에 퇴사 당시에 업무지원으로 나갔던 업체에서 당했던 일이었습니다. 중간과정은 길기 때문에 생략하고..

   주간회의시간에 을,병 업체들이 한데 모여있는데 갑업체 팀장 재털이가 날아다닙니다. 군대에서 가끔 본것 같은데

   다시 볼줄 몰랐습니다. 사람한테 날아가진 않지만.. 보고를 마치고 나면 이 권위적인 팀장 밑의 미니언이 졸졸 쫓아옵니다.

   "팀장님 저희 이제 좀더 상세히 보고좀 들어야 되지 않을까요? 일일회의를 하는건 어떠세요 키키키"

   "음.. 그래 너가 그러면 가서 업체들 보고 이제 다음주부터 일일회의 하자고 해. 나 먼저 들어갈께"

   미니언이 열변 토합니다. "아니 제가 팀장님 화 누그릴려고 얼마나 힘썼는데요.ㅜ 일일회의로 정리 해서 다행으로들 아세요!"

   미니언 퇴근... 결국 일일회의에 맞게 일정을 다시 조정해야 되기때문에 야근이 반복 됩니다.

   팀장은 착수보고 시에 저한테 이런말을 한적이 있었죠.. "자..이제 밤새서 개발해야지 죽어보는거야" 마치 누굴 못죽여봐서

   안달이 난 것 같이 말을 합니다. 보통 이런 스타일은 결과가 좋던 안좋던 야근을 강요하며 쓸데없이 소집을 자주 합니다.

   별로 회의할 내용도 아닌데 불러다 한두시간 쪼아대는건 취미생활이자 자신의 윗선들에게 열심히 관리하고 있군이란

   인상을 남기기 위한 것 같습니다. 이런 분들에게는 뭘 해줘도 그만큼 또 해야됩니다. 결국 벗어나기 전까진 코피를 쏟건

   밤을 새건 계속 일해야 되는 생활을 각오 해야 됩니다.

쓰다보니 엄청 길게 썼네요..;; 다 보셨을련지는 모르겠지만.. 보통 제가 겪은 갑업체 담당자의 유형이었습니다. 이외에도 더

많지만 너무 길것 같아 여기까지만 써야겠네요. 혹시 이글을 보는 분들 중 갑 업체 담당자거나 관련자시라면 제가 위에서 썼던

1번에 해당하는 담당자가 되셨으면 합니다. 개발자, 기획자, 관리자 모두 함께 협력하는 사람들입니다. 관리나 기획을 하는

갑 업체에서 개발자들에게 인간적인 대우를 한다면 십중 5~6은 분명 그만큼 열심히 해줄 겁니다. 무조건 쪼아대면 좋은 제품은

결코 나올 수 없으며 먼 훗날 자신을 되돌아 봤을때 분명 후회되는 행동이 아닐지요.

 

신고

지금 회사에는 쓰는 ERP시스템을 좀더 편하게 보기위해서 MIS비스무레한 걸 만들다가 하도 오랜만에 날짜관련된걸

만드려니 한참 해매게 된..ㅜ Calendar API는 정말 편리하긴 하지만 사용법을 잘 모르면 API 뒤지느라 좀 짜증나기도 하네요.

역시 반복학습과 경험이 중요한것 같습니다.

사용법 :

   -  첫번째 예제 : 파라미터로 기준 날짜(ex)201304, 가감할 숫자를 넘깁니다.

   -  두번째 예제 : 파라미터로 가감할 숫자를 넘깁니다.

   -  세번째 예제 : 파라미터로 기준 날짜(ex)201304를 넘깁니다.

 //파라미터의 해당하는 년월의 전달을 구한다.
 public String getBeforeYearMonthByYM(String yearMonth, int minVal){
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
  Calendar cal = Calendar.getInstance();
  int year = Integer.parseInt(yearMonth.substring(0,4));
  int month = Integer.parseInt(yearMonth.substring(4,6));

  cal.set(year, month-minVal, 0);

  String beforeYear = dateFormat.format(cal.getTime()).substring(0,4);
  String beforeMonth = dateFormat.format(cal.getTime()).substring(4,6);
  String retStr = beforeYear + beforeMonth;

  System.out.println("retStr : "  + retStr);
  return retStr;
 }

 //현재 년월의 전달을 구한다.
 //param : minVal - ex)0,1,2,.....
 public String getBeforeYearMonthByYM(int minVal){
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
  Calendar cal = Calendar.getInstance();
  cal.add(cal.MONTH, -minVal);  
 
  String beforeYear = dateFormat.format(cal.getTime()).substring(0,4);
  String beforeMonth = dateFormat.format(cal.getTime()).substring(4,6);

  String retStr = beforeYear + beforeMonth;
  System.out.println("retStr : " + retStr);
  return retStr;
 }

 //해당년월의 마지막 날짜를 구한다.
 public String getLastDayOfMonth(String yearMonth){  
  String year = yearMonth.substring(0,4);
  String month = yearMonth.substring(4,6);
  
  int _year = Integer.parseInt(year);
  int _month = Integer.parseInt(month);
  
  Calendar calendar = Calendar.getInstance();
  calendar.set(_year, (_month-1), 1); //월은 0부터 시작  
  String lastDay = String.valueOf(calendar.getActualMaximum(Calendar.DATE));
  System.out.println("lastDay of present month  : " + lastDay);
  
  return lastDay;
 }

 

 

신고

지난번에 파일 업로드 프로그레스바 구현에 대한 글을 포스팅 한적이 있었습니다. 당시에는 단일 파일 업로드만 구현 했는데

이번에는 다중 파일 업로드 구현에 대해서 써볼까 합니다. 단일 파일 업로드 기능을 구현할때도 그랬지만 웹에서 파일업로드

기능 구현은 거의 해본적이 없습니다. 보통 OCX를 통한 파일 업로드 방식만  많이 사용해서 봐서 삽질을 많이 했네요.

일단 구현전에 괜찮은 파일 업로드 모듈을 찾아 봤습니다. 아무래도 제가 직접 제작하는것도 좋지만 저보다 뛰어난 분들이

만든 소스가 더 믿음직스러우니까요. 하지만 찾아보시면 보통 JS + Flash나 Flex를 사용한 파일 업로드 모듈을 보시게

될겁니다. 일반 PC환경에서면 크게 거슬리진 않지만 상황이 많이 나아지긴 했어도 모바일 환경하에서는 Flash 플레이어를

설치하지 않는 경우가 훨씬 많고 따로 설치 해야하는것도 은근히 번거롭게 느껴졌습니다. 그리고 요즘에 출시되는

스마트폰들은 성능이 좋지만 제가 현재 쓰고 있는 갤럭시U 모델의 경우 일반 웹도 버겁게 느껴질때가 많기 때문에 Flash나

Flex를 사용한 모듈은 배제하고 찾아보기로 했습니다. 그리고 다양한 기능보다는 파일업로드에 중점을 맞춘 모듈을 찾아보게

됐는데..

첫번째로 찾게 된것은 위와 같습니다. 제가 원했던 심플한 기능이었고 가장 맘에 들었던 것은 sumit 부분은 알아서 컨트롤 할 수

있게 되있다는 것 이었습니다. 그러나 input file을 css로 디자인 하게 되면 기능이 제대로  실행되지 않는다는 글을 보게 되서

쓰지 않게 됐습니다. (확실한 정보는 아닙니다.) 또 하나 찾게 됐던것은..


위와 같은 모듈입니다. (http://blueimp.github.com/jQuery-File-Upload/) 하지만 위 모듈의 경우 모바일 환경에서 일단 UI

때문에도 그냥 사용할 수도 없고 소스는 받아봤지만 커스터마이징 해서 쓰기에 시간이 너무 오래 걸릴것 같아 포기했습니다.

기능 자체는 상당히 깔끔하고 좋습니다. 한개 더 찾은 것 중에 쓸만하다 생각된것이 바로 아래와 같습니다. UI도 수정하기

크게 까다롭지 않고 플러그인 JS자체도 분석하기 수월 했습니다만.. 특정 ID를 가진 Submit버튼을 두고 전송을 해야해서

저의 경우 jQuery.form 의 ajaxSubmit()을 사용해야 할 필요가 있어서 패스하게 됐습니다.

보통은 찾아봤던 모듈들이 위와 같이 파일 업로드 자체만의 기능을 가지고 구현되어 있어서 다른 기능들에 포함해서

구현하려면 우선 모듈을 분석하는것이 먼저라 복잡하게 느껴져서 그냥 전부 패스 하고 직접 만들어 보게 됐습니다.

처음에 구현하려고 생각했던것이 위와 같은 형태입니다. '파일' 버튼(input type 'file'이 스타일링 된것)을 누르면 업로드

리스트가 추가되고 전송 버튼을 눌러 전송하는 형태 입니다. 위와 같이 구현하기 위해서는 가장 귀찮은 것이 파일추가 input

을 스타일링 하는 것입니다. 위 버튼의 경우 input file바깥의 div에 absolute position을 줘서 초과범위는 hidden 시키는

방법으로 input file을 숨겨놓은 형태입니다. 두번째로 문제가 되는것은 업로드할 파일 리스트 입니다. 업로드를 구현하는

back end(JSP,JAVA,PHP)는 구현하기 나름이겠지만 아래의 리스트에 파일 목록만 보일뿐 전송시에 input file의 value를

건네주지 못해서 구현 하나마나가 됩니다. 그러면 구현시 input file을 동적으로 생성하고 뿌려준다음 input file의 value를

주면 되지 않을까가 바로 생각나겠습니다만 보안상 이유로 input file의 경우 readonly 입니다. 때문에 사용자가 직접 파일을

선택하여 추가한 상태가 아니라면 value를 직접 set하는 것은 불가능 합니다. 예제에서는 위와 같이 스타일링 된 파일 버튼으로

파일을 선택하면 input file이 다시 동일 위치에 생성되며 이전의 element는 숨겨지는 형태입니다. 따라서 '파일' 버튼으로

다섯번 파일을 선택했다면 눈에 보이진 않으나 input file element가 다섯개가 생성된 상태입니다. 해당 내용은 예제내의

$("div#debug").text() 를 사용해서 뿌려보시면 이해가 가실 겁니다. 전에 포스팅한 글에서 예제로 업로드를 수행하는

부분의 스프링 MVC컨트롤러를 추가해뒀으므로 이번에는 JSP만 올려놓도록 하겠습니다. 항상 그랬지만 구현이 목적으로

실사용에 문제가 될 수 있는 부분도 있습니다. 이렇게 구현할 수도 있겠다 하는 방법만 보시면 되겠습니다.


multiuprogress.jsp


구현하고 보니 웹이 많이 발전하긴 했으나 파일입출력에 있어서는 아직 지원이 미흡하다는 느낌이 많이 드네요.

여담으로 HTML5에서는 위와 같은 꼼수를 쓰지 않고도 다중 파일 업로드를 구현할 수 있어 무척 반갑네요.

<input type="file" value="" name="upload[]" multiple>

위와같이 multiple 속성 선언으로 input의 '찾아보기' 버튼을 눌렀을때의 윈도우 다이얼로그에서 여러개의 파일을 선택할 수

있습니다. 저는 파이어폭스만 테스트 해봤으나.. 최신버전의 파이어폭스, 사파리, 크롬에서 작동한다고 합니다.

신고

요즘 N,D포털을 비롯해서 거의 모든 사이트에서 Front-end 구현 기술에 빠지지 않고 포함되는 기술로 AJAX가 사용되는

것 같습니다. 사실 AJAX자체는 신기술이라 할 수는 없지만 jQuery같은 훌륭한 자바스크립트 라이브러리가 나오기 전에는

뷰페이지를 표현하는데 있어서 AJAX가 많이 사용됐던것 같진 않습니다. 물론 이전에 prototype.js와 같은 라이브러리가

있긴 했으나 지금처럼 어느곳에서나 눈에 띌정도로 많이 사용됐던 것 같진 않네요. 저 같은 경우에도 AJAX를 깊이있게

사용해본적은 그다지 없고 request중에 비동기적 기능호출이 필요한 경우에만 종종 사용해왔던 것 같습니다.

하지만 대세를 따르다 보니 이젠 AJAX, JSON사용은 필요보다는 필수가 되가고 있네요. JSON이 두루 사용되기 전에는

AJAX를 통해 XML데이터나 Text데이터를 호출해서 사용하는 방식이 주였으나 최근에는 JSON 방식이 주로 사용되는

것 같습니다. JSON표기를 어떻게 사용하는지 확인해 보겠습니다.

- JSON (JavaScript Object Notation)

 이름과 같이 자바스크립트 객체 표기법을 JSON이라고 합니다. 예를 들어 다음과 같은 데이터를 JSON객체로 표현한다고

보겠습니다.

ex) 데이터

            친구
                -학교
                   - 김친구(성격:더러움), 이친구(성격:안착함)
                -회사
                   - 오대리(성격:좋음), 이대리(성격:다혈질)

친구는 학교친구도 있고 회사 친구도 있습니다. 학교친구,회사친구들은 각각 집합으로 볼수 있으니 배열로 표현 할수 있겠죠.

JSON에서는 객체나 배열 안에 또다른 객체나 배열을 표기 할수 있습니다. 그래서 학교친구와 회사친구를 배열로 만들고

학교와, 회사라는 객체에 각각 넣어준뒤 학교친구,회사친구 배열이 들어간 학교,회사 객체를 친구 객체에 추가 한다고 생각

하시면 되겠습니다. 이를 JSON으로 다음과 같이 표현할 수 있습니다.

ex) JSON

  var friend =

              {
                  "school" : {
                      "person" : [{"name":"김친구","personallity":"더러움"},

                                      {"name":"이친구","personallity":"안착함"}]
                  },
                  "company" : {
                      "person" : [{"name":"오대리","personallity":"더러움"},{"name":"이대리","personallity":"다혈질"}]
                  }
              }
;

JSON에서는 표기시 {"key" : "value"} 형식으로 키이름과 키이름에 따른 데이터가 함께 있어야 합니다. 위에서 보시면

person 배열에는 또다른 학교,회사 친구들의 이름과, 성격이 객체로 들어가 있습니다. 여기에 또 다시 배열이나 객체가

들어가는것도 가능합니다. 아래 예를 보시죠.

ex) 데이터

            친구
                -학교
                   - 김친구(성격:더러움)(핸드폰:010-2222-3333,010-3333-4444), 이친구(성격:안착함)
                -회사
                   - 오대리(성격:좋음), 이대리(성격:다혈질)

ex) JSON

  var friend =

              {
                  "school" : {
                      "person" : [{"name":"김친구","personallity":"더러움",

                                       "mobiles":["010-2222-3333","010-3333-4444"]},

                                       {"name":"이친구","personallity":"안착함"}]
                  },
                  "company" : {
                      "person" : [{"name":"오대리","personallity":"좋음"},

                                      {"name":"이대리","personallity":"다혈질"}]
                  }
              }
;

김친구는 핸드폰이 두개가 있습니다. JSON데이터는 다시 XML로 표현하는것도 가능하며 김친구의 핸드폰과 같은 텍스트

노드들의 집합은 배열로 지정합니다. JSON데이터는 아래 예와 같이 XML이나 JavaScript로도 다시 표현 할 수 있습니다.

ex)XML

<friend>

<school>

<person>

<name>김친구</name>

<personallity>더러움</personallity>

<mobiles>

<mobile>010-2222-3333</mobile>

<mobile>010-3333-4444</mobile>

</mobiles>

</person>

<person>

<name>이친구</name>

<personallity>안착함</personallity>

</person>

</school>

<company>

<person>

<name>오대리</name>

<personallity>좋음</personallity>

</person>

<person>

<name>이대리</name>

<personallity>다혈질</personallity>

</person>

</company>

</friend>


ex) Java Script

    var friend = new Object();
    friend.school = new Object();
    friend.company= new Object();
   
    friend.school.person = new Array();
    friend.school.person[0] = new Object();
    friend.school.person[0].name="김친구";
    friend.school.person[0].personallity="더러움";
    friend.school.person[0].mobiles = new Array();
    friend.school.person[0].mobiles[0] = "010-2222-3333";
    friend.school.person[0].mobiles[1] = "010-3333-4444";
    friend.school.person[1] = new Object();
    friend.school.person[1].name="이친구";
    friend.school.person[1].personallity="안착함";
   
    friend.company.person = new Array();
    friend.company.person[0] = new Object();
    friend.company.person[0].name="오대리";
    friend.company.person[0].personallity="좋음";
    friend.company.person[1] = new Object();
    friend.company.person[1].name="이대리";
    friend.company.person[1].personallity="다혈질";   

그렇다면 JSON은 뭐하러 사용할까요? 파싱과정을 거쳐야 하는 불편함이 있을때가 있지만 일단 가독성이 좋기 때문이죠.

하지만 XML의 경우 파싱하여 사용할때 DOM Parser를 사용하는데 DOM(Document Object Model)은  XML 구조를

트리형태로 구성하여 메모리 상에 구조를 만들면서 작은양의 데이터가 오갈때는 큰 무리가 없지만 큰 데이터가 오갈때는

사용자의 PC가 느려질수 있기도 합니다. JSON의 경우 경량 데이터 교환 포맷으로 XML에 비해 가볍다라고 하긴 하지만..

체감상으로 느껴본적은 없는 것 같습니다. 아마도 요즘엔 PC사양이 대부분 듀얼코어 이상의 웹서핑등에 사용될때는

고사양이라 그런 듯 싶습니다. 어쨋든 JSON 표기법은 처음엔 어색하지만 위의 예와 같이 우선 문자열 자체가 줄어들어

코딩량이 줄어들어드는 장점이 있습니다. 하지만 단점으로는 위와 같은 예제에서는 줄을 나눠서 가독성이 좋지만 한줄로

표현하게 되면 가독성이 좋지 않고 데이터 가공까지는 그렇다 쳐도 출력시킬때는 꽤 헷갈릴때가 많습니다.

실제로 개발을 하다보면 위와같이 데이터를 표현하는 경우는 드물고 한줄로 쫙 뿌리는 경우가 많습니다.

ex)JSON 데이터를 한줄로 표현

  var friend = {"school" : {"person" : [{"name":"김친구","personallity":"더러움","mobiles":                     ["010-2222-3333","010-3333-4444"]},{"name":"이친구","personallity":"안착함"}]},
                     "company" : {"person" : [{"name":"오대리","personallity":"좋음"},{"name":"이대                      리","personallity":"다혈질"}]}};

ex)XML 데이터를 한줄로 표현

<friend><school><person><name>김친구</name><personallity>더러움</personallity>

<mobiles><mobile>010-2222-3333</mobile><mobile>010-3333-4444</mobile></mobiles>

</person><person><name>이친구</name><personallity>안착함</personallity></person>

</school><company><person><name>오대리</name><personallity>좋음</personallity>

</person><person><name>이대리</name><personallity>다혈질</personallity></person>

</company></friend>

보통 이와 같은 형태로 DB에 데이터를 일정형식으로 그대로 넣었다가 가져오는 경우도 있고 DB의 데이터를 JSON이나

XML형태로 가공하여 사용하기도 합니다. 그냥 얼핏 봐도 JSON 표기법의 경우 한줄로 표현할 경우 어디까지가 객체의 끝인지

배열의 끝인지를 알수가 없습니다. 물론 XML도 한줄로 표현하면 보기 힘든점은 마찬가지긴 합니다만 그래도 JSON표기에

비해서는 좀더 쉽게 구분이 갑니다. 예제에서야 간단한 데이터로 표현했지만 데이터량이 많아질수록 가독성이 떨어지는

것은 어쩔 수 없는 부분인것 같습니다. 그래도 우선 저의 경우는 XML의 경우 DOM Parser를 거쳐 파싱해야 하는 과정이

복잡하고 버겁게 느껴질때가 많아서 JSON사용이 주류가 되는것에 대해 환영하는 입장입니다. 그렇다고 XML이 JSON보다

나쁘다라고 할수는 없고 개발할때 느끼지만 항상 상황에 적당한 기술 사용이 더 중요한 것 같습니다.

※예제는 실제로 실행해 보지 않아서 확실히 맞는지는 모르겠습니다. JSON표기법을 어떻게 사용하는지 객체,배열의 매핑

방법이 이렇다 하는것만 알아보시고 실제로 브라우저로 테스트 해보시기 바랍니다.

신고

지난 10월 26일 윈도우8이 정식출시 되었습니다. 물론 관심 많으신 분들은 출시이전 컨슈머 프리뷰 버전을 통해서도

경험 해보셨을것이고 그 이전의 빌드를 설치해본 경험도 있으실 것 같습니다. 저 또한 윈도우8 개발 초기 당시인 2011년

중순께에 설치를 해봤고 시작화면의 어색함에 바로 삭제해버린 후 이번엔 정식 출시된 ProK버전을 가지고 설치를 해봤습니다.

설치과정이야 윈도우 XP이후로 거의 손댈 필요없이 간소해졌고 비슷하므로 굳이 소개하진 않겠습니다. 

일단 설치를 마친후 몇가지 자동으로 셋팅을 마친후 시작 화면이 뜹니다. 

처음에는 윈도우8이 제공하는 기본앱이 시작화면을 차지하고 있습니다. 위 화면에서는 기본 앱 몇가지를 제외하고 모두 삭제

후 기존에 사용하던 프로그램들을 설치하여 주로 사용하는 프로그램의 아이콘만 '시작화면에 고정 하기' 기능으로 고정 시킨

상태입니다. 처음에는 시작화면이 매우 어색했습니다. 또한 좌측 중간에 보이는 '데스크톱' 패널이 없다면 항상보던 바탕화면

또한 볼 수 조차 없으니 조금은 당황스러웠고 조작감도 매우 불편하게 느껴졌습니다. 하지만 설치후 어느정도 적응하고 나니

오히려 반응이 빨라서 이 화면이 맘에 들긴 하더군요. 또한 주로 쓰는 프로그램의 경우 작업표시줄에 고정 시켜서 사용하면

되므로 적응하고 나서는 꽤 유용하다고 생각 들었습니다. 다만 배치해놓은 프로그램을 어느정도 종류별로 배치 시켜서 사용

하고 싶으실텐데 가운데 화면상에서 배치하다보면 하나를 맞춰놓으면 하나가 어긋나는.. 퍼즐맞추기 하듯이 배치가 힘들어

오히려 산만하고 짜증나는 시작 메뉴로 느껴질 수도 있겠습니다.

이 시작 화면에서 빈 공간에 오른쪽 마우스 버튼을 클릭하면 우측 하단에 '앱 모두 보기' 메뉴가 나옵니다. 기존의 윈도우로

보자면 시작 버튼 → 프로그램 메뉴 내의 모든 프로그램을 보는것으로 생각하시면 됩니다.

많은 부분을 가려놓았습니다만.. 설치된 프로그램들의 목록은 다음과 같이 A~Z, 가~하 순으로 표시 됩니다. 이 메뉴는 사실

처음 보자마자 정말 부적절 하다고 생각했습니다. 바로 아래 사진과 같이 키보드만 치면 바로 검색창이 뜨기때문에 원하는

프로그램을 검색해서 사용할 수도 있지만 모든 사용자들이 항상 프로그램명을 기억하고 검색해서 사용하진 않습니다.

혹은 알고 있다고 하더라도 기존에 사용하던 패턴이라면 시작메뉴를 눌러서 프로그램을 찾아 실행하는것이 일반화 되있겠죠.

프로그램이 저렇게 난해하게 펼쳐져 있기때문에 저의 경우에는 저 메뉴를 일체 사용하지 않으며 프로그램 제거 시에만

프로그램 설치시 제공되는 언인스톨 아이콘으로 삭제하기 위해서 사용합니다.

시작화면의 '데스크톱' 패널을 누르면 위와같이 익숙한 바탕화면이 나타납니다. XP사용중 윈도우8로 업그레이드 하셨다면

이 화면도 생소 하시겠지만 윈도우7을 사용하신 분들이라면 익숙한 화면 입니다. 이제야 기존에 하던 탐색기를 통한 파일

탐색등을 할수 있겠네요. 위에서도 언급했지만 이 부분의 경우 패치가 필요하지 않나 싶습니다. '데스크톱' 패널이 한번눌린

후에는 화면의 좌측 하단에 마우스를 올리면 시작화면↔바탕화면을 전환시킬수 있으나 '데스크톱' 패널이 한번도 눌리지

않은 상태에서는 바탕화면을 볼 수 조차 없습니다. 물론 어떤 단축키 정도는 있겠지요. 아직 단축키가 있는지는 찾아보지

않았습니다.

위와 같이 우측화면의 최상단 부분에 마우스 오버시 메뉴가 하나 뜹니다. 보통 제어판에서 이뤄지던 작업의 일부분을 이곳을

통해 좀더 간단하게 할 수 있게 구성 되어 있고 직접 제어판을 호출할 수도 있습니다. 또한 우측 하단의 'PC 설정 변경' 메뉴를

통해 시작화면 앱에 관련된 설정을 할 수도 있습니다만 스마트폰에서 앱을 다운받아 설치하듯 스토어를 통해 설치된

프로그램과 윈도우8의 Metro 앱 프로그래밍 방식에 맞춰 만들어진 프로그램 외의 이전에 데스크탑에서 사용하던 프로그램의

경우는 설정 될수 있는 부분이 전혀 없습니다. 따라서 일반 프로그램들의 경우 앱 설정등은 무시하시고 그냥 사용하시면

되겠습니다. 앞으로 윈도우8의 Metro UI를 지원하는 프로그램들이 얼마나 나올지는 모르겠지만 개발자들의 입장에서는 상당히

애매한 개발 범위가 되지 않을까 싶습니다. 세번째 화면의 경우 듀얼 모니터 확장이나 디지털/아날로그 아웃풋 복제 기능등

기존의 윈도우7에서 화면해상도 메뉴에서 다중디스플레이 설정을 하던것을 메뉴로 지원해서 상당히 편리하게 느껴집니다.

PC설정 변경 화면입니다. 설정 부분을 보기에 앞서 상당히 불만인것이.. 처음에 윈도우를 설치하면 사용자 계정을 MS Hotmail

계정을 무조건 사용하도록 유도합니다. 물론 메일 계정은 아무렇게나 입력해도 상관 없습니다만 이 경우 입력한 사용자 이름

으로 사용자 폴더 (일반적으로 C:\Users\xxx) 가 생성 됩니다. 일반적으로 사용시에는 한글 폴더명이 들어가는것이

별 상관 없습니다만 가끔 이 사용자 폴더가 한글이어서 프로그램 설치시나 설정시에 문제가 생길 경우가 있어서 꺼려집니다.

처음 설치후에 윈도우8 기본 앱이나 설정(메일, 일정관리등, 동기화)이 Hotmail계정과 연동되어 사용될 수 있어서 유도하는지는

모르겠으나 선택권이 없게 만들어 버린것 같아서 불만스럽긴 하네요. 설치 후에는 로컬계정으로 변경할수 있으며 이 경우

앱 동기화 기능등은 사용할 수 없게 됩니다만.. 솔직히 윈도우 기본 어플은 아주 먼 옛날 윈도우3.1 버전 부터 지금까지 그다지

애용해본 기억이 없어서 오히려 저의 경우엔 방해가 됐습니다. 기타 설정 내용등은 별 특별한 점은 없고 PC에 익숙하지 않은

사용자가 사용하기에 괜찮은 기능으로 두번째 사진의 'PC복구','윈도우 다시 설치' 등이 큰 도움이 될 수 있겠습니다.

윈도우 비스타때의 악몽을 되살리고 싶었는지 이번에도 UAC(사용자 계정 컨트롤)기능이 기본적으로 켜져있습니다.

항상 이유야 파일 보안등을 위해서 라고 하는것이 MS의 입장이긴 합니다만 시스템적인 작업 (ex)펌웨어 업그레이드 등

이 이뤄질경우 UAC때문에 PC가 맛이 가버리는등의 위험성도 있고 설사 UAC를 끈다고 해도 완벽한 Super Administrator

권한이 있지 않아서 제일 싫어하는 부분중에 하나입니다. 수동으로 레지스트리를 변경하여 UAC를 해제하고 최고 관리자

권한을 획득할수는 있으나 이경우 윈도우 기본 Metro 앱은 사용할 수가 없습니다. 기본 앱이 크게 필요치 않다면 해제하고

쓰시는것이 좋을듯 합니다. (※주의 : 기존의 윈도우 비스타, 윈도우7의 경우의 UAC해제 방법을 그대로 윈도우8에서 사용

하시면 문제가 발생합니다.) 

윈도우8 초기 개발 발표시에 Active X사용 불가 여부로 이슈가 됐던 IE10입니다. 메트로 앱, 데스크톱용 IE로 구분되어 있으며

매트로 앱 IE 10의 경우 ActiveX가 실행되지 않습니다. 데스크톱용 IE의 경우 기존에 사용하던 ActiveX 실행이 가능하므로

염려하실 필요는 없습니다. 사진에 보시는대로 매트로 앱 IE10의 경우 IE가 실행된 상태에서 화면 가장상단에 마우스 오버시

손모양 포인터가 나오면 끌어서 바탕화면에 위치시켜 사용 할 수 있습니다. 이 기능은 큰 디스플레이를 쓰시는 분들에게

괜찮은 기능으로 생각됩니다.

윈도우8 설치후 셋팅해서 사용해 본후 간략하게 여기까지 둘러봤습니다. 일단 제 결론은 "윈도우7 사용자가 굳이 윈도우8로

돈들여서 옮겨올 필요는 없다." 입니다. 인터페이스나 UI가 바뀐점, 약간의 부가적인 기능이 추가된것 때문에 10만원 가량의

돈을 지불하고 사기에는 아까운 OS라고 생각됩니다. MS에서 기존의 타블렛 윈도우의 경쟁력을 높히기 위해 윈도우8을

제작 한것으로 보이나 데스크톱용으로 윈도우8을 쓰기엔 어색한 점이 많습니다. 첫번째로 사용자가 어떤 작업을 하는데 있어서

동선이 매우 깁니다. 터치스크린을 사용하는 것이 아닌이상 마우스를 한번이라도 더 움직여야 하고 위에서도 언급했지만

테스크톱 패널을 누르기 전엔 데스크톱 시작 화면으로 전환 조차 할 수 없습니다. 또한 난해한 전체 앱 배치화면은 아무리

검색이 가능하다고 한들 이전에 사용하던 버전의 윈도우 시작 메뉴에서 정렬된 상태의 프로그램 그룹을 선택할수 있었던것에

비해 매우 불편합니다. 그리고 저의 경우 프로그램 그룹을 따로 생성해서 사용하는 경우가 종종 있는데 이것또한 아이콘들이

저장되있는 폴더를 일일히 찾아가서 만들어야 하는 불편함이 있으며 편리하게 사용하라고 있는 단축키가 이제는 어느정도

숙지해야 하는 요소가 되버린 것 같습니다. 예를 들어 이전 윈도우에서 시작→실행 으로 명령어를 직접 넣어 실행 하던 메뉴의

단축키는 윈도우키+R 입니다. 이전에는 선택권이 있었지만 이제는 실행 메뉴도 단축키를 사용하지 않고는 찾아볼수가

없습니다. 물론 다른 기능들의 경우도 편하게 사용하기 위해선 기본 단축키 정도는 숙지해야 합니다.

ARM용으로 출시된 Windows RT의 경우 성공여부는 지켜봐야 겠지만 적어도 데스크톱 OS로서는 업그레이드 할만큼의

메리트는 그다지 없다고 볼 수 있겠습니다. 보통 MS에서는 기존 윈도우 제품군 출시후 어느정도 시간을 두고 새OS를

출시했는데 윈도우8이 이렇게 빨리 출시되는것을 보니 이제는 태블릿 시장에서의 애플, 구글을 간과할 수가 없나 보네요.

어쨋든 윈도우8을 몇일 사용해 보니 파워유저들이 사용하기엔 전작에 비해 크게 좋은점을 바라기는 힘들것 같고

(몇가지 프로그램들은 호환성 문제도 존재 합니다.)  라이트 유저의 경우에는 오히려 직관적인 인터페이스로 편리 할 수도

있겠습니다.

신고

파일 업로드 라이브러리 선택(COS,Apache Commons Upload)에 대해 포스팅 했던 글에 이어 이번에는 파일 업로드

구현 예제에 대해 올려보겠습니다. 파일 업로드시에 보통 작은 데이터의 경우 손쉽게 jQuery의 ajaxStart, ajaxStop function을

통해 AJAX 요청 시작시, 완료시에 대한 이벤트를 처리 할 수 있으므로 ajaxStart()시에 GIF이미지 등을 화면에 띄워줘서

처리 상태를 사용자에게 알릴 수 있습니다. 보통 큰 용량의 파일이 업로드 될때는 JSP등의 페이지로 처리 하기 보다는 별도의

업로드 모듈을 사용하는것이 낫겠지만 5MB~10MB내의 파일이 오갈때에는 굳이 그럴 필요없이 웹으로만 구현하는것도

충분하다고 생각합니다. PC의 경우 굳이 프로그레스바를 보여주는것 보다는 간단하게 위와 같은 방법으로 처리하는것이

나을지 모르겠으나 스마트폰의 전송속도는 3G망이나 4G나 일반 유선통신 속도와 비교할것이 못되기도 하고 잘 안터지는

지역에서는 스피너 이미지로 프로그레스바를 보이는것 만으로는 사용자가 작업 완료 여부를 알 수 없기 때문에 만들어

보게 됐습니다.


progressbarex.zip

※압축첨부한 예제 파일들의 경우 거의 그대로 적용하시면 실행 되실겁니다. JAVA파일의 경우 package를 환경에 맞게

추가 하시면 됩니다. 구현이 목적으로 성능등은 고민한 흔적같은 것 없습니다. 그냥 예제로 봐주세요.ㅋ

그리고 Spring MVC컨트롤러에 대한 이해가 없으신 분들은 *.do로 호출되는 기능들만 따로 떼서 구현해보셔도 되겠습니다.

예전에 JAVA의 Swing을 사용해서 Thread 구현하여 만들어본 프로그레스바 외에는 이제까지 웹으로 프로그레스바를

구현해본적이 없어서 바로 감이 오지는 않았고 사실 구현 전에는 적당한 예제 소스를 많이 검색해 봤습니다. 근데 괜찮다 싶은

모듈이어도 적용하기가 어려운 경우가 많고 모바일 웹 화면에서 사용하려면 UI도 수정해야 되는 경우가 많았고 대부분의

예제는 JSP → <form> action으로 서블릿 호출 → 서블릿에서 Apache Commons Upload를 통해

전송상태를 PrintWriter로 출력 하는 것이 대부분이었습니다. 또한 파일 업로드 자체는 XMLHttpRequest에서 지원하지

않아 jQuery.form 라이브러리를 사용한 ajaxForm()등을 사용하여 업로드 하는 방식을 많이 보게 됐는데 잘 적용되지

않았습니다. 저의 경우 Spring Web MVC를 사용하여 구현하고 있었는데 이 구조 특성상 request가 완료된후 페이지로

포워딩 하므로 중간에 프로그레스 상태를 listen하여 화면에 출력될때 어떻게 하면 좋을까 고민이 됐습니다. 한참 고민중에

세션이 생각이 나더군요. 제가 만든 예제의 대략적인 프로그레스바 출력 흐름은 다음과 같습니다.

progressbardemo.jsp 에서 파일 선택후 전송버튼 클릭시 AJAX로 fileupload.do

(스프링 컨트롤러(Controller.java)에 매핑된 주소) 호출 ,

호출시에 jQuery.ajaxStart()에서 uploadstatus.do를 호출하여 fileupload.do가 실행중의 프로그레스 상태를

얻어오기 위해 주기적으로 UploadProgressListener를 호출함

fileupload.do에서 Apache Commons Upload를 사용하여 업로드 처리 중

UploadProgressListener에 세션을 셋팅 →

UploadProgressListener의 update()메소드에서 업로드 진행상태를

세션에 셋팅해서 upstatus.jsp로 포워딩함 →

초기에 progressbardemo.jsp에서 ajaxStart()에서 uploadstatus.do가 호출될때 세션에 셋팅된 파일의 전송정보

(읽은 바이트수, 전체크기, 전송속도) 등을 JSON으로 출력함 →

전송완료후 clearInterval로 setInterval로 확인하던 업로드 상태를 중지시킴

jQuery가 AJAX요청시에 지원하는 표현 간단하면서 강력한 기능들이 많은 도움이 됐네요. 이걸 이전 방식으로 XMLHttpRequest

를 일일히 얻어와서 구현하는 형태로 만들었다면 소스량도 많아지고 많이 복잡했겠지요. 예제 내용은 사실 효율적으로 보면

매우 좋지 않습니다. 결과만 보기위해서 만들긴 했습니다만 일단 실 서비스에 적용해서 사용한다면 파일 업로드가 진행중일때

upstatus.jsp가 JSON String을 얻어오기 위해 계속해서 호출이 되므로 여러 사용자가 사용시 서버에 많은 부하가 걸릴 수

있습니다. 이 방법보다는 MVC컨트롤러가 호출될때 세션에 셋팅된 파일 전송 정보 JSON Object를 얻을수 있는 방법을

전에 어느분의 블로그에서 본것 같은데 기억이 가물하네요. 찾아서 한번 적용해 봐야겠습니다.

또한 Spring Framework 자체에서 Apache Commons Upload를 Injection해서 사용할 수 있는 방법도 봤습니다만

일단은 익숙해진 방법으로 구현했습니다. 좀 더 효율적인 방법으로 수정해서 사용하실 분은 공유 해주셨으면 좋겠네요.

보통 파일 업로드 모듈이 Flash나 Flex를 사용한 환경이 많아 모바일 환경에서 사용자체는 가능하나 잘 맞지 않고 현재 제가

만든 게시판의 경우 하이브리드 앱 형태로 웹뷰를 사용한 앱위에서 웹페이지를 호출하는 형태인데 자료를 찾다보니 이 또한

안드로이드 앱 자체에서 업로드 진행상태 구현 방법에 대해서만 많이 나와 있어서 구현해 보게 됐습니다. 다운로드의 경우

에도 구현할수는 있겠지만 보통 다운로드의 경우 브라우저에서 진행상태를 보여주는 경우가 많으므로 생략했습니다.

좀 더 효율적인 방법 제시해주시는 분들은 언제나 환영입니다^^


신고

얼마전 공부겸 해서 Spring Framework 2.5, jQuery, iBatis를 사용한 모바일 전용 게시판을 제작 했습니다.

처음에는 그냥 기본적인 기능만 만들고 다른것 좀 공부 하려고 했는데 만들다 보니 재미가 붙어서 파일 업로드 기능도

추가 하게 됐습니다. 다중 업로드도 구현하려고 했지만 귀찮아서 단일 업로드만 구현하게 됐네요.

여튼.. 파일 업로드를 워낙 오랜만에 구현하려다 보니 기억나는 것도 없고 뭘 사용할까 고민이 됐습니다. 그래서 검색하다보니

가장 많이 사용하는듯 한 라이브러리로 cos.jar가 눈에 띄었습니다.

(com.oreilly.servlet의 약자이죠. 오라일리 책 시리즈로 으로 유명한..)

하나는 Apache Commons Upload 였는데 둘다 구현하기가 까다로운 편은 아니나 cos.jar가 구현방식 자체는 상당히

간편해 보였습니다. 그래서 cos.jar를 사용해서 파일 업로드를 구현하기로 맘 먹게 됐습니다. 하지만 cos.jar를

사용해서 구현 후에 다시 Apache Commons Upload를 사용하게 되었는데 구현시의 다른 부가적인 조작 편의성에 대해

의견을 써볼까 합니다.

※구현 방법등에 대해선 다루지 않겠습니다. 다른 훌륭한 분들이 올리신 구현 소스들은 조금만 검색해보셔도 많으니까요.

   또한 사용예로 제가 중간에 넣은 소스들은 단지 비교를 위한 예제 이므로 동작하지 않습니다. 참고만 하세요.


기능구현은 위와 같이 간단히 하기로 했습니다. 파일 선택 부분은 기능 구현 완료시 게시판의 UI에 추가 하기 위해서 스타일링

했네요. cos.jar 를 사용하던 Apache Commons Upload를 사용하던 업로드 시에는 BInary 코드로 보내게 되므로

<form>을 통해 전송시 enctype="multipart/form-data", method="post" 를 추가해서 보내게 되어 있습니다.

이렇게 전송된 데이터는 cos.jar를 사용할 경우 HttpServletRequest자체를 넘겨받아 내부에서 업로드를 몽땅 처리 하도록 되어

있습니다.

사용예)

MultipartRequest multi = null; 
  try{
    multi = new MultipartRequest(request, uploadFilePath, uploadFileSizeLimit, "utf-8",

new DefaultFileRenamePolicy());

   }catch(Exception e){}

위와 같이 MultipartRequest 의 객체가 생성되면서 넘겨받은 HttpServletRequest, 업로드할 경로, 업로드할 사이즈 제한

값등을 넘겨주면 바로 파일이 업로드 됩니다. 간단히 기능 구현시 이와 같이 사용법이 너무 심플하고 편하기 때문에 많이

이용되고 있는것 같습니다. 그럼 이번에는 Apache Commons Upload의 구현 방식을 보겠습니다.

사용예)

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

if( isMultipart ) {
    File temporaryDir = new File(uploadFilePath);
    DiskFileItemFactory factory = new DiskFileItemFactory();
    factory.setSizeThreshold(factoryThresholdLimit);
    factory.setRepository(temporaryDir);
    ServletFileUpload upload = new ServletFileUpload(factory);

    List items = null;
    try {
        items = upload.parseRequest(request);
    } catch (FileUploadException fe){
        System.out.println(fe);
    }
    if(items!=null){
        Iterator iter = items.iterator();
        while (iter.hasNext()) {
            FileItem fileItem = (FileItem) iter.next();
            if (fileItem.isFormField()){
                //파일을 제외한 나머지 파라미터 처리
            }else{
                //파일 업로드 처리
                try{
                    File uploadedFile = new File(uploadFilePath, serverFileName);
                    fileItem.write(uploadedFile);
                } catch(Exception e){                      
                    System.out.println(e);
                }
            }
        }
    }
}

분석을 떠나서 cos.jar의 구현 방식에 비해 뭔가 좀더 복잡해졌다는 느낌부터 오실 것 같습니다. 물론 더 간단하게 구현은

가능합니다만 그냥 라인 수로만 봤을때 cos.jar가 더 심플합니다. 이제 구현 방식에 대해 비교를 좀 더 해보죠.

cos.jar를 HttpServletRequest를 통채로 넘겨받아 라이브러리 내부에서 전체적인 처리를 합니다. 보통 파일 업로드를

구현할때 단일 모듈로 파일 업로드 자체만을 구현할때도 있겠지만 보통은 파일정보 외의 다른 정보도 처리 해야 할때가

많이 있습니다. cos.jar사용시 기존의 HttpServletRequest로는 파일 정보외의 정보를 받을수가 없습니다. 위의 구현

예로 보시면 MultipartRequest multi 를 통해 다른 파라미터를 처리 할 수 있습니다.

ex) String name = multi.getParameter("name");

cos.jar의 경우 소스를 함께 제공하기 때문에 소스를 수정하여 여러가지 컨트롤을 개발자가 수정할수도 있겠지만

남이 만들어놓은 소스를 일일히 분석해서 구현하기에는 저같이 귀차니즘에 빠진 개발자분들에게는 무리라고 봅니다.

cos.jar가 업데이트가 자주 이뤄지진 않지만 혹시라도 업데이트가 이뤄지면 해당 내용에 맞춰서 다시 수정해야 하는

불편함도 감수해야 할수도 있겠죠. 제 경우에는 외부 라이브러리는 피치못할 사정이 없는한 수정하는 경우를 피하는

편입니다. 어쨋든 저렇게 MultipartRequest의 getParameter()를 사용해서 나머지 파라미터들을 처리 할수는 있으나

파일의 경우 보통 서버에 실 파일명으로 보존하는 경우는 그렇게 많지 않을것 같습니다. 저의 경우 직접 원본파일의

접근을 막기위해 중복되지 않는 난수 키값을 생성해서 원본파일명으로 저장 시키고 사용자가 웹에서 요청시에는

이 파일을 웹에서 접근가능한 context의 Temporary 디렉토리에서 읽어오도록 구현하는 것이 목적이었습니다.

하지만 cos.jar에서는 이와 같은 처리는 소스를 수정하지 않는한 불가능 하며 파일 업로드가 완료된후에 파일명을

바꿔주는 방식으로 밖에는 처리 할수가 없습니다. 혹시나 해서 찾아봤지만 제가 아는한은 수정밖에는 방법이

없었습니다. 그래서 Apache Commons Upload로 업로드 모듈을 교체 하게 됐습니다.

위의 예와 같이

            FileItem fileItem = (FileItem) iter.next();
            if (fileItem.isFormField()){ //
파일을 제외한 나머지 파라미터 처리 }

파일 정보외의 파라미터들을 처리 할수 있으며 이 정보를 HashMap에 담아 처리 할수도 있겠습니다. 물론 cos.jar 사용

시에도 파일정보외의 파라미터들을 처리하는데에는 불편한 점은 없었습니다.

            }else{
                //파일 업로드 처리
                try{
                    File uploadedFile = new File(uploadFilePath, serverFileName);
                    fileItem.write(uploadedFile);
                } catch(Exception e){                      
                    System.out.println(e);
                }
            }


하지만 위와같이 파일 정보에대한 가공처리 후 실제 디스크에 쓰기 작업을 하는 동작이 이뤄져야 할때

Apache Commons Upload를 사용하는 것이 상당히 맘에 들었습니다. 또한 구현하다보니 파일 다운로드 시에는 굳이

진행상태를 표시해줄수 있는 프로그레스바를 구현하지 않아도 진행상태를 알수있어서(모바일의 경우) 구현할 필요가 없었지만

업로드의 경우 프로그레스바가 필요했습니다. 그냥 스피너 프로그레스바로 진행중이라는 상태만 보여줄수도 있겠지만

누구나 내가 기능을 실행했을때 얼마나 진행됐는지를 알고싶어 하는것이 당연하겠지요. 그래서 결국 구현을 하다보니

cos.jar 사용(기본 라이브러리 상태)으로는 이 역시 불가능 하다는 결론을 내렸습니다.

(혹시 방법이 있으신분 있다면 피드백 주시면 대단히 감사하겠습니다!)

Apache Commons Upload의 경우 ProgressListener를 구현하여 프로그레스 상태를 Listen할수 있으므로 진행상태를

웹상에서 보여주는것이 가능합니다.

사용예)

           ProgressListener progressListener = new ProgressListener(){

                private long megaBytes = -1;
                public void update(long pBytesRead, long pContentLength, int pItems) {
                    long mBytes = pBytesRead / 1000000;
                    if (megaBytes == mBytes) {
                        return;
                    }
                    megaBytes = mBytes;
                    System.out.println("We are currently reading item " + pItems);
                    if (pContentLength == -1) {
                        System.out.println("So far, " + pBytesRead + " bytes have been read.");
                    } else {
                        System.out.println("So far, " + pBytesRead + " of " + pContentLength
                                           + " bytes have been read.");
                    }
                }
            };

            //프로그레스 리스너를 파일업로드시 추가한다.
            upload.setProgressListener(progressListener);

실제 디스크에 파일이 저장되기전 Apache Commons Upload의 경우 임시 파일 업로드를 진행합니다. 구현 하기에 따라

임시 업로드 디렉토리를 따뤄둬서 업로드 진행중 실패시 원본 저장소의 데이터를 해치지 않도록 할수도 있겠습니다.

또한 WEB-INF/web.xml 에 org.apache.commons.fileupload.servlet.FileCleanerCleanup 리스너를 추가하여

이와같은 쓰레기 파일들을 알아서 삭제하도록 할수도 있습니다.

성능상의 차이는 체감상 크게 느껴지지 않아서 잘 모르겠습니다. 두 가지 라이브러리가 사용용도에 따라서 훌륭한 모듈이라

모듈의 문제로 속도가 저하 된다거나 하기 보다는 I/O가 발생하는 Task이므로 하드웨어, 전송환경등이 더 중요하다고

생각됩니다. 글을 작성하다보니 cos.jar를 너무 무시 한듯 하네요. ㅎㅎ 정리하자면


cos.jar : 웹 to 웹으로 전송해야되는 간단한 데이터, 진행상태 표시 구현이 필요없는 파일 업로드 기능 구현시

매우 간단하게 구현 가능한 장점을 가짐, 소스가 제공되므로 개발자 편의에 따라 소스를 수정하여 사용할수도 있음.

(다만 얼핏 보기로는 cos.jar가 오픈소스는 아니라고 봤습니다. 이 부분에 대해서는 잘 판단해보시길..)

Apache commons upload : 일반적인 파일 업로드 기능 외에 파일명이나 저장되야할 위치에 대한 조작등 개발

의도에 따른 소스 구현이 좀 더 유연함. 업로드 진행상태를 알 수 있는 프로그레스바 구현을 ProgressListner를

통해 쉽게 구현 할 수 있음.

심플한 구현에 있어서는 cos.jar를 사용하는것이 감히 최고라고 말씀드릴수 있겠네요. 두개의 라이브러리 모두 장점이 있으니

적당한 라이브러리를 선택하셔서 개발시 도움이 됐으면 합니다. 자료를 찾다보니 구현방법은 많이 있는데 딱히 어떤 모듈을 구

현할때 사용햇으면 좋겠다 하는 내용이 없네요.


신고

요즘 외쿡에서 들어오는 스팸글이 많아 해당 글에 대한 IP차단 및 삭제를 진행할때 여러 글을 체크한뒤 삭제를 진행했더니

일시적 오류가 발생했다는 메시지 창과 함께 삭제처리가 안된것 처럼 보여서 몇번 재시도를 했습니다.(세네번 정도)

겉으로는 삭제가 안된것 처럼 보여 새로고침 해보니 방명록 데이터가 삭제가 됐네요;;

글 남기신 분들께는 죄송합니다. 고의로 삭제한 것은 아니에요~

※ P.S

개발자의 입장에서 봤을때 아마도 체크된 글에 대한 IP차단+삭제를 진행할때의 로직이 한번에 수행 되는것 같습니다.

방명록 글에 체크가 됐을때 해당 페이지의 체크박스의 인덱스를 그대로 가져가다 보니 삭제 및 IP차단 실패시에 계속해서

삭제 시도시 체크박스의 체크가 해제되지 않은 상태에서 뒤에 있는 글도 삭제 되버리는 현상이 발생하는것 같네요.

글 삭제+IP차단 기능수행이 성공시에는 체크박스의 체크를 모두 제거 시키기 때문에 이런일이 발생하지 않겠지만

실패시에는 IP차단이 성공하더라도 삭제는 계속해서 이뤄지고 성공여부 체크는 두가지 액션이 모두 성공해야 기능수행이

성공된것으로 간주하는 것 같습니다.

아무래도 IP차단이야 실패한다고 치더라도 삭제의 경우 사용자들이 민감해 할 수 있는 부분이니 IP차단이나 삭제가

실패할경우 체크박스의 체크가 모두 해제되게 한다던지 해서 이런 일이 방지 되야 할 것 같네요.

※ 2012.12.07 내용추가

  - 휴지통 기능이 있었네요. 태터툴즈에서 티스토리로 넘어간 다음부터는 관리 메뉴를 거의 본적이 없는것

    같은데 덕분에 방명록 글을 모두 다시 살려놓긴 했습니다만.. 위에서 말한대로 관리자 메뉴에서 보니 몇몇

    방명록에 글쓰신 분들의  IP는 차단되있고 삭제는 계속해서 이뤄지고 있었습니다 ㅡㅡ; 여튼 다시 돌려놨으니

    다행이네요 ㅎㅎ

신고

해가 갈수록 방안에 테스트용이나 작업용으로 사용하는 PC가 늘어나고 있습니다. 16~17년 전만 해도 PC가  상당히 고가 였고

한 가정에 한개의 PC가 있는경우가 아주 많지는 않았기 때문에 집에 PC가 두대이상 있는 경우가 드물었고 노트북PC의 경우

그 당시 물가로도 300~400만원 상당의 고가의 업무용으로나 가끔씩 볼수 있었기 때문에 한 가정에 한대 이상의 PC가 있는

경우가 드물었습니다. 시간이 흐르면서 PC에 사용되는 하드웨어의 가격이 낮아지고 기존에 사용하던 장비들이 고성능 게임을

하지 않는이상 충분히 사용용도에 따라 계속 사용할 수 있을만큼 성능향상이 이뤄졌기 때문에 요새는 한가정에 PC 두대 정도는

많이들 가지고 있으실거라 생각합니다. 저의 경우 중고로 팔아도 돈도 안될 구닥다리 장비들이긴 하지만 총 11대의 PC가

방에 있습니다. 보시는 분들중에는 미쳤다고 생각하거나 대체 뭘하길래.. 라고 생각하시는 분들이 있으시겠지만 딱히 크게

하는건 없습니다. ㅡㅡ;

그냥 PC환경 구성에 대한 관심과 웹프로그램 작성시 테스트 용도로 활용하는 PC가 많다고 보시면 되겠습니다.

현재 PC구성은 이렇게 되어 있습니다. 모두 항상 사용하진 않지만 세대정도는 항시 사용중인데 전기세의 압박이 심각해서

대부분의 시스템은 노트북을 사용하고 있습니다. 아주 예전에는 고성능 게임을 즐겨서 메인 PC는 데스크탑을 보통 사용했었

는데 나이가 한살씩 먹어가면서 게임도 관심이 떨어져서 자주 하질 않아서 KVM스위치로 연결된 노트북을 주로 사용중 입니다.

PC만 여러대 있을뿐 사양은 모두 구닥다리 입니다. 그나마 가운데 아래 있는 PC가 메인PC로 Intel Q8200(Quad Core), 8GB

RAM을 사용하여 가장 고사양이고 나머지는 펜티엄4나 Core 2 Duo급의 용도에 따라 아주 나쁘지도 좋지도 않은 시스템들

입니다.

맨 아래에 있는 PC는 실사용중인 PC중에 상당히 오래 사용해오고 있습니다. 2001년 초에 AMD의 XP 1400+ CPU로 조립해서 

사용하다가 펜티엄4로 교체하여 사용중인 시스템인데 지금은 우분투 리눅스가 설치된 상태로 중간중간 어디선가 얻은 부품들로

업그레이드 해서 1.5GB RAM을 사용하고 있습니다. 위에 있는 PC는 셀러론 2.66GHz가 장착된 PC로 성능은 매우 안좋습니다.

전력소모를 최대한 줄이기 위해 내장된 HDD는 젠더를 사용해서 노트북용 2.5인치 HDD로 교체해서 사용합니다.

가장 왼쪽의 노트북은 개인적으로 사용하는 웹,FTP,메일서버가 설치된 서버로 24시간 돌아갑니다. 소음이 크지 않은 편이라

책상위에 두게 되었습니다. CRT모니터 오른쪽뒤에는 노트북이 한대 더 숨어있는데 본래는 회사에서 개발용으로 사용되던

시스템이지만 개발용으로는 이제 부적합 해도 개인적 용도로는 훌륭한 시스템이라 업어왔습니다. 메인PC와 KVM스위치를

통해 모니터,마우스,키보드를 공유하여 조금 더 고사양인 작업이 필요하면 메인PC를 사용하고 있습니다.

시스템들중 가장 고사양, 메인으로 사용하는 PC입니다. 이것도 3년전쯤에 맞춘 시스템인데 아직도 사용하는데 있어서

전혀 지장이 없네요. 지금 같은 일반적 용도로만 사용한다면 앞으로도 2년 정도는 충분히 메인PC로 사용될수 있을 듯 합니다.

이렇게 장비가 많아지고 사용하는 전원이 많아지다 보니 멀티탭 갯수도 상당히 많아지고 선정리도 상당히 복잡하게

되버렸습니다. 좀더 깔끔하게 정리해보고 싶지만 딱히 좋은 방법이 떠오르지 않네요.

모든 장비들을 항시 사용하진 않지만 언제든지 사용할 수 있도록 셋팅은 되있는 상태입니다. 네트워크 구성은 공유기를 통해서

들어온 라인을 6포트 스위치를 통해 사용중인데 약간 불편한건 포트가 부족하네요. 16포트 짜리 스위치를 알아봤지만

비싸기도 하고 그렇게 까지 불편하진 않아서 그냥 사용중입니다.

창문바깥으로 있는 두대 PC는 어떻게 보면 좀 아웃사이더가 된 PC들로 혹서, 혹한을 그대로 버텨야 되는 불쌍한 시스템들

입니다 ㅡㅡ; 둘다 사양은 펜티엄3 으로 왼쪽PC는 거의 사용하지 않습니다. 메모리가 최대한 늘려놓은것이 92메가 정도 밖에

안되기 때문에 본래는 다운로더로 사용하려고 했는데 펜티엄3도 누진세 때문인지 은근히 전기세가 많이 나오더군요. 그래서

거의 사용하지 않습니다. 오른쪽 노트북은 IBM Thinkpad인데 원래는 개인 웹서버로 사용하던중 알수 없는 이유로 자주 다운

되어서 새로운 다운로더로 사용 중입니다.

창문을 닫으려면 랜선때문에 다운로더 PC를 사용할수가 없어서 피복을 벗겨냈습니다. 보통은 이렇게 피복을 벗기면 노이즈에

그대로 노출되어서 그다지 좋은 방법은 아니지만 무시하고 그냥 벗겨내서 사용중입니다.

예전에 이렇게 두대의 PC만 있을때는 비교적 구성이 간단했었는데 이제는 PC가 너무 많아져서 적당한 배치가 불가능 할

정도네요. 몇대는 치워버릴까 생각도 하지만 생각보다 실천에 옮기기가 쉽지 않네요;; 공간활용 배치 잘하는 분들애개

다수의 PC 깔끔하게 배치할만한 노하우 좀 어드바이스 받고 싶네요.

신고

구입한지 꽤 됐지만 집에서 거실이나 자신의 방에서 사용할 미니 프로젝터에 관심있으신 분들을 위해 간단한 리뷰를

올려봅니다. 프로젝터라 하면 보통 회사 회의실이나 강당등에서 천장에 고정시켜서 사용하는 사이즈가 큰 프로젝터를

상상하실텐데요. 요즘은 이전에 그렇게 큰 사이즈의 프로젝터들도 상당히 소형화 되어 출시되있어서 배치만 적절히 한다면

가정에서도 비교적 저렴한 가격에 영화관 스크린과 같은 사이즈로 영화나 TV를 즐길수 있는 세상이 되었습니다. 저도

이전부터 구입하려고 눈독을 들이면서 알아봤던 제품중 L사의 미니 프로젝터가 있었습니다.

(사진출처 : http://www.flickr.com/photos/lge/ )

사진에서 보듯 상당히 사이즈가 작습니다. 사진에서 보는 모델들이 제가 사려고 했던 모델들과 동일 한것은 아니지만 보통

현재 출시되는 미니프로젝터의 크기가 위와 비슷합니다. 실제로 본것은 회사내에서 기존에 사용하던 프로젝터가 고장나서

새로 구입하면서 L사의 미니프로젝터를 구입했는데 상당히 작고 확장성 또한 좋았으며 (HDMI,RGB단자, USB단자,

기본프로그램 지원등) 투사거리가 길며 화질 또한 뛰어났습니다.

하지만 저는 방 구조상 배치할수 있는 더 작은 사이즈의 프로젝터는 없을까 찾아보게 되었고 그렇게 찾게 된 프로젝터가

유니드 일렉트로의 비트 LED 프로젝터 UMP-15A 였습니다. 

최근에 출시되는 대부분의 프로젝터들이 LED 광원 방식을 채택하고 있고 LCD, LED의 수명, 밝기등의 수치상 차이는

가능한 한 언급하지 않고 실제 사용했을때의 느낌을 위주로 작성하겠습니다. 일단 가격면에서는 상당히 저렴한편입니다.

정확한 기억은 없으나 3~4달전에 구입했을 당시 30~35만원 정도 들였던 것 같습니다.

고사양의 미니프로젝터들은 이보다는 훨씬 비쌉니다. 이전의 200~300만원이 넘어가는 고가의 프로젝터들에 비해

비교적 저렴하지만 그래도 100만원 내외로 아주 저렴하진 않습니다.

가까이서 찍어서 커보이게 나왔으나 실제 크기는 정말 작습니다. 저도 알아볼 당시 담배갑 크기 정도 된다는 사진을 봤었는데

실제로 보니 한손에 들어오는 사이즈로 상당히 작았습니다. 프로젝터 상단에는 투사거리가 길어졌을 경우 수동으로 포커스를

맞춰줄수 있고 AC전원과 PC와 연결될 RGB케이블이나 AV단자를 연결할 수 있는 전용 케이블을 제공하고 있습니다.

기본적으로 착탈 가능한 리튬이온 배터리를 제공하는데 배터리 성능은 그다지 좋지 않은편으로 35~40분 정도 가량밖에 사용

할 수 없습니다. 이 제품은 원래는 휴대성을 염두해 두고 설계된 제품이겠지만 저의 경우 방에서만 사용할 것이라 상관 없는

부분 이었습니다. 저의 경우 전원을 항상 연결 시켜 두는데 현재 배터리만 사용했을때(총 사용기간 : 4개월 정도) 25분 가량

밖에 사용할 수 없습니다.

다음과 같이 AV 케이블을 제공하여 PC외의 다른 기기와 연결 하여 사용할 수 있습니다. 전용 단자를 사용하기 때문에 케이블

분실 또는 손상되지 않도록 주의 해야겠습니다. AV단자로 다른기기와 연결시 프로젝터에 내장된 모노 스피커로 오디오가 출력

되는데 음질은 별로 좋지 않습니다.

케이블 외에 기본으로 이 미니 삼각대를 제공하여 투사거리를 조절해 줄수 있습니다만 저는 기존에 있던 삼각대가 있기도 하고

이 삼각대로 투사거리 조절할만한 배치가 불가능하여 사용하지 않았습니다.

기존에 캠코더용으로 사용하던 삼각대가 있어서 합체시켜 사용중입니다. 표준규격을 사용하는 삼각대라면 위와 같이 구성

할 수 있습니다. 사진상에 보이진 않지만 단점이 하나 있는것이 프로젝터에 연결되는 어댑터의 크기가 상당히 큽니다. 침대

뒤로 아예 고정시켜놔서 사진을 찍진 않았습니다만 구입을 고려한다면 어댑터 크기도 어느정도는 고려하시는 것이 좋겠습니다.

작동시킨 상태로 찍어봤습니다. 실제 크기와 가장 근접하게 찍힌 사진 인듯 한데요. 사이즈면에서는 최고로 만족스럽습니다.

아래에는 배터리를 탈착할수 있게 되있습니다. 배터리 성능은 위에서 언급한대로 그다지 좋지 못합니다. 영화 한편을 다 볼만큼

버티질 못하지만 휴대시 30분 내의 매우 간단한 프리젠테이션등에는 사용할만 하겠습니다.

보통 프로젝터는 HTPC와 함께 거실에 구성하는 경우가 많은데 저는 방안에서만 사용할것이라 이와 같이 구성해서 사용

중입니다. 제작년까지 잘 사용해 오던 EEE PC 701 로 네트워크 공유(무선)을 통해 동영상을 감상하고 있습니다. 물론 EEE PC

의 성능상 한계와 무선네트워크 속도등의 한계로 HD급 화질의 동영상 시청은 곤란하지만 보통화질의 영상은 감상하기

충분합니다.

전등을 모두 끈 상태에서 블라인드에 투사한 사진입니다. 일단 블라인드 스크린에 역 사다리꼴로 투사되는데 이유는

삼각대를 약간 위로 조절하여 투사했기때문에 평행되지 않아서 생기는 문제인데 다른 고사양의 프로젝터들의 경우

키스톤 조절이나 렌즈 쉬프트 같은 기능을 제공하여 이를 조절할수 있으나 UMP-15A에는 그런 기능은 없기 때문에

프로젝터와 투사할 스크린의 거리와 각도를 잘 맞추는것이 중요합니다. 저는 꼭 각도가 맞아야할 필요까진 없고

대충 화면만 크면 되고 시야만 어느정도 보장되면 상관 없어서 잘 사용하고 있습니다.

화질은 이정도라고 보시면 됩니다. 사진상으로 보시는것과 크게 틀리진 않습니다. 사용된 동영상은 HD 720p 해상도의 영상을

재생시킨 것인데 화질은 그다지 좋지 않다고 보시면 됩니다. 아무래도 초소형화, 단가절감 이유등으로 성능면에서 약간 절약(?)

된 느낌입니다. 하지만 감상하기에 큰 무리는 없으며 충분히 볼만합니다. 화질에 민감하신 분들은 이보다 훨씬 고사양의

프로젝터를 구입하셔서 쓰셔야 할 겁니다. 다만 30인치 정도 크기로 투사했을때는 상당히 좋은 화질로 감상 하실수 있습니다.

형광등을 켜고 투사한 모습입니다. 사진에서 보시면 희미하게 자막정도만 보이는걸 보실 수 있는데 고사양의 프로젝터들은

밝기가 UMP-15A보다 훨씬 좋기 때문에 밝은곳에서 투사해도 식별이 가능한 제품이 많이 있습니다. 저는 어두운 곳에서

영상을 봐도 상관없어서 그냥 사용하고 있습니다. 스크린의 경우 예전에 자취할때 커텐대신 블라인드로 사용할 목적으로 구입

했던것인데 우연치 않게 프로젝터 스크린용으로 잘 사용하고 있네요.

미니프로젝터 실사용기를 간단하게 써봤는데요. 정리하자면..

장점 : 휴대가 간편한 초미니 사이즈, 비교적 저렴한 가격(40~50만원대), AV단자, RGB케이블 제공으로

        기본적인 확장성 제공

단점 : 밝기가 어두운편으로 밝은환경 하에서 화면 식별이 거의 불가능함, 키스톤 or 렌즈시프트 기능이 없어

        프로젝터 배치상태가 정확하지 않으면 스크린에 화면을 맞추기가 불편함.

고화질 홈씨어터를 구성하시려는 분들에게는 이 제품은 별로 어울리지 않습니다. 하지만 저처럼 방안에서 잠들기전에

영화감상이나 뮤직비디오 감상등 간단하게 쓰려는 목적으로 사용한다면 상당히 만족스럽게 사용하실수 있을것 같습니다.

p.s : 이 제품을 처음 받았을때 아마도 미니프로젝트인것에만 신기해 하는 분들이 구입했다가 성능에 실망해서

반품한 경우가 많았는지 충분히 사양을 고려하고 구입하라는 당부가 있더군요. 그말대로 이 프로젝터에 고화질

프로젝터의 성능을 기대 한다면 더 비싼 다른 미니프로젝터를 사셔야 한다고 하고 싶네요.



신고

다섯달 전쯤 회사에서 파일서버 용도로 사용하던 고가의 서버가 죽어버리는 사태가 발생했었습니다.

불행중 다행히도 SAS(Serial Attached SCSI) 형태라 시크타임이 짧은 고가의 하드들은 죽지 않았고

메인보드나 파워의 불량으로 단정짓고 새로 파일서버 구매를 추진했었는데요. 찾다보니 따로 업체와

계약되있지 않은경우엔 일반 상점에서 수급이 쉽지도 않을뿐더러 가격도 천차만별 이었습니다.

일단 구입할 새로운 서버 사양은 이정도 였습니다.

CPU : 듀얼코어
RAM : 2~4GB이상(ECC)
HDD : 4TB이상

HDD나 메모리야 별도 구매한다 손 쳐도 서버 가격이란게 참 너무 비싸더군요. 보통 250~300만원

이상이었고 500만원이 넘는 서버도 많았습니다. 저희 입장에서는 파일서버에 오버헤드가 날만큼의

빈번한 입출력은 거의 발생할일이 없어서 전에 제 블로그에 트랙백으로 걸렸던 이 HP 마이크로 서버

에 약간 관심이 쏠렸습니다. 아무래도 나름 서버군 제품이니 안정성도 어느정도 있을것이다 싶은

생각이 들어서였는데요. 사실 고성능 일반PC로 구성해도 상관은 없었습니다. 이전에 사용하던

서버는 꽤 오래된 HP서버였지만 이 파일서버가 24시간 365일 오류등의 이유로 한번꺼진다고 해서

큰일이 날만한 서버도 아니었기때문이었죠. 여하튼 일반 전자거래 사이트는 세금계산서 발행이 이래

저래 불편한게 많아서 전문 쇼핑몰 사이트에서 구입하였습니다. 그럼 이제 실제 사용하면서 느낀점

을 써보겠습니다.

(사진출처 : http://hpmicrosvr.com/22 , 블로거 ideakeyword님의 블로그)


전면의 모습입니다. 이 사진과 전혀 틀린것 없이 똑같이 생겼습니다. 크기도 사진에 보시는것과 같이

마이크로 서버 답게 작습니다. 다만 들었을때 은근히 무게감이 있습니다. 전면부에 USB포트가 네개

가 있어서 외부로 파일 이동시에 편의성을 제공합니다.

후면 모습입니다. e-SATA단자와 RGB단자, 내장랜포트, USB 2.0 2x, 방열을 위한 120mm팬이

눈에 띕니다. 이 서버를 서버실 밖인 사무실 내 제자리에서  구성하고 있었는데 팬소음의 경우

조용한 편이었습니다. 물론 시간이 어느정도 지나면 모든 PC의 팬들이 그렇듯 소음이 어느정도

커지겠습니다만 귀에 거슬릴 정도의 소음은 아니었습니다. 서버실에서 사용한다면야 어짜피 소음이

크므로 상관없겠습니다만 가정용으로 사용한다면 서버위치 배치시에 소음도 어느정도 고려사항이

되는데요. 상당히 조용한 편이었습니다.


전면부에는 도난방지를 위해 간단하게 만들어진 열쇠가 있습니다. 열쇠로 전면부를 열면 다음과

같이 HDD를 꼽을수 있는 트레이 네개가 보입니다. 트레이 하단부에 보이는 것이 메인보드로 램

확장을 위해서는 다 뜯어내야 합니다. 저같은 경우엔 처음부터 업그레이드된 상태로 구입했습니다.

처음 구입하면 번들형태로 160GB 짜리 시게이트 SATA HDD를 제공합니다. 어짜피 파일서버 용도로

사용이 목적이라면 대용량 HDD를 별도 구입할텐데 번들형태로 제공하는 HDD를 옵션으로 선택하여

판매하여 더 낮은 단가로 구입할수 있게 하는게 더 낫지 않나 싶은 생각이 듭니다.


바로전 사진에서 본 트레이의 모습입니다. 이곳에 HDD를 장착하고 나사 네개를 장착하여 트레이에

밀어 넣으면 설치가 완료됩니다. 주의하실점은 SATA HDD만 지원한다는 것입니다. IDE 형태의 HDD

는 사용불가능합니다. 저의 경우 2TB짜리 HITACHI HDD 두개를 연결하였습니다.

사진으로 소개하는것은 크게 특징이 없어서 이것으로 마치고 실제 사용하면서 느낀점을 남겨봅니다.

- RAID 구성(미러링, 스트라이핑)

일단 저의 경우 가정용이 아닌 기업용으로 이 서버를 구입하였기때문에 2TB짜리 HDD두개를 구입

한 첫번째 이유는 RAID 미러링 구성으로 안정적인 사용이 목적이었습니다. 이 Prolient서버의 경우

요새 대부분의 PC메인보드에서 자체적으로 지원하는 RAID구성 기술을 제공하긴 하는데 상당히

불안정
하였습니다. 원래 저의 목적대로 사용하자면 2TB HDD 네개로 구성하여 두개씩 미러링을

하는형태 였는데 아무래도 구입전에 내장 RAID를 믿을수가 없어서 RAID구성 실패시에 그냥

4TB 저장공간으로 사용하려고 2TB HDD 두개만 구입하였습니다만 실제로 구성해보며 사용해보니

역시나 미러링이 풀려버리는 문제가 발생했습니다. (OS차원의 RAID를 사용한것이 아닙니다.)

또한 파일입출력시 미러링 대상인 HDD에 동기방식으로 파일쓰기를 하면서 일반적으로 사용시 보다

속도가 상당히 더뎠습니다.

(HDD의 경우 두개다 동일한 모델, 동일 스펙이었습니다. 7200RPM, 32M Buffer,) 이대로는

불안해서 안되겠다 싶어 그냥 두개의 저장 공간으로 (2TB*2EA - 4TB)로 사용하기로 결정했습니다.

파일서버 용도인데 RAID구성이 미흡한것은 저가형 제품이라 단가 하락을 위해 어쩔수 없는 부분이

었는지 싶지만 그저 생색만 낸것 같아 상당히 아쉬운 부분이었습니다.

- 속도

어짜피 파일 입출력 속도는 CPU보다 HDD속도 자체에 더 많은 영향이 있으므로 여기서 HDD속도는

언급하지 않겠습니다. 이 서버의 경우 AMD Athlon II 1.3Ghz 듀얼코어 CPU를 사용합니다. 무난하게

사용할수 있을만큼의 스펙을 가지고 있으며 메모리는 1GB*2(ECC)로 구성되있습니다. 제가

사용하는 환경은 동시사용자가 그렇게 많지 않고 대용량 파일이 왔다갔다 하는일은 거의 없으며

외부에서 접근하는 인원을 제외하면 50여명 정도가 이 파일서버를 사용하는데 동시에 10명이상

접근하여도 CPU 사용량에 큰 무리가 없었습니다. 

(동시접속 15명 이상, 파일 입출력시 CPU점유율 40~50%) 다만 사업장에서 100명이상이

이 서버를 사용한다면 무리가 있을듯 합니다. 저의 경우 윈도우2003서버가 드라이버를 지원하지

않는것이 많아 귀찮아서 윈도우7 Ultimate K버전으로 OS를 설치하였는데 일단 윈도우7 자체가

이 서버에서 좀 느린감이 있습니다. 물론 서버용이므로 각종 에어로 효과등은 모두 제거하고

기본고전 UI로 사용합니다만 그럭저럭 쓸만한 정도 인듯 합니다. 저는 귀찮기도 하고 별 문제

없어서 그냥 사용합니다만 굳이 서버군OS를 설치하진 않더라도 가벼운 OS를 사용하시길

권장합니다.

- 전체적 평가

원래 제 자신이 서버 구성이나 NAS등에 관심이 많아 어짜피 회사에서 사용할 새로운 파일서버를

구입하는것이 목적이었고 전에 트랙백으로 걸린 내용이 기억나서 이 제품을 구입해서 사용하게

됐지만 사용하면서 느끼는 것은 이 제품의 취지대로 10인이하의 소기업이나 가정용 파일서버로

사용하는것이 낫겠다는 생각이 듭니다. 가격은 40~50만원대로 '저가형 서버' 라는것을 강조하곤

있지만 그렇게 실속있는 서버라는 느낌은 들지 않았고 서버를 흉내낸 미니PC 정도라고 하고

싶네요. 저평가만 잔뜩 해놓은것 같지만 현재 회사내에서는 서버실에 이 서버가 들어가 있는

상태이며 다운한번 없이 안정적으로 잘 사용하고 있긴합니다. 기존의 NAS제품같은 경우

자체적인 OS사용으로 편의성에 제약이 따름에 비해 OS자율 선택, 전면부에서 HDD트레이 제거나

추가로 유지보수의 편의성이 있고 CPU의 TDP가 12W로 상당히 저전력으로 돌아가며 위에서

언급했듯 팬소음도 상당히 작은편이라 가정용으로 사용하기에도 괜찮은 제품입니다. 다만

선전문구에서 강조하는 RAID구성면에 있어서는 최악의 점수를 주고 싶네요.

신고

지난주에 개인적으로 아주 큰일이 생겼습니다. 출근해서 외장하드를 연결하니 쇳소리가 나는겁니다.

간혹 USB케이블이 제대로 연결 안된경우 인식이 안되는 일이 생기니 그러려니 하고 다시 꼽았다

끼워봤지만 여전했습니다. 옆에 있던 PC에 끼워봐도 마찬가지 더군요. 케이블 불량인가 싶어 다른

케이블을 끼워봐도 역시나 쇳소리.. 이 외장하드에는 제가 일하면서 정리한 내용들이나 업무에

관련된 중요내용들이 한가득 들어있기 때문에 고장나면 상당히 데미지가 큰 상황이었습니다.

그래서 이번 글에서는 시게이트 HDD를 대놓고 까려고 합니다.ㅡㅡ^

사용자 삽입 이미지

사건의 주범 Seagate FreeAgent 320GB


시게이트 HDD는 제가 컴퓨터를 사용하기 시작하던 90년도 초반부터 저와 인연을 맺어왔습니다.

286이었나.. 386컴퓨터에 있던 HDD가 용량 120MB짜리였던걸로 기억하네요. 이 HDD는 지금도

집에 보관중이고 아직도 전원 연결하면 작동 합니다. 그 후로도 500MB, 1GB... 항상 저는 시게이트

HDD만 사용했습니다. 딱 한번 스타크래프트가 나온해에 업그레이드 하면서 (셀러론 300A)

후지쯔 하드를 사용했었습니다.

사용자 삽입 이미지

Fujitsu MPD3084AT (8GB)


후지쯔 HDD도 이 모델이 처음이자 마지막 이었던 것 같습니다. 겨우 2년 정도 썼는데 갑자기

그라인더 갈리는 소리와 함께 고장나서 자료를 다 날려먹은 주범이었죠. 이 당시에는 지금처럼

인터넷이 빠르지도 않았고 자료 하나하나가 구하기 힘든 시절이었기 때문에 후지쯔 HDD는

이후로 믿음이 안가서 사용하지 않았습니다.

여튼 저는 시게이트 HDD를 고집해서 써왔습니다. 80GB, 120GB 모델이 출시됐을때 구입한후 로는

그다지 용량이 많이 필요하지 않아서 몇년간 HDD구입을 안했습니다만 작년에는 처음으로 대용량

HDD를 시게이트 대신 웨스턴 디지털의 블랙 캐비어 1TB를 구입했었죠.

사용자 삽입 이미지

Western Digital Caviar Black 1TB


구입할 당시 항상 그렇듯 당연히 시게이트 HDD를 구입하려고 했으나 검색해보니 펌웨어 업그레이드

전의 HDD들의 불량률이 심각한 수준이고 유저들의 불만이 엄청 나더군요. 그래서 저도 시게이트를

버리게 됐었습니다. 제가 직접 쓰진 않아서 모르겠지만 제 동생이 쓰는 PC에도 불량률 높다는

모델,펌웨어를 사용하는 HDD를 사용중인데 아직까진 멀쩡하군요. 자료 다운도 거의 안받는데다가

장시간 사용하지 않아서 그런지도 모르겠습니다. 제가 지금 쓰는 PC에는 이 캐비어 블랙외에

메인으로 사용하는 SATA형 시게이트80GB HDD와 이전 PC에서 자료 백업용으로 쓰던 120GB IDE

형 , 80GB IDE형 HDD가 달려있습니다. 그리고 다운로더등으로 사용하는 옆 PC에도 중고로 구입한

80GB IDE형 HDD, 다른 PC에 있는 HDD들도 모두 시게이트 제품이네요. 그렇게 잘 써오다가..

제작년 쯤 업무용 자료를 한꺼번에 넣어 다니기 위해 회사돈으로 시게이트 FreeAgent 320GB를 구입

하게 되었습니다. 이때까지 다른 HDD는 망가져도 시게이트는 써오면서 망가져 본적이 없어서

구입하게 됐는데 망가질때 까지 써온 기간은 1년이 조금 넘는것 같습니다.

보통 외장하드가 망가지는 경우는 몇가지 이유가 있긴합니다.

1. USB케이블을 잘못 꽂아서 or 케이블 접촉이 제대로 안되서 장치인식 실패하는 경우가
   잦아서

2. HDD자체가 불량품

3. 컴퓨터 비정상 종료로 꺼졌을때 디스크 검사 등으로 스캔해서 오류수정 하지 않고
   계속해서 엑세스 하는경우

4. 토렌트나 기타 P2P 프로그램 사용으로 디스크 과부하가 걸린 상태가 잦은 상태로
   장기간 사용

5. 너무 온도가 낮은곳이나 높은곳에 장시간 방치할경우

6. 외장케이스가 불량


등등.. 망가지는 이유는 많이 있습니다. 저의 경우 중요한 자료가 많은 HDD이다 보니 고장나지

않게 매우 조심히 가지고 다녔고 사용후에 뽑을때도 항상 안전제거 한뒤 뽑아서 사용했습니다.

가지고 다닐때는 파우치에 고이 넣어서 다녔었죠. 고장나게 사용한적은 전혀 없었다고 생각듭니다.

굳이 고장의 이유라고 한다면 들어있던 자료의 폴더 구조 복잡성, 엄청난 파일갯수(130만개 정도..)

등을 엑세스 하면서 헤드가 피곤하셔서 망가졌다고 밖에는 생각나는게 없군요.

근데 시게이트 HDD를 쭉 써온 저로서는 이 부분도 납득되지 않습니다. 지금 다운로더로 쓰는PC의

외장하드를 물려놓았는데 이것도 80GB 시게이트 2.5인치 HDD입니다. 막쓰는 HDD라 주로 토렌트

를 장시간 돌리고 디스크 과부하도 수없이 걸립니다. (귀찮아서 여러개 대기열에 쌓아놓고 한번에

받고 있기때문이죠..) 게다가 쓰고있는 외장케이스가 약간 불량으로 인식실패도 수없이 했으며

많은 데이터가 오갈때 케이블이 뽑힌적도 셀 수 없이 많지만 여전히 잘 작동하며 HD Tune등으로

봤을때 '상태 양호'로 나옵니다. 벌써 사용한지 3년이 다 되가네요. 지금 웹서버,FTP서버, 홈 파일서버

용으로 사용하는 펜티엄3급 노트북에 달려있는 HDD도 시게이트 80GB HDD입니다. 역시 건강하며

24시간 돌아간지 1년이 넘어도 죽는소리 한번 내지 않고 있습니다. 이런 이유로 믿어왔던 시게이트

제품을 구입했었는데 불과 1년이 조금 넘는 기간을 써왔다고 고장이라뇨 ㅜ_ㅜ.. 게다가 예고도

없었습니다. 평소에 불안했다면 눈치를 챘을텐데 돌연사 해버렸습니다.

사용자 삽입 이미지

Seagate Momentus 5400.6 320GB (ST9320325AS)


가는날이 장날이라고 고장난 시기도 하필 할일이 많고 HDD안에 있던 자료가 필요하던 날 아침시작

부터 죽어버렸으니 짜증을 말로 표현할수가 없더군요. 어짜피 A/S같은건 받을 시간도 없고 받는다

해도 이미 신뢰가 떨어진 상태에서 다시 사용하고 싶지 않아 과감히 A/S를 포기하고 뜯어보니

위 사진의 HDD가 들어있었습니다. 고장난 놈을 놔두고 머리를 굴려봤습니다.

"케이스가 불량인지도 몰라" 생각에 사서 유용하게 써먹는 SATA,IDE to USB 젠더를 꺼내서 물려

보니 쇳소리는 여전하고 초기에는 인식못하지만 diskmgmt.msc(디스크 관리자)를 실행하면 한참

있다 인식이 되더군요.

급한대로 인식시켜놓고 큰 자료는 과감히 포기 하고 작은 자료들만 옮기기 시작했지만.. 역시 쇳소리를

내면서 1MB 복사 하는데 5시간이라고 표시해주는 센스!

여튼 몇몇 자료는 복사해놓은 상태이고 다행히 집에서 사용하는 PC에 싱크프로그램으로 1주일 전쯤

까지 작업했던 내용은 싱크가 되있는 상태라 안도의 한숨을 쉬었습니다.

곰곰히 생각해보면 시게이트 HDD는 120GB 하드 출시이후 서서히 불량률이 높아져가고 있는것

같습니다. 경쟁사에 뒤쳐지지 않기위해 급하게 제품을 찍어내서 일까요? 단지 제 외장HDD가 고장

나서가 아니라 여러 사용자들의 글을 봐도 그렇고 예전만큼의 안정성은 이제 없는것 같습니다.

사용자 삽입 이미지

Western Digital My Passport USB 3.0 500GB

결국 고장난 HDD를 뒤로 하고 기존의 PC에서 사용하던 블랙 캐비어 HDD가 상당히 안정적인것을

1년 조금 넘게 사용하면서 봐온 경험으로 외장HDD도 한번 믿어보기로 하면서 구입했습니다.

크게 비싸진 않더군요. FreeAgent살때도 10만원 가까이에 산것 같은데 이 제품은 7만 8천원정도

주고 구입했습니다. 싱크된 자료들을 새 HDD에 복사하면서 백업의 중요성에 대해 다시 한번 생각

해보게 됐습니다. 집에서 백업한번 안했다면 수많은 자료를 몽창 날려먹은 허탈함과 다시 해야할

수고를 얼마나 오래 겪었을까요? 그래서 다음엔 싱크프로그램에 대한 글을 남겨보려 합니다.

이 글을 보시는 분들도 중요자료는 외장HDD를 믿지 마시고 별도로 3.5인치 안정성 있는 HDD에

넣어두시고 이중, 삼중 백업 해두셨으면 합니다. 이번일로 정신적 데미지가 커서 조만간에

데이터 백업용 HDD를 추가로 사려고 합니다. 또한..

이제 시게이트 HDD는 안녕~ 절대 안삽니다!!!!!

신고

이번에도 엄청 오랜만에 글을 써봅니다. 얼마만인지 기억도 안나네요. ㅎㅎ 올해 초에 쓸데없이

사지말자 하다가 결국은 사게된것이 있습니다..

사용자 삽입 이미지

유경테크놀로지 Viliv S7


바로 요놈이죠. 평소 넷북이나 울트라씬 노트북등에 관심이 있으셨던 분들이라면 아마 이 빌립과

UMID의 mBook등을 잘 아실겁니다. 여하튼 저는 기존에 쓰고있던 ASUS EEE PC 701이 있었기

때문에 구입하는데에 상당히 고민을 했습니다.(이틀정도....)

사용자 삽입 이미지

ASUS EEE PC 701 (4G SSD, 1GB RAM, Dothan 600Mhz CPU)


사진으로 보면 아시겠지만 정말 아껴서 썼습니다. 처음 EEE PC가 나온다는 말에 국내에 들어오기도

전에 사버렸었죠. 그때 제가 설레발 쳤던 글은 여기서 보실수 있습니다. 그 당시엔 정말 좋았습니다.

일단 뒹굴거리면서 영화를 볼수있다는것, 간단한 게임정도는 GMA950도 그럭저럭 소화한다는것도

있고 당시엔 자취를 하고있었는데 굳이 무거운 본체 가지고 낑낑대지 않고 모니터만 들고다니면

큰 화면으로 훌륭하게 썼었죠. EEE PC로 다른 활용도 많이 했던것 같습니다. CPU만 보면 허술하지만

당시에 학교에서 수업관련해서 썼던 VMWare도 잘 썼었습니다. 이렇게 잘썼던 EEE PC를 놔두고

년초부터 새 넷북 or MID를 찾게 된데는 여러가지 이유가 있었는데 가장 큰 이유 두가지는..

1. 스위블

사용자 삽입 이미지


가장 큰 이유는 위 사진처럼 스위블이 아니라는 것이었는데요. 처음 EEE PC를 사고나서 지금은

철수해버린 고진샤 시리즈 스위블 PC들이 쏟아져 나왔을때도 스위블에 상당히 눈독 들였었습니다.

이때는 고민하다 EEE PC로 가게된 큰 이유가 '꼬진샤' 라는 말이 나올정도로 A/S가 좋지 않고

불량률이 높았다는 것때문에 EEE PC로 가게 됐었죠. 그리고 화면을 뒤집은 상태로 터치로

타블렛 처럼 쓸수있다는것이 상당히 편해 보였었습니다.


2. 배터리
사용자 삽입 이미지


EEE PC의 충전기가 작긴 합니다만.. 그렇다고 항상 가지고 다니기엔 무리가 있고 최근에는 외근시에

자주 사용했는데 배터리는 어제 충전하고 뭣좀 했다가 썼다고 10%남아있는데 깜빡하고 그냥 왔고

콘센트 꼽을여유가 없어서 멀티탭 저~기서 끌어와서 써야되는 경우가.. 여러번 있었습니다.

집에서 쓸때도 충전기는 거의 항상 있어야 되서 조금 불편했습니다. 중고로 S7을 사려고 알아보니

배터리가 완충시 7~9시간 사용가능하다고 하기에 중고인점을 감안해도 꽤나 가겠구나 싶었습니다.

배터리 시간은 딱히 시간을 정해놓고 테스트 해본것은 아니지만 7개월 정도 사용된 중고품 상태에서

완충후 액정이 켜진채로 그대로 두고 간간히 프로그램좀 돌려보고 했을때 8시간 정도 갔습니다.

계속 뭔가 작업을 했다고 해도 세네시간은 쓰지 않았을까 싶네요.

이 두가지 큰 이유때문에 사긴 했는데 사실 S7구입전 바로 요놈을 중고로 구입을 했습니다.
사용자 삽입 이미지

Fujitsu Lifebook U2010

이놈은 제 기억에 ASUS EEE PC가 나오고 오래 지나지 않아서 소니 UX시리즈와 함께 혀가 튀어나올

가격으로 출시됐던것으로 기억합니다. 하지만 엄청나게 가벼운 무게 (위 사진 상태로 560g 정도)

화면은 작지만 5.6인치라는 크기의 휴대성이 저를 사로 잡았기 때문에 지금은 몇년 지나 중고가가

35만원 가량으로 낮아져서 구입을 했었죠. 근데 이미 다른 분께 판매가 되서 환불을 받고 다시

찾아보던중에 배터리 시간을 보고 S7을 구입하게 됐습니다. U2010쓰는 분들 중에는 여러가지

문제점을 호소하시는 분들이 많긴 했습니다.. 너무 작은 화면에 너무 높은 해상도나 피봇활성화시

사운드 디바이스가 죽어버리는 문제점등.. 많더군요. 저는 어짜피 이런 PC에서 큰 작업도 하지

않고 가지고 다니면서 인터넷이나 검색하고 외근나가서 시커면 터미널(CRT)정도만 볼수있고

DB접속정도만 할수있을 정도의 성능이면 충분하기에 상당히 아쉬웠습니다. 다른 중고품도

있었는데 상태가 너무 안좋아서 패스 했네요. S7이나 U2010을 사기전에 한번더 고민하게

만들었던것은 제 방에 있는 PC들 때문이었습니다..

사용자 삽입 이미지


이사오고 나서 방 정리상태는 좋아졌지만 이전 집에서 보다 더 많아진 PC들... 그렇다고 다쓰는것도

아닙니다. ㅎㅎ 한대는 제대로 잘 쓰고있는것이 지금 FTP,간단한 게시판 웹서버를 돌리고 있는 중고

로 작년에 구입한 씽크패드가 아래에 쳐박혀 있네요. 거기에 처음엔 원격프로그램으로 제어하다가

귀찮아서 구성한 KVM스위치에 랜선에... 사실 이렇게 쓸필요가 있나 싶긴 합니다. 실제로 많이쓰는

PC는 가운데 하얀케이스 PC가 메인이라 나머지 PC들은 퍼포먼스도 떨어지고 해서 거의 쓰질

않습니다. 거기에 살짝 맘에 안들긴해도 이미 EEE PC가 있는상태에서 2년넘게 쓰긴했지만

또 사기엔 사치라 생각됐지만 정신차려보니 이미 S7에 윈도우7을 깔고있었네요 ㅡㅡ;

뭐 어쨋든 이미 사버렸으니.. EEE PC와 크기를 비교해보고 사기전에 S7에 대한 거의 좋은평가의

리뷰만 많이 본것 같아서 S7에 대해 생각나는 안좋은점을 몇가지 써보겠습니다.

사용자 삽입 이미지

너비는 크게 차이가 없습니다. 다만 위아래 사이즈가 EEE PC보다는 약간 작습니다. 두께는

EEE PC보다 약간 얇은 정도입니다.

사용자 삽입 이미지


이렇게 보니 EEE PC가 상당히 커보이네요. S7이 작긴합니다. 터치패드를 아래에 두지 않고 우측

상단에 조그맣게 배치해서 크기를 줄인점이 좋습니다.
사용자 삽입 이미지

EEE PC예전에 처음 사서 들어봤을때 정말 가볍다고 느꼈는데 이제는 이것도 살짝 무겁게 느껴지네요.
사용자 삽입 이미지

S7도 은근히 무겁습니다. 가볍긴 하지만 U2010만큼 가벼울것 같진 않습니다.

이렇게 사진으로 보시면 S7이나 기타 비슷한 제품 중고품 또는 신상품을 사실때 참고가 되지 않을까

싶어서 올려보네요.

사용자 삽입 이미지

아..한가지 더 U2010이나 S7말고도 눈독들였던것이 위에서 잠깐 언급했던 UMID의 M1입니다.

정말 작습니다. 어짜피 EEE PC 701를 통해 넷북이나 MID자체의 한계를 알고있으니 컴퓨터는

아니면서 기존 넷북들에 비해 훨씬 작고 가벼우면서 들고다니기에 전혀 부담감 없으면서 컴퓨터에서

할수 있는 작업을 할수있는 디바이스......;; 를 항상 원해왔기 때문에 이 제품도 눈에 들어오긴

했지만 이것조차도 스위블이 됐으면 좋지 않았을거라고생각을 해서.. 구입순위에서 밀려났네요.

또한 별건 아니지만 미니USB포트에서 젠더를 통해서 USB를 사용가능한것도 좀 그렇구요.

이건 오늘 지하철에서 실제로 봤는데 정말 작더군요.

이제 S7의 단점을 늘어놔보겠습니다..(S7 안티라서 쓰는건 아닙니다.ㅋ)

1. 의외로 들고다니면서 쓰기에 편한 사이즈는 아니다.
   지하철에서 가끔 갤럭시 탭이나 아이패드를 쓰는분들을 보곤 합니다만 S7도 그렇게 보면
   크게 불편한 사이즈는 아닙니다. 다만 크기가 상당히 애매하다고 느껴집니다. 세로로 들었을때
   좀 어색합니다. 물론 제조시에 많은 고민이 섞여들어갔겠지만 세로로 들었을때 U2010처럼
   균형감 있게 되있다면 더 좋지 않았을까 생각됩니다. 또 대중교통 이용시에 몇번 써봤지만
  사람들이 너무 많을때는 귀찮아서 아이폰을 꺼내게 되더군요.

2. 은근히 무겁다.
   위에서 몇번 말했듯 EEE PC도 처음엔 가볍다 생각했습니다. 물론 너무 작으면 화면을 볼때
   눈이 쉽게 피로해지긴 하지만 저에게 있어서 넷북이나 MID들은 탁월한 휴대성이 제일 우선
   순위여서 S7도 이점에 있어서 약간은 기대치에 못미치긴 합니다. 가방에 넣고 다니기엔
   가볍습니다.

3. 업그레이드를 할수가 없다.
  물론 이런 작은 디바이스들의 경우 크기를 줄이고 성능은 어느정도 살리기 위해 많은점에
  희생이 따르긴 하지만 메모리 정도는 업그레이드 할수있었다면 좋지 않았을까 생각합니다.
  천년만년 쓸것은 아니지만 저는 개인적으로 SSD에 쓰기를 자주 하지 않으려고 하는편이고
 (이건 사실 별 의미 없는 짓일지 모르지만 예전 SSD가 나온지 얼마 안됐을때는 웨어레벨링
  기술이 떨어져 일정량 쓰기를 반복했을때 SSD의 수명이 끝난다라는 글을 볼때부터 그냥
  그렇게 되버렸습니다 ;;)
  램디스크를 아주 약간 인터넷 캐쉬파일 저장용으로 사용하는 터라 메모리를 2GB정도 썼으면
  하지만 S7은 할수없습니다.. 모든것이 다 일체형이라서요.

이정도로 S7의 단점을 정리합니다. 이 세가지 약간의 불만 빼고는 S7은 훌륭합니다. 보통 넷북

이나 노트북을 처음 구입하시려는 분들이 가장 많이 찾아 보는것이 '무슨무슨 게임이 돌아가나요'

혹은 '동영상 재생 기능' 에 대해 많이 찾아보시는데 저의 경우는 노트북으론 게임 자체를 안할뿐더러

해도 정말 캐쥬얼한 게임만 해서 잘 모르겠습니다. 게임을 하기 원하신다면 넷북은 정말

추천 하고 싶지 않고 좀더 가벼운 노트북을 사는게 나을것 같구요. 동영상은 제가 화질에 크게

신경쓰지 않는편이라 S7의 막말로 좀 거지 같은 GMA500 칩셋에도 그다지 크게 불만이 없습니다.

다만 요새는 고화질 동영상 파일들이 많이 돌아다녀서 가끔 그런파일들은 끊김이 있다는것을

제외 하고 말이죠. 원래는 글을 쓰면서 S7, EEE PC의 크기비교를 우선시 하려했는데 주제를

벗어낫네요. 저는 개인적으로 어느정도 성능에 그래도 타이핑은 어느정도 편하고 조금 오래써도

눈이 덜 피로한 기기를 구입한다면 이 S7이나 비슷한 크기의 MID를 권장하고 싶고 난 작고 가벼운게

최고! 라고 생각하시는 분들은 UMID의 mBook같은 제품들에 관심을 두시는것이 좋을듯 싶습니다.

신고

블로그에 거의 반년만에 글을 남겨봅니다. 요새 스마트폰 관련해서 하이브리드 어플을 개발중이라

많이 바쁘네요. 많은 개발자분들이 그러하듯 야근 야근 야근이 계속 되고있습니다.ㅜ_ㅜ 그래도

이번주에는 조금 풀려서 블로그도 한번 들러서 글을 써보게 되네요 ㅎㅎ. 사설은 여기까지 하고..

최근에 개발을 하면서 서버쪽으로 해봐야 될게 있어서 집에서 뒹구는 펜티엄3, 펜티엄4 PC에

각각 윈도우, 리눅스를 설치해서 APM서버설치, FTP서버를 설치했습니다. 각각의 사양은

아래와 같습니다.

Pentium III PC

CPU - Intel Pentium III 750Mhz(소비전력량이 성능에 비해 적은 편입니다. 19.5W)
RAM - PC100 256MB(원래는 96MB였는데 2천원 주고 업그레이드 했네요..;;)
HDD - 삼성전투기 20GB(과거 삼성 HDD소음은 알아주죠ㅎㅎ)

Pentium 4 PC

CPU - Intel Pentium 4 1.4GHz
RAM - PC133 512MB
HDD - 시게이트 20GB

보시다 시피 요즘 PC들에 비하면 초라하기 그지없는 사양입니다만 개인용도 서버로 쓰기엔

훌륭한 사양입니다. 이 두 PC에 서버프로그램들을 설치하고 속도테스트를 해봐야 되서

이것저것 하던중 전에는 소음,전기세 압박문제로 24시간 서버를 돌렸다가 닫았었는데

문득 펜티엄3 PC정도면 소비전력량이 많이 줄겠다는 생각이 들더군요. 그래서 현재 이 PC는

베란다에 내놓고 서버가동중입니다. 이렇게 구축하고 나서 이래저래 검색하다보니 신기한 기기가

눈에 띄더군요.. 바로 이겁니다.

사용자 삽입 이미지

Pogoplug - Home Server
(이미지 출처 : http://internet.watch.impress.co.jp)


처음 봤을때는 뭐하는 물건인가 싶었습니다. 쉽게 개인용도용 NAS라고 보시면 되겠습니다.

어디서나 인터넷만 된다면 저 Pogoplug를 설치해놓고 접속해서 파일을 공유할수 있습니다.

이런기능이야 별거 아니긴 하겠지만 위에서 제가 주절주절 떠들었던 이유는 바로 홈서버로

PC를 쓸경우 대부분 사용용도로 파일서버, 웹서버 위주로 사용하게 되는데 제 목적에 가장

부합하는 것이 바로 이런 기기였습니다. 회사에서 접속해서 집에있는 파일을 가져와야 될때도

있고 외부나갔을때도 유용하게 쓰일수 있겠죠. 이놈에 대해 좀더 자세히 봐보겠습니다.

사용자 삽입 이미지

크기가 아주 작습니다. 흔히 볼수 있는 가정용 공유기 크기보다 약간 작다고 보시면 되겠습니다.
사용자 삽입 이미지

그렇다면 사양은 어떨까요?

Power requirements: 100-240V, 50/60HZ

  • Drive connection: USB 2.0
  • Drive Formats: NTFS, FAT32, Mac OS Extended Journaled and non-Journaled (HFS+),
    EXT-2/EXT-3
  • Network connection: Gigabit Ethernet
  • Operating Systems: MS Windows XP, Vista, Windows 7, Mac OSX 10.5 and above
    (Intel and PowerPC) 32bit kernel only, Linux
  • Web browsers: Safari, FireFox 3, IE 7, IE 8, Chrome

  • 위는 제작사에서 밝히는 상세스펙입니다. 보시면 알겠지만 많은 디스크 포맷과 운영체제를 지원

    하고 기가비트 이더넷 사용이 마음에 드는군요.

    사용자 삽입 이미지
    실사용 예


    위와같이 자료는 USB외장하드나 USB스틱등에 넣어두고 랜선은 공유기나 직접 회선에 꼽아

    사용할수 있게 되있습니다. 현재 시중에 일반적으로 판매되는 NAS들이 그러하듯 이 녀석도

    내장 브라우저를 사용합니다. 가장 유심히 봤던 부분은 전력사용량인데 대기시 5W 이하라고

    하네요. 한가지 제일 중요한게 빠졌는데요. 가격은 꽤 비싸네요 $129에 팔리고 있습니다.

    하지만 아직 국내에선 구입할수 없네요. 뭐 이베이나 아마존등을 통해서 살수야 있겠지만

    너무 비쌀것 같습니다. 국내에 출시 된다면 가정용 서버에 관심있으신 분들에게 많이 팔릴것

    같네요. 저 또한 구입해보고 싶네요. 물론 저 정도 사양의 미니 NAS로 고성능을 바라면

    안되겠죠. 이 제품은 작은 사무실이나 소수의 사람들과 파일공유를 할때 쓰면 좋을듯 싶습니다.

    아직 국내판매되고 있진 않지만 이제는 들고다닐수 있는 NAS가 있다는 것만으로도 많은

    관심을 갖게 하네요. 어쨋거나.. 현재 저는 그냥 펜티엄3에 FTP,WEB서버 구축해서 돌리고

    있습니다. 자료도 별로 없지만 써보실 분은 써보세요.

    http://xens.iptime.org

    웹서버에서 FTP로 자동 포워딩 시켜줍니다. FTP로 바로 접속할분은 ftp://xens.iptime.org 로
    가시거나 FTP클라이언트 (알FTP등..) 를 사용하시면 되겠습니다.

    p.s : 원래는 dnip.net 의 서비스를 애용했었는데 작년 즈음해서 DNIP가 문을 닫았더군요.
    아쉽습니다..


    신고

    예전에 도스시절에 사용했던 명령어 COMMAND.COM처럼 윈도우에서도  CMD.EXE 라는 콘솔

    을 통해 윈도우 명령을 처리할때가 있습니다. 특히 포터블 프로그램들이 CMD창을 많이 사용하게

    되는데 며칠전에 CMD창을 실행하니 오류코드(0xc0000142) 와 함께 실행을 할수없다는 오류를

    내더군요. 어이없는건 직접 %Windir%system32  폴더에 들어가서 실행해보면 멀쩡하게 실행된다는

    것입니다. 얼마전 Tiny7을 EeePC에 설치하고나서 생긴문제인데 자주쓸일은 없지만 위에서 말한것

    처럼 포터블 프로그램이 CMD창을 통해 중간명령을 수행하는경우가 많아 꽤나 불편했습니다.

    검색해보니 역시 뻔한답이 대부분이더군요. '바이러스 검사, 악성코드 검사, 보안패치 설치여부확인'

    하긴 시도할수 있는 해결방법은 일단 저것밖에 없었습니다. 몇개씩 돌려봤지만 악성코드라던지

    바이러스는 없었습니다. 하지만 재부팅을 여러번 해봐도 결과는 같았습니다. 그래서 일단 해본것이

    기존 CMD 삭제후 다른 PC의 CMD를 복사해 오기로 했습니다.

    (이 방법은 바이러스,악성코드에 피해를 입은 경우 적용가능합니다.)

    사용자 삽입 이미지
    다음과 같이  CMD.EXE에서 속성을 보시면 보안탭에 고급이 있습니다. 그냥 복사해서 붙여넣기
    하려고 하면 TrustedInstaller의 권한때문에 수행할수 없다고 뜨므로 권한을 편집하기 위해서입니다.

    사용자 삽입 이미지

     현재 사용자가  Administrator로 되있으므로 이 계정이 모든권한을 가질수 있도록 사용권한변경
     버튼을 눌러줍니다.

    사용자 삽입 이미지

    편집(Administrator) - 모든 권한에 체크해주고  OK해준뒤 기존의 CMD파일을 삭제하고 복사해

    봅니다.여기까지는 실제로 CMD파일이 악성코드나 바이러스로 망가졌을때 쓸수있는 좋은 방법

    입니다. 고치고 나서야 알았지만 저의 경우는 실제로 망가진것이 아니라서 이렇게 해도 전혀 변화가

    없었습니다.

    (0xc0000142 오류가 날경우 해결방법)

    사용자 삽입 이미지

    시작 - 실행 - regedit 를 실행해서

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]

    로 이동해보면 위와같이 ACP,MACCP,OEMCP,OEMHAL이 있습니다. 영문윈도우의 경우와

    한글윈도우의 경우 위의 수치가 다른데 ACP는 기본 안시코드를 표현한것이고 OEMCP는

    OEM기본 코드페이지를 나타냅니다. MACCP는 매킨토시 기본코드페이지를 나타내고 OEMHAL에

    정의된 값은 명령프롬프트(CMD)에서 확장된 문자들에 대해 제어를 합니다. 근데 위에 보시면

    ACP,OEMCP의 값이 949로 되있는데 이는 한글을 나타냅니다. Tiny7의 경우 기본적으로 영문

    으로 코드페이지가 설정되있는데 한글언어팩을 적용하면 위의 수치로 바뀌게 됩니다. 저의 경우

    문제는 커맨드창의 글꼴을 바꾸면서 생겼는데 일단 저의 경우외에도 이 값을 기본값으로 바꿔

    준다면 이상없이 실행될것으로 예상됩니다. 그래서 일단 ACP를 1252(영문포함 웨스턴유럽어)로

    바꿔주고 OEMCP값을 Windows NT 계열의 OS에서 쓰이는 기본 DOS 437로 바꿔줍니다.

    바꿔준뒤 재부팅 하면 커맨드창이 제대로 실행되는것을 보실수 있습니다.

    사용자 삽입 이미지

    재시작후 CMD창이 제대로 실행되는것을 확인한뒤 다시 ACP,OEMCP 수치를 949로 바꾸고 재부팅후

    커맨드창의 속성을 확인하시면 위와같이 현재 코드 페이지가 949로 되있는것을 확인할수 있습니다.

    신고
    오랜만에 글을 씁니다. 글 쓸거리는 많았지만 항상 귀차니즘에 올린적이 없네요. 근데 이번글은 꼭

    올려둬야 속이 시원할것 같아서 올립니다. 윈도우 비스타가 ME꼴이 되서 망하고 윈도우7이 호평을

    얻고 있습니다. 저 또한 학생이 아니지만 아는 사람에게 부탁해서 아카데미판을 한 카피 구입해서

    쓰고있습니다. 한참 전에 올렸던 윈도우 7 베타 설치기에서도 밝힌것과 같이 비스타의 실패를 만회

    하려는듯 상당부분이 개선된것을 알수 있습니다. 제 경우에는 메인PC에서 비스타를 사용할때와

    7의 차이는 반응성이 빨라졌다는것이 좋은점인것 같습니다. 그 외에는 크게 다른점은 못느꼈습니다.

    메인PC사양이 충분하기 때문에 그랬을 것이라 생각합니다만 EeePC나 기타 아톰 플랫폼 넷북들은

    어떨까요? 전에 올린 글에서 보시면 일단 윈도우 폴더의 WinSxS폴더 크기의 압박으로 용량상의

    문제가 심각합니다. (좀더 자세한 내용은 바로위의 링크 참조) 그리고 시각효과를 다 빼버려도

    쓰다보면 비스타 특유의 딜레이가 XP로 롤백하게 하는 큰 이유중 하나라고 생각하네요. 어쨋든

    EeePC 701에서도 윈도우7을 사용해보기위해 처음에는 vLite를 사용해서 정품 윈도우7 Ultimate K

    를 이미지로 만들고 설치 시도했습니다. 결과는 블루스크린... 다시 만들다 보니 이번엔 vLite에서

    에러.. 여러번 시도하다가 베타(빌드 7100)으로 시도해보니 추출,설치 모두 잘되지만 아무래도 꽤

    오래된 베타판이라 쭉 쓰기엔 꺼림칙한 감이 있어 RTM(빌드 7600)으로 시도하니 역시 블루스크린..

    하루를 날려버리고 원인을 찾기도 귀찮아져서 열심히 구글링 했습니다. 아시는분들은 이미 알고

    계시더군요. 외국에서 영문 윈도우7으로 Tweak된 Tiny 7 이라는 개조된 윈도우7이 있었습니다.

    이미 국내 윈도우포럼등에서도 많은 분들이 사용하고 있었는데 모르고 있었습니다.

    진작에 한번 찾아볼껄 ㅡㅡ; 일단 최근에 출시된 넷북들은 저용량 SSD대신 거의 모두 HDD를

    장착하고 있기때문에 이런 넷북들에서 Lite에디션을 쓰는 이유는 속도,반응성이 최우선이겠지만

    EeePC 701의 경우 ASUS에서 넷북 사업을 시작하면서 701 2G Surf등의 모델과 함께 실험적인

    모델들이었고 애초에 출시될때 일반 XP설치도 경량화 시키지 않으면 실사용에 곤란함이 많은

    모델이었기 때문에 가능한 최대로 경량화된 윈도우7이 필요했습니다. 이렇게 까지 해서 꼭

    윈도우7을 써야하나 싶기도 했지만 일단 써보니 좋네요. 설치방법을 쓰기전에 결과부터 쓰자면

    오히려 XP보다 반응성이 좋습니다. (여러가지 셋팅을 마친상태입니다. 불필요한 기능은 꺼버렸죠.)

    램값이 쌀때 2GB로 업그레이드 하지 않아서 1GB로 쓰고있음에도 불구하고 상당히 빠른 반응속도를

    보여주더군요. 사설이 너무 길었는데 이제 설치방법을 보여드리겠습니다.

    준비물 : Windows.Tiny7.Rev01.Unattended.Activated.CD-eXPerience
                *(이 키워드로 구글링 해보시기 바랍니다. 외국에서 배포되서 아마 torrent를 통해서
                   받으시게 될겁니다. 현재 Rev01,Rev02버전으로 나눠져있는데 Rev02버전은 12월
                   초에 Rev01에서 몇가지 버그를 수정한 버전입니다. 가능하면 Rev02버전을 찾으시기
                   바랍니다.)


                한글화를 위한 툴(Visualizator)와 한글키 입력,표시를 위한 툴
                (스누피님이 만드신 KORKBD.EXE 파일이 첨부되있습니다. Tiny7이 IME2002,2003등의
                 레지스트리 정보를 추가해도 한글입력이 되지 않는데 이 파일을 적용시키면 잘됩니다.
                 찾아봐도 링크가 다 깨져있어서 올렸지만 문제가 된다면 삭제하겠습니다.)


    이 두개만 있으면 됩니다. 만약 Tiny7을 못구하셨다면 Tiny7처럼 경량화 해놓은 윈도우7을 EN,RU

    판으로 쉽게 구하실수 있습니다. 찾아보시기 바랍니다. 제 경우에는 처음에는 Tiny7으로 설치했다가

    몇가지 맘에 들지 않는부분이 있어서 비슷한 Lite버전으로 재설치 했습니다. 저는 이미 제 EeePC 701

    에 적용해놨으니 VMWare에 설치해보겠습니다. VMWare사용법은 이 글을 참고하세요.

    (설치과정은 기존 윈도우7과 다를것이 없으므로 생략합니다.)

    사용자 삽입 이미지
    보시면 설치에 요구되는 용량이 4962MB로 나와있지만 신경쓰실 필요없습니다. 넘어가시면 됩니다.

    넷북은 ODD가 없는경우가 대부분이라 설치할때 많은 부분 어려움이 있을텐데요. 제경우에는 젠더가

    있어서 DVD-RW를 직접 USB로 연결해서 설치했기때문에 어려움이 없었습니다. 아니면 USB스틱등

    에 심는수밖에 없겠죠. 써보진 않았지만 필요할지 몰라서 받았던 파일도 첨부합니다.

    [##_1C|cfile22.uf@19721A0B4B36E82B503C2E.rar||_##]
    (사용법은 압축파일 내에 있습니다. 그림파일도 있으니 크게 어렵진 않을듯 합니다.)

    사용자 삽입 이미지
    설치가 끝난후 초기 부팅화면입니다. Activation 화면에서 조금 오래 걸립니다. 절대 커맨드창을

    닫으시면 안됩니다. 액티베이션이 완료되면 자동으로 윈도우를 재시작 하게됩니다.

    사용자 삽입 이미지
    액티베이션 완료후 초기화면입니다. 이 에디션이 나쁘다는것이 아니라 개인적으로 너무 Tweak된듯

    한 윈도우를 별로 좋아하지 않아서 제 EeePC에는 다른버전을 사용했습니다. 개인적인 취향이므로

    성능등에는 아무 차이가 없음을 미리 밝힙니다.

    사용자 삽입 이미지
    이제 설치는 끝났지만 한글이 문제입니다. 보시다시피 Locale이 틀려서 한글이 모두 깨지는것을

    보실수 있습니다. 또한 한글을 사용하는 프로그램들이 설치는 잘되지만 표시언어가 모두 깨져서

    나옵니다.

    언어팩 적용하기
     가장 중요한 부분입니다. 반드시 순서대로 하셔야 합니다. 저 또한 다른 설치방법글을 보고 했지만
     위에서 언급했던 다른 에디션에서 생각대로 되지 않아 Vistalizator를 사용해서 설치했고 잘됩니다.
     
    준비물에 첨부되있는 파일을 열어보시면 Vistalizator가 있습니다. 언어팩을 설치해주는 툴인데

    언어팩을 사이즈가 45MB정도 되는 관계로 첨부 하지 않았습니다. 아래의 키워드로 검색바랍니다.

    (32bit KO-KR)
    windows6.1-kb972813-x86-ko-kr_bb2de1652c37394be4de87438661b129f8b56908.exe

    사용자 삽입 이미지

    언어팩이 준비 됐다면 위와 같이 Control Panel - Region and Languages에서 Formats를 Korea로

    나머지 항목들도 전부 Korea 혹은 Korean 으로 바꿔줍니다. 키보드 셋팅부분은 안바꾸셔도 됩니다.

    사용자 삽입 이미지

    시스템 로캐일 설정부분입니다. Korean으로 바꾸고 나면 재시작 할지를 묻는데 재시작 하시면

    안됩니다. 선택만 하시고 재시작만 하지 마세요.

    사용자 삽입 이미지
    파일중 fonts를 다음과 같이 Install로 설치해준뒤 Vistalizator를 실행시킵니다. 실행시 Ultimate은

    이 프로그램이 필요없다고 하는데 그냥 넘어가시면 됩니다. 원래 Vistalizator는 다국어 지원이

    안되는 홈프리미엄등의 에디션을 위해 만들어진 프로그램입니다.
    사용자 삽입 이미지
    프로그램 상에서 Add Language를 누르면 다음과 같이 cab이나 exe를 선택하라고 나옵니다.

    lp.cab이란 이름으로 랭귀지팩을 따로 CAB파일로 묶어놓은 파일을 구할수도 있지만 제경우에

    실패한 경우가 몇번 있었습니다. 어짜피 Vistalizator에서 자동으로 변환해서 적용하기 때문에

    exe파일을 선택합니다.

    사용자 삽입 이미지
    설치시 매우 중요한 부분입니다. EXE파일을 변환하고 무결성 검사후 다음과 같은 화면이 뜨는데

    여기서 Mode부분을 더블클릭해서 Express모드로 바꿔주고 Silent Installation에 체크한뒤

    Install Language를 하셔야 합니다. 그냥 Install Language하실경우 제대로 설치되지 않습니다.

    사용자 삽입 이미지
    4단계 설치를 마치면 다음과 같이 한글언어팩이 설치되있는것을 보실수 있습니다.

    Change Languages를 눌러 언어팩을 전환한뒤 Exit를 누르면 자동으로 윈도우를 재시작 합니다

    Exit를 누르기전 New Language가 Korean으로 바뀌었는지 확인해 보세요.

    사용자 삽입 이미지
    메뉴가 한글로 표시되는것을 확인하실수 있습니다. 하지만 완벽한 한글 지원은 아닙니다. 일부분

    영어로 메뉴가 표시되는곳이 있기때문에 여러군데 글을 참고해보면 한글팩 적용후 영문MUI는

    지우는것이 좋다라는 의견이 많은데 이경우에 영어팩 삭제가 불가능한것 같습니다. 좀더 알아봐야

    겠지만 일단은 한글 표현이 이상없다는것으로 넘어가겠습니다.

    사용자 삽입 이미지
    첨부된 IME2002 한글입력 레지스트리 패치를 합니다. 압축해제후 실행만 하면 추가됩니다.

    사용자 삽입 이미지
    IME적용뒤 KORKBD를 실행합니다. 위와같이 복사뒤 '국가 및 언어설정' 메뉴가 자동으로 실행됩니

    다. 여기서 한글입력기(IME2002)를 추가하고 재시작 때문에 바꾸지 않았던 시스템 로캐일도 바꿔

    줘야 합니다.

    사용자 삽입 이미지
    위와 같이 키보드 설정에서 추가 버튼을 눌러 한국어 트리 하위메뉴의 한국어 입력 시스템(IME2002)

    를 선택하고 확인합니다.

    사용자 삽입 이미지
    또한 위와같이 기본으로 필드에 입력될 언어를 한국어(IME2002)로 바꿔준뒤 OK버튼을 눌러줍니다.

    언급했던것처럼 이렇게 확인,취소,적용으로 떠야할 버튼들이 영문으로 뜨고있습니다. 사용상 불편은

    없지만 용량을 최대한 줄이자면 영문 언어팩을 삭제해야할텐데 더 알아봐야할 부분입니다.

    사용자 삽입 이미지
    여기 까지 해놓고 보시면 노트패드등에서 위와같이 한글입력시 깨지는 것을 보실수 있습니다.

    서식 표현의 문제일 뿐이므로 다음과 같이 글꼴 - 서식에서 유니코드 글꼴로 바꿔주시면 됩니다.
    사용자 삽입 이미지
    사용자 삽입 이미지
    익스플로러에서 한글을 입력해보면 잘 입력되는것을 보실수 있습니다.

    설치후 용량은 얼마나 될까요?

    사용자 삽입 이미지
    설치후 3GB정도의 용량을 사용합니다. EeePC 701에서는 XP lite버전 설치후 1.8~2GB정도의

    용량이 남는데 윈도우7이 비스타와 마찬가지로 기본설치시 10GB이상을 요구하는 OS인 만큼

    이정도의 용량을 사용하는것은 상당히 메리트 있다고 할수있겠습니다. 물론 윈도우7의 부가

    프로그램들은 거의 쓸수없긴합니다만 제 경우에는 서드파티 유틸리트를 자주 사용하고 윈도우

    기본기능은 뭔가 하나씩 불편하거나 잡다한 파일을 많이 만들어대서 잘 쓰지 않기때문에 상관

    없습니다. 또한 이런 저용량 넷북의 활용은 웹서핑,문서작업등의 캐쥬얼한곳에 중점을 두고 사용

    하는것이 맞겠죠. 그건 그렇고 Tiny7에는 비스타 Lite처럼 문제가 있습니다. 바로 WinSxS폴더

    그리고 윈도우 업데이트 입니다. WinSxS폴더에 대해서는 링크를 참고하세요.

    사용자 삽입 이미지
    몇가지 업데이트는 설치가 되지 않습니다. 보시면 Tiny7의 경우 WinSxS폴더가 업데이트가 진행되도

    기본설치한 윈도우7에 비해 용량변화가 크지 않은것을 확인하실수 있습니다. 필수적인 업데이트만

    진행되도록 락을 걸어놓은듯 싶으나.. 자세한 내용은 확인한 바 없고 추측일 뿐입니다. 위의 경우

    12월 악성코드 제거 업데이트,보안 업데이트를 제외하고 익스플로러 관련 업데이트는 모두 설치가

    불가능하게 되있습니다. 추측이지만 익스플로러를 굳이 사용할 필요없이 파이어폭스,구글 크롬등의

    최신 브라우저를 사용하는것이 외국환경에서는 오히려 편하기 때문에 이렇게 해놓은 것이 아닐까

    생각해보면서 저 또한 넷북으로는 익스플로러보다 파이어폭스,오페라를 애용하기 때문에 덩치만

    커진 익스플로러를 고집할 이유는 ActiveX밖에 없다고 생각하네요.

    여기까지 Tiny7이나 기타 영문버전 윈도우Lite버전 설치, 한글입력문제 해결법등을 알아봤습니다.

    이런짓(?)을 할때마다 느끼지만 한국에서도 윈도우와 어깨를 나란히 하는 운영체제가 나왔으면

    하네요. 여담이지만 올해 여름쯤에 티맥스 윈도우 때문에 떠들썩했는데 시도를 했다라는것에

    주안점을 두고 발표했더라면 괜찮았을것을 곧 티맥스 윈도우가 상용화될것처럼 서둘러 발표하면서

    많은 오류를 범해 국내OS개발에 대한 사기를 무너뜨린것이 아닌가 싶습니다. 여하튼 Tiny7 혹은

    비슷한 다른 에디션을 넷북이나 저사양PC에 설치하면 상당히 원활하게 쓸수 있을듯 합니다.

    언어팩 삭제 문제나 다른 자잘한 문제들은 사용해 보면서 좀더 알아봐야되겠네요. 제경우엔

    이전에 사용하던 XP환경을 전체백업해두고 윈도우7 환경또한 전체백업 해뒀습니다. 만약

    문제가 생기면 XP환경으로 돌아가야 될수도 있다는점을 감안하시고 설치,사용해보시기 바랍니다.
    신고

    정말 너무나 오랜만에 글을 써보는것 같습니다.. 거의 반년만인것 같은데.. 몇달간 자바를 배우느라

    안돌아가는 머리 부여잡고 고생중입니다. 다들 재미없어하는 프로그래밍 얘기를 여기다 쓸줄이야

    ㅋㅋㅋ 그런다고 잘 알면서 쓰는것도 아니고 잘못된 정보가 있을수도 있으니 보시는분들은 나름

    필터링 해서 보시면 되겠습니다. 어짜피 파일도 많고 안의 내용을 전부 글로 쓰기는 시간도 없고

    귀찮기도 해서 대략적인 파일구조만 설명하고 예제로 만든 파일을 첨부했습니다.

    1. iBatis
      iBatis를 처음접했을때는 파일갯수만 늘어나고 복잡하다고 생각했습니다.. MVC Model 1에서
     주구장창 사용해오던 DAO클래스에 너무나 익숙해져서인지 왜 쓰는지도 사실 잘 몰랐고 새로운
     시도는 항상 어려움이 있는것 같습니다. 근데 계속써보니 DAO클래스 만들어서 PrepareStatement
     줄창써서 쿼리날릴때와는 비교도 안되게 편하더군요. 생각해보면 DAO클래스 제작은 노가다에
     가까웠던듯 싶습니다. 물론 단점은 존재하는것 같습니다. DAO클래스는 자바파일이라 쿼리문
     중간에 조건문을 사용해서 다양한 기능을 만들어볼수 있었는데 iBatis를 사용하게 되면 아직
     더많은 속성들을 몰라서인지 몰라도 PL/SQL의 Function이나 Procedure를 사용해야될일이
     많아질것 같았습니다. 물론 PL/SQL을 잘 모르는 저로서는 손발이 떨릴뿐.. 그래도 단순
     히 리스트 출력,수정,삭제,삽입에는 iBatis를 사용하는것이 편한것 같습니다.

    2. Struts2
     Struts1까지는 그래도 MVC Model2를 초기에 공부할때 했던것들과 형식이 유사해서 편했
     습니다만 Struts2를 하면서 라이브러리가 개발자눈에 안보이게 처리하는 부분이 많아서
     적응하는데 좀 시간이 걸렸습니다. 에센셜한 부분에선 깡통같은 머리라 특히 Getter/Setter를
     어떻게 JSP에서 불러올수 있는지는 그냥 신기하기만 하네요. 기존에 JSP에서 Model2를 적용
     해서 만들면서 getAttribute나 setAttribute를 사용하지를 못하니 조금 답답한 면이 없지 않았
     습니다만 계속 만들어보다보니 그저 만들어보는데에는 기존방식과 크게 다르진 않더군요.
     대신 인터셉터가 들어가고 유효성검사(Validation) 체인이 걸리면 규모가 커질경우 보기에
     좀 복잡하겠다는 생각이 들었습니다.

     개발환경 : Eclipse EUROPA,Oracle 10g Database

     전체 UI :

    사용자 삽입 이미지

    모양은 별로 신경쓸 부분이 아니라 생각해서 대충.. 페이징도 없습니다.

    Eclipse Workspace:

    사용자 삽입 이미지

    전체적인 파일구조입니다. 대략적인 설명부터..

    iBATIS
    custom.dao패키지
    SqlMapConfig.xml : DB와 연결할 타입을 설정합니다. DBCP,JDBC,JNDI를 사용할수가 있는데
    ConnectionPool이 어째 잘 적용이 안되서 JDBC로 설정했습니다.

    Query.xml : SqlMapConfig.xml에서 읽어들이는 파일입니다. 여기에는 실제 쿼리문과 custom.dto
    패키지의 CusomDTO클래스 파일을 읽어들일수 있는 설정을 해주게됩니다. 파일이름은 아무렇게나
    만들어도 상관없습니다. 편의를 위해 Query.xml로 했습니다.

    CustomDAO.java : struts1이나 MVC Model1에서 사용하던 DAO클래스와 같은 기능을 담당
    합니다. 물론 이름은 임의로 줘도 상관없고 여기서 SqlMapConfig.xml을 파싱하게됩니다.
    Query.xml은 SqlMapConfig.xml에서 읽어들이기 때문에 같이 파싱이 되겠죠. 이 파일내의
    각 기능을 담당하는 메소드에서 Query.xml에 정의한 실제쿼리문에 대한 id를 참조하게 됩니다.

    Struts2
    struts.xml : Struts2의 각 액션에 대한 정의를 해주는 파일입니다. Struts1에서는 struts-config.xml
    로 정의되있습니다. struts.xml에서는 라이브러리내의 struts-default를 상속받아 인터셉터나 기타
    기능등을 사용할수가 있으며 이 struts-default는 재정의 가능합니다. 또한 라이브러리 내의
    default.properties의 내용을 여기서 struts.properties라는 파일을 만들어서 재정의 가능합니다.
    (물론 제대로 써본적은 없습니다..;;;)

    대략적인 설명은 위와같습니다.

    소스내에 주석을 달아놨는데..개인적인 용도로 썼던주석이라 보기에 좀 거북하겠지만 저같이

    잘이해가 안되신 분들을위해..ㅋ  실행은 index.jsp로 하시면 되고 이클립스에서 war파일을

    import시키면 되겠습니다.

     
    간만에 글을 썼긴한데 설명도 별것도 없고 이상해졌네요. 주석에 모든 설명이 있긴합니다..

    불확실한 정보지만.. 상당히 간단하게 만들었기 때문에 Struts2와 iBatis의 연동방식을

    이해하는데에는 괜찮다고 생각됩니다.

    다음에는 제 개인적 정리 차원에서 iBatis만 다뤄보도록 하겠습니다.

    p.s : 깜빡하고 DB테이블을 넣질 않았네요.. DTO클래스에서 정의된 내용을 가지고 테이블 만드시면
    되겠습니다. 혹시 필요하신분은 댓글 달아주세요~(물론 오라클이 설치되있다고 가정합니다.-0-
    참고로 번호값을 받아올때 시퀀스를 생성하지 않았습니다..)

    신고