Search

'전체'에 해당되는 글 119건

  1. 2014.10.17 블로그 이전했습니다.
  2. 2014.08.13 WWYD vs 젠틀맨
  3. 2014.08.08 뼈의 소리 - 이와아키 히토시
  4. 2014.08.05 과학이란 무엇인가 - 파인만
  5. 2014.07.30 FIFA14 - 내게 4-2-3-1 시스템은 왜 어려운가
  6. 2014.07.28 FIFA14 - 리그 전체를 고려해 주력 포메이션을 설정하자
  7. 2014.07.25 FIFA14 - 종적으로 길게 늘어진 포메이션의 위험성
  8. 2014.07.24 FIFA14 - 선수들의 WorkRate를 고려해 포메이션을 짜자 (2)
  9. 2014.05.21 김진표. 보육교사 공약에 대해. (3)
  10. 2014.02.25 강신주 비교 (7)
  11. 2013.11.17 플루토크라트 - 난 슈퍼스타를 원하지 않아!
  12. 2013.11.12 무리한 설정이 어떻게 소설을 망치는가 - 베르나르 베르베르의 제3인류 (6)
  13. 2013.04.07 착하고 온순한 나를 화나게 만든 책, 가라타니 고진의 "근대문학의 종언" (4)
  14. 2012.11.09 종교를 만들 때 적용해야 할 13가지 원칙. (4)
  15. 2012.10.30 진중권 vs 간결 토론 시청 후기 (2)
  16. 2012.08.27 Detours Hooking Framework - 3-3. Hooking virtual function
  17. 2012.07.06 성경을 어떻게 읽어야 하나.
  18. 2012.06.25 6월 4째주 독서노트 - 에덴의 용, 의식 (1)
  19. 2012.06.14 6월 셋째주 독서 노트 - 베네치아에서의 죽음, 토마스 만
  20. 2012.06.08 6월 첫째주 독서노트
  21. 2012.02.17 boost, io_service 그리고 io_service.run() (3)
  22. 2012.01.27 체리신드롬, 금기는 즐거움이 된다. (8)
  23. 2012.01.09 기억의 습작, 김동률
  24. 2012.01.09 약자의 사랑, 강자의 사랑
  25. 2011.07.29 Detours Hooking Framework - 3-2. Hooking class member function (6)
  26. 2011.07.15 Detours Hooking Framework - 3-1. Hooking class member function
  27. 2011.07.12 Detours Hooking Framework -2. Hooking API (3)
  28. 2011.06.16 Detours Hooking Framework - 1. hooking function (13)
  29. 2011.04.20 Know your enemy
  30. 2011.04.10 DataGrid and Dynamic Column -- Part2

블로그 이전했습니다.

분류없음 2014.10.17 20:44 Posted by 아일레프

blog.illef.kr로 옮겼습니다. 

저작자 표시
신고

WWYD vs 젠틀맨

끄적끄적 2014.08.13 21:49 Posted by 아일레프


약자에 대한 폭력을 다루는 법, 한국과 미국 방송의 차이 글을 통해 WWYD 보게 되었고 적지 않은 감동을 받았다. 감동은 나로 하여금 한국 자막과 함께한 WWYD 모두 찾아보게 만들었다. 그리고 이후 프로그램을 모방한 한국의 젠틀맨을 YouTube에서 찾아보았다. PPSS 글처럼 젠틀맨이 편견을 조장한다고 느끼진 못했지만 분명 WWYD 젠틀맨은 차이점이 보였다WWYD으로 감동을 느꼈으나 젠틀맨을 통해선 그럴 없었다. WWYD를 본 뒤에는 생각에 잠깐 잠기기도 했으나 젠틀맨을 본 뒤에는 그딴거 없었다.

 

그랬을까? 같은 형식의 프로그램인데 그런 것일까?

 

그것을 '대화' 유무에서 찾는다. WWYD에서는 대화가 있으나 젠틀맨에서는 대화가 없다. WWYD에서는 시민들의 생각과 철학을 그들의 말로 느낄 있으나 젠틀맨에서는 시민들의 '' 느낄 있을 뿐이다. 그럴까? 미국에선 토론을 접할 일이 한국보다 많기에 그런 것인가? 한국인은 조곤조곤 말을 하는 것보다 화를 많이 내는 민족이라 그런 것인가?

 

그렇지 않다. 그것은 WWYD이 시민들의 참여만이 아닌 '생각'을 하도록 유도하기 때문이다. WWYD 배우는 마치 훈련받은 사회자와 같은 대사를 한다. 그리고 자기가 왜 이렇게 행동하는지 이유를 말한다. 

 

"제가 잘못된 생각을 하는 건가요?"

"여러분도 그렇게 생각하지 않으세요?"

"전 .... 라고 생각해요"

 

결과 WWYD 속에서 시민은 화가 와중에도 자기 생각을 펼칠 기회를 갖게 되었다. 그들은 약자와 차별받는 사람들의 권리에 대해 논할 기회를 갖게 되었고, 그것을 보는 우리도 주제에 함께 참여해 생각해볼 기회를 갖게 되었다. 배우를 꾸짖는 것뿐 아니라 조심스레 설득하는 목소리를 WWYD 통해 들을 있고, 또한 그들에게 설득되고 감동을 느낄 기회를 얻는다.

 

WWYD 달리 젠틀맨 속의 배우는 시민의 생각을 묻는 대신 한껏 높아진 목소리 톤으로 시민들의 화를 돋운다.

 

"내가 못된 사람이란 거에요?!"

"내가 잘못한 거에요?!"

 

결국 시민과 젠틀맨을 보는 우리가 얻을 있는 것은 '' 내거나 시민과 같이 '어이없는 표정' 지을 기회 뿐이다.

 

젠틀맨은 WWYD 같이 만들면 우리 사회에 생산적인 담론을 끄집어낼 있는 좋은 통로가 있다. 우리 사회에도 미국의 인종차별, 동성애 등과 비견할 만한 충분한 이슈들이 넘친다. 그러니까, 결론은, 모방하려면 제대로 모방하자. 채널 A 에게 이건 너무 무리한 요구인가? 그래도 애써보기 바란다.



저작자 표시
신고

뼈의 소리 - 이와아키 히토시

즐거운 책 이야기 2014.08.08 20:59 Posted by 아일레프

뼈의 소리

이와아키 히토시 지음 | 김완 옮김
애니북스 2006.08.16
펑점

  생애 최고의 만화 '기생수' 작가 - 이와아키 히토시의 초기 단편집 '뼈의 소리' 읽었다짧은 7개의 단편으로 이루어진 책은  개의 단편을 제외하면 일관되게 '인간의 조건' 관해 묻고 있다.

 

연민을 느끼지 않는 인간을 인간이라   있는가?

슬픔을 느끼지 않고눈물을 흘릴  없는 인간을 인간이라 부를  있는가?

 

 대상이 타자라면 그를 싸이코패스소시오패스라고 말하며 넘길  있겠지만만약 스스로가 그렇게 감정이 말소된 사람이라면 그땐 어떻게 해야 하나그런 자신을 용서하고 받아들일  있을까?

 

 질문에 관심이 있다면 히토시의 이야기를 들어보자 질문에 대한 그의 응답이 무엇인지  기울여 보자거친 그림체와우울한 서사가 책을 지배하지만 마지막 장을 덮은 뒤에는 왠지 모르게 가슴이 크게 뭉클해잠시나마 요동침을 느낄  있을 것이다


저작자 표시
신고

과학이란 무엇인가 - 파인만

즐거운 책 이야기 2014.08.05 18:36 Posted by 아일레프

파인만의 과학이란 무엇인가?10점

 















http://illef.tistory.com2014-08-05T09:35:400.31010

저명한 파인만이 쓴 "과학이란 무엇인가"를 읽었다. 얇은 책이고, 파인만의 강의를 책으로 옮긴 것이기에 한장 한장이 쉽게 넘어간다는 것이 이 책의 매력이다. 하지만 강연을 책으로 옮겼기 때문에 간혹 논리적으로 앞뒤가 맞지 않는 경우가 있다는 점과 그의 논거를 뒷받침 하는 예가 필요 이상으로 많이 나와 얇은 책을 더 얇게 만들었다는 점은 날 아쉽게 했다.  

자, 과학이란 무엇인가? 파인만은 이 과학을 3가지 항목으로 분류한다. 새로운 사실을 이끌어 낼 수 있는 방법론과 그때의 사고의 흐름을 과학이라 볼 수 있으며 둘째로 관찰을 바탕으로 특정 규칙을 끄집어내는 것이며 마지막으로 그 규칙을 바탕으로 새로운 기술을 발명하는 것. 이 3가지가 그가 생각하는 과학이다. 여기서 인상적인 것은 첫 번째 사항이다. 새로운 사실을 이끌어 낼 수 있는 방법론, 과학적 규칙을 이끌어 내기 위한 사고의 흐름, 그 자체가 과학이라고 볼 수 있다는 것이다. 파인만은 이것에 대해서 적지 않은 시간을 할애해 설명한다. 이것은 파악되고 설명된 것에 만족하지 않고 그것을 끊임없이 의심하는 것에서 시작한다. 과학자에게 법칙이란 다시 반박당하고 다시 설명되어야 할 사실에 불과하다. 파인만은 이것을 과학의 불확실성이라 설명한다. 그렇다. 우리가 진리라고 믿고 있는 규칙은 언제든지 변할 수 있다.

 과학의 두 번째 성질은 관찰한 사실을 바탕으로 규칙을 만드는 것이다. 그런데 같은 것을 나타내더라도 좋은 규칙과 더 좋은 규칙이 있다. 누군가 태양을 관찰했다고 해보자. 그리고 이런 규칙을 만들었다. "태양은 지구보다 크다." 이것은 훌륭한 규칙이다. 하지만 다른 누군가가 이렇게 말했다고 해보자. "태양의 크기는 지구보다 크며 그 크기의 차는 109배이다." 어떤가? 이는 전자보다 더욱 훌륭한 규칙이다. 왜냐면 첫째로, 이것은 이전 규칙보다 더 명확히 태양이 지구보다 얼마나 큰지를 설명한다. 그리고 두 번째로 이것은 전자보다 더 반박당하기 쉽기 때문이다. 첫 번째 규칙은 태양이 지구보다 크기가 같거나 작다는 사실이 발견될 때만 반박된다. 하지만 두번째 규칙은 "그 크기의 차이가 110배이다"라는 반박에도 열려 있다. 이것은 놀랍도록 재미있는 사실이다. 그것은 바로 명확하고 정밀한 규칙일수록 반박당하기 쉽다는 사실이다. 이것이 바로 같은 관찰에 대한 규칙의 우열의 재는 가늠자이다. 

규칙이 반박되는 것은 언제인가? 그것은 규칙의 예외가 발견될 때이다. 이미 정한 규칙에 예외 상황이 생겼을 때 과학자는 이 예외 상황을 포괄하는 새로운 규칙을 만들어야 한다. 재미있는 것이 무엇이냐면, 이 예외가 발견될 때마다 규칙이 더 복잡해지는 것이 아니라 오히려 간단해질 수 있다는 사실이다. 천동설을 예로 들어보자. 천동설이 가능하게 하도록, 그리고 그것이 사실임을 알리기 위해 천동설은 수많은 법칙으로 둘러싸여 있었다.(아닌 것을 사실로 꾸며내려면 정말 얼마나 많은 노력이 필요한지..) 하지만 지동설이 나오고 그 규칙은 몇 배로 감소했다. 그리고 케플러가 행성이 타원으로 돈다는 아이디어를 꺼내자 그 규칙은 또 다시 몇 배 감소했다. 이것은 정말 신기한 일이다. 많은 예외를 포함하는 새롭고도 정교한 규칙이 오히려 이전 규칙보다 더 간단할 수 있다는 사실은 참으로 신기하다. (케플러가 얼마나 기뻐했을지 그 마음을 짐작 할 수 있다.) 이는 나 역시 마찬가지이다. 프로그램을 만든다. 점차 시간이 지나면서 고객의 요구가 늘어나고 그에 따라 기능이 추가될 때마다 프로그램은 지저분해지고 점점 크기를 늘려간다. 그러나 언젠가 마음을 먹고 처음부터 다시! 라는 마음을 먹고 모든 요구를 관통할 수 있게 규칙을 정립하고 프로그램의 구조를 다시 잡는 순간, 프로그램은 작아지면서 오히려 모든 요구를 만족할 수 있는 프로그램이 되게 된다. 단조로움이 오히려 복잡함보다 더 많은 것을 포괄할 수 있다는 것은 참으로 신비롭다. 

파인만은 발견된 규칙을 응용해 새로운 것을 발명할 수 있게끔 하는 기술 역시 과학이라 불렀다. 여기선 기술, 그리고 과학 그 자체보다 다른 부분의 이슈가 있는데 그것은 바로 과학자의 책임이다. 신기한 일이지만 과학자가 발견하고 발명한 그것은 선과 악의 중립지대에 있다. 파인만은 과학이 발명하는 것은 결코 선과 악 중 하나가 아니라고, 그리고 그것을 사용하는 것은 바로 당신들이라고 말한다. 그리고 자신은 도덕적 책임으로부터 살짝 비켜선다. (파인만은 핵폭탄 프로젝트, 맨하튼 프로젝트에 참가한 과학자 중 한 사람이었다.) 하지만 이 부분에서는 고개가 갸웃할 수 밖에 없다. 자신이 만든 발명품이 많은 사람을 죽음으로 이끌었음을 알면서도 이런 말을 할 수 있다는 사실에도 조금 놀랍다. 난 과학자는 어떤것을 발명하면서 이것이 어떻게 쓰일 수 있을지, 그리고 만약 어떤 방법으로 쓰여선 안 되는 지를 명확히 생각하고 의견을 개진해야 한다고 생각한다. 몇몇 과학자들은 기술 자체는 더 잘 알고 있지만 그것이 어디로 갈지에 대해선 무지한 '척'한다. 이것은 바람직 하지 않다. 

마쳐보자. 파인만이 살던 시대보다 지금은 더욱 과학의 시대이다. 우리가 누리고 있는 것은 대부분 과학이 가져다준 진보의 일부분이다. 그러나 우리를 둘러싸고 있는 것은 과학이 만든 기술이지만 우리가 사는 삶의 태도는 과학적이지 않다. 우리는 빨리 믿기 좋아하고 이미 믿은 그것의 예외가 나타났음에도 불구하고 이전에 가졌던 믿음을 그대로 유지하려 한다. 우린 과학적 시대에 비과학적으로 살고 있는 것이다. 때문에 우리가 진실이라 믿고 있는 사실이 언제든지 반박 가능해질 수 있음을 깊이 알자. 성숙과 더 많은 이해는 늘 자기가 알고 있는 것이 아닌 것한테서 오니까. 그렇게 인류는 진보해 왔으니까. 



신고

FIFA 14 커뮤니티를 둘러보면 게이머들이 가장 많이 애용하고 다른 이에게 추천하는 시스템이 4-2-3-1이라는 것을 있었다. 지금은 유행이 지났는지 모르지만 최근 현대 축구에서 가장 많이 사용되는 시스템도 4-2-3-1 이다. 그런데 내겐 4-2-3-1 지독히도 맞지 않았다. 점유율은 쉽게 가져갈 있었으나 그것이 골로 이어진 적은 많지 않다. 그래서 4-2-3-1 보다 3 또는 4명의 중앙 미드필더를 사용하는 시스템을 선호했다. 하지만 시스템은 선수 일부만 활용할 밖에 없는 시스템이었고, 결과 리그 후반기에 심각한 패배를 여러 경험해야 했다. 경험은 다시 나를 4-2-3-1 돌아서게 했는데 시점에서 내가 4-2-3-1 운영하는 방식에 대해서 고민하게 하였다.

 

4-2-3-1에는 어떤 장점이 있는가

4-2-3-1 굉장히 균형 잡힌 시스템이다. 2명의 수비 미드필더는 4백을 보호할 있고 보다 안전하게 빌드업 있다. 공격 시에는 4명의 선수(RM-LM-CAM-ST) 공격 작업에 참여 있으며, LW-RW-ST 사용하는 4-3-3 보다 수비적으로도 안정적이다. 이러한 -수의 균형이 많은 사람이 4-2-3-1 기본 시스템으로 사용하는 원인이 되고 있다고 생각한다.

경기 외적인 요인도 있다. FIFA14에서 상대적으로 좋은 선수들이 많이 있는 포지션이 LM, RM, CAM인데, 이들을 효율적으로 사용하려면 4-2-3-1외에는 뚜렷한 대안이 없다. ( 4-2-3-1을 잠시 버린 대가로 나스리, 카솔라를 활용할 없었다) 4-4-2 경우는 CAM 자원을 활용할 없으며, 2CM 수비에도, 공격에도 능한 Box to Box 미드필더이어야만 하는데 이런 CM 선수들은 희귀하며, 그렇기에 당연히 비싸다.

 

4-2-3-1운영에는 어떤 단점이 있는가

그렇다면 내게 이런 4-2-3-1 맞지 않는 이유는 무엇인가. 4-2-3-1 장점을 -수의 균형이라고 이야기했는데 이것을 다른 말로 표현하면 '어중간함'이다. 공격도 어중간, 수비도 어중간. 내겐 정말 참을 없는 어중간함이다.

 

공격

먼저 공격을 살펴보자. 4-2-3-1 공격작업을 했을 문제점은 무엇인가? 이를 논하기 위해 아래 Everton 4-2-3-1 전형을 보자.

 



 

4-2-3-1에서 확실한 찬스를 만들기 위해 가장 중요한 포지션은 CAM이다. CAM 최대한 움직임을 넓게 가져가며 LM, RM, ST에게 찬스가 있도록 패스하고 때론 슛하는 것이 중요하다. 이때 4-2-3-1 문제점은 CAM RM, LM, ST 거리가 멀다는 것에 있다. 최대 4명이 공격에 관여한다고 위에 설명했지만 사실 3 그치기 쉽다. CAM LM 연계하기 위해 왼쪽에 있으면 공격 작업은 LM, CAM, ST 그치고 오른쪽의 경우도 마찬가지이다.

 



 

내가 약팀과 상대하거나 지고 있는 경기를 역전하기 위해 사용하는 4-3-2-1 비교하면 차이가 뚜렷이 보인다. 포메이션에서 가운데 자리로 모드리치를 사용하는데, 모드리치가 조금만 위로 전진하면 LF, RF, ST라는 3명의 공격수와 매우 가깝게 있을 있다. 선택지가 3이되는 것이다.

 

그렇기에 4-2-3-1에서 공격 작업은 CAM, ST, LM(RM) 3명만으로 한정된다. 여기서 조금 나아가 생각해보자. CAM -> ST 이어지는 중앙 공간 패스가 성공했다고 한다면 결정적인 찬스로 이어질 경우가 많다. 하지만 CAM-> LM(RM) 경우는 보통 측면 공간을 찢는 것이기에 그것만으로 결정적 찬스를 만들기 어렵다. 만든다고 하더라도 보통 사각 지대이기 때문에 키퍼와의 1:1 상황이라도 키퍼가 비교적 쉽게 막을 있다. 그렇기에 CAM->LM(RM) 경우 한번의 패스론 충분치 않고 한번의 좋은 패스, LM(RM) -> CAM 또는 LM(RM) -> ST 이어지는 패스가 또다시 필요하게 된다. 내가 좋은 패스를 성공할 확률이 50%라면 4-3-2-1 경우 50% 충분하지만 4-2-3-1에는 50%*50%, 25% 되는 것이다. 바로 이것 때문에 전통적인 윙어 보다 가운데로 커트인 하는 -포워드가 현대 축구에서 각광받고 있는 것이다.

 

잠깐, 그렇다면 LM, RM에게 가운데로 커트인 하는 비율을 높이도록 FIFA14에서 전술을 설정할 있지 않을까? 다음과 같이 말이다.

 



 

위는 Player Positioning으로 가운데로 커트인 하는 움직임을 것을 주문한 설정 장면이다. 하지만 실험해본 결과 LM(RM) 가운데로 커트인 하면 ST LM(RM) 위치로 이동해 결국 단순한 스위칭 효과밖에 거두지 못했다. 이것이 실제 축구와 게임의 차이이자 게임의 한계이다.

 

