좋은 내용이 있어서 발췌한다.



<리더는 욕먹는 예언자다>


상장은 기업의 실적과 권한을 자연스럽게 분배해 준다.

상장과 성장과 보상과 분배의 황금 비율은 기업 경영의 영원한 숙제다. 

넥슨은 이 지점에서 심한 상장통을 앓았다.

수평적 조직이라는 넥슨의 장점이 이때만큼은 약점으로 작용했다.


위젯 인수는 내부 개발자들한테 충격이었지만 분명 김정주의 탁월한 경영적 선택이었다.

위젯 인수는 양면적이었다. 넥슨의 위젯 인수로 한국 게임 업계는 지각 변동을 겪게 된다. 

정상원을 비롯한 개발자들이 대거 이탈하면서 넥슨 경쟁사들의 개발력이 강화된다.


역설적으로 경영 실험의 실패는 넥슨에 전문 경영인 체제가 뿌리내리는 바탕이 된다. 


대주주 김정주가 자기 역할을 재정의한 덕분에 넥슨에는 전문 경영인 중심의 경영 문화가 일찍부터 자리잡게 된다.


...


<혁신의 딜레마>


성공의 역설은 성공한 기업이라면 결코 피해갈 수 없는 함정이다.

성공의 역설은 기업의 관료화와 짝을 이룬다.

변화를 두려워하게 된 조직은 과거의 성공을 이상화하고 자신의 과거를 답습하는 자기 복제를 시작한다.


성공의 역설을 극복하는 유일한 방법은 세대교체 뿐이다.


나갔던 인재는 스스로 더 성장해서 들어와야 한다. 그 때 회사는 기꺼이 더 비싼 몸값을 지불해준다. 


지금까지 넥슨을 성장시킨 인재들 가운데 상당수가 한 때는 넥슨을 매몰차게 떠났던 사람들이라는 건 우연이 아니다.


김정주의 최대 장기가 이것이다. 김정주는 퍼즐을 맞추듯 인재와 기술과 자본을 조합한다. 


퍼즐은 세월에 깍이고 넥슨이라는 퍼즐판의 구조가 또 바뀌기 때문이다. 사람은 변하고 판은 바뀐다.






'After reading book' 카테고리의 다른 글

무인양품을 읽고  (0) 2019.10.15
'우버인사이드'를 읽고  (0) 2019.10.13
[펌] 넥슨, 플레이의 좋은 내용  (0) 2019.10.08
"쿠팡" - 우리가 혁신하는 이유  (0) 2019.10.05
구글을 움직이는 10가지 황금율  (0) 2019.08.17
파워풀  (0) 2019.07.25
Posted by 김용환 '김용환'

댓글을 달아 주세요


맨날 까먹는 JPA의 Repository 조회 메소드 규칙은 다음과 같다. 



https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods


Table 3. Supported keywords inside method names
KeywordSampleJPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

IsEquals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNullNull

findByAge(Is)Null

… where x.age is null

IsNotNullNotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)


Posted by 김용환 '김용환'

댓글을 달아 주세요


jackson 2.9 버전부터  com.fasterxml.jackson.databind.util.ISO8601DateFormat 클래스는 deprecated되었다.


아래와 같은 코드는 더 이상 사용하지 않고.


 ObjectMapper mapper = new ObjectMapper();

objectMapper.setDateFormat(new ISO8601DateFormat());


StDateFormat이나, Joda 를 사용하는 것이 좋은 것 같다. 


 ObjectMapper mapper = new ObjectMapper();

 mapper.setDateFormat(new StdDateFormat());




개인적으로  Joda로 변경하니 괜찮았다.

Posted by 김용환 '김용환'

댓글을 달아 주세요



scala circe 라이브러리를 사용하다 아래와 같은 에러를 만났다.

could not find implicit value for parameter encoder: io.circe.Encoder[Message]



대충 코드는 이렇다. Message case class를 json으로 변경하는 것이다.

object MessageType extends Enumeration {
type MessageType = Value
val DEL = Value
val INSERT = Value
}

case class Message(
val version: String,
val pipelineType: MessageType.Value,
val headers: Map[String, Object],
val createdAt: String
)


scala enumeration은 아래와 같이 처리해 주었는데.. 역시 동일한 에러이다. 

