미움받을 용기 : 자유롭고 행복한 삶을 위한 아들러의 가르침 - 기시미 이치로, 고가 후미타케 공저, 전경아 역

미움받을 용기 2 : 사랑과 진정한 자립에 대한 아들러의 가르침 - 기시미 이치로, 고가 후미타케 공저, 전경아 역




이 책은 한참동안 베스트셀러 자리에 놓여있다가 최근에 주춤하고 있는데, 

나는 이제서야 이 책을 손에 들었다.


읽고 싶었다기 보다는, 읽게 됐다.

내용 그 자체로 좋았다기 보다는, 많은 도움이 되었다.


내용에 대해 이런 평가를 하는건, 대화체로 쓰여있는 터라 쉽게 읽혔지만,

"왜?" 를 멈출 수 없는 부분이 좀 있었기 때문이다.


하지만 많은 도움이 됐다고 표현하는 이유가 있다.


현재 나의 상황에 집중하지 말고, 

앞으로 어떻게 할 것인가를 생각해야 한다


이 두권의 책을 통해서 내가 낸 결론이다.


난 이 결론을 통해서, 나를 단련해야겠다는 마음을 먹었다.


그리고, 이 결론에 매우 만족한다.




근데.. 뭐지, 너무 비장한 느낌인데..


스스로 공부하는 아이, 시켜야 공부하는 아이 - 전미란





스마트한 선택들 - 롤프 도벨리


조나단 하이트 - 바른 마음





앤디 위어 - 마션





러셀 프리드먼 - 1차 세계대전





김훈 - 칼의 노래





애덤 그랜트 - 오리지널스



유시민의 글쓰기 특강 - 생각의길, 2015

2015. 08. 03. MON. 월요일을 마친 늦은 새벽. 아, 덥다. 

이 책을 알게 된 것이 언제인가..
가족들끼리 서점 나들이를 나간 어느날이었다.
여느때처럼 어떤 책이 있는지 둘러볼 겸, 베스트셀러 코너로 갔다. 
한참, 블로그를 통해 무언가 기록을 남기고 싶은 생각이 많았던 터였을까, 
유시민의 글쓰기 특강이라는 책이 눈에 들어왔다. 

한두챕터를 훑었다. 
논증, 주장, 취향에 관한 이야기를 봤는데 느낌이 좋았다. 
이 책은 꼭 한번 읽어봐야겠다는 마음이 들었다. 

주말이 모두 지나가고, 사무실에 앉아 주말의 일상을 공유하던 중 우연히 책 이야기가 나왔는데,
나보다 회사 동생이 먼저 책을 사버렸고, 하루만에 읽어버렸다. 

그리고 책을 나에게 빌려줬는데, 이핑계 저핑계로 못읽고 있다가, 
준비중인 프로젝트를 런칭하고 나서 책을 들었다. 

원래 유시민 작가께서 글을 잘 써서인가, 눈에 잘 들어왔고, 이해하기 쉬웠다. 

특히 글쓰기를 넘어, 생각하는 방법에 대해 공감되는 부분이 많았고, 
뚜렷한 가치관, 깊이있는 사색에서 내공이 느껴졌다. 

글쓰기 방법을 논하는 책이다 보니, 아쉬움이 많은 글, 소위 잘 못쓴 글을 예로 들고 
고친 내용과 비교하는 형태가 많이 있는데, 
내용은 좋았으나, 비교라는 형식에서 아쉬움이 좀 있었다. 

일종의 취향인데, 기존에 있던 글과의 비교, 특히 글쓴이 자신의 가치관과 연관이 깊은 글과의 비교 보다, 
이 책을 위한 예문을 통해 전후 비교를 했다면 더 객관적으로 보이는 내용을 담을 수 있지 않았을까.. 

비록, 약간의 아쉬움은 있었을 지언정, 나에게 많은 생각을 던져주는 책이었다. 
어떤 생활로 나의 삶을 채워가는 것이 값진 것인지 생각해봤고, 
그동안 미뤄왔던 독서가 얼마나 중요한 것인지 깊이 깨닳았으며, 
심지어 글쓰기와 프로그래밍의 공통점까지 느꼈다. 