수비

공격의 어중간함은 위에서 살펴보았고, 이제 수비를 분석해보자. 4-2-3-1 2명의 수비형 미드필더를 두기에 수비적으로 매우 안정될 같으나 사실 내겐 그렇지 않았다.

축구 공격은 마무리까지, 슈팅까지 이어지는 것이 좋다. 공격 도중 패스가 끊긴다면 역습을 허용하기 쉽기 때문이다. 4-2-3-1 수비의 문제는 공격 도중 패스가 끊긴다는 것인데, 특히나 CDM 전진해 있을 패스가 끊기면 곧바로 상대의 슛까지 허용하는 경우가 많았다. 이런 일이 자주 생겼을까?

FIFA14 굉장히 만든 게임이다. 굉장히 만들었기 때문에 선수들이 지정 받은 자신의 위치에만 머무르려 하지 않고 전진하며 때론 다른 선수와 위치를 바꾸기도 한다. 4-2-3-1 운용할 공격시 패스를 주고 받다 보면 CDM <-> CAM 위치 교환, CDM <-> LM(RM) 간의 위치 교환이 자주 일어났다. 원인은 LM(CAM) CDM으로부터 패스를 받은 전진하지 못하고 뒤로 물러났기에(결국 내가 공격을 못해서 그렇다) 빈자리를 CDM 전진해 차지하려 했기 때문이다. 여기서부터 문제가 시작된다. CDM 공격 능력보다 수비 능력이 뛰어나므로 그가 전진한 자리에서 공을 받는다면 좋은 패스를 하거나 드리블해 공을 지키기가 어렵다. 만약 전진한 CDM 공을 빼앗긴다면 상대의 역습이 들어왔을 원래 CDM 있어야 자리에 CAM 또는 LM(RM) 있게 된다. 그들은 보통 수비능력이 현저히 떨어지므로 역습을 효율적으로 막아 내기 어렵다. 만약 후방으로 위치를 이동한 LM 이나 CAM 공을 빼앗기면 어떤 일이 벌어지는지는 굳이 말하지 않아도 것이다.

 

 

그럼 어떻게 해야 하나?

다른 포메이션으로의 전환은 4-2-3-1 장점을 포기하는 것이므로 선택하기 어려웠다. 4-2-3-1 그대로 유지하면서 위의 단점들을 커버할 있는 방법은 무엇일까? 그것을 생각할 있다면 아마도 다음 글을 있을 같다. 현재로선 요원하지만.

저작자 표시
신고

리그 후반기에 굉장한 성공을 경험하고 있었다. 정확한 경기 수는 기억나지 않으나 10경기 동안 패배는 번도 없었고, 실점도 3점에 머물렀다. 무승부는 2 기록했으나 그것은 모두 토너먼트 중에 발생한 것이었다. 결국 승부차기를 통해 결승까지 올랐으니 승리에 가까운 무승부라 있었다. 이러한 성공의 원인에는 3명의 DM, 1CM, 1RAM, 1CF 구성된 이전에 소개했던 4-3-2-1 있었다.

 

 

리그 후반기로 갈수록 내가 운영하지 못하는 4-3-2-1 4-3-3 버리고 시스템을 자주 사용했고 그것은 승리라는 결과로 내게 돌아왔다. 내가 승리에 가득 도취해 있을 UEFA (챔피언스 컵이 아닌), FA 결승전에 진출해 있었고 프리미어리그는 3위었으나 선두보다 2경기 치룬 상황이었고, 승점 차는 4점에 불과했다. 그리고 이후 리그에서 겨룰 상대들은 풀럼, 스완지, 사우스햄튼, 헐시티, 맨유, 맨시티 였다. 충분히 트레블을 이룰 있다고 자신했다.

 

그러나 결과는 믿을 없게도 풀럼전 패배, 스완지전 무승부, 사우스 햄튼 패배였다. 이후 FA컵은 우승했으나 UEFA 컵은 준우승에 머물렀고 리그는 겨우겨우 4위로 마감할 있었다.

 

원인은 무엇이었을까? 그것은 4-3-2-1 포메이션의 한계 때문이다. 포메이션은 경기 내적으로는 문제가 없다. 그러나 경기 외적으로는 문제가 많은 포메이션이다.

 

먼저 중앙 미드필더를 4명이나 사용한다. 수준급 미드필더가 4 이상인 팀은 많지 않다. 그래도 내겐 4명의 선수가 있었다. 모드리치, 포그바, 맥카시, 로드웰이 그들이다. 그런데, 4명중 모드리치, 포그바는 누구도 대체할 없는 선수이다. 선수들이 많은 경기를 치르면서 체력이 고갈되어 갔고 후반에는 포메이션을 운용할 없게 되었다

 

결국 리그 후반에는 포메이션을 버리고 4-3-3 또는 4-2-3-1 회귀 해야 했는데 이것 때문에 나는 패배를 자주 경험해야 했다. 먼저 4-3-2-1에서 기용 없었던 선수들의 모럴이 현저히 떨어져 있었고(특히 나스리, 카솔라) 중앙 미드필더 진은 체력이 고갈된 상태였다. 더군다나 선수들을 조정하는 나도 4-3-3 4-2-3-1 운용하는 방법을 손에서 잊어버리고 말았다.

 

모두가 아는 사실을 말하기 위해 너무 많이 주절주절했다. 리그는 경기만 치르는 친선 경기가 아니다. 그러니 경기 외적인 요소들도 충분히 고려하자. 

저작자 표시
신고

후반전에 뒤지고 있을 3명의 공격수(LF, ST, RF) 사용하는 4-3-2-1 사용해왔다. 여기서 3 모두 CM, 2 LF RF, 1 ST이다. 나름 쓸만했기에 4-2-3-1 사용하는 약팀과 경기할 때는 포메이션을 선발 포메이션으로 사용했다.

 


포메이션은 상대의 측면 공격을 좌우 풀백으로만 막아야 하고 역습에도 취약하다. FIFA14 계속하다 보니 측면 공격에 대한 수비는 어느 정도 있었으나 역습은 부담스러웠기에 3 자리의 가운데를 CM 아니라 수비 성향의 DM으로 바꾸는 것을 생각했다. 굳이 숫자로 쓴다면 4-1(DM)-2(CM)-2(LF, RF)- 1(ST) 되겠다.


 

수정된 포메이션으로 4-3-2-1 사용하는 뉴캐슬을 상대했는데, 맙소사. 결과는 3:0 참패였다. 경기 내용은 말도 못할 정도였다. 게다가 뉴캐슬은 리그 번째 경기에서 4-2-3-1 사용하는 내게 2:0으로 패했던 팀이었다. 게임을 다시 로드해 그대로 재경기 해보아도 결과는 비슷했다. 무엇이 문제였을까?

 

일단 뉴캐슬은 약팀이 아니다. 뉴캐슬은 아르파와 로익 레미가 있다. 미들은 티오테가 버티고 있다. 특히나 티오테가 버티는 미들은 굉장했다. 검색해보니 수비력이 뛰어난 미드필더였다. 그는 Aggression 91, 체력이 91, 태클 능력이 81이다. 영입을 고려할 정도로 인상적이었다.

 

그리고 내가 사용한 4-1-2-2-1 쓰레기였다. 제목으로 있겠지만 세로로 너무나 길게 늘어서 있다. 축구를 관전하면 해설자가 - 간격이 넓으면 된다는 말을 많이 하는데, 포메이션은 무려 5개의 층으로 이루어져 있다. 내가 포메이션을 저렇게 구성하니, 자동으로 공수 간격이 넓어졌던 것이다.

 

FIFA14 미드필더 3 DM CM 조합이 아닌 3CM으로 사용하는 것은 위와 같은 문제가 있기 때문이다. 수비- 공격 라인의 횡적 숫자는 3 또는 4 적당하다.

 

나는 경기 도중 팀의 - 간격이 넓다는 것을 인지했다. 그에 대한 조치로 Custom Tatics 수정해 수비라인을 80으로, 극도로 올렸다. 그런데 결과는 좋았다. 그런가? 수비라인을 올리면 수비라인만 올라가는 것이 아니다. 공격라인도 그에 맞춰서 올라가게 되어있다. 게다가  뉴캐슬은 수비라인을 내리고 역습 위주로 경기하는 팀이었다. 수비라인을 올려도 그에 맞춰 상대가 수비라인을 내려버리니 넓은 공수 간격은 그대로였다. 지금 하는 생각이지만 2CF, 1ST 구성하는 공격라인을 ST위치를 아래로 내려 3CF, , LF, CF, RF 구성하는 것이 나았다.

 

그리고 이전 에서 'WorkRate 고려해 포메이션을 짜라' 라고 말했는데, 금새 사실을 잊었던 같다. DM 로드웰, 2CM 모드리치, 포그바로 사용했던 것이다. 모드리치와 포그바는 CM이지만 공격 앞으로 나가는 성향이 강해 CAM처럼 움직였고, 수비라인과 미드필더진 사이에 드넓은 공간을 허용하고 말았다.

 

포메이션 변화는 어떤 것보다 완벽한 실패였다. 그런데 실패는 실패라고도 없었다. 리그 막판을 완전히 망친 실패가 나를 찾아오게 되는데…

저작자 표시
신고

FIFA14 얼티밋 모드로 플레이 하다 커리어 모드로 돌아왔다. 때문에 멀티플레이를 즐길 없다면 커리어 모드(난이도 레전드리) 시즌, UEFA, FA 컵을 경험하는 것이 내게 즐거울 거란 생각 때문이다. 얼티밋 플레이에서 선수 카드를 모으는 재미는 치트엔진을 사용해 제정을 무제한 늘려 원하는 선수를 사는 즐거움으로 대체 했다. 에버튼으로 플레이하고, 얼티밋에서 사용하던 Nasri, Pogba, Reus, Chicharito, Rodwell, Cazola, Dzeco 사왔다. 그런데, 커리어는 얼티밋과 달랐다. 단순한 느낌인지 모르겠으나 훨씬 어렵게 느껴졌고, 얼티밋에서 경기 골을 넣어주던 제코는 5경기에 한골만 넣는 부진을 보였다.

 

결국 시즌 초중반이 지나자 전적은 70% 무승부, 20% 승리, 10% 패배로 채워졌다.(이 선수들로 이런 결과라니... ㅜ.ㅜ)  토튼햄과의 경기가 뼈아팠다. 1:0 패배가 감사할 정도로 경기 내용이 형편 없었다. 첼시에게 1:0 승리 했던 직후 였기에 더욱 아팠다. 이후 아스톤 빌라와 경기를 가졌는데, 마찬가지로 경기 내용이 형편없었다. 세이브- 로드 신공을 발휘해 다시 아스톤 빌라와 경기를 가졌는데, 또다시 패배했다. 그런 것일까? 토튼햄은 강팀이어서 그렇다치고, 아스톤 빌라 약팀인데? 토튼햄과 아스톤 빌라는 4-4-2 사용한다는 공통점이 있었다. 내가 4-4-2 약한 것일까? 포메이션은 통상 4-4-2 강하다는 4-2-3-1인데? 어떻게 대처해야 하나?

 

먼저 원인을 곱씹어 보았다. 포메이션은 4-2-3-1 2 자리에 모드리치와 포그바를 기용했는데, 이것이 문제라고 판단했다. 모드리치와 포그바는 모두 공격시 앞으로 뛰어나가는 성향의 선수이다.(Work Rate H/M) 앞으로 뛰어 나가니 역습을 허용하면 가운데서 4백을 보호해줄 선수가 없었던 것이다. 그렇다면 어떻하나? 모드리치와 포그바중 하나를 버려야 하나? 그렇게 하긴 싫은데?

 

그래서 노트에 포메이션을 이것 저것 그리다가 아래와 같은 변태 포메이션을 생각했다. 4-3-2-1인데, 3 모두 DM이다. 로드웰, 모드리치, 맥카시로 구성했다. 2 포그바(CM) 로이스, 1 치차리토이다

 



 

결과는? 4 0. 대성공 이었다. 비정상적인 시스템으로 이길 있었을까? 그것은 WorkRate 때문이다. 좌측 측면은 수비 성향의 DM 로드웰과 맥카시가 적절히 막아주었고, 공격 성향의 CM 포그바는 적극적으로 공격에 참여해 무려 2골을 터트렸다. REUS 1 2어시를 기록했다. 놀라운 것은 가운데 DM 모드리치였다. 그는 DM- CAM, 측면 가리지 않고 넓은 활동 범위를 기록했고 하나의 공격포인트도 기록하지 못했지만 MOM 차지 했다.

 

FIFA14 포메이션을 생각할 하나의 포메이션이 아니라 공격시, 수비시의 포메이션도 생각해야 한다는 것을 알았다. 4-3-2-1 경우 공격시에는 4-2-3-1 처럼 가동된다. (모드리치, 포그바의 공격성향이 H이므로). 수비시에는 기본 4-3-2-1처럼 움직인다

 

후에 이 전술로 이후에 토튼햄과 싸웠고, 1 0으로 승리할 있었다. (경기 내용은 1:0이란 스코어 보다 더욱 훌륭했다.) 그리고 절대로 지면 안되는 경기에서는 어김없이 전술로 재미를 봤다. 물론 훗날에 전술의 엄청난 약점을 알게 되었는데, 그것은 다음, 다음 기회에 말해보겠다

저작자 표시
신고
TAG FIFA14

김진표. 보육교사 공약에 대해.

끄적끄적 2014.05.21 10:10 Posted by 아일레프

2014 6.4 지방선거를 앞둔 지금경기 도민인 나는 서울 시민이 매우 부럽다누구를 선택할지 결정하는 것이 너무나 쉬워 보이기 때문이다그곳에서  곳은 더욱 희어지고 있고검은 곳은 스스로를 검게 칠하고 있으니흑백을 가려 지지할 후보를 선택하는 것은 너무 쉬워 보인다허나 경기도는 그렇지 않다까다롭다야당을 응원하는 내가 여당의원같은 야당의원 김진표에게 한표를 던지기가  개운치않기 때문이다.

 

김진표는 과가 있는 인물이다선대인 그의 과를 지속적으로 지적해  것으로 유명하다사실 내가 가진 그의 어두운 이미지는 상당부분 선대인으로 부터  것이다대표적인 그의 과실 또는 논란거리는 위키  정리되어 있다내가 심히 불안한것은  논란거리들에해당하는 행동의 근거와 명분을 찾기 어렵다는 것과그가 이것에 대해 명확한 해명을 하지 않았다는 것에 있다매우 불편한 사실이다. (해명 하지 않았다는 것은 사실과 달라 취소선을 그었습니다. 인터뷰가 있는데, 들어봐야겠군요) 

 

게다가 이번에 그가 발표한 보육교사 전면 공무원 전환 공약은 어떤가? '좋은 보육' 대한 필요성은 대부분의 국민들이 공감한다그러나보육교사를 공무원으로 전환하면 '좋은 보육' 실현되는가아니단계적으로 보육교사를 공무원으로 전환한다는 것이 과연 가능하기나  것인가예산 비용은 얼마인가이렇게 자연스럽게 따라오는 질문들에 대해 그가 명확히 답변하고 있지 않아 매우매우 실망스럽다

 

