<< Previous | Home

역사속의 소스 코드들

옛 영광을 뒤로하고 사라져간 소스들을 찾아보았습니다.
Tags :

왜 Vim은 방향키로 hjkl로 했을까?

why vim uses the hjkl keys as arrow keys

UNIX 편집기로 Emacs과 쌍벽을 이루는 VI는 전설적인 개발자 빌 조이에 의해 만들어졌죠. 그런데, VI를 가장 많이 쓰는 키가 바로 이동키인데, 그것이 hjkl로 되어 있다는 것에 궁금한분들이 계실텐데, 아시는 분들도 많겠지만 모르는 분들을 위해 좀 정리를 해 둡니다.

당시 빌 조이가 몸담고 있던 버클리에 도입된 단말기가 "ADM-3A"란 터미널인데(아래 그림 참조), 이 단말기엔 방향키와 더불어 마우스가 없었답니다.


그래서, 자판기의 방향키가 "hjkl"로 표시되어 있어 VI에서도 동일한 키로 방향키를 선택하게 되었던거죠.


[참조 사이트]
Tags : ,

왜 철학을 공부하는가?

Why Study Philosophy?

하버드 대학에서 철학을 공부해야하는 이유에 대해 명확하게 기술되어 있어서 간략하게 나마 번역해 보았습니다. 살아가는데 좋은 교훈이지 않을까 생각합니다.

원문은 "Why Study Philosophy?" 입니다.

캘리포니아 대학 버클리 캠퍼스의 철학자인 존 캠벨은 철학에 대해 어떻게 생각하는지에 대한 내용입니다. -- “철학은 우리가 평소 굉장한 속도로 움직이고 있는 것을 분해하고, 설명하고, 평가합니다. 그러면 대안이 가능한지가 명확하게 됩니다.“

철학을 배우는 것은 과거 존재했던 위대한 사상가들과의 대화를 하면서 수천년 동안 인류를 괴롭혀 온 물음에 대해 사투를 벌이고 해결책을 찾는 것입니다. 학생들은 한, 두과목을 선택하든, 집중적으로 공부하든, 대학 생활의 가장 보람있는 지적 경험을 철학을 배우는 시간에서 찾을 수 있습니다. 철학을 공부하는 주된 이유는 반성과 심사 숙고의 본질적인 가치를 발견하는 것입니다.

그럼에도 불구하고 철학에 매료되어 있는 많은 학부생은 철학 학위가 빈곤의 길이라고 걱정하고 있습니다. 그러나, 실제로 철학을 배움으로써 얻을 수 있는 스킬은 변덕스럽고 급변하는 경제 환경속에서 높은 시장 가치가 있습니다. 많은 전문 기술은 결국 무용지물이 되고, 어떤 경우에도 대부분의 사람들은 살면서 여러번 직업을 바꾸게 됩니다. 철학이 당신에게 가르쳐주는 기술은 항상 수요가 많습니다. 명확하게 생각하고 쓰는 능력, 예측 불허들을 예상하는 능력, 복잡한 생각을 명료하게 설명하고, 관련성을 이해할 수 있게 하고, 더 넓은 맥락에서 사물을 보며 관습에 도전을 하는 능력을 길러줍니다. 즉, 철학은 어떤 업종에서도 적용될 수 있는 능력을 제공합니다.

하버드 대학 철학 전공 학생들은 다양하고 가치 있는 경력을 추구해 왔습니다. 철학과 졸업생들은 법률, 금융, 컨설팅, 사업, 인터넷 스타트업, 의학, 언론, 예술, 비영리 직업, 교육, 학술(철학과 다른 학문 분야 모두) 분야에서 성공을 거두고 있습니다. 당신 스스로에게 물어야 것은 "나는 철학 학위로 무엇을 할 수 있느냐?"가 아니라, "나는 이 철학의 학위로 할 수 없는게 무엇인가?"입니다.

DI(의존성 주입)가 필요한 이유와 Spring에서 Field Injection보다 Constructor Injection이 권장되는 이유

왜 DI(의존성 주입)가 필요한가?

왜 DI(의존성 주입)가 필요한가?에 대한 좋은 해답으로 Google Guice Motivation페이지에서 잘 설명해 주어서 인용해 본다.

동기요인
관련된 모든 객체들을 밀결합하는 것은 어플리케이션 개발 부분에서 짜증나는 일이 된다. 어플리케이션에는 서비스, ​​데이터, 그리고 프리젠테이션 클래스들을 연결하는 방법에는 여러 가지가 있다. 이러한 접근 방법을 비교하기 위해 피자 주문에 관련된 빌링 코드를 작성할 것이다.
public interface BillingService {
  Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);
}

구현과 함게 단위 테스트를 작성할 것이다. 테스트는 실제 신용 카드로 결제가 안되게 가짜 카드 결제를 구현한 FakeCreditCardProcessor클래스가 필요하다.

