Insight? Practice!

Road to myself. 자기자신에게로 이르는 길.

[번역] 하루만에 끝내는 AngularJS

이글은 Todd라는 개발자가 작성한 “Ultimate guide to learning angular js in one day”라는 블로그 글을 번역한 것이다.

정말 하루만에 끝낼 순 없지만 -_- 그래도 좋은 시작점이 되리라 생각한다. 다같이 AngularJS에 빠져보자!


AngularJS 란?

Angular는 자바스크립트로 만든 client 측 MVC/MVVM 프레임워크로 모던 단일 페이지 웹 애플리케이션(혹은 웹사이트) 개발의 정수이다. 그리고 모던 웹을 다루는 새로운 방법이자 HTML5가 가져올 미래를 위한 큰 발판이다. 이 글은 필자가 Angular를 경험해보며 알게 된 것들과 조언, 우수 사례를 바탕으로 작성해본 이름하여 하루만에 끝내는 Angular 강좌다.

용어

Angular는 새로 등장하는 용어와 “MVC로 생각”하는 방법으로 인해 약간의 러닝커브가 있다. 여기서 MVC는 모델-뷰-컨트롤러 를 의미한다. 이제부터 Angular를 구성하는 필수적인 API와 용어를 살펴보자.

MVC

분명 MVC는 들어봤을 것이다. 프로그래밍 언어에서 애플리케이션/소프트웨어의 구조를 잡거나 아키텍처를 정하는 방법으로 많이 사용 되고 있다. 일단 간단히 의미를 살펴보자:

  • 모델 : 보통 JSON으로 표현되는 애플리케이션의 특정한 데이터 구조를 말한다. 뷰가 서버와 통신하기 위해 꼭 필요한 내용이니 더 진행하기 전에 다음 JSON을 잘 살펴보자. 예를 들어 User ID 그룹은 다음과 같은 모델을 가질 수 있다:
{
  "users" : [{
    "name": "Joe Bloggs",
    "id": "82047392"
  },{
    "name": "John Doe",
    "id": "65198013"
  }]
}

이 정보를 jQuery의 $.ajax 메서드를 래핑한 Angular의 $http 를 통해 XHR(XMLHttp Request)로 서버에서 가져오거나 페이지를 로딩할 때 코드에서 직접 (데이터베이스에서) 읽어오도록 할 수 있다. 그리고 모델을 변경한 다음 다시 반영할 수도 있다.

  • : 뷰는 간단하다. HTML 혹은 렌더링된 결과를 말한다. MVC 프레임워크를 사용한다면 뷰를 갱신할 모델 데이터를 내려받은 뒤 HTML에서 해당 데이터를 보여줄 것이다.

  • 컨트롤러 : 말 그대로 한번 생각해보자. 무언가를 조정한다. 근데 무엇을 조정할까? 데이터다. 컨트롤러는 서버 에서 직접 로 접근하는 일종의 중간 통로로서 필요할 때마다 서버와 클라이언트 통신으로 데이터를 변경한다.

AngularJS 프로젝트 설정 (아주 기초)

이제 실제 AngularJS 프로젝트를 만들어보자. 시작하기 전에 ng-app 선언으로 앱을 정의하는 부분이라든가 뷰와 통신하는 컨트롤러 또는 Angular에 내재된 DOM 바인딩 등 몇 가지 살펴볼 게 있다. 이제부터는 아주 기초적인 부분이다:

다음은 ng-* 선언을 추가한 HTML이다:

<div ng-app="myApp">
    <div ng-controller="MainCtrl">
        <!-- controller logic -->
    </div>
</div>

그리고 Angular 모듈과 컨트롤러다:

var myApp = angular.module('myApp', []);

myApp.controller('MainCtrl', ['$scope', function ($scope) {
  // Controller magic
}]);

더 진행하기 전에 모든 로직을 담을 Angular 모듈 을 하나 만들어보자. 모듈을 정의하는 방법은 다양하며 그 중 하나가 다음과 같이 로직을 묶는 방법이다(필자는 이런 방식을 별로 안좋아한다):

angular.module('myApp', [])
.controller('MainCtrl', ['$scope', function ($scope) {...}])
.controller('NavCtrl', ['$scope', function ($scope) {...}])
.controller('UserCtrl', ['$scope', function ($scope) {...}]);

필자가 해봤던 Angular 프로젝트를 생각해보면 전역 모듈을 만드는게 가장 좋은 방법이다. 하지만 이렇게 세미콜론을 쓰지않고 함수 체인을 갑자기 끊으면 불필요한 컴파일 에러를 만들어내기 때문에 비효율적이다. 다음 코드를 보자:

var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope', function ($scope) {...}]);
myApp.controller('NavCtrl', ['$scope', function ($scope) {...}]);
myApp.controller('UserCtrl', ['$scope', function ($scope) {...}]);

새로 만드는 파일마다 myApp 을 네임스페이스처럼 사용할 수 있으니 바로 애플리케이션에 집중할 수 있을 것이다. 그렇다. 각 컨트롤러, 디렉티브, 팩토리 등 모든 것에 대해서 각각 새로운 파일을 만들 것이다(이부분은 나에게 고마워해야 할 것임). 그리고 이들을 모두 엮어서 Grunt같은 실행기로 하나의 스크립트 파일을 DOM에 적용한다.

컨트롤러

이제 MVC와 기본적인 설정을 살펴봤으니 Angular의 컨트롤러를 어떻게 사용하는지 살펴보자.

다음 예제를 진행하기 전에 컨트롤러를 통해서 데이터를 DOM에 주입하는 아주 쉬운 단계부터 살펴보자. Angular는 HTML과 통신하기 위해 {{ handlebars }} 와 같은 템플릿 형식의 문법을 사용한다. HTML에 데이터를 하드코딩하지 않아야지만(이상적으로는) Angular를 제대로 사용하는 것이다. 다음은 DOM에 간단한 문자열을 넣는 예제다:

<div ng-app="myApp">
    <div ng-controller="MainCtrl">
         {{ text }}
    </div>
</div>
var myApp = angular.module('myApp', []);

myApp.controller('MainCtrl', ['$scope', function ($scope) {
    
    $scope.text = 'Hello, Angular fanatic.';
    
}]);

다음은 실행한 결과다:

여기서 가장 중요한 개념은 특정 컨트롤러안에 모든 기능을 담는 $scope 라는 개념이다. $scope 는 DOM의 현재 요소/영역을 참조하며(this 와는 다르다), 요소안의 데이터와 로직을 주시하는 아주 멋진 관찰 기능을 가지고 있다. 이 기능으로 DOM에 자바스크립트 public/private 스코프를 멋지게 만들 수 있다.

$scope 개념이 처음에는 조금 이상해 보일지 몰라도 서버로부터 DOM을 만드는 아주 좋은 방법이다(정적 데이터인 경우도 역시)! 예제를 보면 DOM으로 데이터를 어떻게 주입하는지에 대한 기본 개념을 익힐 수 있을 것이다.

이제 사용자의 로그인 세부 내용을 보여주기 위해 서버에서 조금 더 자세한 데이터를 받아왔다고 가정해보자. 지금은 정적 데이터이고 실제 JSON으로 받아오는 건 나중에 살펴보겠다.

먼저 자바스크립트를 설정한다:

var myApp = angular.module('myApp', []);

myApp.controller('UserCtrl', ['$scope', function ($scope) {
    
    // user details라는 네임스페이스를 사용하자. DOM에서 알아보기도 좋을 것이다.
    $scope.user = {};
    $scope.user.details = {
      "username": "Todd Motto",
      "id": "89101112"
    };
    
}]);

그 다음 화면에 보여주기 위해 DOM에 데이터를 지정한다:

<div ng-app="myApp">
    <div ng-controller="UserCtrl">
        <p class="username">Welcome, {{ user.details.username }}</p>
        <p class="id">User ID: {{ user.details.id }}</p>
    </div>
</div>

결과:

컨트롤러는 JSON 데이터로 서버와 통신하는 함수(이벤트 함수도!)와 데이터 만을 다룬다는 걸 기억하는 게 중요하다. DOM 조작을 컨트롤러에서 해선 안되며 jQuery도 일단은 생각하지 말자. DOM 조작은 디렉티브로 하면 되니까 조금 있다 다시 살펴보자.

중요팁: Angular 문서를 보면(이 글을 쓰는 지금) 컨트롤러를 생성하는 방법을 다음과 같이 설명하고 있다:

var myApp = angular.module('myApp', []);

function MainCtrl ($scope) {
  //...
};

… 이렇게는 하지 말자. 모든 함수가 전역 함수가 되버려서 앱 안에 집어넣기도 어렵다. 또한 코드를 압축하기도 어렵고 테스트를 쉽게 실행하기도 힘들다. 따라서 전역 네임스페이스는 생성하지 말고 컨트롤러는 앱 안에 꼭 집어넣자.

디렉티브

디렉티브(Directives from existing scripts/plugins 포스트를 참고)의 가장 간단한 형태는 애플리케이션이 필요한 곳에 여러 번 사용할 수 있는 작은 HTML 조각 형태다. 디렉티브를 사용하면 애플리케이션에 별다른 노력없이도 쉽게 DOM을 주입하거나 사용자 정의 DOM의 상호작용을 적용할 수 있다. 디렉티브는 간단하지 않을 뿐더러 러닝커브가 생각보다 꽤 높긴 하지만 다음 절부터 읽어보면 분명 도움이 될 것이다.

그래서 디렉티브가 어디에 유용한 걸까? DOM 컴포넌트를 포함해서 많은 부분에 유용하다. 앱에서 사용하는 UI에 따라 다르긴 하지만 탭과 네비게이션 요소 등에 특히 유용하다. 이런 식으로 설명해보겠다. ng-showng-hide 를 생각해본적이 있다면 그게 바로 디렉티브다(DOM을 주입하지는 않지만).

이번 예제에서는 아주 간단한 버튼(customButton 이라는 이름의)을 생성해서 필자가 일일히 직접 타이핑하기 싫어하는 마크업을 한번 주입해 보겠다. DOM에 디렉티브를 정의하는 다양한 방법이 있지만 내가 사용한 방법은 다음과 같다:

<!-- 1: 속성으로 정의 -->
<a custom-button>Click me</a>

<!-- 2: 요소로 정의 -->
<custom-button>Click me</custom-button>

<!-- 3: 클래스로 정의(IE 구버전 호환을 위해) -->
<a class="custom-button">Click me</a>

<!-- 4: 주석으로 정의 (데모로는 별로 안좋긴 하다) -->
<!-- directive: custom-button -->

사용자 정의 요소는 HTML5의 웹 컴포넌트로 추가될 예정이라서 필자는 디렉티브를 속성 형태로 사용하는 걸 선호하지만 오래된 특정 브라우저에서 오류를 낸다고 한다.

이제 디렉티브를 어떻게 사용하고 주입하는지 알아봤으니 사용자 정의 버튼을 생성해보자. 애플리케이션의 전역 변수인 myApp 을 사용해서 디렉티브를 선언하는 방법이다:

myApp.directive('customButton', function () {
  return {
    link: function (scope, element, attrs) {
      // DOM 조작과 이벤트 설정은 여기서!
    }
  };
});

.directive() 메서드로 디렉티브를 선언하고 디렉티브 이름으로 ‘customButton’을 사용했다. 디렉티브 이름에 대문자를 사용하면 DOM에서는 하이픈으로 이를 구분해서 사용하게 된다(위 예제처럼).

디렉티브는 여러개의 속성을 가지는 객체를 반환한다. 처음 배우는 입장에서 필자가 가장 중요하게 생각하는 건 restrict, replace, transclude, template, templateUrl, link 속성이다. 이 속성들을 추가해보자:

myApp.directive('customButton', function () {
  return {
    restrict: 'A',
    replace: true,
    transclude: true,
    template: '<a href="" class="myawesomebutton" ng-transclude>' +
                '<i class="icon-ok-sign"></i>' +
              '</a>',
    link: function (scope, element, attrs) {
      // DOM 조작과 이벤트 설정은 여기서!
    }
  };
});

결과:

브라우저의 요소 검사 로 마크업이 잘 주입됐는지 확인해보는 걸 잊지 말자. 아이콘도 없고 멋진 폰트도 사용안했지만 어떻게 동작하는지는 알 수 있을 것이다. 자 다음은 디렉티브의 각 속성에 대한 설명이다:

  • restrict: 어떻게 요소의 사용을 제한할 수 있을지 다시 한번 생각해보자. 오래된 IE를 지원해야하는 프로젝트를 진행중이라면 분명 속성/클래스 정의가 필요할 것이다. ‘A’라고 지정하면 속성 으로만 사용할 수 있다는 의미이고 ‘E’는 요소, ‘C’는 클래스, ‘M’은 주석 으로만 사용할 수 있다는 것을 의미한다. 기본 값은 ‘EA’이고 이 처럼 여러 개의 제한을 동시에 걸수도 있다.

  • replace: 디렉티브에 정의한 DOM의 마크업을 변경할 수 있음을 의미한다. 예제를 보면 처음의 DOM이 디렉티브의 템플릿으로 어떻게 변경됐는지 알 수 있을 것이다.

  • transclude: 간단하게 말해서 집어넣는 것이다. transclude를 이용하면 기존의 DOM 내용을 디렉티브안에 복사할 수 있다. ‘Click me’라는 문자열이 렌더링될 때 디렉티브로 옮겨진 것을 봤을 것이다.

  • template: 템플릿은 주입할 마크업을 의미한다. HTML의 아주 작은 일부분을 정의할 때 특히 좋다. 주입된 템플릿은 Angular로 컴파일되며 이로 인해 handlebar 템플릿 태그도 사용할 수 있다.

  • templateUrl: template 속성과 비슷하지만 <script> 태그 혹은 파일을 지정할 때 사용한다. HTML의 일부분을 다른 파일로 관리할 필요가 있을 때 템플릿 파일의 URL로 파일 이름과 경로(보통 templates 디렉토리)를 표시해주면 된다.

myApp.directive('customButton', function () {
  return {
    templateUrl: 'templates/customButton.html'
    // 나머지 디렉티브 내용...
  };
});

그리고 다음은 템플릿 파일 내용이다(이름은 중요하지 않음):

<!-- customButton.html 내용 -->
<a href="" class="myawesomebutton" ng-transclude>
  <i class="icon-ok-sign"></i>
</a>

이렇게 했을 때 정말 좋은 점은 브라우저가 HTML 파일을 캐싱 한다는 점이다. 브라보! 캐싱되는걸 원하지 않는다면 <script> 태그안에 템플릿을 선언하면 된다:

<script type="text/ng-template" id="customButton.html">
<a href="" class="myawesomebutton" ng-transclude>
  <i class="icon-ok-sign"></i>
</a>
</script>

이렇게 하면 Angular에게 이 ID로 ng-template 을 선언했다고 알려주게 된다. 그러면 Angular는 ng-template 혹은 *.html 파일을 찾기 시작할 것이다. 필자는 *.html 파일을 선호하는데, 쉽게 관리할 수 있고 성능도 잘 나오며 DOM도 깔끔하게 유지할 수 있기 때문이다. 최소한 1개 이상 혹은 100개가 넘는 디렉티브를 사용할 것일고 이 중에서 분명 원하는 걸 쉽게 찾고 싶지 않겠나.

서비스