남경필이  질문들을 토론 중에 했는데 귀에는 남경필의 질문이 합리적으로김진표의 답변은 질문의 본질을 회피하는 것으로 들렸다. (http://news1.kr/articles/1684582)

토론  나왔던 발언들은 아래와 같다.

 

 

 

 : 도내 공무원이 5만명을 밑돈다는 남 후보의 지적은 틀린 것으로 국공립교사 9만명, 사립교사 2만명 등 총 11만명의 교사가 있다. 새누리당도 무상보육에 10조 투입하고 있는 상황에서 7만명의 공무원화는 문제가 없다.

 

 :  공약은 철회하는 것이 맞다. 보육교사 10만원~15만원 지원해 처우 개선하는 것은 전적으로 동의한다. 그러나 공무원화는 어려운 것이다. 이에 따른 급여만 연간 약 1조3000억원이 든다. 전문가들은 2조가 들지, 얼마가 들지 모른다고 한다. 표를 얻기 위한 졸속공약이다.

 

 :  후보는 15일 보육은 국가 책임으로 정부가 책임져야 한다고 말했다.  후보 말대로 많은 예산이 들지만, 교육자의 자질을 높이는 것은 중요한 것이다.

 

 : 공무원 만들어 준다는 것은 잘못된 것으로 예산 따지지 않고 공약했다면 포퓰리즘이다. 경제전문가인 김 후보가 예산을 따지지 않고 공약한 것은 잘못이다.

 

 : 대강 사업에 27조원 쏟아부은 새누리당이다. 학부모들에 보육 부담을 떠넘겨 출산율 저하로 이어지고 있다. 국가가 부담해야 할 예산을 부담하지 않아 경기도지사가 추진하려 하는 것이다단계적으로 공무원화로 학부모 만족도를 높이고, 2019년 국가 경제규모가 커지니까 당연히 8조원은 국가가 부담할 수 있다.

 

 : 사회복지사도 1만8000여 명에 달하는 데 보육교사 공무원화는 사회 갈등을 조장하는 공약이다. 지금이라도 공약 철회 해야 한다고 생각한다. 김 후보가 교육부총리 시절에는 왜 추진하지 않았는지 진정성에 의문이 든다.

 

 : 교육부총리 시절 계속 말하고 추진하려 했다. 당시 유치원교사 월 10만원 지원을 처음 시작했고 이것이 밑거름돼 현재 유치원교사 50만원, 어린이집교사 30만~40만원 지원이 시행되는 것이다.

 

 

다시 한번 생각하는데보육교사를 공무원으로 전환하면 교육자의 자질이 저절로 높아지는 것인가유치원 교사어린이집 교사에게 금액을지원한 것은 매우 잘한 것이나금액 지원차원에서 공무원 전환 추진으로 '도약'하려는 근거와 명분은 과연 무엇인가갑작스럽게 4대강을 언급하는 것은  그런 것인가? 4대강을 언급하면 모든 공약을  실현할  있다. 4대강은 극악이기 때문에 그것과의 비교를 통해 자신의 공약의 근거를 확보하는 것은 매우 어리석은 짓이다 남경필의 질문이 매우 합리적이라 생각하며 김진표는 이에 대한 고민을 깊게 해야만 하고그에 대한 해답을 자신 안에서 찾을  없다면  공약은 마땅히 철회해야만 한다.

 

야당 지지자는 콘크리트가 아니다우린 당신을 검증할 것이고 검증에서 당신이 좋은 점수를 받지 못한다면 당신이 새누리당원이아니라는 이유만으로 당신에게 표를 던지지 않을 것이다이점을 김진표, 당신이 명심했으면 좋겠다당신에게 투표하는 손이 부끄럽지 않을 있도록 거짓없이 진실되게 공약을 만들고떳떳하게 승리하기 바란다부탁이다



---> 이 글과 반대되는 주장을 하는 글이 있어 추가 합니다. 이 글을 보신 분들은 우리아이 보육, 김진표의 생각과 남경필의 생각 도 함께 읽어 보시기 바랍니다. 


---> 남경필 "보육공무원? 돈있나" vs 김진표 "4대강보다 낫다" 이것도 함께 보시길..



저작자 표시
신고

강신주 비교

끄적끄적 2014.02.25 10:15 Posted by 아일레프
  1. 자기계발서는 자본을 섬기라 하고, 기독교는 신을 섬기라 하지만 강신주는 스스로 주체가 되라 한다.

  2. 자기계발서, 기독교, 강신주는 개개인을 변화시켜야 대상으로 본다.

  3. 자기계발서, 기독교, 강신주 모두 실현 불가능한 목표를 제시한다.

  4. 자기계발서, 기독교는 세상을 긍정하라 하고 자신을 부정하라 하지만 강신주는 자신을 긍정하라 한다.

  5. 기독교와 자기계발서는 개인보고 착하게 살라 하지만 강신주는 착하게 살지 말라 한다.

  6. 자기계발서, 기독교, 강신주 모두 뭔가를 물어보면 정답을 알고 있다는 듯이 말한다.

  7. 자기계발서, 기독교는 자본주의를 긍정하나 강신주는 자본주의를 저주한다.

  8. 기독교와 강신주 모두 이분법적 잣대를 들이댄다. ( 아니면 도다)

  9. 기독교와 강신주는 사람들의 모든 행동에서 위선을 발견하고, 그것을 깐다.

  10. 기독교와 강신주 모두 자기가 하는 말이 무조건 옳다 한다.

  11. 기독교와 강신주 모두 일종의 회계를 유도한다. 청중은 회계를 통해 카타르시스를 얻는다.

  12. 몇몇 멘토들과 목사들, 그리고  강신주는 싸가지 없게 말하고, 청중은 그것으로 카타르시스를 얻는다.

  13. 기독교는 회계한 개인에게 신이 있기에 괜찮다고 말하고, 강신주는 스스로 일어날 있는 힘이 자신에게 있다 한다.

  14. 기독교의 목사는 신도가 자신을 따르길 원하지만 강신주는 청중이 자신을 마침내 버리길 원한다.

  15. 자기계발서, 강신주 책이 서점에 많다.

  16. 자기계발서에는 저자의 성실함과 피냄새가 보이지 않는다. '최근의' 강신주 책에는 피냄새가 나지 않는다.

  17. 자기계발서는 병신같고,  기독교 서적은 펼치기도 괴로우며, 강신주 책은 때때로 훌륭하나 때때로 성의 없다.

  18. 연습하면 자기계발서는 누구나 있는 책이나 강신주 책은 그정도 까지는 아니다.

  19. 자기계발서, 기독교는 대놓고 강자를 편든다. 강신주는 약자를 편들지만 강신주 논리를 강자가 사용하면 끔찍한 일이 벌어진다.

  20. 자기계발서, 기독교 목사들은 대놓고 거짓말 쟁이들 이지만 강신주는 내적 모순이 없으려 애를 쓴다. 허나 어쩔 없이 내적 모순이 있다.

  21. 자기계발서 저자, 힐링서 저자, 기독교 목사, 강신주를 맹목적으로 따르는 사람들이 있다.

  22. 강신주를 '맹목적'으로 따르는 것은 강신주의 가르침에 반하는 것이므로 그러지 말자.

  23. 자기계발서, 기독교는 까야 제맛이다. 강신주는 아직까지 이용가치가 높으므로 적당히 까자


저작자 표시
신고
TAG 강신주

어렸을때 모든 동네엔 하나쯤 비디오 대여점이 있었어. 그런데 지금은 찾기 힘들다. 대여점을 갈 필요가 없어졌기 때문이야. YouTube를 통해 왠만한 동영상을 볼 수 있고, 영화를 볼땐 먼저 토렌트나 웹하드를 찾아. 그래도 없으면 IPTV를 통해 비디오를 보지. 우리는 편리하게 원하는 바를 얻을 수 있게 되었어. 더이상 몸을 움직여 비디오방으로 가지 않아도 돼.


 우리가 얻은게 또 하나 더 있어. 자본주의란 오디션의 슈퍼스타, ‘플루토크라트‘ 를 얻었어! 이들이 얼마나 대단한 사람인줄 알아? 전세계 부의 반을 가지고 있는 세계 상위 1%의 사람들이야! 이건 정말 너무나도 어마어마한 부라서 감히 체감 할 수도 없어! 아, 그들 부모가 부자여서 그들도 부자인게 아니냐고? 너 빨갱이니? 아니야! 그들 대부분은(모두는 아니지만) 자수성가 했다구! 망한 옆집 비디오 대여점 주인과는 다르게 그들은 오늘날 기술혁명이 어떻게 삶을 바꿀 것인가를 읽어 냈어. ‘세계화’가 가진 무한한 가능성을 그들은 읽어 냈다구! 자본주의를 그저 욕만하는 멍청이들과 다르게 그들은 자본주의란 룰을 읽어 낼 수 있는 통찰력을 가지고 있었고, 그 통찰력을 실행에 옮길 수 있는 에너지를 함께 가지고 있었어. 정말 멋지지 않니? 


 하지만 슬픈 사실도 있어. 우린 이웃 비디오 주인 아저씨를 잃었어. 그뿐만 아니야. 우린 비디오 대여점 주인으로 살아갈 수 있는 길도 잃었어. 몹시나 슬픈일이야. 한때 내 꿈이 비디오방 주인이었거든. 그리고 이상해. 분명 몇 십년 전보다 내 삶은 윤택해진 것 같은데, 편리해 진 것 같은데, 분명 내 아버지가 예전에 벌었던 월급보다 많은 돈을 지금 벌고 있는데, 내일에 대한 불안감이 이렇게도 선명해. 이게 얼마나 선명한지 종족번식의 욕구를 이길 수 있는 선명함이야. 언제 나락으로 떨어질 지 모른다는 불안감. 미친 불확실성의 시대. 그곳에 내가, 우리가 살고 있는것 같아. 


내가 너무 비관적이지? 맞아. 불확실성의 시대라는 것은 떨어질 수도 있지만 올라갈 수 있는 유동성이 확보된 사회라는 건데. 맞아 난 너무 나쁜쪽으로만 생각하는것 같아. 이지성이란 사람도 말하잖아? 책만 많이, 그리고 제대로 읽어도 성공할 수 있다고.(물론 헛소리지만) 근데, 내가 성공해서 플루토크라트가 되면 뭐해? 플루토크라트 아닌 99%의 사람들은 여전히 불확실성에 있는걸. 그들도 생각해야지.(나 완전 짱 착하지?) 그리고 플루토크라트가 되면 뭐해? 그렇게 되고 난 다음에도 피곤하게 일해야 할텐데. 출장을 밥먹듯이 가고. 혹시나 2등으로 떨어질까 불안해하는건 마찬가지일테고. 나 일하는거 싫어. 열심히 일해서 갑부 되는거 보다 적당히 일하고 적당히 안정되게 살면 좋겠어. 그런데 이거 갑부되기 보다 적당히 일하고 안정되게 사는 삶이 더 어려운거 같아. 역시 공무원이 최고라구! 


 아 그런데, 그 플루토크라트란 사람들은 도대체 어디 있는거야? 실제로 있긴 한걸까? 티비나 신문에선 보이는데 내 주위에는 단 한명도 없어. 정말 있긴 한거야? 혹시 상상속의 동물 아닐까? 혹시 그건 환상이 아닐까? 


너무 시덥잖은 말만 했지? 이것도 나름 서평이니까 플루토크라트 책이야기로 넘어가자. 이 책은 플루토크라트를 설명하고, 플루토크라트를 낳은 인큐베이터 현대 자본주의의 특징을 말해줘. 세계화, 파편화, 기술 혁명 등등. 그리고 그들이 이런 것들을 어떻게 이용해 현재의 자리에 있을 수 있었는지도 말해줘. 별로 알고 싶지도 않은 그들의 문화에 대해서도 이런 저런 사례를 들어가면서 자세히 말해줘. 너무 자세히 말해줘서 귀찮을 지경이야! 여튼 사례 중심의 서술이란게 특징이야.(요즘 경제학 책들은 대부분 이런 식으로 나오더군, 사실 좀 불만이야.) 그리고 자신의 궁극적 의견이라 할 수 있는 결론에 대한 양이 너무 짧아! 그리고 좀 온순해. 불만스러워. 


저자의 결론은 이런 것 같아.(확실하지 않아) 플루토크라트란 현상은 가치 중립적이야. 그들은 선한 사람일 수도 있고 악한 사람일 수도 있어. 오히려 플루코트라트란 유동성, 역동성을 낳을 수 있는 측면은 자본주의가 제대로 동작한 하나의 예일 수도 있어. 문제는 선하거나 악한 사람일 플루토크라트가 악해지도록 유도케 하는 현 자본주의 시스템에 있어. 


너무 파편화 되서 모르겠지만 사실 세계는 여러개의 리그로 나누어져 있다고. 하부리그 상부리그 이런 식으로. 유동성이란 것은 하부리그의 팀이 상부리그로 가고, 상부리그의 팀이 하부리그로 가는 것이 빈번하게 이뤄진다는 것이지. 그런데, 이 유동성 덕분에 상부리그에 속하게 된 사람들은 이제 더이상 유동성을 원치 않게 될 가능성이 커. 당연하지. 자신은 올라갈때로 올라갔으니까 변화를 원치 않는거야. 이건 어쩔 수 없는거지. 그 사람들을 탓할 수도 없어. 그들은 결국 시스템을 입맛에 고치려 할거야. 오디션 프로그램의 참가자가 되는 것이 아니라 심사위원이 되려는 것이지. 그것은 불공평한 사회로 이어질 것이고 가진자는 더욱 가질 것이고, 가난한 자는 더욱 가난해지겠지. 


결국 플루토크라트를 낳았던 사회는 플루토크라트들에 의해 더이상 플루토크라트를 생산할 수 없게 될거야. 물론 그렇게 되지 않을 수도 있어. 플루토크라트들이 선한 마음씨를 가지고 있다면 그렇게 되지 않겠지. 하지만. 개인의 선함, 악함에 미래가 결정되는 시스템은 결코 좋은 시스템이라 할 수 없다구. 그래서 저자는 규칙을 말하는 거야. 약간 뻔한 결론이지? 


책 이야기는 그만두고다시 한번 돌아와서 말해보자. 그런데 도대체 플루토크라트란게 있긴 한거야? 내가 정말 본적이 없어서 그래. 상상 속의 동물 아닐까? 99%의 세계에 살고 있는 우리에게 플루토크라트란 너무 먼 곳에 있는 존재라서 개별 사례를 들어도 도대체 와닿지가 않아. 내가 그것에 대해 알아야 하는지도 모르겠고. 


그러니까.. 내가 말하고 싶은 사소한 불만은 이런거야. 결국 저자는 불공평 이야기를 하는데, 그 이야기를 하기 위해 꺼낸 플루토크라트란 소재가 과연 적절한가? 라는 질문이야. 플루토크라트 없는 우리들 주위에도 충분히 불공평을 발견할 수 있거든. 아, 써놓고 보니까 그렇구나. 불공평만을 이야기 하려고 한다면 플루토크라트를 말할 필요가 없어! 불공평은 차고 넘친다구! 결국 저자는 플루토크라트에 대해 말하고 싶었던 거구나. 저자는 플루토크라트 ‘빠’ 였어! 그럼 저자는 플루토크라트란 영웅을 더이상 볼 수 없는 사회가 무서워서 이런 글을 쓴것일까? 아마도 그런것 같다. 그녀는 진성 플루토크라트 빠순이였어! 쳇! 


그게 싫어! 난 플루토크라트와 상관 없어! 플루토크라트 퉤퉤퉤! 난 플루토크라트가 되고 싶지 않기에 더 이상 플루토크라트를 낳을 수 없는 시스템이 된다해도 괜찮아! 굳이 그것을 떠올리지 않고서라도 이 세계는 불공평이 너무 넘쳐난다고! 그것들을 먼저 해소해줘! 플루토크라트 따위 더이상 생겨나지 않아도 좋아! 내가 원하는 것은 슈퍼스타가 아니라 내 신청곡을 노래해줄 수 있는 길거리 가수니까.


 끝!


저작자 표시
신고

위대한 왕국이 퇴색해 가는 것은 후진 공화국이 붕괴되는 것보다 훨씬 더 서글프다

개미와 타나토노트는 고전으로 불릴 수 있는 명작이다. 그는 참신했고 치밀했다. 베르나르식 소설이 가지고 있는 참신함은 여전히 유효하다. 하지만 난 더 이상 그의 글에서 예전의 치밀함과 성실함을 찾을 수 없다. 제3인류는 베르나르란 왕국이 퇴색되었음을 여실히 보여준다. 대충 떠올려봐도 비판할 거리가 참 많다.

  1. 무리한 설정, 그것을 애써 설명하려 하지만 실패함.
  2. 무리한 설정에 의해 탄생된 '멍청하고 전혀 매력이 없는' 주인공들
  3. 이해할 수 없는 인물들의 행동들
  4. 헐리우드적 클리쉐들(뜬금없는 추격신, 스트립 댄스, 적절한 타이밍의 사건들, 서양인이 세계를 구한다!)
  5. 개연성 없음, 반복되는 우연들(소설가의 중요한 자질 중 하나는 우연을 다루는 솜씨이다.)
  6. 뜬금없는 한국 찬양.
  7. 등등등...

위 모든 것을 포괄하는 글을 쓰는 것은 내 필력으론 무리이다. 때문에 1과 2에 집중해 제3인류에 대해 비판해본다.


베르나르가 진화를 들고 왔다. '아버지들의 아버지'에서 빠진 고리를 찾았던 그는 제3인류에선 인간 이후의 인류와 인간 이전의 인류를 창조했다. 생물의 크기가 그것의 생존력에 영향을 줄 것이란 아이디어를 바탕으로 그는 거인, 인간, 초소형 인간으로 이어지는 '인류 라인업'을 제시한다. 이것이 제3인류의 기본 설정이다. 설정을 비판할 수는 없다. 내가 안타까워하는 것은 그가 이 설정에 ‘그럴듯함’을 부여하기 위해 여느 때처럼 과학이란 방법론을 가져왔고, 그것이 실패했다는 것에 있다.


설정은 설정일 뿐이다. 설정은 소설가가 마련한 장치며, 소설 속 작은 세계다. 때문에 작가는 기독교의 신처럼 "설정이 있으라"라고 하면 된다. 하지만 이것에 과학이란 이름을 붙이고 설정에 대해 근거를 제시하려고 한다면, 대충하면 안된다. 철저해야 한다. 그것이 베르나르급 작가라면, 초판으로 20쇄를 찍는 작가라면, 더욱 철저해야 한다.


소설이 시작하자마자 등장하는 인간 이전의 인류, '거인' 설정을 보자. 주인공의 아버지는 남극을 탐험하다 거인 화석과 거인이 그린 벽화를 발견한다. 화석과 벽화를 본 아버지는 이전에 거인이 지구를 지배했다고 믿게 된다. 베르나르는 독자가 주인공 아버지의 입장에서 거인 화석과 벽화를 보게 함으로써 독자가 이 설정을 자연스레 받아들이게 하고 싶었던 것이다. 하지만 난 그럴 수 없었다.


"전 지구를 지배했다는 거인들의 화석이 왜 오직 이 남극에만 존재하는거지?"


질문이 생겼던 것이다. 거인의 화석이 왜 남극에서만 발견될까? 라는 질문은 너무도 자연스럽게 나오는 질문이고, 예측 가능한 쉬운 질문이다. 그러나 그곳의 그 누구도 이런 질문을 하지 않는다. 고고학자인 그들은 과학적 근거로 벽화의 내용에 설득력을 더하는 것이 아니라 각종 신화들과 미스터리들을 꺼낸다. 그 누구도 질문하지 않는다.


왜 그들은 질문 하지 않을까? 우린 답을 알고 있다. 베르나르가 대답할 수 없기 때문이다. 베르나르의 대답이 신화와 미스테리말곤 없기 때문이다. 베르나르가 자신이 설명할 수 없는 거대한 설정을 했기에, 주요 인물의 질문하는 입을 막은 것이다. 그것 때문에 주요 인물들이 멍청해져버렸다. 독자는 그들에게서 학자의 매서운 눈빛을 느낄 수 없다. (그 학자는 벽화 근처에서 다이너마이트도 주저 없이 터트린다.)


주인공 다비드를 잠시 살펴보자. 1권 162페이지를 보면 피그미족이 인류의 미래라고 주장하는 다비드와 그것에 반대하는 콩고 가이드의 대화를 볼 수 있다. 잠깐 보자.


- 내가 보기에 피그미들은 미래의 인류를 대표하고 있어요

- 피그미들이 미래의 인류를 대표한다고요? (중략) 당신은 모든 것을 뒤집어서 생각하고 있어요, 세상을 거꾸로 이해하고 있단 말입니다. (중략) 스마트폰이 없어요! 성도 없고 그저 이름만 있죠. (중략) 그들은 말귀를 도통 못 알아듣죠. (중략) 그들은 거울을 보면 환장을 해요. 거울을 어떻게 만드는지 모르는 자들이죠. (중략) 그들은 거울 단계를 넘지 못한 아이들일 뿐이라고요!

- 그렇다면 미래는 아이들의 것일지도 모르죠

- 피그미 사회는 여자들이 지배하는 사회에요. 수효도 여자가 더 많아요. (중략) 유아들은 사망률이 높아요. 피그미들의 수명은 아주 짧아서 40세를 넘기는 경우가 드물죠. 정말이지 진화했다고 말할 수가 없는 인간들이라고요. 하물며 미래의 인류라니, 그건 말도 안 돼요! (중략) 커지는 것이 미래에요. 모든 전문가들이 그 점에 동의하고 있어요

- 자연은 때때로 전문가들의 허를 찌르기도 하는 것 같습니다. 그들 모두의 견해가 일치하는 경우에도 말입니다. 내 아버지께서 말씀하시길, <틀린 생각을 하는 사람들이 다수라고 해서 그들이 옳은 것은 아니다> 라고 했죠.

- 아무튼 이러고저러고 복잡하게 생각할 필요도 없어요. 미래의 인간은 당연히 더 크고 더 강하고 더 아름답고 더 건강할 겁니다. 그건 분명해요!

- 피그미들이 이곳의 풍토병에 잘 걸리지 않는다는 사실에 대해서는 아직 대답하지 않았어요. 나는 그들이 에이즈에도 저항력이 있을 것으로 믿고 있어요. 그러지 않은가요?

- 이미 대답했는데 내 말을 귓등으로 들었군요. 그들은 병원에 가지 않고 죽기 때문에 무슨 병을 앓는지 알 수가 없다니까요.


이것은 기묘한 대화이다. 피그미들을 욕하는 가이들의 말도 신뢰가지 않지만, 다비드의 말버릇은 짜증을 불러일으킨다. 가이드는 그나마 나름의 근거를 제시하며 자신의 의견을 다비드에게 피력하고 있다. 그런데 다비드는 어떤가? 그는 근거를 전혀 제시하지 않는다. 단순히 이렇게 말한다. "미래는 아이들의 것일지도 모르죠", "틀린 생각을 하는 사람들이 다수라고 해서 그들이 옳은 것은 아니죠". 다비드의 말이 맞을 수도 있다. 그러나 타인을 설득시키려면 저런 '잠언'을 내뱉는 것이 아니라 자신의 말의 근거를 제시해야 한다. 상대방의 말은 귓등으로 들어야 한다. 저렇게 '정신승리'를 외치는 주인공에게서 난 어떤 매력도 느낄 수 없다.


1권 353페이지를 보면 오로르를 초소형인간(제3인류) 창조 프로젝트에 끌어들이기 위해 다비드가 오로르를 설득하는 것을 볼 수 있다. 매우 참혹한 대화다.

- 오비츠 대령이 우리에게 제안하는 것은 어떤 반동 세력에게도 저항할 수 있는 새로운 인류를 만들어 내자는 거에요. 

- 더 작은 인류, 그리고 더 여성적인 인류를 만들어낸다고 해서 무슨 도움이 된다는 거죠? 

- 오비츠 대령은 그들이 훌륭한 전사가 될 수 있다고 생각하는 것 같아요. 키가 아주 작고 저항력이 강해서 정상적인 사람들이 들어갈 수 없는 곳에 침투해서 작전을 벌일 수 있는 특수 요원들 말이에요.(중략) 

그들이 미니 첩보원들을 만들어 내는 데 성공한다 한들, 그래서 얻을 게 뭐가 있죠? - 그 프로젝트는 이란과 연결되어 있어요. 자파르는 권력을 유지하기 위해 대량 살상 무기를 사용하는 것도 주저하지 않을 거에요. 우리가 첩보원들을 보내 그들의 군사 시설을 파괴할 수 있다면 늙은 수염쟁이들의 독재에 맞서 투쟁하는 민주적인 대학생들에게 도움이 될 거에요. 

- 난 이란 사람들에게 관심이 없어요. (중략) 문제가 되는 것은 핵무기나 이란이 아니라 인구 과잉이라구요. 

- 당신이 그렇게 냉소적으로 나오다니, 뜻밖이군요. 

- 이건 냉소주의가 아니라 현실주의에요. (중략) 아무튼 그들이 죽든 말든 내가 알 바 아니에요. 

- 지금은 그렇게 말해도 나는 희망을 버리지 않고 당신이 이성의 편에 서도록 만들 거예요.


이토록 생각없는 인물간의 대화를 보는 것 만으로 가슴이 아픈데, 그 인물들이 선한 역할을 담당하는 주인공이라는 것이 더욱 슬프다. 게다가 다비드는 대화 말미에 "당신이 이성의 편에 서도록 만들거에요"라며 또다시 정신승리를 시전하고 있다. 신념은 굳건한데, 그것을 말할 수 있는 지능을 가진 머리가 다비드에겐 없다. 오직 믿음과 신념으로만 똘똘 뭉쳐진 주인공인 것이다.


그런데 이 대화를 좀 더 자세히 살펴보자. 베르나르는 자신이 만든 주인공 다비드가 하늘 말이 어떤 의미를 가지는지 전혀 모르는 것 같다. 자, 다비드가 가진 신념이란 굉장히 무서운 신념이다. 그가 오로르에게 제안하는 것은 유전공학을 이용해 초소형 인간을 만들자는 것이다. 그리고 그 초소형 인간을 첩보원으로 활용하자는 것이다. 이것은 정말 끔찍한 생각이다. 인류를 걱정하는 주인공이라면 이것이 생명윤리에 어긋날 수 있다는 생각을 해야 한다. 제3인류가 이후에 어떤 상황을 만들것인지 조금이라도 생각해야 한다.(전생에 그가 만든 인간이 거인을 멸종시켰는데, 어떻게 그와 비슷한 생각도 하지 않는 것일까?) 하지만 다비드는 이런 생각을 깊게 하지 않는다.


더욱 무서운 것은 다비드가 그녀를 설득하는 방식이다. 다비드는 이란이란 거대 악을 막아야 한다며 이 프로젝트에 참여해달라고 그녀를 설득한다. 이거, 위험하다. 거대 악을 설정하고 자신의 행동을 정당화하고 남을 설득하는 것은 자신의 행동에 정당성을 찾을 수 없을 때나 하는 방식이다. 이것은 우리가 욕하는 정치인들이나 사용하는 방식이다. 베르나르는 이런 인물을 선한 주인공의 자리에 놓는 것은 피했어야 했다.(악역으론 어울린다.)


다른 측면에서 생각해보자. 초소형 인간을 만들고, 그들을 첩보원으로 만들고, 그들을 이란 사태의 현장에 투입하려면 대략 20년은 걸릴 것이다. 이것은 너무 늦다. 너무도 늦다. 윤리적 문제를 떠나서 실용적으로 살펴도 고개를 젓게 하는 방안이다. (물론 소설이 진행되면서 그들은 초소형 인간을 만드는데 성공하고, 다행히 그들의 발달 속도가 인간보다 10배정도 빨라 몇년 안에 초소형 인간을 이란에 투입하지만, 다비드가 오로르를 설득하는 시점에서 다비드와 오로르는 초소형 인간의 발달 속도가 인간보다 10배 빠른지 알 수 없었다 -오! 만들고 보니 발달 속도가 10배 빠르네? 만세! - )


그렇다면 우리들의 똑똑한 주인공들이라면 질문해야 한다. 하지만 오로르는, 다비드는 질문하지 않는다. 질문을 할 수도 없다. 왜냐? 베르나르가 대답할 수 없기 때문이다. 베르나르는 자신이 수습할 수 없는 설정을 가져왔고, 그냥 가져만 놓는 것이 아니라 무리하게 그것을 이야기 속에 녹이려 했다.


질문하지 않는 인물들, 생각하지 않는 인물들, 자신만의 신념만이 가득한 인물에게서 난 전혀 매력을 발견할 수 없다. 그들의 비정상적 결정을 옹호할 수단을

"이것은 운명이에요, 징후에요!"

라는 말로만 베르나르가 대신한다면, 맙소사, 왕국이 무너지고 있다.


베르나르는 인류를 걱정하기 전에 갈수록 지능이 퇴화되는 소설 속 인물들에 대해 더 많이 걱정해야 한다. 베르나르는 그들에게 윤리와 도덕을 가르치고, 질문하는 능력을 고양시켜야만 한다. 그리고 그렇게 하기 위해선 일단 본인의 지능을 진화시켜야 한다. 이제 인류에 대한 걱정은 잠시 접어두시라. 지구의 멸망보다 당신의 소설이 더 급하다.



저작자 표시
신고

기대감을 가지고 구입한 "근대문학의 종언 - 가라타니 고진" 이었는데, 이것이 내게 큰 실망감을 안겨줄지는 미처 몰랐다. 실망감의 원인이 그의 주장이나 논지가 나와 다르기 때문이 아니라 그가 글을 지독히 못쓰기 때문이라는 사실이 더 날 놀라게 한다. 고작 10페이지 읽으면서도 시험에 시달려야 했다. "고진이란 사람은 유명한데, 이 사람이 글을 못쓸리가 없어. 내가 이해를 못하고 있는 것일꺼야" 하지만 난 시험에 들었다. 이 사람은 글을 못쓰는 사람이다! 


아래에 "고진이 글을 못쓰는 사람이다" 라는 명제의 근거를 몇가지 제시한다. 누군가 이것에 대해 "고진이 글을 못쓰는게 아니라 이해력이 모자란 것"이라고 근거를 들어 말해줬으면 하는 것이 내 바람이다. (아래의 인용은 모두 근대문학의 종언 중에서 "3장 근대문학의 종언"에서 온것이다. )


프랑스에서는 사르트르의 존재가 너무 큰 나머지 다음 사람들에게 부담으로 작용했습니다. 때문에, 독립적으로 존재하기 위해 무리하게 사르트르를 비판하거나 조소하거나 하는 사람이 많았습니다. 그러나 들뢰즈가 솔직하게 인정하고 있는 것처럼 실은 모두 동경하고 있었던 것입니다. 또 사르트르는 그에 대한 비판으로서 이루어진 것을 전부 선취하고 있었습니다. 예를 들어 데리다는 '현전성의 철학'을 비판했지만, 사르트르가 '상상력'에 대해 쓴 것이 그것입니다. 또 앙티로망 역시 원래 사르트르가 높이 평가했던 것으로, '구토'는 최초의 앙티로망이었습니다. p45, 강조는 인용자

- 강조된 문장을 보자. "사르트르가 그에 대한 비판으로서 이루어진 것을 전부 선취하고 있었다"라고 했을 때, 도대체 비판으로서 이루어진 것이 뭐란 말인가? 저 문장 뒤에 나온 고진의 예시를 보면 데리다가 '현전성의 철학'을 비판했는데, 사르트르가 상상력에 대해 썼다고 한다. 그렇다면, 사르트르가 상상력에 대해 쓴 것이 데리다의 현전성의 철학 비판으로 이루어진 것이란 말인가? 그렇다고 치자. 그런데 '선취(이미 얻다)'란 단어가 도대체 왜 붙는 것인가? 강조된 문장은 고진은 자기 자신이 말하고자 하는 바를 너무 성의 없게 함축해 문장을 쓴다는 것을 보여준다. 또는 글을 못쓴다는 것을 보여준다.


... 그러나 내가 근대문학의 종언을 정말 실감한 것은 한국에서 문학이 급격히 영향력을 잃어갔기 때문입니다. 그것은 충격이었습니다. 1990년대에 나는 한일작가회의에 참가하거나 한국의 문학자와 사귈 기회가 많았습니다. 그래서 일본은 이렇게 될지라도 한국만은 그렇게 되지 않을 것이라는 느낌이 들었던 것입니다. p48, 강조는 인용자

- 맙소사, 고진은 한국에서 문학이 급격히 영향력을 잃지 않을 것이라는 느낌을 받았단다. 그런데 놀랍게도 그 느낌의 근거가 한일작가회의에 참가하거나 한국의 문학자와 사귈 기회가 많았기 때문이란다. 아니, 장난하나? 한국의 문학자와 사귀면서 이러 이러한 정보를 얻었기에 어떠한 느낌을 받았다고 써야한다. "사귈 기회가 많았기 때문에" 라는 것 자체가 저 주장의 근거로 사용되다니.


... 그는 자신이 문학을 했던 것은 문학이 정치적 문제에서 개인적 문제까지 온갖 것을 떠맡는다, 그리고 현실적으로 해결할 수 없을 것 같은 모순조차도 떠맡는다고 생각했기 때문인데, 언제부턴가 문학이 협소한 범위로 한정되어 버렸다, 그런 것이 문학이라면 내게는 필요가 없었다, 때문에 그만두었다는 것입니다. 나는 동감을 표했습니다.

- 위 문장은 굉장히 부자연스럽게 읽힌다. 쉼표(,) 의 쓰임이 굉장히 이상하다. 고진은 마치 '그'가 말하는 문장을 그대로 옮겨 적은 듯 하다 그런데 "떠맡는다고 생각했기 때문인데," 부분은 그대로 옮겨 적은것으로 읽히지 않는다. 그런데 "언제 부턴가 문학이 협소한 범위로 한정되어 버렸다, 그런 것이 문학이라면 내게는 필요가 없었다,"는 그의 말을 그대로 옮겨 적은 것으로 읽힌다. 이것은 문장의 일관성을 해치는 것이다. 고진은 글을 못쓴다.


문학의 지위가 높아 지는 것과 문학이 도덕적 과제를 짊어지는 것은 같은 것이기 때문입니다. 그 과제로부터 해방되어 자유롭게 된다면, 문학은 그저 오락이 되는 것입니다. 그래도 좋다면 그것으로 좋은 것입니다. 자, 그렇게 하시기 바랍니다. 더구나 나는 애당초 문학에서 무리하게 윤리적인 것, 정치적인 것을 구할 필요는 없다고 생각합니다. 분명히 말해 문학보다 더 큰 것이 있다고 생각합니다. 그와 동시에 근대문학을 만든 소설이라는 형식은 역사적인 것이어서, 이미 그 역할을 다했다고 생각하는 것입니다. p53, 강조는 인용자

- 장난하나? 앞 문장은 문학이 단순히 오락화 되는 것을 비판해 놓고서, "나는 애당초 문학에서 무리하게 윤리적인 것, 정치적인 것을 구할 필요는 없다고 생각합니다" 라니. 뭐하자는 건가? 왜 자신이 말하고자 하는 바를 확실히 말하지 않나? 난 이부분에서 책 읽기를 접었다. 빨간줄 그은 곳이 많아(좋은 표현에 그은 것이 아니다. 엉터리 문장에 그엇다.) 팔수가 없다는 사실이 날 슬프게 한다.


누군가 말해줬으면 한다. 내가 잘못 읽은 것인가? 그렇다면 어떻게 독해하는 것이 올바른가?

저작자 표시
신고
  1. 세상을 설명하는 하나의 단순한 원리, 또는 법칙을 만들어라. 
  2. 세계를 선과 악의 이분법으로 나누고 우리들은 굳건한 선의 자리에 있음을 내내 확인 시켜라. 반대로 세상은 악으로 설명하라. 
  3. 1과 2에 의해 기존의 모든 종교를 설명할 수 있도록 논리를 만들어라. 
  4. 종교 내에서 다툼이 있거나 교리 내의 모순이 있다면 우리도 세상에 속한 사람이기 때문에 그렇삼! 이라고 말하며 얼버무려라. 
  5. 선과 악을 구성원 스스로 구분짓지 못하게 하라. 
  6. 종교의 경전을 읽기 쉽게 하되, 해석의 가짓수가 무궁무진 할 수 있도록 하라. 
  7. 종교 경전 해석의 권위는 종교 지도자가 갖도록 하라. 구성원이 해석 못하게 해야 한다가 아니다. 다만 지도자의 해석이 구성원의 해석보다 무조건 높다는 뜻이다. 
  8. 7로써 삶에 존재하는 딜레마 해결 방법을 종교 지도자가 마련하게 하고, 그것을 절대적으로 따르게 하라.   
  9. 너~무 높아 인간으로서는 도저히 실현 시킬 수 없는 목표를 제시하라. 
  10. 종교 구성원이 죄책감을 가지게 하라. 9이 가능하다면 종교 구성원을 매번 죄책감에 시달리게 할 수 있을 것이다. (기독교의 원죄는 정말 대박이다.)
  11. 10이 가능해졌다면 이제 그 죄책감을 적절히 해소 시켜줘라. 일종의 채찍과 당근인데, 명심해라. 채찍과 당근에서 더 중요하고, 많은 공을 들여야 하는 것은 채찍이다. 
  12. 긍정적 사고를 가르쳐라. 이것으로 변화와 개혁의 초점을 사회가 아닌 오직 개인에게 맞추게 하라. 
  13. 마지막. 개개인을 노예로 살게 하라. 절대 주인되지 않게 하라. 

저작자 표시
신고
TAG 종교

진중권 vs 간결 토론 시청 후기

끄적끄적 2012.10.30 10:28 Posted by 아일레프

어제 실시간 검색어에 진중권이 있는 것을 보고 진중권과 간결이란 분이 토론한 영상을 보았다. 프로축구선수와 갓 축구를 접한 아이가 대결하는 축구시합을 보는 듯 정말 상대가 안되게 진중권이 속된말로 간결이란 분을 밟았다. 이것을 보고 느낀것이, 실력이 비등 비등한 상대가 겨루는 것을 보는 것도 재미있지만 실력의 차이가 크게 벌어지는 사람 간의 시합을 보는 것도 엄청난 새디스트적 즐거움을 준다는 것이다.(물론 당하는 쪽이 나의 반대편에 서 있을 경우에만 성립하겠지만) 또 하나 알게 된 것은 실력에 격차가 있는 상대의 겨룸을 보는 것도 굉장히 많은 도움이 된다는 사실이다.



1. (
진중권 말로) '프로'가 토론을 대할때 얼마나 많은 준비를 하는지 알게 되었다. 내가 깜짝 놀란 부분은, 간결이 정수 장학회의 이사장 임명권을 교육감이 가진다고 말했을때 진중권이 바로 네이버 백과사전을 인용하며 간결이 알고 있는 팩트를 수정해준 장면이었다. 영상을 보아 진중권은 실시간으로 네이버 백과사전을 검색하지 않은 것으로 보인다.(만약 실시간으로 검색했다면 이 글이 뻘짓이 되는 것이다.) 그렇다면, 진중권은 토론전에 정수 장학회의 이사장 임명권을 누가 가지고 있는지 - 이 사소해보이는 -를 검색해보고 나왔다는 것이다. 토론이란 1차적으로는 팩트의 싸움이기 때문에 토론자는 많이 자료를 얻고 머리속에 충분히 숙지한 후 무대에 올라간다. 그들에게 팩트의 양과 질은 전쟁에 참전하는 병사의 총알과도 같은 것이다. 진중권이 토론에서 사뿐히 승리한 이유는 일단 말빨이 좋은 것도 있겠지만 원체 아는게 많은 양반이고, 그런 양반이 간결보다 더 많이 준비를 하고 무대에 올라왔다는 것에 있다. 





2. '
프로'가 토론을 대할때 상대방 말과 상대방의 논리를 찾는 것에 많은 기운을 쏟는 다는 것을 알았다. 그러니까 귀가 좋아야 한다는 것이다. 영상을 보라. 진중권은 상대방의 말을 듣고 있는데, 상대는 진중권이 말할때 자신이 말할 부분을 찾는 것 처럼 보인다.(인터넷으로 도움을 받고 있는 것처럼 보이기도 한다.) , 1차적으로 토론이란 팩트의 싸움이기에 총알의 양과 질에서 승부가 결정되는 경우 대부분이다. 그런데 이경우 싸움은 대부분 판정승 정도로만 승패가 결정되지만 결정적인 KO승은 상대가 상대방의 논리에 의해 스스로 자멸 할때 나온다.(이 영상에서는 이런 장면이 크게 2번 나온다.) 이것은 자기가 자신의 총으로 스스로를 쏘는 격이고 이 상태에 빠지면 그야말로 멘붕 상태에 빠지게 된다. 그런데, 이것이 가능하려면 졸라- 잘 들어야 한다. 이는 상대방의 무기가 뭔지 잘 알아야 그 무기를 상대에게 향하도록 할 수 있기 때문이다. 이 영상은 이것을 가르쳐 주었다. 반대로 자신이 이 상황에 빠지지 않으려면 인내심이 있어야 한다. 자신이 이 토론에서 사용할 무기가 무엇인지를 아껴두어야 한다. 이 토론이 이렇게 끝난것은 토론 내의 요소도 있겠지만 토론 전에 자신의 무기가 뭔지 훤히 보여줬다는 것에서도 찾을 수 있다. 그는 인내심이 너무- 없었다. 그는 진중권의 약점을 알고 있다고 자신했고 그것에 그치지 않고 맙소사, 그것을... 공개해버렸다

흥미로운것은 진중권도 토론을 시작된 후 바로 자신이 꺼내들 무기를 언듯 보여주었다는 것에 있다. "당신들이 문제삼은 것이 이것이고 나도 그것을 인정한다. 그런데 이것을 인정하면 당신들은 더 큰 위기에 봉착한다." 허나 간결은 그 말을 전혀 듣지 않고, 진중권에게 강의식으로 말하지 말라면서 자기 할말을 한다. 그때 간결은 진중권이 말을 계속 하게 하면 안된다라고 생각한 것 같은데 큰 실수였다. 사회자가 끊지 않는한 사람말을 끝까지 듣는게 좋다




3.
실수에 대한 인정은 빠르면 빠를 수록 좋다. 자신이 잘못 알고 있었다는 것이 밝혀지면 빠르게 자신의 의견을 버려야 한다. 바둑에서는 싸움에서 이기기 위해 심지어 대마도 죽인다.(물론 다른 큰 것을 얻을 수 있다고 확신한 경우에만) 그리고 자기가 승리할 수 없다는 것을 알았을 때는 겁나 빠르게 돌을 던져 자신의 패배를 보인다.(기권이란 제도는 패자를 위해 있는 것이다.) 진중권이 자신의 실수를 인정하는 모습을 보라. ~하게 인정한다. "맞아요, 그건 내가 잘못 알고 있었어요." So Cool! 그러나 그는 더 큰것을 얻어왔다. 코딱지를 내주고 살점을 얻어왔다. 반면 간결은 어떤가? 자신이 이길 수 없는 싸움을 계속 끌고 간다. 맞지 않아도 될 매를 계속 맞는다.(아 보는 사람이 힘들어 그만해 그만해!!) 이럴 필요 없다. 질것 같은 부분은 빨리 버리고 에너지를 충전해놔야 한다

 

, 여기서 또 언급하고 싶은 부분은 살을 주고 뼈를 얻기 위해 프로가 자주 함정을 설치한다는 것이다. 이런걸 덥석 물지 않기란 굉장히 어려운 것이어서 많은 참을성이 필요하다.(일단 인간은 허영심이 가득하니까.) 이런 참을성을 기르려면 2번이 선행되어야 할 것이다. 상대의 말을 잘 듣고, 큰 싸움이 뭔지를 알고 있어야 한다. 진중권은 이것을 간결에게 명백히 보여줬다.




이것은 다른 것인데, 진중권이 나꼼수를 왜 경계하는지 알게 되었다. '소설'쓰지 말라는 것이다. 현재 나와있는 사실로도 공격할 거리가 무궁무진한데 왜 소설을 써서 - 팩트가 아닐 수도 있는 사실을 말해서 - 자기 편의 약점을 만드냐는 것이다. 상대편의 패배를 보니 그것의 위험성을 알 것 같다. (물론, 나꼼수는 자신들이 소설쓴다는 것을 잘 알고 있다는 지점에서 간결분과 차이점을 보인다.)



여튼 60분여 되는 영상을 보며 시간이 금방 갔다. 진중권님보다도 간결님에게 참 감사하다. 일단 간결은 용기가 있었다. 그 용기는 참으로 갖기 힘든 것이다. 그리고 간결은 자신이 이 싸움에서 졌다는 것을 알 정도의 지성은 있었다. 그정도의 지성을 갖춘 사람도 많지 않다. 이제 후폭풍을 이겨내면 아마도 훌륭한 사람이 될 수 있을 것이라고도 본다.(학력도 좋고, 생기기도 잘 생겼다.) 파이팅! 



-
-

 

저작자 표시
신고

아이고. 이 글을 이제야 쓰게 되었습니다. 죄송합니다. 그동안 이 주제에 대해 개인적으로 물어보시는 분들에게는 메일로 답변 드렸는데, 답변 내용을 여기에도 올립니다.

 

가상함수 후킹과 COM 후킹은 근본적으로 동일하니 같은 방식을 사용하시면 됩니다.

 

Detours로 후킹 할 때 가장 중요한 것은 후킹의 대상이 되는 Function의 주소를 찾는 것입니다. 그런데 아시다시피 가상함수의 주소는 일반적인 방법으로 알 수 없습니다.

 

직접 프로그램을 만들어 디버깅 한 후 가상함수와 Object간의 Offset을 알아내야 해당 가상 함수의 Real주소를 알게 됩니다.

 

Real주소를 알았다면 Detours로 후킹하는 것은 간단하지요.

 

 

아래에서 큰 글자로 표시된 부분을 살펴보시기 바랍니다. Pvtbl 은 가상함수 테이블의 주소이고, -25888 값은Offset입니다.

 

 Offset을 확인하려면 한번 가상함수를 디버깅해 해당 주소를 알아 낸 후 해당 주소 – pvtbl 을 계산하면 됩니다.

 

COM Object또는 가상 함수를 가지는 객체가 변경되지 않았다면 이 Offset값은 항상 고정입니다.

 

#include <stdio.h>

#include <Windows.h>

#include <detours.h>

static PDETOUR_TRAMPOLINE Trampoline;

 

 

class CMember{

public:

           void Target(void);

};

 

void CMember::Target(void){

           printf("Cmember :: Target! (this:%p)\n"this);

 

}

 

class CDetour{

public :

           void WINAPI Mine_Target(CHAR* string);

           static void (CDetour::*Real_Target)(void);

};

 

void CDetour::Mine_Target(CHAR* string){

           printf("  CDetour::Mine_Target! (this:%p)\n"this);

           //__asm push edx;

           __asm mov eax, [esp];

           __asm push eax;

           __asm push eax;

           __asm call [Trampoline];

           //void (WINAPI *OriginalTarget)(CHAR* ) = (void (WINAPI *)(CHAR*))((PBYTE)Trampoline);

           //OriginalTarget(string);

}

 

void (CDetour::*CDetour::Real_Target)(void) = (void (CDetour::*)(void))&CMember::Target;

 

void WINAPI testMethod(CHAR* test){

           printf("general method %s\n",test);

}

 


 

class IInterface{

public :

           virtual void WINAPI test(CHAR* string)=0;

};

 

class ImplementClass : public IInterface{

public :

           void WINAPI test(CHAR* string){

                     printf("test test");

           }

};

 

 

 

int main(intchar**){

           LONG error;

           IInterface* inter;

           ImplementClass imple;

           inter = &imple;

       void* pvtbl;

       memcpy(&pvtbl, inter, sizeof(pvtbl));

    DetourTransactionBegin();

    DetourUpdateThread(GetCurrentThread());

       PBYTE tmp = (PBYTE)pvtbl - 25888;

           PVOID DetourPtr;

           PVOID TargetPtr;

       

           testMethod("asdfadsf");

         

           inter->test("test -- before detourAttach");

          

           DetourAttachEx(&(PVOID&)(tmp),(PVOID)(&(PVOID&)CDetour::Mine_Target), &Trampoline, &TargetPtr, &DetourPtr);

           error = DetourTransactionCommit();

          

 

           inter->test("test----inner virtual function");

 

    if (error == NO_ERROR) {

        printf("\n");

    }

           else{

                     printf("fail to attach\n");

           }


 

 

           getchar();

           return 0;

}

저작자 표시
신고

성경을 어떻게 읽어야 하나.

분류없음 2012.07.06 18:03 Posted by 아일레프

성경은 서로 다른 저자가 작성한 66개의 소책자로 이루어져 있는데, 기독교 인들은 각기 다른 그 책이 모여 하나의 통일된 메시지를 알리고 있다고 믿는다. 한국 보수주의 교단에서는 이 성경이란 책은 오류가 없으며 그 자체로 하나의 완벽한 진리다라고 가르친다. 이 오류가 없다는 것은 전체적인 메시지 차원의 오류가 없음 뿐 아니라 각각의 문장에도 오류가 없음을 말한다.

 

난 성경 그 자체를 진리라 여기는 의견에 반대한다. 진리란 단어에는 공간과 시간을 뛰어 넘는 보편성이란 의미가 숨어 있다. 그런데 성경을 읽는 수 많은 독자가 있고 그 중 몇몇은 나름대로 생각하는 '진리'를 발견했다고 생각하면서 명은 그것으로 종파를 만들었다. 그런데, 각각의 독자가 읽어낸 진리가 서로 다르고 모순 된다면, 이것은 어떻게 봐야 할까?

 

놀랍게도 성경은 모순 투성이다. 독자들은 독자가 원하는 결론을 그곳에서 언제든지 이끌어 있다. 전쟁을 원하는 사람은 그에 맞는 구절을 찾을 있고, 평화를 원하는 독자 역시 마찬가지다. 그런데 이처럼 성경에서 이끌어낸 2개의 의견이 있고 그것들이 서로 모순된다면 하나는 옳은 것이고 하나는 그른 것이다. 그런데 어떻게 2 모순된 의견에서 진짜를 고를 있는가? 서로 모순된 의견들 사이에서 우린 그것들 "진짜" 골라 있는 방법이 과연 있는 것인가? 이것은 불가능하다. 모순된 의견이 존재할 그것 하나를 택하는 기준이 어디까지나 개인의 가치 판단에 있기 때문이다. 의견을 이끌어 내는 것도 개인의 가치 판단이며 여러 의견 하나를 진리의 위치에 올리는 것도 개인의 가치 판단이다.

 

이것의 근본적인 이유는 앞서 말했듯이 성경 전체가 일관된 메시지를 보여주지 않기 때문이다. 물론 성경의 메시지가 일관 되다고 말하는 사람도 있다. 성경이 창조, 타락, 구속의 과정 속에서 나타난 하나님과 예수님을 온전히 보여준다고 생각한다. 그런데 이것은 너무나 러프한 해석이다. 교회의 목사들의 역할 하나는 삶에 시시각각 나타나는 딜레마를 해결할 있는 관점과 선택지를 말하는 것에 있는데, 이런 상황에서 "창조, 타락, 구속"이란 러프한 일관적 메시지는 전혀 도움이 되지 않는다. 동성애, 사형제도, 성매매와 같이 우리가 대면하는 가치판단 / 의사 결정에 성경은 일관적인 근거를 전혀 제시해주지 않는다.

 

그런데 이런 성경의 특징, 서로 상의한 이견에 대한 근거를 얻을 있는 텍스트라는 성질에 성경이 이렇게 오래 동안 읽히고 드높은 자리를 차지하는 원인이 있다. 이유는 자신의 가치 판단의 근거로 성경이 훌륭히 활용될 있기 때문이다. 게다가 성경에는 권위가 있다. 때문에 어떠한 의견은 보다 높은 권위를 가지게 된다.

 

내가 말하고자 하는 바는 개인이 생각하는 '가치' 성경이나 신보다 선행한다는 것이다. 신에 대한 이야기를 해보자. 기독교인들은 신을 최고의 자리에 놓고 있다지만 사실 그들이 가장 높은 자리에 올려 놓은 것은 신이 아니라 자신의 '가치'이다. 기독교 인은 '악한' 신을 자신의 머리 속에 그려 상상하고 싶어하지 않는다. 구약의 신을 보며 때때로 혼란스러움을 느끼는 것은 바로 그것 때문이다. 우리가 가지고 있는 신 관념에는 그것보다 선행되는 가치가 숨어 있다. (난 이것을 지극히 당연하다고 생각한다.)

 

그런데 문제가 되는 부분은 그 뒤에 발생된다. 가치가 선행 되었고 그것에 어울리는 관념은 뒤따라 온다. 그런데 신이 가지는 그 권위에 의해 선행된 가치, 또는 주장이 신적 권위를 얻게 되는 것이다. 그런데 그 가치란 무엇인가? 그것은 독자가 보고 싶고, 읽고 싶고, 해석하고 싶어하는 가치이다. 그것은 성경 이전에 있었다. 그러니까 말하자면 성경이란 개개인이 옳다고 생각하는 특정 '가치' 아름답게 보여주기 위해 이용되는 도구에 불과 하다는 것이다. 다시 말하면, 성경은 그 자체로 완전한 메시지를 말하지 못하며, 단지 자신의 메시지에 신적 권위를 싣고 싶은 사람들에게 끊임 없이 이용될 뿐이다. 성경은 '말하는 독립자' 아니며 '이러 이러하게 읽히는 ' 불과 하다.

 

때문에 어떤 사람이 성경을 보고 이끌어낸 의견으로 우린 단독자, 신의 의견이 아닌 사람과 만날 뿐이다. 그러면 어떻게 해야 하나? 사실이 이렇다면 매번 우리는 성경에 대해 더욱 다양한 해석들을 창조하고 그것에 대해 자유롭게 논의 해야 한다. 그리고 그것에 어떠한 권위를 싣는 것을 삼가야 한다. 성경에 대한 권위를 낮추고 개인 의견에 대한 권위의 위치를 올려야 한다. 그래야 서로 평등한 위치에서 토론과 논쟁이 가능하며 그것으로 좋은 의견을 이끌어 있다. 성경 해석을 목사라는 권위자에게만 남겨서는 안될 것이다. 그리고 해석에 신적 권위를 부여해선 안될 것이다.

 

그러니까 최종적으로 말하고 싶은 것은 바로 지젝의 메시지. "우린 처음부터 다시 시작해야, 처음 부터 다시 사유해야 합니다."

저작자 표시
신고
TAG 성경

에덴의 용


에덴의 용은 뇌 과학에 대한 이야기이다. 더불어 진화 심리학에 대해서도 약간 맛볼 수 있다. 책에서 가장 인상적인 부분은 바로 동물도 언어로 인간과 의사소통을 할 수 있다는 점이었다. 책에는 침팬지의 예가 나온다. 연구자가 침팬지에게 수화를 가르쳐줬더니 침팬지가 그 수화를 제법 잘 사용하고, 가르쳐준 단어를 응용해 새로운 문장을 창조해내기도 하더라는 결과는 내게 제법 충격적이었다. 거기에 더해 자신에게 수화를 가르쳐준 인간이 멀리 떠나게 되자 침팬지가 그를 보며 한 수화의 내용 - 침팬지는 수화로 Don't Cry me라고 말했다고 한다 - 은 가슴을 찡하게 했다. 

인간의 뇌에 대해 몰랐던 사실을 알게 해준 좋은 책이었다. 


의식(하룻밤의 여행시리즈)

하룻밤의 여행 시리즈의 특징은 짧은 쪽 수 안에 주제와 관련된 여러가지 논의가 서로 대화하듯이 쓰여있다는 점에 있다. '의식'또한 마찬가지 인데 이 책 안에서는 인간의 의식을 대하는 두가지 방식에 대해 최대한 중립적으로 서술하고 있다. 


인간의 의식을 생각해볼때 두가지 방식이 있을 수 있다. 하나는 이원론 이며 또다른 하나는 일원론이다. 

이원론은 인간은 육체와 영혼으로 나누어져 있다는 입장이며 일원론은 인간은 오직 영혼, 또는 관념 이거나 오로지 '물질'이라는 입장이다. 


인간이 육체와 영혼으로 나누어져 있다는 것은 대부분의 인간이 가지고 있는 일종의 믿음이다. 사람이 죽어서 그 영혼이 천국, 또는 지옥으로 가거나 다시 환생한다고 믿는다면 당신은 이원론자이다. 그런데 이 이원론은 중요한 문제가 남는데 비물질인 영혼과 물리적인 법칙에 지배받는 물질이 어떻게 상호 교류하는가 이다. 이것은 정말 해결할 수 힘든 난제로 보인다. 


일원론 중에 관념론은 사람이 구성하고 있는 세계란 일종의 '상상' 또는 '환상'일 뿐이라는 의견이다. 이 관점에서 보면 처음부터 물질이란 없는 것이다. 다만 이렇게 생각하고 나면 어떤 문제가 남는가면, 이 세상의 구성원은 정말 다양하고 수 없이 많은데 어떻게 이들의 '상상' 또는 '환상'이 이렇게 완벽하게 교류할 수 있는가 하는 문제이다. 


일원론 중 유물론은 인간을 구성하는 것은 오로지 '물질' 이라는 것이다. 이에 따르면 인간이 가지고 있는 생각은 모두 물리적인 반응에 불과하다. 이 유물론을 극단적으로 몰고가면 내가 글을 쓰는 것도, 지금 내가 느끼고 있는 것도 모조리 다 물리적인 인과관계에 의해 설명될 수 있다는 것인데 이것은 가장 그럴듯 하나 역시 난제가 있다. 뇌를 살펴보면 우리가 하는 행동이 어떻게 신호가 가고 그것이 어떤 신경 체제를 통해 전달되어 행동으로 나타나는지 설명할 수 있다. 그러나 우리가 가지고 있는 '기억', 그리고 '감상'이 어떻게 저장되고 인간의 행동에 어떠한 영향을 미치는 지는 알 수 없다. 특히나 기억이 뇌의 어느 부분에 저장되는지 밝혀진 바가 없다. 이것이 만약 밝혀 진다면 놀라운 들이 벌어질 것이다. 왜냐하면 이것이 가능하다면 기억을 물리적인 것으로 환원할 수도 있기 때문이다. 따라서 내가 기억하고 있는 어떤 기억을 사진이나 텍스트로 뽑아 낼 수도, 혹은 가짜 기억을 인간에게 삽입 할 수도 있을 것이다. 그렇게 되면 정말 무서운 세상이 올 것이다. 부디 그런 날이 오지 않기를 바랄 뿐. 


AI가 가능할 것이라고 생각하는 사람은 이 의식 책을 꼼꼼이 읽어 보길 권한다. 많은 사색을 할 수 있는 좋은 책이다. 




저작자 표시
신고

베네치아에서의 죽음, 토마스 만


믿음사편 토마스 만 단편집을 읽은 후 내용이 제법 좋아 열린책들 '베네치아에서의 죽음'을 다시 보았다. 여러 단편들이 수록되어 있는데 재미도 있고 생각할 거리를 던져 주었다.


토마스만은 단편 대부분은 사람들을 '예술가' 와 '일반자'로 구분한 뒤 '예술가'의 비애를 소개하는 것으로 줄거리가 이루어져 있다. 그들의 비애란 무엇일까? 그것은 바로 인식하는 자, 관찰하는 자로서만 존재하고 참여하는 자, 행동하는 자로 존재할 수 없는 사람의 비애를 말한다. 


대부분의 주인공들은 예술가 지만 일반 사람 '시민'이라 표현되는 그들의 삶을 부러워하고 동경한다. 물론 예술가로서 자신에 우월감을 느끼며 남들을 아래로 내려 보지만 그것은 어느새 슬픔으로로 변하게 된다. 


나와 같이 허영심으로 문학과 예술을 즐기는 사람들은 이들의 마음을 결코 알 수 없을 것이다. 천재들, 예술가들, 또는 재능은 없으나 예술가로 태어난 사람들의 마음을 난 글로 추측할 뿐 감히 알 수 없다. 


...

그리고 알고 싶지만, 실제로 느끼고 싶지는 않다. 지금도 때때로 고독하고 외로우니까.  

저작자 표시
신고

6월 첫째주 독서노트

즐거운 책 이야기 2012.06.08 09:16 Posted by 아일레프

읽지 않은 책에 대해 말하는 법, 피에르 바야르.  


흥미롭게 읽고 있음. 책 초반에 움메르토 에코의 '장미의 이름'을 자신만의 방식으로 독특하게 해석하는 저자의 시각에 놀랐음.  

더 읽어 봐야겠지만 1/3쯤 읽은 시점에서 저자는 이렇게 말하고 있는 것처럼 느껴짐. 

"온전히 텍스트만을 바라보는 독서란 가능한 것인가?" 

바야르는 저것이 애초에 불가능하다고 보고 '사람은 자신의 삶(또는 자신)과 텍스트를 완전히 분리해서 볼 수 없다라는 전제'를 확장 하고 확장해 나름 대로의 의견을 제시하고 있음. 그런데 그 의견이 굉장히 흥미로움.  

"책읽기"에 대한 책을 좋아하는 분이면 재미있게 읽을 수 있으리라 생각함. 



죽음과 소녀, 아리엘 도프르만 

'경계선 너머' 까지 읽었는데 개인 적으로 '경계선 너머'만 맘에 들고 '과부들'과 '죽음과 소녀'는 그저 그랬음.  

과부들이 재미 없었던 이유는 그 당시 내 심리적 상태가 이런 이야기를 읽기 힘든 상황이라 줄거리의 흐름을 제대로 따라가지 못했기 때문이라고 스스로 유추하고 있음. 연극을 기대함.  

죽음과 소녀는 음. 재미있었으나 주제 자체가 흔하고, 결론도 예상에서 크게 벗어나지 않아 내게 큰 임팩트를 주지 못했음. 

경계선 너머는 재미있었음. 우리나라의 사정에도 잘 맞아 이해도 잘 가고, 중간 중간 나오는 유머에 킥킥 대며 웃을 수 있었음. 실제 연극을 보는 것 처럼 쉽게 상상할 수 있게 만들어 줘서 참 좋았음.  

이제 연옥을 기대함.  



기적의 시대, 보리슬라프 페키치  

이전에 사놓은 책. 처음 읽었을 때는 내 안의 기독교 단물이 아직 많이 남아 있어 조금 읽고 그냥 덮었었는데 어제 몇 편을 읽음.  

이건 정말 패러디의 극강임. 삐딱하게 생각하기의 극강이라고도 볼 수 있을 것 같은데, 중간 중간 웃음이 나오다가도 각 이야기의 마지막을 읽을 때면 정말 오싹 오싹 함.(게다가 그 반전을 일으킨 장본인이 사실 기적을 행한 예수이니.. 거참. ) 

음.. 오싹한 기분을 느끼고 싶은 기독교 인은 한번 읽어 보기 바람. (살 필요까지는 없고 서점에서 이야기 하나 정도만 읽으면 괜찮을 듯 싶음)  



저작자 표시
신고

boost, io_service 그리고 io_service.run()

프로그래밍 2012.02.17 22:09 Posted by 아일레프

boost, 엄청 훌륭하다. 정말 좋다. C++의 놀라움은 C#의 Linq와 같은 기능을 추가적인 프로그램 언어 문법이 없어도 (lambda와 같은) meta programing 으로 구현할 수 있다는 사실에 있다. 물론 Template 코드인 특정 함수가 Parameter로 어떤 Type을 원하는지 알기란 나같은 초짜로서는 너무 버겁다. 하지만 이때 탓해야 할 것은 부족한 나. boost그리고 그것이 가능하게 하는 c++언어는 눈물 나도록 아름답고 내가 완전히 그것을 정복하기란 불가능해 보일 정도로 높은 위치에 있다. 엉엉엉

여기까진 순전히 사족이고, 본론을 시작한다. 요즘 TCP/IP Socket 프로그래밍 할 일이 계속 생겨서 이 boost를 계속 활용하고 있는데, 정말 어이 없는 실수로 4시간을 소비했다. 이것이 실수인지 판단하기 위해 얼마나 많은 시간을 허비했는지. 엉엉엉.

io_service는 Thread Pool과 비슷한 역할을 한다. boost::asio::ip::tcp::socket은 이런 io_service의 성질을 이용해 async_write, async_read를 구현할 수 있다. 자, 그럼 내가 고생했던 사항을 여기 간단하게 고쳐 적어본다.

boost::asio::io_service io_service;
TcpIpClientManager manager(io_service);
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
io_service.post(boost::bind(&MainStarter::PostProcessUsingIoService, this));

자, 위 코드의 의도는 알 것이다. TcpIpClientManager라는 객체는 TCP/IP통신을 위해 만든 객체이다. 내부에 private boost:asio::ip::tcp::socket을 가지고 있다.

io_service.run()을 호출하면 주 thread가 Block되므로 thread로 돌렸다. 그리고 PostProcessUsingIoService라는 함수를 io_service에 맡겨 실행되게 했다.

자, 어떤 일이 벌어질까? 이 코드의 문제점은 무엇일까? 이 경우 PostProcessUsingIoService 함수는 실행 될 수도 있고, 실행되지 않을 수도 있다. 무슨 말인고 하니, TcpIpClientManager 내부에서 io_service를 사용해 어떤 활동을 하는 코드가 실행 중이라면 저 코드는 실행된다. 그런데 만약 io_service를 사용하는 모든 일들이 종료 되었다면 저 코드는 실행되지 않는다. io_service가 run 상태가 아니기 때문이다.

이것은 io_service문서를 조금만 주의 깊게 보면 알 수 있다.

Some applications may need to prevent an io_service object's run() call from returning when there is no more work to do. For example, the io_service may be being run in a background thread that is launched prior to the application's asynchronous operations. The run() call may be kept running by creating an object of type io_service::work:

젠장. 왜 document를 보지 않고 예제만 보고 프로그래밍을 했기에 이와 같은 멍청한 일이 생겼다. 나를 탓할 수 밖에 없다. 그래서, PostProcessUsingIoService가 t실행 되는 것을 항상 보장 하려면 - 즉 io_service가 항상 run상태이게 하려면 - 다음과 같이 work를 사용해야 한다.

boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
TcpIpClientManager manager(io_service);
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
io_service.post(boost::bind(&MainStarter::PostProcessUsingIoService, this));

결론.

Document는 졸라 중요합니다. 당신의 소중한 시간을 낭비하지 않기 위한 최선의 방법은 당장 구현된 예제를 보는 것이 아니라 Document를 보는 것입니다.

신고

책장에는 체리 신드롬 이라는 만화가 있습니다. 이것은 19 인데요, 대부분 그럴 듯해 보이는 책들과 뭔가 철학이 있을 같은 책들 가운데 끼어있는 책은, 굉장히 놀랍습니다. 왜냐하면 만화가 '금기' 보통 사람들이 생각하기에는 받아들여 없는 금기를 하나도 아닌 두 가지를 짬뽕해서 굉장히 묘하고도 아름답게 풀어내고 있기 때문입니다. 이야기를 읽고 있으면 이야기가 더럽다거나, 몹쓸 일이라거나 그런 생각이 들지 않습니다.

 

 예전 아다치 미쓰루의 만화, 제목이 기억이 안 나는데 그것도 이와 비슷하게 '금기' 소재로 삼고 있었습니다. 바로 아버지와 딸의 사랑을 다루었는데요, 물론 친딸은 아니었습니다. 그런데 만화는 감흥을 주지 못했습니다. 서사가 조금 억지스러운 구석이 있다고 느껴졌기 때문입니다. 우린 "롤리타"라는 유명한 소설도 알고 있는데요, 소설도 제겐 별로였어요. 주인공의 마음을 받아들이지 못하겠더군요. 자식은 변태에요. 도저히 그와 하나가 되어 이야기를 읽어 나갈 없었었습니다. 그런데 체리 신드롬의 이야기는 어떻습니까? 말도 안될 법한 설정이 부드럽고 유연하게 흘러갑니다.  에피소드 하나 하나가 즐겁습니다. 캐릭터는 어떻습니까? 평범한 남성을 대표하는 듯 보이지만 강단 있고 멋진 남자 주인공, 너무나도 섹시하고 매력적인 그의 여자친구, 어려지는 병에 걸려 초등학생의 외모를 가진 귀엽고 귀여운 하나의 여자친구. 모두들 사랑스럽고 생기가 넘칩니다.

 

일부일처제의 사회에서 결코 받아들여질 없는 남자 한 명과 여자 명의 트라이앵글 같은 사랑, 그리고 3 간의 섹스, 그리고 롤리타. 이것은 금기입니다. 이런 것들이 사회에서 받아들여지면 안돼요. 그런데 이러한 것들은 이야기와 함께 있을 때 있을법한 일로 변해버립니다. 우리가 흔히 남이 하면 '불륜', 내가 하면 '로맨스' 라는 말을 하죠? 이것은 너무나도 당연한 겁니다. 우리는 남의 불륜을 보면 단어 자체가 가지는 부정적인 이미지만을 떠올리고 사람들을 도덕적인 잣대로 평가합니다. 그런데 자신의 불륜은 단어 자체로 읽히지 않거든요. 그것은 단어 아니라 자신이 놓여진 여러 가지 상황과 맥락, '이야기'속에서 읽혀집니다. 그렇다면 그것은 과연 로맨스 것이지요. 그것은 자신에게 도덕을 뛰어넘는 사랑의 경험이 있게 됩니다.  그것이 이야기, 서사가 가지는 힘입니다. 맥락과 상황 안에서 금기는 있을 법한 일이 잠시 머릿속에서 현실이 됩니다. 그리고 즐겁게 하지요. ,우를 갈라야 하고 ,백을 논해야 하는 현실 속에서 잠시 벗어나 도덕과 금기를 벗어나 무지개 회색지대로 가봅시다. 채리 신드롬은 좋은 만화입니다

저작자 표시
신고

기억의 습작, 김동률

노래이야기 2012.01.09 12:40 Posted by 아일레프


 
시간이 지나도 선명히 남아 여전히 내 앞의 다른 이와도 우월한 위치를 차지하는 너. 시간이 지날 수록 아름다운 기억만이 남아 널 떠올리게 해. 내가 가진 추억과 기억은 널 예쁘게만 그려 놓았기에 내 꿈은 미래에 있지 않고 과거의 너에 놓여있어.
슬픈 일이 있을 , 아픈 일이 있을 무엇보다 기억이 스스로 무너지지 않게 지탱해 주는 힘이 되는걸.

하지만 이것이 어떤 의미를 가질 수 있을까. 너의 마음 속으로 들어갈 수 있다면 과연 알 수 있을 텐데. 철 없던 그 때의 내 모습이 네게 어떤 의미가 되었는지. 지금 네게 그려진 내 모습은 어떤지. 난 이것으로 네게 갈 수 있을까. 그때의 그 커다랗던 꿈으로. 지금의 기억으로. 네게로. 너에게로
 

저작자 표시
신고

약자의 사랑, 강자의 사랑

끄적끄적 2012.01.09 12:30 Posted by 아일레프


어떤 사람들은 나약해서 상대방의 마음을 얻고자 직접 실행에 옮길 수 없다
. 이것을 나약하다고 표현한 이유는, 실행에 옮길 없는 가장 이유가 자기 방어 심리에서 기인하기 때문이다. 거절 당했을 느껴지는 비참함을 감당할 만큼 그들은 강하지 못하다. 때문에 그들은 그냥 기다릴 뿐이다. 자신에게 호감을 표하고 나아가 자신을 좋아하고 사랑해 이성을 기다린다. 그리고 그 이성이 내게도 마음에 들기를 그저 기다릴 뿐이다. 기다림 끝에 어떤 이가 나타나 자신에게 호감을 표한다고 해도 또는 그녀가 자신을 진정 좋아하는지 확신이 서기 전에는 움직이지 않는다. 이런 사람은 근본적으로 약하며, 또한 그렇고 내가 보는 많은 사람들도 범주에 든다.

 

그런데 때때로 강한 사람들이 있다. 이 사람은 자신을 좋아해줄 누군가를 찾기보다 자신이 좋아할 누군가를 찾는다. 그리고 그 또는 그녀를 발견 했을 때 실행에 옮긴다. 때문에 그들은 강하다. 그들은 원하는 것을 가질 있고 자신이 사랑하는 이의 사랑을 받을 있다고 감히 믿는다. 이 강한 사람들은 자신의 모습을 남에게 호감을 받을 수 있는 형태로 굳이 변경하거나 꾸밀 필요가 거의 없다. 다만 그들은 찾을 뿐이다. 마치 찾기만 하면 그 또는 그녀를 내 것으로 만들 수 있다는 듯이 찾는다.

 

강한 이가 자신을 꾸밀 필요가 거의 없는 반면 약한 이들은 자신을 꾸며야 하고 남에게 호감 있는 모습을 유지하기 위해 자신을 억제해야 하고 때로는 자신이 하기 싫은 일을 해야 한다. 사회가 원하는 틀에 들어갈 있도록 자신의 모습과 색깔을 바꾼다. 때때로 그들은 이러한 행동을 "준비한다."라고 말한다. 그리고 이것은 자신의 짝을 많이, 빨리 만나는 그들 최고의 전략이다. 자신의 모습을 그렇게 예쁘게 꾸며 놓은 뒤 그들은 기다리고 기다린다. 자신의 정성스런 치장에 넘어올 하나의 이성을.

 

내가 약자, 강자로 이 형태를 분류한 이유는 약자가 수동적으로 행동한다면 강자는 능동적으로 행동하기 때문이고 약자가 기다린다면 강자는 창조하기 때문이다. 약자가 자신을 사랑해줄 사람을 기다린다면 강자는 사랑할 사람을 찾는다. 약자는 사랑 받길 원하지만 강자는 사랑하길 원한다. 그리고 지금껏 약자로써의 사랑을 꿈꿔 왔지만 이제 강자로서의 사랑을 꿈꾼다. 나여, 내가 사랑하게 하라.

저작자 표시
신고
TAG 사랑

이전 글

Detours Hooking Framework 1 - Hooking Function

Detours Hooking Framework 2 - Hooking API

Detours Hooking Framework 3-1 Hooking class member function

 

잠시 복습

지난 글에서 class member function을 호출했을 때 컴파일 된 assembly에 대해 살펴보았다. 나머지는 전부 제쳐두고 다음을 기억하면 됐다.


    int result1 = a.TestFunction(10);
008E1612  push        0Ah  
008E1614  lea         ecx,[a]  
008E1617  call        TestClass::TestFunction (8E111Dh)  

class의 instance의 주소를 ecx에 넣고 해당 함수를 콜 한다는 규칙이었다. 자, 이것을 기억해두고 class member function을 후킹해보자.

 

 

Hooking class member function


#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <detours.h>

class TestClass{
private :
    int mInt;

public :
    TestClass() : mInt(0){}
    TestClass(int initValue) : mInt(initValue){}
    int TestFunction(int iValue);
};

int TestClass::TestFunction(int iValue){
    return mInt + iValue;
}

int HookingFunction(int value){
    std::cout<<"Hooked" <<std::endl;
    //여기서­ 진짜 멤버 함수를 호출해야한다.
    return 0;
}
int main(int argc, char** argv){
    TestClass a(10),b(20);

    int result1 = a.TestFunction(10);

    return 0;
}


위 코드에서 TestClass의 TestFunction함수를 후킹해보자. Detours를 사용하기 위해 TestClass::TestFunction과 HookingFunction의 address를 넘겨야 한다. 이때 main()함수의 코드는 다음과 같게 된다.


int HookingFunction(int value){
    std::cout<<"Hooked" <<std::endl;
    //여기서 진짜 멤버 함수를 호출해야 한다.
    return 0;
}

PVOID functionAddress ;

int main(int argc, char** argv){
    TestClass a(10),b(20);
    functionAddress = (PVOID)(&(PVOID&)TestClass::TestFunction);

    DWORD error;
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*)&functionAddress, HookingFunction);
    
    error = DetourTransactionCommit();

    int result1 = a.TestFunction(10);

    return 0;
}