생성자 함수 직접 호출
다음 코드는 카드 결제 클래스(CreditCardProcessor)와 트랜잭션을 기록하는 클래스(TransactionLog)를 new 연산자로 인스턴스화하는 경우이다.
public class RealBillingService implements BillingService {
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = new PaypalCreditCardProcessor();
    TransactionLog transactionLog = new DatabaseTransactionLog();
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

이 코드는 모듈화와 테스트 가능성에 문제가 있다. 진짜 신용 결제를 할 클래스(CreditCardProcessor)로 컴파일 시점에 직접적으로 의존하고 있기 때문에 테스트를 하면 카드에 청구되어 버린다. 또한 결제가 실패했을 때나 서비스가 중지상태가 되면 테스트하기가 힘들어진다.

Factory 클래스
Factory 클래스가 클라이언트와 서비스 구현을 분리해준다. 간단한 Factory 클래스에서 인터페이스를 구현한 mock getter와 setter로 사용할 수 있다. Factory 클래스는 아래와 같이 일부 boilerplate 코드와 함께 구현되어 있다.
public class CreditCardProcessorFactory {
  private static CreditCardProcessor instance;
  public static void setInstance(CreditCardProcessor processor) {
    instance = processor;
  }

  public static CreditCardProcessor getInstance() {
    if (instance == null) {
      return new SquareCreditCardProcessor();
    }

    return instance;
  }
}

클라이언트 코드에서는 new 호출하는 대신에 Factory 메소드로 변경하면 된다.
public class RealBillingService implements BillingService {
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = CreditCardProcessorFactory.getInstance();
    TransactionLog transactionLog = TransactionLogFactory.getInstance();

    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

Factory를 사용하면, 표준 단위 테스트를 작성할 수 있게 된다.
public class RealBillingServiceTest extends TestCase {
  private final PizzaOrder order = new PizzaOrder(100);
  private final CreditCard creditCard = new CreditCard("1234", 11, 2010);

  private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog();
  private final FakeCreditCardProcessor processor = new FakeCreditCardProcessor();

  @Override public void setUp() {
    TransactionLogFactory.setInstance(transactionLog);
    CreditCardProcessorFactory.setInstance(processor);
  }

  @Override public void tearDown() {
    TransactionLogFactory.setInstance(null);
    CreditCardProcessorFactory.setInstance(null);
  }

  public void testSuccessfulCharge() {
    RealBillingService billingService = new RealBillingService();
    Receipt receipt = billingService.chargeOrder(order, creditCard);

    assertTrue(receipt.hasSuccessfulCharge());
    assertEquals(100, receipt.getAmountOfCharge());
    assertEquals(creditCard, processor.getCardOfOnlyCharge());
    assertEquals(100, processor.getAmountOfOnlyCharge());
    assertTrue(transactionLog.wasSuccessLogged());
  }
}

이 코드도 뭔가 어설프다. 글로벌 변수가 mock 구현을 가지고 있기 때문에 setUp 및 tearDown에 세심한 주의를 필요로 한다. tearDown 메소드가 실패하면 글로벌 변수는 테스트 인스턴스를 참조하고 있다. 이렇게 되면 다른 테스트에 문제를 야기할 수 있고, 여러 테스트를 병행할 수 없게 된다.

그러나 가장 큰 문제는 의존성이 코드속에 숨겨져 있다는 것이다. 만약 CreditCardFraudTracker를 의존성을 추가했다고 하자. 테스트가 실패했을 때, 의존하는 어떤 클래스에 문제가 있었는지를 알기 위해서는 테스트를 한번 더 실행해야 한다. 만약 운영하고 있는 서비스에서 Factory를 초기화하는 것을 잊고 테스트를 수행할 때 과금이 나올때까지 문제가 있는지 알 수 없게 된다. 어플리케이션이 비대해지면 의존성의 주의가 요하는 Factory 클래스는 생산성을 떨어떨어지게 하는 원인이 된다.

품질 문제는 QA나 인수 테스트에서 발견할 수 있다. 지금으로도 충분할지도 모르지만, 확실히 더 나은 방법이 있다.

의존성 주입(DI)
Factory처럼 DI도 디자인 패턴이다. 핵심 원칙은 행태와 의존성 해결을 분리하는 것이다. 여기 예제에서 보면, RealBillingService는 TransactionLog와 CreditCardProcessor를 lookup하는 책임을 지지 않는다. 대신 그들은 생성자를 인수로 전달한다.
public class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

더 이상 Factory는 필요가 없다. 게다가, boilerplate 코드인 setUp과 tearDown를 없애서 테스트 케이스가 훨씬 간결해졌다.
public class RealBillingServiceTest extends TestCase {

