<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jineecode</title>
    <link>https://jineecode.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 22:02:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>지니코딩</managingEditor>
    <image>
      <title>jineecode</title>
      <url>https://tistory1.daumcdn.net/tistory/4345723/attach/a0acf9347f4443818db8d5b9e04bebe3</url>
      <link>https://jineecode.tistory.com</link>
    </image>
    <item>
      <title>프리온보딩 프론트엔드 챌린지 3차</title>
      <link>https://jineecode.tistory.com/286</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;CSR(Client-side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트를 이용하여 브라우저에서 직접 페이지를 렌더링하는 것을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&lt;br /&gt;일단 로드되고 나면 사이트 내에서 돌아다닐 때 로드되는 과정이 없어지므로 사용성이 좋아진다.&lt;br /&gt;서버를 호출할 때마다 전체 UI를 다시 로드할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점:&lt;br /&gt;애플리케이션 규모가 커질수록 JS양이 증가하여 페이지가 다른 면에서 무거워질 수 있다.&lt;br /&gt;초기 페이지 로드에 더 많은 시간이 걸린다.&lt;br /&gt;SEO에 친화적이지 않다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유에 대하여 설명해주세요.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR의 단점을 극복하며 SPA의 장점도 가져갈 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSR을 하므로 SEO에 친화적이지 않은 CSR의 단점을 극복할 수 있다.&lt;/li&gt;
&lt;li&gt;초기 페이지 로드가 빨라진다.&lt;/li&gt;
&lt;li&gt;SPA로 구성되어 있으므로 html을 새로고침하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next.js 프로젝트를 세팅한 뒤 yarn start 스크립트를 실행했을 때 실행되는 코드를 nextjs github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;yarn start&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 서버에서 사용자에게 보여줄 html을 모두 구성하여 사용자에게 페이지를 보여주는 방식이라는 것을 인지하고 천천히 확인해 보았다.&lt;br /&gt;Next.js를 create 하면 '_app.jsx'을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import &quot;../styles/globals.css&quot;
import type { AppProps } from &quot;next/app&quot;

function MyApp({ Component, pageProps }: AppProps) {
  return &amp;lt;Component {...pageProps} /&amp;gt;
}

export default MyApp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyApp은 &lt;code&gt;Component&lt;/code&gt;와 &lt;code&gt;pageProps&lt;/code&gt;를 props로 받는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Component: 요청한 페이지. &lt;code&gt;/pages/index.jsx&lt;/code&gt; 파일이 props로 들어온다. &lt;code&gt;index.jsx&lt;/code&gt;를 삭제하면 404 Error 페이지가 뜬다.&lt;/li&gt;
&lt;li&gt;pageProps: &lt;code&gt;getInitialProps&lt;/code&gt;를 통해 내려 받은 props 이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nextjs repo&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 레포지토리를 확인해보자&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;cli&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;yarn start&lt;/code&gt;를 하므로 cli를 가장 먼저 확인해보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/cli/next-start.ts&quot;&gt;packages/next/cli/next-start.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;import { startServer } from &quot;../server/lib/start-server&quot;
import arg from &quot;next/dist/compiled/arg/index.js&quot;