위 작업으로 a.TestFunction(10)을 실행했을 때 HookingFunction이 호출되게 된다. 기존 TestClass::TestFunction이 존재하던 Address에 jmp HookingFunction이란 코드가 대치되었기 때문이다. 그런데 HookingFunction코드 내에서 어떻게 TestClass::TestFunction을 실행할 수 있을까?

 

TestClass::TestFunction 을 실행하기 위한 조건은 다음과 같았다. 먼저 TestClass의 instance, a의 주소를 ecx에 넘겨 주어야 하고 TestClass::TestFunction의 address를 call해야 한다. 그런데 문제가 있다. a의 주소를 HookingFunction 내에서 알아낼 방법이 없다는 것이다. a의 주소는 ecx로 넘어 오기 때문에 알 수 있지 않냐고 생각할 수 있겠지만 ecx를 일반함수 HookingFunction에서 보존한다는 보장이 없기 때문이다. 만약 ecx값을 보존한다면 다음과 같이 코딩하면 되겠지만 이는 실패한다.


int HookingFunction(int value){
    std::cout<<"Hooked" <<std::endl;
    int result;
    __asm push value;
    __asm call [functionAddress];
    __asm mov [result], eax;
    return result;
}


그럼 어떻게 해야할까? 답은 의외로 간단하다. 일반 함수로 Hooking하는 것이 아니라 클래스의 멤버함수로 Hooking하면 된다. 다음과 같이 말이다.


