Home

조직력의 발전방향은?

우리는 회의에서 논의를 통해 의견을 나눌수록 극단을 지양하고 합리적 중용의 결과를 찾으려는 것이 목표일 것이다. 하지만, 특히 한국에서 회의 구성원들의 지위가, 유명세가 발언의 양, 방향을 암묵적으로 지휘하는 경우가 많다.

지위에 의해 의식이 서열화 되고 발언양이 많아짐으로 논의의 목적이 합리적인 중용을 표방하는데, 이를 무시하고 지위자의 발언 방향으로 귀결되는 경우가 많다. 이는 결국 회의 주도자의 생각과 열정을 뭉개버리고 무념의 팔로워로 전락하게 만들 수 있다.

그리고 유명세가 있는 사람의 발언은 논점이 아닌 전반적인 부분에서 그 사람의 지식을 근거없이 과대 평가를 해버려 그에게 근거없는 자신감을 불어 넣어줘 버리고, 중도보다는 확신과 고집으로 인해 역시 잘못된 논의를 만들어 버리는 경우도 있다.

  • 그렇다면 비전문가, 평직원들의 생각 속에서 더 좋은 아이디어나 논리적인 사고가 발현할 확률은 어떻게 되는 것일까?
  • 집단지성을 성과로 이어지기엔 시간이 허락하지 않는게 현실인데, 그럼 대안은?
  • 맨투맨 의사소통은 언제까지 해야할까?
  • 그럼 다양성, 자율성은 어디까지 보장해 주어야 하는 것일까?

Angularjs 1.2에서 1.3 업그레이드

본 포스트는 Angularjs 1.2에서 1.3으로 업그레이드를 하실분들에게 도움을 주고자 Migrating from 1.2 to 1.3" 문서를 정리한 것입니다. 아직 1.3에 추가적인 버그가 나와서 패치가 되고 있지만 성능부분이 더 좋아졌다고 하니 1.3이 안정화되면 업그레이드 하시길 권고해 드립니다. ^^

1.3 특징

1. 성능
- DOM 조작이나 digest 등 많은 처리에서 3 ~ 4배 빨라졌다.

2. 주목할 기능
- One-time bindings - 표현식 앞에 "::"를 붙여서 interpolate(값할당)가 한번 일어나며, 그 이후에는 감시(watch)되지 않는다.
- ngAria - 기본적으로 (접근성 관련) 액세스 가능한 사용자 지정 구성 요소를 구현하기 위한 새로운 모듈.
- ngMessages - 폼 검증에 대한 피드백(결과)을 쉽게 표시하기 위한 메세지 지시자.
- ngModelOptions - 바인딩된 모델(model binding)의 동작을 사용자 정의가 가능한 지시자. 예로는 아래와 같다.
  • debounce는 모델 업데이트 타이밍 제어가 가능.
  • getter-setter 형식의 모델 바인딩 가능.
  • update-on-blur는 blur시 모델 업데이트.
- Strict DI - 단순화한 DI 구문을 사용해 minify할 수 없는 코드를 찾을 수 있는 옵션(Implicit injection 문법 사용시 오류를 발생시킴).
- Material Design 지원.

마이그레이션 가이드

1. $parse
- prevent invocation of Function's bind, call and apply
angular 표현식에서는 function의 .bind .call .apply를 호출할 수 없게 된다. 기존 function의 행동을 예측할 수 없는 형태로 변경되지 않게 하기 위해서이다.

- forbid __proto__ properties in angular expressions
angular 표현식에서는 proto 속성이 동작하지 않는다.

- forbid __{define,lookup}{Getter,Setter}__ properties
angular 표현식에서는 {define, lookup} {Getter, Setter}를 사용할 수 없다. 만약에 몇몇 사유에 의해 필요한 경우, 위험 요소를 즐이기 위해 wrap/bind를 scope 객체를 통해 사용해라.

- forbid referencing Object in angular expressions
angular 표현식에서는 Object를 사용할 수 없다. Object.keys가 필요한 경우는 scope를 통해 접근할 수 있게 해라.