한두번 다시 읽고, 놓친 부분이 있는지 찾아보고 싶다.

http://www.yes24.com/24/goods/17245030

글쓰기가 두려운 그대에게

1. 논증(論證)의 미학(美學)
취향을 두고 논쟁하지 말라
주장은 반드시 논증하라
주제에 집중하라

>> 난 여기 논증의 미학 부분에서 가장 큰 감명을 받았다.

특히 취향과 주장의 차이, 다른 사람이 반박을 할 수 있는 주장을 해야한다는 부분에 크게 동의했다.

2. 글쓰기의 철칙
글쓰기는 기능이다
발췌 요약에서 출발하자
글쓰기의 철칙 1
글쓰기의 철칙 2
혹평과 악플을 겁내지 말자


>> 글쓰기의 철칙에서 가장 와닿았던 말은 많이 읽어야 한다는 것.

많이 읽는다고 글을 잘 쓰는건 아니지만, 글을 잘 쓰는 사람들 중 책을 많이 읽지 않은 사람은 없다!

3. 책 읽기와 글쓰기
독해력
모국어가 중요하다
번역서가 불편한 이유
말이 글보다 먼저다
추천도서 목록을 무시하라


>> 하나의 단락에서 얼마나 많은 정보를 이해해낼 수 있는지 중요하다는 것을 느끼고,

글을 읽으면서 비판적인 자세를 통해 내가 얼마나 더 배울 수 있는지 느꼈다.


4. 전략적 독서 
독해란 무엇인가
글쓰기에 유익한 독서법
《자유론》과 《코스모스》 
전략적 도서 목록


>> 전략적 도서 목록에서 읽고싶은 책으로 코스모스를 골랐다.

조만간 읽어야지!

5. 못난 글을 피하는 법 
못난 글 알아보기
우리글 바로쓰기
중국 글자말 오남용
일본말과 서양말 오염
단문 쓰기
거시기 화법
우리말의 무늬


>> 단문 쓰기와 거시기 화법 부분이 가장 기억에 남는다.

단문 쓰기를 연습해야겠다는 생각이 들었고, 거시기 화법을 쓰지 않아야겠다는 생각을 했다.

내가 얼마나 애매 모호하게 문장을 구사하고 있었는지를 느낄 수 있었다.


6. 아날로그 방식 글쓰기 
글쓰기 근육
짧은 글쓰기
군더더기 없애는 법
소통의 비결


>> 내가 얼마나 군더더기가 많은 글을 쓰고있었는지 돌아보게 만들었다.

군더더기를 없애자.

7. 글쓰기는 축복이다
사는 만큼 쓴다
돈으로 살 수 없는 것
글쟁이의 정신승리법

8. 시험 글쓰기
시험 글쓰기의 특별함
시험 전에 할 일

실전 연습과 그룹 첨삭
<끝>



5.1.xx 에서 5.6.xx 로 업그레이드 할 일이 생겼다.

table partition 을 적용하는데 5.5 이후 버전에서 가능한 상황이었는데,
그래서 일단 사용중이던 VM 을 하나 새로 복사해서 테스트를 해봤다.

yum 으로 설치를 했는데,
일단 MySQL 용 yum repository 파일을 다운로드 받았다.




그리고 yum 의 repository 에 MySQL 5.6 을 추가했다.


For an EL6-based system, the command is in the form of:

shell> sudo yum localinstall mysql-community-release-el6-{version-number}.noarch.rpm  




기존 MySQL 도 삭제해야 하는데,
yum remove 명령어로, mysql 관련 패키지들을 모두 지웠다.

그리고 mysql-community-server 를 yum 으로 설치!

설치가 끝나고 나서 우여곡절이 좀 있긴 했지만, 어찌됐던 설치는 마무리 되었다.

우여곡절은.. yum 으로 설치하려는데 5.1 의 패키지와 충돌이 나는 문제가 있었는데,
yum remove 를 모두 해주지 않아서 생겼던 문제인 것 같기도 한 상태이나 정확한 원인은 모르겠다.

분명히 안됐었는데 갑자기 되기 시작했거든.. -_-;

