유월의 아버지

‘잠시 후 그가 갑자기 주먹을 쥐고 과장된 몸짓으로 테이블을 쾅 내리쳤다. 지켜보던 아들과 아버지는 깜짝 놀랐다. 박원택이 두 사람을 빤히 응시했다.  “보세요!놀라셨죠? 이렇게 책상을 ‘탁’하고 치니까 ‘억’하고 쓰러졌어요. 심장마비로 쓰러진 겁니다.” 그것이 그들의 결론이었다. ‘

‘아버지는 형언할 수 없는 시대를 살아왔다는 듯 나의 물음에 “어떻게 말로 다 할 수 있나?”라는 대답을 습관처럼 되풀이했다. 그에게 가장 많이 들은 말이다. … 이 책은 그 막막함의 증거이고 기록이다. 말과 말 사이 흐르던 침묵을 언어화하는 일은 내겐 너무 벅찬 일이었다.’

‘박정기는 나에게 민주주의는 완성되지 않는다는 것을 일깨워 주었다. 그는 민주 정부가 들어선 이후에도 싸움을 멈추지 않았다. 민주주의는 완성될 수 없다는 것을, 끝이 없는 싸움이라는 것을 자신의 일대기를 통해 내가 깨닫게 해주었다. ‘6월’이 상징하는 것은 그런 것이 아닐까?’

소수의 누군가는 희생하고 그 희생 위에 있는 사회에서 그를 기억하고 사회를 바꾸자고 하기에는 참 힘든 사회다. 세상에 당연하고 어쩔 수 없는 것은 없다고 생각한다. 기억하자.

  

Hot Module Replacement 삽질기

Webpack에 Hot Module Replacement를 적용하는 과정에서 발생한 삽질기 정리

  1. Express를 사용하는 Node 서버를 사용하기로 했기 때문에 Webpack dev server를 사용할 수 없었다.
    => Webpack Hot Middleware를 사용하였다.
  2. localhost/__webpack_hmr 파일 에러 발생
    => Express에서 API 변경 시 처리를 위해서 Nodemon이 적용되었는데 Nodemon이 업데이트하는 코드가 클라의 js 빌드 파일을 업데이트하면서 발생한 에러
  3. webpack-hot-middleware를 설치하였는데 리액트가 업데이트가 안되고 새로고침하라는 메시지가 나왔다.
    => React에서는 react-transform-hrm을 설치해줘야 했고 Component를 proxy로 교체해서 변경사항 발생 시, 이를 통해서 해당 컴포넌트와 관련 요소들을 업데이트해줬다. 기존에 React Component에서 Component를 상속받지 않고 함수로만 작성한 경우 HMR이 동작하지 않았다.
    => Redux에서는 store의 hot.accept()를 호출해서 reducer들을 교체하는 작업을 해줘야 했다. (store.replaceReducer())
  4. hmr 적용하고 서버 빌드시 에러가 발생하였다.
    => 서버 렌더링하는 코드에서 클라이언트 코드를 호출을 하는데 의존성있는 모듈에서 module && module.hot을 체크하는게 아니라 module.hot으로만 체크를 하다보니 서버 빌드에서는 포함이 안되어 있는 hot module 코드로 인해서 에러가 발생하였다.

모든 코드를 보진 못했지만 hmr에서 코드 변경시 스트림으로 변경된 코드를 클라이언트로 보내고 클라이언트에서는 수정된 모듈과 해당 모듈이 hot module에서 받아들일지를 체크한 후 받아들이지 않는 경우 에러를 발생시키는 코드가 재미있었다. 문제는 반영하는 노력보다 그냥 새로고침 하는게 빠르지 않을까 하는 생각이….

끝~

유니버셜 링크(Universal Link)

앱개발에서 필요한 유니버셜 링크가 아니라 웹 개발자 입장에서 본 유니버셜 링크를 정리했다.

유니버셜 링크란?