2. Angular.copy
- preserve prototype chain when copying objects
원본 Object의 prototype을 카피 Object에 적용하기 위해 angular.copy가 변경되었다. 이전에는 원본 Object의 프로토타입 체인 속성을 직접 복사했다.
카피된 객체의 hasOwnProperty 속성만 iterate하면 prototype의 속성은 포함되지 않는다. 이것은 좀 더 합리적인 행동이라고 생각하고 실제 어플리케이션이 행동에 의존하는 경우는 드물다.
만약, 애플리케이션이 행동에 의존하는 경우는 객체(상속 속성 포함)의 모든 속성을 hasOwnProperty로 필터링하지 않도록 iterate하라.
이 변경 사항은 IE8에서 동작하지 않는 기능을 사용할 수도 있다는 것에 주의하라. 만약 IE8에서 동작시키고 싶은 경우는 Object.create와 Object.getPrototypeOf의 polyfill을 사용해라.

3. core
- drop the toBoolean function 'f', '0', 'false', 'no', 'n', '[]'는 더이상 falsy로 간주되지 않고, JavaScript의 falsy값인 false, null, undefined, NaN, 0, ""만 parser에 의해 falsy로 처리된다.

4. $compile
- always error if two directives add isolate-scope and n…
하나의 요소에 isolate scope와 다른 scope를 요청하면 오류가 발생된다. 변경 전에는 isolate가 아닌 scope의 directive 다음에 isolate scope의 directive 순으로 컴파일러가 적용할 경우에는 두개의 directive가 child scope와 isolate scope를 요청하는 것이 가능했다. 지금은 순서에 관계없이 컴파일러 오류를 발생시킨다.
만약 당신의 코드가 지금 $compile:multidir 오류를 던진다면 같은 Element에 여러 directive가 isolate와 isolate 아닌 scope를 요청하지 않았는지 체크하고 코드를 고쳐라.

5. NgModel
- ensure pattern and ngPattern use the same validator
ng-pattern(ng-pattern="exp") 또는 pattern 속성(pattern="{{exp}}")을 angular 표현식으로 사용하고 있다면, 그리고 표현식이 문자열로 평가되는 경우, validator는 정규 표현식 객체 리터럴(/abc/i)로 문자열을 분석하지 않고 전체 문자열을 정규 표현식으로 되어 버린다. 즉, 이 의미는 플래그가 RegExp 객체로 처리되지 않는다. 이 문제를 해결하기 위해 정규식 객체를 angular 표현식의 값으로 사용해라.
//before
$scope.exp = '/abc/i';

//after
$scope.exp = /abc/i;

6. Scope
-
change Scope#id to be a simple number
Scope#$id는 문자열이 아니라 숫자로 되어 있다. 이 id는 주로 디버깅 목적으로 이용되고 있어서, 다른 곳에는 영향을 주지 않을 거라고 생각하고 있다.

7. forEach
- cache array length
forEach는 배열의 초기의 아이템 수만 iterate할 수 있고, iteration 도중에 배열에 추가된 항목은 forEach의 대상이 되지 않는다.
이 변경으로 인해 forEach가 Array#forEach와 동작이 비슷하게 되었다.

8. jqLit​​e
- data should store data only on Element and Document nodes
text/comment 노드에 jqLit​​e의 데이터를 넣을 수 있었는데, jQuery처럼 Element와 Document 노드에만 가능하게 되었다.

9. $resource
- allow props beginning with $ to be used on resources
$resource가 속성을 삭제하는 행동을 기대하는 경우 수동으로 직접 수행해야 한다.

10. angular.toJson
- only strip properties beginning with $$, not $
toJson이 속성을 삭제하는 행동을 기대하면 수동으로 직접 수행해야 한다.


11. $compile
- deprecate `replace` directives
Element를 replace하는 directive를 정의하기 위한 replace 플래그는 다음 Angular의 주요 버전에서는 삭제된다. 이 기능은 성가신 문제(속성을 어떻게 병합하는지 등)가 있으며,이 기능을 해결하는 것보다 더 많은 문제를 야기하고 있다. 또한, Web Components는 DOM에 사용자 지정 요소가 존재하는 것이 일반화되었다.

12. $parse
- remove deprecated promise unwrapping
promise unwrapping 기능은 제거되었다. 이것은 1.2.0-rc.3에서 이미 depreciated되어 있다. 아래 두 함수는 제거 되었다.
  • $parseProvider.unwrapPromises
  • $parseProvider.logPromiseWarnings

13. Scope
- $broadcast and $emit should set event.currentScope to null
$broadcast와 $emit 이벤트의 전파(propagation)를 종료한 시점에서 이벤트 currentScope 속성을 null로 재설정하게 된다. currentScope 속성에 비동기적으로 액세스하는 코드는 targetScope를 사용하도록 마이그레이션 하라.