서비스는 종종 헷갈리는 부분이다. 경험에 비춰보면 서비스는 기능적인 차이점을 제공하지 않으면서도 뭔가 더 좋아보이는 디자인 패턴이다. Angular 소스를 분석해보니 Angular는 같은 컴파일러를 사용하면서 많은 기능을 제공하는듯 하다. 분석해보니 서비스는 싱글톤 으로 사용해야하고 객체 리터럴이나 좀 더 복잡한 유즈 케이스처럼 더 복잡한 기능은 팩토리를 사용해야 한다.

다음 예제는 2개의 숫자를 곱하는 서비스이다:

myApp.service('Math', function () {
  this.multiply = function (x, y) {
    return x * y;
  };
});

컨트롤러안에서 서비스를 다음처럼 사용할 수 있겠다:

myApp.controller('MainCtrl', ['$scope', function ($scope) {
    var a = 12;
    var b = 24;

    // 결과는 288
    var result = Math.multiply(a, b);
}]);

맞다. 곱셈은 엄청 쉬워서 서비스가 필요하지도 않지만 핵심은 알 수 있었을 것이다.

서비스(혹은 팩토리)를 생성할때는 의존성 주입을 사용해서 Angular에게 새로 만든 서비스의 존재를 알려줘야 한다. 알려주지 않으면 컴파일 에러가 발생하거나 컨트롤러가 동작하지 않을 것이다. 컨트롤러 선언부분에 function ($scope) 를 봤을텐데 이게 바로 간단한 의존성 주입 방법이다. function ($scope) 앞에 있는 [‘$scope’] 도 봤겠지만 이건 나중에 설명하겠다. 다음 예제는 의존성 주입을 통해 Angular에게 서비스가 필요하다고 알려주는 방법이다:

// Math를 주입한다
myApp.controller('MainCtrl', ['$scope', 'Math', function ($scope, Math) {
    var a = 12;
    var b = 24;

    // 결과는 288
    var result = Math.multiply(a, b);
}]);

팩토리

팩토리로 서비스를 만드는 건 이제 간단하다. 객체 리터럴을 팩토리안에서 생성하거나 다음처럼 몇 가지 메서드를 추가하면 된다:

myApp.factory('Server', ['$http', function ($http) {
  return {
    get: function(url) {
      return $http.get(url);
    },
    post: function(url) {
      return $http.post(url);
    },
  };
}]);

Angular의 XHR을 래핑한 코드를 작성해봤다. 컨트롤러에 의존성을 주입한 다음 이렇게 간단히 사용하면 된다:

myApp.controller('MainCtrl', ['$scope', 'Server', function ($scope, Server) {
    var jsonGet = 'http://myserver/getURL';
    var jsonPost = 'http://myserver/postURL';
    Server.get(jsonGet);
    Server.post(jsonPost);
}]);

혹시 서버 변경사항을 폴링하고 싶으면 Server.poll(jsonPoll) 을 설정하거나 Server.socket(jsonSocket) 을 사용할 수도 있겠다. 이렇게 컨트롤러에 서비스를 주입해서 사용하면 컨트롤러의 코드를 최소로 유지할 수 있다. 즉 나만의 도구를 만들어서 사용하는 것처럼 코드를 모듈화할 수 있는 길이 열리는 것이다.

필터

필터는 배열의 데이터와 함께 사용하며 루프 밖에서도 사용 할 수 있다. 데이터를 순회하면서 특정 조건에 만족하는 데이터만 추리고 싶을 때 필터를 사용하면 된다. 예를 들어 <input>에 입력된 값으로 사용자를 추리고 싶을 때처럼 말이다. 필터를 사용하는 방법은 컨트롤러 안에 선언하거나 메서드로 정의해서 사용해도 된다. 다음은 필터를 전역으로 선언한 방법이다:

myApp.filter('reverse', function () {
    return function (input, uppercase) {
        var out = '';
        for (var i = 0; i < input.length; i++) {
            out = input.charAt(i) + out;
        }
        if (uppercase) {
            out = out.toUpperCase();
        }
        return out;
    }
});

// 데이터를 제공하는 컨트롤러
myApp.controller('MainCtrl', ['$scope', function ($scope) {
    $scope.greeting = 'Todd Motto';
}]);

다음은 DOM에서 사용하는 방법이다:

<div ng-app="myApp">
    <div ng-controller="MainCtrl">
        <p>No filter: {{ greeting }}</p>
        <p>Reverse: {{ greeting | reverse }}</p>
    </div>
</div>

결과:

그리고 ng-repeat 안에서 다음과 같이 필터를 사용한다:

<ul>
  <li ng-repeat="number in myNumbers |filter:oddNumbers">{{ number }}</li>
</ul>

다음은 컨트롤러 안에서 필터를 선언하는 예제다:

myApp.controller('MainCtrl', ['$scope', function ($scope) {
    
    $scope.numbers = [10, 25, 35, 45, 60, 80, 100];
    
    $scope.lowerBound = 42;
    
    // 필터가 되어줘
    $scope.greaterThanNum = function (item) {
        return item > $scope.lowerBound;
    };
    
}]);

그리고 이 필터를 ng-repeat 에서 다음과 같이 사용한다:

<li ng-repeat="number in numbers | filter:greaterThanNum">
  {{ number }}
</li>

결과:

지금까지 AngularJS와 API의 중요한 부분만 살펴봤다. 물론 수박 겉핥기 정도로 살펴본 것 뿐이지만 여러분만의 Angular 애플리케이션을 만드는 데는 충분할 것이다.

양방향 데이터 바인딩

양방향 데이터 바인딩이라는 말을 처음 들었을 때는 무슨 말인지 제대로 이해하지 못했다. 양방향 데이터 바인딩을 한 문장으로 표현하자면 완전히 동기화된 데이터 정도가 가장 좋겠다. 즉 모델 을 갱신하면 에 반영되고, 를 갱신하면 모델 에 반영되는 형태를 말한다. 이는 별다른 작업 없이도 데이터가 동기화된다는 뜻이다. 예를 들어 <input> 하나에 ng-model 을 바인딩하고 값을 입력하기 시작하면 동시에 모델이 생성(기존에 존재하면 갱신)된다.

<input>을 하나 생성해서 ‘myModel’이라는 모델을 연결해보자. 그리고 이중괄호 문법으로 모델을 정의하면 뷰와 즉시 연동될 것이다:

<div ng-app="myApp">
    <div ng-controller="MainCtrl">
        <input type="text" ng-model="myModel" placeholder="Start typing..." />
        <p>My model data: {{ myModel }}</p>
    </div>
</div>
myApp.controller('MainCtrl', ['$scope', function ($scope) {
    // 빈 문자열로 초기화하고 모델 데이터를 읽어온다. 
    $scope.myModel = '';
}]);

결과:

XHR/Ajax/$http 호출과 JSON 바인딩

지금까지 $scope 에 기본적인 데이터를 넣는 방법과 모델이 어떻게 양방향 데이터 바인딩으로 동작하는지를 알아봤으니 이제 실제 서버의 XHR 호출을 시도해볼 차례다. 웹사이트에 Ajax 요구사항이 없을 수도 있으니 필수는 아니겠지만, 웹 애플리케이션에서 데이터를 가져오는 부분부터 살펴보자.

로컬 환경에서 개발할 때는 보통 자바, ASP.NET, PHP 등으로 로컬 서버를 사용할 것이고 로컬 데이터베이스 혹은 실제 서버에 접속해서 API로 통신할 것이다. 분명 이 부분은 별반 다르지 않으리라 본다.

‘달러 http’라고 입력하자. 이제부터 좋은 친구가 되어줄 것이다. $http 메서드는 Angular가 서버 데이터에 접근하는 기능을 멋지게 래핑한 메서드로 눈감고 사용할 수 있을 정도로 쉽다. 다음은 ‘GET’ 요청을 보내고 서버에서 데이터를 받아오는 간단한 예제다. 문법이 jQuery와 꽤 비슷해서 금방 이해할 수 있을 것이다:

myApp.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {
  $http({
    method: 'GET',
    url: '//localhost:9000/someUrl'
  });
}]);

이렇게 하면 Angular는 콜백을 좀 더 효율적이고 읽기 쉬운 형태로 작성할 수 있는 promise 라는 걸 반환한다. Promise는 .myPromise() 처럼 점을 사용해서 함수 체인을 구성할 수 있는데 예상대로 성공했을 때와 실패했을 때의 핸들러를 제공한다:

myApp.controller('MainCtrl', ['$scope', function ($scope) {
  $http({
    method: 'GET',
    url: '//localhost:9000/someUrl'
  })
  .success(function (data, status, headers, config) {
    // 성공! 데이터를 가져왔어
  })
  .error(function (data, status, headers, config) {
    // 이런. 뭔가 잘못되었음! :(
  });
}]);

읽기도 쉽고 간지도 난다. 이제 DOM에 모델을 바인딩하고 모델 데이터를 갱신해서 뷰와 서버를 잘 엮어보자. Ajax 호출로 DOM에 사용자 이름을 추가한다고 해보자.

먼저 데이터를 바인딩할 JSON 구조를 정해야 한다. 백엔드 개발자가 애플리케이션이 사용할 API를 만들테니 다음처럼 간단하게 시작해보자:

{
  "user": {
    "name": "Todd Motto",
    "id": "80138731"
  }
}

즉 서버가 객체 하나를 반환해주고 (다른 이름으로 ‘data’를 호출할 것이다 [promise 핸들러에 data 가 있음]) data.user 속성을 읽어와야 한다는 것을 의미한다. data.user 속성 안에는 nameid 가 있다. 접근하기도 쉬우니 ‘Todd Motto’라는 값을 돌려주는 data.user.name 을 찾아서 적용해보자!

자바스크립트 (코드 안에 주석을 통해 설명하겠다):

myApp.controller('UserCtrl', ['$scope', '$http', function ($scope, $http) {

  // 사용자 객체를 생성
  $scope.user = {};

  // 빈 문자열로 초기화
  $scope.user.username = '';

  // 서버에 사용자 이름을 요청
  $http({
    method: 'GET',
    url: '//localhost:9000/someUrlForGettingUsername'
  })
  .success(function (data, status, headers, config) {
    // 서버로부터 받아온 사용자 이름을 모델에 할당!
    $scope.user.username = data.user.name;
  })
  .error(function (data, status, headers, config) {
    // 이런. 뭔가 잘못되었음! :(
  });
}]);

DOM에서는 다음과 같이 설정하면 된다:

<div ng-controller="UserCtrl">
  <p>{{ user.username }}</p>
</div>

이제 사용자 이름이 출력될 것이다. 자 이제 정말 흥미로운 선언적 데이터 바인딩에 대해 살펴보자.

선언적 데이터 바인딩

Angular의 철학은 기능이 풍부한 동적 HTML을 생성해서 웹 클라이언트 측에서는 상상할 수 없었을 만큼 많은 일을 보이지 않게 처리해주는 것이다. 이게 바로 Angular가 하려고 하는 일이다.

이제 메일 목록과 각 메일의 제목, 보낸 날짜를 Ajax 요청으로 가져와서 DOM에 그리는 기능을 구현한다고 생각해보자. Angular의 힘을 느껴볼 시간이다. 먼저 메일에 대한 컨트롤러를 만들자:

myApp.controller('EmailsCtrl', ['$scope', function ($scope) {

  // 이메일 객체를 생성
  $scope.emails = {};

  // 서버에서 데이터를 받아온 것처럼 꾸며보자. 
  // 그냥 객체의 배열이다.
  $scope.emails.messages = [{
        "from": "Steve Jobs",
        "subject": "I think I'm holding my phone wrong :/",
        "sent": "2013-10-01T08:05:59Z"
    },{
        "from": "Ellie Goulding",
        "subject": "I've got Starry Eyes, lulz",
        "sent": "2013-09-21T19:45:00Z"
    },{
        "from": "Michael Stipe",
        "subject": "Everybody hurts, sometimes.",
        "sent": "2013-09-12T11:38:30Z"
    },{
        "from": "Jeremy Clarkson",
        "subject": "Think I've found the best car... In the world",
        "sent": "2013-09-03T13:15:11Z"
    }];

}]);

HTML에 이걸 넣을 필요 없다. 동적인 HTML 조각을 만들기 위해 애플리케이션이 무엇을 해야 하는지 선언하는 선언적 바인딩을 사용할 시간이다. Angular가 기본으로 제공하고 어떤 콜백이나 상태 변경 없이도 데이터를 순회하며 결과를 렌더링하는 ng-repeat 디렉티브를 사용해보자:

<ul>
  <li ng-repeat="message in emails.messages">
    <p>From: {{ message.from }}</p>
    <p>Subject: {{ message.subject }}</p>
    <p>{{ message.sent | date:'MMM d, y h:mm:ss a' }}</p>
  </li>
</ul>

결과:

date 필터 도 추가했으니 UTC 날짜로 그려주는 걸 볼 수 있을 것이다.

선언적 바인딩의 강력함을 더 확인하려면 Angular가 제공하는 ng-* 디렉티브를 공부해보자. 서버와 모델, 뷰, 데이터를 그려주는 부분이 어떻게 잘 조화되는지 알 수 있을 것이다.

Scope 함수

선언적 바인딩에 이어서 scope 함수도 사용하면 멋진 애플리케이션을 만들 수 있다. 이제 데이터에서 메일 중 하나를 삭제 하는 기능을 구현해보자:

myApp.controller('MainCtrl', ['$scope', function ($scope) {

  $scope.deleteEmail = function (index) {
    $scope.emails.messages.splice(index, 1)
  };

}]);

고급 팁: 모델에서 데이터 를 지우는 동작을 생각해보는 건 중요하다. 실제 DOM과 연관된 요소를 지우는 게 아니기 때문이다. Angular는 MVC 프레임워크로서 양방향 바인딩과 콜백없이 모든걸 처리해준다. 우리가 해줘야 할 일은 데이터에 반응하는 코드를 현명하게 작성하는 것 뿐이다!

ng-* 디렉티브를 통해 Scope에 함수를 바인딩해보자. 여기서는 ng-click 디렉티브를 사용한다:

<a ng-click="deleteEmail($index)">Delete email</a>

이 방법은 내부에 클릭 핸들러를 정의하는 것과 여러 가지 면에서 많이 다르다. 이유는 추후 살펴보기로 하자. $index 를 매개변수로 넘긴 게 보일 텐데 Angular가 어떤 메일을 지워야 하는지 알려주기 위함이다(얼마나 많은 코드와 로직이 필요없는지 보라!).

결과 (메일이 삭제된다!):

선언적 DOM 메서드

이제 DOM 메서드 로 넘어가보자. 역시 디렉티브이며 보통 스크립트 로직으로 작성해서 DOM에 기능을 제공하는 형태다. 이를 잘 설명할 수 있는 예제로 간단한 토글 네비게이션이 좋겠다. ng-showng-click 을 사용해서 깔끔한 토글 네비게이션을 만들어보자:

<a href="" ng-click="toggle = !toggle">Toggle nav</a>
  <ul ng-show="toggle">
    <li>Link 1</li>
    <li>Link 2</li>
    <li>Link 3</li>
</ul>

이 코드는 컨트롤러가 없는 MVVM을 의미하여 나중에 다시 살펴보도록 한다.

결과 (토글된다!):

표현식

Angular에서 마음에 드는 부분 중에 하나가 자바스크립트의 for문을 사용해서 반복되는 코드를 작성하는 부분이다.

혹시 이렇게 작성해본적 있지 않는가?