원인 파악 안된 상태에서 해결됐다고 넘어가는거 안좋아하지만, 암튼 원인을 모르기때문에 어쩔수가 없네 ㅠ_ㅠ

여기까지 해서 mysqld 가 뜨기는 했다.



그리고 기존 데이터베이스를 통으로 덤프 떠서 넣고, 서버를 실행시켜서 클라를 붙였더니 (mysqldump --all-databases 옵션 사용)
이번에는 "Cannot load from mysql.proc." 에러가 발생했다.




mysql_upgrade 를 안했는데 되는게 당연히 안되는게 맞지.

그래서 mysql_upgrade 를 수행했는데, 이게 또 엄청 안되더라.

에러도 그냥 FATAL Error 라고 하면서 안되는 황당한 상황.

여기저기 찾아봤는데 --force 옵션을 주라는 말만 나오고, 답이 쉽게 나오지 않는 상태였는데,
결과적으로 이야기해보면, mysql_upgrade 로 준 옵션으로 mysql 서버에 연결이 안되서 발생한 문제였음

-h 로 서버 주소를 주고나서 괜찮아졌다.

원인을 바로 알려주지 않는 에러메시지는 너무 힘들다. 그래서 이렇게 남겨둔다!

끝!

상황


글로벌 서비스 준비중인데, 모든 시간을 KST 로 작업하고 있었다는..


그래서 모두 UTC 기준으로 변경을 하고 싶어서 프로그래머들끼리 가볍게 이야기를 나눠봤다.


여기서 전제조건!


1. 퍼블리셔는 OS 시간을 변경해줄 수 없음

2. 기존 Code 의 변경은 최소화 (로직을 변경하면서 버그를 만들 수 있는 상황이 아님)



우선, 기준이 되는 UTC 를 사용하려면, DB 에서 쓰고있던 Procedure base logic 들도

CURRENT_TIMESTAMP() -> UTC_TIMESTAMP() 로 변경 (1) 이 필요했고,

Java Code 에서 Calendar, Date, Timestamp 를 쓰는 코드를 모두 점검 (2) 해야 했다.


(1) 은 replace 가 용이한 상황이어서 난이도 하하하

(2) 는 일일이 봐야해서 난이도가 높다고 생각했지만 생각보다 코드가 많지 않아서 중하하


라고 판단함




수정 감행


시간이 그리 많지 않아서 신속하게 결정하고, 아래와 같이 진행했다.



1. TimeZone 기반으로 Calendar 를 하나의 함수를 통해 얻어오도록 수정


기존에는 Calendar.getInstance() 를 여기저기서 호출했는데,

특정 날짜관련 클래스를 하나 만들고 거기서 getNow() 를 통해 Calendar 를 얻어가도록 수정



2. DB 의 모든 CURRENT_TIMESTAMP() 를 UTC_TIMESTAMP() 로 수정


기존에는 CURRENT_TIMESTAMP() 를 사용하고 있어서

MySQL 의 서버 시간 설정에 의한 시간값을 얻어가고 있었는데

UTC_TIMESTAMP() 를 이용함으로써, 서버 시간설정에 영향을 받지 않도록 수정



그 후, 원하는대로 값이 저장되는지 확인해봤다.


그런데, 좀 이상했다..


분명, WAS Code 에서 2015-07-01 00:00:00 을 얻었는데,

DB 에 2015-07-01 09:00:00 이 저장됨


그리고 WAS Code 에서 그 값을 읽어서 클라이언트로 전달하면서

클라이언트는 그 시간이 UTC 라고 믿고 기기가 KST 를 표시해야 하니, +09:00 를 추가로 함 -_-


서버가 클라로 줘야하는 시간이 UTC 로 약속되어있기 때문에,

서버에서 DB 의 값을 읽을 때 UTC 로 변환해야할 필요가 있는것 처럼 느껴졌었는데,


Calendar 를 얻어가는 함수 내부에서 TimeZone 을 UTC 로 설정하고 있기 때문에

코드상의 문제는 아닌것 같았고, 좀더 debugging 해서 결국 문제점을 찾았다.




핵심 문제


핵심 문제는 Timestamp 를 사용해서 DB 쪽으로 인자를 전달하는 과정에서