iOS9 사용자들이 웹사이트(사파리)의 링크를 클릭했을 때 사파리가 아닌 앱으로(유니버셜 링크를 지원할 때) 매끄럽게 이동하는 기능을 제공해준다. 유니버셜 스킴을 사용하는 경우 얻을 수 있는 장점은 다음과 같다.

  • 커스텀 스킴하는 경우 스킴을 호출할 때 내가 만든 앱이 호출되는 것을 보장해주지 않는다. 만약, 먼저 설치된 앱이 해당 스킴에 대해서 자신이 처리할 수 있다고 iOS에게 응답하면 해당 앱이 실행되게 된다. 그러나, 유니버셜 링크는 앱과 관련된 사이트의 웹주소를 사용하기 때문에 관련 없는 앱이 실행되지 않는다.
  • iOS에서 앱이 설치될 때, 관련된 사이트에 올라간 apple-app-site-association(사이트의 URL을 열 때 어떤 앱을 열지를 정의한) 파일을 체크해준다.
  • 커스텀 스킴을 실행하는 경우 앱이 없을 때 아무런 동작을 할 수 없지만 (보통은 타이머로 체크해서 스토어로 이동)  유니버셜 링크에서는 해당 컨텐츠를 웹에서 항상 볼 수 있기 때문에 유연성을 제공해준다.
  • 마찬가지로 다른 앱에서 사용자의 앱을 호출하는 경우에도 앱 설치 여부에 대해서 고민할 필요가 없게 된다. (없는 경우 대체 웹이 뜨니까)

사실, 위에 적어논 장점은 큰 장점이라 할 수 없어서 9.0.2 전까지는 유니버셜 링크에 대해서 신경도 안썼는데 9.0.2 업그레이드 이후부터 다음의 문제가 생겼다. (장점이라기 보다는 웹 개발은 대체재의 성격을 띄게 되다보니 앱으로 옮겨야 하나 생각이 들게했…)

‘유효하지 않은 링크’ 이슈

iOS 9.0.2로 버전이 업데이트 되면서 해당 버전의 사파리에서 커스텀 스킴을 실행하는 경우 해당 링크가 유효하지 않다는 팝업창이 뜨면서 스킴을 실행할 수 없는 이슈가 발생하였다. 앱이 설치되어 있는 경우라도 Open이 아닌 Cancel을 한 번이라도 누르고 나면 유효하지 않다는 팝업창이 뜨게 된다.

유효하지않은링크

(이러한 이유로 유니버셜 링크를 강제 적용하게 되면서 관련 글을 정리하게 되었다… )

유니버셜 링크의 동작

유니버셜 링크의 동작은 크게 두 개로 나눌 수 있다. 하나는 외부 사이트 또는 앱에서 진입하는 경우이고 또 다른 하나는 내부 사이트에서 웹을 실행하는 경우이다.

외부에서진입시

외부에서 진입하는 경우 실행하는 유니버셜 링크의 apple-app-site-association 파일을 보고 해당 링크와 연결되어 있는 앱의 ID를 확인해본 후, 앱이 설치되어 있다면 앱을 띄우게 된다. 없다면 웹 사이트를 띄우면서 앱 설치 배너를 보여주게 된다.

app_banner

배너의 디자인 변경 지점은 제한되어 있고 노출여부를 결정할 수 없었는데 9.2.1 버전에서 유튜브에서 앱배너가 노출이 안되고 있어서 당황…

내부사이트에서진입시

내부 사이트에서 앱을 띄우는 경우는 조금 복잡해진다. (정확하게는 iOS가 아닌 서비스에서 앱을 띄울 지, 웹을 띄울 지를 결정하고 싶거나 apple-app-site-association의 경로 정책이 애매한 경우 등등… 나의 경우에는 해당 웹페이지가 외부 앱의 인앱 브라우저에서 보여져야 했다.) 고민하다 N사 B서비스에서 괜찮은 방법을 찾았는데, 여기에서는 ul (universal link)이라는 도메인을 새로 추가한 후, 해당하는 도메인에 apple-app-site-association 파일을 올려서 실행할 앱을 지정하였다. 앱에서 보기버튼을 누르게 되면 ul.some.com/blah~ 도메인이 호출되면서 앱이 있는지를 확인하고 앱이 있는 경우에는 앱을 실행, 없는 경우에는 302 리다이렉션 응답에 앱 설치 주소를 내려주는 방법을 사용하여서 해결하였다. (오~~ 굳!)

주의할 점

apple-app-site-association 파일을 변경하더라도 앱이 실행되지 않는 경우가 발생해서 기능이 동작하지 않는 것으로 생각한 경우가 많았었는데 이 경우는 관련 앱을 삭제하면 된다. 앱 설치할 때 해당 파일을 캐시하는 것으로 추측된다. (이 망할놈들은 기술 문서에 iOS 내부 동작에 대한 자세한 설명이 없다.)