elem.onclick = function (data) {
  if (data.length === 0) {
    otherElem.innerHTML = 'No data';
  } else {
    otherElem.innerHTML = 'My data';
  }
};

이 코드는 데이터의 상태에 따라 DOM을 수정하는 코드로 GET 요청에 대한 콜백으로 사용될법하다. Angular를 사용하면 이렇게 자바스크립트를 따로 작성하지 않아도 이 코드를 충분히 구현할 수 있다!

<p>{{ data.length > 0 && 'My data' || 'No data' }}</p>

이렇게 작성하면 콜백없이도 애플리케이션에서 데이터를 풀링하거나 읽어온 뒤 자신을 동적으로 갱신한다. 데이터가 없으면 알려줄 것이고 데이터가 있어도 말해줄 것이다. Angular는 양방향 바인딩이라는 마법으로 이러한 경우를 모두 자동으로 처리해준다.

결과:

동적 뷰와 라우팅

단일 페이지 웹 애플리케이션(혹은 웹사이트!)에는 헤더, 푸터, 사이드바, 본문이 있고 URL에 따라 내용이 표시되는 게 보통이다.

Angular를 사용하면 동적 뷰 를 통해 이를 쉽게 설정할 수 있다. 동적 뷰를 사용하는 방법은 URL을 기준으로 $routeProvider 를 통해 특정 뷰를 얻어온 다음 적용하면 된다. 간단한 예를 살펴보자:

myApp.config(['$routeProvider', function ($routeProvider) {

  /**
   * $routeProvider
   */
  $routeProvider
  .when('/', {
    templateUrl: 'views/main.html'
  })
  .otherwise({
    redirectTo: '/'
  });

}]);

URL이 ‘/’ (사이트의 루트) ‘이면’ main.html 가 주입된다는 것을 알 수 있다. 초기 뷰로 index.html 대신 main.html 을 설정하는 게 좋은데 왜냐하면 index.html 페이지를 이미 단일 페이지 셋업에 사용했기 때문이다. 그리고 다른 URL에 대해서 뷰를 추가하는 것도 무척 쉽다:

myApp.config(['$routeProvider', function ($routeProvider) {

  /**
   * $routeProvider
   */
  $routeProvider
  .when('/', {
    templateUrl: 'views/main.html'
  })
  .when('/emails', {
    templateUrl: 'views/emails.html'
  })
  .otherwise({
    redirectTo: '/'
  });

}]);

이로서 이메일 목록을 보여주는 emails.html 을 간단하게 추가했다. 결국 매우 복잡한 애플리케이션을 아주 적은 노력으로 만들 수 있다는 것이다.

$routeProvider 서비스에는 공부할만한 게 더 많이 있지만 여러분의 몫으로 남겨둔다. 그리고 Ajax 호출이 진행 중일 때 이벤트를 보내는 $http 인터셉터같은 것도 있다. 새로운 데이터를 받아오는 동안에 로딩표시를 보여주는 용도로 사용할 수 있다.

전역 static 데이터

Gmail은 JSON으로 작성된 많은 양의 초기 데이터를 한 페이지에서 처리한다(오른쪽 클릭 - 페이지 소스 보기). 페이지에 데이터를 즉시 반영하고 싶으면 Angular를 사용해보자. 렌더링 속도까지 빨라질 것이다.

필자가 앱을 개발할 때는 자바 태그를 DOM안에 넣었고 렌더링될 때 서버로부터 데이터를 받아왔다. [필자가 Java 경험이 없어서 아래처럼 선언했지만 어떤 언어든 사용가능하다.] 다음은 페이지에 JSON을 작성해서 컨트롤러에 넣고 즉시 바인딩하는 방법이다:

<!-- index.html 내용 (물론 페이지 맨 아래) -->
<script>
window.globalData = {};
globalData.emails = <javaTagHereToGenerateMessages>;
</script>

페이지가 해석되는 동안 자바 태그가 데이터를 렌더링할 것이고 Angular는 이메일 목록을 즉시 렌더링 할 것이다. 이제 컨트롤러에 데이터를 넣어보자:

myApp.controller('EmailsCtrl', ['$scope', function ($scope) {

    $scope.emails = {};
    
    // 초기 데이터를 설정!
    $scope.emails.messages = globalData.emails;
    
}]);

압축

Angular 코드 압축에 대한 이야기를 해볼까 한다. 아마 자바스크립트 코드를 압축해본 적이 있을테고 이로 인해 오류가 난 적도 있을 것이다!

AngularJS 코드를 압축하는 건 쉽다. 함수 앞의 배열에 주입해야하는 의존관계만 잘 정의하면 된다:

myApp.controller('MainCtrl',
['$scope', 'Dependency', 'Service', 'Factory',
function ($scope, Dependency, Service, Factory) {

  // 코드

}]);

압축되고 나면 다음과 같다:

myApp.controller('MainCtrl',
['$scope', 'Dependency', 'Service', 'Factory',
function (a,b,c,d) {

  // a = $scope
  // b = Dependency
  // c = Service
  // d = Factory

  // $scope 별칭이 사용됨
  a.someFunction = function () {...};

}]);

주입하는 의존 객체의 순서에 주의하자. 순서가 달라지면 분명 여러분은 물론 팀에 골치아픈 일이 생길 것이다.

MVC와 MVVM의 차이점

AngularJS 포스트를 마무리 지으면서 AngularJS의 자부심인 MVC/MVVM의 차이점에 대해 간단히 다뤄볼까 한다:

  • MVC: 컨트롤러와 통신한다, 모델-뷰-컨트롤러

  • MVVM: 기술적으로는 자기자신과 통신하는 선언적 데이터 바인딩이다. 모델-뷰-뷰-모델. 모델은 뷰와 통신하고 뷰는 모델과 통신한다. Angular의 양방향 데이터 바인딩은 별다른 작업없이도 스스로 알아서 통신한다. 또한 컨트롤러없이 로직을 작성할 수도 있다!

예를 들어 다음은 데이터를 제공하는 컨트롤러없이도 ng-repeat 을 생성하는 예제다:

<li ng-repeat="number in [1,2,3,4,5,6,7,8,9]">
  {{ number }}
</li>

테스트해보니 잘 동작하긴 하지만 깔끔하게 작성하려면 항상 컨트롤러를 사용하길 추천한다.

결과:

HTML5 웹 컴포넌트

이전에 봤던것처럼 AngularJS에서도 사용자 정의 요소를 만들 수 있다:

<myCustomElement></myCustomElement>

이건 사실 HTML5의 미래를 웹에 구현한 것으로 Angular를 사용하면 HTML5의 웹 컴포넌트와 <template>요소를 비슷하게 사용할 수 있다. 웹 컴포넌트는 뷰를 생성하기 위한 동적 자바스크립트를 주입할 수 있는 사용자 정의 요소로 구성된다. - Angular를 사용하면 이 멋진 기능을 지금 구현할 수 있는 것이다. 즉 Angular는 이를 먼저 생각하고 다가올 웹 기술을 미리 검증한 것이다 - 경의를 표한다.

스코프 주석

내 생각에 스코프 주석은 업무를 도와주는 역할을 멋지게 해낸다. HTML에 다음과 같은 주석을 사용하는 것과 비교해보면 더욱 그렇다:

<!-- header -->
<header>
  Stuff.
</header>
<!-- /header -->

Angular를 소개할때면 DOM 대신 뷰와 스코프를 생각하라고 말하곤 한다. 고의로 컨트롤러간의 데이터를 공유하지 않는 한 스코프는 사실 말 그래도인 닫힌 범위 라서 다른 곳에서는 데이터를 접근할 수 없다. 따라서 한 스코프의 영역을 스코프 주석으로 구분하는 게 훨씬 도움이 된다:

<!-- scope: MainCtrl -->
<div class="content" ng-controller="MainCtrl">

</div>
<!-- /scope: MainCtrl -->

AngularJS 디버깅

Angular를 개발하고 디버깅할때는 구글이 추천하는 아주 멋진 크롬 확장 기능을 사용하면 좋다. 이름은 Batarang이고 여기서 받을 수 있다.

자 그럼, 즐거운 코딩되시길.

추가로 읽어볼 것들

2013년 회고

어느새 2013년이 지나갔구나. 한해 동안 겪은 많은 일들 중에 개발 관련된 부분만 회고해본다. 꼬꼬마 개발자는 이렇게 한해를 보냈구나 정도로 봐주시면 좋겠다.

Plus (잘한 점)

  • Client개발에서 Server개발로 옮겼다. 올해 가장 잘한일이라고 생각한다. 많이 배웠다. 확실히 해당 분야 업무를 하지 않으면 배움에는 한계가 있다. 더 굴러봐야지.
  • Angular.js 공부를 시작했다. 기술 하나를 진드근하게 파고 싶었다. 회사 주 업무가 back-end 라서 상대적으로 경험이 부족한 front-end 기술을 찾아보다가 너무 멋져서 바로 선택했다. 내년에는 본격적으로 더 열심히 파볼 생각.
  • 사내해커톤 입상. 대단한건 아니지만 머 그래도 해커톤에서 상받은 건 처음. 재밌는 경험이었다.

Minus (아쉬운 점)

  • 무지를 드러내는 일을 두려워 했다. 특히나 2013년에는 분야를 바꿔서 그런지 두려운 일이 너무 많았다. 그리고 그걸 숨겼다.
  • 부족한 실력에 마음이 조급했다. 어차피 30년은 코딩할꺼니 마음편히 먹자.
  • 구체적으로 정하지 않은 목표는 대부분 지키지 못했다.
  • 블로그를 거의 하지 않았다. 마치 저 멀리 구석에서 팔장끼고 아무말없이 서있기만 했다는 느낌. 2014년에는 더 많이 듣고 더 많이 말할 생각.
  • 회사일이 너무 정신이 없었다.

목표

  • 개발서적 8권
    목표의 반 밖에 읽지 못했다. 스터디가 뜸했던 이유도 있겠지만 팀을 옮기는 바람에 마음의 여유도 부족했다. 책을 읽으면서 내용을 정리하고 다 읽으면 블로깅하는 것도 잘 실천하지 못했다.

  • 일반서적 1권
    너무 편식하는 경향이 있어서 일반서적을 읽어보자 했건만 역시나 편식했다. 1권이라도 읽은 게 다행이랄까. (그것도 중고생 교양도서다;;)

  • 발표자료 3개
    스터디가 없어서 발표할 기회가 별로 없으니, 발표자료가 만들어지질 않는다. 그나마 사내 강의용으로 만든 자료들과 아꿈사 연말모임에서 발표하느라 만든 게 전부다. 아직 공개도 하지 못했다. 이런.

  • 개인 프로젝트
    시작만 이것저것해놓고 이렇다할 결과를 보인게 하나도 없다. 그래서 공개한 것도 없다. 결국 실패. 그래서 개인 프로젝트를 만들어 공개하는 게 대단한 것이다.

  • 영어
    영어실력이 너무 떨어진다는 사실을 가슴으로 깨달은 게 가장 큰 성과랄까. GrammerInUse를 반복해서 봤던 게 가장 좋았다. 영어가 개발과 무슨 상관?이라고 생각하시는 분이 혹시 있으시다면 지금이라도 늦지 않았다. 얼른 영어를 시작하시라. 누구처럼 땅을치며 후회하지말고 ㅜㅜ.

  • 언어. Javascript
    본격적으로 시작해본 새로운 언어. 서버쪽으로 옮기고 나서도 꾸준히 봤다. 내년에도 계속 이어서 볼 예정. 1년마다 새로운 언어를 익히라고 하지만 아직 난 그럴 수준은 아닌 듯하다. 진중하게 하나를 마스터하고 난 다음에 다른 걸 파는게 좋겠다. 2년마다 1개로 하자.

  • 스터디
    상반기에는 아꿈사 스터디가 별로 없었다. 돌이켜 생각해보면 다른 스터디라도 뛸걸 그랬나 보다. 아쉽다.

  • 블로깅 3개
    내가봐도 대단한 성적이다. -_- 이유가 무엇일까? 익숙하지 않은 게다. 의도적인 연습이 없어서 그런건지도 모른다. 생각날 때 글을 쓰지 않으면 영원히 쓸 수 없는거다. 좌절하지말고 찬찬히 시도해보자. 2014년에는 더 나아지겠지. 벌써 1개 작성하지 않았는가.

대부분 작년의 목표에서 수치만 변경하여 2014년 목표로도 이어서 하려고 한다. 그리고 올해 새롭게 도전해보려는 것들.

  • 생각하는 시간을 따로 마련하기
  • 기사 번역 10개
  • 오픈소스참여

2014년 회고에는 잘한 점이 더 풍부해지길 바래본다.

지금의 나는 그냥 툭 튀어나온게 아니다. 한해동안 이런저런 사건을 겪으면서 지금의 내 행동과 생각이 만들어진거다.

[책] Spring in Action

잘 모르는 분야로 진입할때는 그 분야에서 꽤 유명한 프레임워크를 시작점으로 삼는게 좋겠다. 왜냐하면 새로운 도메인을 이해하는데 있어서 그 지식을 기본바탕으로 사용할 수 있기 때문이다. 하나에 익숙해지면 같은 도메인의 다른 프레임워크도 한결 수월하게 익힐 수 있을 것이고 무엇보다 중요한건, 하나를 익혔다는 조그마한 자신감을 가질 수 있다는 점이다. 그래서 집어든 첫번째 프레임워크. 스프링이다.

처음부터 토비책을 볼까 했지만 엄청난 두께에 압도당해서 도저히 시작할 수가 없더라. 중요개념만 먼저 살펴보자는 생각으로 얇고 좋은 책을 찾으니 이 책이 나왔다.

제목그대로 스프링에 눈뜨기 좋은 책이다. DI, IoC, AOP, MVC, REST 등의 필수개념들을 최대한 쉽게 설명하고 있고 특히 초반 DI, IoC를 적절한 은유로 설명하는게 마음에 들었다. 간단한 프로젝트로 스프링 MVC 를 설명하는것도 좋았다. 예제들이 개념익히기에는 충분하지만 실제로 사용하기에는 뭔가 부족한 느낌이라 실용적인 예제가 많은 책을 함께보는것도 좋겠다.

다 읽고 나니 스프링에 대해서 어느정도 알게된 느낌이다. 하지만 확실히 어렴풋하다. 스프링을 처음 시작하는 사람에게 딱 좋은 책이지만, 이책만 봐서는 안되겠다.

간간히 어색한 문장이 나오긴 하지만 번역은 잘된 편. 이해하는데는 무리가 없다.