  private final PizzaOrder order = new PizzaOrder(100);
  private final CreditCard creditCard = new CreditCard("1234", 11, 2010);

  private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog();
  private final FakeCreditCardProcessor processor = new FakeCreditCardProcessor();

  public void testSuccessfulCharge() {
    RealBillingService billingService
        = new RealBillingService(processor, transactionLog);
    Receipt receipt = billingService.chargeOrder(order, creditCard);

    assertTrue(receipt.hasSuccessfulCharge());
    assertEquals(100, receipt.getAmountOfCharge());
    assertEquals(creditCard, processor.getCardOfOnlyCharge());
    assertEquals(100, processor.getAmountOfOnlyCharge());
    assertTrue(transactionLog.wasSuccessLogged());
  }
}

이렇게 되면 언제 의존성을 추가하거나, 삭제하고자 하면 어떤 테스트를 수정하면 좋을지 컴파일러가 알려준다. 의존성은 API의 시그너처내에서 노출되게 된다.

그러나 불행히도, BillingService의 클라이언트는 의존성을 스스로 찾아야 한다. 이런 문제는 DI 패턴을 통해 다시 해결책을 찾을 수 있다. BillingService에 의존하고 있는 클래스는 생성자에서 BillingService를 받을 수 있다. 그러나 최상위 클래스를 위해서는 프레임 워크가 있는 편이 유리하다. 그렇지 않으면, 서비스 사용이 필요할 때 재귀적으로 의존 관계를 구축하게 될 경우도 있다.
public static void main(String[] args) {
    CreditCardProcessor processor = new PaypalCreditCardProcessor();
    TransactionLog transactionLog = new DatabaseTransactionLog();
    BillingService billingService
        = new RealBillingService(processor, transactionLog)로
    ...
  }

결론적으로 DI는 코드의 모듈화와 테스트 가능성을 향상시켜 주기 때문에 필요하다.

다음으로 좀 묵은 주제이기도 하지만, 주변을 둘러봐도 그렇고, 일단 의존성 추가나 삭제가 간결하고 코드량도 많지 않고, 문제 발생한 경우도 없어서인지 Field Injection을 많이 사용하게 됩니다.
하지만, 왜 Spring Team에서 Constructor Injection을 추천하는지 리마인드 차원에서 정리해 봅니다.

Spring의 Dependency Injection

1. Constructor Injection
Spring 4.3에서 단일 생성자의 경우 @Autowired가 필요가 없다.
@Component
public class ConstructorInjection {
     private final LoginService loginService;
     private final SignupService signupService;

    @Autowired
    public ConstructorInjection(LoginService loginService, 
                SignupService signupService) {
         this.loginService = loginService;
         this.signupService = signupService;
    }
}

2. Field Injection
@Component
public  class FieldInjection {
    @Autowired
    private LoginService loginService;
    @Autowired
    private SignupService signupService;
}

3. Setter Injection
@Component
public  class SetterInjection {
     private LoginService loginService;
     private SignupService signupService;

    @Autowired
    public  void setLoginService(LoginService loginService) {
         this.loginService = loginService;
    }