implicit val decoder: Decoder[MessageType.Value] = Decoder.enumDecoder(MessageType)
implicit val encoder: Encoder[MessageType.Value] = Encoder.enumEncoder(MessageType)



혹시나 circe는  Map의 value type을 중요하게 본다. 역시 에러다

case class Message(

val version: String,
val pipelineType: MessageType.Value,
val headers: Map[String, Any],
val createdAt: String
)



아래와 같이 value type을 String으로 변경하니 동작한다

case class Message(
val version: String,
val pipelineType: MessageType.Value,
val headers: Map[String, String],
val createdAt: String
)



경험해보니 최대한 간결한 패턴의 case class를 써야 circe가 잘 동작한다. 




Posted by 김용환 '김용환'

댓글을 달아 주세요


보통 소켓을 다루는 간단한 자바 애플리케이션 예시의 경우, socket을 close하지 않아도 자연스럽게 정리된다.


공식 RabbitMQ 자바 Client을 사용할 때 

publish 코드에서 사용하는 connection을 종료하지 않으면 계속 hang된다.



val connection = connectionFactory.newConnection
val channel = connection.createChannel
channel.exchangeDeclare(exchange, builtinExchangeType, false)
messages.foreach { message =>
channel.basicPublish(exchange, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"))
}


jstack을 통해  비동기 쓰레드 폴링을 확인할 수 있다.

"Process Proxy: RabbitPublisher" #473 prio=6 os_prio=31 tid=0x00007f854bd53000 nid=0x1572b runnable [0x000070000cde6000]

   java.lang.Thread.State: RUNNABLE

at sun.nio.ch.KQueue.keventPoll(Native Method)

at sun.nio.ch.KQueuePort$EventHandlerTask.poll(KQueuePort.java:196)

at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:276)

at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

at java.lang.Thread.run(Thread.java:748)



항상 자원을 close를 처리할 필요가 있다.

