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와 전혀 다릅니다.)

후지산 등반

에어 서울의 민트 패스를 구매하는 바람에 시즈오카를 다시 가게 되었고 시즈오카에 가다보니 명물인 후지산에 가게 되었고 후지산에 가다보니 정상까지 가게되었습니다.

시즈오카 공항에서 후지산 가기

시조오카 공항에 도착해서 후지산까지 이동은 구글지도 경로를 사용했습니다.

  1. [1시간]시즈오카 역까지 공항버스를 이용
  2. [대기시간까지 포함하면 2시간] 시즈오카(Shizuoka) 역에서 고텐바(Gotemba) 역까지는 JR을 타고(Numazu 역 경유) 이동
    (고텐바에서 후지산 등반 시작점인 가와구치코 역(Kawaguchiko)까지 가는 버스가 밤에는 없어서 여기에서 1박을 하였습니다.)
  3. [비가 와서 1시간 반정도 소요] 고텐바에서 가와구치코역 까지는 버스(http://bus-en.fujikyu.co.jp/highway/)를 이용하였는데 계절 별 운행 시간이 달라집니다.
    (하산시 숙소가 가와구치코여서 해당 지역으로 이동 후 등산)
  4. [1시간 반정도 소요] 가와구치코 역에서 후지산 5고메까지 가는 버스는 왕복으로 끊으면 할인됩니다.

* 갔다오고 나서 알게된 내용을 추가하자면 나리타 공항에서 가와구치코로 바로 가는 버스가 있습니다. 그리고 시즈오카 역에서 가와구치코로 이동하는 버스가 하루 한 대 있었습니다.
** 고텐바에서 후지산 등반하는 코스는 상급자 코스라고 들어서 해당 방법은 제외하였습니다.

후지산 등반

후지산 등반은 공식 사이트(http://www.fujisan-climb.jp/kr/index.html)에서 확인하실 수 있습니다. 총 4개 루트이고 그 중 가장 쉽다는 요시다 등산로를 이용하였는데요. 공식 사이트에 나와있는 시간보다는 적게 걸렸습니다. (http://www.fujisan-climb.jp/en/i1f37q0000000l3h-att/Yoshida2018snow_en.pdf)

등산 길과 하신 길이 나눠져 있어서 주의하셔야 하구요. 산장(Toyo kan – 2800m 부근)에서 1박을 하였습니다. 후지산의 대부분 산장은 전화로 예약을 받고 있어서 예약을 못하고 있다가 토요칸에서는 영어로된 예약 페이지를 제공하고 있어서 여기로 예약을 하였습니다.

준비물은 Matcha 사이트(https://matcha-jp.com/ko/581)의 글을 보시면 충분할 것 같구요. 아쉬웠던 점을 꼽으라면 산 정상 바람이 무척 쎄서 방한복 준비를 잘 하셔야 되구요. 헤드렘프 대신에 손전등을 선택한게 아쉬운 점이었습니다. 화장실 이용은 200엔 동전을 넣어야 하기 때문에 준비를 잘 해두셔야 하며 산장에는 세수나 샤워할 수 있는 시설이 없기 때문에 티슈를 넣어가면 좋습니다. 2800m에서 컵라면이 500엔이었다면 올라갈면 갈 수록 100엔씩 올라갑니다. 산 정상에서는 800엔에 팔더군요.

4시 반 정도에서 해가 뜨는데요. 저희는 1시에 일어나서 1시 반에 출발하였음에도 불구하고 해 뜨기 전에 겨우 도착하였습니다. 정상으로 갈수록 길이 좁아지는데 체력이 떨어지는 분들에 의한 등반 지체가 심하기 때문에 1시 부근에서 올라가시는걸 추천드리고 싶네요.

이상 팁 공유 완료~

-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