이상, 개삽질 공유.. 끝.

끝없는 삽질이 추가되었다.
UniversalLinkNotWorked

  1. 유니버셜 링크 실행 시, xhr 호출이 포함되어 있는 경우 스킴 실행하는 경우처럼 팝업이 떴다.
    유니버셜 링크를 a태그의 href에 적용하거나 location.href 값을 바로 실행하는 경우에는 이슈가 없었으나 서버로부터 동적으로 받아와서 유니버셜 링크를 실행하는 경우에 발생.
  2. setTimeout()으로 실행하는 경우에는 동작하는 것으로 봐서는 비동기 상황인 경우보다는 실행 컨텍스트에서만 동작할 수 있는 (팝업 띄우기 같은) 이슈로 생각된다. 그러나, setTimeout()으로 실행하는 경우도 최초 1회는 성공하였으나 폴링방식으로 띄웠던 2회부터는 동작하지 않는 이슈가 있었다.
  3. setInterval()의 경우 정상적으로 동작하였으나 두 번째 실행할 때부터는 오동작 할 때와 동일한 이슈가 생겼다.
  4. 페이지 새로 고침하는 경우 또는 location이 변경되었을 때는 정상 동작하는 것으로 보고 location을 초기화 또는 리셑하는 방법을 찾아보았고 http://stackoverflow.com/questions/32689483/ios9-try-to-open-app-via-scheme-if-possible-or-redirect-to-app-store-otherwise 글의 https://bartt.me/openapp 에서 loaction.href.reload()를 초기화하는 것을 보고 시도한 결과 앱띄우는데 성공하였다…

과연 다음 버전 업데이트 할 때까지 이 꼼수가 먹힐런지는…

추가 삽질

특정 url로 페이지 진입시 location.href를 변경해서 유니버셜 링크로 포워딩 시켰는데 스토어로만 이동하는 문제가 있었다. 이 경우에 대해서는 기존 스킴 방식으로 띄우도록 수정;;;

 

gitignore 문서

https://git-scm.com/docs/gitignore

gitignore 문서에서 관심있는 내용만 메모

  • #로 시작하는 줄은 주석이다.
  • 두 개가 연속으로 나오는 별표는 다음의 의미를 갖는다.
    • “**/”로 시작되는 경우 전체 디렉토리를 의미한다.
    • “/**”로 끝나는 경우는 디렉토리 안의 모든 것을 의미한다.
    • “a/**/b”와 같이 중간에 나오는 경우는 “a/b”, “a/x/b”, “a/x/y/b” 등등등과 같이 디렉토리가 없거나 하나 이상의 디렉토리를 의미한다.
    • 그 외의 경우는 유효하지 않다.

 

 

Redux

Redux의 소개 글을 보면 왜 Redux를 써야 하는지 어떤 생각으로 설계를 하였는지를 설명하면서 코드 외에도 어떻게 짜라는 방법을 설명해주고 있다. Redux 글과 강의를 보다 내 나름대로의 정리가 필요해서 글로 정리하였다. 이 글은 Redux에 대한 소개를 다루지 않으며 Redux todo 프로그램을 기준으로 왜 이렇게 짜여지는 어떤 부분에 장점이 있는지를 다룬다.  todo  소스코드는 egghead.io에 소개된 redux 강의를 기준으로 하였으며(소개 글의 코드와 다르다) redux기본은 redux 사이트를 보는것을 추천 한글로 된 소개글은 태곤님의 소개글을 추천한다.

기능

Screen Shot 2015-12-18 at 10.57.17 PM일단, 예제 프로그램으로 소개되고 있는 todo 프로그램의 기능은 다음과 같다.

  1. 상단의 input을 통해서 todo를 추가 할 수 있다.
  2. 추가된 todo는 중간의 todo 목록으로 보여지며 todo를 클릭하면 완료 여부를 표시할 수 있다.
  3. 하단의 필터는 입력한 todo 목록을 상태별로 나눠서 볼 수 있게 해준다.

위 기능들이 동작을 할 때 현재 프로그램이 어떤지를 저장한게 상태(State)라고 한다. todo 프로그램에서는 해야  할 일을 정의한 todo와 이들의 목록인 todos 그리고 현재 화면에서 어떤 타입의 todo를 보여주고 있는지 여부인 visibilityFilter가 todo 프로그램의 상태로 저장되는 값이다.