val connection = connectionFactory.newConnection
val channel = connection.createChannel
channel.exchangeDeclare(exchange, builtinExchangeType, false)
messages.foreach { message =>
channel.basicPublish(exchange, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"))
}
channel.close()
connection.close()


Posted by 김용환 '김용환'

댓글을 달아 주세요

카카오에 다니면서 가장 좋았던 점은  수평적인  문화를 경험했다는 점이다.


아무도 시키지 않았지만 회사의  리스크를 해결할 때  개발 경쟁력을 높일 때 기분이 좋았다.


여전히 말단 직장인인데도 승진 못했다고 어려워하지 않은 이유가  바로 수평적인  문화에 있다고 할 수 있다.




"쿠팡" - 우리가 혁신하는 이유 책을 읽고 Aggresive 수평문화를 가진 문화를 엿볼 수 있어서 좋았다. 말로만 듣던 힘들다는 점이 이거였군.... ㅎㅎㅎ



좋은  내용을 발췌한다.



Wow - 고객의 신뢰를 최우선한다.

Focus - 결과를 낼 수 있는 일을 찾아서 깊이 있게 판다.

Fail Fast - 우리는 실패를 두려워하지 않고,실패로부터 배운다.

Be Open - 군중심리에 휩쓸리지 않고 자신의 목소리를 낸다.

Believe - 나와 내 동료들과 회사의 미래를 믿는다.


PO는 쿠팡의 핵심 직군으로, 개별 파트의 소사장 개념으로, 미니 CEO라고 불린다.

성과를 평가하지 않지만 대화와 설득으로 일을 수행한다.

 



쿠팡은 커머스 전략을 위해 세 가지에 집중한다


-셀렉션 (selection)- 파는 물건의 종류

-프라이스 (price)

-컨비니언스 (convenience)



이를 통해 얻는 효과들은 아래와 같다.


- 사입을 통한 가격 경쟁력 향상

- 직접배송을 통한 서비스 품질 제어

- 직접배송을 통한 배송 서비스 질 향상

- 기술력(간편결제, 애플리케이션 등)을 통한 서비스 편의성 향상



기술력(개발자)가 혁신이다.

'After reading book' 카테고리의 다른 글

'우버인사이드'를 읽고  (0) 2019.10.13
[펌] 넥슨, 플레이의 좋은 내용  (0) 2019.10.08
"쿠팡" - 우리가 혁신하는 이유  (0) 2019.10.05
구글을 움직이는 10가지 황금율  (0) 2019.08.17
파워풀  (0) 2019.07.25
'플랫폼 제국의 미래'를 보고  (0) 2019.06.24
Posted by 김용환 '김용환'

댓글을 달아 주세요

[펌] quagga bgpd 데몬

Cloud 2019. 10. 4. 13:26



https://www.quagga.net/


Quagga는 Unix 플랫폼, 특히 FreeBSD, Linux, Solaris 및 NetBSD에서 사용되는 OSPFv2, OSPFv3, RIP v1 및 v2, RIPng, BGP-4의 구현을 제공하는 라우팅 소프트웨어이다. Quagga는 Kunihiro Ishiguro가 개발한 GNU Zebra의 포크 버전이다.


Quagga 아키텍처는 기본 데몬 인 zebra로 구성되어 있으며 기본 Unix 커널에 대한 추상화 계층 역할을하며 Unix 또는 TCP 스트림을 통해 Zserv API를 Quagga 클라이언트에 제공합니다. 이러한 Zserv 클라이언트는 일반적으로 라우팅 프로토콜을 구현하고 라우팅 업데이트를 zebra 데몬과 통신합니다. 기존 Zserv 구현은 다음과 같습니다.





https://ixnfo.com/en/setting-up-bgp-in-quagga.html

Posted by 김용환 '김용환'

댓글을 달아 주세요

[scala] jackson, ujson

scala 2019. 10. 4. 11:58


scala 에서 json을 사용할 때 jackson을 바인딩하는 것다..

import java.util.TimeZone

import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
import com.fasterxml.jackson.databind.util.StdDateFormat
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

object JasksonJsonUtil {
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

val stdDateFormat = new StdDateFormat()
stdDateFormat.setTimeZone(TimeZone.getDefault)
mapper.setDateFormat(stdDateFormat)

def toJson(value: Object): String = {
mapper.writeValueAsString(value)
}
}


ujson이 좀 나은 것 같다.  


ujson에서는 scala 계에서 유명한 lihaoyi 라이브러리(com.lihaoyi:ujson)를 활용한다.




import org.scalatest.{FunSuite, Matchers}
import java.text.SimpleDateFormat
import java.util.Date

import ujson.Value

class JasksonJsonUtilTest extends FunSuite with Matchers {
test("JacksonJsonUtil.toJson") {
case class Model(name: String, date: Date)
val actual = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .parse("2019-10-04T10:14:27.783+0900")
val json: String = JasksonJsonUtil.toJson(Model("expire", actual))
UJson(json).get("date").str should be("2019-10-04T10:14:27.783+0900")
}

case class UJson(jsonString: String) {
val value: Value = ujson.read(jsonString)

def get(keys: String): Value.Value = {
var x = value
keys.split("[.]").foreach { key =>
x = x(key)
}
x
}
}
}


Posted by 김용환 '김용환'

댓글을 달아 주세요


spark app을 graceful하게 종료하는 방법이 정리된 링크이다.


https://www.linkedin.com/pulse/how-shutdown-spark-streaming-job-gracefully-lan-jiang/


https://github.com/lanjiang/streamingstopgraceful/blob/master/src/main/scala/com/cloudera/ps/GracefulShutdownExample.scala

Posted by 김용환 '김용환'

댓글을 달아 주세요



일래스틱서치에서 저장된 index가 월별일 때 특정 월만 검색하고 싶다면 body나 request에 포함할 수 있다.


curl -X GET "http://inhouse.google.com:9200/reqlog-*/_search?ignore_unavailable=true" -H 'Content-Type: application/json' -d'

{

    "query": {

        "terms" : {

            "_index" : ["reqlog-2019-09, reqlog-2019-10"]

        }

    }

}'


또는

curl -X GET http://inhouse.google.com:9200/reqlog-*/_search/reqlog-2019-09, reqlog-2019-10/_search?ignore_unavailable=true

{

    "query": {

        ...

    }

}



 ignore_unavailable의 기본 값은 false이다. 검색시 인덱스가 없으면 에러가 발생한다.
 true로 설정해서 검색시 인덱스 없으면 에러가 발생하지 않는다.



Posted by 김용환 '김용환'

댓글을 달아 주세요