14. jqLit​​e
- stop patching individual jQuery methods
jQuery의 detach() 메서드는 $destroy 이벤트를 트리거하지 않는다. Element에 붙인 Angular 데이터를 삭제하려면 remove()를 사용하라.

15. $http
- remove deprecated responseInterceptors functionality
지금까지는 response interceptor를 다음과 같이 등록할 수 있었다.
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, 
  dependency2) {
  return function(promise) {
    return promise.then(function(response) {
      // do something on success
      return response;
    }, function(response) {
      // do something on error
      if (canRecover(response)) {
        return responseOrNewPromise
      }
      return $q.reject(response);
    });
  }
});

$httpProvider.responseInterceptors.push('myHttpInterceptor');

v1.1.4(4ae46814)에서 도입된 API는 다음과 같다.
$provide.factory('myHttpInterceptor', function($q) {
return {
  response: function(response) {
    // do something on success
    return response;
  },
  responseError: function(response) {
    // do something on error
    if (canRecover(response)) {
      return responseOrNewPromise
    }
    return $q.reject(response);
  }
};
});

$httpProvider.interceptors.push('myHttpInterceptor');

이 API의 자세한 내용은 interceptors에서 확인할 수 있다.

16. injector
- invoke config blocks for module after all providers
이전에는 config 블록이 provider 등록 전에 불려졌기 때문에 동작을 제어하기가 가능했지만, 지금은 항상 config 앞에 provider를 등록되게 되어져 있어서 동작을 제어 할 수 없게 되었다.

예:
이전에는 다음과 같은 코드가 작동하고 있었다.
angular.module('foo', [])
.provider('$rootProvider', function() {
  this.$get = function() { ... }
})
.config(function($rootProvider) {
  $rootProvider.dependentMode = "B";
})
.provider('$dependentProvider', function($rootProvider) {
   if ($rootProvider.dependentMode === "A") {
     this.$get = function() {
      // Special mode!
     }
   } else {
     this.$get = function() {
       // something else
     }
  }
});

$rootProvider와 $dependentProvider 등록 사이에 config 블록이 있어서 응용 프로그램의 동작을 변경할 수 있었지만, 이제는 이러한 방법이 싱글모듈 내에 있어서 더이상 실현될 수 없다.

17. ngModelOptions
- move debounce and updateOn logic into NgMod…
이 커밋은 NgModelController의 API를 변경하고 있다.

$setViewValue(value) - 이 메소드는 $viewValue를 변경하지만, 이전처럼 $modelValue가 변경되면 즉시 커밋하지 않는다. 지금은 관련된 ngModelOptions directive에서 지정된 트리거의 발생에 의해 커밋되도록 되었다. ngModelOptions에 debounce 지연이라는 트리거가 지정되어 있는 경우에는 변경의 커밋은 더 연기(debounce)된다. 대부분의 경우 NgModelController 사용 방법에 큰 영량을 미치지는 않는다. updateOn이 디폴트로 포함되어 있어 $setViewValue는 트리거에 의해 즉시 커밋될(잠재적 연기 가능) 것이다. $cancelUpdate() - $rollbackViewValue()로 이름이 ​​변경되었다. 그리고 현재의 $viewValue 값이 복귀하기 위해서는 $lastCommittedViewValue값으로 뒤로 가야한다. 그럴려면 보류중인 debounce 업데이트와 input을 다시 render하는 작업을 취소한다.

$cancelUpdate()를 사용하는 코드는 다음 예제처럼 마이그레이션 하라.

전 :
$scope.resetWithCancel = function (e) {
	if (e.keyCode == 27) {
	  $scope.myForm.myInput1.$cancelUpdate();
	  $scope.myValue = '';
	}
};

후 :
$scope.resetWithCancel = function (e) {
	if (e.keyCode == 27) {
	  $scope.myForm.myInput1.$rollbackViewValue();
	  $scope.myValue = '';
	}
}

18. $interpolate
- split .parts into .expressions and .separators
$interpolate에 의해 반환되는 function은 .parts 배열을 가질수 없다. 대신, 두개의 배열을 갖게 된다.
  • .expressions - interpolate된 텍스트의 expression 배열. expressions은 $parse로 분석되며, 계산되어질 때 문자열로 변환된다.
  • .separators - 텍스트에서 interpolation 사이를 구분하는 문자열의 배열로,이 배열은 병합하기 쉽게하기 위해 항상 .expressions 배열보다 1 아이템 길다.

