Wednesday, December 14, 2016

흐트러진 마음을 잡아 보고자 써보는 '애자일 소프트웨어 개발 선언'


애자일 소프트웨어 개발 선언 

우리는 소프트웨어를 개발하고, 또 다른 사람의 개발을 도와주면서 
소프트웨어 개발의 더 나은 방법들을 찾아가고 있다. 
이 작업을 통해 우리는 다음을 가치있게 여기게 되었다:

공정과 도구보다 개인과 상호작용을
포괄적인 문서보다 작동하는 소프트웨어를 
계약 협상보다 고객과의 협력을
계획을 따르기보다 변화에 대응하기를

가치있게 여긴다. 이말은, 왼쪽에 있는것들고 가치가 있지만, 
우리는 오른쪽에 있는것들에 더 높은 가치를 둔다는 것이다.

애자일 선언 이면의 원칙

우리는 다음 원칙을 따른다:

우리의 최우선 순위는, 가치 있는 소프트웨어를
일찍 그리고 지속적으로 고객을 만족시키는 것이다.

비록 개발의 후반부일지라도 요구사항 변경을 환영하라.
애자일 프로세스들은 변화를 활용해 고객의 경쟁력에 도움이 되게한다.

작동하는 소프트웨어를 자주 전달하라. 두어 주에서 
두어 개월의 간격으로 하되 더 짧은 기간을 선호하라.

비니지스 쪽의 사람들과 개발자들을 프로젝트 전체에
걸쳐 날마다 함께 일해야 한다.

동기가 부여된 개인들 중심으로 프로젝트를 구성하라.
그들이 필요로하는 환경과 지원을 주고
그들이 일을 끝내리라고 신뢰하라.

개발팀으로, 또 개발팀 내부에서 정보를 전하는 가장
효율적이고 효과적인 방법은 면대면 대화이다.

작동하는 소프트웨어가 진척의 주된 척도이다.

애자일 프로세스들은 지속 가능한 개발을 장려한다.
스폰서, 개발자, 사용자는 일정한 속도를 계속 유지 
할수 있어야 한다.

기술적 탁월성고 좋은 설계에 대한
지속적 관심이 기민함을 높인다.

단순성이 -- 안하는 일의 양을 최대화하는 기술이 -- 필수 적이다.

최고의 아키텍처, 요구사항, 설계는 
자기 조직적인 팀에서 창발한다.

팀은 정기적으로 어떻게 더 효과적이 될지
숙고하고, 이에 따라 팀의 행동을 조율하고 조정한다.

원문 : http://agilemanifesto.org/iso/ko/manifesto.html

* 현실에 안주하며, 퇴보적인 내 모습에 반성하며, 굳건히 믿는 애자일 선언문을 다시 써본다. 
언제나 그렇듯 가장 빨리, 최고의 제품을 개발하는것이 1번 순위이다.
모든것에 대해 최선인지 의심하고 하루하루 발전해나가야한다.


Tuesday, December 13, 2016

[Cracking the Coding Interview] Chap.9 System Design and Scalability.

Scalability 질문은 당신이 실제로 어떻게 일하는지를 알기위해 던져진다.
여기에 어떤 함정도, 특별한 알고리즘이 필요 한것이 아니고.
당신이 이런 문제들을 어떻게 해결하는지 보기 위함이다.

질문을 하고, 면접관과 함께 토론해야 한다.

이런 질문에 답하는 방법

이야기를 하라.
넓게 생각하라, 특정 알고리즘 혹은 특정 부분에 몰두 하지 말고, 큰 그림을 봐야 한다.
화이트 보드를 적극 활용하고, 면접관이 우려하는 점에 대해 명확히 이해하라.
조심스럽게 가정하고, 그런 가정들에 대해서 면접관에게 전파해라.
평가는 필요 할때만해라.

Design  : Step-by-Step

혼자서 문제를 풀려고 하지말고, 대화를 하면서 문제를 풀어 나가야 한다.

Step 1 : 문제의 범위 산정하기.

면접자가 요청한 문제가 내가 이해한것과 같은지 확인 해야 한다.

예 : TinyUrl

permenant 한지, UserAccount 시스팀이 필요 한지,  클릭횟수 추적이 필요 한지 등을 물어 봐야 한다. 질문이 끝나고 나면 아래와 같이 리스트업한다.

