실전 프로젝트 - 8

박경준·2021년 8월 31일
0

pwa와 service-worker

pwa 웹앱 적용을 위해서 service-worker가 반드시 필요했다. 하지만 우리 앱의 특성 상 상수로 가져와야 하는 데이터가 많지 않았고, 오히려 유저의 사용에 따라 계속 데이터가 변경되는 페이지들이 더 핵심 기능이었다. 때문에 캐싱을 요긴하게 쓸만한 영역이 없을것이라 생각했다.

redux-persist와 error boundary

새로고침을 하면 redux의 state 데이터는 날아가게 됨... 그런데 우리 앱의 특성 상 페이지가 넘어가면서 redux state를 그대로 이용해야 하는 경우가 많았는데, 특정 페이지에서 새로고침을 하면 데이터 에러가 나게 됨. 사용하려는 데이터가 undefined라던지 null이라던지...

  1. 1차적으로는 데이터 에러 페이지(ErrorBoundary.js)를 따로 만들어줬음.
    데이터 에러가 났을때, 기존에는 흰 화면을 띄워줬지만, 에러페이지를 띄워줌으로써 다시 홈으로 돌아가서 redux state를 재정돈할수도 있고, 유저에게도 에러 이후 상황을 케어해줄수 있었다.
    ErrorBoundary에서는 componentDidCatch life cycle 함수를 쓰기위해 클래스형 컴포넌트를 사용하였고 유저들은 어떤 데이터 에러를 겪는지 보기위해 Sentry를 연결시켜서 모니터링을 하고, 추후 에러 수정에 반영하여 쉽게 작업할 수 있었다.

// App.js
...
return (
    <ErrorBoundary>
      ...
    </ErrorBoundary>
  );
// ErrorBoundary.js
...
class ErrorBoundary extends Component {
  state = {
    error: false,
  };

  componentDidCatch(error, info) {
    logger('에러가 발생했습니다.');
    logger({
      error,
      info,
    });
    this.setState({
      error: true,
    });
    if (process.env.NODE_ENV === 'production') {
      Sentry.captureException(error, { extra: info });
    }
  }

  render() {
    if (this.state.error) {
      return (
        <Cont>
          <ImgContainer src={LoginBackground}></ImgContainer>
          <Text
            type="contents"
            textAlign="center"
            fontSize="18px"
            fontWeight="500"
          >
            찾으시는 데이터가 없습니다.
          </Text>
          <Text
            type="contents"
            textAlign="center"
            fontSize="15px"
            fontWeight="500"
          >
            메인으로 돌아가기
          </Text>
          <Image
            src={Home}
            width="40px"
            height="40px"
            _onClick={() => {
              history.replace('/');
              window.location.reload();
            }}
          />
        </Cont>
      );
    }
    return this.props.children;
  }
}

export default ErrorBoundary;
  1. ErrorBoundary를 이용해 에러 처리를 해주었지만, 에러가 안나게 하는것이 당연하게도 더 중요하지 않을까 하는 생각을 하게 됨...
    그 방안으로는 1) 로컬 DB를 사용한다. 2) 매 페이지마다 api를 호출해서 데이터를 state에 저장한다.
    두가지가 있었는데, 매 페이지마다 api를 호출하는건 당연하게도 서버 부하 관련해서 백엔드 팀의 저항이 있었고, 데이터가 렌더링까지 전달되는 속도도 느려질수 있기 때문에 좋지 않은 방법이라고 생각함.
    그렇다면 데이터를 로컬 DB에 저장해야 하는데, 로그아웃만 하지 않는다면, 계속 데이터가 남아있을수 있도록 로컬 스토리지를 이용해야겠다고 생각함.
    정말 우연히 redux를 로컬 DB와 연결해주는 redux-persist라는 기술을 알게 되었고 내 store.js에 적용해주었다. 이제 새로고침 해도 데이터 에러가 나지 않는다!
  • 캐시 스토리지 API, IndexedDB도 저장소로 활용할수 있을까 하고 고민해보았다.
    네트워크로 불러온 리소스나 파일을 캐싱해야 한다면 캐시 스토리지 API(Cache Storage API)를 활용하는 것이 좋았을것이다.
    IndexedDB는 말그대로 데이터 베이스의 형태를 하고있어서, 받아온 데이터를 실제 db에 저장하는 형태를 띄고 있었다. 이렇게까지 저장할 필요는 없었다.
// configStore.js

...
import { persistStore } from 'redux-persist';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

import Exercise from './modules/exercise';
import User from './modules/user';
import Challenge from './modules/challenge';
import Feed from './modules/feed';
import Calendar from './modules/calendar';

export const history = createBrowserHistory();

const rootReducer = combineReducers({
  exercise: Exercise,
  user: User,
  feed: Feed,
  challenge: Challenge,
  calendar: Calendar,
  router: connectRouter(history),
});

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['exercise', 'challenge', 'feed'],
};

const enhancedReducer = persistReducer(persistConfig, rootReducer);

const middlewares = [thunk.withExtraArgument({ history: history })];

...

const composeEnhancers =
  typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
      })
    : compose;

const enhancer = composeEnhancers(applyMiddleware(...middlewares));

let store = (initialStore) => createStore(enhancedReducer, enhancer);

export default store();
// index.js

ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById('root'),
);
profile
빠굥

0개의 댓글