읽으면서 떠올랐던 스프링에 대한 생각들을 두서없이 나열해본다. 스프링에 대한 첫인상인 셈.

  • DI 와 AOP 라는 개념이 익숙하면서도 새롭게 느껴진다. 도메인이 달랐던것 뿐인데 새로운 세상이 있구나. 어느 도메인에서나 써먹을 수 있는 core 지식을 더 갖고 싶다.
  • 신선하다. SpEL. 스프링안에 스크립트 언어. 반지의 제왕 작가가 소설을 위한 새로운 언어를 하나 만든 얘기가 생각났다.
  • 스파게티 코드가 될 수 있는 가능성을 Spring 프레임워크에서 최대한 막아주는 느낌.
  • 적절히 등장하는 은유가 참 좋다. 나도 나중에 책을 쓰게 된다면 가능한한 은유를 많이 써야겠다. 이해하기 너무 좋네.
  • AOP 는 해킹의 느낌이 강하다. 중간에 내가 원하는 코드를 실행하게 만들다니 말이야.
  • 확실히 스프링 프레임워크는 우아하다. 프레임워크를 만들어야 한다면 스프링의 우아함이 좋은 출발점이 되겠다.
  • 스프링으로 간소화된 Java 코드는 마치 script 언어나 함수형 언어처럼 보인다.
  • 분명히 server 도메인에 특화된 프레임워크인데도 도메인과 상관없는 순수한 Java 로 보이기위해 아니 정말로 순수해지기 위해 노력을 많이 하는구나. 그래서 간결하고 강력하다.
  • 편하게 사용하기 위해서 빈 ID 를 특정한 이름으로 설정해야하는등의 눈에 잘 띄지않는 규약이 존재한다는건 조금 별로다.
  • 스프링 웹 플로는 마치 레고블럭을 조립하는것 같다.
  • 스프링에서 제공해주는 xml 을 설정하면 무언가 저절로 쨘하면서 돌아간다. 마치 wizard 를 사용해서 프로젝트를 만드는 느낌이다. 안에서 어떻게 돌아가는건지는 하나도 모르니 답답하다.
  • 모든것을 POJO 로 만들고 dependency 는 xml 로 최대한 처리하자. 이왕이면 자동으로.
  • 최대한 generalize! 세부적인 impl 이 달라져도 그대로 사용할 수 있도록 generalize
  • 그냥 책만 한번 읽을때는 몰랐는데, 예제로 구성된 다른책을 한번 보고 Toy project 를 진행하면서 다시 보니 새롭다. 이런. 예제가 있는 책과 함께 보는게 좋겠다.
  • 새로운 생각은 알고 있는 지식을 바탕으로 나온다. 여러가지 프레임워크를 더 경험해보자.

걸린시간

예상뽀모도로는 18번. 실제 걸린 시간은 세상에 32번. ( 32 x 30 min = 16시간 )

처음 접하는 개념들이 많았으니 오래걸릴 수 밖에. 처음 추정할 때 고려했었어야 했다.

흰 띠를 매다

한동안 매너리즘에 빠졌었다. 무언가 정신없이 일은 하고있는데, 재미는 없었다. 익숙한 일만 반복적으로 하고있는 나를 발견하고 이건 뭔가 아닌것 같다는 느낌이 들었다. 그러다 우연히 다시 보게 된 견습생 패턴 중 하나.

“어떤 전문가들은 특정한 분야에 계속 머물 수 있다면 무엇이든 하려고 하며, 자신의 학습, 실무, 프로젝트의 범위를 점점 좁혀만 간다. 하지만 장인이라면, 익숙하지 않은 기술분야나 새로운 업무영역을 배울때 자신의 전문기술을 옆으로 밀어두고 흰띠를 매는 용기와 겸손을 가질 필요가 있다.” - 프로그래머의 길, 멘토에게 묻다

“Some experts will do everything they can to remain wedded to a single context, narrowing the scope of their learning, their practice, and their projects. Craftsmen, on the other hand, need to have the courage and humility to set aside their expertise and wear The White Belt as they pick up an unfamiliar technology or learn a new domain.” - Apprenticeship Patterns

분명 나의 세계는 좁아지고 있었다. 특정 분야에 머무르지 말고 달라져야겠다 생각했다. 물밑작업을 시작했고, 운좋게도 아무 지식이 없는 상태에서 결국 팀 옮기기에 성공했다.

용기를 낸 덕분에 팀은 옮겼지만 아는게 하나도 없으니 답답했다. 지식이 부족하니 예전만큼의 속도가 나지 않았고, 간단해보이는것도 해결하지 못했다. 조금만 하면 금방 따라잡을 것이라는 생각에 2달동안 허겁지겁 혼자 책을 찾아봤다. 하지만 생각보다 지식은 늘지 않았다. 지식을 소화시키지도 않은채 많은 양을 그냥 삼키고만 있었다.

조급했다.

이렇게 간단해보이는것도 모르다니! 부끄럽다. 분명 내가 알고 있는 무언가와 닯아있지 않을까? 사실은 내가 알고있는 것인데 알아채지 못한건 아닐까?

이런 생각으로 무지를 드러내는 일이 머뭇거려지곤 했다. 물어보지말고 혼자 찾아보면 된다는 마음이 앞섰다. 아. 뭔가 이상하다 싶었다.

“장인에게 가장 중요한 특징 중 하나는 학습하는 능력, 즉 무지의 영역을 파악해서 이 영역을 줄이려 애쓰는 것이다. 당신은 그 맨땅의 크기에 당혹감을 느낀 나머지, 보이지 않게 숨겨두는 편을 택하고 자존심을 지킬수도 있을 것이다. 아니면 자신과 자신을 믿는 사람들에게 정직하게 그것을 드러내 놓고 도움을 요청하는 편을 택할 수도 있다.” - 프로그래머의 길, 멘토에게 묻다

“One of the most important traits that a craftsman can possess is the ability to learn, identifying an area of ignorance and working to reduce it. Like bare patches in a garden, ignorance can be reduced by cultivating your seeds of knowledge. Water your seeds through experimentation, practice, and reading. You can choose to hide these bare patches from the light, embarrassed by their size, covering them to keep your pride intact. Or you can choose to expose them, being honest with yourself and the people who are depending on you, and asking for help.” - Apprenticeship Patterns

아. 맨땅의 크기가 너무 커서 당혹스러웠구나.

그래. 질문하자. 모른다고 이야기하자. 아무것도 알지못하는 초심자의 마음으로 돌아가서 흰띠를 매어보자.

“새로운 상황에 들어설때는, 학습을 통해 얻은 자신감은 그대로 두면서 이전에 얻은 지식은 한편으로 밀어두어라. 배웠던 것은 잊어버려야 한다.” - 프로그래머의 길, 멘토에게 묻다

“While retaining the confidence you have gained through your learning, set your previous knowledge aside as you approach new situations. As Yoda so wisely says in The Empire Strikes Back, “You must unlearn what you have learned.”” - Apprenticeship Patterns

기존의 지식을 알고있다는 생각이 새로운 것을 배워야하는 나를 가로막고 있었는지도 모르겠다. 지식이 많지도 않으면서 이런 감정을 느끼는 내가 참 우습기도 하지만 -_- 일단은 다 잊어버리고 마음편히 시작해보자. 그리고 조바심내느라 신경쓰지도 못했던 자신감이란 아이도 다시 찾아와보자.

성과를 내야한다는 조급한 마음은 잠시 내려놓고, 배우는 이 과정을 한번 즐겨봐야겠다.

화이팅.

[책] 피플웨어

회사를 다니다보니 원치않는 관리업무를 해야할때가 종종 있다.

개발자와 관리자는 엄연히 다른 분야다. 각자의 전문 영역이 있으니 동시에 할 수 없다. 뭐 어떻게든 할수는 있겠지만 둘 다 잘하기는 불가능에 가깝다.

개발자와 관리자 사이에서 오랜시간동안 고민한 결과 난 개발자의 길을 걷기로 했다.

잡스형님이 추천한 방법대로 꾸준히 질문을 하고 연속으로 나오는 대답을 보면서 판단했는데 꽤 오랜시간 생각했지만 관리자를 택한 적이 한번도 없었다. 그만큼 심사숙고해서 내린 진지한 결정이랄까.

개발자의 길을 걷기로 했으니 이제 관리자의 지식은 필요없다고 생각했다. 하지만 이 책을 읽고 생각이 달라졌다.

“프로젝트와 팀의 사회학은 프로그램 개발자의 전문영역에서 약간 벗어나 있을지는 모르지만 능력을 넘어서는 영역은 아니다”

관리자의 지식을 모를 필요는 없구나. 아니 더 알아야하지 않을까? 그래야지만 좋은 관리자와 일할 수 있으니까 말이다. 좋은 관리자와 나쁜 관리자의 차이가 궁금해졌다. 관리자들은 무슨 공부를 해야하는 걸까.

웬디는 책상 위로 발을 뻗은 채 허공을 쳐다보고 있었다. 상사가 들어오더니 이렇게 물었다. “웬디! 지금 뭐하고 있는 건가?” 웬디는 말했다. “생각 중인데요.” 그러자 상사가 말했다. “그거 집에 가서 하면 안 되겠나?”

이렇게 반응하는 상사가 얼마나 많겠는가. 눈치보여서 책 조차 보지 못하고 항상 모니터에 코를 박고 일하는 모습을 보여야하는 현실이 서글프다.

좋은 관리자를 판단하기에 더할나위없이 좋은 지침서다. 함께 일하는 내 관리자에게 이책을 꼭 추천하자. 읽으라고 한권 사주자. 그래도 읽지 않을꺼라는 사실이 조금 슬프긴 하지만!

값진 내용이 너무나 많아서 내용 요약은 포기하고 인상깊은 문장만 정리했다. 꼭 책을 사서 읽어보시길 권한다.

기술문제가 아니라 사람 문제다
- 프로젝트와 팀의 사회학은 프로그램 개발자의 전문영역에서 약간 벗어나 있을지는 모르지만 능력을 넘어서는 영역은 아니다.
- 사람들이 업무의 인간적인 측면보다 기술적인 측면에 주로 매달리는 가장 큰 이유는 기술적인 부분이 더욱 중요하기 때문이 아니라 거기에 매달리는 것이 훨씬 더 쉽기 때문이다.

햄버거 마인드
- 만들어서 팔기만 하면 된다는 식의 햄버거 마인드는 개발분야에서는 치명적인 결과를 야기할 수 있다.
- 실수를 허용하지 않는 분위기로 인해 평균적인 기술 수준은 약간 향상될지도 모르지만, 팀은 위기에 처하게 될 것이다.
- 스스로의 동기가 아니라 상사가 강요한 동기에 의해 일한다는 사실만큼 직원들의 사기를 떨어뜨리는 것은 없을 것이다.
- 일반적인 소프트웨어 개발자들은 자기가 맡은 주제에 대한 책을 한 권도 가지고 있지 않으며 있다 하더라도 제대로 읽지 않는다.

진정한 생산성의 의미
- 초과 근무한 시간만큼 직원들은 일을 다소 덜한다고 보면 된다.
- 자신이 덜 중요한 가치(일)를 위해 더욱 중요한 가치(가족, 사랑, 집, 젊음)를 희생해 왔다는 것을 깨닫게 되면 그의 마음은 분명 황폐해질 것이다.
- 시간에 쫓기며 일하는 사람들은 일을 더 잘 하는 것이 아니라 단지 더 빠르게 일할 뿐이다.

여유 시간이 있어야 품질을 따진다
- 일터에서 감정을 자극하는 주된 원인은 자존심을 상하게 하는 것들이다. 사람들은 자존심을 자신이 생산하는 제품의 질과 관련시키는 경향이 있다. 관리자가 제품의 질을 떨어뜨릴 수 있는 조치를 취하면, 직원의 감정은 관리자와 대립하게 될 것이다.
- 품질은 무한정이지만 그것은 품질 향상에 많은 비용을 투자할 용의가 있는 사람들에게만 유효하다.

다시 본 파킨슨 법칙
- 파킨슨 법칙. 업무는 그에 할당된 시간만큼 늘어지는 경향이 있다. 땡!
- 회사 일정에 쫓겨 일을 해야 할 때 직원들은 근무 시간만 채우도록 일을 늘이는 경향이 있다.

만병통치약은 없다
- 관리자가 진정 해야 하는 일은 사람들에게 일을 시키는 것이 아니라 그들이 일에 전념할 수 있는 환경을 만들어 주는 것이다.

비생산적인 작업 환경
- 시설 감시자적인 사고 방식을 가진 관리자들은 감옥을 설계하는 방식으로 사무 공간을 설계한다.

도대체 여기선 일할 수가 없어요
- 가장 업무 능력이 뛰어난 사람들은 가장 업무 능력이 떨어지는 사람보다 10배쯤 뛰어나다.

사무실 시설비 아끼기
- 소음은 1인당 할당 면적에 정비례한다. 그러므로 각 개인의 할당 면적을 반으로 줄인다면 소음도는 2배로 증가한다. 면적의 차이는 생산하는 제품의 결함도의 차이로 이어진다.

머리로 일한 시간, 몸으로 일한 시간
- 일에 정신 없이 집중하고 있을 때, 사람들은 심리학자들이 ‘flow’라고 부르는 이상적인 상태에 빠지게 된다. 플로는 한 가지에 깊이 집중하여 거의 명상 상태에 빠지는 것을 의미한다. 이 상태는 도취 상태와 어느 정도 비슷하기 때문에 이 상태에 빠진 사람은 시간의 흐름을 거의 의식하지 못한다.
- 웬디는 책상 위로 발을 뻗은 채 허공을 쳐다보고 있었다. 상사가 들어오더니 이렇게 물었다. “웬디! 지금 뭐하고 있는 건가?” 웬디는 말했다. “생각 중인데요.” 그러자 상사가 말했다. “그거 집에 가서 하면 안 되겠나?”

전화로부터 벗어나자
- 어떤 혁신적인 도구보다도 중요한 것은 태도를 바꾸는 것이다. 사람들은 때때로 전화를 받지 않아도 된다는 것을 배워야 하며 시간이 (시간의 양이 아니라 질이) 중요하다는 사실을 알아야 한다.

사무실에 다시 문을 달자
- 직원들의 소음에 대한 불만 사항에 대한 해결책은 그 원인을 해결하거나 소음이 안 들리게 만드는 것이다.
- 일상적인 업무의 대부분은 좌뇌의 연쇄 처리 센터에서 행해진다. 음악은 특별하게 이 작업을 방해하지는 않는다. 왜냐하면 뇌의 오른쪽이 음악을 소화하기 때문이다. 우뇌가 배경 음악을 듣느라 바쁘다면 창의적인 도약 과정이 생겨날 기회는 사라진다.

진화하는 업무 공간
- 당신의 업무 그룹 전체를 회사 건물 밖으로 옮겨 달라고 요청하라. 그리고 나서 직원들이 자신의 공간을 적절히 배치하고 찾아내도록 하라.

혼블로워 효과
- 관리자가 자신이 원하는 대로 부하 직원을 변화시키기는 쉽지 않아 보인다. 처음에 어떤 업무에 부적합한 인물은 앞으로도 결코 적합한 인물이 되지 못할 것이다. 따라서 가장 중요한 것은 처음부터 적합한 인물을 선택하는 것이다.

직원을 제대로 뽑으려면
- 비즈니스는 사람과 기계 간의 커뮤니케이션 능력보다는 대인 커뮤니케이션 능력에 더욱 의존한다. 따라서 적어도 채용 과정에 있어서 사회학적이고 인간적인 커뮤니케이션 기술에 초점을 맞출 필요가 있다.
- 오디션 날짜를 정하고 지원자와 같이 일하게 될 동료들로 소규모의 청중을 구성한다. 채용은 전적으로 당신의 결정이지만, 나중에 같이 일하게 될 동료들의 의견은 매우 중요한 것이 될 수 있다.

여기서 일하는 것에 만족합니다
- 높은 이직률을 지닌 회사의 경우, 사람들은 심각하게 근시안적인 성향을 띤다. 왜냐하면 그들은 회사에 그리 오래 머무르지 않을 것이라고 생각하기 때문이다.
- 왜 사람들은 이직을 하는가. 자신이 외부인에 불과하다는 느낌, 일회용에 불과하다는 감정. 누가 직원을 부품으로 보는 조직에 충성을 다하겠는가