State

redux_model

프로그램의 모든 상태들이 이 상태 트리 하나에 저장된다. MVC의 모델이 지녀야 할 값 외에도 프로그램의 현재 화면 상태, View의 상태도 이 트리에 모두 저장한다. (Single source of truth는 redux의 삼원칙중 처음에 나온다.) 상태 트리를 하나에 모두 저장하게 되면 현재 프로그램이 어떤 상태인지, 어떻게 변했는지를 알기가 쉬우며 디버깅도 쉬워진다. 프로그램 구현시 어려운 부분인 do/undo도 상태의 전/후를 저장하면 쉽게 처리할 수 있다.

Redux_동기화케이스

복수 개의 모델을 가지는 경우 단위별 개발이 빠르고 쉽게 만들 수도 있지만, 모델 상호간에 의존된 값을 사용하게 되는 경우 동기화를 해줘야 하는 이슈가 발생할 수도 있고 동기화를 하게 되는 경우는 각 모델간의 생성 순서나 동기화 순서가 이슈가 될 수 있다. Flux를 사용할 때 복수 개의 class로 만들어진 store를 사용할 때, 사용자의 요청에 따라서 응답하는 store들이 다른경우 서버 렌더링하는 경우에 응답에 따른 store 생성순서를 처리/관리하는 빙용이 생기게 되는데 한 개의 트리에 모든 상태를 저장하게 되면 이러한 이슈들을 해결할 수 있게 된다.

상태 트리를 만들 때는 트리의 구성은 다음의 기준을 제안하고 있다.

  • 어떠한 중첩된 구조 없이 가능한한 정규화된 상태를 유지한다.
    (샘플 코드에서는 nomalizr를 사용하여 중첩 데이타를 펼쳐서 사용)
  • 모든 엔터티들이ID를 키로 가지게 하고 엔터티 또는 리스트에서 특정 엔터티를 참조하고 싶을 때는 ID를 사용한다.
  • 더 자세한 것은 normalizr의 기준을 참고한다.

reference

Action

Ajax 응답을 받거나 사용자 인터랙션이 발생하였을 때 변경사항을 상태 트리에 알려주는 것은 액션(action)이다. 액션은 순수 객체로 만들어진다.  액션은 무엇이 변경되었는지를 표현하기 위한 type을 가진다. 액션은 최소한의 데이타로 표현되어야 하며 이 액션이 적용되었을 때 상태가 어떻게 바뀌었는지 유추할 수 있어야 한다.

todo 프로그램에서 액션은 다음의 3가지를 정의하고 있다.Redux_Actions

액션을 정의할  때는 몇 가지 컨벤션을 권장한다.

  • 액션의 타입은 문자열 상수 (const string)로 정의한다.
    • 심볼(Symbol)을 사용할 수도 있지만 문자열과 달리 기록하고 다시 실행하기 어려운 문제점을 가지고 있기 때문에 권장하지 않는다.
  • 상수를 사용하는 것을 권장한다.
    • 액션종류들을 한 곳에 모음으로써 어떠한 상수가 있는지 확인하기 쉽다.
    • 액션들의 이름을 짓는경우에 대한 일관성을 가질 수 있다.
    • PR에 올라오는 액션종류(추가,삭제,수정)에 따라 기능이 얼만큼 작업이 되었는지 같은 팀원이 인지하기 쉬워진다.
    • 상수를 가져다 사용하기 때문에 오타가 발생할 여지를 줄인다.
  • 액션을 생성할 때는 문자열 리터럴을 그대로 사용하기 보다는 액션을 생성하는 메소드를 사용하는 것을 권장한다.
    • 액션을 생성하는 곳이 여러 곳인 경우 함수만 변경 함으로서 한 번에 수정이 가능하다.

Screen Shot 2015-12-31 at 9.31.16 AM

Reducer

어떠한 액션에 대해서 상태를 어떻게 변경할지를 결정하는 것은 Reducer다. Reducer는 함수로 구성이 되며 이전 상태와 액션을 받아서 변경된 상태를 리턴한다. Reducer는 순수함수로 부수 효과가 없는 함수여야 한다. 즉, 자신이 처리할 액션인 경우 인자로 받은 상태에 변경 후의 새로운 상태를 만들어 리턴함으로서 참조 객체 변경으로 인한 오류를 막을 수 있게 된다. Reducer는 체인 형태로 동작해야 하기 때문에 자신이 처리하지 않는 액션에 대해서는 항상 인자로 받은 상태를 리턴해줘야 한다.