* Shortening a URL to a TinyUrl
* Analytics for a URL
* Retrieving the URL associated with a TinyURL.
* User accounts and link management.

Step 2 : Make Reasonable Assumptions

어떠한 가정을 하는건 좋지만, 항상 정당한 이유가 있어야한다.
무한한 메모리를 사용한다고 하거나, 하루에 100명만 사용한다고 하거나 하면 안된다.

Step 3 : Draw the major components

Frontend 서버나, Data Store 서버 등의 중요 컴포넌트들을 그리면 된다.
이 단계에 대해서 Scalability 같은거는 접어 두고 가능한한 간단한척 하고 그리는게 좋다.

Step 4 : Identify the Key Issues

기본적인 디자인이 완료 되면, 어떤 문제가 있을지 생각해두는게 좋다.
병목지점이라던지, 이 시스템을 만드는데 있어서 가장 큰 도전이 될만한 것들에 대해서 생각해두도록 하자.

예를들면 TinyURL은 보통 불규칙적인 접근 패턴을 가지는데, 커뮤니티등에 공유되어서 갑자기 엄청 많이 접근 될수도 있다.

Step 5: Redesign the the Key Issues.

정리한 이슈들을 해결할수 있도록 Diagram을 업데이트.
이때 당신이 디자인한 결과의 한계에 대해서 오픈마인드를 가져야한다. 면접과는 이를 인지할테고 그와 적극적으로 소통해야 한다.

Algorithms that Scale : Step-By-Step

보통은 전체 시스템을 재디자인하라고 하지 않고, 특정 기능이나 알고리즘에 대해서 다시 작성할것을 요구 받는다. 이때 명심해야 할점을 Scalability 를 고려해야한다는 점이다.

Step 1 : Ask Questions

앞서 말한것처럼 요구 사항을 제대로 인지 해야한다.

Step 2 : Make Believe

일단 무한한 메모리를 가진 머신을 통해서 처리할수 있다고 생각하고 답을 작성하라.

Step 3 : Get Real

다시 원래 문제로 돌아가서, 얼마나 많은 데이터를 한번에 처리 할수 있으며, 데이터를 쪼개었을때 나타나는 문제에 대해서 생각해봐야 한다.  보통은 한 머신이 데이터를 볼때 다른 데이터를 어떻게 찾을수 있는지가 문제이다.

Step 4 : Solve Problems

이제 발생할수 있는 문제들에 해결 방안을 제시 해야 한다. 문제가 해결 되었을 수도 있고, 아닐수도 있다. 문제가 해결 되지 않았다거나, 다른 문제가 발생하면 그문제들에 대해서 깊게 고민해야 한다. 지금 당장 완벽한 시스템을 만드는게 중요한것이 아니라, 문제를 찾아내고, 수정하고 하는 과정이 중요한것이다.

Key Concepts

Horizontal vs Vertical Scaling

* Vertical Scaling 은 특정한 node 에 대한 성능향상, 예를 들어서 메모리를 늘리는 등.

* Horizontal Scaling 은 node 수를 늘리는 방식이다. 예를 들면 서버 수를 늘려서 부하를 분산시키는것이다.

Load Balancer 

뭐 말이 필요 없는듯. 말 그대로

Database Denormalization and NoSQL

시스템이 커질수록 RDBMS 에서의 JOIN 연산이 병목지점이 된다.
Denormalization(역정규화)를 하면 이런 문제를 피할수 있다.

NoSQL을 이용해서 처리할수도 있는데, NoSQL은 관계형 DBMS가 아니기 때문에 JOIN 연산이 없고, scale하기 좋게 디자인 되어 있다.

Database Partitioning(Sharding)

DB Sharding은 데이터를 쪼개는 기능이다.

* Vertical Partitioning

가장 기초적인 샤딩 기법으로, 테이블별로 DB를 나누는 것이다.
Vertical Partitioning의 단점은 분리된 DB일지라고 그 DB가 매우 커지면 또다시 다른 방법으로 Partitioning을 해야 한다는것이다.

* Key-Base(or Hash Based) Partitioning

Key 단위로 Table을 분리하는 기법. 문제는 서버를 추가할때 마다, Data Relocating 작업을 해야 하는데, 무지하게 비싼 작업이다.

* Directory-Base Partitioning