// next Start
const nextStart: cliCommand = argv =&amp;gt; {
  const validArgs: arg.Spec = {
    // Types
    &quot;--help&quot;: Boolean,
    // ...
  }

  let args: arg.Result&amp;lt;arg.Spec&amp;gt;

  try {
    args = arg(validArgs, { argv })
    // 옵션 재할당
  } catch (error) {
    if (isError(error) &amp;amp;&amp;amp; error.code === &quot;ARG_UNKNOWN_OPTION&quot;) {
      return printAndExit(error.message, 1)
    }
    throw error
  }

  // 서버에서 쓰이는 변수들
  const dir = getProjectDir(args._[0])
  const host = args[&quot;--hostname&quot;] || &quot;0.0.0.0&quot;
  const port = getPort(args)
  const keepAliveTimeoutArg: number | undefined = args[&quot;--keepAliveTimeout&quot;]

  // ...

  // 서버 실행
  startServer({
    // startServer:  https://github.com/vercel/next.js/blob/canary/packages/next/server/lib/start-server.ts
    dir,
    hostname: host,
    port,
    keepAliveTimeout,
  })
    .then(async app =&amp;gt; {
      const appUrl = `http://${app.hostname}:${app.port}`
      Log.ready(`started server on ${host}:${app.port}, url: ${appUrl}`)
      await app.prepare() // app 구축 준비!
    })
    .catch(err =&amp;gt; {
      console.error(err)
      process.exit(1)
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 필요한 변수를 준비하고 서버를 스타트하는 것 같다.&lt;br /&gt;&lt;code&gt;startServer&lt;/code&gt;가 어떻게 서버구축을 해주고 있는지 찾아보았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;server&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/server/lib/start-server.ts&quot;&gt;packages/next/server/lib/start-server.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import type { NextServerOptions, NextServer, RequestHandler } from &quot;../next&quot;
import next from &quot;../next&quot;

export function startServer(opts: StartServerOptions) {
  let requestHandler: RequestHandler

  const server = http.createServer((req, res) =&amp;gt; {
    return requestHandler(req, res)
  })

  return new Promise&amp;lt;NextServer&amp;gt;((resolve, reject) =&amp;gt; {
    let port = opts.port
    let retryCount = 0
    let upgradeHandler: any

    server.on(&quot;listening&quot;, () =&amp;gt; {
      const addr = server.address()
      const hostname =
        !opts.hostname || opts.hostname === &quot;0.0.0.0&quot;
          ? &quot;localhost&quot;
          : opts.hostname

      const app = next({
        // next가 뭔가 해주고 있는 것을 발견
        ...opts,
        hostname,
        customServer: false,
        httpServer: server,
        port: addr &amp;amp;&amp;amp; typeof addr === &quot;object&quot; ? addr.port : port,
      })

      requestHandler = app.getRequestHandler()
      upgradeHandler = app.getUpgradeHandler()
      resolve(app)
    })

    server.listen(port, opts.hostname)
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;next&lt;/code&gt;가 어디에 있는지 찾아보있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/server/next.ts&quot;&gt;packages/next/server/next.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;export class NextServer {
  constructor(options) {
    this.options = options
  }

  get hostname() {
    return this.options.hostname
  }
  get port() {
    return this.options.port
  }

  getRequestHandler(): RequestHandler {
    return async (
      req: IncomingMessage,
      res: ServerResponse,
      parsedUrl?: UrlWithParsedQuery
    ) =&amp;gt; {
      const requestHandler = await this.getServerRequestHandler()
      return requestHandler(req, res, parsedUrl)
    }
  }

  getUpgradeHandler() {
    return async (req: IncomingMessage, socket: any, head: any) =&amp;gt; {
      const server = await this.getServer()
      // @ts-expect-error we mark this as protected so it
      // causes an error here
      return server.handleUpgrade.apply(server, [req, socket, head])
    }
  }

  // ...

  async prepare() {
    const server = await this.getServer()
    return server.prepare()
  }

  // ...

  async createServer(options) {
    if (options.dev) {
      const DevServer = require(&quot;./dev/next-dev-server&quot;).default
      return new DevServer(options)
    }
    const ServerImplementation = await getServerImpl()
    return new ServerImplementation(options)
  }

  async getServer() {
    if (!this.serverPromise) {
      setTimeout(getServerImpl, 10)
      this.serverPromise = this.loadConfig().then(async conf =&amp;gt; {
        this.server = await this.createServer({
          ...this.options,
          conf,
        })
        if (this.preparedAssetPrefix) {
          this.server.setAssetPrefix(this.preparedAssetPrefix)
        }
        return this.server
      })
    }
    return this.serverPromise
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터 너무 아득해졌는데 NextServer라고 Class명을 지은 거 보니 next에서 뭔가 &lt;code&gt;nextServer&lt;/code&gt;를 만드는 것(create) 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/server/next-server.ts&quot;&gt;packages/next/server/next-server.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import { RenderOpts, renderToHTML } from &quot;./render&quot;

export default class NextNodeServer extends BaseServer {
  // ...

  protected async renderHTML(
    req: NodeNextRequest,
    res: NodeNextResponse,
    pathname: string,
    query: NextParsedUrlQuery,
    renderOpts: RenderOpts
  ): Promise&amp;lt;RenderResult | null&amp;gt; {
    renderOpts.serverComponentManifest = this.serverComponentManifest
    renderOpts.serverCSSManifest = this.serverCSSManifest
    renderOpts.fontLoaderManifest = this.fontLoaderManifest

    return renderToHTML(
      req.originalRequest,
      res.originalResponse,
      pathname,
      query,
      renderOpts
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 아득해져가는데 눈에 들어오는 class를 데려와봤다. &lt;code&gt;NextNodeServer&lt;/code&gt; class 안에 &lt;code&gt;renderHTML&lt;/code&gt;을 발견했다.&lt;br /&gt;뭔가 여기서 SSR의 느낌이 나서 render 해주는 파일을 찾아 들어가봤다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/server/render.tsx&quot;&gt;packages/next/server/render.tsx&lt;/a&gt;&lt;br /&gt;renderToHTML을 찾아서 까봤다...&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export async function renderToHTML() {
  // ...
  const AppContainer = ({ children }: { children: JSX.Element }) =&amp;gt; (...)

  const AppContainerWithIsomorphicFiberStructure: React.FC&amp;lt;{...}&amp;gt; = ({children}) =&amp;gt; {return(...)}

  const Body = ({ children }: { children: JSX.Element }) =&amp;gt; {
    // 개발자 도구에서 본 듯한 DOM 발견!
    return inAmpMode ? children : &amp;lt;div id=&quot;__next&quot;&amp;gt;{children}&amp;lt;/div&amp;gt;;
  };

  const renderDocument = async () =&amp;gt; {
      // Document를 render해주는 함수를 발견

    async function loadDocumentInitialProps(
      renderShell?: (
        _App: AppType,
        _Component: NextComponentType
      ) =&amp;gt; Promise&amp;lt;ReactReadableStream&amp;gt;
    ) {
      const renderPage: RenderPage = (
        options: ComponentsEnhancer = {}
      ): RenderPageResult | Promise&amp;lt;RenderPageResult&amp;gt; =&amp;gt; {
    // ...

        const html = ReactDOMServer.renderToString(
          &amp;lt;Body&amp;gt;
            &amp;lt;AppContainerWithIsomorphicFiberStructure&amp;gt;
              {renderPageTree(EnhancedApp, EnhancedComponent, {
                ...props,
                router,
              })}
            &amp;lt;/AppContainerWithIsomorphicFiberStructure&amp;gt;
          &amp;lt;/Body&amp;gt;
        );

    // ...
  }

  const documentResult = await renderDocument();

  const htmlProps: HtmlProps = {...};

  const document = (
    &amp;lt;AmpStateContext.Provider value={ampState}&amp;gt;
      &amp;lt;HtmlContext.Provider value={htmlProps}&amp;gt;
        {documentResult.documentElement(htmlProps)}
      &amp;lt;/HtmlContext.Provider&amp;gt;
    &amp;lt;/AmpStateContext.Provider&amp;gt;
  );

  const documentHTML = ReactDOMServer.renderToStaticMarkup(document);

  const [renderTargetPrefix, renderTargetSuffix] = documentHTML.split(
    &quot;&amp;lt;next-js-internal-body-render-target&amp;gt;&amp;lt;/next-js-internal-body-render-target&amp;gt;&quot;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 모두 이해할 수는 없었지만, 서버에서 HTML를 만들어가는 과정이 여기서 일어나고 있다는 것이 느껴졌다.&lt;br /&gt;특히 &lt;code&gt;const html = ReactDOMServer.renderToString&lt;/code&gt; 이 부분이 핵심이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 클라이언트 파일을 확인해보기로 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;client&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/client/next.js&quot;&gt;packages/next/client/next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import { initialize, hydrate, version, router, emitter } from &quot;./&quot;

initialize({})
  .then(() =&amp;gt; hydrate())
  .catch(console.error)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;initialize()&lt;/code&gt;이후 &lt;code&gt;hydrate()&lt;/code&gt;를 실행하고 있다.&lt;br /&gt;&lt;code&gt;./&lt;/code&gt; 경로를 찾아보았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/packages/next/client/index.tsx&quot;&gt;packages/next/client/index.tsx&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;export async function initialize(opts: { webpackHMR?: any } = {}): Promise&amp;lt;{...}&amp;gt; {}

function renderApp(App: AppComponent, appProps: AppProps){...}

function renderReactElement(){}

function doRender(input: RenderRouteInfo): Promise&amp;lt;any&amp;gt; {...}

async function render(renderingProps: RenderRouteInfo): Promise&amp;lt;void&amp;gt; {...}

export async function hydrate(opts?: { beforeRender?: () =&amp;gt; Promise&amp;lt;void&amp;gt; }) {...}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;next.js&lt;/code&gt; 파일 에서 보았던 &lt;code&gt;initialize&lt;/code&gt; 함수와 &lt;code&gt;hydrate&lt;/code&gt; 함수와 더불어 무언가를 &lt;code&gt;render&lt;/code&gt;하는 것 같은 함수를 가져와봤다.&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;  // next/client/index.jsx
  export async function initialize(opts: { webpackHMR?: any } = {}): Promise&amp;lt;{
  assetPrefix: string
  }&amp;gt; {
  initialData = JSON.parse(
  document.getElementById(&quot;**NEXT_DATA**&quot;)!.textContent!
  )
  window.**NEXT_DATA** = initialData
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;__NEXT_DATA__&lt;/code&gt; id를 가지고 있는 엘리먼트를 parse해서 &lt;code&gt;initialData&lt;/code&gt;에 할당하고 있다.&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;  // next/client/index.jsx
  export async function initialize(opts: { webpackHMR?: any } = {}): Promise&amp;lt;{
  assetPrefix: string
  }&amp;gt; {
  initialData = JSON.parse(
  document.getElementById(&quot;**NEXT_DATA**&quot;)!.textContent!
  )
  window.**NEXT_DATA** = initialData

  // With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time
  // So, this is how we do it in the client side at runtime
  **webpack_public_path** = `${prefix}/_next/` //eslint-disable-line
  }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주석이 달려있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 assetPrefix를 사용하면 더 이상 빌드 시 assetPrefix를 설정할 수 없습니다. 이것이 런타임에 클라이언트 측에서 수행하는 방법입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;친절하게 주석이 적혀있었으나... 당장에 무슨 말인지 이해할 수 없었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;appElement = document.getElementById(&quot;__next&quot;)
return { assetPrefix: prefix }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;__next&lt;/code&gt; id를 가지고 있는 엘리먼트를 appElement에 할당한 이후, &lt;code&gt;assetPrefix&lt;/code&gt;를 반환한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function renderApp(App: AppComponent, appProps: AppProps) {
  return &amp;lt;App {...appProps} /&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;renderApp&lt;/code&gt; 함수는 &lt;code&gt;App&lt;/code&gt;과 &lt;code&gt;appProps&lt;/code&gt; 매개변수를 받아서 &lt;code&gt;AppComponent&lt;/code&gt;를 리턴한다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function renderReactElement(
  domEl: HTMLElement,
  fn: (cb: () =&amp;gt; void) =&amp;gt; JSX.Element
): void {
  // mark start of hydrate/render
  if (ST) {
    performance.mark(&quot;beforeRender&quot;)
  }

  const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete)

  if (process.env.__NEXT_REACT_ROOT) {
    if (!reactRoot) {
      // Unlike with createRoot, you don't need a separate root.render() call here
      reactRoot = ReactDOM.hydrateRoot(domEl, reactEl)
      // TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing
      shouldHydrate = false
    } else {
      const startTransition = (React as any).startTransition
      startTransition(() =&amp;gt; {
        reactRoot.render(reactEl)
      })
    }
  } else {
    // The check for `.hydrate` is there to support React alternatives like preact
    if (shouldHydrate) {
      ReactDOM.hydrate(reactEl, domEl)
      shouldHydrate = false
    } else {
      ReactDOM.render(reactEl, domEl)
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/react-dom.html#render&quot;&gt;React18이 안정화&lt;/a&gt; 될 때까지 ReactElement render 방식을 if문으로 나누고 있다.&lt;br /&gt;상황에 따라서 &lt;code&gt;ReactDOM.hydrateRoot&lt;/code&gt;하거나 &lt;code&gt;ReactDOM.hydrate&lt;/code&gt;하거나 &lt;code&gt;ReactDOM.render&lt;/code&gt;으로 렌더하고 있다.&lt;br /&gt;&lt;code&gt;ReactDOM.hydrateRoot&lt;/code&gt;문을 탔다면, 별도로 &lt;code&gt;root.render()&lt;/code&gt;하지 않아도 된다.&lt;br /&gt;&lt;code&gt;shouldHydrate&lt;/code&gt;의 boolean 상태에 따라 &lt;code&gt;reactEl&lt;/code&gt;의 fn가 달라진다 (markHydrateComplete || markRenderComplete)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ReactDOM.render()&lt;/code&gt;는 렌더링하기 원하는 컴포넌트, 렌더링 되길 원하는 컴포넌트들이 포함된 DOM 요소를 인수로 받는다. (&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started#interrogating_the_index&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started#interrogating_the_index&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;function doRender(input: RenderRouteInfo): Promise&amp;lt;any&amp;gt; {
  let { App, Component, props, err }: RenderRouteInfo = input

  const appProps: AppProps = {
    ...props,
    Component,
    err,
    router,
  }

  // ...뭔가 &amp;lt;style&amp;gt; 을 붙이고 있는 것 같음...

  const elem: JSX.Element = (
    &amp;lt;&amp;gt;
      &amp;lt;Head callback={onHeadCommit} /&amp;gt;
      &amp;lt;AppContainer&amp;gt;
        {renderApp(App, appProps)}
        &amp;lt;Portal type=&quot;next-route-announcer&quot;&amp;gt;
          &amp;lt;RouteAnnouncer /&amp;gt;
        &amp;lt;/Portal&amp;gt;
      &amp;lt;/AppContainer&amp;gt;
    &amp;lt;/&amp;gt;
  )

  // We catch runtime errors using componentDidCatch which will trigger renderError
  renderReactElement(appElement!, callback =&amp;gt; (
    &amp;lt;Root callbacks={[callback, onRootCommit]}&amp;gt;
      {process.env.__NEXT_STRICT_MODE ? (
        &amp;lt;React.StrictMode&amp;gt;{elem}&amp;lt;/React.StrictMode&amp;gt;
      ) : (
        elem
      )}
    &amp;lt;/Root&amp;gt;
  ))

  return renderPromise
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 잘 만든 것들을 &lt;code&gt;doRender&lt;/code&gt; 함수로 렌더한다고 이해했다.&lt;br /&gt;이때 &lt;code&gt;renderReactElement&lt;/code&gt;가 재등장하는데, &lt;code&gt;renderReactElement&lt;/code&gt; 함수의 첫 번째 매개변수는 &lt;code&gt;domEl: HTMLElement&lt;/code&gt;였고 두 번째 매개변수는&lt;code&gt;fn: (cb: () =&amp;gt; void) =&amp;gt; JSX.Element&lt;/code&gt;였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;appElement&lt;/code&gt;는 &lt;code&gt;initialize&lt;/code&gt;함수에서 &lt;code&gt;appElement = document.getElementById('__next')&lt;/code&gt;로 할당하었다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;callback 함수&lt;/code&gt;는 &lt;code&gt;&amp;lt;Root&amp;gt;&lt;/code&gt; 밑으로 &lt;code&gt;elem&lt;/code&gt;을 넣어주고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 과제를 잘 이해한 건지 모르겠다. 너무 어렵게 생각한 것 같기도 하고... 제대로 이해한 것인지 확신도 들지 않았다.&lt;br /&gt;챌린지 이후 보다 더 NextJs를 이해할 수 있기를 바라며...&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot;&gt;https://web.dev/rendering-on-the-web/&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://iancoding.tistory.com/173&quot;&gt;https://iancoding.tistory.com/173&lt;/a&gt;&lt;/p&gt;</description>
      <category>챌린지</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/286</guid>
      <comments>https://jineecode.tistory.com/286#entry286comment</comments>
      <pubDate>Fri, 16 Sep 2022 10:53:00 +0900</pubDate>
    </item>
    <item>
      <title>다크모드 토글 시, 폰트가 깜빡이는 현상 (Feat. Styled-Component)</title>
      <link>https://jineecode.tistory.com/282</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사건의 발단&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전엔 못 본 거 같은데 다크모드 버튼을 토글할 때, 폰트가 깜빡이는 현상을 목격.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 든 생각은 이게 말로만 듣던 FOUC ('style이 적용되지 않은 내용'이 '깜빡'이는 현상) 인가? 라는 생각에 도달함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 엄밀히 말해, 폰트만 이런 모습을 보이고 있는 것 같아서 FOUT(텍스트가 깜빡여보이는 현상. 즉, 브라우저가 웹 글꼴을 다운로드하기 전에 텍스트가 대체 글꼴로 렌더링되는 현상)으로 추측했음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말로만 듣던 FOUT를 목격하는 건 처음이라 당황스러우면서도 신나게 버그를 해결하기로 결심&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 그럼 해결 방법을 찾아보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://web.dev/avoid-invisible-text/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://web.dev/avoid-invisible-text/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662438722469&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;글꼴 로드 중 보이지 않는 텍스트 방지&quot; data-og-description=&quot;글꼴은 로드하는 데 시간이 걸리는 대용량 파일인 경우가 많습니다. 이를 처리하기 위해 일부 브라우저는 글꼴이 로드될 때까지 텍스트를 숨깁니다(&amp;quot;보이지 않는 텍스트의 깜박임(FOIT)&amp;quot;. 성능을 &quot; data-og-host=&quot;web.dev&quot; data-og-source-url=&quot;https://web.dev/avoid-invisible-text/&quot; data-og-url=&quot;https://web.dev/i18n/ko/avoid-invisible-text/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gnKLe/hyPHiiN7lY/IfI7uWFlBwZKmEmNfAUIIk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/bBia2j/hyPHoDkqrb/jW7lflavCoZD9SGHmsmvU0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://web.dev/avoid-invisible-text/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://web.dev/avoid-invisible-text/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gnKLe/hyPHiiN7lY/IfI7uWFlBwZKmEmNfAUIIk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/bBia2j/hyPHoDkqrb/jW7lflavCoZD9SGHmsmvU0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;글꼴 로드 중 보이지 않는 텍스트 방지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;글꼴은 로드하는 데 시간이 걸리는 대용량 파일인 경우가 많습니다. 이를 처리하기 위해 일부 브라우저는 글꼴이 로드될 때까지 텍스트를 숨깁니다(&quot;보이지 않는 텍스트의 깜박임(FOIT)&quot;. 성능을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;web.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 font-display를 소개하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 css를 사용하면 글꼴이 준비되지 않았을 경우, 각 브라우저가 어떻게 대응하는지 알려주고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차근차근 &lt;a href=&quot;https://github.com/uhj1993/portfolio2022/commit/1824116ddd63fdc6baec07ce84e43e75e9d3ddfa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;따라&lt;/a&gt;하며 &lt;a href=&quot;https://github.com/uhj1993/portfolio2022/commit/23a3c7b13ef6f16a395320bbbe323a55de1dde97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;FontFaceObserver도 설치해서 테스트&lt;/a&gt; 해보았으나...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결되지 않았다....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리로 테스트를 했을 때, 아주 잘 렌더링되고 있었고 여전히 폰트 깜빡임 현상은 남아있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 폰트 렌더링에 문제가 있을 것이므로, 어떻게 사용자 지정 폰트를 지정해주었는지 확인해본 결과, Styled-Component에서 &lt;a href=&quot;https://github.com/uhj1993/portfolio2022/commit/b76a076edf5f96a07c21ce9ddfb4cdd93d1862a9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;globalstyle로 font-family를 준 것&lt;/a&gt;을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 그럼 styled-component를 잘못 쓰고 있을 지도 모른다는 생각에 구글링을 해보았다. 아니나 다를까!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/styled-components/styled-components/issues/1593&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/styled-components/styled-components/issues/1593&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662439137069&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Dynamic style change causes custom fonts to be re-requested from the server &amp;middot; Issue #1593 &amp;middot; styled-components/styled-component&quot; data-og-description=&quot;I couldn't tell if this has anything to do with #1576, but it seems like potentially its own issue. I don't have @media rules, but I do have @font-face rules. System: OS: macOS High Sierra ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/styled-components/styled-components/issues/1593&quot; data-og-url=&quot;https://github.com/styled-components/styled-components/issues/1593&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dLHyMG/hyPHl0TluY/RyEGt3hbmUsCYpIuYLscT1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/styled-components/styled-components/issues/1593&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/styled-components/styled-components/issues/1593&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dLHyMG/hyPHl0TluY/RyEGt3hbmUsCYpIuYLscT1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Dynamic style change causes custom fonts to be re-requested from the server &amp;middot; Issue #1593 &amp;middot; styled-components/styled-component&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I couldn't tell if this has anything to do with #1576, but it seems like potentially its own issue. I don't have @media rules, but I do have @font-face rules. System: OS: macOS High Sierra ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Styled-Components는 스타일이 Render 될 때 마다 head 태그의 style 태그를 변경한다. 새로운 스타일이 등장할 때마다 &lt;b&gt;폰트를 재요청&lt;/b&gt;하는 현상이 나타난 것&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 다크모드를 토글할 때마다 style에 변화가 일어나는 것을 확인한 태그이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;head 아래 style에 담겨있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.53.09.png&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Dw7Y/btrLuBVlTjg/8LgbkoJ0S0gokLPDvlpBs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Dw7Y/btrLuBVlTjg/8LgbkoJ0S0gokLPDvlpBs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Dw7Y/btrLuBVlTjg/8LgbkoJ0S0gokLPDvlpBs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Dw7Y%2FbtrLuBVlTjg%2F8LgbkoJ0S0gokLPDvlpBs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;787&quot; height=&quot;332&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.53.09.png&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;network/font 탭에서도 토글할 때마다 폰트가 계속 담기는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.51.49.png&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ShAql/btrLsqfY4df/gl9gDB9sEqo6oVXtcth11k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ShAql/btrLsqfY4df/gl9gDB9sEqo6oVXtcth11k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ShAql/btrLsqfY4df/gl9gDB9sEqo6oVXtcth11k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FShAql%2FbtrLsqfY4df%2Fgl9gDB9sEqo6oVXtcth11k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;298&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.51.49.png&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.52.00.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D0xGq/btrLu9YD1zk/6aJyHWdOuKKYuC0jtoKCX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D0xGq/btrLu9YD1zk/6aJyHWdOuKKYuC0jtoKCX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D0xGq/btrLu9YD1zk/6aJyHWdOuKKYuC0jtoKCX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD0xGq%2FbtrLu9YD1zk%2F6aJyHWdOuKKYuC0jtoKCX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;349&quot; data-filename=&quot;스크린샷 2022-09-06 오후 12.52.00.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 빛이 보인다. 그래서 해결 방법은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/uhj1993/portfolio2022/commit/b1a13fcb7b1ef8b0d7ac226be8d5322af274c003&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@font-face를 globalStyle에 넣지 말고 쏙 빼내서 css로 다루면 된다.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 참고로 이 프로젝트는 NextJS로 만들어져서, 글로벌 css에 대한 규칙도 정해져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음대로 css를 import 할 경우 &lt;a href=&quot;https://nextjs.org/docs/messages/css-global&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Global CSS Must Be in Your Custom \&amp;lt;App&amp;gt; 에러&lt;/a&gt;를 직면하게 될 것이다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 전체 코드&lt;/p&gt;
&lt;pre id=&quot;code_1662439796354&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// pages/_app.tsx

import '../src/styles/font.css';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
          &amp;lt;GlobalStyle /&amp;gt;
          &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default MyApp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1662439900307&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/styles/font.css

@font-face {
  font-family: 'Pretendard-Regular';
  font-display: swap;
  src: url('/fonts/PretendardVariable.ttf') format('woff');
  font-weight: 400;
  font-style: normal;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1662439862270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/styles/globalStyle.ts 

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  html,
  body {
    font-family: Pretendard-Regular, -apple-system, sans-serif;
  }
 `&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 통신 에러가 아닌 성능 에러를 마주하니 재미있었다 ㅠㅠ...&lt;/p&gt;</description>
      <category>CSS/css in js</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/282</guid>
      <comments>https://jineecode.tistory.com/282#entry282comment</comments>
      <pubDate>Tue, 6 Sep 2022 13:55:35 +0900</pubDate>
    </item>
    <item>
      <title>프리온보딩 프론트엔드 챌린지 2차</title>
      <link>https://jineecode.tistory.com/281</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프리온보딩&lt;span&gt;&amp;nbsp;&lt;/span&gt;프론트엔드 챌린지 2차 수강 도중 애매하게 알고 있었거나 새로이 알게된 정보 정리&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. new.target&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new.target&amp;nbsp;속성(property)은&amp;nbsp;함수&amp;nbsp;또는&amp;nbsp;생성자가&amp;nbsp;new&amp;nbsp;연산자를&amp;nbsp;사용하여&amp;nbsp;호출됐는지를&amp;nbsp;감지할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 사용자를 위해 일부러 에러를 낼 수 있지만, 동료 개발자를 위해 에러를 낼 줄도 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 생성자를 사용하여 init 코드를 써야만 한다고 해보자. 동료 개발자가 이를 쓰지 않았을 때, 에러를 내주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 쓸 수 있는 것이 new.target 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662425671544&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;new.target - JavaScript | MDN&quot; data-og-description=&quot;new.target 속성(property)은 함수 또는 생성자가 new 연산자를 사용하여 호출됐는지를 감지할 수 있습니다. new 연산자로 인스턴스화된 생성자 및 함수에서, new.target은 생성자 또는 함수 참조를 반환합&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jd9pO/hyPHhYf6eE/1W5G2XbivFVMkK0Q6s4zuK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new.target&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jd9pO/hyPHhYf6eE/1W5G2XbivFVMkK0Q6s4zuK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;new.target - JavaScript | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;new.target 속성(property)은 함수 또는 생성자가 new 연산자를 사용하여 호출됐는지를 감지할 수 있습니다. new 연산자로 인스턴스화된 생성자 및 함수에서, new.target은 생성자 또는 함수 참조를 반환합&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1662425880052&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Foo() {
  if (!new.target) { throw 'Foo() must be called with new'; }
}

try {
  Foo();
} catch (e) {
  console.log(e);
  // expected output: &quot;Foo() must be called with new&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. tdz?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;풀어쓰면 일시적인 사각지대라는 뜻&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662426052940&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;new Car('red'); // Uncaught ReferenceError: Car is not defined

class Car {
  constructor(color) {
    this.color = color;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1662426062827&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;greet('World'); // 'Hello, World!'

function greet(who) {
  return `Hello, ${who}!`;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDZ은&amp;nbsp;let,&amp;nbsp;const,&amp;nbsp;class&amp;nbsp;&lt;b&gt;구문의&amp;nbsp;유효성&lt;/b&gt;을&amp;nbsp;관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 'new Car('red)'는 사각지대에 있는 셈이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;69510373-a072b280-0ef1-11ea-9d1d-6272f4078e9e.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TF1FF/btrLu2x0I5F/Zsss6zNDKPBhKLnSBiFAK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TF1FF/btrLu2x0I5F/Zsss6zNDKPBhKLnSBiFAK1/img.png&quot; data-alt=&quot;https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TF1FF/btrLu2x0I5F/Zsss6zNDKPBhKLnSBiFAK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTF1FF%2FbtrLu2x0I5F%2FZsss6zNDKPBhKLnSBiFAK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1133&quot; data-filename=&quot;69510373-a072b280-0ef1-11ea-9d1d-6272f4078e9e.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1133&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯, tdz는 let, const, class 구문의 유효성을 관리한다. 반면, var, function &lt;span style=&quot;color: #222222;&quot;&gt;선언은 tdz에 영향을 받지 않는다. 이것들은 현재 스코프에서 호이스팅 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tdz는 선언 전에 변수를 사용하는 것을 허용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로&lt;span&gt;&amp;nbsp;&lt;/span&gt;var&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수는 선언 전에도 사용할 수 있기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;var&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용은 피하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조:&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20191014&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ui.toast.com/weekly-pick/ko_20191014&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662426417143&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;TDZ을 모른 채 자바스크립트 변수를 사용하지 말라&quot; data-og-description=&quot;간단한 질문을 하나 하겠다. 아래 코드 스니펫에서 에러가 발생할까? 첫 번째 코드는 인스턴스를 생성한 다음 클래스를 선언한다.&quot; data-og-host=&quot;ui.toast.com&quot; data-og-source-url=&quot;https://ui.toast.com/weekly-pick/ko_20191014&quot; data-og-url=&quot;https://ui.toast.com/weekly-pick/ko_20191014/undefined/weekly-pick/ko_20191014&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YFkoC/hyPHoJQDT4/uHtL8ux1ATIYmLja9bkBfk/img.png?width=1440&amp;amp;height=800&amp;amp;face=0_0_1440_800,https://scrap.kakaocdn.net/dn/LYr0x/hyPHkAHtMo/cRzsuMGyMuIN3hDHVRTCek/img.png?width=1440&amp;amp;height=800&amp;amp;face=0_0_1440_800,https://scrap.kakaocdn.net/dn/uqzeQ/hyPHrGCptB/dqw5wPm2TLqk80sVXcFcm1/img.png?width=1000&amp;amp;height=1133&amp;amp;face=0_0_1000_1133&quot;&gt;&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20191014&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ui.toast.com/weekly-pick/ko_20191014&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YFkoC/hyPHoJQDT4/uHtL8ux1ATIYmLja9bkBfk/img.png?width=1440&amp;amp;height=800&amp;amp;face=0_0_1440_800,https://scrap.kakaocdn.net/dn/LYr0x/hyPHkAHtMo/cRzsuMGyMuIN3hDHVRTCek/img.png?width=1440&amp;amp;height=800&amp;amp;face=0_0_1440_800,https://scrap.kakaocdn.net/dn/uqzeQ/hyPHrGCptB/dqw5wPm2TLqk80sVXcFcm1/img.png?width=1000&amp;amp;height=1133&amp;amp;face=0_0_1000_1133');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;TDZ을 모른 채 자바스크립트 변수를 사용하지 말라&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;간단한 질문을 하나 하겠다. 아래 코드 스니펫에서 에러가 발생할까? 첫 번째 코드는 인스턴스를 생성한 다음 클래스를 선언한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ui.toast.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 매개변수가 3개 이상이 되면 객체로 넘겨주자&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 풀사이클?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;개발팀이 우수한 생산성 도구를 활용하여 소프트웨어 개발 수명주기(디자인, 개발, 테스트, 배포, 운영, 지원) 전체를 처리하는 모델&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 조건부 요소로 배열을 초기화하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663027836919&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;3 Ways to Initialize an Array with Conditional Elements in JavaScript&quot; data-og-description=&quot;This blog will run you through 3 ways to initialize a JavaScript Array with conditional elements.&quot; data-og-host=&quot;codeburst.io&quot; data-og-source-url=&quot;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&quot; data-og-url=&quot;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kBg6e/hyPLmyyAaw/aKpM3KCKncaUGrM85QmeB1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/mhAGr/hyPLgd3BNV/WVxqNmbDIRkZVGnopt5B70/img.png?width=1400&amp;amp;height=787&amp;amp;face=0_0_1400_787&quot;&gt;&lt;a href=&quot;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codeburst.io/3-ways-to-initialize-an-array-with-conditional-elements-in-javascript-c95397615a7e&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kBg6e/hyPLmyyAaw/aKpM3KCKncaUGrM85QmeB1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/mhAGr/hyPLgd3BNV/WVxqNmbDIRkZVGnopt5B70/img.png?width=1400&amp;amp;height=787&amp;amp;face=0_0_1400_787');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;3 Ways to Initialize an Array with Conditional Elements in JavaScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This blog will run you through 3 ways to initialize a JavaScript Array with conditional elements.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codeburst.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 삼항 연산자를 사용하여 배열 내부에 새로운 배열을 spread 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663027870399&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myArray = [
    'a', 'b', 'c', 'd',
    ... condition ? ['e'] : [],
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;condition이 true면 e가 추가되고, false면 배열에 아무것도 추가되지 않음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 거짓값 필터링&lt;/h4&gt;
&lt;pre id=&quot;code_1663028014347&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myArray = [
    'a', 'b', 'c', 'd',
    condition ? 'e' : null,
].filter(Boolean);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1663028056424&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myArray = [
    'a', 'b', 'c', 'd',
    condition ? 'e' : null,
].filter(x =&amp;gt; x !== null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. TS 컨벤션 참조 사이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) google typescript style guide&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://google.github.io/styleguide/tsguide.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://google.github.io/styleguide/tsguide.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663028303949&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Google TypeScript Style Guide&quot; data-og-description=&quot;Google TypeScript Style Guide TypeScript style guide This is the external guide that's based on the internal Google version but has been adjusted for the broader audience. There is no automatic deployment process for this version as it's pushed on-demand b&quot; data-og-host=&quot;google.github.io&quot; data-og-source-url=&quot;https://google.github.io/styleguide/tsguide.html&quot; data-og-url=&quot;https://google.github.io/styleguide/tsguide.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://google.github.io/styleguide/tsguide.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://google.github.io/styleguide/tsguide.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Google TypeScript Style Guide&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google TypeScript Style Guide TypeScript style guide This is the external guide that's based on the internal Google version but has been adjusted for the broader audience. There is no automatic deployment process for this version as it's pushed on-demand b&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;google.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) typescript 코딩 지침 (기여자용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript/wiki/Coding-guidelines&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/microsoft/TypeScript/wiki/Coding-guidelines&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663028430580&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.&quot; data-og-description=&quot;TypeScript is a superset of JavaScript that compiles to clean JavaScript output. - GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/microsoft/TypeScript/wiki/Coding-guidelines&quot; data-og-url=&quot;https://github.com/microsoft/TypeScript&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/P1Tl4/hyPLmk19HA/aCkcO2nFR9VGw8VfRXgvyk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript/wiki/Coding-guidelines&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/microsoft/TypeScript/wiki/Coding-guidelines&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/P1Tl4/hyPLmk19HA/aCkcO2nFR9VGw8VfRXgvyk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript is a superset of JavaScript that compiles to clean JavaScript output. - GitHub - microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. soundness : 건전성. 형식 체계 내에서 증명 가능한 명제가 의미론 상으로도 참이 되는 성질&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에는 soundness가 있는데 대표적으로 type assertion 이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663029178094&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const usersAge = (&quot;23&quot; as any) as number;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. shorthand&amp;nbsp;properties&amp;nbsp;(프로퍼티&amp;nbsp;축약)&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티의&amp;nbsp;key&amp;nbsp;와&amp;nbsp;value&amp;nbsp;에&amp;nbsp;할당할&amp;nbsp;변수명이&amp;nbsp;동일한&amp;nbsp;경우&amp;nbsp;value&amp;nbsp;를&amp;nbsp;생략&amp;nbsp;가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. concise&amp;nbsp;methods&amp;nbsp;(간결한&amp;nbsp;메소드)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜론과 함수 키워드를 생략한 간결한 문법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 어떤 객체의 value가 false값만 들어온다면, 타입스크립트로도 최대한 좁혀서 사용한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 todo가 만들어질 때, isCompleted는 무조건 false로 들어온다.&lt;/p&gt;
&lt;pre id=&quot;code_1663029506842&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface CreateTodo {
  id: string;
  content: string;
  isCompleted: boolean; // &amp;lt;- 여기
  category?: string;
  tags?: string[];
}

const createTodo = ({content, category, tags}:CreateTodo):CreateTodo =&amp;gt; {
  return {
    id: String(new Date()),
    isCompleted: false,
    content,
    category,
    tags
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 isCompleted는 boolean으로 타입지정이 되어있지만 이는 좋은 코드가 아니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663029670894&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface CreateTodo {
  id: string;
  content: string;
  isCompleted: false;
  category?: string;
  tags?: string[];
}

const createTodo = ({content, category, tags}:CreateTodo):CreateTodo =&amp;gt; {
  return {
    id: String(new Date()),
    isCompleted: false,
    content,
    category,
    tags
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Good!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장을 사용하여 위 코드보다 더 좋게 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663029748526&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: string;
  content: string;
  isCompleted: boolean,
  category: string;
  tags: string[]
}

interface CreateTodo extends Todo{
  isCompleted: false;
}

const createTodo = ({content, category, tags}:CreateTodo):CreateTodo =&amp;gt; {
  return {
    id: String(new Date()),
    isCompleted: false,
    content,
    category,
    tags
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. 자주 사용하는 type은 최대한 재활용해서 쓰는 게 좋다.&lt;/h4&gt;
&lt;pre id=&quot;code_1663029859456&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: string;
  content: string;
  isCompleted: boolean;
  category?: string;
  tags: string[];
}

interface DeleteTagParamsType {
  id: string;
  tagIdx?: number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 문제는, id가 string이 아닌 다른 타입으로 바뀔 때 일어난다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 바꾼다면 좀 더 안정적으로 타입을 지정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663029972574&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: string;
  content: string;
  isCompleted: boolean;
  category?: string;
  tags: string[];
}

interface DeleteTagParamsType {
  id: Todo['id'];
  tagIdx?: number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장을 사용하는 방법도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663030024977&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: string;
  content: string;
  isCompleted: boolean;
  category?: string;
  tags: string[];
}

interface DeleteTagParamsType extends Todo {
  tagIdx?: number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typescript 유틸리티를 사용할 수도 있다. (Omit, Pick...)&lt;/p&gt;
&lt;pre id=&quot;code_1663030177051&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: string;
  content: string;
  isCompleted: boolean;
  category?: string;
  tags: string[];
  tagIdx?: number;
}

type shoppingItem = Pick&amp;lt;Todo, &quot;tagIdx&quot;&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7. 스위치문엔 default를 사용해 엣지케이스(방어코드)를 꼭 넣어주자.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엣지 케이스란? : 알고리즘이 처리하는 데이터의 값이 알고리즘의 특성에 따른 일정한 범위를 넘을 경우에 발생하는 문제&lt;/p&gt;
&lt;pre id=&quot;code_1663030553189&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;default: {
  throw new Error ('what Id?')
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8. if문을 너무 깊게 쓰기보다, 한 번 빼서 분리하는 게 좋다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;9. 함수형 type을 지정할때 void가 return 되는 type이 많다면, 한 번 의심해보는 게 좋다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 너무 많은 일을 하고 있을 수 있으므로...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;10. if문으로 숫자의 boolean을 판별할 때 유의한다.&lt;/h4&gt;
&lt;pre id=&quot;code_1663030740505&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(!id) { // dangerous!!!
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1663030762085&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!!(0) //false
!!(-1) //true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;11. 매개변수에 id 등을 넣을 때, 그냥 id를 쓰기보다 가능한 어떤 id인지 매개변수명으로 명시하면 좋다.&lt;/h4&gt;
&lt;pre id=&quot;code_1663030841770&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;deleteTodo(selectedId) {
  this.todoList = this.todoList.filter(({id})=&amp;gt; id !== selectedId)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;12. any와 다를 바 없는 타입 지정을 피하자&lt;/h4&gt;
&lt;pre id=&quot;code_1663030954865&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const $app: HTMLElement = document.createElement('div'); // 좋지 않음

const $app: HTMLDivElement = document.createElement('div'); // 이렇게 하자&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;13. 타입스크립트를 쓴다고 해서 코드가 완전히 안전하다고 할 수 없다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 런타임에 개입하지 않기 때문에 (이는 soundness 특성 때문) 개발자는 Narrowing으로 타입을 좁혀주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;14. unknown ?&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 이 타입이 뭔지 모르겠어~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;any와 비슷하지만 unknown의 가장 큰 특징은 에러를 발생시킨다는 것에 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663031228786&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let num: unknown = 99;

if(typeof num === 'string') {
  num.trim();
}

(num as string).trim()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결법은 타입가드를 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;15. 타입을 좁힐 때 리터럴 타입을 적절히 이용한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 Route를 상수로 정의한 객체가 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;newPath를 string으로 받으면, changeRoute에 존재하지 않는 route를 넣어도 typescript는 잡아주지 못한다.&lt;/p&gt;
&lt;pre id=&quot;code_1663031637040&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Route = {
  ABOUT: '/about',
  TOPICS: '/topics'
}

function changeRoute(newPath: string) {
  const state = {'page_id': 1, 'user_id': 5}
  const title = ''

  history.pushState(state, title, newPath)
}

window.addEventListener('popstate', ()=&amp;gt; changeRoute('/auth'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리터럴 타입을 이용해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1663031750581&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Route = {
  ABOUT: '/about',
  TOPICS: '/topics'
}

type RoutePaths = 'about' | 'topics'

function changeRoute(newPath: RoutePaths) {
  const state = {'page_id': 1, 'user_id': 5}
  const title = ''

  history.pushState(state, title, newPath)
}

window.addEventListener('popstate', ()=&amp;gt; changeRoute('/auth'))
// Argument of type '&quot;/auth&quot;' is not assignable to parameter of type 'RoutePaths'.(2345)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나하나 리터럴 타입을 적어주기보다 다음과 같이 응용해보는 건 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663031908573&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Route = {
  ABOUT: '/about',
  TOPICS: '/topics'
} as const

type RoutePaths = typeof Route [keyof typeof Route]

function changeRoute(newPath: RoutePaths) {
  const state = {'page_id': 1, 'user_id': 5}
  const title = ''

  history.pushState(state, title, newPath)
}

window.addEventListener('popstate', ()=&amp;gt; changeRoute('/about'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>챌린지</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/281</guid>
      <comments>https://jineecode.tistory.com/281#entry281comment</comments>
      <pubDate>Tue, 6 Sep 2022 10:15:09 +0900</pubDate>
    </item>
    <item>
      <title>CI/CD ! Github Actions</title>
      <link>https://jineecode.tistory.com/280</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. CI?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Continous Integration은 코드를 지속적으로 통합해나가는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 통합은 단순히 코드와 코드를 합치는 것뿐만이 아니라 코드를 테스트하고 유효한지 검사하는 확인.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. CD?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Continuous Deployment는 CI 과정을 통해서 성공적으로 통합된 코드들을 실제 사용자가 사용하고 있는 Production 환경에 배포하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 설치형 / 클라우드형&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치형: Jenkins&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드형: Github Actions&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Github Actions 구성요소&lt;/p&gt;
&lt;pre id=&quot;code_1662361975621&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: CI/CD
on:
  push:
    branches:
      - master
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Workflow의 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;on (event)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여기에 기술된 이벤트에 의해 workflow가 작동한다. 특정 동작일 수 있고, schedule이 될 수도 있다. 위 코드로 예를 들자면, master 브랜치에서 push가 발생할 때 이 workflow가 실행된다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다른 이벤트 참조&amp;nbsp;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;jobs
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;workflow에서 실행할 작업의 단위. jobs 아래로 build, deploy 등이 들어올 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;name: job의 이름. 생략 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;runs-on: &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;해당 job을 실행할 가상 환경으로 ubuntu, linux, macOs 로 설정할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;steps: &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Job이 실행되는 하나의 가상 환경에서 이루어질 일련의 작업. job이 추상적이라면, step은 조금 더 실질적이다. command나 action을 실행할 수 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;name, run&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;name: 생략 가능하며 name은 알아서 정해진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;run: command 를 실행한다. command는 여러줄로 생성 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;uses&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;Action을 사용한다는 뜻. action을 다른 저장소에서 가져와 사용하거나 workflow 파일이 존재하는 동일한 저장소에 만들어서 사용할 수 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;GitHub Marketplace에서 Action들을 검색하고 활용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;actions/checkout@v2 : workflow를 실행하는 저장소의 최신 내역을 받아온다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;with: Action에게 넘겨주는 파라미터.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 실전! Github Actions 써보기 (ex. gh-pages)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일 생성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;.github/workflows/'파일이름'.yml&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href=&quot;https://github.com/marketplace/actions/deploy-to-github-pages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브 마켓플레이스&lt;/a&gt;를 참조하여 yml을 꾸며보자&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1662362873549&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: Build and Deploy
on: [push]
permissions:
  contents: write
jobs:
  build-and-deploy:
    concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
    runs-on: ubuntu-latest
    steps:
      - name: Checkout  ️
        uses: actions/checkout@v3

      - name: Install and Build   # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
        run: |
          npm ci
          npm run build

      - name: Deploy  
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          folder: build # The folder the action should deploy.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662363171741&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Github pages를 github actions로 자동 배포하기&quot; data-og-description=&quot;Github blog를 Github에서 제공하는 github actions로 배포하기&quot; data-og-host=&quot;ohseunghyeon.github.io&quot; data-og-source-url=&quot;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&quot; data-og-url=&quot;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cXOCMk/hyPF4yPS8y/KncB81EjpxfZ9Co7FAXwsk/img.png?width=650&amp;amp;height=409&amp;amp;face=0_0_650_409,https://scrap.kakaocdn.net/dn/MJcZY/hyPFYFq7pY/6hjyq4q3V2NwSVku2kB1P0/img.png?width=650&amp;amp;height=380&amp;amp;face=0_0_650_380,https://scrap.kakaocdn.net/dn/tdBpJ/hyPF6i8G3u/7apLfksgMbwVExGgdJ8St0/img.png?width=650&amp;amp;height=308&amp;amp;face=0_0_650_308&quot;&gt;&lt;a href=&quot;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ohseunghyeon.github.io/blogging/deploying-github-pages-with-github-actions/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cXOCMk/hyPF4yPS8y/KncB81EjpxfZ9Co7FAXwsk/img.png?width=650&amp;amp;height=409&amp;amp;face=0_0_650_409,https://scrap.kakaocdn.net/dn/MJcZY/hyPFYFq7pY/6hjyq4q3V2NwSVku2kB1P0/img.png?width=650&amp;amp;height=380&amp;amp;face=0_0_650_380,https://scrap.kakaocdn.net/dn/tdBpJ/hyPF6i8G3u/7apLfksgMbwVExGgdJ8St0/img.png?width=650&amp;amp;height=308&amp;amp;face=0_0_650_308');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Github pages를 github actions로 자동 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Github blog를 Github에서 제공하는 github actions로 배포하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ohseunghyeon.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@adam2/Github-Action-%EC%A3%BC%EC%9A%94-%EB%AC%B8%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@adam2/Github-Action-%EC%A3%BC%EC%9A%94-%EB%AC%B8%EB%B2%95&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662363186778&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;▶ Github Action 주요 문법 배우기&quot; data-og-description=&quot;이번 글에서는 본격적으로 workflow를 작성하기 위한 기본적인 문법들을 학습하는 시간을 가져보도록 하겠습니다. github 사용이 익숙하다면(PR, Push, review, issue 등등..) 이해가 더 쉬울 거라고 생각&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@adam2/Github-Action-%EC%A3%BC%EC%9A%94-%EB%AC%B8%EB%B2%95&quot; data-og-url=&quot;https://velog.io/@adam2/Github-Action-주요-문법&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BvEpf/hyPHoJem5j/ggbAbdQtL7tPue486kZzI0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bDSAo2/hyPHtRig3K/nOKUJnCf1OufSAe6uty1g0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cNLNRZ/hyPF82kh7j/c4jeQbRdsd06K1EnxKrSN0/img.png?width=1902&amp;amp;height=841&amp;amp;face=0_0_1902_841&quot;&gt;&lt;a href=&quot;https://velog.io/@adam2/Github-Action-%EC%A3%BC%EC%9A%94-%EB%AC%B8%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@adam2/Github-Action-%EC%A3%BC%EC%9A%94-%EB%AC%B8%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BvEpf/hyPHoJem5j/ggbAbdQtL7tPue486kZzI0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bDSAo2/hyPHtRig3K/nOKUJnCf1OufSAe6uty1g0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cNLNRZ/hyPF82kh7j/c4jeQbRdsd06K1EnxKrSN0/img.png?width=1902&amp;amp;height=841&amp;amp;face=0_0_1902_841');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;▶ Github Action 주요 문법 배우기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 본격적으로 workflow를 작성하기 위한 기본적인 문법들을 학습하는 시간을 가져보도록 하겠습니다. github 사용이 익숙하다면(PR, Push, review, issue 등등..) 이해가 더 쉬울 거라고 생각&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/280</guid>
      <comments>https://jineecode.tistory.com/280#entry280comment</comments>
      <pubDate>Mon, 5 Sep 2022 16:33:46 +0900</pubDate>
    </item>
    <item>
      <title>브랜치 정리하기</title>
      <link>https://jineecode.tistory.com/279</link>
      <description>&lt;h3 id=&quot;원격-저장소-브랜치-삭제하기&quot; data-ke-size=&quot;size23&quot;&gt;원격 저장소 브랜치 삭제하기&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;git branch -d &amp;lt;branchname&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 local repositorie에서만 삭제가 되고 원격에서는 삭제되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 저장소의 브랜치까지 삭제하기 위해서는 명령어가 한가지 더 필요하다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;git push origin --delete &amp;lt;branchname&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;로컬-브랜치-사본-정리&quot; data-ke-size=&quot;size23&quot;&gt;로컬 브랜치 정리 (--prune 옵션 사용)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 저장소에 브랜치가 삭제되었으나, 로컬 저장소에는 그 브랜치가 유효한 경우, 로컬 브랜치도 같이 정리해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 정리할 브랜치가 어떤 것인지 체크해본다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;git branch -r // 원격 저장소의 브랜치 목록
git branch -a // 모든 브랜치 목록&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 원격 저장소의 삭제된 로컬 브랜치를 정리하려면&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;git fetch &amp;lt;remotename&amp;gt; --prune&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 원격 저장소에 대한 삭제된 브랜치 정보를 정리하려면&lt;/p&gt;
&lt;pre class=&quot;brainfuck&quot;&gt;&lt;code&gt;git fetch --all --prune&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/279</guid>
      <comments>https://jineecode.tistory.com/279#entry279comment</comments>
      <pubDate>Tue, 30 Aug 2022 13:59:13 +0900</pubDate>
    </item>
    <item>
      <title>프리온보딩 프론트엔드 챌린지 1차</title>
      <link>https://jineecode.tistory.com/278</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프리온보딩 프론트엔드 챌린지 1차 수강 도중 애매하게 알고 있었거나 새로이 알게된 정보 정리&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. map 등 메서드를 사용할 때, number[]를 썼다면 number 관련 method가 추론된다. num 위에 커서를 올리면 타입추론이 되고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gRgDQwBMBAzALoDcAUIsigHQC2cADgBQCWUApoxgHwwuvejAD0YmIAGFwKHjgFNnABh2AXcZiAcFsAnTfQCUQA&quot;&gt;https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gRgDQwBMBAzALoDcAUIsigHQC2cADgBQCWUApoxgHwwuvejAD0YmIAGFwKHjgFNnABh2AXcZiAcFsAnTfQCUQA&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1661754227230&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;TS Playground - An online editor for exploring TypeScript and JavaScript&quot; data-og-description=&quot;The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gRgDQwBMBAzALoDcAUIsigHQC2cADgBQCWUApoxgHwwuvejAD0YmIAGFwKHjgFNnABh2AXcZiAcFsAnTfQCUQA&quot; data-og-url=&quot;https://www.typescriptlang.org/play/?&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gRgDQwBMBAzALoDcAUIsigHQC2cADgBQCWUApoxgHwwuvejAD0YmIAGFwKHjgFNnABh2AXcZiAcFsAnTfQCUQA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gRgDQwBMBAzALoDcAUIsigHQC2cADgBQCWUApoxgHwwuvejAD0YmIAGFwKHjgFNnABh2AXcZiAcFsAnTfQCUQA&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;TS Playground - An online editor for exploring TypeScript and JavaScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 타입단언 (as 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661754941330&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Documentation - Everyday Types&quot; data-og-description=&quot;언어의 원시 타입들.&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&quot; data-og-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Documentation - Everyday Types&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;언어의 원시 타입들.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Non-null assertion operator (Null이 아닌 어선셜 연산자. &lt;b&gt;!&lt;/b&gt;를 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661754728774&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Documentation - TypeScript 2.0&quot; data-og-description=&quot;TypeScript 2.0 Release Notes&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot; data-og-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Documentation - TypeScript 2.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript 2.0 Release Notes&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Definite Assignment Assertions (확정할당 연산자. &lt;b&gt;!&lt;/b&gt;를 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661754793765&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Documentation - TypeScript 2.7&quot; data-og-description=&quot;TypeScript 2.7 Release Notes&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&quot; data-og-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Documentation - TypeScript 2.7&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript 2.7 Release Notes&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. React TypeScript&amp;nbsp;cheat&amp;nbsp;sheet&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react-typescript-cheatsheet.netlify.app/docs/basic/setup/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://react-typescript-cheatsheet.netlify.app/docs/basic/setup/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661755199185&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Setup TypeScript with React | React TypeScript Cheatsheets&quot; data-og-description=&quot;Prerequisites&quot; data-og-host=&quot;react-typescript-cheatsheet.netlify.app&quot; data-og-source-url=&quot;https://react-typescript-cheatsheet.netlify.app/docs/basic/setup/&quot; data-og-url=&quot;https://react-typescript-cheatsheet.netlify.app//docs/basic/setup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ENZfZ/hyPBRr9B2e/wHsQEMgcOvHsqOksMkYyh1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/PLgT9/hyPBPVoFjv/DTPbdbdYm3InJJyzU6xpA1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/eklmRQ/hyPBIvcP58/g2Gfl1l77rHQOlCObXldpK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=858_146_1260_584&quot;&gt;&lt;a href=&quot;https://react-typescript-cheatsheet.netlify.app/docs/basic/setup/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-typescript-cheatsheet.netlify.app/docs/basic/setup/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ENZfZ/hyPBRr9B2e/wHsQEMgcOvHsqOksMkYyh1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/PLgT9/hyPBPVoFjv/DTPbdbdYm3InJJyzU6xpA1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/eklmRQ/hyPBIvcP58/g2Gfl1l77rHQOlCObXldpK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=858_146_1260_584');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Setup TypeScript with React | React TypeScript Cheatsheets&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Prerequisites&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-typescript-cheatsheet.netlify.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리나 프레임워크를 사용할 때, 어떻게 추론해야 할 지 알 수 없다면?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`&amp;lt;button onClick={e=&amp;gt;{}}/&amp;gt;` 등을 작성하고 e에 마우스를 가져다대서 타입을 알아냅니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;구글 검색&lt;/li&gt;
&lt;li&gt;치트시트 참조&lt;/li&gt;
&lt;li&gt;React / index.d.ts 참조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. textarea와 input에서 onChange 이벤트를 사용할 때 | 를 사용하여 두 타입을 모두 명시하는 것이 좋을까, 두 타입의 부모 타입을 주는 게 좋을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 이럴 때 제네릭을 사용하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 에러가 발생한다고 해서 그것이 꼭 에러객체라는 건 아니다.&lt;/p&gt;
&lt;pre id=&quot;code_1661761414025&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;throw 'h1' // Uncaught h1
throw new Error('h1') // Uncaught Error: h1&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661761884512&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function readUser(json) {
  let user;

  try {
    user = JSON.parse(json);
  } catch (err) {
    if (err instanceof Error) {
    // instanceof 연산자를 사용하면 객체가 특정 클래스에 속하는지 아닌지를 확인할 수 있습니다.
      console.log(err)
    } else {
      throw err;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RQ&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 모듈 시스템이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;gt; 모듈:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #313130;&quot;&gt;개발하는 애플리케이션의 크기가 커지면 언젠간 파일을 여러 개로 분리해야 하는 시점이 온다. 이때 분리된 파일 각각을 '모듈(module)'이라고 부른다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 모듈로 분리된 JS파일을 불러오는 방식을 정의한 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661762967112&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[javascript] module 시스템&quot; data-og-description=&quot;목표 - javascript의 모듈 시스템에 대해서 알아 봅니다. 모듈이란? - 하나의 큰 기능을 문해 해결을 쉽게하기 위하여 여러 작은 기능으로 분리하는데 이 작은 기능을 모듈이라고 합니다. - 아래 예&quot; data-og-host=&quot;doitnow-man.tistory.com&quot; data-og-source-url=&quot;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; data-og-url=&quot;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkjrQG/hyPBH4gbU6/sV8a0eA7J47UwDn5o0vkv1/img.png?width=291&amp;amp;height=294&amp;amp;face=0_0_291_294,https://scrap.kakaocdn.net/dn/bjpbLR/hyPBVOXI2Y/mhojISW7zclM5DSvKnoEik/img.png?width=291&amp;amp;height=294&amp;amp;face=0_0_291_294,https://scrap.kakaocdn.net/dn/dJtKzl/hyPBVnR4lb/ZweB9unKnNbuLlwCyfEq80/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://doitnow-man.tistory.com/entry/javascript-module-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkjrQG/hyPBH4gbU6/sV8a0eA7J47UwDn5o0vkv1/img.png?width=291&amp;amp;height=294&amp;amp;face=0_0_291_294,https://scrap.kakaocdn.net/dn/bjpbLR/hyPBVOXI2Y/mhojISW7zclM5DSvKnoEik/img.png?width=291&amp;amp;height=294&amp;amp;face=0_0_291_294,https://scrap.kakaocdn.net/dn/dJtKzl/hyPBVnR4lb/ZweB9unKnNbuLlwCyfEq80/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[javascript] module 시스템&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;목표 - javascript의 모듈 시스템에 대해서 알아 봅니다. 모듈이란? - 하나의 큰 기능을 문해 해결을 쉽게하기 위하여 여러 작은 기능으로 분리하는데 이 작은 기능을 모듈이라고 합니다. - 아래 예&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;doitnow-man.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1661763128344&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;./a.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;./b.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

// 글로벌 스코퍼를 공유하게 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. IIFE: 정의와 동시에 즉시 실행되는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;Immediately Invoked Function Expression (즉시 실행되는 함수 표현식)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;a href=&quot;https://velog.io/@doondoony/javascript-iife&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@doondoony/javascript-iife&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661763079133&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;자바스크립트의 IIFE&quot; data-og-description=&quot;즉시 실행 함수에 대해 알아보자&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@doondoony/javascript-iife&quot; data-og-url=&quot;https://velog.io/@doondoony/javascript-iife&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/etmH39/hyPDbbzw0j/DE1amwmr1JB53YbVHLOed1/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/X5jiU/hyPC6agw3m/uSKZftlTN9VkvEF52cbPVk/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/bkkRgK/hyPBRMzCwW/zWp5vK2ke9akIKq49O9RU1/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350&quot;&gt;&lt;a href=&quot;https://velog.io/@doondoony/javascript-iife&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@doondoony/javascript-iife&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/etmH39/hyPDbbzw0j/DE1amwmr1JB53YbVHLOed1/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/X5jiU/hyPC6agw3m/uSKZftlTN9VkvEF52cbPVk/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/bkkRgK/hyPBRMzCwW/zWp5vK2ke9akIKq49O9RU1/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의 IIFE&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;즉시 실행 함수에 대해 알아보자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1661763039042&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function () { console.log('Hello World') })(); // Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보편적으로 전역 스코프를 오염시키지 않기 위해 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 웹팩이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 모듈 번들러.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션에 필요한 모든 모듈을 병합하고 압축해서 하나의 결과물(번들)을 생성해주는 도구.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹팩에서의&amp;nbsp;모듈은&amp;nbsp;javascript&amp;nbsp;파일뿐만&amp;nbsp;아니라&amp;nbsp;애플리케이션을&amp;nbsp;구성하는&amp;nbsp;HTML,&amp;nbsp;CSS,&amp;nbsp;Javascript,&amp;nbsp;Images,&amp;nbsp;Font&amp;nbsp;등&amp;nbsp;많은&amp;nbsp;파일들을&amp;nbsp;모듈이라고&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 순수함수란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부의 상태를 변경하지 않으면서 동일한 인자에 대해 항상 똑같은 값을 리턴하는 함수&lt;/p&gt;
&lt;pre id=&quot;code_1661763471003&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
 return &amp;lt;div&amp;gt;App&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수인가?: 함수 컴포넌트는 어떻게 보면 그냥 '함수'이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 목적은?: view를 리턴하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리덕스는 순수함수를 기반으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 사이드이펙트란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트&amp;nbsp;관점에서&amp;nbsp;보면&amp;nbsp;사이드&amp;nbsp;이펙트는&amp;nbsp;(자바스크립트)&amp;nbsp;코드가&amp;nbsp;외부&amp;nbsp;세계에&amp;nbsp;영향을&amp;nbsp;주거나&amp;nbsp;받는&amp;nbsp;것이다.&amp;nbsp;조금&amp;nbsp;모호하지만&amp;nbsp;함수&amp;nbsp;관점으로&amp;nbsp;생각하면&amp;nbsp;조금&amp;nbsp;더&amp;nbsp;명확하다.&amp;nbsp;함수가&amp;nbsp;일관된&amp;nbsp;결과를&amp;nbsp;보장하지&amp;nbsp;못하거나,&amp;nbsp;함수&amp;nbsp;외부&amp;nbsp;어디든지&amp;nbsp;조금이라도&amp;nbsp;영향을&amp;nbsp;주는&amp;nbsp;경우&amp;nbsp;모두&amp;nbsp;사이드&amp;nbsp;이펙트를&amp;nbsp;갖는&amp;nbsp;것이라&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(출처: &lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20171124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ui.toast.com/weekly-pick/ko_20171124&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1661763560636&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {

    useEffect(()=&amp;gt;{
     // fetch('') - 외부세계에 영향을 미치는 부수효과. 사이드 이펙트.
    },[])

 return &amp;lt;div&amp;gt;App&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 코드를 동기적으로 실행한다면 useEffect가 끝날 때까지 렌더링이 멈출 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수의 목적은 'App'을 리턴하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch는 사이드 이펙트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 렌더링이 다 된 뒤 useEffect가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'Effect'만 없다면 순수함수가 될 수 있었을 텐데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661763867802&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(state, props) =&amp;gt; UI&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state, props에 의해서만 UI가 영향을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state, props 외, 다른 것이 들어와서 UI가 바뀌면 순수함수가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. 음... 잘 와닿지 않는데요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. fetch로 들어온 데이터 때문에 UI가 바뀐다면 순수함수가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. useState의 첫 번째 인자에 함수를 넣는 이유?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 처음 한 번만 실행하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 캐시란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주쓰는 데이터를 가까운 저장소에 두는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버야. 캐시가 있니?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응: 그럼 캐시 쓰자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니: 캐시에 데이터 저장할게&lt;/p&gt;
&lt;pre id=&quot;code_1661764757721&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cache[request.url] ? cache[request.url] : cache[request.url] = request.url&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6-1. react-query에서 캐시키의 활용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a, b 컴포넌트에서 같은 캐시키를 갖고 있다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b에서 바뀌면 a도 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 직렬화란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plain object&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-객체 리터럴 {}을 이용해서 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-775eb85f-e6da-4471-86e9-ce7b74065c68&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-{key:value, key2:value, ...}와 같은 형식으로 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;-키 값으로 관리되는 순서가 중요치 않는 데이터를 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 json 직렬화가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON.stringfy(JSON형식의 객체)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 객체 =&amp;gt; &lt;b&gt;문자열로&lt;/b&gt; 변환하며, 이를 직렬화(serialize)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;: &lt;/span&gt;통신할&lt;span&gt; &lt;/span&gt;때는&lt;span&gt; &lt;/span&gt;문자열로&lt;span&gt; &lt;/span&gt;직렬화하여&lt;span&gt; &lt;/span&gt;주고&lt;span&gt; &lt;/span&gt;받는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; &lt;/span&gt;안전하다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 불변성이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가&amp;nbsp;생성된&amp;nbsp;이후&amp;nbsp;그&amp;nbsp;상태를&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;디자인&amp;nbsp;패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 엔드포인트란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 이용할 때 사용하는 커뮤니케이션 채널의 한쪽 끝에 해당하는 URL&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get. post 등의 메서드로 엔드포인트가 끝나야 이상적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. 로컬 스토리지는 동기적인 동작이다. 메인스레드가 정지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>챌린지</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/278</guid>
      <comments>https://jineecode.tistory.com/278#entry278comment</comments>
      <pubDate>Mon, 29 Aug 2022 18:19:58 +0900</pubDate>
    </item>
    <item>
      <title>타입단언</title>
      <link>https://jineecode.tistory.com/277</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661142797879&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Documentation - Everyday Types&quot; data-og-description=&quot;언어의 원시 타입들.&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&quot; data-og-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#type-assertions&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Documentation - Everyday Types&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;언어의 원시 타입들.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 TypeScript보다 당신이 어떤 값의 타입에 대한 정보를 더 잘 아는 경우도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 코드상에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;document.getElementById가 사용되는 경우, TypeScript는 이때&lt;span&gt;&amp;nbsp;&lt;/span&gt;HTMLElement&lt;span&gt;&amp;nbsp;&lt;/span&gt;중에 _무언가_가 반환된다는 것만을 알 수 있는 반면에, 당신은 페이지 상에서 사용되는 ID로는 언제나&lt;span&gt;&amp;nbsp;&lt;/span&gt;HTMLCanvasElement가 반환된다는 사실을 &lt;b&gt;이미 알고 있을 수&lt;/b&gt;도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우, _타입 단언_을 사용하면 타입을 좀 더 구체적으로 명시할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 타입 단언이 없을 시&lt;/p&gt;
&lt;pre id=&quot;code_1661142453054&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myCanvas = document.getElementById(&quot;main_canvas&quot;)

// const myCanvas: HTMLElement | null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 타입 단언 사용&lt;/p&gt;
&lt;pre id=&quot;code_1661142468941&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
* ts에서
* 
*/
const myCanvas = document.getElementById(&quot;main_canvas&quot;) as HTMLCanvasElement;
// const myCanvas: HTMLCanvasElement

/**
* tsx에서
* 
*/
const myCanvas = &amp;lt;HTMLCanvasElement&amp;gt;document.getElementById(&quot;main_canvas&quot;);
// const myCanvas: JSX.Element&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JS/Typescript(공개용)</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/277</guid>
      <comments>https://jineecode.tistory.com/277#entry277comment</comments>
      <pubDate>Mon, 22 Aug 2022 13:34:00 +0900</pubDate>
    </item>
    <item>
      <title>'ParsedUrlQuery | undefined' 형식에 'userSeq' 속성이 없습니다.</title>
      <link>https://jineecode.tistory.com/276</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;getServerSideProps에서 context를 가져올 때 타입에러가 발생할 경우, 확장시켜서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1659920943547&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getServerSideProps: GetServerSideProps = async (context) =&amp;gt; {
  const { userSeq } = context.params; 
  //'ParsedUrlQuery | undefined' 형식에 'userSeq' 속성이 없습니다.ts(2339)


	...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 query는 string으로 받기 때문에, userSeq가 number로 와야할 경우, 형 변환을 따로 해주는 게 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1659921007153&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ParsedUrlQuery } from 'querystring';

export const getServerSideProps: GetServerSideProps = async (context) =&amp;gt; {
  interface IParams extends ParsedUrlQuery {
    userSeq: string;
  }

  const { userSeq } = context.params as IParams;

 ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wallis.dev/blog/nextjs-getstaticprops-and-getstaticpaths-with-typescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wallis.dev/blog/nextjs-getstaticprops-and-getstaticpaths-with-typescript&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JS/Typescript(공개용)</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/276</guid>
      <comments>https://jineecode.tistory.com/276#entry276comment</comments>
      <pubDate>Mon, 8 Aug 2022 10:11:24 +0900</pubDate>
    </item>
    <item>
      <title>Container/Presentational Pattern</title>
      <link>https://jineecode.tistory.com/275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191919;&quot;&gt;- 애플리케이션 로직과 뷰(UI)를 분리한 패턴&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #191919;&quot;&gt;- UI 구분이 쉬워지며 모킹, 스토리북을 더 쉽게 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Presentational Components&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터가 사용자에게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;어떻게&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;보여지는지&lt;/u&gt; 신경쓰는 컴포넌트. 즉, UI.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Container Components :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;u&gt;&lt;b&gt;어떤&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터&lt;/u&gt;가 사용자에게 보여 지는지&lt;span&gt;&amp;nbsp;&lt;/span&gt;신경쓰는 컴포넌트 .&lt;span&gt; 즉, API 통신 등을 담당.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Container Components&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659510152441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState, useEffect } from &quot;react&quot;;

export default function useDogImages() {
  const [dogs, setDogs] = useState([]);

  useEffect(() =&amp;gt; {
    async function fetchDogs() {
      const res = await fetch(
        &quot;https://dog.ceo/api/breed/labrador/images/random/6&quot;
      );
      const { message } = await res.json();
      setDogs(message);
    }

    fetchDogs();
  }, []);

  return dogs;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Presentational Components&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659510235465&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import useDogImages from &quot;./useDogImages&quot;;

export default function DogImages() {
  const dogs = useDogImages();

  return dogs.map((dog, i) =&amp;gt; &amp;lt;img src={dog} key={i} alt=&quot;Dog&quot; /&amp;gt;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 예시&lt;/p&gt;
&lt;pre id=&quot;code_1659510311712&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
const FaceLoader = () =&amp;gt; { // Container with Suspense
  return (
    &amp;lt;Suspense fallback={&amp;lt;Loading /&amp;gt;}&amp;gt;
      &amp;lt;FaceFetcher /&amp;gt;
    &amp;lt;/Suspense&amp;gt;
  );
}
const FaceFetcher = () =&amp;gt; {  // Container
  const [face] = useFetch('face');
  return &amp;lt;Face face={face} /&amp;gt;;
};

const Face = ({ face }) =&amp;gt; { // Presentational
  return (
    &amp;lt;&amp;gt;
      {'...'}
      {face}
      {'...'}
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;react-query, suspense, errorboundary &lt;/b&gt;여러 라이브러리의 조합&lt;/p&gt;
&lt;pre id=&quot;code_1659510766530&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Main() {
  return (
    &amp;lt;ErrorBoundary fallback={&amp;lt;div&amp;gt;에러 발생!&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;로딩 중...&amp;lt;/div&amp;gt;}&amp;gt;
        &amp;lt;UserInfo /&amp;gt;
      &amp;lt;/Suspense&amp;gt;
    &amp;lt;/ErrorBoundary&amp;gt;
  )
}

function useUser() { // useQuery 분리
  return useQuery(
    `getUser`,
    () =&amp;gt; {
      return apiClient.get&amp;lt;User&amp;gt;(`URL`)
    },
    { suspense: true }
  )
}

function UserInfo() {
  const { data: user } = useUser() // UI만 신경쓰게 하자 
  return &amp;lt;div&amp;gt;{user.name}&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.patterns.dev/posts/presentational-container-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.patterns.dev/posts/presentational-container-pattern/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1659510376524&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Container/Presentational Pattern&quot; data-og-description=&quot;Enforce separation of concerns by separating the view from the application logic&quot; data-og-host=&quot;www.patterns.dev&quot; data-og-source-url=&quot;https://www.patterns.dev/posts/presentational-container-pattern/&quot; data-og-url=&quot;https://www.patterns.dev/posts/presentational-container-pattern/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cK4pnF/hyPjlfkH71/AONGPNypkIQYvK7xJXnTVK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://www.patterns.dev/posts/presentational-container-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.patterns.dev/posts/presentational-container-pattern/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cK4pnF/hyPjlfkH71/AONGPNypkIQYvK7xJXnTVK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Container/Presentational Pattern&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Enforce separation of concerns by separating the view from the application logic&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.patterns.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1659510380926&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스토리북 작성을 통해 얻게 되는 리팩토링 효과&quot; data-og-description=&quot;카카오엔터테인먼트 FE 기술블로그&quot; data-og-host=&quot;fe-developers.kakaoent.com&quot; data-og-source-url=&quot;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&quot; data-og-url=&quot;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnJncj/hyPjlsStY7/kk1HYSFkkVB08e06DC6GcK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fe-developers.kakaoent.com/2022/220609-storybookwise-component-refactoring/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnJncj/hyPjlsStY7/kk1HYSFkkVB08e06DC6GcK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스토리북 작성을 통해 얻게 되는 리팩토링 효과&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오엔터테인먼트 FE 기술블로그&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fe-developers.kakaoent.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JS/react</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/275</guid>
      <comments>https://jineecode.tistory.com/275#entry275comment</comments>
      <pubDate>Wed, 3 Aug 2022 16:06:30 +0900</pubDate>
    </item>
    <item>
      <title>React-query의 에러 핸들링</title>
      <link>https://jineecode.tistory.com/274</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 요청이 실패하면 이상적으로는 쿼리가&lt;span&gt;&amp;nbsp;&lt;/span&gt;오류&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태가 되기를 원할 것입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;그렇게 하지 않고 대신 성공한 쿼리가 표시&lt;span&gt;&amp;nbsp;&lt;/span&gt;된다면 이는 queryFn&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 실패한 Promise를 반환하지 않았음을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query는 상태 코드나 네트워크 요청에 대해 전혀 알지 못합니다(또는 신경 쓰지 않습니다).&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryFn&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 제공 해야 하는 해결되거나 거부된 Promise가&lt;span&gt;&amp;nbsp;&lt;/span&gt;필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query가 거부된 Promise를 발견하면 잠재적으로 재시도를 시작하고 오프라인인 경우 쿼리를 일시 중지하고 결국 쿼리를 오류 상태로 만들 수 있으므로 올바르게 수행하는 것이 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류를 올바르게 처리하려면 React Query에서 거부(rejected)된 Promise가 필요합니다. 운 좋게도, 이것은 여러분이&amp;nbsp;&lt;a href=&quot;https://axios-http.com/&quot;&gt;&lt;span&gt;axios&lt;/span&gt;&lt;/a&gt;와 같은 라이브러리를 다룰 때 얻을 수 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약&lt;span&gt; &lt;/span&gt;당신이&lt;span&gt;&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;&lt;span&gt;fetch API&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt; 4xx&lt;/span&gt;나&lt;span&gt; 5xx&lt;/span&gt;와&lt;span&gt; &lt;/span&gt;같은&lt;span&gt; &lt;/span&gt;잘못된&lt;span&gt; &lt;/span&gt;상태&lt;span&gt; &lt;/span&gt;코드에&lt;span&gt; &lt;/span&gt;대해&lt;span&gt; &lt;/span&gt;거부된&lt;span&gt; Promise&lt;/span&gt;를&lt;span&gt; &lt;/span&gt;제공하지&lt;span&gt; &lt;/span&gt;않는&lt;span&gt; &lt;/span&gt;다른&lt;span&gt; &lt;/span&gt;라이브러리를&lt;span&gt; &lt;/span&gt;사용하고&lt;span&gt; &lt;/span&gt;있다면&lt;span&gt;, &lt;/span&gt;당신은&lt;span&gt; &lt;/span&gt;직접&lt;span&gt;&amp;nbsp;queryFn&lt;/span&gt;을&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;변환해야&lt;span&gt; &lt;/span&gt;합니다&lt;span&gt;.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. fetch API 를 쓸 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1659493970664&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Query Functions | TanStack Query Docs&quot; data-og-description=&quot;A query function can be literally any function that returns a promise. The promise that is returned should either resolve the data or throw an error. All of the following are valid query function configurations:&quot; data-og-host=&quot;tanstack.com&quot; data-og-source-url=&quot;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&quot; data-og-url=&quot;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OiLmV/hyPjjhhtEn/hGTNjHP7G1R6ueXe5tARlK/img.png?width=1846&amp;amp;height=1100&amp;amp;face=0_0_1846_1100,https://scrap.kakaocdn.net/dn/dH4wsx/hyPhrVwKL9/pxbetUm7M6drEnwbTrTOdK/img.png?width=1846&amp;amp;height=1100&amp;amp;face=0_0_1846_1100&quot;&gt;&lt;a href=&quot;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OiLmV/hyPjjhhtEn/hGTNjHP7G1R6ueXe5tARlK/img.png?width=1846&amp;amp;height=1100&amp;amp;face=0_0_1846_1100,https://scrap.kakaocdn.net/dn/dH4wsx/hyPhrVwKL9/pxbetUm7M6drEnwbTrTOdK/img.png?width=1846&amp;amp;height=1100&amp;amp;face=0_0_1846_1100');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Query Functions | TanStack Query Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A query function can be literally any function that returns a promise. The promise that is returned should either resolve the data or throw an error. All of the following are valid query function configurations:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tanstack.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 잘못된 코드&lt;/p&gt;
&lt;pre id=&quot;code_1659494017019&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['todos', todoId], async () =&amp;gt; {
  const response = await fetch('/todos/' + todoId)
  //   4xx or 5xx are not treated as errors
  return response.json()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아래처럼 &lt;b&gt;예외처리를&lt;/b&gt; 반드시 해주세요!!!&lt;/p&gt;
&lt;pre id=&quot;code_1659493978001&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['todos', todoId], async () =&amp;gt; {
  const response = await fetch('/todos/' + todoId)
  if (!response.ok) {
    throw new Error('Network response was not ok')
  }
  return response.json()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 로깅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로, 로깅 목적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryFn 내부에서 오류가 포착된다는 것입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;catch에서 오류를 다시 발생시키지 않으면 암시적으로 성공한 Promise를 다시 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 잘못된 코드&lt;/p&gt;
&lt;pre id=&quot;code_1659494194583&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['todos', todoId], async () =&amp;gt; {
  try {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  } catch (error) {
    console.error(error)
    //   here, an &quot;empty&quot; Promise&amp;lt;void&amp;gt; is returned
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아래처럼 failed Promise를 반환해주세요!&lt;/p&gt;
&lt;pre id=&quot;code_1659494237821&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['todos', todoId], async () =&amp;gt; {
  try {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  } catch (error) {
    console.error(error)
    // ✅ here, a failed Promise is returned
    throw error
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 물론 useQuery의 &lt;b&gt;onError 콜백&lt;/b&gt;을 사용해도 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1659494272202&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(
  ['todos', todoId],
  async () =&amp;gt; {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  },
  { onError: (error) =&amp;gt; console.error(error) }
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;ErrorBoundary&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries&quot;&gt;오류 경계(Error Boundaries)&lt;/a&gt;&lt;/span&gt;는 렌더링 중에 발생하는 런타임 오류를 포착하기 위한 리액트의 개념으로, 이를 통해 적절하게 반응하여 폴백 UI로 표시할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트를 원하는 세분화로 &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;ErrorBoundary&lt;/span&gt;를 래핑할 수 있으므로 나머지 UI는 해당 오류의 영향을 받지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;ErrorBoundary&lt;/span&gt;는 렌더링 중에 발생하지 않기 때문에&lt;b&gt; 비동기 오류를 포착할 수 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;ErrorBoundary&lt;/span&gt;가 React Query에서 동작하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 내부적으로 오류를 잡아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span&gt;다음 &lt;/span&gt;렌더링 주기에 다시 던져(throw)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 오류 경계가 오류를 선택할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은&lt;span&gt; &lt;/span&gt;매우&lt;span&gt; &lt;/span&gt;천재적이면서도&lt;span&gt; &lt;/span&gt;간단한&lt;span&gt; &lt;/span&gt;오류&lt;span&gt; &lt;/span&gt;처리&lt;span&gt; &lt;/span&gt;방식이며&lt;span&gt;, &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;작업을&lt;span&gt; &lt;/span&gt;수행하기&lt;span&gt; &lt;/span&gt;위해&lt;span&gt; &lt;/span&gt;필요한&lt;span&gt; &lt;/span&gt;것은&lt;span&gt;&amp;nbsp;useErrorBoundary&amp;nbsp;&lt;/span&gt;플래그를&lt;span&gt; &lt;/span&gt;쿼리에&lt;span&gt; &lt;/span&gt;전달&lt;span&gt;(&lt;/span&gt;또는&lt;span&gt; &lt;/span&gt;기본&lt;span&gt; &lt;/span&gt;구성을&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;제공&lt;span&gt;)&lt;/span&gt;하는&lt;span&gt; &lt;/span&gt;것입니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useErrorBoundary&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;라는 함수는 오류 경계로 이동할 오류와 지역적으로 처리할 오류를 지정할 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659494767749&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['todos'], fetchTodos, {
  //   only server errors will go to the Error Boundary
  useErrorBoundary: (error) =&amp;gt; error.response?.status &amp;gt;= 500,
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은&lt;span&gt;&amp;nbsp;&lt;a href=&quot;https://react-query.tanstack.com/guides/mutations&quot;&gt;&lt;span&gt;mutations&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;에도&lt;span&gt; &lt;/span&gt;효과가&lt;span&gt; &lt;/span&gt;있으며&lt;span&gt;, form&lt;/span&gt;을&lt;span&gt; &lt;/span&gt;제출&lt;span&gt; &lt;/span&gt;할&lt;span&gt; &lt;/span&gt;때&lt;span&gt; &lt;/span&gt;매우&lt;span&gt; &lt;/span&gt;유용합니다&lt;span&gt;. 4xx &lt;/span&gt;범위의&lt;span&gt; &lt;/span&gt;오류는&lt;span&gt; &lt;/span&gt;지역적으로&lt;span&gt; &lt;/span&gt;처리할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;반면&lt;span&gt;(&lt;/span&gt;예&lt;span&gt;: &lt;/span&gt;백엔드&lt;span&gt; &lt;/span&gt;유효성&lt;span&gt; &lt;/span&gt;검사에&lt;span&gt; &lt;/span&gt;실패한&lt;span&gt; &lt;/span&gt;경우&lt;span&gt;), &lt;/span&gt;모든&lt;span&gt; 5xx &lt;/span&gt;서버&lt;span&gt; &lt;/span&gt;오류는&lt;span&gt; &lt;/span&gt;오류&lt;span&gt; &lt;/span&gt;경계로&lt;span&gt; &lt;/span&gt;전파될&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;4. 토스트 에러&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;전역에 등록하면, 각 쿼리에 대해 한 번만 오류 토스트가 표시됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659504788484&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import toast from 'react-hot-toast'

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error) =&amp;gt;
      toast.error(`Something went wrong: ${error.message}`),
  }),
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 적절하게 섞어 쓰기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query에서 오류를 처리하는 세 가지 주요 방법은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useQuery에서 반환된 오류 속성&lt;/li&gt;
&lt;li&gt;onError&lt;span&gt;&amp;nbsp;&lt;/span&gt;콜백(쿼리 자체 또는 글로벌 QueryCache/MutionCache)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;ErrorBoundary&lt;/span&gt;&amp;nbsp;사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1659504871256&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error, query) =&amp;gt; {
      //   only show error toasts if we already have data in the cache
      // which indicates a failed background update
      if (query.state.data !== undefined) {
        toast.error(`Something went wrong: ${error.message}`)
      }
    },
  }),
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React-Query</category>
      <author>지니코딩</author>
      <guid isPermaLink="true">https://jineecode.tistory.com/274</guid>
      <comments>https://jineecode.tistory.com/274#entry274comment</comments>
      <pubDate>Wed, 3 Aug 2022 15:12:17 +0900</pubDate>
    </item>
  </channel>
</rss>