19. $animate
- insert elements at the start of the parent container instead of at the end
$animate 부모 컨테이너의 마지막 요소로하는 after 매개 변수를 더이상 디폴트가 아니다. 대신 after가 지정되어 있지 않은 경우에는 새로운 요소를 부모 컨테이너의 첫 번째 자식으로 삽입 할 수 있다.
기존 코드를 업데이트하기 위해서는 $animate.enter() 또는 $animate.move()의 모든 인스턴스를
$animate.enter(element, parent);

에서
$animate.enter(element, parent, angular.element(parent[0].lastChild));

로 변경하여야 한다.

- make CSS blocking optional for class-based animations
전이(transitions)를 사용하거나, 설치 CSS class(class-add와 class-remove 등) 기반의 애니메이션 코드는 스타일이 즉시 적용되는 것을 보장하려면 빈 transition 값을 주어야 한다. 다른말로는, 애니메이션의 코드가 설치 class에서 정의된 스타일을 적용하고, CSS class에서 "transition:0s none" 값이 존재하지 않는 한 즉시 적용되지 않는다. 이 상황은 전이(transitions)는 애니메이션이 한번 시작된 후 베이스 CSS 클래스에 존재하는 경우에만 해당된다.

전 :
.animated.my-class-add {
	opacity:0;
	transition:0.5s linear all;
}
.animated.my-class-add.my-class-add-active {
	opacity:1;
}

후 :
.animated.my-class-add {
	transition:0s linear all;
	opacity:0;
}
.animated.my-class-add.my-class-add-active {
	transition:0.5s linear all;
	opacity:1;
}

자세한 내용은 ngAnimate 문서를 봐주세요.

20. $compile
- add support for $observer deregistration
attr$observe 호출할 경우 더이상 observer function을 리턴해주지 않고, 대신 등록 해제(deregistration) function을 리턴해 준다. 다음의 예에 따라 코드를 마이그레이션하세요.

전 :
directive('directiveName', function() {
return {
  link: function(scope, elm, attr) {
    var observer = attr.$observe('someAttr', function(value) {
      console.log(value);
    });
  }
};
});

후 :
directive('directiveName', function() {
return {
  link: function(scope, elm, attr) {
    var observer = function(value) {
      console.log(value);
    };

    attr.$observe('someAttr', observer);
  }
};
});

21. $httpBackend
- don't error when JSONP callback called with no parameter
에러와 empty 응답에 대한 JSONP의 동작이 변경되었다. 이전에는, JSONP 응답이 비어 있는 경우에는 에러로 간주되었지만 지금은 에러를 발견하기 위한 이벤트를 올바르게 수신할 수 있게 되었다. 이젠 empty 응답도 성공적으로 수신할 수 있다.

22. build
- remove IE8 target from all test configs
IE8은 더이상 지원되지 않는다.

23. input
- support types date, time, datetime-local, month, week
date, time, datetime-local, month, week의 Type은 항상 Date 객체가 필수이다.

용어

  • interpolation : {{}} 값을 채움.
  • debounce : 지정한 값만큼 지난 후 바인딩된 모델값의 변경.

박사란?

그림으로 보는 박사 이야기"인데, 곰곰히 보면 우리에게 주는 교훈은 큰거 같습니다. 가끔 보면서 자신의 위치를 살펴보는 것도 괜찮을 것 같네요.

인류의 모든 지식을 담았다고 생각하는 원을 상상해 보자.



초등학교를 졸업하면 약간의 지식을 얻는다.



고등학교를 졸업할 무렵에는 조금 더 지식을 습득한다.


대학의 학사 학위를 취득하면 전공 분야에서 전문성을 얻을 수 있게 되고



대학원 석사 학위를 취득하면 전문성이 더 깊어진다.



다양한 연구 논문을 읽는다면, 인류 지식의 가장 자리에 다다를 수 있고



가장 자리에 있게 되면 그 부분에 집중하게 된다.



몇 년 동안, 그 경계에서 계속 파게 되고



어느 날, 인류 지식의 경계를 넘어서게 되고



이 홈 부분을 박사라고 부른다.



물론 당시에는 세계가 달라 보인다.



하지만, 큰 그림(전체)을 잊지 마라.



그래서 계속 노력해야 한다.
Tags :