내가 찾는 데이터가 어디 잇는지, lookup table 을 통해 관리하는것이다. 서버 추가등의 이슈에 비교적 쉽게 대응할수 있지만, 두가지 결점이 있다.
첫번째로, lookup table에 문제가 생기면, 전체 장애로 이어진다.
두번째로 lookup table에 부하가 많이 걸린다.

대부분의 architecture들을 이런 기법들을 조합해서 사용한다.

Caching

생략.

Asynchronous Processing & Queues

시간이 오래 걸리는 작업을 동기적으로 처리하면, 응답시간이 늦어 지기 때문에,  비동기로 처리해야 한다.

때때로, pre-process  하는 형식으로 처리할수도 있다. 예를 들면 포럼에서 인기 잇는 포스트와 댓글 수 등을 미리 처리 하는것이다. 아주 약간 실데이터와 차이가 있을수 있지만 대부분 용납할수준일것이다. 실시간으로 데이터를 로딩해, 느린 응답시간을 가지는 것보다 훨씬 좋은 선택이다.

또 다른 때에는, 사용자에게 기다리게하고, 작업이 완료된것을 알려줘야 할수도 있다.

Networking Metrics

* Bandwidth

한번에 보낼수 있는 최대 데이터양을 뜻한다. bps,gps 등으로 표기된다.

* Throughput

Bandwith가 한번에 보낼수 있는 최대 데이터양을 뜻하는 반면에 Throughput은 실제로 전송된 데이터의 크기를 뜻한다.

* Latency

end-to-end 과점에서 데이터가 전송되는데 걸리는 시간을 의미한다.

MapReduce

대용량 데이터를 처리하기 위한 기술.

이름에 나와 있듯이 Map 과 Reduce 로 구분된다.

* Map은 데이터를 K,V 쌍으로 나누고.
* Reduce 는 특정 K를 가지고 여러방식으로 "reduce"한후 에 새로우 K,V를 만들어 낸다. 때때로 Reduce는 추가적인 Reduce를 위해 재귀적으로 사용될수 있다.

MapReduce 는 대용량 데이터를 Scalable 하게 병렬처리하는 기법이다.

Considerations

시스템 디자인을 할때는 Key Concepts 와 별개로 몇개의 이슈에 대해 생각해봐야 한다.

* Failure

필연적으로 시스템 구성요소 오류가 나기 마련이고, 우리는 다수 혹은 모든 종류의 요소들의 오류에 대한 계획이 있어야 한다.

* Availability and Reliability

Availability 는 시스템 가용시간을 퍼센트 형태로 표기한것이다.
Reliability는 특정시간단위에 대해 시스템이 가용될수 있는 확률을 표기하는것이다.

* Read-heavy vs Write-heavy

읽기, 혹은 쓰기 편향적인 시스템이라면 디자인할때 고려해야 한다.
쓰기 편향이면, write-task를 queuing 하는 식으로 디자인할수 있다. ( 이때 쓰기 실패 일때는 대비해야 한다. ) 읽기 편향이면 cache를 꼭 넣어야 한다.

* Security

우리가 설계한 디자인에서 마주칠 Security 이슈에 대해 생각해봐야 한다.

이런 점들의 장단점에 대해 꼭 면접관과 공유 해야함을 기억해야한다.

There is no "perfect" system

어떤 시스템을 설계하든 한번에 완벽한 시스템 디자인은 없다. 언제나 장단점이 있기 마련이다. 두사람이 같은 시스템을 완전히 다르지만 훌륭하게 디자인할수 있다.

이런 질문들에 대한 우리의 목표는, 요구사항을 이해하고, 문제를 구조화 한다음, 합리적인 가정을 토대로 시스템을 디자인하는것이며, 우리가 디자인한 시스템의 약점에 대해 열린 마음으로 대하는것이다.

Wednesday, December 7, 2016

해외에서 살아보기 #1

언제부터였을까, 개발자로서의 삶이 무료하게만 느껴집니다.
도전과 모험이 넘치는 스타트업 회사들에 몸담은 지도 5년 남짓한 시간.
더는 도전과 모험들은 새롭지 않고, 개발자로서의 역량은 나아가지 않고 뒷걸음만 치고 있는듯한 기분이 듭니다.
이러한 권태를 극복해보고자 해외에서 살아보고자 합니다.