자가 수정 시스템
- 변화 자체가 중요한게 아니라 변화를 가져오는 행동 자체가 더 중요하다는 것이다.

전체는 부분의 합보다 더 크다
- 직원들이 조직의 목적을 자동적으로 받아들일 것이라고 믿는 것은 어리석은 낙관주의의 표출이다.
- 팀의 목표는 목표의 달성이 아니라 목표의 일치이다.
- 단결된 팀에는 몇가지 뚜렷한 특징이 있다. 그 중 가장 중요한 것은 프로젝트 기간 동안에는 이직률이 낮다는 것이다. 그들에게는 팀의 생산물에 대한 공동 소유의 감정이 존재한다.
- 팀원들이 즐겁게 일한다는 것은 팀이 단결되었다는 것을 보여주는 결정적인 신호이다. 그러한 팀은 건강하고, 상호 작용이 쉽게 일어나고 확신에 차 있으며 따뜻하다.

블랙팀의 교훈
- 검은 옷을 입고 우스꽝스럽고 과장된 행동을 하는 것은 단지 그들이 느낀 재미의 일부였다. 하지만 보다 본질적인 것은 따로 있었다. 팀 내의 공감대 형성이라는 궁극적 목표를 그들이 이루어 냈다는 것이다.
- 이러한 현상은 계속 반복되어 지금은 초기의 구성원이 한 명도 남아 있지 않다. 하지만 여전히 블랙팀의 정신은 그대로다.

팀 죽이기
- 직원들이 스스로 노력해봤자 인정받지 못한다고 믿게 되면, 그것은 바로 당신이 그들을 믿지 않는다는 것을 크게 외치는 것이나 다름없다. 그것처럼 팀 형성을 방해하는 것도 없다.
- 품질이 저하된 제품. 싸구려 물건을 개발하고 있는 동료들은 서로의 얼굴을 똑바로 쳐다보려 하지도 않을 것이다. 일이 끝나갈 떄쯤이면 서로 빨리 헤어지지 못해 안달일 것이며, 좀 더 나은일을 하기 위해 떠나갈 것이다.
- 데드라인을 반드시 지켜야 한다고 강요하면 할수록 그 일은 이미 성공하기 어려움을 예고하는 것이나 다름없다. 직원들이 받아들이는 메시지는 다음처럼 명확하다. “우리 상사는 우리를 존중하거나 배려하는 마음이 눈꼽만큼도 없다. 그는 우리가 협박을 받아야만 일을 한다고 생각해”
- 대부분의 조직들은 의식적으로 팀을 죽이려 하지 않는다. 단지 조직의 운영방식이 그럴 뿐이다.

스파게티 회식
- 새 프로젝트를 같이 하게 될 사람들끼리 모임을 갖는다는 것이다. “같이 스파게티를 만들어 먹읍시다” 아직까지 프로젝트에 관한 일은 전혀 시작도 하지 않았지만, 벌써 당신의 팀은 첫 번째로 팀의 성공을 거둔 셈이다.

서로 신뢰하는 문화
- 자아를 존중할 수 없게 만드는 일터는 그 자체로 이미 병든 것이다.
- 뛰어난 관리자들은 관리 분야에 대해 자신이 갖고 있는 자연스러운 권위를 보여 줌으로써 사람들을 관리한다는 뜻이다. 장인과 도제의 차이는 자연스러운 권위를 갖고 있느냐 없느냐의 차이이다.
- 복종이라는 수단에 어쩔 수 없이 기대게 되는 관리자는 스스로 자연스러운 권위를 갖고 있지 못하기 때문에 그렇다.

팀 형성을 위한 공감대 형성
- 이런 말은 관리자로서 당신의 감정이 상할지도 모르지만, 관리자는 본질적으로 그들이 관리하는 팀의 일원이 아니다. 팀은 동등한 개인으로서 기능하는 동료들로 이루어진 집단이다.
- 좋은 팀에서는 각 개인이 특히 잘할 수 있는 분야에서 돌아가며 리더쉽을 발휘한다. 한 사람이 계속 리더 역할을 하지 않는 이유는, 그렇게 되면 그 사람은 더 이상 팀원들의 동료가 아니게 되어 팀의 상호 교류가 깨어지기 때문이다.

때론 무질서가 필요하다
- 관리자들은 모든 무질서를 싹 없애 버리는 것이 자기 일이라고 생각하는 것이다. 하지만 상호 신뢰를 중시하는 관리자들은 다르다. 무질서한 상태를 직원들이 처리하도록 일부러 나눠 주는 것이다. 그렇게 함으로써 직원들은 스스로 무질서한 상태를 질서정연한 것으로 만드는 재미를 느끼게 된다.
- 4년간 모의 코딩 대회를 열면서 우리는 때때로 소란스럽고 치열한 경쟁 속에서 져서는 안 되는 상황을 경험해 보게 하는 것이 건설적인 취지의 무질서를 기업에 도입할 수 있는 좋은 방법임을 깨달았다.

자유 전자적 속성을 지닌 사람들
- 이들은 상부에서 내리는 지시보다는 바로 자신이 세운 방향에 따라 일을 추진하는 것이 훨씬 더 회사의 이익에 도움이 되는 쪽으로 일을 해왔던 것이다. 이들이 자유롭게 나아갈 수 있도록 길을 터 주어야 할 때이다.

잠자는 거인을 깨워라
- 하나의 변화를 시도하는 것만으로도 충분하다. 회사 조직에 단 하나일지라도 참다운 변화를 가져올 수 있다면 그것은 커다란 성과인 것이다.
- 회사 내의 불합리함이 도를 넘었다면 직원들은 약간의 자극만 받아도 그것을 의식하게 된다. 작은 목소리로 이건 정말 말도 안돼 라고 누군가 말하기만 해도 충분하다. 일단 공개적으로 그런 얘기를 꺼내면 더 이상 그것을 무시할 수 없을 것이다.

다시 본 팀 죽이기
- 사람들이 초과 근무를 하는 이유는 과제를 주어진 시간 안에 끝마치기위해서라기보다는, 일을 정해진 기간까지 끝마치지 못했을 때 비난 받게 될 것을 우려해 자신을 보호하기 위함이다. (제리 와인버그)

과도한 경쟁은 단결을 해진다
- 같이 일해야 하는 팀 구성원들 간에 경쟁 의식이 심해지면 장기적으로 어떤 효과가 생길까? 동료끼리 업무에 대해 지도해주는 문화가 사라질 것이다.
- 우리는 대부분의 업무에 대한 지도 과정이 바로 동료들 사이에서 이루어지고 있다는것을 깨달았다.
- 전체 합창단의 노래에 틀린 음정이 있는데 혼자만 맡은 부분을 완벽히 했다고 칭찬해 주는 사람은 아무도 없을 것이다. 그러므로 뒤늦게나마 우리는 음악 앙상블이 스포츠 팀보다는 우리가 말하려고 하는 단결된 업무 팀의 이미지에 더욱 잘 부합하는 것이라고 해야겠다.

프로세스 개선 프로그램
- 가장 해볼 만한 가치가 있는 프로젝트는 당신 회사의 프로세스 레벨을 완전히 한 등급 낮춰 줄 그런 프로젝트이다.

변화를 두려워하지 말라
- 당신이 일으키려는 변화의 성공은 ‘신뢰는 하지만 의문을 제기하는 사람들’을 어떻게 다루느냐에 달려 있다.
- 변화에 대한 사람들의 반응은 본질적으로 이성적인 것이 아니라 감정적인 것이다.
- 전혀 변화하지 않는다면 발전은 없다.
- 처음 스키를 배우는 어른들과 아이들을 잘 관찰해 보면, 어른은 다치는 것보다도 실수로 인해 다른 사람들의 웃음거리가 될까 봐 더 신경을 쓰고 있다는 것을 알 수 있다. 아이들은 그런 생각을 거의 하지 않는다.

사람에게 투자하자
- 몇 천명의 직원을 해고하면 그들에게 지급해야할 임금을 절감하게 된다는 것이다. 이러한 경영 서적들의 분석이 간과하고 있는 것은 회사가 직원들에게 투자한 비용이다.
- 지식 노동자들로 구성된 회사들은 가장 중요한 것이 인적 자본에 대한 투자임을 깨달아야 한다. 좋은 회사들은 항상 사람에게 투자한다.

조직 학습이 필요하다
- 핵심적인 조직 학습이 상부에서도 일어나지 않고 말단에서도 일어나지 않는다면 당연히 회사 조직의 중간 위치에서 일어날 것이다. 즉 가장 욕을 많이 먹는 집단인 중간 관리자층이다.

궁극적으로 관리자가 저지르는 죄
- 바로 직원들의 시간을 낭비하게 한다는 것이다.
- 회의 참석자들이 한 명의 중심 인물과 순서대로 의견 교환을 한다면 전체 인원을 모아 놓은 이유는 사라져버린다.
- 업무 상황을 보고받기 위해서 굳이 회의를 열 필요는 없었다. 관리자는 정보를 알기 위해서가 아니라 자기 권위를 확신하기 위해서 회의를 연 것이다.
- 관리자가 프로젝트에 필요한 분석 작업과 설계 작업을 수행할 때 적은 수의 인원을 배치하면 사내에서 그의 위치가 불안정해진다는 것이 일반적인 현상이라는 사실은 정말 서글픈 현실이 아닐 수 없다.

공동체 형성
- 우리가 공동체를 찾을 수 있는 가장 큰 기회는 일터에 있다. 만약 찾을 수 있다면 말이다.
- 모두가 만족할만한 공동체를 건설한 회사 직원들은 떠나지 않는다.

걸린시간

예상뽀모도로는 10번. 실제 걸린 시간은 8번

[발표자료] Dependency Breaking Techniques

24 Dependency Breaking Techniques from LegacyCode.

예전에 레거시코드 활용전략을 읽을 때 한번 정리하면 좋겠다 싶었는데 계속 미루다가 아꿈사 스터디 발표하는 김에 정리해보았다.

일단 내용을 알아야 어느 상황에 사용할 수 있을지 알테니 내용을 먼저 정리해보고 싶었다. 그래서 when to use 가 조금 부실하다. 생각날때마다 보강할 예정.

그리고 technique 하나를 그림 하나로 표현해보려 노력했는데 역시나 쉽지 않았음 ㅜㅜ

정리하면서 내린 결론은 LegacyCode 와의 싸움은 역시 쉽지 않다. 따라서 억지스럽고 지저분한 방법일지라도 대안이 없다면 그게 바로 최선의 방법이라는 것.

[책] 프로그래머가 알아야 할 97가지

프로그래머가 알아야 할 97가지. Collective wisdom from the experts

전문가가 들려주는 97가지 시리즈중 프로그래머 버전이다.

개인적으로는 번역에 참여한 두번째 책.

참 다양한 이야기들을 들려준다. 이야기가 97개나 있으니 좋은 이야기도 있고 공감이 잘 안되는 이야기도 있다. 그런데 신기하게도 97 가지의 이야기를 찬찬히 듣다보면 그들이 하는 얘기중에 무언가 공통된 점을 발견하게 된다.

바로 개발자가 가져야하는 마음가짐이다.

지속적인 학습
완벽한 코드를 작성하겠다는 장인정신
모두를 위한 배려
훌륭한 프로그래머가 되겠다는 마음

집필에 참여한 많은 사람들이 이렇게 쓰자고 미리 정한 것도 아닐텐데 말이다. 결국 Expert로서 후배들에게 이야기해주고 싶은 내용을 꼽으라면 비슷한 내용이 나온다는 얘기다. 이것이 이 책에서 내가 생각하는 가장 중요한 포인트다.

“소프트웨어 회사에서의 다년간의 경험으로, 저는 적당한 프로그래머와 훌륭한 프로그래머 사이의 진짜 차이의 대한 결론을 내렸습니다. 바로 마음가짐입니다”

차이를 만드는 것은 마음가짐이다. 능력이 아니다.

초보 프로그래머라면 좋고 나쁘고를 판단하기 전에 먼저 이러한 마음가짐을 한번 가져보자. 한창 배우는 중인 견습 프로그래머라면 바쁜 일정과 여러가지 변명들로 마음 한구석에 쟁여놓았던 마음가짐을 다시 한번 꺼내보자. 전문가라면 이 마음가짐을 주변사람들에게 스물스물 전파해보자.

미약하나마 이 책을 통해 이러한 일들이 벌어진다면 참 좋겠다.

한줄 요약과 함께 마음에 드는 글은 추천(★)표시를 해두었으니 시간이 없으신 분들은 추천글만이라도 꼭 읽어보시길!

참고로 원문은 모두 공개 되어있다.

1. Act with Prudence by Seb Rose
- 기술적 부채를 가능한한 빨리 갚을것. 안그러면 이자가 엄청날 것임.

2. Apply Functional Programming Principles by Edward Garson
- 함수형 프로그래밍의 맥락을 깊이 이해하라.

3. Ask “What Would the User Do?” (You Are not the User) by Giles Colborne
- 사용자를 관찰해보자. 그들이 어떻게 생각하는지 알게 될 것이다.

4. Automate Your Coding Standard by Filip van Laenen
- 코드는 혼자만의 것이 아니다. 우리 모두의 코드이기 때문에 코딩표준이 필요하다. 자동화하지 않으면 녹아들기 힘듬.

5. Beauty Is in Simplicity by Jørn Ølmheim
- 단순한 코드가 아름답다. 간단한 코드가 우아하다.

6. ★ Before You Refactor by Rajith Attapattu
- 리팩토링은 좋은 것이지만 하기전에 한번 생각해보자.
- “코드의 스타일과 구조가 여러분의 개인적 선호도를 충족시키지 못한다는 것은 리팩토링의 정당한 사유가 될 수 없습니다”

7. Beware the Share by Udi Dahan
- 코드를 공용화할때 맥락을 고려해야 한다. 즉 의존성이 높아진다면 안하느니만 못하다.
- “제가 만든 공용 라이브러리들은 한 발의 신발끈을 다른 발에 묶어 놓은 것과 같이 서로에 대한 의존도가 높았습니다”

8. ★ The Boy Scout Rule by Uncle Bob
- 체크아웃할 때 보다 더 개선해서 체크인! Clean Code!

9. Check Your Code First before Looking to Blame Others by Allan Kelly
- 아니 이게 무슨소리오. 당신 코드는 완벽하고 컴파일러가 문제라니.

10. Choose Your Tools with Care by Giovanni Asproni
- 툴을 사용하자. 좋은거 많다. 선택은 주의해서.

11. ★ Code in the Language of the Domain by Dan North
- 암묵적 규칙을 남발하지 말고 도메인 용어. 즉 알아들을 수 있는 용어로 코딩하라.

12. Code Is Design by Ryan Brush
- 설계가 중요하다.

13. Code Layout Matters by Steve Freeman
- 코드 레이아웃은 생각보다 훨씬 중요하다.
- “우선 팀원들로 하여금 기본적인 자동 포맷터를 사용하지 않도록 해야합니다. 그리고 여러분 또한 도구의 도움을 받지 않고 스스로 레이아웃을 구성할 수 있어야 합니다”

14. ★ Code Reviews by Mattias Karlsson
- 코드리뷰는 반드시 해야한다. 지식공유를 위해. 어떻게? 상냥하게. 거부감 없이 편하고 재미있게. 비판적이기보다는 건설적으로.

15. Coding with Reason by Yechiel Kimchi
- 코딩할때 주의해야할 것들.