class HookingClass{
public:
    int HookingFunction(int value){
        std::cout<<"Hooked" <<std::endl;
        int result;
        __asm push value;
        __asm mov ecx, [this];
        __asm call [functionAddress];
        __asm mov [result], eax;
        return result;
    }
};


int main(int argc, char** argv){
    TestClass a(10),b(20);
    functionAddress = (PVOID)(&(PVOID&)TestClass::TestFunction);

    DWORD error;
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*)&functionAddress, (PVOID)(&(PVOID&)HookingClass::HookingFunction));
    
    error = DetourTransactionCommit();

    int result1 = a.TestFunction(10);

    return 0;
}


위 코드를 실행시키면 HookingClass::HookingFunction(int value)내에서 TestClass::TestFunction을 실행할 수 있음을 알 수 있다. __asm mov ecx, [this];  에서 [this]가 곧 instance a의 주소이다. 이때 __asm으로 어셈블리 코드를 직접 사용하는 것이 싫으면 다음과 같이 하면 된다.


#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <detours.h>

class TestClass{
private :
    int mInt;

public :
    TestClass() : mInt(0){}
    TestClass(int initValue) : mInt(initValue){}
    int TestFunction(int iValue);
};

int TestClass::TestFunction(int iValue){
    return mInt + iValue;
}

