<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>anko와 함께하는 developer :D</title>
    <link>https://mikkeller.tistory.com/</link>
    <description>Front-end web development Plan!</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 06:25:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>앙꼬오빠</managingEditor>
    <item>
      <title>커스텀 훅(custom hook)</title>
      <link>https://mikkeller.tistory.com/91</link>
      <description>&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;커스텀 훅은 use 라는 이름을 접두사로 사용해줘야하고&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;일반 컴포넌트처럼 동일하게 useState, useEffect, useCallback, useMemo 등 다 사용 가능&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;(아니 use hook 들이 들어가야만 커스텀 훅이라고 할 수 있다, use hook들을 사용하지도 않았는데 커스텀 훅이라고 정해놓고 use라는 접두사를 붙여서는 안된다)&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;a href=&quot;https://react.dev/learn/reusing-logic-with-custom-hooks#should-all-functions-called-during-rendering-start-with-the-use-prefix&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&amp;ldquo;use&amp;rdquo; 접두사에 대한 리액트 문서&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React/react</category>
      <category>커스텀훅</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/91</guid>
      <comments>https://mikkeller.tistory.com/91#entry91comment</comments>
      <pubDate>Mon, 1 Jul 2024 09:30:33 +0900</pubDate>
    </item>
    <item>
      <title>useEffect()</title>
      <link>https://mikkeller.tistory.com/90</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useEffect 의 작동 원리&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1719535220624&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect, useRef } from &quot;react&quot;;

const useClick = (onClick) =&amp;gt; {
  const ref = useRef();
  useEffect(() =&amp;gt; {
  	// 1. 컴포넌트가 마운트되었을 때 실행되는 코드
    const element = ref;

    if (element.current) {
      element.current.addEventListener(&quot;click&quot;, onClick);
    }


    return () =&amp;gt; {
    // 2. 컴포넌트가 언마운트되기 전에 실행되는 코드
      if (element.current) {
        element.current.removeEventListener(&quot;click&quot;, onClick);
      }
    };
  }, []); // 3. 빈 배열은 컴포넌트가 마운트될 떄 한 번만 실행

  if (typeof onClick !== &quot;function&quot;) {
    return;
  }
  return ref;
};