16. A Comment on Comments by Cal Evans
- 주석도 쓸모있을 때가 있다. 그치만 주석이 없을정도로 읽기쉽게 코딩하는게 낫다.

17. ★ Comment Only What the Code Cannot Say by Kevlin Henney
- 주석을 코드처럼 다뤄야 한다. 따라서 당연히 코드와 중복되면 안된다. 즉 코드가 말하지 않는 것을 주석으로 설명해야 한다.

18. ★ ★ Continuous Learning by Clint Shank
- 지속적인 배움은 스스로에게 달려있다. 강추.

19. Convenience Is not an -ility by Gregor Hohpe
- API를 구현할때의 편의성보다는 API를 사용할때의 편의성이 더 중요하다.

20. Deploy Early and Often by Steve Berczuk
- 릴리즈와 설치 프로세스도 미리미리 신경쓰자.

21. Distinguish Business Exceptions from Technical by Dan Bergh Johnsson
- 기술적인 예외와 비지니스 로직에 의한 예외를 따로 처리하자.

22. ★ Do Lots of Deliberate Practice by Jon Jagger
- 수련은 의도적인 반복이다. 수련의 목적은 능력 향상이다.
- “전문성을 개발하기 위한 핵심은 단순히 무엇인가를 반복하는 것이 아니라 여러분의 능력을 넘어서는 일에 도전하고, 그 일을 열심히 하고, 그 일을 계속하면서 성과를 계속 분석해 보고 실수를 고쳐 나가는 것”

23. Domain-Specific Languages by Michael Hunger
- DSL 이란게 있다.

24. ★ Don’t Be Afraid to Break Things by Mike Lewis
- 오믈렛을 만들때 달갈깨는 것을 두려워하지 마라. 리팩토링으로 인해 코드가 잠시 불안정해지는것을 두려워하지 마라.
- “숙련된 외과의사는 수술을 위해 절개가 필요하지만, 이것은 일시적이며 곧 봉합되어 회복될 것이라는 사실을 알고 있습니다. 여러분의 코드를 수술하는 것을 두려워하지 마십시오”

25. Don’t Be Cute with Your Test Data by Rod Begbie
- 장난삼아 넣어놓은 임시 데이터를 고객이 본다면?

26. Don’t Ignore that Error! by Pete Goodliffe
- 에러는 보일때 잡자. 안그러면 후회한다.
- “여러분이 에러를 무시했다면, 여러분은 장님이 되는 것입니다. 더 나쁜일이 일어나지 않을 것이라고 스스로를 속이는 것과 같은 것입니다”

27. Don’t Just Learn the Language, Understand its Culture by Anders Norås
- 새로운 언어의 사고방식을 배우자.

28. Don’t Nail Your Program into the Upright Position by Verity Stob
- 너무 많은 예외처리로 예외를 먹어버리지 말자.

29. Don’t Rely on “Magic Happens Here” by AlanGriffiths
- 프로젝트에는 다양한 역할이 필요하다. 역할 하나를 없애고 잘 돌아가길 기대하는건 기적을 바라는거다.

30. ★ Don’t Repeat Yourself by Steve Smith
- 프로세스도 중복을 피하자. DRY, OCP, SRP

31. Don’t Touch that Code! by Cal Evans
- 운영서버에 직접 코드를 고지치마라.

32. Encapsulate Behavior, not Just State by Einar Landre
- 객체지향 모델링의 장점을 활용해서 캡슐화를 제대로 하자.

33. Floating-point Numbers Aren’t Real by Chuck Allison
- 부동소수점은 정확하지 않다. 조심해서 사용하자.

34. Fulfill Your Ambitions with Open Source by Richard Monson-Haefel
- 회사에서 채워지지 않는 개발욕망이 있다면 오픈소스로 채워보자.
- “일로써 소프트웨어를 개발하는 것이 아니라 열망하기 때문에 소프트웨어를 개발할 수 있다면 참 좋을 것 같습니다”

35. The Golden Rule of API Design by Michael Feathers
- API를 설계할때 그 API를 사용하는 코드를 테스트할 방법도 고려해서 설계하자

36. The Guru Myth by Ryan Brush
- 구루도 사람이다. 다 알꺼라는 생각은 버리자.

37. ★ Hard Work Does not Pay Off by Olve Maudal
- 끝없는 잔업으로 일만한다고 해서 절대 성과가 나오지 않는다. 왜냐하면 프로그래밍과 소프트웨어 개발 작업에는 지속적인 학습이 수반되어야 하기 때문이다.
- “전문 프로그래밍은 길 끝에 도달해야만 목표 지점을 볼 수 있는 달리기와는 다릅니다. 대개의 소프트웨어 프로젝트는 어두운 곳에서 대략 그려진 지도만을 가지고 정해진 길을 찾아가야 하는 장거리 오리엔티어링 마라톤과 더 비슷합니다”

38. How to Use a Bug Tracker by Matt Doar
- 버그 리포트는 최대한 상세하게 적자. 버그는 작업의 표준 단위가 아니다.

39. Improve Code by Removing It by Pete Goodliffe
- 필요없는 부가적인 코드는 제거하자.

40. Install Me by Marcus Baker
- 쉬운 설치, 간결한 사용법은 생각보다 중요하다.

41. Inter-Process Communication Affects Application Response Time by Randy Stafford
- 느리다 싶으면 IPC부터 점검해보자

42. Keep the Build Clean by Johannes Brodwall
- Warning도 보일때마다 잡아야한다.

43. Know How to Use Command-line Tools by Carroll Robinson
- IDE말고도 커맨드라인 명령어로 다 할 수 있다. 특히 자동화에서는 필수.

44. ★ Know Well More than Two Programming Languages by Russel Winder
- 다양한 언어의 패러다임을 익히자. 상호 교류는 전문 지식의 핵심.

45. Know Your IDE by Heinz Kabutz
- IDE의 기능을 최대한 활용하자

46. Know Your Limits by Greg Colvin
- 소프트웨어가 뛰어넘기 힘든 현실의 한계가 있다.

47. Know Your Next Commit by Dan Bergh Johnsson
- 업무를 작은 보폭으로 짧게짧게 나누어라. divide and conquer.

48. Large Interconnected Data Belongs to a Database by Diomidis Spinellis
- RDBMS 사용을 두려워하지 말자. 많이 발전했고 장점도 많으며 계속 발전중이다.

49. ★ Learn Foreign Languages by Klaus Marquardt
- 도메인 언어든 제2외국어든 사람과 대화할 때 사용하는 진짜 언어를 하나 더 익히자.
- “제가 알고있는 최고의 프로그래머들은 모국어뿐 아니라 다른 언에도 능통합니다. 언어를 유창하게 하면 또한 문제점을 추상화하는 명확한 사고를 이끌어 낼 수 있습니다. 그리고 이런 것이 프로그래밍입니다” - “다른 언어를 안다는 것은 다른 영혼을 소유하게 되는 것”

50. ★ Learn to Estimate by Giovanni Asproni
- 추정하는 능력이 필요하다. 잘하기 위해서는 역시나 좋은 방법을 배운 후 꾸준한 연습뿐.

51. Learn to Say “Hello, World” by Thomas Guest
- 함수 하나도 충분히 독립적으로 실행할 수 있다는 사실. 단위 테스트를 작성하는 개발자라면 당연히 알테지만.

52. Let Your Project Speak for Itself by Daniel Lindner
- 지속적인 통합을 적용한 다음 문제가 생겼을때 이메일 노티 대신 싸이렌이 울리도록 해보자. 절대 무시할 수 없을 것이다. 재밌기도 하고. Extreme Feedback Device (XFD) 라 한다.

53. The Linker Is not a Magical Program by Walter Bright
- 링크하는 과정도 알아야한다.

54. The Longevity of Interim Solutions by Klaus Marquardt
- 임시 해결책을 적용해야하는 상황이 있다. 임시 해결책은 글자그대로 임시일 뿐이다.
- “여러분이 변화시킬 수 없는 것을 그대로 용납하는 평안을 누리고, 변화시킬 수 있는 것을 바꾸는 용기를 얻고, 그 차이를 아는 지혜를 얻기를 바랍니다”

55. Make Interfaces Easy to Use Correctly and Hard to Use Incorrectly by Scott Meyers
- 인터페이스 설계를 잘못사용할수 없도록 잘 설계하자. GUI 포함해서.

56. Make the Invisible More Visible by Jon Jagger
- 눈에 보이지 않는 것은 해결하기도 어렵다. 장애요소는 무엇이든 빨리드러나도록 하자.
- “프로젝트가 정상적인 개발 일정에 맞춰 진행되고 있었는데, 1주일후에 6개월이 지연되야 한다고 알려진다면, 프로젝트에는 문제가 있는 것입니다. 가장 큰 문제는 6개월의 지연이 아니라 6개월 지연을 감출 정도로 강력한 비가시성입니다”

57. Message Passing Leads to Better Scalability in Parallel Systems by Russel Winder
- 복잡한 병렬 문제를 해결하는 방법은 서로 독립적인 환경이 되도록 공유 메모리를 사용하지 않는 것이다. 대신 메시지 전달방식을 써보자. like Erlang.

58. ★ A Message to the Future by Linda Rising
- 지금 내가 작성하는 코드는 미래의 누군가에게 보내는 메세지이다.
- 읽기 쉬운 코드 = 완벽하고 아름다운 코드 = 잊혀지지 않는 멜로디 같은 코드 = 아름다운 세상을 만드는 코드

59. Missing Opportunities for Polymorphism by Kirk Pepperdine
- if-then-else 대신 다형성을 활용하자.
- “물론 if-then-else 구문을 사용하는 것이 더 실용적인 경우도 있겠지만 대부분 다형성을 적용한 코드가 더 적은 양으로도 가독성 높으며 견고한 코드를 만들 수 있습니다”

60. News of the Weird: Testers Are Your Friends by Burk Hufnagel
- 테스트를 해주는 테스터를 미워하지 말고 고마워해야한다.

61. One Binary by Steve Freeman
- 커스텀 바이너리 여러개 생성하지말고 딱 하나만 생성하자.

62. ★ Only the Code Tells the Truth by Peter Sommerlad
- 각종 문서, 주석 등은 모두 거짓일 수 있다. 가장 정확한 것은 바로 코드이다. 따라서 진실을 명확하게 말해주는 코드를 작성할 수 있도록 신경을 써야한다.
- “프로그램이 무엇을 하는지 알고자 한다면 소스코드를 살펴보는 것이 가장 확실한 방법입니다”

63. Own (and Refactor) the Build by Steve Berczuk
- 빌드는 개발과정의 필수 부분이다. 따라서 빌드 스크립트도 코드만큼 중요한 것이다.

64. Pair Program and Feel the Flow by Gudny Hauknes, Ann Katrin Gagnat, and Kari Røssland
- 짝 프로그래밍 좋아요. 장점이 이렇게나 많다구요. 하지만 어렵자나!
- “작업을 다른 짝에게 넘기기 위해 도중에 인터럽트를 거는 것이 납득이 잘 안될 수도 있지만, 우리는 이것이 문제없다는 것을 발견했습니다”

65. Prefer Domain-Specific Types to Primitive Types by Einar Landre
- 기본 타입보다 지금의 맥락에 맞는 타입을 따로 정의해서 사용하는게 더 낫다.

66. Prevent Errors by Giles Colborne
- 에러가 발생할 여지를 막아버리면 당연히 에러가 발생하지 않는다.

67. ★ ★ The Professional Programmer by Uncle Bob
- 전문 프로그래머란?
- 지속적인 학습을 통해 자신의 경력을 책임진다 + 완벽한 코드를 작성하려는 자세 + 팀 플레이어 + 버그는 절대 용납하지 않는다 + 대충대충하면서 엉망으로 만들지 않는다
- “만약 여러분이 유체 이탈을 해 의사가 여러분의 심장 절개 수술을 진행하는 장면을 지켜보고 있다고 상상해 보십시오. 그 의사가 전형적인 소프트웨어 개발자처럼 서두르면서 엉망으로 진행하기를 원하나요? 의사가 수술을 마치고 나서 ‘다시 돌아와서 나중에 고쳐도 될까요?’ 라고 말하기를 원하십니까?”

68. Put Everything Under Version Control by Diomidis Spinellis
- 코드뿐만 아니라 프로젝트와 관련된 모든 것들을 Repository 에 밀어넣고 관리하자. 논리적인 변경을 각각 별도로 커밋하자. 한꺼번에 하면 구분하기가 어렵다.

69. Put the Mouse Down and Step Away from the Keyboard by Burk Hufnagel
- 전체 맥락을 다시 생각해보기 위해서는 잠시 두뇌를 환기시키는게 좋다. 뽀모도로에서 의도적으로 5분을 쉬는것도 역시나 같은 이유때문이다.

70. Read Code by Karianne Berg
- 책도 좋다. 코드를 설명해놓은 문서도 좋다. 하지만 더 좋은 건 코드를 직접 읽어보는 것이다.

71. Read the Humanities by Keith Braithwaite
- 소프트웨어가 동작하는 방식과 소프트웨어를 개발하는 방식은 사실 사람들이 서로 돕고 함께 살아가는 이 세상과 닮아있다. 따라서 먼저 이 세상을 이해해보는건 어떨까?

72. ★ Reinvent the Wheel Often by Jason P Sage
- 이미 잘 쓰고있는 바퀴를 의도적인 학습을 목표로 다시 만들어보자. 분명히 값진 경험이 될 것이다.
- “항해와 관련된 영화를 보는 것과 직접 항해를 하는 것은 다르다”

73. Resist the Temptation of the Singleton Pattern by Sam Saariste
- 싱글톤에는 단점이 많다. 사용전에 꼭 필요한지 다시한번 생각하자. 특히 단위테스트가 어렵다는 단점이 가장 맘에 안든다.

74. The Road to Performance Is Littered with Dirty Code Bombs by Kirk Pepperdine
- 코드의 복잡성과 의존성을 파악하는 도구를 활용해서 미리미리 코드의 폭탄을 제거해놓자. 안그러면 성능개선을 위해서든 뭐든 코드변경이 필요할때 폭탄이 터져버릴것이다.

75. Simplicity Comes from Reduction by Paul W. Homer
- 코드를 간단하게 유지하자. 그럴려면 잘 돌아가는 기존의 코드도 간결하게 리팩토링할 의지와 용기가 있어야한다.

76. ★ The Single Responsibility Principle by Uncle Bob
- SRP. 단일 책임의 원칙. 하나의 서브시스템, 모듈, 클래스, 함수에는 한 가지 이상의 변경 이유가 있어서는 안된다.

77. Start from Yes by Alex Miller
- 긍정적인 마인드로 접근하면 긍정적인 면이 보이고 부정적인 마인드로 접근하면 부정적인 면만 보인다.

78. ★ Step Back and Automate, Automate, Automate by Cay Horstmann
- 한번 이상 반복될거라 생각되면 자동화해버리자. 일단 자동화가 되면 생각보다 편하게 실행할 수 있고 평소보다 훨씬 더 많이 실행할것이며 그에 따라 얻는 이득이 분명 있을 것이다.

79. Take Advantage of Code Analysis Tools by Sarah Mount
- 정적 코드 분석 툴을 활용하자. 분명히 문제인데도 툴이 잡아내지 못한다면 자신만의 분석툴을 만들어보자.