Timestamp 값이 Calendar의 TimeZone 에 의해 변경되지 않고

JVM의 TimeZone 에 의해 결정된 시간으로 저장되는 것을 몰랐던 것이 문제였다.


깊이 몰랐던 것이 문제여서, 이번 기회에 정리를 해두려고 한다.



Calendar 의 속성


Calendar 는 setTimeZone 에 의해 설정된대로 충실히 동작한다.


예를 들어, 2015-07-01 00:00:00 KST 인 값도 TimeZone 만 변경하면

get(Calendar.HOUR_OF_DAY) 를 했을때 설정된 TimeZone 에 의한 값을 return 한다.


set(Calendar.HOUR_OF_DAY, hour) 도, 내부적으로 설정된 TimeZone 에 의한 시간으로 set 한다.


그래서 현재 Calendar 가 무슨 TimeZone 을 갖는지 확인하고,

비교하는 데이터가 동일한 TimeZone 의 값인지 조심해야 한다.



Date 의 속성


Date 는 TimeZone 과 Independent 하다.


Calendar.getInstance().getTime() 의 Date 는, 내부적으로 fastTime 을 갖고 있고,

이 값이 TimeZone 에 의해 해석되는 형식이다.


여담인데, CallableStatement 의 인자를 setDate 로 넘기면, 날짜만 넘긴다.. -_-



MySQL 의 속성


MySQL 에도 TimeZone 이 엄연히 존재하지만,

JDBC 의 setTimestamp 를 통해 전달된 인자를 변경 없이 충실히 저장해주기 때문에

MySQL 내부에서 UTC_TIMESTAMP() 를 사용하면, 사실상 TimeZone Independent 한 작업을 할 수 있을 것 같다.




결론


여하한 이유로 OS 의 시간을 변경할 수 없다면,

JVM 에서 UTC 를 사용하고, MySQL 에서 UTC_TIMESTAMP() 를 사용하는게 좋은 것 같다.


만약, JVM 에서 UTC 를 설정하기조차 할 수 없었다면,

너무 슬펐을 것 같다. -_-


마지막으로, Tomcat6 에서 TimeZone 설정 변경법




/etc/tomcat6/tomcat6.conf 


... 


# Cannot resolve user database reference - naming-factory-dbcp

# the real problem is a cnfe that is avoided by configuring 

# the -Djavax.sql.DataSource.Factory. This fixes the rpm install. 

JAVA_OPTS="${JAVA_OPTS} -Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourceFactory -Duser.timezone=GMT” 


... 



특정 DB 에 있는 테이블의 스키마가 필요하다는 형의 연락에,
간단하게 table name, column name, column type 을 보여주는 파이썬 코드를 만들어보았음

__author__ = 'novice'

import pymysql

host_addr = '아이피'
user_acc = '디비 계정'
passwd = '비번'
db_name = '데이터베이스'

conn = pymysql.connect(host=host_addr, user=user_acc, passwd=passwd, db=db_name)

cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from information_schema.tables where table_schema = %s', db_name)

for row in cur:
table_name = row['TABLE_NAME']

print('-------------------------------------------------------------------------------------')
print('TABLE NAME : ', table_name)
print('-------------------------------------------------------------------------------------')

t_cur = conn.cursor(pymysql.cursors.DictCursor)
t_cur.execute('select * from information_schema.columns where table_schema = %s and table_name = %s',
(db_name, table_name))

for columns in t_cur:
print(columns['COLUMN_NAME'], '\t', columns['COLUMN_TYPE'], )

print('-------------------------------------------------------------------------------------')
print()

t_cur.close()

cur.close()
conn.close()

끝!