    @Autowired
    public  void setSignupService(SignupService signupService) {
         this.signupService = signupService;
    }
}

왜 Constructor Injection을 권장하나?

1. 단일 책임의 원칙
생성자의 인자가 많을 경우 코드량도 많아지고, 의존관계도 많아져 단일 책임의 원칙에 위배된다. 그래서 Constructor Injection을 사용함으로써 의존관계, 복잡성을 쉽게 알수 있어 리팩토링의 단초를 제공하게 된다.

2. 테스트 용이성
DI 컨테이너에서 관리되는 클래스는 특정 DI 컨테이너에 의존하지 않고 POJO여야 한다. DI 컨테이너를 사용하지 않고도 인스턴스화 할 수 있고, 단위 테스트도 가능하며, 다른 DI 프레임 워크로 전환할 수도 있게 된다.

3. Immutability
Constructor Injection에서는 필드는 final로 선언할 수 있다. 불변 객체가 가능한데 비해 Field Injection은 final는 선언할 수 없기 때문에 객체가 변경 가능한 상태가 된다.

4. 순환 의존성
Constructor Injection에서는 멤버 객체가 순환 의존성을 가질 경우 BeanCurrentlyInCreationException이 발생해서 순환 의존성을 알 수 있게 된다.

5. 의존성 명시
의존 객체 중 필수는 Constructor Injection을 옵션인 경우는 Setter Injection을 활용할 수 있다.

Lombok을 활용한 Constructor Injection

참고로 개발 편이성은 좋아질 수 있으나, 의존관계의 복잡성을 명확하게 보여주진 못하게 된다.
@RequiredArgsConstructor는 초기화 되지 않은 final 필드를 매개 변수로 취하는 생성자를 생성하고 @NonNull이 필드는 null 체크가 실행되고 파라미터가 null인 경우는 NullPointerException을 발생시킨다.

1. Spring 4.3 이상
@RequiredArgsConstructor
@Component
public class ConstructorInjection {
    @NonNull
    private final LoginService loginService;
    @NonNull
    private final SignupService signupService;
}

2. Spring 4.3 이전
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Component
public class ConstructorInjection {
    @NonNull
    private final LoginService loginService;
    @NonNull
    private final SignupService signupService;
}

[참고 사이트]
Tags : , ,

CTO는 무슨 일을 하나?

What does a CTO do?

CTO에 대해서는 좋은 아티클을 추천한다면 개인적으로 #define CTO와 함께 What does a CTO do?를 공유하곤 하는데, 그 중 후자가 더 CTO가 해야할 역할에 대해 잘 기술해 놓은 것 같아서 원저자의 하락하에 번역해 봅니다. What does a CTO do? 아티클의 저자는 Cloudera의 Founder이자 CTO인 Amr Awadallah입니다.



이하는 번역내용을 기술합니다.

이 질문은 내가 많이 받은 질문이기도 하고, 1년 전(2012년에 Cloudera CTO가 되었을 때)만 해도 이에 대한 해답을 찾으려고 노력을 많이 했다. 그래서 온라인상에서 자료도 많이 찾았고, 많은 소프트웨어 기업의 CTO들을 찾아 논의하기도 했다.

여기에서는 내가 가진 지식과 조사 결과들을 가지고 다음 네가지 물음에 대한 답변을 요약하고자 한다.
  • 질문 1. CTO의 미션(사명)은 무엇일까?
  • 질문 2. CTO는 구체적으로 어떻게 평가되어야 할까?
  • 질문 3. CTO는 사내 업무와 대외 업무를 어떻게 시간을 배분해야 할까?
  • 질문 4. 회사의 내부 구성원들에게 CTO가 달성해야하는 구체적인 책무는 무엇일까?

덧붙여 말하자면, 내가 다음에 언급하는 것은 내부 엔지니어 조직을 관리하고 있는 CTO가 아닌, 순수한 CTO에 초점을 맞추고 있으며, 일반 기업의 CTO나 다른 도메인에서는 편향되어 있을 수 있다.

1. CTO의 미션은 무엇인가?

CTO의 임무는 다음 3가지 축으로 요약될 수 있다.

A) 회사의 장기적인 기술 전략 책임자
  • CTO는 기업의 기술 전략을 제대로 유지/관리하고, 명확하게 구체화하고, 또한 지속적으로 올바른 방향으로 발전시켜 나가야 한다.
  • CTO는 역동적으로 진화하고 있는 경쟁 영역에서 회사가 최고의 기술 성과를 지속적으로 발휘할 수 있도록 해야하는 책임이 있다.
  • CTO는 기업의 미래를 좌우하는 중요한 기술적 트렌드 정보를 세상으로부터 식별하고, 기업 내부에 적절하게 연결하며, 그리고 기술 전략과 비즈니스 전략의 적절한 균형을 유지해야 한다.

B) 기술 책임자(테크니컬 에반젤리스트)
  • CTO는 회사의 장기적인 비전을 중심으로 내부 사람들에게 영감과 흥미를 유발해야 한다. 또한 외부의 사람들에게는 세상은 결국 그렇게 될 것이며, 자신의 회사가 최선의 선택임을 확신시켜야 한다.
  • CTO는 시장의 요구를 강조해야하고, 고객을 신뢰할 수 있어야 하고, 비즈니스 가치와ROI(투자 수익률)을 폭넓은 이해당사자들에게 명확하게 이야기할 수 있어야 한다.

C) 엔지니어들의 정신적 리더
  • CTO는 엔지니어 팀을 회사의 장기적인 기술적 목표를 향해 이끌어야 한다.
  • CTO는 새로운 엔지니어가엔지니어 조직에 합류할 수 있도록 영감을 주어야 하며, 인재를 식별하고 조직에 공급할 수 있어야 한다.
  • CTO는 최고의 기술 인재를 유치하고, 이탈없이 지속적으로 보유할 수 있도록 기술 문화도 만들고 유지해야 한다.

2. CTO의 성과를 어떻게 측정할 것인가?

나는 다음 세 가지 측면이 있다고 생각한다.

A) 기술 전략의 방향성 조정.
이것은 양면성을 가지고 있다. 만약 기업이 핵심 기술 동향을 놓치면 불리해지고, 또한, 기술 전략과 비즈니스 전략의 중대한 불일치가 있는 경우에도 불리해진다. 이 점에 관해서는 썬 마이크로 시스템즈의 Greg Papadopolous의 말을 인용한다.
"CFO는 분기별 수익에 대해 일일이 책임을 갖는 것은 아니지만, 회계에 한번이라도 예상치 못한 잘못을 저질렀을 경우 해고해야 한다. 마찬가지로, CTO는 분기별 출시되는 제품에 대해 일일이 책임을 질 필요는 없지만, 만약 핵심 기술의 변곡점(예, 인터넷)을 놓치면 그를 해고해야 한다."