80. Test for Required Behavior, not Incidental Behavior by Kevlin Henney
- 코드가 구현되어있는대로 테스트하기 보다는 기대하는 동작대로 테스트를 작성하자.
- “코드로부터 무엇을 하는지를 명백하게 알 수 있음에도 이를 그저 반복해 확인하는 것은 가치 있는 일이 아니며 잘못된 진척률과 안도감을 줄 수 있습니다. false sense of progress.

81. Test Precisely and Concretely by Kevlin Henney
- 테스트를 작성할때는 기대하는 값에 대해서 정확히 검증하도록 하자. 어설프게 검증하면 구멍이 생긴다.
- “소프트웨어를 설계하는 방법에는 두 가지가 있다 : 한 방법은 설계를 아주 단순하게 해서 명백하게 결함이 존재하지 않게 하는 것이고, 다른 방법은 설계를 아주 정교하게 해서 명백한 결함이 존재하지 않게 하는 것이다”

82. Test While You Sleep (and over Weekends) by Rajith Attapattu
- 내가 일하고 있지 않는 시간에도 테스트는 돌아가게 해놓자. 어차피 그시간에 컴터는 논다. 회사 전기세를 걱정하지 않아도 되잖냐. 특히 시간이 오래걸리는 테스트라면 딱이다.

83. Testing Is the Engineering Rigor of Software Development by Neal Ford
- 테스트는 이제 소프트웨어 개발에서 엄격히 지켜야하는 규칙이다.
- “교량 건설자는 결코 상사로부터 ‘건물의 구조를 분석하느라 애쓰지 마. 우리 마감일은 빠듯하니까’ 라는 말을 듣지는 않을 것입니다”

84. Thinking in States by Niclas Nilsson
- 상태를 명확히 구분해야하는 시나리오라면 State Machine 을 사용하자.

85. Two Heads Are Often Better than One by Adrian Wible
- 짝 프로그래밍으로 배울 수 있는게 많다.

86. Two Wrongs Can Make a Right (and Are Difficult to Fix) by Allan Kelly
- 버그 두개가 서로 영향을 주어서 둘다 드러나지 않는 경우가 있다. 버그를 알아차리기도 힘들지만 수정하기는 더 힘들다. 그래서 어떻게해야한다? 잘 찾아서 고치자.

87. Ubuntu Coding for Your Friends by Aslam Khan
- 깨진 창문을 내버려 두지 말자. 코드는 너 혼자만의 것이 아니다. 당신이 작성한 나쁜 코드로 스트레스받을 동료 개발자를 생각하자.
- “사람은 다른 사람이 있기에 사람이다. 개발자는 다른 개발자가 있기에 개발자다. 코드는 다른 코드가 있기에 코드다”

88. The Unix Tools Are Your Friends by Diomidis Spinellis
- 유닉스 툴은 참 유용하다.

89. Use the Right Algorithm and Data Structure by JC van Winkel
- 알고리즘과 자료구조에 대해서는 기본적으로 잘 알아야한다. 그래야 효율적인 코드를 작성할 수 있다. 아니 문제있는 코드 작성을 피할 수 있다.

90. Verbose Logging Will Disturb Your Sleep by Johannes Brodwall
- 필요없는 로그 말고 필요한 로그만 적어보자.

91. WET Dilutes Performance Bottlenecks by Kirk Pepperdine
- Write Every Time. 동일한 코드가 중복되는 현상은 좋지 않다. 성능 개선을 위해 분석할때도 당연히 안좋다. 중복이 좋은게 뭐가 있겠나. 무조건 DRY 다.

92. When Programmers and Testers Collaborate by Janet Gregory
- 개발자와 검증자는 적이 되기 쉽다. 하지만 서로에게 배울 점이 분명히 있으며 서로 도움이 되는 부분도 분명히 존재한다. 너무 미워하지 말자.

93. Write Code as If You Had to Support It for the Rest of Your Life by Yuriy Zubarev
- 코드를 대하는 자세는 어때야 할까? 결혼 상대자를 찾는 것처럼 진중한 마음이어야 한다. 잠깐의 Enjoy 마인드는 안된다.

94. Write Small Functions Using Examples by Keith Braithwaite
- 도메인을 생각하면 고려해야하는 세계가 작아지고 훨씬 문제가 쉬워진다.

95. ★ Write Tests for People by Gerard Meszaros
- 테스트는 코드를 어떻게 사용해야하는가와 이 코드가 무슨일을 하는지를 설명해준다. 결국 테스트를 작성하는 것은 나를 위해서가 아닌 것을. 테스트의 장점은 수도없이 많다. 작성하자. 테스트. 두번 작성하자.

96. ★ You Gotta Care about the Code by Pete Goodliffe
- 프로그래밍을 함에 있어서 가장 중요한 것은 마음가짐이다. 아니 무슨일이든 마찬가지 일 것이다.
- “소프트웨어 회사에서의 다년간의 경험으로, 저는 적당한 프로그래머와 훌륭한 프로그래머 사이의 진짜 차이의 대한 결론을 내렸습니다. 바로 마음가짐입니다. 좋은 프로그래밍은 소프트웨어 회사의 현실적인 제약과 압력 속에서도 전문적인 접근 방법을 사용하려 하고, 여러분이 할 수 있는 최상의 소프트웨어를 작성하기를 원할 때 만들어집니다”

97. Your Customers Do not Mean What They Say by Nate Jackson
- 고객은 그들이 무엇을 원하는지 잘 모른다. 개발자도 모른다. 그러면 코드도 뭘하는지 모르게 된다. 고객을 자주 만나서 소통하자.

걸린시간

뽀모도로 예상 8번 -> 실제 16번. ( 16 x 25분 = 8시간) 소요 시간을 추정할 때 각 이야기들을 한줄로 요약하는 시간을 생각하지 못한듯.

[책] 애자일 마스터

애자일 마스터. 원제는 The Agile Samurai

Xper 모임에서 알게된 책이다. 미리 알았더라면 베타리딩에 참여했을텐데 아쉬움이 남는다.

애자일을 처음 접하는 사람에게 단 1권의 책만 소개해야 한다면 주저없이 이 책을 골라야겠다. 애자일 입문서로서 충분히 좋은 책.

번역서라는 생각이 들지 않을 만큼 번역도 잘되었을뿐더러 이해를 도와주는 귀여운 그림도 많아서 좋다. 편한마음으로 읽으면서 사무라이 스승님과 대화하다보면 어느새 머리속에는 애자일 선언문이 정리된다.

책 마지막즈음의 말들이 주옥같다.

애자일 프로젝트로 안내해 줄 지도같은건 없다. 여러분 스스로 여러분의 팀과 프로젝트에 맞는 최고의 방법이 무엇인지 알아내야 한다.

누구를 설득하려고 하지 말자. 다른 사람들에게 그들이 무슨일을 해야 한다고도 말하지 말자. 그 대신, 행동을 보여주자. 다른 사람들이 항상 보고 있지는 않겠지만, 그저 여러분이 해야할 일을 해나가는 것이다.

애자일은 여행의 과정이지 목적지가 아니다. 애자일화 되는 것이 목적이 아니라 훌륭한 제품을 개발해서 고객에게 최상의 서비스를 제공하는 것이 목적이라는 것을 꼭 기억하도록 하자.

실천법에 너무 목매지 말자. 여러분만의 상황과 맥락에 적합하게 적용하기 바란다. 그러다가도 여러분이 과연 애자일을 잘 실천하고 있는지 궁금해진다면 다음의 두 가지 질문을 스스로에게 해보자.
우리는 매주 가치 있는 것을 고객에게 인도하는가?
우리는 계속 발전하기 위해 노력하고 있는가?

만약 이 질문에 네라고 대답할 수 있다면, 여러분은 애자일을 하고 있는 것이다.

요즘 팀에 애자일을 적용하다보니 찔리는 말이 많다. 특히 실천법에 너무 목맨다는 말이 가장 찔린다. 내공도 부족하고 경험도 부족하기 때문이리라. 꾸준히 부딪히는 수밖에 없을듯 하다.

특히 같은 말이라도 상대방이 기분나쁘지 않게 말하는 능력은 참 갖고 싶다는 생각이 든다.

애자일 실천법에 대한 간단한 설명들도 좋았다.

다음은 읽으면서 정리한 내용.

책 본문에는 귀여운 그림이 많아서 이해도가 훨씬 높아지니 꼭 책을 보시길 ^^

1장. 애자일의 핵심


세가지 단순한 진실
프로젝트 초기에 요구사항을 모두 수집하기는 불가능하다.
수집한 요구사항들이 무엇이든 반드시 변하기 마련이다.
시간이나 비용이 허락하는 것보다 해야 할 일들이 항상 더 많다.


2장. 애자일 팀 만나기


팀원 각각의 역할을 미리 정의하지 않는다. 누구나 무슨 일이든 할 수 있다. 그런데 신기하게도 이처럼 혼란스럽고 체계적이지 않은 것 같은 틀 속에서 높은 품질의 소프트웨어가 끊임없이 생산된다.

같은 공간에서 일하기, 참여하는 고객, 자기조직화, 책임감과 자율성, 교차기능팀

애자일 고객, 개발팀, 애자일 애널리스트, 애자일 개발자, 애자일 테스터, 애자일 프로젝트 관리자, 애자일 UX 디자이너


3장. 모두 한 버스에 타는 법


인셉션 덱은 프로젝트와 관련이 있는 사람들을 모아, 모든 사람들이 프로젝트에 기대하는 바가 동일하도록 서로 적절한 질문을 통해 생각을 공유한다면 프로젝트가 성공할 확률이 높을 것이다라는 아이디어에서 시작되었다.


4장. 크게 보기


질문하라. 우리는 왜 여기 모였나요?
엘리베이터 피치 만들기
제품 광고를 직접 디자인해보자
NOT 리스트를 작성하라
프로젝트와 관련된 다양한 사람들과 만나라


5장. 실현 방안


리스크를 찾아내기. 리스크는 당신이 이미 아는 것이거나 당신이 죽었다 깨어나도 생각지 못한 일, 이 둘 중 하나일 테니까. 그러니 그때그때 리스크가 발생하는 대로 처리하라.

규모가 크고 시간의 제한이 없는 프로젝트의 문제점은 끊임없이 장밋빛 전망을 해놓고 결국 출시하지 못한다는 데에 있다.
정말 큰 프로젝트를 출시하고자 할 때, 작고 다룰 수 있을 만한 크기로 나눠야 한다.
가장 이상적인 기간은 6개월 이내다.

트레이드 오프 슬라이더.

프로젝트를 위협하는 전설의 사총사. (시간, 비용, 품질, 범위)


6장. 사용자 스토리 수집하기.


사용자 스토리는
고객이 자신의 소프트웨어에 원하는 기능을 짧게 표현해 놓은 것.
짧게. 인덱스 카드로. 다 적을 필요가 없음. 자세한건 face to face.
키워드만을 적어놓기. 세부사항은 나중에.
서로간에 대화를 하도록 권장하는 도구 역할.

스토리의 6가지 요소 INVEST
Independent, Negotiable, Valuable, Estimatable, Small, Testable

템플릿. 누구를 위해, 왜 어떤 이유때문에, 무엇을 원한다.


7장. 추정치 정하기.


미래에 대한 정확한 예측은 불가능.

인간에게 어떤 것을 상대적으로 추측하는 능력이 생각보다 훨씬 뛰어나다.

점수기반 시스템을 사용하면 달력에 나타난 시간에 목매지 않아도 된다.

삼각측량
기준으로 삼을 스토리를 몇 개 사용해서 다른 스토리들의 상대적인 크기를 추정하는 것.

플래닝포커
팀원들이 각각 스토리의 추정치를 먼저 정한 후에 결과를 모두와 비교하는 게임.
추정치가 대략 비슷하다면 유지하고 다르다면 논의를 통해 의견이 일치될때까지 다시 추정한다.


8장. 애자일로 계획짜기.


팀의 속도. team velocity
사용자 스토리를 작동하는 소프트웨어로 만드는 속도.

범위에 유연하라.
새로운 스토리를 추가하고도 그만한 크기의 다른 스토리를 포기하지 않으려는 것은 희망사항일 뿐.

1. 마스터 스토리 리스트 작성하기
릴리스.
함께 묶어 전달했을 때 고객에게 가치가 있는 스토리들을 논리적으로 분류해놓은 그룹.
출시할 만한 최소한의 기능을 모아놓은 세트.
MMF. Minimal Marketable Feature set.

2. 크기 정하기.
소프트웨어 기능 중 64%는 사용자가 거의 사용하지 않거나 한 번도 쓰지 않는다.

3. 우선순위 정하기
번개가 언제 칠지는 아무도 모른다. 프로젝트가 취소되거나 기간이 단축된다는 소식이 언제 들려올지는 아무도 모른다는 말이다. 그래서 우리는 항상 중요한 것을 먼저 개발해야 한다.

4. 팀의 업무속도 측정하기

5. 날짜 예상하기

번다운차트, 번업차트.

변화란 항상 존재하는 것이다. 우리는 그저 이를 얼마나 창의적으로 드러내고, 다루어야 할지를 고민해야 할 뿐이다.


9장. 이터레이션 관리: 구현하기


애자일 분석.
필요한 만큼만 분석하라. 언제고 필요하다면 그때마다 살을 붙이면 된다. 불필요한 짐을 많이 갖고 여행하면 발걸음만 늦어진다. 그러니 시작은 가볍게 하고, 필요한 부분은 그때그때 충족해 나가도록 하라.

순서도를 그려보고 페르소나도 활용

스토리가 완성되려면 만족해야하는 조건을 인수 테스트로 작성.

이터레이션0. 작업 환경 준비하기

칸반.
한팀은 오직 정해진 수만큼의 작업만 동시에 수행할 수 있다.


10장. 애자일 커뮤니케이션 계획


스토리 계획회의 - 쇼케이스 - 이터레이션 계획 회의 - 미니회고

“그런데 단위 테스트가 부족하네요” → “그정도의 섬세함을 단위 테스트에도 적용해봐요. 아마 곧 세계 최고가 될 거에요”

일일 스탠드업을 진행할 때 하면 안되는 것
어제 무엇을 했는가 → 세상을 바꾸기 위해 어제 무엇을 했는가
오늘은 무엇을 할 것인가 → 오늘 그 작업을 어떻게 끝낼 것인가
내 작업 속도를 늦추거나 방해하는 요소가 있는가 → 불행히도 지금 내 앞을 가로막고 있는 이 장애물을 어떻게 없앨 것인가


11장. 시각적인 작업환경 조성하기


인셉션 덱, 릴리즈 상황판, 스토리 보드, 팀 속도, 번다운 차트


12장. 단위 테스트: 제대로 작동하는지 확인하기


버그를 고치기전에 실패하는 단위 테스트를 작성하자

자동화되어 실행하기 쉬운 단위 테스트의 가치는 헤아릴 수가 없다. 소프트웨어에 조금이라도 변화가 있을 때마다 이 단위 테스트를 실행해서 새로운 코드가 고장낸 것이 무엇인지 재빨리 확인할 수 있기 때문이다.

단위 테스트를 전쟁에 나가기 전에 입어야 할 갑옷이라고 생각하자.


13장. 리팩터링: 기술적 부채 갚기


기술적 부채란 임기응변, 난도질, 복사해붙이기 같이 우리가 생산성과 일정이라는 미명 아래에 코드에 저질러 놓은 죄악들이 오랫동안 누적된 결과다.

이런 문제들은 한 번에 알아채지 못하게 슬그머니 찾아든다는 사실이 가장 위험하다. 초기 코드에 만들어진 각각의 위반 사항들은 작고 그리 중요하지 않아 보인다. 하지만 대부분의 부채가 그렇듯이 시간이 가면서 누적된 효과는 결국 큰 골칫거리가 되고 만다.