플레이어가 성장하는 도중 어디서 이탈을 많이 하는지 측정하기 위해, Stage 진행에 대한 Funnel 정보 (이하, Stage Funnel) 를 그려봤다.
대부분의 아이디어는 컨설팅을 받을 때 배운 것들이고, 그때 했던 것을 복기하는 차원에서 혼자서 해봤다.
우선 간단하게 데이터 정제를 DB 쿼리로 해봤고, 특정 스테이지 기준으로 더이상 정보가 없는 경우 해당 스테이지에 머물렀다고 간주했음
기간이 영향을 줄 수 있는데, 우선은 Funnel 을 그리는데에 집중했고, 이탈을 정의하는 방법에 따라 쿼리의 Where 절을 변경하면 되겠다.
  • 생각의 전개

    • 플레이어의 성장을 측정하기 위해 성장 기준을 무엇으로 삼을지 고민

    • “레벨", "스테이지 진행" 중 "스테이지 진행”을 선택

      • 레벨은 직관적이지만 후반 구간일수록 대변하는 플레이어 집단의 특성이 분산될 것으로 예상

        예를 들어, 특정 레벨 구간 N 의 플레이어 집단에 대해
        소유한 재화의 평균을 M(N), 표준편차를 D(N) 이라고 하고,
        3Lv과 50Lv 을 서로 비교해보면,
        M(3) 과 M(50) 의 관계는 예측하기 어렵겠지만, 적어도 D(3) < D(50) 일 가능성이 크다고 생각된다

        요약) 플레이를 많이 함 -> 재화 획득 / 소진을 여러번 경험함 -> 보유 재화의 분산이 큼

      • 스테이지 진행도 뒤로 갈수록 레벨과 비슷한 양상을 보이게 되나,
        레벨보다는 훨씬 덜 할 것으로 판단되는데,
        특정 스테이지를 플레이 해도 레벨이 오르지 않는 경우가 많은 것을 생각해보면 그저 당연한 일일 뿐..

    • 특정 스테이지에 머물러 있다는 정보도 중요하지만,
      사실 “왜” 머물러 있는지가 가장 중요할거라고 생각하고,
      “왜” 에 대한 가장 단순한 접근으로, 머물러 있기 직전에 해당 스테이지를 클리어 했는지 여부를 기준으로 그려봤음

    • 실패했는데 나가는것과 성공했는데 나가는 것이 어떤 의미를 갖고 있을지 고민중...

library(RMySQL)

# connect to db
conn <- dbConnect(MySQL(), user='', password='', dbname='', host='')