B) 엔지니어 문화의 건강성 체크
기술 조직에 대한 설문조사를 통해 엔지니어의 행복도나 생산성이 동시에 보장 되는지를 측정할 수 있다. 그 지수에 문제가 있는 경우, 기존의 우수한 엔지니어는 마찰을 느끼게 되는 것이고, 외부의 재능있는 엔지니어를 유치 할 수 없다.

C) 회사의 내부 고객 만족도
CFO와 유사한 CTO는 회사내의 모든 직원들에게 고객 지원 센터 같은 존재다. 따라서 적절한 측정 항목은 조직 내부의 직원 만족도가 될 수 있다. CTO 대한 직원 만족도(CTO로부터 부가가치를 얻고 있지 못하다고 느끼면)가 낮으면 그를 해고해야 할 것이다.

3. CTO는 사내 업무와 대외 업무를 어떻게 시간을 배분해야 할까?

이 문제에 대한 완벽한 답은 없지만, 옳은 대답은  둘다 조금씩 존재한다. 외부의 지식 없이는 CTO는 조직 내에서 좋은 일을 할 수 없으며, 그 반대도 마찬가지이다. 둘 모두(내부/외부 업무)를 해야만 CTO는 조직의 비즈니스에 공헌할 수 있다. 또한 가족이나 여행을 생각해야 한다. CTO는 조직내에서 지속적으로 일해야하므로 당신만의 최적의 장소도 찾아야 한다. 이에 대한 답은 회사가 어떤 단계에 있는지에 따라 달라진다. 이런 배후 사정을 고민해서 오늘이라는 하루를 다음과 같이 구분했다.

A) 외부: 70%
  • 판매/고객관리: 35%
  • 마케팅/기술 전도사/산업 분석: 20%
  • 비즈니스 기획/파트너 관리: 15%

B) 사내: 30%
  • 외부에서 수집한 정보들을 내재화 해 그 지식을 적절한 내부팀에 공유한다.
  • 기술과 제품, 비즈니스를 지속적으로 연결하여 공개적으로 명시된 비전에 회사가 현재 가고 있는 방향과 일치하는지, 기술 로드맵이 회사의 비전과 일치하는지 확인한다.
  • 회사의 비전을 명확히 표현하는 White Paper나 산출물을 만들기 위해 마케팅 팀 지원을 함.
  • 엔지니어 문화, 프로세스, 인력 유지 등
  • 지적 재산권 포트폴리오 보호

4. 회사내 각 부서를 위해 CTO로서 구체적인 책무는 무엇인가?

다음 다섯 개의 부분이 CTO로서 완수해야 할 책무가 있다고 할 수 있다. CEO/기업 전략, 엔지니어링/제품 개발, 세일즈, 비즈니스 개발, 마케팅. 아래는 그 구성 요소들에 대한 핵심 역할을 기술한다.

A) CEO/기업 전략
  • 회사의 비즈니스에 결정적인 영향을 미칠 수 있는 모든 기술 전환점 예측.
  • 장기적인 관점에서 회사의 기술 전략 방향과 어떤 기술에 베팅할 것인가를 CEO(CFO/COO)에게 조언한다.
  • CEO가 회사의 기술 방향에 대해 주어진 시간에서 취할 수있는 충분한 정보와 최선의 대안들을 제시한다.
  • 회사의 장기적인 이익을 무엇보다 우선시하는 중립적인 견해를 제공함으로써 CEO를 위한 이사회가 되어야 한다. CFO와 유사하게 CTO는 중요한 비즈니스 리소스를 직접적으로 소유하고 있지 않기 때문에 이를 효율적으로 수행할 수 있는 유일한 위치이다.

