๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Web_Project

ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๐Ÿ–ฅ๏ธ Front-End

โœ… ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋˜๋Š” babel-preset-react-app์ด @babel/plugin-proposal-private-property-in-object ํŒจํ‚ค์ง€๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์„œ ๊ฒฝ๊ณ  ๋ฐœ์ƒ

// ์„ค์น˜๋กœ ํ•ด๊ฒฐ 
yarn add --dev @babel/plugin-proposal-private-property-in-object

 

โœ… Promise๊ฐ€ state์— ๋“ค์–ด๊ฐ€ ์žˆ์–ด์„œ ์ƒ๊ธด ์ฑ„๋„ํ†ก ์˜ค๋ฅ˜. Redux state์—๋Š” ๋น„๋™๊ธฐ ์ž‘์—… ๋˜๋Š” Promise์™€ ๊ฐ™์€ ๊ฐ’์ด ํฌํ•จ๋˜์–ด์„œ๋Š” ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ.

// ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€
serializableStateInvariantMiddleware.ts:212 A non-serializable value was detected in the state, in the path: `channelTalk`. Value: Promise 
Take a look at the reducer(s) handling this action type: channelTalk/bootChannelTalk.
(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)

 

โœ… redux-thunk๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•จ.

import { createSlice } from "@reduxjs/toolkit";
import * as ChannelService from "@channel.io/channel-web-sdk-loader";

// ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด thunk๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์•ก์…˜ ์ƒ์„ฑ์ž
export const bootChannelTalk = () => async (dispatch) => {
  const { REACT_APP_CHANNEL_PLUGIN_KEY } = process.env;

  // ์Šคํฌ๋ฆฝํŠธ ๋กœ๋“œ ํ›„ ์ต๋ช…์œผ๋กœ ๋ถ€ํŠธ
  ChannelService.loadScript();
  ChannelService.boot({
    pluginKey: REACT_APP_CHANNEL_PLUGIN_KEY,
  });

  // ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ๋™๊ธฐ์ ์ธ ์•ก์…˜์„ ๋””์ŠคํŒจ์น˜
  dispatch(channelTalkSlice.actions.bootChannelTalkSuccess());
};

const channelTalkSlice = createSlice({
  name: "channelTalk",
  initialState: {},
  reducers: {
    bootChannelTalkSuccess: (state, action) => {
      // ๋ถ€ํŠธ ์„ฑ๊ณต ํ›„ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง ์ž‘์„ฑ
    },
    // ... ๋‹ค๋ฅธ ์•ก์…˜ ๋ฐ ๋ฆฌ๋“€์„œ๋“ค
  },
});

export const { bootChannelTalkSuccess } = channelTalkSlice.actions;
export default channelTalkSlice.reducer;

 

โœ… styled-components์—์„œ props๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋œฌ ๊ฒฝ๊ณ 

styled-components: it looks like an unknown prop "pt" is being sent through to the DOM, which will likely trigger a React console error. If you would like automatic filtering of unknown props, you can opt-into that behavior via `<StyleSheetManager shouldForwardProp={...}>` (connect an API like `@emotion/is-prop-valid`) or consider using transient props (`$` prefix for automatic filtering.)

index.js์—์„œ StyleSheetManager๋กœ App ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์ค€ ๋‹ค์Œ shouldForwardProp์†์„ฑ์— true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ถ”๊ฐ€. ์ž„์‹œ props์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ฒŒ ํ•ด์คŒ.

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <ThemeProvider theme={theme}>
        <StyleSheetManager shouldForwardProp={() => true}> {/* ์ถ”๊ฐ€ */}
          <GlobalStyle />
          <App />
        </StyleSheetManager>
      </ThemeProvider>
    </Provider>
  </React.StrictMode>
);

 

 

styled-components: API Reference

API Reference of styled-components

styled-components.com

 

 

โœ… FE&BE ๊ฐ„ Cookie ์ „๋‹ฌ ์ด์Šˆ (localhost:3000์—์„œ ํ† ํฐ์ด %25๊ฐ€ ๋ถ™์–ด์„œ ๋‚˜์˜ค๋Š” ๋ฌธ์ œ)

 

- ํ”„๋ก ํŠธ์—”๋“œ๋Š” localhost:3000์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๊ณ , ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋Š” IP ์ฃผ์†Œ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋„๋ฉ”์ธ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค. ์‹ค์ œ๋กœ same-origin policy์— ์–ด๊ธ‹๋‚˜๊ฒŒ ์ ‘๊ทผ์„ ํ•˜๋ฉด, ๋„คํŠธ์›Œํฌ ํƒญ set-cookie ๋ถ€๋ถ„ ํ† ํฐ ๊ฐ’ ์˜†์— ! ๊ฒฝ๊ณ  ์‚ฌ์ธ์ด ๋–ด๋‹ค.