코드를 쓰는 것은 마치 훌륭한 산문을 작성하는 것과 비슷하다. 분명하고 쉽게 이해할 수 있으면서도 의도하는 바가 무엇인지 파악하는데 많은 노력이 필요해서는 안 된다. 리팩터링은 객체 지향 프로그래머들이 바로 그런 목적을 달성하기 위해 사용하는 비밀병기이다.

거의 끝나가는 프로젝트에서 큰 규모의 리팩터링을 하는 것은 별 의미가 없다. 열심히 일한 보상을 받을 시간이 없기 때문이다.

리팩터링을 작은 단위로 자주 해서 한번에 큰 규모의 피랙터링을 할 필요가 없도록 노력하거라.


14장. 테스트 주도 개발


새로운 코드를 쓰기 전에 먼저 새로운 코드로 성취하고자 하는 바가 무엇인지를 보여주는 실패하는 단위 테스트를 작성한다. 바로 이때 개발자들은 설계에 대해 신중하게 생각하게 된다.

규칙1. 실패하는 테스트를 먼저 작성할 때까지 어떤 코드도 새로 쓰지 않는다.
규칙2. 문제가 생길 가능성이 있는 것이라면 무엇이든 반드시 테스트한다.

여러분의 팀이 TDD를 재빨리 받아들이지 않는다고 당황할 필요는 없다. 이는 단위 테스트나 리팩터링보다 한 차원 높은 코딩 기법이니까.


15장. 지속적인 통합: 출시 준비


지속적인 통합이란 개발자가 지속적으로 소프트웨어를 변경하고, 이 변경사항들을 계속해서 통합하는 활동을 말한다.

아무리 좋은 체크인 프로세스와 자동화된 빌드가 있다고 하더라도, 정말 중요한 것은 바로 작은 단위로 작업하려는 마음가짐이다.

언제든 출시할 수 있는 소프트웨어를 준비하자는 태도가 바로 내가 진정 하고자 하는 말이란다. 버그를 발견하면 즉시 고쳐야지, 카펫 밑에 숨겨 놓고 언젠가 고치겠다는 태도는 버리라는 말이다.

걸린시간

추정은 뽀모도로 7번. 실제로는 10번 걸렸다. ( 10번 x 25분 = 약 5시간 )

[책] Clean Code

필독서다.

예전에 한번 읽었었는데 자꾸 눈에 밟히기도 하고 위키에도 정리할 겸 한번 더 읽었다. 2번 읽을줄 알았으면 한번은 원서로 읽을껄 그랬나 싶다.

책을 통해 위안을 받고 싶었는지도 모르겠다.

빠듯한 일정, 코드에는 아무도 신경쓰지 않는 문화, 의지 부족, 클린이 뭔지 모르는 무지. 안좋은 코드가 나올 수 밖에 없는 현실을 경험하고 보니 클린 코드가 더 절실했으리라.

2번 읽어도 밥 형님은 최고다. 가슴을 울리는 말들이 이어진다. 모든 프로그래머가 봐야할 책임에 틀림없다. 그런데 번역하신 분의 블로그를 보니 많이 안팔렸다고 한다. 이럴수가. 이런 명서중의 명서가 안팔린다니. 좋은 책이 무조건 흥행을 보장하지는 않는구나. 이렇게 좋은 책을 사람들이 많이 안읽는다는 사실이 슬프게 느껴진다. 게다가 번역도 너무 멋진 책인데!

밥형님이 직접 보여주시는 리팩토링 before/after 는 어디에서도 얻지 못할 고급 정보임에 틀림없다.

왠만하면 작게 추려보려고 했는데 너무 좋은 말이 많아서 그나마 간추린 내용이 이정도다.

아직 안보셨다면 지금 당장 사보시라! 2번 보시라!

1장. 클린 코드

궁극적으로 코드는 요구사항을 표현하는 언어라는 사실을 명심한다.

우리 모두는 자신이 짠 쓰레기 코드를 쳐다보며 나중에 손보겠다고 생각한 경험이 있다. 다시 돌아와서 나중에 정리하겠다고 다짐했었다. 물론 그 때 그 시절 우리는 르블랑의 법칙을 몰랐다. “나중은 결코 오지 않는다”

시간을 들여서 클린 코드를 만들려는 노력이 비용을 절감하는 방법일 뿐 아니라 전문가로서 살아남는 길이라는 사실을 인정하리라.

일정에 쫓기더라도 대다수 관리자는 좋은 코드를 원한다. 그들이 일정과 요구사항을 강력하게 밀어붙이는 이유는 그것이 그들의 책임이기 때문이다. 좋은 코드를 사수하는 일은 바로 우리 프로그래머들의 책임이다.

기한을 맞추는 유일한 방법은, 그러니까 빨리 가는 유일한 방법은, 언제나 코드를 최대한 깨끗하게 유지하는 습관이다.

그림을 보면 좋은 그림인지 나쁜 그림인지 구분이 간다. 그렇지만 좋은 그림을 구분할 줄 안다고 좋은 화가라는 뜻은 아니다. 다시 말해, 클린 코드와 나쁜 코드를 구분할 줄 안다고 클린 코드를 작성할 줄 안다는 뜻은 아니다.

클린 코드는 ‘보기에 즐거운’ 코드다.

나쁜 코드는 너무 많이 하려고 애쓰다가 의도가 뒤섞이고 목적이 흐려진다. 클린 코드는 한가지에 ‘집중’한다.

테스트 케이스가 없는 코드는 클린 코드가 아니다. 아무리 코드가 우아해도, 아무리 가독성이 높아도, 테스트 케이스가 없으면 깨끗하지 못하다.

클린 코드는 주의깊게 작성한 코드다. 누군가 시간을 들여 깔끔하고 단정하게 정리한 코드다.

코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 말한다.

이 논리에서 빠져나갈 방법은 없다. 주변 코드를 읽지 않으면 새 코드를 짜지 못한다. 주변 코드를 읽기 쉬우면 새 코드를 짜기도 쉽다. 주변 코드를 읽기 어려우면 새 코드를 짜기도 어렵다. 그러므로 급하다면, 서둘러 끝내려면, 쉽게 짜려면, 읽기 쉽게 만들면 된다.

시간이 지날수록 코드가 좋아지는 프로젝트에서 작업한다고 상상해보라. 전문가라면 너무도 당연하지 않은가?

이 책을 읽는다고 우수한 프로그래머가 된다는 보장은 없다. 단지 우수한 프로그래머가 생각하는 방식과 그들이 사용하는 기술과 기교와 도구를 소개할 뿐이다.


2장. 의미있는 이름

좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.

따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 소리다.

발음하기 쉬운 이름은 중요하다. 프로그래밍은 사회적 활동이기 때문이다.

똑똑한 프로그래머와 전문가 프로그래머 사이에서 다른 점 하나를 들자면, 전문가 프로그래머는 명료함이 최고라는 사실을 이해한다.

프로그래머는 코드를 최대한 이해하기 쉽도록 짜야한다. 집중적인 탐구가 필요한 코드가 아니라 대충 훑어봐도 이해할 코드가 목표다. 의미를 해독할 책임이 독자에게 있는 논문 모델이 아니라 의도를 밝힐 책임이 저자에게 있는 잡지 모델이 바람직하다.


3장. 함수

함수를 만드는 첫 번째 규칙은 ‘작게!’다. 함수를 만드는 두 번때 규칙은 ‘더 작게!’다.

각 함수가 너무도 명백했다. 각 함수가 이야기 하나를 표현했다. 각 함수가 너무도 멋지게 다음 무대를 준비했다. 바로 이것이 답이다.

함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만 해야 한다.

단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 가지 작업을 하는 셈이다.

근본 개념과 세부 사항을 뒤섞기 시작하면, 깨어진 창문처럼, 사람들이 함수에 세부 사항을 점점 더 많이 추가한다.

함수로 부울값을 넘기는 관례는 정말로 끔직하다. 왜냐고? 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이니까!

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안된다.

어쩌면 중복은 소프트웨어에서 모든 악의 근원이다.

소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다. 논문이나 기사를 쓸 때는 먼저 생각을 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐 때까지 말을 다듬고 문장을 고치고 문단을 정리한다. 처음부터 탁 짜내지 않는다. 그게 가능한 사람은 없으리라.


4장. 주석

우리는 코드로 의도를 표현하지 못해, 그러니까 실패를 만회하기 위해 주석을 사용한다.

부정확한 주석은 아예 없는 주석보다 훨씬 더 나쁘다.

진실은 한 곳에만 존재한다. 바로 코드다. 코드만이 자기가 하는 일을 진실하게 말한다. 코드만이 정확한 정보를 제공하는 유일한 출처다. 그러므로 우리는 (간혹 필요할지라도) 주석을 가능한 줄이도록 꾸준히 노력해야 한다.

주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다. 이유가 있어 남겨놓았으리라고 중요하니까 지우면 안 된다고 생각한다. 그래서 질 나쁜 와인병 바닥에 앙금이 쌓이듯 쓸모없는 코드가 점차 쌓여간다.


6장. 객체와 자료 구조

변수를 private으로 선언하더라도 각 값마다 get과 set 함수를 제공한다면 구현을 외부로 노출하는 셈이다.

객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다. 아무 생각 없이 조회/설정 함수를 추가하는 방법이 가장 나쁘다.

객체 지향 코드에서 어려운 변경은 절차적 코드에서 쉬우며, 절차적 코드에서 어려운 변경은 객체 지향 코드에서 쉽다!


8장. 경계

외부 코드를 익히기는 어렵다. 외부 코드를 통합하기도 어렵다. 두 가지를 동시에 하기는 두 배나 어렵다. 다르게 접근하면 어떨까? 곧바로 우리 쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히면 어떨까? 이를 학습 테스트라 부른다.

학습 테스트는 이해를 높여주는 정확한 실험이다. 공짜 이상이다. 투자하는 노력보다 얻어지는 성과가 더 크다.

외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자.


9장. 단위 테스트

애자일과 TDD 덕택에 단위 테스트를 자동화하는 프로그래머들이 이미 많아졌으며 점점 더 늘어나는 추세다. 하지만 우리 분야에 테스트를 추가하려고 급하게 서두르는 와중에 많은 프로그래머들이 제대로 된 테스트 케이스를 작성해야 한다는 좀더 미묘하고 더 중요한 사실을 놓쳐버렸다.

코드에 유연성, 유지 보수성, 재사용성을 제공하는 버팀목이 바로 단위 테스트다. 이유는 단순하다. 테스트 케이스가 있으면 변경이 두렵지 않으니까! 테스트 케이스가 없다면 모든 변경이 잠정적인 버그다. 아키텍처가 아무리 유연하더라도, 설계를 아무리 잘 나누었더라도, 테스트 케이스가 없으면 개발자는 변경을 주저한다. 버그가 숨어들까 두렵기 때문이다.

클린 테스트 코드를 만들려면? 세가지가 필요하다. 가독성, 가독성, 가독성.

테스트 함수마다 assert 하나만 사용하기 보다는 개념 하나만 테스트하라.

클린 테스트 5가지 규칙(FIRST). 빠르게, 독립적으로, 반복 가능하게, 자가 검증하는, 적시에.
Fast, Independent, Repeatable, Self-validation, Timely.

테스트 코드가 방치되어 망가지면 실제 코드도 망가진다. 테스트 코드를 깨끗하게 유지하자.


10장. 클래스

클래스를 만들 때 첫 번째 규칙은 크기다. 클래스는 작아야 한다. 두 번째 규칙도 크기다. 더 작아야 한다.

함수는 물리적인 행 수로 크기를 측정했었다. 클래스는 다른 척도를 사용한다. 클래스가 맡은 책임을 센다.

간결한 이름이 떠오르지 않는다면 분명 클래스 크기가 너무 커서 그렇다.

도구 상자를 어떻게 관리하고 싶은가? 작은 서랍을 많이 두고서 기능과 이름이 명확한 컴포넌트를 나눠 넣고 싶은가? 아니면 큰 서랍 몇 개를 두고서 모두를 던져 넣고 싶은가?
큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다. 작은 클래스는 각각 책임이 하나이며, 변경할 이유가 하나이며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.

“함수를 작게, 매개변수 목록을 짧게”라는 전략을 따르다 보면 때때로 몇몇 메소드만이 사용하는 인스턴스 변수가 아주 많아진다. 이때가 십중팔구 새로운 클래스로 쪼개야 한다는 신호다.


11장. 시스템

‘처음부터 올바르게’ 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다. 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 된다. 이것이 반복적이고 점진적인 애자일 방식의 핵심이다.


12장. 창발성

켄트 백이 제시한 간단한 설계 규칙 4가지.
모든 테스트를 실행한다. 중복은 없앤다. 프로그래머 의도를 표현한다. 클래스와 메소드 수를 최소로 줄인다.

코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까!

작은 재사용을 제대로 익혀야 큰 재사용이 가능하다.

자신의 작품을 조금 더 자랑하자. 함수와 클래스에 조금 더 시간을 투자하자. 더 나은 이름을 선택하고, 큰 함수를 작은 함수 여럿으로 나누고, 자신의 작품에 조금만 더 주의를 기울이자. 주의는 대단한 재능이다.

경험을 대신할 단순한 개발 기법이 있을까? 당연히 없다.


13장. 동시성

동시성은 결합을 없애는 전략이다. 즉, 무엇과 언제를 분리하는 전략이다.

독립적인 스레드로, 가능하면 다른 프로세서로 돌려도 괘찮도록 자료를 독립적인 단위로 분할하라.

각 알고리즘을 공부하고 해법을 직접 구현해보라. 그러면 나중에 실전 문제에 부닥쳤을 때 해결이 쉬워지리라.

임계영역 개수를 줄인답시고 거대한 임계영역 하나로 구현하는 순진한 프로그래머도 있다.

‘일회성’ 문제를 계속 무시한다면 잘못된 코드 위에 코드가 계속 쌓인다.


14장. 점진적인 개선

여러분이 깨끗하고 우아한 프로그램을 한 방에 뚝딱 내놓으리라 기대하지 않는다. 지난 수십 여 년 동안 쌓아온 경험에서 얻은 교훈이라면, 프로그래밍은 과학보다 공계에 가깝다는 사실이다. 클린 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미다.

프로그램을 망치는 가장 좋은 방법 중 하나가 개선이라는 이름 아래 구조를 크게 뒤집는 행위다.

TDD는 시스템을 망가뜨리는 변경을 허용하지 않는다. 변경을 가한 후에도 시스템이 변경 전과 똑같이 돌아가야 한다는 소리다.

리팩토링은 루빅 큐브 맞추기와 비슷하다. 큰 목표 하나를 이루기 위해 자잘한 단계를 수없이 거친다. 각 단계를 거쳐야 다음 단계가 가능하다.

단순히 돌아가는 코드에 만족하는 프로그래머는 전문가 정신이 부족하다. 나쁜 코드보다 더 오랫동안 더 심각하게 개발 프로젝트에 악영향을 미치는 요인도 없다. 나쁜 일정은 다시 짜면 된다. 나쁜 요구사항은 다시 정의하면 된다. 나쁜 팀 역학은 복구하면 된다. 하지만 나쁜 코드는 썩어 문드러진다.

걸린시간

뽀모도로 14번. (25분 x 14 = 약 7시간)