B) 엔지니어링/제품 개발
  • CTO는 그날 그날의 기술적인 전략/전술에 대한 관리에 책임을 가질 필요는 없지만, 제품이나 엔지니어링 VP와 긴밀하게 협력하여 전반적인 개발 방향이 회사의 전략적 기술 비전과 부합하도록 해야한다.
  • CTO는 큰 틀에서 전략적 기술 배팅에 우선 순위를 두어야지, 일상적으로 이루어지는 실행에 대해서는 관여해서는 안된다. 일상적인 업무도 중요하지만(놓기 힘들수도 있지만), 장기적인 전략적 사고에 중점을 두는게 필요하다.
  • CTO의 주요 과제중에 하나는 제품 관리나 엔지니어링 관여하지 않고 사람이나 프로젝트, 아이디어들을 어떻게 지원해 주는가를 고민해 성공시키는게 중요하다. 그래서 CTO는 강력한 영향력을 행사할 수 있는 교육을 받아야 하고 어떤 아이디어에 집중할 것인지를 엄격하게 선택하는 것이 필요하다.
  • 엔지니어링 VP를 대변하는(편에 서는) 임원이 되어야 하며, 그가 발표를 하는데 도와주어야 하고 다양한 팀이 직면하고 있는 문제들에 대해 브레인스토밍을 할 수 있도록 도와주어야 한다. CTO와 엔지니어링 VP는 긴밀한 협력 관계여야하며, 매우 중요한 관계이므로, 이를 위해 노력하는데 상당한 시간을 할애해야 한다.
  • 인재 모집이나 유지 노력도 해야 한다.(학계와의 관계도 포함된다.)
  • 전체 조직의 역량 중복이나 낭비를 제거하기 위해 지속적으로 최적화를 하고 부서간의 내실을 다지게 한다.
  • 개발 조직간의 협조나 올바른 방향을 유도하고, 필요한 경우, 기술적 파편화나 어떤 아키텍처를 채용해야 하는가에 대한 분쟁 등을 조정해야 한다.
  • 정기적으로 해커톤을 개최하고 초기 단계에서부터 책임자가 되어 혁신을 조성, 발전 시켜야 한다.
  • 제품 라인 전반에 걸쳐 마스터 아키텍트 역할을 해야한다. 하지만, Cloudera에서는 내가 별로 할 수 있는게 많지 않았지만, 다행히 Cloudera는 나보다 우수한 아키텍트가 많이 있고, 내가 할 수 있었던 것은 그들이 그것에 집중할 수 있도록 도와 줄 뿐이다.(주로 갈등이 클 경우에만 참여했고 그것도 Cloudera에서는 드문 일이었다.)

C) 세일즈
  • 고객과 효과적으로 친밀한 관계를 가지려면 세일즈 조직과 협력하라.
  • 고객과 효과적으로 친밀한 관계를 가지려면 세일즈 조직과 협력하라.
  • 고객과 효과적으로 친밀한 관계를 가지려면 세일즈 조직과 협력하라.
  • 중요하기 때문에 세 번 강조했지만, 전략적 고객과 대등하게 좋은 관계를 구축해서 세일즈 조직을 지원하라.
  • 고객과의 미팅에 참여하여 장기적인 기술 로드맵을 명확하게 하고 자신의 회사가 업계를 선도한고 있다는 확신을 고객에게 심어주자.
  • 시장의 권위자와 이야기해 보고, 고객의 요구를 경청하고, 그 안의 문제를 빠르게 간파하자. 그리고 회사의 제품에 대해 좋은 조언을 제공하자.
  • 이것도 매우 중요하다.  자신이 모른다고 생각하면 질문에 대해 더 자세히 이야기할 수 있는 적합한 사람을 연결해 준다. 좋은 CTO는 그 자리에서 아는체하거나 약을 팔지 않는다.

D) 비즈니스 개발과 파트너십
  • 전략적 파트너 계약을 통해 비즈니스 개발팀을 지원하고 이들 조직의 동료들과 좋은 관계를 유지한다.
  • 파트너 기술 및 구매 목표에 대한 기술적 실사를 통해 해당 기술이 회사의 플랫폼, 제품 및 문화에 적합한지 확인한다.
  • CTO는 동일한 분야의 기술 스타트업들을 추적하고 그들의 가능성에 대해 수집한 정보를 기반으로 순위를 매겨 가지고 있어야 한다. CTO는 가능한 인수 목표, 그 회사가 놓치고 있는 전문성이 무엇인지, 어떤 회사가 부수적인 영역에서 최고의 성과를 내고 있는지? 경쟁자가 그 회사를 해칠 수 있는 것은 무엇인지? 어떤 회사가 최고의 기술팀을 보유하고 있는지? 나는 이 부분에 대해서 공동 창업자인 Jeff Hammerbacher에 많이 의지하고 있다.
  • 기술 커뮤니티에 회사를 대표하여 파트너와 함께 회사의 존재를 알려야 한다.
  • 새로운 파트너의 기술이 자사의 기술 로드맵에 중요한 영향을 줄 수 있는지를 예측해야 한다.(예를 들어, 새로운 유형의 스토리지나 컴퓨터 디바이스 등)
  • 시장의 끊임없는 변화로 인한 장기적인 경쟁 추세(우위)가 어떻게 되는지를 예측한다.

E) 마케팅
  • 이 회사의 기술에 대한 공인으로서의 역할을 한다.
  • 컨퍼런스, 강연회, 언론/미디어/애널리스트의 활동을 통해 회사의 비전과 기술의 방향을 전파하자.
  • 자신에게 중요한 업계의 애널리스트와 좋은 관계를 유지하자.
  • 회사의 제품을 중심으로 대규모의 활동이 왕성한 커뮤니티(예 : 밋업, 해커톤, 업계 컨퍼런스 등)를 구축하고 있는 마케팅 팀을 지원하자.
  • 트위터, 블로그 쓰기, 아티클이나 백서 등을 통해 소셜을 연계한 마케팅을 한다.