첫 회사에 다닐 때, 운이 좋게도 유럽에서 약 6개월간의 파견근무를 한 적이 있습니다.
그때 보았던 해외 인력들이 작성했던 코드들은 창의적이면서도 구조적으로도 손색이 없었던 기억이 납니다. 코드를 작성하는 일은 논리적이기도 하고 기계적이기도 하지만 개인적으로는 미적인 영역에도 속한다고 생각합니다. 저 역시 그러하지만, 논리적인 코드를 작성하는 데는 익숙하지만 아름다운 코드를 작성해내지는 못하고 있습니다. 물론, 사람에 따라 다르겠지만 새로운 환경에서 다른 문화권의 사람들과 함께 일하는 것이 저에게 많은 자극을 줄 것이라 기대해봅니다.

저는 세상에서 가장 아름다운 여성의 남편이자, 4살 여자아이의 아빠입니다.
다행히, 아내 역시 해외에 살아 보고 싶은 로망이 항상 있었기에 적극적으로 찬성하며 좋아하지만, 해외 거주시 추가로 부담해야 할 렌트비, 육아 비용, 거기에 줄어들 소득을 생각하니 아마득하긴합니다. 아직 대소변도 제대로 가릴지 모르는 아이가 낯선 곳에서 말도 통하지 않는 친구와 선생님을 만나 잘 적응 할 수 있을지도 무척 걱정됩니다.

모든 도전이 그러하듯이 기대와 희망, 그리고 걱정과 불안이 동반합니다. 일단 실행하고 걱정과 불안은 그때그때 슬기롭게 해결할 수 있길 바라며, "해외에서 살아보기" 프로젝트를 시작해 봅니다.


Sunday, November 20, 2016

Lookup expressjs #2 lib/application.js

* application.js



var compileETag = require('./utils').compileETag;

var slice = Array.prototype.slice;


이처럼 function들을 즉각적으로 사용하지 않고,  variable로 사용하는 컨벤션이 있는데,
딱히 좋은지는 모르겠습니다.

compleEtag function 은 그렇다 치더라도,
slice 의 경우 코드에서 Array.prototype.slice 인지, user defined 된 slice 라는걸 알기 어렵다는 단점이 우선 눈에 띕니다..

좋은 점은 반복적으로 사용되면, 코드를 줄이는 효과가 있을것 같기는 한데,
slice 와 같이 well-known function 에 대해서 user define 하지 않는다는 규칙이 Team 에 존재한다면 괜찮을수 있다 생각됩니다.

그렇지만, new member가 숙지해야 될 규칙이 하나더 생기기에 역시 그렇게 좋은 효과로 보기 어렵습니다.


application.js 는 expressjs application 설정등 초기화하고, application life cycle 로 판단되는데, 일단 살펴 봐야 겠습니다.


app.defaultConfiguration = function defaultConfiguration() {

  var env = process.env.NODE_ENV || 'development';



.....



  debug('booting in %s mode', env);



  this.on('mount', function onmount(parent) {

    // inherit trust proxy

    if (this.settings[trustProxyDefaultSymbol] === true

      && typeof parent.settings['trust proxy fn'] === 'function') {

      delete this.settings['trust proxy'];

      delete this.settings['trust proxy fn'];

    }



    // inherit protos

    this.request.__proto__ = parent.request;

    this.response.__proto__ = parent.response;

    this.engines.__proto__ = parent.engines;

    this.settings.__proto__ = parent.settings;

  });



 ....



  if (env === 'production') {

    this.enable('view cache');

  }



  ...

};


init() -> defaultConfiguration() 을 호출 하는데, 보시는 바와 같이 NODE_ENV 등의 각종 설정들이 초기화 됩니다.
한가지, production 모드 일때 view cache 가 활성화되는걸 볼수 있네요.
이점은 좀 유의 해야 할 필요가 있는것 같습니다.


Look up expressjs #1 ./lib/express.js

개발자로써의 삶을 나태하게 보내는것에 대한 반성으로, 원래 좋아하던 오픈소스 뜯어 보기를 다시한번 하고자 합니다.

지금 주력으로 개발하고 있는 것은 nodejs + expressjs 입니다.
nodejs 는  원래 먼저 보고자 했는데, cpp 로 작성된 프로젝트 이기도 하고, nodejs 코딩 컨벤션에 대해서 좀더 알고 싶은 욕구를 해소하기 위해 expressjs 로 선정했습니다.