Redux_reducer

위의 그림을 예로 리듀서를 설명해보면 ADD_TODO 액션과 상태 트리를 받아서 새로운 상태 트리를 만들었지만 액션과 관련된 트리만 새롭게 변하고 필터와 관련된 좌측 부분은 그대로 유지된다. 상태 트리가 무한정 커질 수도 있지만 필요한 트리의 가지만 변경된다는 점과 변경안된 부분의 레퍼런스가 유지되어서 해당 값을 참조하는 곳에서 빠르게 동작할 수 있다는 장점이 있다.

Redux에서는 생성한 Reducer들을 함께 묶어주는 combinedReducers()를 제공해주는데 todo 프로그램에서는 할일 목록들을 관리해주는 todos와 필터 설정과 관련된 visibilityFilter를 사용하고 있으며 각각의 함수들은 자신의 관심사에만 집중한 형태로 구성된다. todos 함수는 할일 전체 목록에 대해서만 처리하며 할일 자체에 대해서는  todo 함수를 통해 다시 할당하게 된다.

Redux_separate_concern.png

아래는 todos와 todo를 처리하는 리듀서로 변경된 상태는 항상 새로운 객체를 생성해서 리턴하고 변경되지 않은 경우에는 기존 상태를 리턴하는 것을 확인할 수 있다.

Redux_reducer

Store

Screen Shot 2015-12-31 at 9.32.39 AM

스토어의 기본 골격은 위와 같다. 관찰자들을 등록하고 액션을 받았을 때 이를 전파시켜주는게 기본 골격이다. 즉, subscribe()를 통해 상태 트리가 변경된 경우 이를 청취할 함수를 등록하고 dispatch()를 통해 발생한 액션을 받으며 현재 상태를 얻어갈 수 있도록 getState()를 제공한다.

todo 프로그램에서는 사용자가 클릭과 같은 동작을 실행했을 때 액션을 생성하고 이를 store.dispatch(action)에 전달한 다음 store에 저장되어 있는 reducer들을 실행하면서 변경사항을 생성한 다음 새로운 상태 트리를 render()에 전달하게 된다. render()가 호출될 때 상태 트리의 속성들은 react의 속성(props)로 전달되어 리액트 컴포넌트가 새로 그려질 때 사용되게 된다.

Redux_flow

화면이나 네트워크에서 액션이 발생하여 스토어로 전달되고 스토어에서 생성된 새로운 상태가 다시 화면으로 전달되어 화면이 갱신되게 된다. 흐름을 보게 되면 단방향으로 구성되며 여기에는 동기 흐름만이 존재하며 비동기 처리는 middleware를 통해 이 흐름 밖에서 처리하게 된다.

Container component

액션으로 변경된 상태트리를 받는 리액트 컴포넌트를 Container component라 부른다. Todo  프로그램에서는 App(강의 코드에서는 TodoApp)이 여기에 해당된다. Container component의 아래에는 Presentational component들로 구성되며 르는 redux를 알지 못하면서 화면 그리는데 집중하는 요소들이 있다.

Redux_component

TodoList에서는 상태트리에서 todos 목록만 인자로 받아서 화면에 그리고 todo 항목을 클릭했을 때 이벤트를 배포하는 함수를 인자로 받아서 이벤트 핸들러에 설정하는 형태로 층을 분리해서 구성하고 있다.

Middleware

Middleware는 액션이 발생되어서 전파되는 시점부터 reducer에 도착하는 지점 사이에 서드파티를 추가할 수 있는 기능을 제공한다.(the moment it reaches the reducer로 사용되었는데 이 의미가 리듀서의 시작점이 아니라 리듀서 종료 지점으로 봐야할 것 같다. Implement middleware도 참조)  액션이 발생하여 상태 트리가 변경되었을 때 전/후의 흐름을 체크하거나 충돌 리포터를 사용하여 실행 시 예외를 체크 보고 할 수 있는 기능을 체인 형태로 제공할 수 있다. (Redux 의 Middleware 항목을 보면 Middleware를 어떻게 만들었는지, 개념에 대해서 잘 소개 되어 있다.)