요약하면, 위대한 CTO는 한발 뺀 시선으로 숲 전체를 바라보고 고객과 직원 모두의 목소리를 낼 수 있어 기업에게 중요한 존재이다. 당신이 신입 CTO이든, 베테랑 CTO 든 나는 이 아티클이 도움이 되기를 바란다. 여기에서 소개하고 있는 의견에 동의하지 않거나 간과되고 있는 CTO의 중요한 역할이 있으면 언제든지 알려 달라. 건투를 빈다.


프로그래밍에서 인지 편향

Cognitive Biases in Programming

프로그래밍을 하면서 인간은 수많은 생각과 판단을 마주하게 되는데, 그에 못지 않은 인지 편향이 개발자들로 하여금 최선의 결정을 방해해서 결국 버그를 만들게 하고, 생산성을 저해하며 자신감을 떨어끄리기도 하고 일정을 지연시키기도 합니다. 개발자들이 알아야두면 도움이 될 것 같아, "Cognitive Biases in Programming"에 대한 아티클을 번역해 봅니다.

개발자로서써, 우리는 생산성을 방해하는 다양한 문제에 대해 잘 알고 있다. 하지만, 우리는 큰 관점에서 생각하는 것을 놓치는 경우가 종종 있다.

어떤 것은 인지하기 힘든 미세한 것일수도, 어떤건 큰 영향을 주는 것일수도, 여러분이 잘 처리 할 수 있는 것일수도, 잘 못할 수도 있는 것들이 존재한다. 이러한 모든 요소가 하나로 결합되어 일종의 내부 피드백 루프를 형성하여 생산성 저하, 버그 및 큰 좌절로 이어질 수 있다.

이들 한, 두가지의 영향을 최소화 할 수 있다면 그 주기(나쁜 사이클)를 깨고 나머지 것들을 무력화시킬수도 있다. 여기에서는 프로그래밍할 때 여러분들이 알아야 할 5가지 인지 편향에 대해 이야기해 본다.


과도한 가치 폄하(Hyperbolic Discounting)

나중의 더 큰 보수 대신에 지금 당장의 이익을 우선시 하는 것.
여러분중에 테스트 코드 작성을 연기 한 적이 있나요? Vim 사용중에, 화살표 키를 사용하여 커서를 이동시킨적이 있나요? 축하해요. 여러분은 과도한 가치 폄하를 보여준 것이에요. 당장의 이득에 눈이 멀어 화살표 키를 사용한다는 것은 올바른 구문을 찾기 위해서 정확한 라인으로 이동하는 과정에는 큰 고통(긴 시간)을 초래한다. 당장 익숙하지 않는 HJKL을 익힌다면 원하는 곳으로 빨리 갈 수 있어 미래의 이익은 훨씬 높아진다. 결과적으로, 당신은 많은 시간을 절약하게 된다.

이케아 효과 (IKEA Effect)

문제에 대한 자신의 해결책은 과대 평가하는 반면, 다른 솔루션을 과소 평가하는 것.
이케아 효과는 소비자가 직접 조립해서 만든 제품을 훨씬 고 평가(더 많은 가치를 줄 것이라는)하는 경향이 있는데, 이것 또한 인지 편향이다. 우리는 문제에 대한 자신의 해결책을 과대 평가하는 경향이 있고, 반면에 다른 해결책은 과소 평가하는 경향이 있다. 만약, 당신이 멋지고 독창적인 도구가 아닌 그저 그런 사내 도구를 사용하여 회사에 일한 적이 있다면, 내가 무슨 말을 하고 싶은 것인지 알 것이다.

어설픈 최적화 (Premature Optimization)

필요한 것을 이해하기도 전에 최적화하는 것.
자명하다. 엔진을 고치지 않고 낡은 자동차를 빨리 달라게 하는데에 공기 역학적 스포일러 날개를 추가하는 것은 전혀 도움이 되지 않는다. 가장 좋은 예로 실험에 목표를 둔 코드에 성능적으로 완벽한 코드를 작성하는 것이다.

계획 오류 (Planning Fallacy)

작업을 완료하는 데 필요한 시간을 낙관적으로 예상하는 것
계획 오류(Planning Fallacy)는 대부분의 사람에 관련된 이야기다. 그것이 우리든, 프로젝트 매니저든, 제품을 사용하는 고객이든 실제로 언제 작업이 끝날지에 대해 낙관적인 경향이 있다. 이것은 아래 격언이 잘 설명해 준다 : 코드의 첫 90%가 개발 시간의 첫 90%를 차지하고, 코드의 나머지 10%가 또 다른 90%의 개발 시간을 차지한다. 즉, 총 180%를 소요하게 되는 의미로, 당초 예상한 기간보다 훨씬 초과하는 경향을 표현한 것이다. (90 대 90의 법칙)

