2020-08-08 WIL

시간을 쪼개서 그날 한 일을 정리하고, 모르는 것을 찾아보는데 시간을 적당히 사용하지 못하고 있다. 분발해야지.

React

1. StrictMode에서 두 번 렌더링

React에 redux 설정을 하고 첫 테스트를 하는 과정에서 페이지가 두 번씩 렌더링되는 증상을 발견했다.

처음에는 redux 설정이나 hook을 잘 못 사용한 것이 아닌지 꼼꼼하게 검토해봤는데 이상한 점을 찾을 수 없었다.

원인을 찾기 위해 검색하다보니 공식 페이지에서 원인을 발견했다.

https://ko.reactjs.org/docs/strict-mode.html

위의 메서드들은 여러 번 호출될 수 있기 때문에, 부작용을 포함하지 않는 것이 중요합니다. 이 규칙을 무시할 경우, 메모리 누수 혹은 잘못된 애플리케이션 상태 등 다양한 문제를 일으킬 가능성이 있습니다. 불행히도, 보통 이러한 문제들은 예측한 대로 동작하지 않기 때문에 발견하는 것이 어려울 수 있습니다.

Strict 모드가 자동으로 부작용을 찾아주는 것은 불가능합니다. 하지만, 조금 더 예측할 수 있게끔 만들어서 문제가 되는 부분을 발견할 수 있게 도와줍니다. 이는 아래의 함수를 의도적으로 이중으로 호출하여 찾을 수 있습니다.

  • 클래스 컴포넌트의 constructor, render 그리고 shouldComponentUpdate 메서드
  • 클래스 컴포넌트의 getDerivedStateFromProps static 메서드
  • 함수 컴포넌트 바디
  • State updater 함수 (setState의 첫 번째 인자)
  • useState, useMemo 그리고 useReducer에 전달되는 함수

StricMode가 설정되어 있었고, 제거했을 때 렌더링이 한 번만 되는 것을 확인했다.

development 모드에서만 적용되고, production 모드에서는 두 번 호출되지 않는다고 하니 안심하고 사용하자.

2. render warning -> useEffect로 해결

Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.

위와 같은 warning이 발생했을 때 평소에 못 보던 warning이라 순간 당황했다.

warning이 발생한 코드는 다음과 같다.

import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import useLogin from './hooks/useLogin';
import Main from './Main';
import * as ROUTES from './routes';

export default function MainContainer({ history }: RouteComponentProps): JSX.Element {
  const { isLoggedIn, loading } = useLogin();

  if (!loading && isLoggedIn) {
    history.push(ROUTES.ADDITIONAL_INFO);
  }

  return <>{!isLoggedIn && <Main />}</>;
}

코드만 봐도 바로 이상한 것을 발견하는 분도 계시겠지만, 원인을 찾지 못해서 한참 고생을 했다.

위 코드를 다음과 같이 수정하여 해결했다.

import React, { useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import useLogin from './hooks/useLogin';
import Main from './Main';
import * as ROUTES from './routes';

export default function MainContainer({ history }: RouteComponentProps): JSX.Element {
  const { isLoggedIn, loading } = useLogin();

  useEffect(() => {
    if (!loading && isLoggedIn) {
      history.push(ROUTES.ADDITIONAL_INFO);
    }
  }, [loading, isLoggedIn, history]);

  return <>{!isLoggedIn && <Main />}</>;
}

loading, isLoggedIn state의 변화가 있을 때 history.push를 이용하여 page 전환을 시도했다.

이것을 useEffect 등을 이용하지 않고 직접 시도한 것이 문제의 원인이었다.

이전에는 이런 코드를 작성하지 않아서 warning을 못 만났던 것 같은데 이번엔 왜 그랬을까.

3. axios default 설정

import axios from 'axios';
import getTokenFromLocalStroage from './utils/getTokenFromLocalStroage';
import * as ROUTES from './routes';

export const setTokenInHeader = (): void => {
  const token = getTokenFromLocalStroage();
  if (!token) return;
  axios.defaults.headers.common.Authorization = `Bearer ${token}`;
};
setTokenInHeader();

axios.defaults.baseURL = 'http://localhost:3030/';

서버에서 token을 받아 로컬 스토리지에 저장하고 header에 실어서 서버에 보내기로 정했다.

그래서 axios에 위와 같이 default 설정을 했다.

page를 reload하면 token이 사라지는 경우가 있어 setTokenInHeader를 함수로 만들고 필요할 때 마다 호출하도록 했다.

HTML CSS

1. 반투명한 header에 가려지는 element가 header에 비쳐보일 때

반투명한 header에 가려지는 element에 position: relative를 설정하면 header에 element가 비쳐보인다.

이것을 해결하려면 해당 element의 z-index를 header보다 낮게 설정하면 된다.

Android

1. android web view에서 title 영역을 제거

app > res > value > styles.xml

windowActionBar false, windowNotitle true를 설정한다.

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

</resources>

ETC

1. hammerspoon을 이용한 key binding 성공

esc와 한영전환을 키 하나로 처리하는 lua script 작성에 성공했다.

hs.hotkey.bind({"ctrl"}, "[", function()
  local inputEnglish = "com.apple.keylayout.ABC"
  local input_source = hs.keycodes.currentSourceID()

  if not (input_source == inputEnglish) then
    hs.keycodes.currentSourceID(inputEnglish)
    hs.eventtap.keyStroke({}, 'right')
  end

  hs.eventtap.keyStroke({}, 'ESCAPE')
end)

Written by@[Suho]
뭐든지 만들어보자.