class HookingClass{
public:
    int HookingFunction(int value);
    static int (HookingClass::*Real_Target)(int);
};

int HookingClass::HookingFunction(int value)
{
    std::cout<<"Hooked" <<std::endl;
    return (this->*Real_Target)(value);
}
int (HookingClass::* HookingClass::Real_Target)(int) = (int (HookingClass::*)(int))&TestClass::TestFunction;

int main(int argc, char** argv){
    TestClass a(10),b(20);

    DWORD error;
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)HookingClass::Real_Target, (PVOID)(&(PVOID&)HookingClass::HookingFunction));
    
    error = DetourTransactionCommit();

    int result1 = a.TestFunction(10);

    return 0;
}


굉장히 아름다운 이 방법은 detours의 sample에 포함된 소스코드를 참조한 것이다. 붉은 색으로 된 코드를 보고 그 안에 담긴 의미를 음미해보라. 다음 마지막이 될 다음 포스트에는 virtual function을 후킹하는 법에 대하 알아보도록 한다.

 

신고

이전 글

Detours Hooking Framework 1 - Hooking Function

Detours Hooking Framework 2 - Hooking API

 

잠시 복습

첫번째 Detours Hooking Framework 글에선 일반 Function을 Hooking하는 법을 살펴보았고 두번째 글에서는 Detours로 API를 후킹하는 법에 대해 알아보았다. Detours가 훌륭한 것은 동일한 방식으로 다른 분류의 Hooking을 할 수 있다는 사실이다. 사용자의 입장에선 내부적으로 어떤 일이 벌어지는지 신경쓸 필요가 없다. 조금만 신경쓰면 된다. 잠시 이전에 Detours가 어떻게 Hooking을 했는지 복습해보자. 일반 함수의 경우 함수 심볼 위치에 있는 명령어를 사용자가 원하는 명령어로 변경해줌으로써 이 일이 가능했다.

위를 보면 TestFunction: 심볼 값에 위치한 명령이 jmp TestFunction에서 jmp HookingFunction으로 변경된 것을 확인할 수 있다. 또한 Dynamic Link된 API를 Hooking할 때는 좀더 복잡한 Code Patch방식을 사용한다는 것이 확인되었다. 만약 해당 API Function의 주소가 A라면 해당 주소에 jmp HookingFunction이라는 명령어가 들어가는 식이었다. 하지만 근본적인 방식은 특정 메모리 주소에 Jmp HookingFunction이라는 명령어를 쓰는 방식으로 동일했다. 그리고 변경되기 전 메모리 주소의 명령어들은 다른 메모리 주소로 옮겨 진다. 이것은 현대의 컴퓨터가 폰노이만 머신, 즉 Code와 Data가 동시에 같은 메모리상에 존재하기 때문에 가능한 것이었다.(VirtualProtect함수로 해당 메모리 주소에 WRITE속성을 추가하고 memcpy로 명령어를 쓰는 것으로 이루어진다.) 당연히 후킹을 위해선 반드시 후킹하고 싶은 함수 또는 API의 메모리 주소를 알아야한다.

 

Class member function call을 디벼보자.

class member function을 후킹하려고 한다. 먼저 후킹이 가능하려면 해당 member function의 주소를 알아내야 한다. 그런데 그것만으로 될까?


#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <detours.h>

class TestClass{
private :
    int mInt;
public :
    TestClass() : mInt(0){}
    TestClass(int initValue) : mInt(initValue){}
    int TestFunction(int iValue);
};

int TestClass::TestFunction(int iValue){
    return mInt + iValue;
}

int main(int argc, char** argv){
    TestClass a(10),b(20);
    int result1 = a.TestFunction(10);
    int result2 = b.TestFunction(10);
    return 0;
}


위와 같은 초 단순한 예를 한번 생각해보자. 후킹을 하기 위해서 해당 함수의 메모리 주소를 알아야한다. 그렇다면 여기서 a.TestFunction 함수와 b.TestFunction 함수의 메모리 주소는 같을까 다를까? 같다면 하나의 작업으로 여러 Instance의 TestFunction을 후킹할 수 있을 것이고 다르다면 Instance마다 TestFunction을 후킹해야한다. 이를 확인하기 위해 a.TestFunction 함수 콜과 b.TestFunction 함수 콜이 Assembly로 어떻게 컴파일 되는지 살펴보자.


    int result1 = a.TestFunction(10);
008E1612  push        0Ah  
008E1614  lea         ecx,[a]  
008E1617  call        TestClass::TestFunction (8E111Dh)  
008E161C  mov         dword ptr [result1],eax  
    int result2 = b.TestFunction(10);
008E161F  push        0Ah  
008E1621  lea         ecx,[b]  
008E1624  call        TestClass::TestFunction (8E111Dh)  
008E1629  mov         dword ptr [result2],eax  


위 어셈블리를 통해 알 수 있듯이 a.TestFunction 함수와 b.TestFunction은 같은 주소 값을 가진다. 이것은 동일한 클래스의 Instance가 여러개 존재하더라도 해당 클래스의 Function은 같은 메모리 주소에 있다는 것을 의미한다. 그렇다면 해당 클래스 멤버 함수는 자신이 어떤 Instance에 속해 실행되는지 어떻게 알 수 있을까? 옳지, call 명령 전의 lea ecx, [a]에 해답이 있을 것 같다. lea ecx, [a]명령이 완료되면 a변수의 주소가 ECX 레지스터에 들어가게 된다. 아하, TestClass::TestFunction은 이 ECX 레지스터에 있는 a의 주소를 통해 정확한 mInt의 값을 얻어 내는 것이구나! a.TestFunction(10)으로 들어가 확인해보자.


