Redux > subscribe()

업무 때문에 Redux 코드를 들여다 보다가 subscribe() 구성이 재미있게 되어서 정리를 해보았습니다. (TS는 아직 익숙하지 않아서 3.7.2 버전 기준입니다. )

이벤트 등록/해제

이벤트 저장소는 배열(listeners = [])입니다. 등록되는 listener들을 subscribe()로 등록을 하고 등록을 해제하는 함수를 리턴해 줌으로서 listener들을 개별적으로 관리하는 작업을 사용자 측이 관리하도록 처리를 넘겼습니다. 중복 등록 해제를 제거하기 위해서 isSubscribed 변수를 두어서 등록해제 하였는지를 체크하고 unsubscribe 함수의 지역 변수로 관리하는 형태를 취하고 있네요. (쉽게 설명하기 위해서 current/next listeners를 하나로 통합하였습니다.)

function createStore () {
  let listeners = []

  function subscribe (listener) {
    let isSubscribed = true
    listeners.push(listener)

    return function unsubscribe () {
      if (!isSubscribed) {
        return
      }
      isSubscribed = false

      const index = listeners.indexOf(listener)
      listeners.splice(index, 1)
    }
  }

listeners의 변조 방지

아마도, store의 이벤트를 청취하고 있는 listener 중 일부는 state가 변경된 경우 동적으로 청취를 멈추거나 추가하고 싶은 코드가 있었고요. 그런데 이 변경이 발생하는 도중에 dispatch()가 호출이 되면서 변경된 배열에 의한 이벤트 청취를 못하게 되는 경우가 발생하지 않았을까 싶은데요.

ensureCanMutateNextListeners()를 들여다보면 현재의 listeners들을 slice()함수로 새로운 배열로 생성을 합니다. 이미 nextListeners를 지정한 경우에는 최적화를 위해서 한 번만 생성하는 체크가 되어 있습니다.

  let currentListeners = []
  let nextListeners = currentListeners

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

이 함수는 listener가 추가/삭제되는 경우 먼저 호출이 되어 현재 dispatch()에서 처리되고 있는 listeners들에 영향을 미치지 않게 listener를 nextListeners에 추가/삭제하게 해줍니다.

  function subscribe (listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe () {
      if (!isSubscribed) {
        return
      }
      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

dispatch()의 코드도 살짝 보면 dispatch() 호출 시에 nextListeners를 currentListeners로 최신 청취자들로 갱신을 한 다음에 지역 변수에 담아서 dispatch 호출시에 등록되어 있던 listener들에게 이벤트 변경이 되었음을 알려주게 됩니다.

  function dispatch (action) {
    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    return action
  }

출처:
https://github.com/reduxjs/redux/blob/v3.7.2/src/createStore.js#L101
– Vuex: https://github.com/vuejs/vuex/blob/329828dba41dec59a6880e69a32f9da36c62f3bd/src/store.js#L105

2050 거주불능지구

살인적인 폭염 → 열사병, 온열 스트레스가 증가한다. 온도가 증가하면 작물의 이파리가 두꺼워진다. 이산화탄소 흡수능력이 떨어지고 생산량이 감소하게 된다. 북쪽 땅이 따뜻해지지만 비옥해지기까지 시간이 걸린다. 눈이 녹으면 햇빛을 반사시켜서 온도를 떨어뜨리던 (알베도 효과) 기능이 사라지고 온도도 증가하게 된다.

물이 진짜 부족하지는 않다. 정칙적인 문제에 가깝다. 수온이 상승하면 온수 친화적인 박테리아가 증가하게 되며 마실 물이 줄어들게되고 어류 자원도 고갈하게 된다. 바다의 온도가 상승하게 되면 바다가 더 많은 이산화 탄소를 가두게 되면서 해양이 산성화되고 산호초를 파괴하면서 어류 자원의 균형이 잃게 된다.

대기 오염의 증가는 인구를 감소하게 되지만 태양열을 반사시켜서 상승할 온도를 감소 시키는 역효과도 발생시킨다. 미세 플라스틱의 오염은 물에서도 발생하며 플라스틱 생성시 이산화 탄소를 생성하고 분해되면서 메탄과 에틸렌을 방출하는 오염을 발생시킨다. 북극 빙하가 녹으면 해수면이 상승하면서 옛 질병이 깨어나는 상황이 발생할 수 있다. 온도의 변경이 기존 질병이 이동하면서 열대지역의 병이 온대 지역으로 이동하고 있다. 공존해왔던 박테리아가 돌연 병균으로 변하는 상황도 발생한다.

온도 상승은 개인간의 폭력, 분쟁도 증가시키는데 0.5도 상승시 10~20% 상승하게 된다. 해수면의 상승하게 되면 저지대의 도시가 잠기는 것도 문제지만 방사능 정화가 덜 된 마셜제도도 잠기게 되므로 심각한 오염이 될 수 있다.

파타고니아, 파도가 칠 때는 서핑을

1. 역사

  • 우리 회사는 가진 자원과 한계를 모두 써버렸다…유지할 능력이 없는 성장에 의존하게 되었다. 이 문제를 무시하거나 문제가 사라져 버리기를 바라고만 있을 수는 없었다. 우선순위를 다시 생각하고 새로운 경영방침을 도입해야 했다.
  • 사명선언: “최고의 제품을 만들되 불필요한 환경 피해를 유발하지 않으며 환경 위기에 대한 공감대를 형성하고 해결 방안을 실행하기 위해서 사업을 이용한다.”
  • 사명선언(2019년부터): “우리는 우리의 터전, 지구를 되살리기 위해 사업을 한다.”

2. 철학

  • 제품 디자인 철학: 제품의 모든 측면을 명확하게 정의하는 품질 기준이 정립되자 어떤 것이 최고인지 판단하는 일이 어렵지 않았다…하지만 품질을 따질 때에는 오렌지를 사과와 비교하는 오류를 범하지 않는 것이 중요하다.
  • 구매자로 외주 공장에 영향력을 갖고 있다면 작업 환경을 개선하게 하여서 생태계를 개선해야 한다.
  • 함께 일하는 사람이 우리가 생각하는 방식으로 생각하도록 바꿔야 한다. 명세서 만으로는 부족하다. 디자인 기준에 맞게 일을 할 수 있는 필요한 지식과 도구를 갖춰야 한다.
  • 개미는 중앙의 제어가 없다. 단순한 상호작용으로 사회적 네트워크를 만든다. 네이비 씰처럼 모두 임무를 숙지할 수 있어야 한다. 팀원이 임무를 알고 리더가 기능을 못하는 경우 대체가 가능해야 한다.
  • 달인이 되는 것은 단순함을 향해 노력하는 것. 복잡한 기술대신 지식을 습득하는 것이다. 많이 알수록 필요한 것은 적어진다.

독학은 어떻게 삶의 무기가 되는가

전략: 독학의 방향성(테마)를 잡자.

  • 원하는 것보다 가진 것을 위주로 강점을 활용하자.

인풋: 독서 또는 미디어 (형태의 제약을 받지 말자)

  • 사람과의 만남을 통한 네트워크 형태의 공부도 있다.
  • 독학의 방향을 고정시키지 말자 우연하게 만나서 새로운 배움을 얻을 수도 있다.
  • 친숙한 것만 하려 하지 말고 친숙하지 않은 것도 일부러 찾아보자.
  • 쓰레기를 넣으면 쓰레기가 나온다.

질문은 모르는데서 생기는 것이 아니라 알고나서야 생긴다.

추상화, 구조화: 핵심을 추출하여 기본 매커니즘을 만든다.

  • 독학을 통해 배운 지식을 추상화 시켜서 모델링을 만들면 S’, S”와 같은 새로운 명제에도 적용할 수 있다.
  • 모델링을 통해 새로운 컨텍스트를 만들 수 있다.

축적: 디지털 또는 아날로그로 지식을 축적하자.

  • 축적을 하면 기억해야 하는 것들을 잊을 수 있다. 이 비용을 다른 곳에 집중할 수 있다.
  • 검색할 수 있게 태그를 붙이자. 태그들을 통해서 새로운 지식의 조합을 찾을 수 있다.
  • 좋아하는 것과 함께 반감의 요소도 축적하자.
  • 시사점 기록, 9군데 정도 요약할 요소를 골라내서 축적한다.

교보문고: 독학은 어떻게 삶의 무기가 되는가

Chrome dev 파일 덮어쓰기

Chrome dev에서 파일 덮어쓰기(Override)

  1. Sources -> Overrides 탭을 열고 로컬 폴더를 지정한다.
  2. Sources탭에서 js/html 파일을 수정하고 저장하면 로컬 폴더에 결과가 저장되고
  3. 웹 사이트를 새로 고침하면 기존 파일 대신에 위에서 저장한 파일이 돌아가는 것을 볼 수있다.

 

ISO 8601, Safari와 Chrome의 차이

Safari와 Chrome에서 날짜 처리가 다른 것은 알고 있었지만 ISO 8601 텍스트 처리가 다를 줄은 또 몰랐네요. 서버에서 받은 텍스트가 ISO8601 시간 텍스트를 문자로 받았고 new Date()로 생성 또는 Date.parse()한 결과가 둘이 다르게 해석됩니다. (아래 캡쳐 이미지는 위가 Chrome, 아래가 Safari)

Screen Shot 2019-06-21 at 9.11.40 AM

Screen Shot 2019-06-21 at 9.12.00 AM

Chrome에서는 ‘2018-04-16T00:00:00’ 시간에 대한 해석이 한국시간(GMT+0900) 기준의 2018년4월16일 00:00:00으로 해석하고 Safari에서는 2018년4월16일 00:00:00을 GMT+0000시간으로 계산한 다음에 한국시간(GMT+0900) 으로 보여지게 됩니다.

Screen Shot 2019-06-21 at 9.16.18 AM

Screen Shot 2019-06-21 at 9.16.49 AM

ISO8601의 타임존(TZD:Time zone Designator)를 직접 설정하게 되면 동일한 시간을 보여줍니다. 위에는 Zulu(GMT+0000)를 기준으로 입력하였고 아래는 한국시간(GMT+0900)으로 설정한 경우입니다.

Screen Shot 2019-06-21 at 9.20.37 AM

Screen Shot 2019-06-21 at 9.20.46 AM

참고자료 + 재미있게 읽은글:

 

nuxt에서 proxy 설정하기

nuxt에서 proxy가 필요했던 이유:

SPA 모드로 로컬에서 개발시 nginx 없이 API 서버를 호출하면서 결과값 기준으로 개발을 하고 싶었습니다.

Webpack의 devServer의 proxy

Nuxt에 webpack이 포함되어 있어서 webpack의 dev server가 들어가 있을거라 생각하고 nuxt의 build 옵션에서 proxy를 설정해봤는데요. 설정이 동작하지 않더군요… nuxt proxy로 검색한 결과물을 찾아보았는데 대부분이 서버렌더링을 사용한 경우라서 원하는 답을 찾지 못해서 nuxt 개발모드 코드를 살펴보았는데요.

dev 모드에서 nuxt 실행 따라가기

node_modules의.bin 폴더에서 dev 모드 실행시 호출 코드를 찾아봤는데요. Nuxt 인스턴스를 생성한 다음 코드 변경을 감지해서 새로운 인스턴스에서 nuxt.listen()을 호출합니다. listen() 코드(lib/core/nuxt.js)를 따라가면 renderer.app.listen()을 다시 호출하는데요.  여기의 app이 node connect 를 서버로 사용하는 것을 확인할 수 있습니다.

serverMiddleware

nuxt 인스턴스가 생성할 때 renderer의 ready()를 호출하는데요. 이 메소드 안에서 setupMiddleware()를 호출하고 여기에서 connect 서버에서 사용할 middleware를 등록합니다. dev인 경우에는 webpack dev middleware와 hot middleware를 등록하게 되며 사용자가 serverMiddleware 옵션을 설정한경우 이들을 connect에 등록하게 됩니다.

여기에서 사용되는 미들웨어는 nuxt의 connect 서버가 호출되기 전에 클라이언트 또는 서버사이드 렌더링 호출하기 전에 호출되게 됩니다. (nuxt의 routes middleware와 전혀 다릅니다.)

-webkit-touch-callout

일단 callout 또는 call-out이 뭔지 찾아봤는데 위키피디아에서는 무언가를 자세히 설명하기 위해 설명과 함께 선, 화살표 또는 비슷한 그래픽을 사용하는 것을 callout이라고 설명하고 있다. (computerhope의 callout 정의)

callout

-webkit-touch-callout은 사용자가 타겟을 터치하거나 꾹 누르는 경우(롱터치) 보여지는 기본 ui를 제어하는 속성이다. 비표준이며 iOS에서 터치 동작을 막고 싶을 때 사용할 수 있다.

출처: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-touch-callout

책 잘 읽는 방법

Book

책을 읽으면 잘 살수 있느냐는 질문에 저는 이렇게 답하고 싶어요. 정해진 운명보다 조금 더 나은 삶을 살 수 있다고요.

by 김봉진 님의 ‘책 잘 읽는 방법’에서

책에서 인용한 김영하 님의 “읽을 책을 사는 것이 아니라 산 책 중에서 읽는것이다.”도 마음에 와 닿는 글이었네요.