Redux_concept

Middleware 개요 부분의 코드만 추려보면 다음과 같다. (실제 코드와는 다름에 주의!) store와 dispatch를 인자로 받아서 middleware의 코드를 적용한 dispatch를 리턴해서 액션이 발생하면 적용된 middleware들을 모두 실행하게 된다. Redux에서는 Middleware를 제외하고는 동기화 흐름만 지원하기 때문에 비동기 동작들은 Middleware로 구현되며 Ajax 호출과 같은 경우는 Thunk를 사용해 구현된다.

Redux_middleware_code

 Thunk

Redux에서 비동기 동작은 Redux Thunk Middleware를 통해서 실행된다. 인자로 받는 액션이 함수인 경우 이를 실행시켜 주는 간단한 함수이지만 비동기 동작을 통한 액션 발생을 함수 안으로 캡슐화 시켜주는 기능을 하게 된다.

thunkMiddleware

참고 자료

※ redux 문서를 보다보면 지속적으로 용어나 설명이 추가/변경이 되고 있어서 가장 최신 내용은 영문의 공식 문서를 보시기를 추천합니다. “왜?”라는 질문과 함께 ‘이렇게 풀었다’라는 설명을 보다보면 개발문서의 정석으로 느껴지네요.

XMLHttpRequest 동기 요청시 실패

synchronous_fail크롬에서 다음 뉴스를 보다 광고 로딩에 실패해서 개발자 도구를 열었더니 다음의 메시지가 나왔다.

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help, check http://xhr.spec.whatwg.org/.

메신 스레드에서 XMLHttpRequest를 동기로 호출하는 방법이 디프리케이트 되었다는 얘기.
바꿔야 할건 없겠지만 변경사항을 강요받는 것 같아서 살짝 짜증이… -_-;;;

Omnigraffle, 사각형의 한쪽면만 둥글게 만들기

Quora에 소개 되었던 한쪽면이 라운딩 처리 된 사각형 만들기

기본적인 라운딩 처리는  ‘Inspectors > Corner Radius’에서 가능하지만 모든 코너에 라운딩이 적용되어서 한 쪽면에 적용하려면 여러 도형을 겹쳐서 생성했어야 했는데 Quora에서 재미있는 방법이 소개되어서 메모.

omnigraffle_rounded_one_sides

출처: Quora – How do I make a tab in Omnigraffle with rounded corners on top with a flat bottom?

Mac, bash_profile을 사용하여 서버띄우기

맥에서 IE8 관련 작업을 한 후, VMWare에서 확인을 어떻게 쉽게 할 수 있을까 고민하고 있을 때 ohgyun이 팁을 알려주었다. .bash_profile은 시스템 로그인 할 때마다 실행되는데 여기에 다음과 같이 파이썬으로 서버를 실행하는 코드를 추가하면 쉽게 로컬에 서버를 띄운 후, VMWare에서 확인하면 된다는 것

  1. 먼저 .bash_profile을 만든 다음 vi ~/.bash_profile
  2. 서버를 띄우는 함수를 만들어서 추가한다.
    function server() {
            local port="${1:-8000}"
            open "http://localhost:${port}/"
            # Set the default Content-Type to `text/plain` instead of `application/octet-stream`
            # And serve everything as UTF-8 (although not technically correct, this doesn’t break anything for binary files)
            python -c $'import SimpleHTTPServer;\nmap = SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map;\nmap[""] = "text/plain";\nfor key, value in map.items():\n\tmap[key] = value + ";charset=UTF-8";\nSimpleHTTPServer.test();' "$port"
    }
    
  3. 콘솔에서 실행할 파일들이 있는 디렉토리로 가서 server를 치면 현재 디렉토리를 기준으로 서버를 띄우게 된다.

ohgyun 만쉐~

github 이메일 설정

이슈: github에 코드를 커밋했는데 반영이 안되었다.

해결책: 로컬 저장소에 설정된 이메일 주소가 GitHub 계정의 이메일 설정에 추가되어야지만 반영이 된다.(by Alan) 사내 github의 경우 사내 메일 주소를 제공하기 대문에 github.com에서 쓰던 메일 주소가 달라서 발생한 문제도 있었는데…

git 이메일 설정: https://help.github.com/articles/setting-your-email-in-git/