int TestClass::TestFunction(int iValue){
008E1590  push        ebp      //Stack Frame을 사용하기 위해 ebp를 Stack에 저장한다. 
008E1591  mov         ebp,esp  //현 esp 값을 ebp에 저장한다. 
008E1593  sub         esp,0CCh //함수내에 사용될 스택의 크기를 0x0CC만큼 확보한다. 
008E1599  push        ebx      //ebx 레지스터의 값을 스택에 저장한다. 
008E159A  push        esi      //esi 레지스터의 값을 스택에 저장한다. 
008E159B  push        edi      //edi 레지스터의 값을 스택에 저장한다. 
008E159C  push        ecx      //ecx 레지스터의 값을 스택에 저장한다. 
008E159D  lea         edi,[ebp-0CCh]  //여기서 부터 rep stos까지의 명령은 하나의 명령 set으로 보면된다. 
008E15A3  mov         ecx,33h         //ecx에 0x33 값을 넣는다.  
008E15A8  mov         eax,0CCCCCCCCh  //eax에 0xcccccccc값을 넣는다. 
008E15AD  rep stos    dword ptr es:[edi]  
//rep stos dword ptr es:[edi]는 edi, ecx, eax와 연관된다. 
//보통 함수의 첫 시작에 불리는 이 명령어 집합은 스택에 초기값을 넣는 것에 활용된다. 
//이 명령은 edi의 주소 값에 eax값을 넣는다. 그리고 그 다음에는 edi+4의 주소 값에 eax값을 넣는다. 
//다음에는 edi+8의 주소 값에 eax값을 넣는다. 이것을 총 ecx내의 수 만큼 반복한다. 즉 0x33번 반복한다.(0x33*4 = 0xcc)
008E15AF  pop         ecx  //ecx값을 복구한다.
008E15B0  mov         dword ptr [ebp-8],ecx  //ecx값을 ebp-8의 주소에 넣는다. 
    return mInt + iValue;
008E15B3  mov         eax,dword ptr [this] //this 주소 값을 eax에 넣는다.   
008E15B6  mov         eax,dword ptr [eax]  //eax의 주소의 value값을 eax에 넣는다. 
008E15B8  add         eax,dword ptr [iValue]  //eax에 iValue값을 더해 넣는다. 
}
008E15BB  pop         edi  //edi값 복구  
008E15BC  pop         esi  //esi값 복구
008E15BD  pop         ebx  //ebx값 복구
008E15BE  mov         esp,ebp  //esp = ebp
008E15C0  pop         ebp  //ebp값 복구
008E15C1  ret         4  //esp = esp +4, 함수 호출했던 주소로 가기 위해 또한번 pop. 


 

Stack

음... Stack관련 명령만 대해 먼저 정리하자. 먼저 알아야 할 것은 함수가 불리기 전의 ESP값과 함수가 불린 후의 ESP 값이 동일해야 한다는 사실이다. 그리고 Stack은 함수 내 변수 공간을 위해 활용된다. 이 Stack의 변화를 보기 위해 Stack관련 명령어들만 정리한 다음 그림을 보기 바란다.

이 과정으로 ESP가 함수 호출이 끝나면서 자연스럽게 복귀됨을 알 수 있다. 이 assembly와 같이 EBP 레지스터를 이용해 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법을 Stack Frame기법이라 한다. 보통 이 과정은

push EBP
mov EBP, ESP
...
mov ESP, EBP, 
pop EBP
ret 

로 압축된다. 이 stack frame을 이용하면 함수 호출 깊이가 깊어져도 스택을 완벽하게 관리할 수 있다. 더 관심있으신 분들은 검색해보시길~ 

 

 

ECX, 그리고 EBP-8 == this

하여간 여기서 중요한 것은 Stack Frame이 어떻게 동작하는가가 아니다. 우리가 궁금한 것은 어떻게 TestClass::TestFunction이 자신의 Instance의 mInt변수값을 알아 낼 수 있는가였다. 이 부분에 해당하는 코드만 따로 보자.


main함수 내에서

    int result1 = a.TestFunction(10);
01041612  push        0Ah  
01041614  lea         ecx,[a]  
01041617  call        TestClass::TestFunction (104111Dh)  
0104161C  mov         dword ptr [result1],eax  

 

TestClass::TestFunction 내에서

010415B0  mov         dword ptr [ebp-8],ecx  
    return mInt + iValue;
010415B3  mov         eax,dword ptr [this]  
010415B6  mov         eax,dword ptr [eax]  
010415B8  add         eax,dword ptr [iValue]  
}


위 부분이 궁금한 부분을 압축한 코드라 할 수 있다. 보자. 함수 호출전에 ecx값에 a변수의 주소가 들어갔었다. TestFunction내에서 ebp-8의 주소에 ecx값을 넣고 있다. 이때 ebp 에는 -12값이 들어있으므로 -20에 ecx값, 곧 a의 변수 값을 넣고 있다. 이 다음에 나오는 코드가 mov eax, dword ptr [this]이다. 이것은 무엇일까?

놀랍게도 이것은 mov eax, dword [ebp-8]과 동일한 의미를 지닌다. 중요한 포인트인데, 바로 클래스 멤버 함수의 ebp-8에는 해당 클래스의 instance의 주소가 들어가게 된다는 사실이다. 이것은 일종의 약속이다. 그리고 해당 instance의 주소에는 클래스 멤버 변수들의 값이 들어있다. 이 경우 해당 instance의 주소에 mInt값이 들어있으므로 mov eax, dword ptr[eax]로 해당 멤버 변수의 값을 eax에 로드할 수 있는 것이다. 그렇다면 만약 TestClass에 int mInt2라는 변수가 추가되어있다면 어떨까? 그리고 이 변수는 어떻게 클래스 함수에서 접근할까? 딩동댕! 다음과 같은 꼴일 것이다.

mov eax, dword ptr[this+4]

 

 

Class Member Function Pointer

비밀들이 벗겨지고 있다. 위에서 설명한 이유 때문에 일반 Function Pointer로 함수 멤버 함수를 호출할 수 없는 것이다. 예를 들어 TestClass::TestFunction의 주소를 얻어와 이것을 일반 Function Pointer로 만든 후 해당 Function Pointer를 호출해보자.


TestClass a(10),b(20);
typedef int (*FPointer)(int value);
PVOID functionAddress = (PVOID)(&(PVOID&)TestClass::TestFunction);
FPointer pointer = (FPointer)functionAddress;
pointer(10);


위 코드는 정상적으로 빌드가 되고 pointer에 TestClass::TestFunction의 주소 값이 정상적으로 들어가지만 Run-time Exception을 발생시킨다. 그리고 이것은 너무나 당연한 결과이다.


00B41621  push        0Ah  
00B41623  call        dword ptr [pointer]  


class의 Instance를 넘겨주는 명령이 빠져있는 것을 볼 수 있다. 이와 반대로 Class Member Function Pointer를 활용해보자.


typedef int (TestClass::*PFClassMember)(int);
PFClassMember classMemberFP = &TestClass::TestFunction;
(a.*classMemberFP)(10);


    int result3 = (a.*classMemberFP)(10);
0107161B  push        0Ah  
0107161D  lea         ecx,[a]  
01071620  call        dword ptr [classMemberFP]  

ecx에 변수 instance a의 주소값을 넘겨주고 있다. 이로써 해당 함수 콜이 정상적으로 동작할 수 있다.

 

 

결론

일반 함수 콜과 클래스 멤버 함수 콜이 어떻게 다른지 기초적인 부분을 알아보았다. 다음 포스트에서는 이 TestFunction 클래스 함수를 후킹해보자! 

 

**혹시 이 포스트에서 제가 잘못 알고 있는 부분이 있다면 꼭 태클해주세요. 감사합니다.

신고

Detours Hooking Framework -2. Hooking API

프로그래밍 2011.07.12 16:44 Posted by 아일레프

지난 글에서 Detours로 Function을 후킹하는 방법을 살펴보았다. 이 포스트에는 API를 Hooking하는 법을 알아본다. 물론, 매우 간단하고 쉽다.

 

API Hooking

API를 후킹하는 방법은 지난 글에서 말했던 방식과 완전히 동일하다. 단, 내부에서 Detours가 Hooking하는 방식은 다르다.


#include <Windows.h>
#include <stdio.h>
#include <detours.h>

int main(int, char **){
    ::MessageBoxA(NULL, "messageBox", "Hello", MB_OK);
}

위 코드에서 MessageBoxA를 후킹해 MessageBox 창의 caption을 "Hooked"로 바꿔보자. 코드는 다음과 같을 것이다.


#include <Windows.h>
#include <stdio.h>
#include <detours.h>

int (WINAPI *pfTrueMessageBox)(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType) = MessageBoxA;

int WINAPI HookingMessageBox(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType){
        
        return pfTrueMessageBox(hWnd, lpText, "Hooked", uType);
}

int main(int, char **){

    DWORD error;
    ::DetourTransactionBegin();
    ::DetourUpdateThread(::GetCurrentThread());
    ::DetourAttach((PVOID*)&pfTrueMessageBox, HookingMessageBox);
    error = DetourTransactionCommit();

    if(error != NO_ERROR){
        printf("fail to attach\n");
    }

    ::MessageBoxA(NULL, "messageBox", "Hello", MB_OK);

}

이 프로그램을 실행시키면 다음 결과를 확인할 수 있다.

Caption이 바뀌었음을 확인 할 수 있다. 자, 이제 내부에서 어떤 일이 벌어졌는지 확인해보자.

먼저 Hooking되기 이전의 MessageBoxA 함수를 호출하는 명령은 다음과 같다.

::MessageBoxA(NULL, "messageBox", "Hello", MB_OK);
00EC165C  mov         esi,esp 
00EC165E  push        0 
00EC1660  push        offset string "Hello" (0EC7854h) 
00EC1665  push        offset string "messageBox" (0EC7844h) 
00EC166A  push        0 
00EC166C  call        dword ptr [__imp__MessageBoxA@16 (0ECC404h)] 
00EC1672  cmp         esi,esp 
00EC1674  call        @ILT+445(__RTC_CheckEsp) (0EC11C2h) 

명령어를 통해 알 수 있듯이 __imp__MessageBoxA의 심볼 값 - 0xECC404h주소의 value 를 call하라는 명령어 이다. 그리고 0xECC404h주소에는 0x7670fd1e 값이 들어있다.

 

그리고 해당 주소에는 MessageBoxA 함수의 코드가 위치해 있다.

7670FD1E  mov         edi,edi 
7670FD20  push        ebp 
7670FD21  mov         ebp,esp 
7670FD23  push        0 
7670FD25  push        dword ptr [ebp+14h] 
7670FD28  push        dword ptr [ebp+10h] 
7670FD2B  push        dword ptr [ebp+0Ch] 
7670FD2E  push        dword ptr [ebp+8] 
7670FD31  call        7670FCD6 

또한 MessageBoxA의 function Pointer를 저장한 pfTrueMessageBox 의 값은 0x7670FD1E이다.

코드를 진행해 Detours로 Attach한 뒤의 Assembly 명령어를 살펴보면 다음과 같은 변화가 있다는 것을 알 수 있다.

7670FD1E  jmp         HookingMessageBox (0EC12BCh) 
7670FD23  push        0 
7670FD25  push        dword ptr [ebp+14h] 
7670FD28  push        dword ptr [ebp+10h] 
7670FD2B  push        dword ptr [ebp+0Ch] 
7670FD2E  push        dword ptr [ebp+8] 
7670FD31  call        7670FCD6 

먼저 위와 같이 0x7670FD1E주소의 명령어가 변경되었다. 그리고, pfTrueMessageBox의 값이 0x6fff0060으로 변경되었다.

그리고 0x6fff0060의 위치에는 다음 명령어가 존재함을 확인 할 수 있다.

6FFF0060  mov         edi,edi 
6FFF0062  push        ebp 
6FFF0063  mov         ebp,esp 
6FFF0065  jmp         7670FD23

6FFF0060 - 6FFF0063은 Detour로 Attach하기 전 0x7670FD1E - 0x7670FD21의 명령어였다.  

정리해보자. 이전 포스트에서 Detours는 단순히 함수 심볼 값의 명령어를 변경할 뿐이었다. 하지만 이번에는 새로운 명령어가 기존의 위치에 추가되고, 기존의 명령어가 다른 위치로 이동됨으로써 Hooking 동작이 이루어졌다. 물론 다른 방식으로 이 API후킹이 이루어질 수도 있다. 0xECC404h 주소의 값에 HookingMessageBox 주소(0x0EC12BCh)를 적어 놓는 것도 하나의 방법일 것이다. 하지만 Detour는 기존의 코드를 변경하는 방식을 택했다는 것을 확인해주기 바란다. 만약 Detours를 통하지 않고 직접 API를 후킹하고 싶다면 www.reversecore.com/63을 확인하면 된다. 정말 자세히 잘 설명 되어있다. 다음에는LoadLibrary, GetProcAddress를 통해 얻어진 API를 후킹하는 법에 대해 살펴볼 것이다.

 

 

** 2011. 07. 12 추가 내용

LoadLibrary, GetProcAddress로 얻어진 함수를 후킹하는 방법을 새 글로 다루려고 했는데 사실 이것은 이 포스트의 내용과 다르지 않기에 여기에 추가한다.

위에서 MessageBoxA함수를 호출할 수 있는 것은 User32.dll이 동적 링크 되었기 때문이다. 그런데 User32.dll을 동적 링크하라는 명령이 보다시피 프로그램내에서 존재하지 않고 있다. 이는 User32.dll이 암시적으로 동적로딩되었기 때문이다. (PE Loader가 프로그램이 시작할 때 이 일을 한다.) 하지만 때때로 프로그래머는 명시적으로 dll을 로딩하고 해당 dll의 exported된 함수를 사용하고 싶을 때가 있는데 이때 사용하는 명령어가 LoadLibrary, GetProcAddress이다. 위 프로그램과 같은 프로그램을 만들되, 명시적 Dynamic Link를 사용해보자. 프로그램은 다음과 같을 것이다.


typedef int (WINAPI *PFMessageBox)(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType);

PFMessageBox pfTrueMessageBox;
int main(int, char **){
    HMODULE user32Module = LoadLibrary(TEXT("user32.dll"));

PFMessageBox messageBox = (PFMessageBox)::GetProcAddress(user32Module,
"MessageBoxA"); messageBox(NULL, "messageBox", "Hello", MB_OK); }


위 프로그램을 Detours로 후킹하면 다음과 같다.


#include <Windows.h>
#include <stdio.h>
#include <detours.h>

typedef int (WINAPI *PFMessageBox)(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType);

PFMessageBox pfTrueMessageBox;

int WINAPI HookingMessageBox(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType){
        
        return pfTrueMessageBox(hWnd, lpText, "Hooked", uType);
}

int main(int, char **){
    HMODULE user32Module = LoadLibrary(TEXT("user32.dll"));
    pfTrueMessageBox = (PFMessageBox)::GetProcAddress(user32Module, "MessageBoxA");
    DWORD error;
    ::DetourTransactionBegin();
    ::DetourUpdateThread(::GetCurrentThread());
    ::DetourAttach((PVOID*)&pfTrueMessageBox, HookingMessageBox);
    error = DetourTransactionCommit();

    if(error != NO_ERROR){
        printf("fail to attach\n");
    }
    
    PFMessageBox messageBox = (PFMessageBox)::GetProcAddress(user32Module, "MessageBoxA");
    messageBox(NULL, "messageBox", "Hello", MB_OK);

}


pfTrueMessageBox와 messageBox 함수 포인터가 가리키는 주소가 다르다는 것에 주의해야 한다. pfTrueMessageBox에는 기존의 MessageBoxA를 호출하는 명령 있으며 messageBox에는 HookingMessageBox를 호출하는 명령이 있다. 그리고 이 일을 하기 위해 Detours가 하는 일은 암시적 동적 링크된 API를 후킹하는 방식과 완전히 동일하다.

신고

Detours Hooking Framework - 1. hooking function

프로그래밍 2011.06.16 00:37 Posted by 아일레프

최근 Hooking을 해야 할 일이 생겼다. 그런데 이 Hooking할 함수가 dll의 일반 export함수도 아니고, 클래스 멤버의 함수도 아니고, COM객체의 virtual 함수라 이리저리 많은 삽질을 해야했다. 그러다 Detours란 Hooking Framework를 만나게 되었는데 와, 이거 정말 훌륭하더라. 물론 Virtual 함수를 Hooking하는 것은 이리저리 많은 삽질을 거쳐야 했지만, 프로그래밍이란게 한번 알고 나면 너무 간단해 허무한 것이다. 고로 지금 허무하다.

 

설치.

Detours Hooking Framework 이 사이트에서 Detours Express 2.1을 다운로드 받고 설치하자. default 경로는 C:\Program Files (x86)\Microsoft Research\Detours Express 2.1 이다. 해당 폴더에 Command Prompt로 이동해 nmake all 명령을 실행하자. 본인은 nmake all 명령이 실패했는데 나의 문제는 Visual Studio의 Command Prompt를 이용하고 set DETOURS_TARGET_PROCESSOR =x86 을 설정함으로써 해결할 수 있었다.

 

Function Hooking

이제 Detours를 사용해보자. Visual Studio에 DetoursTest를 만들고 C/C++, Linker 환경을 설정하자. C/C++의 Additional Include Directories에 Detours Express의 Include 디렉토리를 추가하고, Linker의 Additional Library Directories에 Detours Express의 lib 디렉토리를 추가한다. 그리고 Linker의 Additional Dependencies에 detoured.lib, detours.lib를 추가한다. 그후 main.cpp란 파일을 추가 한 후 다음 코드를 넣자.


#include <Windows.h>
#include <stdio.h>
#include <detours.h>

void TestFunction(){
    printf("TestFunction Called\n");
}

void HookingFunction(){
    printf("before - TestFunction call");
    TestFunction();
    printf("after - TestFunction call");
}

int main(int, char**){
    TestFunction();
    getchar();
}

main에서 TestFunction을 call했으므로 당연히 CMD 창에 "TestFunction Called" 문장이 나타날 것이다. 이제 TestFunction을 Hooking해 TestFunction()을 호출하면 HookingFunction()이 호출되게 할 것이다. 그리고 이 일은 굉장히 간단하다. 프로그램을 다음과 같이 바꿔보자.


#include <Windows.h>
#include <stdio.h>
#include <detours.h>

void TestFunction(){
    printf("TestFunction Called\n");
}
void (*pfTrueTestFunction)() = TestFunction;
void HookingFunction(){
    printf("before - TestFunction call\n");
    TestFunction();
    //pfTrueTestFunction();
    printf("after - TestFunction call\n");
}

int main(int, char**){
    DWORD error;
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*)&pfTrueTestFunction, HookingFunction);
    error = DetourTransactionCommit();

    if (error == NO_ERROR) {
        printf("\n");
    }
    else{
        printf("fail to attach\n");
    }
    TestFunction();
    getchar();
}

프로그램을 실행하면 어떤 일이 벌어질까? before - TestFunction Call 문장이 반복되어 출력될 것이다. 원하는 동작을 원한다면 HookingFunction의 TestFunction()을 주석처리하고 pfTrueTestFunction function pointer로 기존 메소드를 출력하면 된다.

어떻게 이 일이 가능하게 되었을까? DetourAttach 함수 콜에 Breakpoint를 설정하고 TestFunction과 pfTrueTestFunction의 값을 확인해보자.

TestFunction 주소 값과 pfTrueTestFunction 값이 다르지만 pfTrueTestFunction 주소 값의 Assembly 명령어를 확인하면 Jmp 명령어로 TestFunction으로 점프하는 코드가 있음을 확인 할 수 있다. 그리고 그 명령어 위에 'TestFunction:'이 있는데 이것이 함수 symbol값이다. 

이제 DetourAttach명령과 DetourTransactionCommit()까지 프로그램을 진행시켜보자. Watch창으로 TestFunction과 pfTrueTestFunction을 다시 확인해보자. 재미있게도 pfTrueTestFunction의 주소 값이 바뀌었다.

pfTrueTestFunction의 값이 변경되었음을 알 수 있다. 0x010c0060메모리 주소를 보면 TestFunction의 메모리로 점프하는 코드가 있다.

 

더 재미있는 것은 0x010e12bc의 메모리주소의 값이 변경되었다는 사실에 있다.

TestFunction: 심볼 값에 Jmp TestFunction 명령이 있었는데 Jmp HookingFunction으로 변경되었다. 이것이 DetourAttach명령과 DetourTransactionCommit이 하는 일이다. 이것으로 후킹이 완료되었다. 

 