리팩토링이나 코드 리뷰에 사용할 체크리스트


리팩토링이나 코드 리뷰에 사용할 체크리스트로 사용해도 좋을법한 약어들을 공개합니다. 아직 부족하지만 같이 채워나가요. ^^
  • DC(Duplicate Code) - 중복된 코드(중복된 코드나 조건들).
  • BPC(Bad Performance Code) - 성능에 좋지 않는 코드.
  • BN(Bad Name) - 변수나 클래스, 함수 등에 너무 길거나 가독성 떨어지고 일관되지 않은 이름.
  • VS(Violates Specifications) - 가이드나 지침을 따르지 않음.
  • TCL(Too Complicated/Larged) - 불필요하거나 너무 크고 복잡한 함수나 클래스.
  • LP(Long Parameter) - 불필요하거나 너무 긴 인자값들.
  • II(Inconsistent Indentation) - 비일관적인 들여쓰기.
  • NEC(Not Enough Test Cases) - 테스트 케이스가 너무 적음.
  • TMIS(Too Much If/Switch) - 조건 분기가 많음.
  • NC(No Comments) - 주석이 너무 없음.
  • SS(Shotgun Surgery) - 하나의 함수나 클래스를 변경했는데 많은 것들이 영향을 받는다.
  • FE(Feature Envy) - 다른 클래스의 속성과 메소드들을 사용해 다른 클래스와 강한 관계가 성립한다. 모듈(클래스) 독립적이지 못하다.
  • BC(Boilerplate Code) - Getter/Setter 등 맹복적으로 추가되는 반복적인 코드들.
  • TE(Throws Exception) - 예외 처리.
  • NG(Not General) - 너무 범용적이지 않음.
  • DD(Dead Code) - 사용되지 않거나 미리 만들어진, 불필요한 코드들.
  • MN(Magic Number) - 매직 넘버.
  • PD(Platform Dependency) - 플랫폼(언어, 환경 등) 종속성이 있다.
  • AOS(Abuse Open Source) - 오픈 소스 남용.
추가적으로 생각나는 것은 지속적으로 업데이트 하겠습니다. 그리고 여기에 추가할 좋은 약어들이 있으면 코멘트(댓글)나 이메일 남겨주시면 고맙겠습니다. ^^

위임에 관한 70%룰

완벽한 일 마무리와 과로 사이의 균형을 판별하는 공식이 있다네요. 그것이 바로 70%룰이랍니다.

보통 일을 제대로 끝낼려면 다른 사람에게 일을 맡기기보다 자신 스스로가 해야 한다고 생각하는 사람이 많을겁니다. 하지만, 70%룰을 통해 자신의 일을 다른 사람이 해서 70% 정도의 품질만 보장되면 그 사람에게 맡기는 것이 중요하다고 하네요.

모든 일을 혼자 다 할 순 없는 법이니깐요.
CEO가 하는 일을 적어도 자신이 했을때와 비교했을 때 70% 정도의 퀄리티로 할 수있는 사람이 있다면 그 사람에게 일을 맡겨라. 일이 같은 수준의 완성도가 되지 않는다면 좌절하나요? 하지만, 완벽함을 잊으라. 말처럼 쉬운 것은 아니다. 하지만, 맡기는 경우라면 완벽을 요구하는 것은 금물이다. CEO는 작업에 모든 시간을 할애할 필요는 없다. 일에 무한정 시간을 투입할 순 없다. 제때 결과물을 내는 것이 중요하고 거기에 더해 더 영향력이 높은 프로젝트에 투자하는 것이 필요하다.

일을 위임시에 중요한 것은 달성할 목표를 명확히 하고, 그것을 달성하기 위해 무엇이 필요한지를 알고 그것을 팀 구성원에게 전달하는 것이다. 위임에서 가장 어려운 부분은 일을 믿고 기다리는 것이다. 팀의 멤버가 일을 잘 해나갈 것이다라고 믿는 것이다. 위임에서 또 중요한 것은 당신의 팀 구성원이 당신과는 전혀 다른 방법으로 목표를 달성하려고 하는 것을 이해하는 것이다. 그리고 완벽함을 잊기 위해서 무엇을 우선해야 할 것인가를 결정해야 한다. 그 결정할 일은 "완벽함"을 추구 방법이나, 다른 방법도 충분히 결과를 낼 수 있다는 것이다. 팀에 재량권을 주면 과제를 수행하는데 더 새롭고, 좋은 방법을 발견하는 등 더 놀라는 일을 발견하게 될 것이다.
일을 제대로 위임하고 그것을 허용하는 자신의 마음이 중요할거 같네요.