- ๋„๋ฉ”์ธ์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด ์ด๋ฏธ ๋ฐฐํฌ๋œ ํ”„๋ก ํŠธ์—”๋“œ ์‚ฌ์ดํŠธ์—์„œ ์ž‘์—…ํ•˜๋‹ˆ ํ† ํฐ์ด ์ •์ƒ์ ์œผ๋กœ ์ƒ๊ฒผ๋‹ค.

 

โœ… Redux์—์„œ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด redux-persist๋ฅผ ์ ์šฉํ•˜๋‹ค๊ฐ€ Uncaught TypeError: baseReducer is not a function ์—๋Ÿฌ ๋ฐœ์ƒ

- ์ฃผ์–ด์ง„ ์ฝ”๋“œ์—์„œ "baseReducer"๋ผ๋Š” ๋ณ€์ˆ˜ ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๊ธฐ๋Œ€ํ•œ ํ˜•์‹์ด ์•„๋‹ˆ๋ผ๋Š” ์—๋Ÿฌ์˜€๋‹ค.

๐Ÿ”ฅ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

- redux-persist๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” configureStore์— ์ „๋‹ฌ๋˜๋Š” reducer๋Š” ํ•จ์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค.

  - authslice.js ํŒŒ์ผ์—์„œ authslice → authReducer๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

- ์ถ”๊ฐ€๋กœ rootReducer ๊ฐ์ฒด๋ฅผ ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. store.js ํŒŒ์ผ์—์„œ combinedReducers ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

 

๐Ÿ”ฅ redux-persist ์ ์šฉ์ด ์™„๋ฃŒ๋˜์—ˆ์œผ๋‚˜ non-serializable ์—๋Ÿฌ ๋ฐœ์ƒ

- ์ด ์˜ค๋ฅ˜๋Š” Redux์˜ serializable action์„ ์œ ์ง€ํ•˜๋Š” ์›์น™์— ์–ด๊ธ‹๋‚˜๋Š” ๋น„์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์€(non-serializable) ๊ฐ’์ด ์•ก์…˜์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

- ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด middleware๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ๊ณ ์‚ฌํ•ญ์ด์ง€๋งŒ, ๋„๋Š” ๋ฐฉ๋ฒ•๋„ redux toolkit ๊ณต์‹๋ฌธ์„œ์—์„œ ์•Œ๋ ค์ค€๋‹ค.

 

Usage Guide | Redux Toolkit

 

redux-toolkit.js.org

- store.js ํŒŒ์ผ์—์„œ serializableCheck๋ฅผ false๋กœ ์ง€์ •ํ•œ๋‹ค.

const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      // Serializable ์ฒดํฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ non-serialization ๊ฐ€๋Šฅํ•œ ๊ฐ’ ์‚ฌ์šฉ ํ—ˆ์šฉ
      // ํŠน๋ณ„ํ•œ ์ƒํ™ฉ์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ, ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•œ ์„ค์ •
      serializableCheck: false,
    }),
});

 

โœ… [Violation] Avoid using document.write() ๊ฒฝ๊ณ 

 

document.write()์— ๋Œ€ํ•œ ๊ฐœ์ž…  |  Blog  |  Chrome for Developers

Chrome์—์„œ document.write()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”๊ฐ€๋œ ์ผ๋ถ€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฐจ๋‹จํ•จ

developer.chrome.com

์š”์•ฝํ•˜์ž๋ฉด document.write()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ์ฑ„๋„ํ†ก์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. ์ฑ„๋„ํ†ก์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๊ฒฝ๊ณ ๋Š” ๋‚จ๊ฒจ๋‘์–ด์•ผ ํ•œ๋‹ค.

์ฑ„๋„ํ†ก์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฝ๊ณ ์ฐฝ์„ ๋‚ด๋ฒ„๋ ค ๋‘๊ธฐ๋กœ ํ–ˆ๋‹ค.

  • ์ฑ„๋„ํ†ก์—์„œ ์‚ฌ์šฉํ•˜๋Š” document.write() ๋ถ€๋ถ„
var t = document.getElementById("ch-plugin-script-iframe"),
  r = !1,
  o = function () {
    var e = t.contentDocument || t.contentWindow.document;
    e.open(),
      e.write(
        '<!DOCTYPE html><script async type="text/javascript" src="https://cdn.channel.io/plugin/ch-plugin-core.446b7109.vendor.js" charset="UTF-8"></script>'
      ),
      e.write(
        '<script async type="text/javascript" src="https://cdn.channel.io/plugin/ch-plugin-core-20240205163803.js" charset="UTF-8"></script>'
      ),
      e.write(
        '<html lang="en"><head><meta charset="utf-8"></head><body><div id="main"></div></body></html>'
      ),
      e.close(),
      (r = !0);
  };

t.onload || o(),
  (t.onload = function () {
    r || o();
  });