* expressjs ( http://express.js.com )

사이트 소개에 따르면 expressjs 는 경량하고도 유여한 Web Application Framework 입니다.
대부분의 nodejs로 구춘된 web application 은 expressjs 를 사용하고 있으며, 저도 현재 프로젝트에 사용하고 있습니다.

원래 이런 framework 선정 전에 먼저 살펴보고, 대안을 찾아 보고 장단점을 비교해봐야 하는데 귀찮아서 바빠서 이과정을 생략하고 도입했습니다. 참 바보 같은 삶을 살고 있습니다.

지금이라도 늦기전에 살펴보도록 하겠습니다.

* index.js , lib/express.js

두파일이 expressjs lookup journey 의 입구 입니다.
예측해보건데, index.js 는 껍데기 이고, lib/express.js 는 좀더 디테일 한 껍데기 일것입니다.

네 index.js 는 lib/express.js를 require하여 export 하는 역할만 하니, skip
lib/express.js 를 살펴보겠습니다.

이 파일도 역시 application.js router.js response.js request.js 를 require 하는 껍데기에 가까운데, application object 생성하는 createApplication() function 이 정의 되어있고, 일부 deprecated method 에 대한 warning 을 담당하고 있습니다.


function createApplication() {

  var app = function(req, res, next) {

    app.handle(req, res, next);

  };



  mixin(app, EventEmitter.prototype, false);

  mixin(app, proto, false);



  app.request = { __proto__: req, app: app };

  app.response = { __proto__: res, app: app };

  app.init();

  return app;

}


음.. 모르는게 한두가지 보이네요
mixin 과 __proto__

* mixin

https://en.wikipedia.org/wiki/Mixin

mixin 은 다른 class 의 method 를 상속하지(inherit) 하지 않고, 가져오는 (include) 개념이다.
언어에 따라 include 라고 하기도 한다고 하니, 그정도로 이해할수 있겠다.

nodejs application object가 EventEmitter와 proto를 mix 인해서 사용하는것으로 보면 될테고,
app 내에서, request, response module을 접근하고자 app.request, app.requese할 assign 하는 개념으로 보면 될것이다.

음.. 여기서 proto 는 또 뭐지 ㅎ


var proto = require('./application');


proto 는 application module 임으로, application은  EventEmitter, Application 모듈이 mixin 되었다고 보면 되겠다.


* __proto__

http://i5on9i.blogspot.kr/2014/09/proto-prototype.html

Tuesday, September 27, 2016

User Interface Testing - Testing with Xcode


Concepts and APIs

UI Testing 은 unit testing과 근본적으로 다르다.
Unit Testing은 app 스코프안에서 앱의 변수, 상태 등등을 전체적으로 접근 하며, function이나 method를 테스트 한다.
반면에 UI Testing은 User Level에서 테스트함으로 앱안의 함수등을 접근 할수 없다.
즉 User 관점에서 테스트 하는것이다.

APIs

- XCUIApplication
- XCUIElement
- XCUIElementQuery

** 가이드 문서에 별다른 내용이 없다 ㅠ
이건 가이드라기 보다 introduction에 가까운거 아닌가, 맨땅에 헤딩좀 해야 겠다.

Writing Tests of Asynchronous Operations- Testing with Xcode


XCTest에서 expectation 을 통해 Async Op.을 테스트 할수 있다.
XCTestCase+AsynchronousTesting.h 참조.


// Test that the document is opened. Because opening is asynchronous,

// use XCTestCase's asynchronous APIs to wait until the document has

// finished opening.

- (void)testDocumentOpening

{

    // Create an expectation object.

    // This test only has one, but it's possible to wait on multiple expectations.

    XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:@"document open"];



    NSURL *URL = [[NSBundle bundleForClass:[self class]]

                              URLForResource:@"TestDocument" withExtension:@"mydoc"];

    UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];

    [doc openWithCompletionHandler:^(BOOL success) {

        XCTAssert(success);

        // Possibly assert other things here about the document after it has opened...



        // Fulfill the expectation-this will cause -waitForExpectation

        // to invoke its completion handler and then return.

        [documentOpenExpectation fulfill];

    }];



    // The test will pause here, running the run loop, until the timeout is hit

    // or all expectations are fulfilled.

    [self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {

        [doc closeWithCompletionHandler:nil];

    }];

}