[참조 사이트]
Tags :

Javascript에서 함수형 프로그래밍 맛보기

함수형 프로그래밍을 이해하기 쉽게 설명한 아티클(Don’t Be Scared Of Functional Programming)이 있어서 소개합니다. 정리하면서 느낀점은, 일차적으로 함수형 프로그래밍을 이해하는 것이겠지만, 이차적으로는 코드의 간결성이 좋아지고 리팩토링에서 좋은 사례로 활용할 수 있을 것 같아 좋았습니다. ^^

함수 프로그래밍의 기본 특성

여기서 말하고자 하는 가장 기본적인 함수형 프로그래밍의 특징을 정리하면 아래와 같다.

첫번째, 함수형 프로그래밍에서의 데이터는 Immutable해야 한다. 기존의 데이터를 변경하지 않고 새로운 자료 구조를 만든다. 예를 들어 배열내의 몇몇 데이터에 대해 조작이 필요하다면 원본 배열을 수정하기보다는 업데이트된 값을 가지고 새로운 배열을 만든다.

두번째로 함수형 프로그래밍은 Stateless(상태를 유지하지 않아야)해야 한다. 이것은 프로그램을 수행중에 어떤 일이 일어날지, 아니면 일어나지 않을지에 대해서 모르는 상태로 모든 일을 수행하는 것을 말한다. 즉, 함수 처리에서 다른 무언가에 의존하지 않는다는 것을 말한다. Immutable과 결합해 마치 진공관 속에서 작동하는 것처럼 함수들을 바라볼 것이다. 계산을 수행하면서 외부 값에 영향을 받지 않고 인자로 받은 데이터값에 대한 처리밖에는 하지 않는다.

아래에서 다시 설명하겠지만, 이런 함수형 프로그래밍의 특성을 가지고 구현할 때 모범 사례같은 룰이 있다.
  1. 모든 함수는 최소한 하나의 인자를 수용한다.
  2. 모든 함수는 데이터나 혹은 다른 함수를 반환한다.
  3. 루프는 사용하지 않는다.

실제 함수형 프로그래밍 구현 사례

위의 함수형 프로그래밍의 기본적인 특성을 인지하고 아래의 코드를 보면서 실제 사례를 통해 어떻게 일반 프로그램이 함수형 프로그램으로 변환되는지 보자. 그 예로 인구(population)와 평균 기온(temperature)을 구해서 그래프 즉, 비주얼라이제이션하는 사례를 살펴보는데 여기서는 데이터를 구하는 부분만 어떻게 함수형 프로그래밍 방법이 사용되는지 살펴보자. 먼저 서버의 API에서 응답하는 데이터가 data라는 변수에 저장되고 그 형상은 아래와 같다.
var data = [
  { 
    name: "Jamestown",
    population: 2047,
    temperatures: [-34, 67, 101, 87]
  },
  {
    name: "Awesome Town",
    population: 3568,
    temperatures: [-3, 4, 9, 12]
  }
  {
    name: "Funky Town",
    population: 1000000,
    temperatures: [75, 75, 75, 75, 75]
  }
];

인구(population)와 평균 기온(temperature)을 비교하기 위해 그래프나 차트를 사용하기를 원한다면, 그래프를 그리기 전에 먼저 위의 데이터를 약간 변경해야 한다. 그래프 라이브러리는 다음과 같은 x, y좌표를 원할 것이다.
[
  [x, y],
  [x, y]
  …
]

x는 평균 기온, y는 인구를 나타낸다. 함수형 프로그래밍이라는 관점을 의식하지 않고 구현하면 아래와 같다.
var coords = [],
    totalTemperature = 0,
    averageTemperature = 0;

for (var i=0; i < data.length; i++) {
  totalTemperature = 0;
  
  for (var j=0; j < data[i].temperatures.length; j++) {
    totalTemperature += data[i].temperatures[j];
  }

  averageTemperature = totalTemperature / data[i].temperatures.length;
  coords.push([averageTemperature, data[i].population]);
}

함수형 프로그래밍 방식으로 전환하기 위해서는 위에 설명했듯이 다음과 같은 모범사례를 사용하면 좋다.
  1. 모든 함수는 최소한 하나의 인자를 수용한다.
  2. 모든 함수는 데이터나 혹은 다른 함수를 반환한다.
  3. 루프는 사용하지 않는다.