# select tidy data for stage funnel from stats log db
rs <- dbSendQuery(conn, "select cid, max(cd6) as field, 'lose' as result from _stats_log
     where ts > '2015-01-01 00:00:00'
          and ea like '%lose%'
          and el = 'player'
          and ec = 'combat'
     group by cid
union all
select cid, max(cd6), 'win' as result from _stats_log
     where ts > '2015-01-01 00:00:00'
          and ea like '%win%'
          and el = 'player'
          and ec = 'combat'
     group by cid;
")

# fetch result set
table <- fetch(rs, -1)

# clear db resources
dbClearResult(rs)
dbDisconnect(conn)

# result and field are factor
table$result <- as.factor(table$result)
table$field <- as.factor(table$field)

# show summary information
summary(table)

# draw result
library(ggplot2)

p <- ggplot(table, aes(field, fill=result))
p + geom_bar(position="dodge")



R 에서 의사결정나무 (Decision Tree) 를 간단하게 그려봤다

g1_grade, g1_type 의 값에 따라 succeeded 가 결정된다고 가정하고
이를 위한 예제용 데이터를 만들어봤다

의사결정 나무를 그리는 코드는 여기를 참고해서 만들어봤음 : http://www.statmethods.net/advstats/cart.html

코드 예제에서 rpart method=“anova” 로 되어있는데, 그렇게 그렸더니 원하는 결과가 나오지 않아서
박장시님 도움으로 rpart method=“class” 라고 써야한다는 것과 rattle package 에 대해 배움! - 장시님, 감사 (_ _)



# install prerequisite packages
# 이건 처음에 한번만 하면 됨
install.packages("rpart")
install.packages("rpart.plot")
install.packages("rattle")



# load library of rpart
library(rpart)
library(rpart.plot)
library(rattle)


# make sample data
df <- data.frame(g1_grade=3, g1_type=1, succeeded=1, seq=1:1000)
df <- rbind(df, data.frame(g1_grade=3, g1_type=3, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=3, g1_type=2, succeeded=0, seq=1:900))
df <- rbind(df, data.frame(g1_grade=3, g1_type=1, succeeded=0, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=1, succeeded=1, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=1, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=2, g1_type=2, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=2, g1_type=2, succeeded=1, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=3, succeeded=1, seq=1:200))
df <- rbind(df, data.frame(g1_grade=2, g1_type=3, succeeded=0, seq=1:900))

# show structure of df
str(df)
'data.frame': 5700 obs. of 4 variables:
$ g1_grade : num 3 3 3 3 3 3 3 3 3 3 ...
$ g1_type : num 1 1 1 1 1 1 1 1 1 1 ...
$ succeeded: num 1 1 1 1 1 1 1 1 1 1 ...
$ seq : int 1 2 3 4 5 6 7 8 9 10 ...

# make decision tree
tree <- rpart(succeeded ~ g1_type + g1_grade, data = df,
               method = "class", control = rpart.control(minsplit = 10))


# draw it with just simple plot!
plot(tree)
text(tree, use.n=TRUE, all=TRUE, cex=.8)

# check working directory
getwd()

# create post script file to export tree to pdf
post(tree, file = "tree.ps", title = "How to success")

# draw it with fancy tree!
fancyRpartPlot(tree)




그림들은 모두 같은 그림이고 포장만 좀 다르다.
fancyRpartPlot 이 가장 보기 좋다!

데이터를 해석해보자면,

* 전체 데이터 중 75% 는 succeeded = 0, 25% 는 succeeded = 1
     이게 조금 헛갈릴 수 있는데, 각 노드의 두번째 줄에 표시된 값이
     평가 대상 변수의 분포 비율이라고 보면 됨

* 1번 -> 2번 : 65% 가 넘어갔음. 이 데이터는 g1_type = {2, 3}.
     succeeded 분포는 0 이 92%, 1 이 8% 로,
     이를 통해 g1_type 이 2, 3 인 데이터는 succeeded = 1 이 될 확률이 8% 밖에 안된다고 볼 수 있음

* 1번 -> 3번 : 35% 가 넘어갔음. g1_type = 1
     succeeded 분포는 0 이 45%, 1 이 55% 로,
     g1_grade 기준으로 분기가 또 이루어짐

     * 3번 -> 6번 : 3번 조건인 데이터 중 (전체의 35%),
          전체의 16% 에 해당하는 데이터가 g1_grade < 2.5
          이를 통해 g1_type = 1 && g1_grade < 2.5 인 경우 succeeded = 1 이 될 확률이 11% 

     * 3번 -> 7번 : 3번 조건인 데이터 중 (전체의 35%),
          전체의 19% 에 해당하는 데이터가 g1_grade >= 2.5
          이를 통해 g1_type = 1 && g1_grade >= 2.5 인 경우 succeeded = 1 이 될 확률이 91% 

목적상 succeeded = 1 을 원한다면,
데이터의 g1_type = 1 로 만드는 것이 가장 큰 도움이 되고, 그 다음으로 g1_grade 를 2.5 이상으로 만들어야 함

의사결정 나무에 대해 살짝 알아봤는데,
코드 작성 없이 데이터 만으로 의사결정 나무를 만들어주고 시각화 해주는 R 이 매력적으로 느껴짐!



R 에서 MySQL 에 접속해서 SELECT 한 결과로 보고서를 그려보자

JOIN 이 가능하니, MySQL 에 로그를 넣어놓으면 가공하기 더 좋을 것 같다

참고로 아래 그래프는 우리 데이터에서 grade 별 count 에 대한 graph!



# load libraries for access and draw
library ("DBI")
library ("RMySQL")
library ("ggplot2")


# connect to database server
conn = dbConnect(MySQL(), user=’user', password=‘password', dbname=‘dbname', host=‘host')

# get table list
dbListTables(conn)

# query table content
rs = dbSendQuery(conn, "SELECT * FROM table")

# fetch and make data frame from ResultSet
table = fetch(rs)

# get column names
names(table)

# prepare plot for drawing
d = ggplot(table, aes(factor(column)))

# draw simple bar
d + geom_bar()

# release ResultSet resource
dbClearResult(rs)

# release DB Connection resource
dbDisconnect(conn)



+ Recent posts