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/

Menthas

Qiita에 재미있는 글이 올라왔다. Menthas라는 서비스로 개발과 관련된 글들을 큐레이션하는 서비스 인데, 이 서비스를 소개하는 글이 참 매력적으로 쓰여졌다.

  1. 들어가는 글
  2. 서비스가 만들어진 배경
    • 개발자들이 정보를 어디서 었는지를 정의하고 이 때의 단점(주변 사람들로부터 얻는 정보들이다 보니 다양한 정보가 아닌 필터링된 정보를 받게 된다.  정보를 얻는 곳에서 개발자가 꼭 필요하지 않은 정보들도 노출된다. 등등)들을 정리하면서 대안을 제시한다.
    • 배경을 정리하면서 서비스가 만들어진 목표를 정리한다.
  3. 서비스 개요
  4. 서비스를 어떻게 사용하는지
  5. 서비스의 특징
    1. 서비스의 메커니즘과 덧글 알고리즘 (큐레이션에 대한 구체적인 설명)
    2. 큐레이터의 선출 방법
    3. 자문회를 통한 필터링 시스템
  6. 구현
  7. 결론과 서비스의 성장 방향

개발자들을 대상으로 하는 서비스라서 구체적으로 설명하는 글이 될 수도 있지만 내 서비스가 왜 만들어졌는지 누구를 타겟으로 하고 있는지를 채용하는 사람 또는 함께 갈 사람에게 소개하는 방식으로 참 정답으로 느껴졌다.

여기에 소개된 서비스: http://menthas.com/javascript