자 그러면, 위 프로그램을 함수형 언어 방식로 변경해 보자. 우선 배열(온도)의 합계를 내는 함수를 먼저 만들어보자.
function totalForArray(arr) {
  // add everything
  return total;  
}
그런데 3번 룰인 루프 사용 금지에 의거해 루프를 사용하지 않기 위해서는 재귀 방식을 사용하여 구현한다.
// 인자로는 전체 합계와 배열을 가진다.
function totalForArray(currentTotal, arr) {
  
  currentTotal += arr[0]; 

  //Array.shift를 사용하지 않고 Immutable을 보장하기 위해서 배열편집보다는 새로 만든다. 
  var remainingList = arr.slice(1);

  // 재귀처리를 하고 현재 합계와 나머지 배열을 인자로 전달하고 
  if(remainingList.length > 0) {
    return totalForArray(currentTotal, remainingList); 
  }
  
  // 나머지 배열이 없으면 전체 합계을 리턴한다.
  else {
    return currentTotal;
  }
}

주의) 재귀는 가독성을 올려주고 함수형 프로그래밍에서는 필수다. 하지만, Javascript와 같은 싱글 오퍼레이션에서 재귀 호출이 많은 경우는 가끔 문제를 유발하는 경우도 있다.(10,000 calls in Chrome, 50,000 in Firefox and 11,000 in Node.js)

이제 온도의 합은 아래와 같이 호출하면 얻을 수 있게 되었다.
var totalTemp = totalForArray(0, temperatures);
더 분해할 수 있는 부분이 totalForArray함수의 currentTotal을 처리하는 부분이다.
function addNumbers(a, b) {
  return a + b;
}

그래서 결국 아래와 같은 totalForArray 함수를 만들수 있다.
function totalForArray(currentTotal, arr) {
  currentTotal = addNumbers(currentTotal, arr[0]);

  var remainingArr = arr.slice(1);
  
  if(remainingArr.length > 0) {
    return totalForArray(currentTotal, remainingArr);
  }
  else {
    return currentTotal;
  }
}

배열에서 단일값을 반환하는 것은 함수형 프로그래밍에서 매우 일반적인 특징이다. 그래서 Javascript에서는 reduce라는 편리한 함수가 존재한다. 그래서 온도의 합계를 계산하는 reduce 방식은 아래와 같다.
Array.prototype.reduce 사용법은 여기를 참고하자.
var totalTemp = temperatures.reduce(function(previousValue, currentValue){
  return previousValue + currentValue;
});

그리고 덧셈 부분을 조금 전에 만든 덧셈의 함수(addNumbers)를 사용하면 아래와 같이 된다.
var totalTemp = temperatures.reduce(addNumbers);

배열의 합계를 할 경우 재귀나 reduce 사용 판단의 혼란을 가중시키지 않고 일반적인 함수를 만든다면 아래와 같아진다.
function totalForArray(arr) {
  return arr.reduce(addNumbers);
}

var totalTemp = totalForArray(temperatures);

두번째 일로 이제까지 구한 합계에서 평균을 내는 함수를 만든다.
function average(total, count) {
  return total / count;
}

지금 만든 평균과 좀 전에만든 합계 로직을 결합하면 어떻게 될까?
function averageForArray(arr) {
  return average(totalForArray(arr), arr.length);
}

var averageTemp = averageForArray(temperatures);

마지막으로, 객체 배열에서 하나의 속성을 빼내는 함수를 만들어 보자. 재귀함수 대시에 우리는 JavaScript가 가지고 있는 map 내장함수를 사용한다.
var allTemperatures = data.map(function(item) {
  return item.temperatures;
});
이는 위의 data 객체에서 온도만 추출해내는 것으로 결과는 아래와 같이 된다.
var allTemperatures = [-34, 67, 101, 87, -3, 4, 9, 12, 75, 75, 75, 75, 75];

객체 배열에서 속성을 추출하는 것도 일반적인 작업이므로 함수로 만든다.
function getItem(propertyName) {
  return function(item) {
    return item[propertyName];
  }
}

주의할 점은, 이 함수는 함수를 반환한다. 실행 호출자에게 맡기고 있다는 점이다. map과 연동하면 아래와 같이 된다.
var temperatures = data.map(getItem('temperature'));