export default useClick;&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;1.&amp;nbsp; 마운트(Mount)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Component의 Mount는 &lt;b&gt;&lt;u&gt;React Component가 DOM에 추가되고 화면에 나타나는 과정&lt;/u&gt;&lt;/b&gt;을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Component가 처음 생성되고 DOM에 삽입될 때를 Mount라고 한다.&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. 언마운트(unMount)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Component의 unMount는 &lt;b&gt;&lt;u&gt;React Component가 DOM에서 제거되는 과정&lt;/u&gt;&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;3. 의존성 배열 Dependency(deps)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 배열 값으로 둘 경우에는 Component가 Mount될 때 한 번만 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 값이 있는 경우 해당 상태 값이 변경될 때마다 useeEffect()의 함수가 실행되고, 변경될 때만 실행되는 것이 아니라 처음 Mount 될 때도 실행된다.&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;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;여기서 unMount 될 때 eventListener를 제거하는 이유는 무엇일까?&lt;/b&gt;&lt;/h3&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;/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;eventListener는 DOM 요소에 대한 참조를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Component가 unMount가 됐음에도 불구하고 eventListener가 남아있다면 해당 Component가 더 이상 필요하지 않더라도 메모리에서 해제되지 않을 수 있다.&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;useEffect 훅에서 반환(return)하는 함수는 Component가 unMount되기 전에 실행되는 clean-up 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React/react</category>
      <category>Mount</category>
      <category>unmount</category>
      <category>useEffect</category>
      <category>마운트</category>
      <category>언마운트</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/90</guid>
      <comments>https://mikkeller.tistory.com/90#entry90comment</comments>
      <pubDate>Fri, 28 Jun 2024 10:53:19 +0900</pubDate>
    </item>
    <item>
      <title>Axios 사용하기</title>
      <link>https://mikkeller.tistory.com/89</link>
      <description>&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;GET:&lt;/b&gt; 데이터 조회&amp;nbsp; &amp;nbsp;axios.get(url)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;POST:&lt;/b&gt; 데이터 등록 axios.post(url, data)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PUT:&lt;/b&gt; 데이터 수정 axios.put(url/&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;u&gt;&lt;b&gt;id&lt;/b&gt;&amp;nbsp;&lt;b&gt;값&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;, data)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DELETE:&lt;/b&gt; 데이터 제거 axios.delete(url/&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;&lt;u&gt;id 값&lt;/u&gt;&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;TEST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;API JS&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718170455170&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from &quot;axios&quot;;

export default class Memo {
  constructor() {
    this.apiClient = axios.create({
      baseURL: &quot;http://localhost:3001&quot;,
    });
  }

  async getMemos() {
    try {
      const response = await this.apiClient.get(&quot;memo&quot;);
      const data = await response.data;
      return data;
    } catch (error) {
      console.log(&quot;memo error message&quot;, error);
    }
  }

  async createMemo(memo) {
    return await this.apiClient.post(&quot;memo&quot;, memo);
  }

  async deleteMemo(id) {
    return await this.apiClient.delete(`memo/${id}`);
  }

  async updateMemo(memo, id) {
    return await this.apiClient.put(`memo/${id}`, memo);
  }
}&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;b&gt;TEST API JSON&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718170605789&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;memo&quot;: [
    {
      &quot;id&quot;: &quot;5c151155-3217-4d12-8523-880a853698e8&quot;,
      &quot;title&quot;: &quot;제목1&quot;,
      &quot;body&quot;: &quot;내용1&quot;
    },
    {
      &quot;id&quot;: &quot;12d3629f-ecc6-4547-bc32-482220458d52&quot;,
      &quot;title&quot;: &quot;제목2&quot;,
      &quot;body&quot;: &quot;내용2&quot;
    },
    {
      &quot;id&quot;: &quot;767f607d-e528-4c7f-81cc-e47560179438&quot;,
      &quot;title&quot;: &quot;제목3&quot;,
      &quot;body&quot;: &quot;내용3&quot;
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript/REST API</category>
      <category>axios</category>
      <category>delete</category>
      <category>Get</category>
      <category>post</category>
      <category>Put</category>
      <category>react axios</category>
      <category>Rest</category>
      <category>restapi</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/89</guid>
      <comments>https://mikkeller.tistory.com/89#entry89comment</comments>
      <pubDate>Wed, 12 Jun 2024 14:38:33 +0900</pubDate>
    </item>
    <item>
      <title>Next.js에서의 fetch api</title>
      <link>https://mikkeller.tistory.com/88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;next js는 기본 웹 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;server component에서 next js가 내가 fetch 한 것을 기억하기 때문이다.&lt;/p&gt;</description>
      <category>Next.js</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/88</guid>
      <comments>https://mikkeller.tistory.com/88#entry88comment</comments>
      <pubDate>Sun, 5 May 2024 18:50:29 +0900</pubDate>
    </item>
    <item>
      <title>useFormStatus()</title>
      <link>https://mikkeller.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;useFormStatus()&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;이 hook은 자신의 부모 components를 보고, 가장 가까운 form을 찾으려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 useFormStatus는 form이 작성된 components에서는 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해 form 이 작성된 components가 아니라 useFormStatus hook을 사용할 자식 요소 안에서 사용해야 한다.&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;가장 가까운 부모 form을 찾아서 그 부모 form의 상태를 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;form이 pending인지 아닌지 알려주므로 유용하다 (disabled에 pending 값을 넣음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713764473726&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//부모 component


export default function Login() {
  return (
  	&amp;lt;form action={action}&amp;gt;
      &amp;lt;FormButton text=&quot;Login&quot; /&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713764214306&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//자식 component

&quot;use client&quot;;

import { useFormStatus } from &quot;react-dom&quot;;

interface Props {
  text: string;
}

export default function FormButton({ text }: Props) {
  const { pending } = useFormStatus();
  return (
    &amp;lt;button
      disabled={pending}
    &amp;gt;
      {pending ? &quot;Loading...&quot; : text}
    &amp;lt;/button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Next.js</category>
      <category>Next</category>
      <category>next14</category>
      <category>NextJS</category>
      <category>useFormStatus</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/87</guid>
      <comments>https://mikkeller.tistory.com/87#entry87comment</comments>
      <pubDate>Mon, 22 Apr 2024 14:46:14 +0900</pubDate>
    </item>
    <item>
      <title>컴포넌트가 리렌더링 되는 시점은?</title>
      <link>https://mikkeller.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 함수형 컴포넌트는 랜더링 -&amp;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;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;u&gt;컴포넌트가 리랜더링되는 시점&lt;/u&gt;&lt;/b&gt;&lt;/span&gt;은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. state가 변경 되었을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 상속받은 props가 변경 되었을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 부모 컴포넌트가 리렌더링 된 경우 해당 자식 컴포넌트는 모두 리렌더링 된다&lt;/p&gt;</description>
      <category>React/react</category>
      <category>React</category>
      <category>Render</category>
      <category>rendering</category>
      <category>rerrendering</category>
      <category>랜더링</category>
      <category>렌더링</category>
      <category>리랜더링</category>
      <category>리렌더링</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/85</guid>
      <comments>https://mikkeller.tistory.com/85#entry85comment</comments>
      <pubDate>Tue, 30 Jan 2024 10:54:16 +0900</pubDate>
    </item>
    <item>
      <title>CSR, SSG, ISR, SSR 차이점 정리</title>
      <link>https://mikkeller.tistory.com/84</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;C&lt;/b&gt;lient&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;S&lt;/b&gt;ide&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;R&lt;/b&gt;endering&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;렌더링하는 주체자가 클라이언트(브라우저)&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점?&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한번 로딩되면, 빠른 UX 제공&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버의 부하가 작음&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제점?&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;페이지 로딩 시간(TTV)이 길다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 활성화 필수임&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SEO 최적화가 힘듬&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보안에 취약함&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CDN에 캐시가 안됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*TTV(time to view)&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;S&lt;/b&gt;tatic&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;S&lt;/b&gt;ite&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;G&lt;/b&gt;eneration&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 하는 주체자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;u&gt;서버&lt;/u&gt;&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 렌더링 하냐?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;빌드할때 렌더링&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 로딩 시간(TTV: time to 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;SEO 최적화가 좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안이 뛰어남&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN에 캐시가 됨&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제점?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;정적임&lt;/u&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;gt; 이런 문제점을 해결하기 위해 나온게 ISR, SSR&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;S&lt;/b&gt;erver&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;S&lt;/b&gt;ide&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;R&lt;/b&gt;endering&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;렌더링하는 주체자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;u&gt;서버&lt;/u&gt;&lt;/b&gt;,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;언제 렌더링 하냐?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;u&gt;요청시 렌더링&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점?&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;페이지 로딩 시간이 빠름&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트가 필요 없음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SEO 최적화가 좋음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보안이 뛰어남&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실시간 데이터를 사용&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자별 필요한 데이터를 사용함&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제점?&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SSG, ISR 보다 비교적 느릴 수 있음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버의 과부하가 걸릴 수 있음&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;I&lt;/b&gt;ncremental &lt;b&gt;S&lt;/b&gt;tatic &lt;b&gt;R&lt;/b&gt;egeneration&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 하는 주체자가 &lt;b&gt;&lt;u&gt;서버&lt;/u&gt;&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 렌더링 하냐? &lt;b&gt;&lt;u&gt;주기적으로 렌더링&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;/b&gt;SSG와 동일한 원리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 정해진 주기에 따라 페이지를 다시 생성함&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 로딩 시간(TTV: time to view)이 빠름&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 필요 없음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SEO 최적화가 좋음&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보안이 뛰어남&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CDN에 캐시가 됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;데이터가 주기적으로 업데이트 됨&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제점?&lt;/b&gt;&lt;/h4&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;gt; 이런 문제점을 해결하기 위해 나온게 SSR&lt;/p&gt;</description>
      <category>Next.js</category>
      <category>csr</category>
      <category>ISR</category>
      <category>Next.js</category>
      <category>NextJS</category>
      <category>SSG</category>
      <category>SSR</category>
      <category>랜더링</category>
      <category>렌더링</category>
      <category>서버사이드랜더링</category>
      <category>서버사이드렌더링</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/84</guid>
      <comments>https://mikkeller.tistory.com/84#entry84comment</comments>
      <pubDate>Fri, 5 Jan 2024 12:17:34 +0900</pubDate>
    </item>
    <item>
      <title>Compound 패턴</title>
      <link>https://mikkeller.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;u&gt;컴파운드 컴포넌트 패턴&lt;/u&gt;&lt;/b&gt;&lt;/span&gt;은 여러 컴포넌트들이 모여 하나의 동작을 할 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인시스템을 만들기 위해 래퍼런스를 찾는 중, Bootstrap이 해당 패턴을 사용하여 컴포넌트를 만들고 있었다.&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;장점:&lt;/b&gt; 컴파운드 패턴은 동작 구현에 필요한 상태를 내부적으로 가지고 있는데 이것을 사용하는 쪽에서는 드러나지 않아 걱정 없이 사용할 수 있다. 또 이 패턴을 사용하면 자식 컴포넌트들을 일일히 import할 필요 없이 기능을 사용할 수 있다.&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;Context API 를 활용해서 컴파운드 패턴을 구현했고,&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;Toggle, List, Item 컴포넌트가 FlyOutContext Provider에 접근할 수 있도록 해당 컴포넌트는 FlyOut의 자식 컴포넌트로 렌더링해야 한다. Static property로 만들어 줬다.&lt;/p&gt;
&lt;pre id=&quot;code_1702525881866&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';
import { createContext, useContext } from 'react';

// Context Create
const FlyOutContext = createContext();

// Provider
export function FlyOut({ children }) {
  const [open, setOpen] = useState(false);
  const toggle = () =&amp;gt; {
    setOpen((prev) =&amp;gt; !prev);
  };
  const providerValue = { open, toggle };
  return (
    &amp;lt;FlyOutContext.Provider value={providerValue}&amp;gt;
      {children}
    &amp;lt;/FlyOutContext.Provider&amp;gt;
  );
}

// Children Component
function Toggle() {
  const { open, toggle } = useContext(FlyOutContext);

  return (
    &amp;lt;div
      className=&quot;absolute p-[10px] h-[50px] border bg-black&quot;
      onClick={() =&amp;gt; toggle(!open)}
    &amp;gt;
      토글
    &amp;lt;/div&amp;gt;
  );
}

function List({ children }) {
  const { open } = useContext(FlyOutContext);

  return (
    open &amp;amp;&amp;amp; (
      &amp;lt;ul className=&quot;absolute top-[50px] bg-[skyblue] px-[10px]&quot;&amp;gt;{children}&amp;lt;/ul&amp;gt;
    )
  );
}

function Item({ children }) {
  return &amp;lt;li&amp;gt;{children}&amp;lt;/li&amp;gt;;
}

// Toggle 컴포넌트가 FlyOutContext 프로바이더에 접근할 수 있도록 해당 컴포넌트는 FlyOut의 자식 컴포넌트로 렌더링 해야한다.
FlyOut.Toggle = Toggle;
FlyOut.List = List;
FlyOut.Item = Item;&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;아래와 같이 FlyOut 컴포넌트만 import 해줘도 Toggle, List, Item 컴포넌트 등을 사용할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1702526229389&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { useNavigate } from 'react-router-dom';
import classNames from 'classnames';
import formatAgo from '../../util/date';
import styles from './VideoCard.module.scss';
import { FlyOut } from '../FlyOut/FlyOut';

export default function VideoCard({ video, type, ...rest }) {
  const { title, channelTitle, publishedAt, thumbnails } = video.snippet;
  const navigate = useNavigate();
  const isList = type === 'list';
  return (
    &amp;lt;li
      className={isList ? 'flex gap-1 m-2' : 'relative'}
      onClick={() =&amp;gt; {
        navigate(`/videos/watch/${video.id}`, { state: { video } });
      }}
    &amp;gt;
      &amp;lt;FlyOut&amp;gt;
        &amp;lt;FlyOut.Toggle /&amp;gt;
        &amp;lt;FlyOut.List&amp;gt;
          &amp;lt;FlyOut.Item&amp;gt;수정&amp;lt;/FlyOut.Item&amp;gt;
          &amp;lt;FlyOut.Item&amp;gt;삭제&amp;lt;/FlyOut.Item&amp;gt;
        &amp;lt;/FlyOut.List&amp;gt;
      &amp;lt;/FlyOut&amp;gt;

      &amp;lt;img
        className={isList ? 'w-60 mr-2' : 'w-full'}
        src={thumbnails.medium.url}
        alt={title}
      /&amp;gt;

      &amp;lt;div className={styles.content}&amp;gt;
        &amp;lt;h3 className={classNames(styles.title, 'line-clamp-3')}&amp;gt;{title}&amp;lt;/h3&amp;gt;
        &amp;lt;p className={styles.channel}&amp;gt;{channelTitle}&amp;lt;/p&amp;gt;
        &amp;lt;p className={styles.publishedAt}&amp;gt;{formatAgo(publishedAt, 'ko')}&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-14 12.51.45.png&quot; data-origin-width=&quot;2464&quot; data-origin-height=&quot;1562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx0wSm/btsBVENLRE2/GqaFy2IBhJOqQkCTUZQG31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx0wSm/btsBVENLRE2/GqaFy2IBhJOqQkCTUZQG31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx0wSm/btsBVENLRE2/GqaFy2IBhJOqQkCTUZQG31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx0wSm%2FbtsBVENLRE2%2FGqaFy2IBhJOqQkCTUZQG31%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;2464&quot; height=&quot;1562&quot; data-filename=&quot;스크린샷 2023-12-14 12.51.45.png&quot; data-origin-width=&quot;2464&quot; data-origin-height=&quot;1562&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;</description>
      <category>React/design patterns</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/83</guid>
      <comments>https://mikkeller.tistory.com/83#entry83comment</comments>
      <pubDate>Thu, 14 Dec 2023 12:57:19 +0900</pubDate>
    </item>
    <item>
      <title>react-router-dom</title>
      <link>https://mikkeller.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;react-router에서 자주 사용했던 함수들을 정리,&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;h4 data-ke-size=&quot;size20&quot;&gt;useParams()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path: '/videos/&lt;span style=&quot;color: #ee2323;&quot;&gt;:keyword&lt;/span&gt; '&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path 값의 페이지에서 param 값을 가져올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1701747212592&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useParams } from &quot;react-router-dom&quot;;
const { keyword } = useParams();&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;useNavigate()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onclick={() =&amp;gt; navigate(`/videos/watch/${video.id}`)}&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.location 과 vue의 router.push 로 생각해주면 되겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1701747378522&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useNavigate } from &quot;react-router-dom&quot;;
const navigate = useNavigate();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useLocation()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;navigate(`/videos/watch/${video.id}`,&amp;nbsp;{&amp;nbsp;state:&amp;nbsp;{&amp;nbsp;video&amp;nbsp;}&amp;nbsp;})&amp;nbsp;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useNavigate로 페이지가 랜딩 됐을 때 넘겨준 이전 페이지에서 값을 받아올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;navigate의 두번째 인자 값에 { state: { key: value } } 로 넘겨주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 key, value 값을 동일하게 전달했었기 때문에 축약해서 전달이 가능했다.&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;전달 받은 페이지에서 useLocation 으로 값을 받아올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1701747743663&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useLocation } from &quot;react-router-dom&quot;;
const {
  state: { video },
} = useLocation();&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React/react</category>
      <category>react-router</category>
      <category>react-router-dom</category>
      <category>useLocation</category>
      <category>useNavigate</category>
      <category>useParams</category>
      <category>리액트라우터</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/82</guid>
      <comments>https://mikkeller.tistory.com/82#entry82comment</comments>
      <pubDate>Tue, 5 Dec 2023 12:41:32 +0900</pubDate>
    </item>
    <item>
      <title>챌린지를 시작하다.</title>
      <link>https://mikkeller.tistory.com/81</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2f8cb1ad.jpg&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddwjip/btsBlcwG4xj/TWBsRYJDM2h8MgT4UGfXwK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddwjip/btsBlcwG4xj/TWBsRYJDM2h8MgT4UGfXwK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddwjip/btsBlcwG4xj/TWBsRYJDM2h8MgT4UGfXwK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddwjip%2FbtsBlcwG4xj%2FTWBsRYJDM2h8MgT4UGfXwK%2Fimg.jpg&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;2880&quot; height=&quot;1580&quot; data-filename=&quot;2f8cb1ad.jpg&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1580&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;&lt;b&gt;원티드 프리온보딩 프론트엔드 챌린지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비즈니스 로직 완전 정복 with React&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;&lt;a href=&quot;https://www.notion.so/ankodeveloper/d4d7be092641490ab2afe141d2844499&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;프론트엔드 챌린지는 노션에 기록하려고 합니다 :D&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>프론트엔드 챌린지</category>
      <author>앙꼬오빠</author>
      <guid isPermaLink="true">https://mikkeller.tistory.com/81</guid>
      <comments>https://mikkeller.tistory.com/81#entry81comment</comments>
      <pubDate>Mon, 4 Dec 2023 12:12:00 +0900</pubDate>
    </item>
  </channel>
</rss>