최신 편향(Recency Bias)

과거에 일어난 일보다 최근의 사건에 높은 가치를 두는 것. 최신 경험을 더 가치있다고 생각하는 것.
최신 편향(Recency Bias)은 문제를 해결해야 할 때 자주 마주치곤 한다. 우리는 비슷한 문제를 해결했기 때문에 그 해결책을 사용하자. 명심하자. 동일한 디자인 패턴을 반복해서 사용하는 자신을 발견하지 않았냐? 그렇다면, 당신은 같은 시각으로 다른 문제를 들여다보고 있는지도 모른다. 우리는 바이어스(편견)를 완전히 제거할 수는 없다. 그러나, 편견이 우리에게 어떻게 영향을 미치고 있는지를 앎으로써 그것이 야기하는 문제를 완화시킬 수는 있다.

프로그래머의 악몽이란?

Quora에서 재밌는 글을 발견해서 여기에 남깁니다. 심심힐 때 한번 보고 리프레쉬 하세요. ^^
  • 당신의 코드나 혹은 버그가 우발적으로 누군가를 죽이거나 해할 수 있다.
  • Internet Explorer(웹 개발자의 경우)
  • 요구사항이 다시 변경되었다.(잦은 요구 사항 변경)
  • GitHub의 병합이 충돌한다.
  • 의도한 것과 다른 디렉토리를 rm -rf *를 입력해 버렸다.
  • Stack Overflow 다운!
  • Stack Overflow에서 자신이 답을 찾고 싶은 것과 똑같은 질문을 발견했는데, 1년 전에 질문인데도 여전히 대답이 쓰여져 있지 않다.(xkcd : Wisdom of the Ancients)
  • Stack Overflow에서 할 수 있는 질문 개수의 한계에 다달았다.
  • 실제 제품에 버그가 발생했는데 로컬에서 재현이나 발생하지 않는다.
  • 버그 발생 확률은 낮지만, 무시할 수준은 아니다.
  • 버그의 원인이 고부하시에만 일어나는 경쟁 조건(race condition)에 해당된다.
  • 버그의 원인을 모른다.
  • 버그의 원인이 되는 코드를 쓴 것은 내가 아니지만, 수정해야하는 책임은 있다. 코드를 작성한 사람들은 더 이상 회사에 없다.
  • 버그를 야기한 이슈가 99% 신뢰하는 라이브러리에서 확인하게 된다. 시간적으로 마지막에 본다는 점이다.
  • 하드웨어 버그인데, 모든 사람들이 소프트웨어 버그라고 지목한다.
  • 다수의 사람들이 디버깅하려고, 몇년에 걸쳐 노력했지만, 아무도 성공하지 않았다.
  • 버그가 논리적 오류인데, 상당히 오랜시간 실행되고 나서야 재현된다.
  • 디버깅에 자신의 아무것도 모르는 분야에서의 경험이 필요하다.
  • 버그 수정 일정에 여유가 없다.
  • 밥줄이 걸려있어, 버그를 무시할 수 없다.
  • 세미콜론 키가 손상되어서 작동되지 않는다.
  • 훌륭하게 잘하고 있는 프로젝트에 코멘트가 없다는 것을 1년 후에 발견하고 수정하면서 외친다. "나는 도대체 왜 이런 일 했어?" "정말이 내가 짠 코드인가?" 자신의 집에서 미아가 된 기분이다.
  • 문서가 없는 라이브러리.
  • ==대신 =를 사용했다.
  • 자만. 준비 부족. 싸구려 견적. 공감이 아니라 분노.
  • 내 코드가 동작은 하지만, 이유를 모른다.
  • 커뮤니케이션 부족 : 프로그래머는 자신이 만드는 것에 대해, 그것이 어떻게 사용되는지를 이해해야 한다. 상황을 정립할 필요가 있다. 이유는 무엇을 만드는 데에는 100가지 이상의 결정이 존재하게 된다. 컨텍스트를 이해하고 있으면 판단이 내릴 수 있다.
  • 오버 커뮤니케이션 : 회의, 회의, 회의.. 회의는 프로그래머를 죽여버린다.
  • 뭔가를 명확히하는데 하루가 걸린다. 그런데 "클라이언트"가 다른 타임존에 있다.
  • 문서가 없는것보다 더 나쁜것은 쓸모없는 문서이다.
  • 들여 쓰기가 엉망이고, 불합리한 구조 때문에 디버깅을 하 수 없다.
  • 보스가 옛날 버전의 프로그램을 테스트하고 있다.