좀 더 가독성과 범용성을 높이기 위해서 아래와 같은 함수를 만든다.
function pluck(arr, propertyName) {
  return arr.map(getItem(propertyName));
} 

var allTemperatures = pluck(data, 'temperatures');

이제 객체 배열에서 원하는 프로퍼티를 추출할 수 있게 되었다.
자 그럼 위에서 제시했던 문제의 해법을 적용하자면, x는 평균기온, y는 전체 인구를 추출해야한다.
var populations = pluck(data, 'population');
var allTemperatures = pluck(data, 'temperatures');
var averageTemps = allTemperatures.map(averageForArray);

위의 처리결과 다음과 같은 두개의 배열값을 취득하게 되었다.
// populations
[2047, 3568, 1000000]

// averageTemps
[55.25, 5.5, 75]

마지막으로, 2개의 배열을 하나로 통합 함수를 만든다.
function combineArrays(arr1, arr2, finalArr) {
  // 셋째인자의 배열이 널일 경우를 대비하여 초기화해 줌
  finalArr = finalArr || [];

  // 첫번째 배열요소를 추출해 출력 배열에 삽입함
  finalArr.push([arr1[0], arr2[0]]);

  var remainingArr1 = arr1.slice(1),
      remainingArr2 = arr2.slice(1);

  // 남아있는 배열이 비어있는 경우 리턴 
  if(remainingArr1.length === 0 && remainingArr2.length === 0) {
    return finalArr;
  }
  else {
    // Recursion!
    return combineArrays(remainingArr1, remainingArr2, finalArr);
  }
};

var processed = combineArrays(averageTemps, populations);

결국, 결합함수에 의해서 처리되는 결과값은 아래와 같아진다.
var processed = combineArrays(pluck(data, 'temperatures')
.map(averageForArray), pluck(data, 'population'));

// [
//  [ 55.25, 2047 ],
//  [ 5.5, 3568 ],
//  [ 75, 1000000 ]
// ]

결국에는 아래와 같은 코드로 귀결된다.

이것이 함수형 프로그래밍의 전부는 아니겠지만, 이를 활용할 경우 리팩토링이나 코드 작성 지침에 좋은 훈수 역할을 할 수 있을 것으로 생각됩니다.

빌드시스템 Pants에 대해

Foursquare/Twitter/Square에서 사용중인 증분 빌드 시스템인 Pants(오픈소스)에 대해서 정리했습니다. 참고 사이트는 트위터 블로그에 쓰여진 "Hello Pants build"를 참고하였습니다.

큰 프로젝트의 경우 codebases가 커짐으로인해 그에 수반되는 개발 작업의 어려움도 증가하게 된다. 빌드도 느려지고, 또한 현재 존재하는 툴들은 그리 스케일하지도 않다.

그래서 하나의 해결책으로 내놓은 것이 코드를 분리해 여러 저장소에 나누어 관리를 하는 것이다. 그러나 이렇게함으로써 codebases가 수백개로 겉잡을 수 없게 되 종속성 관리도 어렵게 되어버린다. 그래서 결국 개발자의 개발 생산성에 악영향을 준다는 것이다.

다른 해결책의 하나로 Python으로 개발된 Pants를 사용하는 것인데, 이는 커진 코드베이스를 하나의 저장소로 정리해 종속성도 효율적으로 관리되다보니 개발 생산성도 올라간다는 것.

Pants는 Google의 내부 빌드시스템을 참고로 작성되었으며, Foursquare/Twitter/Square에서도 사용중이며, 실제 필요한부분, 즉, 변경된 부분만 컴파일하는 증분 빌드 기법을 적용하고 있다.

주요 강점은 아래와 같다.
  • 현재는 Java/Scala/Python을 지원.
  • 다른 언어를 추가해도 문제가 없음.
  • 코드 생성을 지원 : thrift, protocol buffers, 커스터마이징 가능한 코드 생성기도 지원.
  • 외부 JVM/Python 종속성을 지원함.
  • 테스트 수행함.
  • 배포 가능한 패키지를 만들 수 있음.
  • 스케일 지원.
  • 증분빌드 지원.
  • 스칼라 언어의 빌드는 다른 도구들보다 빠름.
  • 독립형 Python의 executable(PEX 파일)를 빌드함.
  • 로컬/분산 캐시를 지원.
  • Linux와 Mac OSX에서 돌아감.