마치며

Detours로 특정 함수 심볼 값을 변경해 후킹을 해보았다. 이런 방식으로 기존의 exe파일이나 dll파일을 후킹할 수도 있다. detours의 bin 폴더 내에는 setdll이란 명령어가 있는데 이를 이용하면 된다. 이 setdll명령은 기존의 exe파일이나 dll파일에 외부의 dll을 로드하게하는 명령어이다. 예를 들어
setdll /d:dllInject.dll notepad.exe
명령을 실행시키면 이후에 notepad.exe를 실행했을 때 프로그램 내부에서 dllInject.dll이 로드되게 된다. 즉 LoadLibrary("dllInject.dll")이 실행되게 되는 것이다. 이 dll이 로드되면 dll내부의 DllMain함수가 실행되게 되고, 만약 DllMain함수 내에서 DetourAttach 등으로 Hooking동작을 하는 코드가 있다면 notepad.exe내에서 호출하는 함수, API들을 Hooking할 수 있다.
 

 2로 계속됩니다.

** 2001. 07. 01 수정.
setdll /d:dllInject.dll executable.exe를 실행시키면 dllInject.dll이 executable.exe에 Inject된다고 위에 설명했었다. 이것은 사실이다. 그런데 난 멋도 모르고 어떻게 추측했었냐면, .text의 EntryPoint에 LoadLibrary("dllInject.dll") 코드를 명시적으로 실행시켜 dllInject.dll의 DllMain 함수가 실행되게 할 줄 알았다. 하지만 사실은 이와 달라 정정한다. Dll Loading의 방식에는 2가지가 있는데 그 중 하나는 명시적 로딩, 즉 코드 내에서 LoadLibrary("dllInject.dll")이라고 명시적으로 코딩을 하는 방법이고, 또 하나는 암시적 로딩으로써 프로그램이 시작하는 순간에 같이 로딩되는 방법이 있다. 암시적 로딩을 위해 PE 파일은 어떤 DLL파일을 로드 해야 하는지 명시해 PE Loader에게 알려야 한다. Detours 는 PE파일의 이 부분을 변경한다. 즉, dllInject.dll이 필요하다고 PE Loader에게 알리는 것이다. 자세한 부분은 www.reversecore.com/23에 잘 설명되어 있다. 

신고

Know your enemy

끄적끄적 2011.04.20 13:06 Posted by 아일레프



완전경쟁이란 모델, 그리고 그 안에서 수요와 공급이 만나는 접점인 ‘가격’ 이것이 주는 안정감과 수리적 계산식이 주는 명쾌함을 부정하긴 어렵다. 하지만 ‘모델’이라는 한계를 벗어나기 어려운 이 완전경쟁을 굳건한 사실로 받아들이면 우린 눈 앞의 진실에 대면하지 못하게 된다.

현 세계에서 완전경쟁이 ‘모델’의 한계에서 벗어나기 어려운 까닭은 그 완전경쟁이 다수의 공급자, 다수의 수요자를 전제하고 있기 때문이다. 다시 말하면 이 모델은 집단 내에서 큰 역할을 수행할 수 없는 – 가격에 직접 영향을 가할 수 없는 – 개인을 상정하고 있다.

하지만 현실은 이와 달라서 공급자가 소수이거나 수요자가 소수인 경우도 얼마든지 있다. 이 경우 다수인 공급자, 수요자 간에 필요 이상의 경쟁이 발생하고 이 경쟁의 법칙을 결정하는 것은 정치적 규제 또는 권력인 경우가 대부분이다. 때문에 순수한 경제학은 오늘날 적용되기 어려우며 오히려 이 세계 내에 진정 영향을 발휘하는 경제학은 모두 정치-경제학이다.

오늘날 경쟁이란 거의 진리처럼 여겨지고 있으나 사실 경쟁은 매우 고통스럽고 지긋지긋하다. 경쟁을 즐겁게 바라볼 수 있는 사람은 경쟁 내부가 아닌 경쟁 외부에서 경쟁을 바라보는 자들이다. 때문에 경쟁에 참여한 경쟁자들은 경쟁의 문에서 빠져나오기 위해, 다수의 공급자 수요자가 아니라 소수의 ‘과점’으로 가기 위해 치열하게 경쟁하는 것이다.

물론 그 경쟁의 문을 통과한 자들 역시 그들 나름대로 경쟁을 한다. 하지만 보다 덜 엄격한 경쟁의 법칙이 자리한 그 리그 내에서 그들은 서로의 법칙을 만들고 때로는 서로 간에 연대의식을 쌓아 가기도 하며 우리는 이것을 담합이라 부른다. 담합이란 경쟁자들끼리 “우리 좀 적당히 경쟁하자”라는 약속을 확인하는 것이라 할 수 있다. 경제학에선 죄수의 딜레마를 예로 들며 이 담합이 실제로는 쉽지 않다고 말하지만 죄수의 딜레마는 절대 소통할 수 없는 분리된 공간의 죄수를 가정하고 있기 때문에, 곧 철저한 비협력 게임이론이라 그런 것이고 실세계에서 담합의 예는 적지 않다. 어쨋든 그들의 담합은 그들을 두 가지 결론으로 나가게 하는데 하나는 가격을 낮추지 않는 것이며 또 하나는 그들이 이미 통과한 경쟁의 문을 좁혀 잠재적인 경쟁자들의 싹을 잘라버리는 것이다. 그들은 교묘히 경쟁하고 교묘히 협력한다.

그들은 하위리그의 경쟁을 입법하는 자로서 경쟁의 법칙을 잔인하게 좁혀가면서 본인들은 사디즘적 즐거움을 누리고 일련의 법으로부터 자유로운 듯 행동하고 있다. 법은 큰 물고기는 놓치고 작은 물고기만을 잡는 그물이라 함은 여기서 기인한다.

사실이 이렇다면 어떤 리그 안에서 경쟁하는 개인은 마땅히 두개의 집단과 마주치고 힘겨루기를 해야한다. 하나는 리그 내 경쟁자들의 집단이며 또 하나는 리그 간 경쟁자들이다. 즉 우리는 알게 모르게 수평적으로는 경쟁하고 있으며, 상위 리그에 대해서는 투쟁하며, 하위 리그는 억압하고 있다.

우리 자본주의 사회에서 리그 내 경쟁이 사회를 보다 나은 곳으로 향하게 한다며 그 가치가 점점 더 높아지고 있지만 리그 간 경쟁도 사회를 보다 진보적인 방향으로 나아가게 한다. 사실 우리가 현재 누리고 있는 21세기의 윤택은 리그 내 경쟁 뿐 아니라 리그 간 경쟁에 더 많은 빚을 지고 있다.

하지만 상부 리그를 향해 투쟁하는 사람들을 저들은 불온하다 하고, 빨갱이라 부르며 그 의미를 폄하하고 있다. 그들은 현 사회의 질서를 옹호하며 리그 간 투쟁을 행하는 자들의 목소리를 억제한다. 수평적 경쟁은 찬란한 ‘시장’의 이름으로 아릅답게 치장하는 한편 수직적으로 투쟁하는 자들에 대해선 매도하는 전략을 택해 하부 리그의 힘이 모여 하나의 덩어리로 향해 가는 것을 억제하며 스스로 분열하게끔 유도한다.

이러한 그들의 행동들을 도덕이란 단어를 제시하며 폄하할 생각은 없다. 사실 그들의 이런 전략은 지극히 자연스럽다. 내가 말하고 싶은 것은 상부 리그가 하부 리그에게 행하는 전략들이 자연스럽다면 하부 리그가 상부 리그를 향해 투쟁하는 것도 자연스럽다는 사실이다. 때문에 맘 속에 뜨거운 불을 가지고 있는 사람들이여, 그 불을 스스로 부끄러워하지 말자.

사회를 최대 이득과 균형으로 이끄는 것은 시장의 기능 뿐 아니라 계급간의 다툼이며, 순환적인 힘겨루기에 있다. 수평적 경쟁에 힘쓰는 것도 중요하지만 수직적인 싸움에 눈 돌리는 것은 멍청한 일이다. 우리의 적은 그들이 늘 말해왔듯이 내부가 아닌 외부에도 있다. Know your enemy.

신고

DataGrid and Dynamic Column -- Part2

프로그래밍 2011.04.10 16:51 Posted by 아일레프

지난 Post, DataGrid and Dynamic Column – Part1에서 DataGrid의 Column을 컴파일 타임 이전이 아니라 Runtime에 결정할 수 있는 하나의 예시를 보였다. 이는 Type에 index Property, “[ ]” 가 존재한다는 것과 DataGridTextColumn의 Binding에 사용되는 Binding객체의 Path에 “[ ]”를 사용할 수 있기 때문에 가능한 것이었다. 하지만 이 방법은 논리와 View가 밀접하게 연관되어 MVVM과 같은 Pattern에서 기꺼이 사용하기에는 뭔가 아쉬움이 남게된다. Part2에서는 이를 해결할 수 있는 또 다른 해법을 제시한다.

TypeDescriptor 그리고 ICustomTypeDescriptor

지난 Post에 DataGrid에 사용될 Column은 DataGrid에 바인딩 되는 클래스의 Property에 연관을 가지기에 Property를 어떻게 Dynamic하게 설정할 수 있는지를 고민해야 한다고 말한 바 있는데, 이는 ICustomTypeDescriptor의 존재로 해결 될 수 있다. 

WPF의 ItemsControl와 ItemsControl을 상속받는 ListBox, ComboBox, DataGrid등의 컨트롤들은 ItemsSource에 바인딩된 IEnumerable 객체를 바로 Items에 사용하지 않고 ICollectionView라는 형태로 변환해 사용한다. 이 ICollectionView는(예를 들어 ListCollectionview)는 TypeDescriptor를 적극적으로 사용하는데, TypeDescriptor는 ICustomTypeDescriptor를 구현하는 Type에 대해 별도의 동작을 하게 된다. ICustomTypeDescriptor, Part 1 를 보면 이에 대해서 자세히 확인 할 수 있으며 그 내용의 핵심은 아래 그림으로 간단히 요약될 수 있다. 

즉 TypeDescriptor는 ICustomTypeDescriptor를 구현하는 객체에 대해서는 Reflection이 아니라 ICustomTypeDescriptor의 GetProperties라는 메소드로 Property를 얻어오게 된다는 것이다. 따라서 DataGrid에 바인딩 될 객체가 ICustomTypeDescriptor를 구현하게 하고, DataGrid의 AutoGenerateColumn을 true로 설정함으로써 별도의 Column설정 없이 원하는 화면을 얻을 수 있다. 이때 Column Header는 Property의 이름과 동일하게 된다.

 

ICustomTypeDescriptor를 구현하는 DataForBinding 객체를 제작

public interface ICustomTypeDescriptor
{
    AttributeCollection GetAttributes();
    string GetClassName();
    string GetComponentName();
    TypeConverter GetConverter();
    EventDescriptor GetDefaultEvent();
    PropertyDescriptor GetDefaultProperty();
    object GetEditor(Type editorBaseType);
    EventDescriptorCollection GetEvents();
    EventDescriptorCollection GetEvents(Attribute[] attributes);
    PropertyDescriptorCollection GetProperties();
    PropertyDescriptorCollection GetProperties(Attribute[] attributes);
    object GetPropertyOwner(PropertyDescriptor pd);
}

ICustomTypeDescriptor를 구현하기 위해선 위와 같은 여러 메소드들을 만들어야 한다는 부담감이 드는데, 여기서는 GetClassName, GetComponentName, GetPropertiess, GetPropertyOwner만을 설정해도 충분하다. 그리고 이중 GetProperties메소드가 가장 큰 역할을 하게 된다. 우리가 원하는 화면을 다시 한번 확인해보자.

위 화면을 보면 우리가 1개의 “Key” Property와 N개의 “날짜” Property을 필요로 한다는 것을 알 수 있다. 즉 GetProperties가 반환하는 PropertyDescriptorCollection내에 1개의 “Key”에 해당 되는 PropertyDescriptor와 N개의 “날짜” PropertyDescriptor가 포함되게 되면 된다. 좋다. 구현을 해보자.

public class DataForBinding : ICustomTypeDescriptor
{

    //중략..

    IEnumerable<Data> originalDatas;
    string key;

    PropertyDescriptorCollection propertyDescriptorCollection = new PropertyDescriptorCollection(null);

    public DataForBinding(IEnumerable<Data> datas, IEnumerable<DateTime> datesForDisplay)
    {
        if (datas == null)
            throw new ArgumentNullException("datas");
        if (datas.Count() == 0)
            throw new ArgumentException();
        if (datesForDisplay == null)
        {
            throw new ArgumentException("datesForDisplay");
        }

        this.originalDatas = datas;
        this.key = datas.First().Type;
        if (!datas.All(d => d.Type == key))
        {
            //하나의 Row에 대응되는 Data들은 모두 Type이 동일해야 하므로
            throw new ArgumentException();
        }

        propertyDescriptorCollection.Add(new KeyPropertyDescriptor(key));
        foreach (DateTime dateTime in datesForDisplay)
        {
            propertyDescriptorCollection.Add(new DatePropertyDescriptor(datas, dateTime));
        }
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return propertyDescriptorCollection;
    }

    public string GetClassName()
    {
        return typeof(DataForBinding).Name;
    }

    public string GetComponentName()
    {
        return typeof(DataForBinding).Name;
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }
}

위에서 Bold체로 표시된 부분을 보면, 1개의 KeyPropertyDescriptor와 N개의 DatePropertyDescriptor객체가 PropertyDescroptorCollection에 포함되게 됨을 알 수 있다. 이제 남은 일은 이 각 Property에 해당되는 KeyPropertyDescriptor와 DatePropertyDescriptor를 구현하는 일이다.


KeyPropertyDescroptor와 DatePropertyDescroptor의 구현 

KeyPropertyDescriptor와 DatePropertyDescriptor는 PropertyDescriptor라는 Abstract클래스를 상속받아 구현된다. 이를 위해 다음과 같은 메소드들을 구현해야 하는데 우리가 보여주는 DataGrid는 모두 ReadOnly로 동작하기 때문에 Set, Reset 등의 프로퍼티는 구현할 필요가 없다.

public override bool CanResetValue(object component)
public override Type ComponentType
public override object GetValue(object component)
public override bool IsReadOnly
public override Type PropertyType
public override void ResetValue(object component)
public override void SetValue(object component, object value)
public override bool ShouldSerializeValue(object component)
그럼 KeyPropertyDescriptor부터 구현해보자. 
public class KeyPropertyDescriptor : PropertyDescriptor
{
    string key;
    public KeyPropertyDescriptor(string key):base("Key", null)
    {
        this.key = key;
    }

    #region PropertyDescroptor methods
    public override bool CanResetValue(object component)
    {
        return false;
    }

    public override Type ComponentType
    {
        get { return typeof(DataForBinding); }
    }

    public override object GetValue(object component)
    {
        return key;
    }

    public override bool IsReadOnly
    {
        get { return true; }
    }

    public override Type PropertyType
    {
        get { return typeof(string); }
    }

    public override void ResetValue(object component)
    {
        throw new InvalidOperationException("이 프로퍼티는 ReadOnly로만 동작해야 한다.");
    }

    public override void SetValue(object component, object value)
    {
        throw new InvalidOperationException("이 프로퍼티는 ReadOnly로만 동작해야 한다.");
    }

    public override bool ShouldSerializeValue(object component)
    {
        return false;
    }
    #endregion
}

Key 프로퍼티의 구현은 위와 같이 간단하다. 이는 DatePropertyDescriptor도 마찬가지이다. 단, 차이가 있다면 Property이름의 제약조건으로 인해 “2009/10/2”라는 Property를 그대로 사용할 수 없다. 따라서 약간의 변환이 필요하다.

public class DatePropertyDescriptor : PropertyDescriptor
{
    private IEnumerable<Data> datas;
    private DateTime dateTime;

    public DatePropertyDescriptor(IEnumerable<Data> datas, DateTime dateTime) : 
        base(string.Format("P{0}_{1}_{2}",dateTime.Year, dateTime.Month, dateTime.Day), null)
    {
        // TODO: Complete member initialization
        this.datas = datas;
        this.dateTime = dateTime;
    }

    #region PropertyDescroptor methods
    public override bool CanResetValue(object component)
    {
        return false;
    }

    public override Type ComponentType
    {
        get { return typeof(DataForBinding); }
    }

    public override object GetValue(object component)
    {
        StringBuilder stringBuilder = new StringBuilder();
        foreach (Data data in datas.Where(d => d.DateTime.Day == dateTime.Day && d.DateTime.Month == dateTime.Month))
        {
            stringBuilder.AppendLine(string.Format("{0:00}:{1:00}", (int)data.DateTime.TimeOfDay.TotalHours, data.DateTime.TimeOfDay.Minutes)
                                        + " - " + data.Detail);
        }
        return stringBuilder.ToString();
    }
    //후략…
    #endregion
}



DataGrid ItemssSource설정, Header설정

이제 모든 준비가 끝났다. 남은 것은 서비스로 부터 얻은 데이타를 DataForBinding객체로 변환해 DataGrid의 ItemsSource로 설정하는 일이다.

public MainWindow()
{
    InitializeComponent();
    IEnumerable<Data> datas = GetData(); //Data Service로 부터 원본 Data집합을 얻어낸다.
    this.dataGrid.ItemsSource = new ArrayList(GetBindingData(datas).ToList());//바인딩을 위한 객체로 변환한다.       
}

private IEnumerable<DataForBinding> GetBindingData(IEnumerable<Data> datas)
{
    IEnumerable<IGrouping<string, Data>> groupDatas = datas.GroupBy(data => data.Type);

    foreach (IGrouping<string, Data> group in groupDatas)
    {
        yield return new DataForBinding(group, datas.GroupBy(d => d.DateTime.Date).OrderBy(s => s.Key).Select(s => s.Key));
    }
}
<DataGrid x:Name="dataGrid"  AutoGenerateColumns="True" IsReadOnly="true" />

주목해야 할 것은 별도의 Column설정이 필요하지 않으므로 이 부분이 View와 독립적으로 이루어질 수 있다는 점이다. 따라서 이를 MVVM패턴으로 변환하는 것도 간단하다.  또한 유의 해야 할 점은 new ArrayList(GetBindingData(datas).ToList());  와 같이 ArrayList로 변환해 ItemsSource를 설정해야 한다는 점이다. List나 IEnumerable객체를 바로 사용하면 DataGrid가 ICustomTypeDescriptor의 GetProperties를 이용하지 않는다.(이 부분은 미지로 남아있다.) 여하튼, 이 코드를 실행시키면 다음과 같은 화면을 얻을 수 있다.

하지만 보시다시피 Column의 Header가 P2009_10_1과 같이 표현되므로 이를 2009/10/1 로 표시하는 작업을 따로 해주어야 한다.

    <DataGrid x:Name="dataGrid"  AutoGenerateColumns="True" IsReadOnly="true" >
        <DataGrid.Resources>
            <local:GridColumnConverter x:Key="GridColumnConverter"/>
            <Style TargetType="DataGridColumnHeader">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBlock Text="{Binding Converter={StaticResource GridColumnConverter}}"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>
    </DataGrid>

위 설정을 마치면 다음과 같이 우리가 워하는 화면을 볼 수 있다.

 

이 방법의 문제점

ICustomTypeDescriptor를 사용하는 이 방법은 Part1의 방법과 같은 DataGridColumn의 작업을 별도로 요구하지 않기에 View와 논리를 분리할 수 있다는 장점이 있다. 그러나 아쉬운 것은 이를 위해 ICustomTypeDescriptor와 PropertyDescriptor를 구현하는 별도의 객체를 매번 만들어 줘야 한다는 것이다. 이를 해결할 수 있을까? .NET 4에는 ExpandoObject라는 객체가 추가됨으로 이 문제에 대한 해결의 가능성을 보여준다. 그러나 ExpandoObject만을 사용하는 것으로는 문제가 해결되지 않음을 확인했다. 몇가지 작업이 필요한 것 같은데 이 작업이 성공한다면 Part3 포스트가 나올 수 있을 것이다.

신고


 

티스토리 툴바