<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HyeonSi</title>
    <link>https://hyeonsi.tistory.com/</link>
    <description>열혈 청춘 프로그래머 성장 블로그.
아니면 주로 재미없는 글을 씀.</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 04:22:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HyeonSi</managingEditor>
    <image>
      <title>HyeonSi</title>
      <url>https://tistory1.daumcdn.net/tistory/4720382/attach/1ffb6c020e2d4e6aa16e1f2893cd8adb</url>
      <link>https://hyeonsi.tistory.com</link>
    </image>
    <item>
      <title>2024.05.01(Wed.) Zustand와 함께 알아보는 전역 상태 관리의 원리</title>
      <link>https://hyeonsi.tistory.com/171</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dS2xzn/btsG5lu0UYp/xDt0Noca2Y4i2BjAOe6G20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dS2xzn/btsG5lu0UYp/xDt0Noca2Y4i2BjAOe6G20/img.jpg&quot; data-alt=&quot;볼떄마다 느끼는 참 개발자 스러운 곰&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dS2xzn/btsG5lu0UYp/xDt0Noca2Y4i2BjAOe6G20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdS2xzn%2FbtsG5lu0UYp%2FxDt0Noca2Y4i2BjAOe6G20%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;1200&quot; height=&quot;600&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;볼떄마다 느끼는 참 개발자 스러운 곰&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개요&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react의 전부라고 할 수 있는, 상태관리의 근본적인 원리도 모르고 쓰기는 react에게 미안하다.&lt;/li&gt;
&lt;li&gt;실무에서 느낀 상태관리의 의의에 대해 다시 생각해 보고 싶었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 상태?&lt;/h3&gt;
&lt;figure id=&quot;og_1714522912943&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;Managing State &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;react.dev&quot; data-og-source-url=&quot;https://react.dev/learn/managing-state&quot; data-og-url=&quot;https://react.dev/learn/managing-state&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boS83r/hyVZmZfREt/eIkBvGwpSjwED5YclYV1u1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/dqeXE4/hyVZivNwRu/dXbBUF7QBlsRMr1Sft1uWK/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://react.dev/learn/managing-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react.dev/learn/managing-state&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boS83r/hyVZmZfREt/eIkBvGwpSjwED5YclYV1u1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/dqeXE4/hyVZivNwRu/dXbBUF7QBlsRMr1Sft1uWK/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567');&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;Managing State &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The library for web and native user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react dev docs에 따르면, 직접적으로 명령에 대해 명시하지 않고, 시각적 상태에 따라 보고 싶은 UI를 설명한다고 한다.&lt;/li&gt;
&lt;li&gt;즉, UI 변경에 따른 결과를 말 그대로 '상태'라고 할 수있고, 그것을 저장, 변경, 컴포넌트 간 전달한다.&lt;/li&gt;
&lt;li&gt;이때, 상태는 UI 변경점에 따른 것이므로, 상태가 변하면 UI 도 자연스럽게 업데이트 될 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. useState&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하루에도 수 십번 사용하는 근본 hook.&lt;/li&gt;
&lt;li&gt;상태를 변경하고, 사용할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1714523441059&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [data, setData] = useState([])&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;li&gt;근본적 원리는, useState는 초기값을 처음 인자로 받아 disoatcher 인스턴스를 생성해, 유저가 원할 때마다 반환해 준다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;특이점&lt;/span&gt; :&amp;nbsp; &amp;nbsp;우리는 처음 'const'로 useState를 구조분해할당 했기 때문에 data가 직접 바뀌지는 않는다.&lt;/li&gt;
&lt;li&gt;전역에 생성된 &lt;b&gt;closuer&lt;/b&gt;가 useState가 실행될 때마다 초기값을 비교하고 할당해 준다.&lt;/li&gt;
&lt;li&gt;setState가 실행되면 받은 인수로 closuer의 값을 변경시켜 주고, 리렌더링을 실행하며, 바뀐 값을 useState가 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 전역 상태 관리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결국, useState의 값은 한 컴포넌트 안에서만 사용가능하다.&lt;/li&gt;
&lt;li&gt;같은 상태를 위계가 다른 컴포넌트에서 사용하려고 하거나, 상태를 props 드릴링 하다보면 상태의 생명주기를 알 수 없게 되고, 무분별한 리렌더링이 일어난다.&lt;/li&gt;
&lt;li&gt;그래서 한번 선언한 상태를 다른 컴포넌트에서도 사용하기 위해 전역 수준에서 상태를 관리하기도 한다.&lt;/li&gt;
&lt;li&gt;기본적인 방법으로 Context API를 사용할 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Context API는 '의존성 주입'을 위한 API 이다.&lt;/li&gt;
&lt;li&gt;ContextProvuder 컴포넌트를 만들어 내부에 useState를 사용해 상태를 만들어주고 value를 props로 받는다.&lt;/li&gt;
&lt;li&gt;value가 바뀔 때마다 상태를 변경해 주고 다시 바뀐 상태를 반환 한다.&lt;/li&gt;
&lt;li&gt;이후 ContextProvuder 아래의 children들은 useContext hook으로 상태에 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;단점&lt;/span&gt; :&amp;nbsp; props의 value가 바뀔 경우 모든 children들은 리렌더링 된다. 결국 상태이기 때문이다. 그래서 불필요한 리렌더링을 막기 위해 memoization이 필수적이다. 결국, 어디서 memoization이 일어나고, 상태가 변경하는지를 정확히 알지 못하게 되는 경우가 프로젝트 사이즈가 커지면 종종 일어나기 때문에 상태의 생명주기를 어지럽히는 요인이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Zustand&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;redux보다 간단하고, 필요 없는 렌더링을 조절하기가 간단한 전역 상태 관리 라이브러리이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1714525156261&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;Zustand&quot; data-og-description=&quot;&quot; data-og-host=&quot;zustand-demo.pmnd.rs&quot; data-og-source-url=&quot;https://zustand-demo.pmnd.rs/&quot; data-og-url=&quot;https://zustand-demo.pmnd.rs/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://zustand-demo.pmnd.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zustand-demo.pmnd.rs/&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;Zustand&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zustand-demo.pmnd.rs&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1714525183732&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { create } from 'zustand'

const useStore = create((set) =&amp;gt; ({
  count: 1,
  inc: () =&amp;gt; set((state) =&amp;gt; ({ count: state.count + 1 })),
}))

function Counter() {
  const { count, inc } = useStore()
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;span&amp;gt;{count}&amp;lt;/span&amp;gt;
      &amp;lt;button onClick={inc}&amp;gt;one up&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&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;useState와 마찬가지로, 초기값, set 함수의 동작을 create 하고, hook으로 어디서든 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;일반적으로 create로 만든 hook 들을 한 파일에서 관리하고, 사용해야 할 컴포넌트에서 가져와 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 적절한 상태관리란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태의 '범위'를 알고 사용하는 것이 중요하다. 각 스코프에 맞는 사이즈의 상태를 만들고, 상태의 생명주기를 개발자가 마음대로 조절할 수 있어야 한다. 불필요하게 모든 상태를 전역으로 사용하려 한다거나, 무분별한 props 드릴링으로 상태를 컴포넌트 간에 전달하는 방향은 다른 개발자들을 괴롭히는 일이다.&lt;/li&gt;
&lt;li&gt;필요에 의한 상태만을 만든다. 상태로 관리해야 할 것은 'UI 변화에 따른 값'이다. 한번 선언해 놓고 더 이상 변하지 않는 값을 가진 경우나, 초기값만 이 중요한 경우라면 굳이, 상태로 만들지 않고 선언해서도 충분히 사용할 수 있다. 불필요한 리렌더링을 줄이는 것도 큰 의의가 있다.&lt;/li&gt;
&lt;li&gt;여러 가지 전역 상태관리 라이브러리를 혼용해서 사용하지 않는다. 상태의 생명주기를 더욱 따라가기 힘들게 할 뿐이다.&lt;/li&gt;
&lt;li&gt;의존적이지 않고, 결합적이지 않은 코드를 작성하기 위해서는 전역 상태 관리를 사용하지 않는 것이 좋다. 필요에 따라 사용할 수 도 있지만 분명, 재사용에 큰 제약을 만들 것이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/171</guid>
      <comments>https://hyeonsi.tistory.com/171#entry171comment</comments>
      <pubDate>Wed, 1 May 2024 10:15:36 +0900</pubDate>
    </item>
    <item>
      <title>2024.04.27(Sat.) &amp;lt;React Query를 통한 state에 관한 고찰 &amp;gt;</title>
      <link>https://hyeonsi.tistory.com/170</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOg9NI/btsGZUjHngY/rPHJduyat6nDnqIThkxNd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOg9NI/btsGZUjHngY/rPHJduyat6nDnqIThkxNd1/img.png&quot; data-alt=&quot;0부터 1까지만 알아보자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOg9NI/btsGZUjHngY/rPHJduyat6nDnqIThkxNd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOg9NI%2FbtsGZUjHngY%2FrPHJduyat6nDnqIThkxNd1%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;1280&quot; height=&quot;249&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;0부터 1까지만 알아보자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react query를 왜 사용하는가? 에 대한 질문은 너무 쉽게 할 수 있다. 왜냐고? 캐싱, 최적화, 언제 어디서든 서버에서 최신의 상태를 가져다가 전역으로 사용할 수 있는 매우 편한 라이브러리가 아닐수 없다! 하지만 세상에 완벽한게 어디있나, 분명 그에도 단점은 존제 할 것이다. 그래서 단점을 찾기 전에 가장 근본적인 질문부터 하나하나 알아 보려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Query&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 많은 프론트 개발자들은 query와 친하지 않다.(사실 뭔지도 잘 모르겠다. 하지만 직접 쿼리를 써보라고 하면 또 쓴다...!? 어째서..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- query : &lt;b&gt;질의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- DB와 통신에 필요한 요청을 질의하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이를 위해 만들어지는 질문을 &lt;b&gt;SQL&lt;/b&gt;이라고 부른다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. react-qurey&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 상태관리를 위한 라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- fetching, caching, synchronizing, updating을 간단하게 hook으로 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존의 미들웨어는 복잡한 로직으로 만드는 과정이 쉽지않고, 이를 위해 보일러 플레이트를 만들어 여러 프로젝트에 적용한다고 해도, 코드의 양도 많아진다. 이를 react query는 대체할 수 있다.&lt;br /&gt;(하지만 여기서 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;단점&lt;/span&gt;&lt;/b&gt;, 이렇게 내부 로직을 잘 알고 쓴다면 상관없지만, 다양한 hook들이 어떠한 방식을 통해 데이터를 다루고 있으며 데이터의 흐름을 직접 제어할 수 없다는 점에서 단점이 있다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- QueryClientProvider가 React Context API를 사용해 가장 최상단에 존제하기 때문에, 전역으로 사용할 수 있는 서버의 상태가 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. react-qurey의 기본 동작 순서&lt;/h3&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터의 상태를 확인한다.&lt;/li&gt;
&lt;li&gt;데이터 상태의 종류
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fresh : 최신화 된 데이터. 혹은, 아직 staleTime이 지나지 않아서 개발자가 보장하는 최신화된 데이터 &lt;br /&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; 이미 최신화된 데이터라면, 중복으로 네트워크 요청을 막음&lt;/li&gt;
&lt;li&gt;fetching : 서버와 데이터를 통신하는 중&lt;/li&gt;
&lt;li&gt;stale : 최신화 되지 않은 옛날 데이터 -&amp;gt; react qurey는 이 경우 다시 fetching -&amp;gt; fresh한 데이터로 상태를 바꾸려 한다.&lt;/li&gt;
&lt;li&gt;delete : 삭제된 데이터. 혹은, cacheTime이 지나 케시에도 없는 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상태에 따라 서버와 통신&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.간단한 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버와 통신하여 데이터가 최신인지를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이때, 최초의 통신이 발생한다. 그래서 데이터가 최신인지를 확인하게 된다. 그리고 이는 state로 관리된다. (useQurey)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 최신이라면, 캐싱하고 알맞는 queryKey에 바인딩 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만일 데이터가 stale하여 다시 서버와 통신이 일어나 데이터가 최신화 되면, state도 바뀐 것이므로 리렌더된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 더 자세한 내용은 아래 글 참고&lt;/p&gt;
&lt;figure id=&quot;og_1714196300890&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;React Query의 구조와 useQuery 실행 흐름 살펴보기 | 카카오엔터테인먼트 FE 기술블로그&quot; data-og-description=&quot;함성준(kevin) 개발에는 인생(喜怒哀樂)이 담겨있습니다. 커피 좋아합니다.&quot; data-og-host=&quot;fe-developers.kakaoent.com&quot; data-og-source-url=&quot;https://fe-developers.kakaoent.com/2023/230720-react-query/&quot; data-og-url=&quot;https://fe-developers.kakaoent.com/2023/230720-react-query/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bp7O7B/hyVVGYply6/99BjKYdRRng3pek6NhL910/img.png?width=2794&amp;amp;height=1986&amp;amp;face=0_0_2794_1986&quot;&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2023/230720-react-query/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fe-developers.kakaoent.com/2023/230720-react-query/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bp7O7B/hyVVGYply6/99BjKYdRRng3pek6NhL910/img.png?width=2794&amp;amp;height=1986&amp;amp;face=0_0_2794_1986');&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;React Query의 구조와 useQuery 실행 흐름 살펴보기 | 카카오엔터테인먼트 FE 기술블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;함성준(kevin) 개발에는 인생(喜怒哀樂)이 담겨있습니다. 커피 좋아합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;6. state와의 접점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- react query는 데이터 패칭 라이브러리가 아니다.&lt;/p&gt;
&lt;figure id=&quot;og_1714196857762&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;Thinking in React Query&quot; data-og-description=&quot;In this talk, we will learn how a different mindset can help us understand React Query and work with it efficiently.&quot; data-og-host=&quot;tkdodo.eu&quot; data-og-source-url=&quot;https://tkdodo.eu/blog/thinking-in-react-query&quot; data-og-url=&quot;https://tkdodo.eu/blog/thinking-in-react-query&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bls16S/hyVVEsKOCz/WGyqKHPYTrilyBLWpbebjk/img.png?width=1200&amp;amp;height=630&amp;amp;face=84_487_125_532,https://scrap.kakaocdn.net/dn/y8YUR/hyVVJ8EQox/EQ7K9u1S4P3kSkmO34PPAK/img.png?width=1200&amp;amp;height=630&amp;amp;face=84_487_125_532,https://scrap.kakaocdn.net/dn/bNXUJU/hyVVCuUS4S/8HEt5Ap5EV1T6JHNpLDkR1/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277&quot;&gt;&lt;a href=&quot;https://tkdodo.eu/blog/thinking-in-react-query&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tkdodo.eu/blog/thinking-in-react-query&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bls16S/hyVVEsKOCz/WGyqKHPYTrilyBLWpbebjk/img.png?width=1200&amp;amp;height=630&amp;amp;face=84_487_125_532,https://scrap.kakaocdn.net/dn/y8YUR/hyVVJ8EQox/EQ7K9u1S4P3kSkmO34PPAK/img.png?width=1200&amp;amp;height=630&amp;amp;face=84_487_125_532,https://scrap.kakaocdn.net/dn/bNXUJU/hyVVCuUS4S/8HEt5Ap5EV1T6JHNpLDkR1/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277');&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;Thinking in React Query&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this talk, we will learn how a different mindset can help us understand React Query and work with it efficiently.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tkdodo.eu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1714196828660&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;[번역] React Query 적으로 사고하기&quot; data-og-description=&quot;React Query Maintainer인 Tkdodo가 알려주는 리액트 쿼리적으로 사고할 수 있는 3가지 요소들을 다루어봅니다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@seungchan__y/React-Query-%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EA%B3%A0%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@seungchan__y/React-Query-적으로-사고하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lBzpl/hyVVAX99JH/ceZKLJ2ydpqYUQnW87AeNK/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277,https://scrap.kakaocdn.net/dn/bxEbpB/hyVVLMahYc/wn4Skr8wDk6W1Vzz1cOPF0/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277,https://scrap.kakaocdn.net/dn/fRjiD/hyVVxUHWxT/UgUinmVUXG6nCGNH3HNkCk/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277&quot;&gt;&lt;a href=&quot;https://velog.io/@seungchan__y/React-Query-%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EA%B3%A0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@seungchan__y/React-Query-%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EA%B3%A0%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lBzpl/hyVVAX99JH/ceZKLJ2ydpqYUQnW87AeNK/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277,https://scrap.kakaocdn.net/dn/bxEbpB/hyVVLMahYc/wn4Skr8wDk6W1Vzz1cOPF0/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277,https://scrap.kakaocdn.net/dn/fRjiD/hyVVxUHWxT/UgUinmVUXG6nCGNH3HNkCk/img.png?width=960&amp;amp;height=541&amp;amp;face=433_210_494_277');&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;[번역] React Query 적으로 사고하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React Query Maintainer인 Tkdodo가 알려주는 리액트 쿼리적으로 사고할 수 있는 3가지 요소들을 다루어봅니다.&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;- 비동기 상태 관리자 (&lt;b&gt;Async State Manager)&amp;nbsp;&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라이언트 단에서는 네트워크를 통신해서 불러온 데이터도 '상태'로 관리하고 UI에 필요한 데이터도 '상태'로 관리하게 된다. 필요에 따라 스코프를 넓혀 '전역 상태'로도 관리하게 된다.&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;- 그리고 서버 상태는 효율적으로 관리될 필요가 더욱 크다. 필요할때 &lt;b&gt;데이터&lt;/b&gt;를 불러 &lt;b&gt;상태&lt;/b&gt;를 변경해 최신화 하고, 이미 최신의 &lt;b&gt;데이터&lt;/b&gt;를 유지하고 있다면 &lt;b&gt;상태&lt;/b&gt;를 업데이트 할 필요가 없어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이때, 데이터의 최신화 여부가 상태의 업데이트를 함께 다뤄준다는 점이&amp;nbsp; react query의 최고 장점 되시겠다.&lt;/p&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/170</guid>
      <comments>https://hyeonsi.tistory.com/170#entry170comment</comments>
      <pubDate>Sat, 27 Apr 2024 14:59:35 +0900</pubDate>
    </item>
    <item>
      <title>2023.08.11(Fri.) &amp;lt;Atomic Design Pattern&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/169</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d310fi/btsq1cmw05W/J6HnKG0OdrCGvRKxSs9vc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d310fi/btsq1cmw05W/J6HnKG0OdrCGvRKxSs9vc0/img.jpg&quot; data-alt=&quot;작은 것들을 모아 정리 하는 느낌이랑 비슷한 느낌이라 가져온 짤입니다 틀니 딱딱딱! (윈도우98입니다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d310fi/btsq1cmw05W/J6HnKG0OdrCGvRKxSs9vc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd310fi%2Fbtsq1cmw05W%2FJ6HnKG0OdrCGvRKxSs9vc0%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;562&quot; height=&quot;217&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작은 것들을 모아 정리 하는 느낌이랑 비슷한 느낌이라 가져온 짤입니다 틀니 딱딱딱! (윈도우98입니다)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제의 발단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RN 강의를 듣던 중 강사님이 참 폴더 구조들을 정리를 잘한다고 생각했다.&lt;/li&gt;
&lt;li&gt;지금 프로젝트도 나름 정리를 하여 사용한다고 생각했지만, 뭔가 10% 부족하다고 느꼈다. 폴더 구조에 대한 정답은 없기 때문이다.&lt;/li&gt;
&lt;li&gt;그래서 그나마 정답에 가까운 것을 찾아 보려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Atomic Design Pattern?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React의 구조는 수많은 컴포넌트로 되어 있다. 그러므로 똑같은 코드를 여러 번 사용하는 극한의 재사용성을 보여줄 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 아무리 유용한 컴포넌트라도 어디에 있는지 찾지 못 하면 낭패다. 마치 침대 위 TV리모컨처럼 말이다.&lt;/li&gt;
&lt;li&gt;그래서 유형별로, 혹은 특정 공통점을 기준으로 잘 정리해 두려고 한다. 이때 atomic design pattern은 '디자인'을 중점적으로 이용하여 정리하고 그를 먼저 만들고 조립하는 디자인 시스템을 뜻한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 그림으로 보는 Atomic Design Pattern&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oDSAu/btsq1FaVdBI/RZA1HRPPQGLN4UsOJWTEx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oDSAu/btsq1FaVdBI/RZA1HRPPQGLN4UsOJWTEx1/img.png&quot; data-alt=&quot;출처 https://uxdesign.cc/atomic-design-2022-what-we-can-learn-from-eames-and-other-design-giants-4d8e2f579daa&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oDSAu/btsq1FaVdBI/RZA1HRPPQGLN4UsOJWTEx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoDSAu%2Fbtsq1FaVdBI%2FRZA1HRPPQGLN4UsOJWTEx1%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;700&quot; height=&quot;501&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 https://uxdesign.cc/atomic-design-2022-what-we-can-learn-from-eames-and-other-design-giants-4d8e2f579daa&lt;/figcaption&gt;
&lt;/figure&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3640&quot; data-origin-height=&quot;1680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWcyuk/btsq0vzOpq2/GszOxRge6csUAMwk2wiSlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWcyuk/btsq0vzOpq2/GszOxRge6csUAMwk2wiSlK/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://actgovproject.bitbucket.io/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWcyuk/btsq0vzOpq2/GszOxRge6csUAMwk2wiSlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWcyuk%2Fbtsq0vzOpq2%2FGszOxRge6csUAMwk2wiSlK%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;3640&quot; height=&quot;1680&quot; data-origin-width=&quot;3640&quot; data-origin-height=&quot;1680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://actgovproject.bitbucket.io/&lt;/figcaption&gt;
&lt;/figure&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;/li&gt;
&lt;li&gt;작은 기능을 하는 태그, 디자인에 필요한 아이콘 등이 합쳐저서 하나의 기능을 하는 작은 컴포넌트가 되고, 그들이 모여 큰 레이아웃을 형성하게 되면서, 데이터를 넣으면 큰 페이지가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 어떻게 진행 되는가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Atoms : 가장 작은 디자인 요소의 단위&lt;br /&gt;2. Molecules : 적어도 하나의 기능은 할 수 있는 컴포넌트 단위&lt;br /&gt;3. Organisms : 하나의 기능을 하는 요소들을 합쳐 디자인적 요소까지 더한 유저들에게 의미를 부여한 단위&lt;br /&gt;4. Template : 데이터를 연결하기 전의 레이아웃 단위&lt;br /&gt;5. Page : 유저들이 보는 단위&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 실전편&lt;/h3&gt;
&lt;pre id=&quot;tree-panel&quot; class=&quot;cmake&quot; style=&quot;color: #d4d4d4; text-align: start;&quot;&gt;&lt;code&gt; atomic-design-pattern-test
 ┣  components
 ┃ ┣  atoms
 ┃ ┣  moecules
 ┃ ┣  organisms
 ┃ ┗  templates
 ┗  pages&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;li&gt;가끔 헷갈리는 부분이 있다. 무엇이 molecules이고, 무엇이 organisms 일까??
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;나는 너무 고민하지 않기로 했다. 그저 '우리'가 보기 편하면 모든 것이 완벽하다고 생각했기 때문에 기획자와 디자이너와 적당한 합의 후에 디자인 시스템부터 나누어 생각했다.&lt;/li&gt;
&lt;li&gt;우리가 이 컴포넌트는 molecules라고 말하면 molecules인 것이고, organisms이면 organisms인 것이다. 그래서 우리들만의 규칙을 만들어 나누어 주었다. (ex: 결제나 비즈니스 로직이 들어간 컴포넌트면 organisms로 보자)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 결론&lt;/h3&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;li&gt;결국 우리가 선택한 구조는 페이지를 기준으로 기능적인 부분을 때서 나눠놓은 패턴이 되었다(아, 정리했는데 ㅎㅎ)&lt;/li&gt;
&lt;li&gt;프로젝트마다 어떤 디자인 페턴을 선택하는지가 이후에 프로젝트의 규모가 거대해졌을 때도 큰 영향을 미치니, 최대한 확장성과 정확한 규칙을 토대로 만들어 가는 것이 좋을 것 같다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/169</guid>
      <comments>https://hyeonsi.tistory.com/169#entry169comment</comments>
      <pubDate>Sat, 12 Aug 2023 00:13:17 +0900</pubDate>
    </item>
    <item>
      <title>2023.07.27(Tue.) &amp;lt;얻은 알고리즘 문제 : 해싱, 시간파싱&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/168</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;한 번 사용한 최초문자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서 한번만 사용한 문자를 찾으려고 합니다. 매개변수 s에 문자열이 주어지면 한번만 사용한 문자 중 문자열에서 가장 먼저 나타난 문자의 인덱스 번호를 반환하는 프로그램을 작성하세요. 인덱스는 1부터 시작합니다. 한번만 사용한 문자가 없을 경우 -1를 반환하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s answer&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;statitsics&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;aabb&quot;&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;stringshowtime&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;abcdeabcdfg&quot;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한사항: &amp;bull; 문자열 s의 길이는 100을 넘지 않습니다. &amp;bull; 문자열은 소문자로만 이루어져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력예제 1 설명 : 한번만 사용한 문자는 a, c이고, 문자열에서 먼저 나타난 것은 a이고 인덱스는 3입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690467506657&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const func = (string) =&amp;gt; {
  // 주어진 문자열을 각 문자로 분리하여 배열로 만든다.
  const strArr = string.slice().split('');
  // 결과를 저장할 객체를 생성한다.
  const result = {};

  // strArr 배열의 각 문자에 대해 처리,
  for (let i = 0; i &amp;lt; strArr.length; i++) {
    const currentChar = strArr[i];
    // 현재 문자가 result 객체의 프로퍼티로 존재하는지 반복문으로 확인한다.
    const existingValue = result[currentChar];

    // 현재 문자의 값이 존재하는지 확인한다.
    // 존재하는 경우 해당 값을 1 증가시킨다.
    // 존재하지 않는 경우(빈 값인 경우) 새로운 키를 생성하고 값을 1 준다.
    result[currentChar] = existingValue ? existingValue + 1 : 1;
  }

  // 0일 경우 -1이기 때문에 중간에 한번 걸러준다.
  const filterFunc = (result) =&amp;gt; {
    return result === 0 ? -1 : result;
  }
  return filterFunc(Object.values(result).findIndex((el) =&amp;gt; el === 1) + 1);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/168</guid>
      <comments>https://hyeonsi.tistory.com/168#entry168comment</comments>
      <pubDate>Thu, 27 Jul 2023 23:19:11 +0900</pubDate>
    </item>
    <item>
      <title>2023.07.12(Wed.) &amp;lt;React 항상 z-index 최상위에 있는 Modal 만들기&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/167</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2QVeZ/btsnpjpoeEt/tTglmhDiIeVXmTfTEYa040/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2QVeZ/btsnpjpoeEt/tTglmhDiIeVXmTfTEYa040/img.jpg&quot; data-alt=&quot;대단한 기술을 하찮은 곳에 사용합니다. 하지만 가장 중요하죠.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2QVeZ/btsnpjpoeEt/tTglmhDiIeVXmTfTEYa040/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2QVeZ%2FbtsnpjpoeEt%2FtTglmhDiIeVXmTfTEYa040%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;387&quot; height=&quot;619&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대단한 기술을 하찮은 곳에 사용합니다. 하지만 가장 중요하죠.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제의 발단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스타일 가이드를 고치면서 확인하니, z-index가 모든 alert, modal등에서 꼬여있다.&lt;/li&gt;
&lt;li&gt;가장 최상의 단위를 잡고, 위계를 생각하며 기획이 되어야 하지만 추가되는 사항이 많고, 리팩토링 하지 못 해서 꼬여있었다.&lt;/li&gt;
&lt;li&gt;그래서 임시로 추가할 alert에 z-index를 9999로 설정하고 만들었다.&lt;/li&gt;
&lt;li&gt;하지만 하위 노드에서 이미 modal을 만들고, 그것을 기준으로 다음 컴포넌트들을 만들어 z-index와 상관없이 최상위로 올라오지 않는 이슈가 있었다.&lt;/li&gt;
&lt;li&gt;그래서 아예 처음부터 가장 최상위에 올려서 렌더링 하기로 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Potal?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 DOM의 외부에 렌더링을 하게 만들어주는 함수&lt;/li&gt;
&lt;li&gt;첫 번째 인자에는 컴포넌트, 두 번째 인자로 DOM을 넣어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1689166430427&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Dispatch, SetStateAction, useEffect } from 'react';
import { createPortal } from 'react-dom';

export function Alert({
  children,
  setState,
}: {
  children: React.ReactNode;
  setState: Dispatch&amp;lt;SetStateAction&amp;lt;boolean&amp;gt;&amp;gt;;
}) {
  // useEffect로 timeout을 걸어 3초 뒤 자동으로 꺼지게 만든다.
  useEffect(() =&amp;gt; {
    const timeout = setTimeout(() =&amp;gt; {
      setState(false);
    }, 3000);

    // 다시 alert가 열리더라도, 처음 timeout을 초기화 하여 가장 나중에 열린 alert를 3초 유지시킨다.
    return () =&amp;gt; clearTimeout(timeout);
  }, []);

  // 가장 최상위 DOM을 선택한다.
  const modalRoot = document.getElementById('modal-root');
  
  // createPortal로 return한다. 
  return createPortal(
    &amp;lt;Backdrop onClick={() =&amp;gt; setState(false)}&amp;gt;
      &amp;lt;div onClick={(e) =&amp;gt; e.stopPropagation()}&amp;gt;
        {children}
      &amp;lt;/div&amp;gt;
    &amp;lt;/Backdrop&amp;gt;,
    modalRoot!
  ) as any;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 소감&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음 스타일 가이드를 잡을때, z-index도 따로 설정에 잡아 놓는 것이 가장 좋겠다.&lt;/li&gt;
&lt;li&gt;예를 들어, 항상 최상위에 오는 Backdrop을 최상위, 그 아래로 header, footer 등으로 말이다.&lt;/li&gt;
&lt;li&gt;그리고 상수로 z-index를 관리하면 중간에 다른 단계를 넣거나 수정하더라도 편하게 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/167</guid>
      <comments>https://hyeonsi.tistory.com/167#entry167comment</comments>
      <pubDate>Wed, 12 Jul 2023 21:56:29 +0900</pubDate>
    </item>
    <item>
      <title>2023.06.29(Tue.) &amp;lt;Blob Type Response의 에러 처리&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/166</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YwsaH/btslUMso2B1/7WpdxuWHr6k78kKOXkmTP0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YwsaH/btslUMso2B1/7WpdxuWHr6k78kKOXkmTP0/img.jpg&quot; data-alt=&quot;그만 둬요 제발. 사실 제가 했습니다. 죄송합니다. 실수에요. 근데 정말 어 할만한 상황이었어요. 들어보세요.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YwsaH/btslUMso2B1/7WpdxuWHr6k78kKOXkmTP0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYwsaH%2FbtslUMso2B1%2F7WpdxuWHr6k78kKOXkmTP0%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;566&quot; height=&quot;558&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;1109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그만 둬요 제발. 사실 제가 했습니다. 죄송합니다. 실수에요. 근데 정말 어 할만한 상황이었어요. 들어보세요.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제의 발단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;axios response type을 'Blob'으로 받아 서버에서 엑셀 다운로드를 가능하게 하는 API를 연결하는 작업을 하고 있었다.&lt;/li&gt;
&lt;li&gt;받는 타입을 Blob으로 해야 브라우저의 다운로드 기능까지 연결되어 처리가 가능하다.&lt;/li&gt;
&lt;li&gt;기능은 원하는 방향으로 잘 작동하나 문제는 에러가 났을 경우이다.&lt;/li&gt;
&lt;li&gt;response도 blob 형식으로 온다. 그래서 서버와 합의한 error code를 뽑아 상황에 맞는 에러 처리가 되지 않았다.&lt;/li&gt;
&lt;li&gt;액세스 토큰이 만료되었을 때, axios 인스턴스에 설정해 놓은 대로 reissueToken이 안 되는 현상을 발견하여 확인해 보니, 토큰 만료 시 나오는 에러 code를 잡지 못하고 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 처음 상황&lt;/h3&gt;
&lt;pre id=&quot;code_1688040292661&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getData() {
  return await axiosInstance.get(`${baseURLV3}/data`, {
    responseType: 'blob',
  });
}&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;위와 같이 api를 호출하는 코드를 만들어 놓은 상황&lt;/li&gt;
&lt;li&gt;에러가 날 경우 Blob 객체 형태로 error를 받음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해결법&lt;/h3&gt;
&lt;pre id=&quot;code_1688040365813&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getData() {
  return await axiosInstance.get(`${baseURLV3}/data`, {
    responseType: 'blob',
  });
}

...
catch(err) {
	const errorResponse = JSON.parse(await err.response.data.text());

	if(errorResponse.code === '토큰이 만료 되었을 때 나는 에러 코드') {
		return reissueToken()
	} else {
		return console.log('is not token issue')	
	}
}&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;b&gt;JSON.parse(await error.response.data.text());&lt;/b&gt; 으로 다시 처리하면 우리가 알던 방식 그대로 error의 형태를 바꿀 수 있다.&lt;/li&gt;
&lt;li&gt;대신, &lt;b&gt;비동기&lt;/b&gt;로 작동한다는 것!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/166</guid>
      <comments>https://hyeonsi.tistory.com/166#entry166comment</comments>
      <pubDate>Thu, 29 Jun 2023 21:07:33 +0900</pubDate>
    </item>
    <item>
      <title>2023.06.26(Mon.) &amp;lt;UX/UI 아티클 정리&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/165</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;비즈니스 모델 vs 사용자 경험&lt;/h3&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;li&gt;사용자 경험 : 사용자가 생각하는 제품을 이용할때 축적하게 되는 경험&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발자가 생각하는 관점 vs 유저가 생각하는 관점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저에게 어디까지 친절할 것인가? &amp;rarr; 사용하지도 않는 버튼으로 가득찬 TV 리모컨&lt;/li&gt;
&lt;li&gt;&amp;lsquo;개발자 지식의 저주&amp;rsquo; &amp;rarr; 개발자와 똑같이 유저가 모든 환경과 기능을 이해하고 사용할 것이라는 착각. Qa 때도 자주 발생한다.&lt;/li&gt;
&lt;li&gt;통제감 &amp;rarr; 단순하게 만들어야 유저는 제품에 대해 통제감을 느끼고 사용할 의지를 느끼게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디자인도 중요하지만, 만들어지는 방식, 사용법등 제품에 대한 모든 것을 단순화 하는 것이 더욱 도움이 된다.&lt;/li&gt;
&lt;li&gt;조니 아이브 : 본질적이지 않은 부분을 제거하여 그 제품의 본질만을 남기는 것 = 단순화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;좋은 사용자 경험이란? &amp;rarr; 비즈니스 모델을 생각한 (중점이 되는) 유저 사용감의 상승
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 개발자도 비즈니스 모델을 이해하고, 비즈니스 모델을 극대화 하는 사용자 경험을 만들어야 한다. 이것이 단순화가 될 수 도 있고, 오히려 복잡해 질 수 도 있다.&lt;/li&gt;
&lt;li&gt;사용자 경험에 절대적인 명제는 없으며, 비즈니스 모델, 로컬의 문화 등의 모든 부분을 종합하여 고려해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@shaun/136&quot;&gt;내가 사용자 경험을 말할 때&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네이버 활용백서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://campaign.naver.com/naverdetails/newpc/?pcode=naver_pcanniversary&quot;&gt;네이버 PC 메인&lt;/a&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;/li&gt;
&lt;li&gt;텍스트는 양을 줄이고 기능을 강조&lt;/li&gt;
&lt;li&gt;기능확인 탭 &amp;rarr; 네비게이트 버튼 있음 (최상, 하단에서는 사라짐, 컨텐츠를 보고 있을 경우에만 존제)&lt;/li&gt;
&lt;li&gt;개인설정 탭 설명은 동영상으로 대체 &amp;rarr; 너무 많은 정보를 담고 있고, 자신이 관련없는 개인설정일 경우에 확인 할 필요가 없는 컨텐츠이므로&lt;/li&gt;
&lt;li&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;li&gt;단점 : 유저가 보지 않을 경우 말짱 도루묵 &amp;rarr; 그만큼 사용하기 어려운 검색 포털 &amp;rarr; 검색 포털이 이렇게 어려울 필요가 있을까? &amp;rarr; 비즈니스 모델과 관련이 있을 것으로 예상, 주된 비즈니스 모델이 &amp;lsquo;광고&amp;rsquo;인데 네이버 광고는 블로그, 카페 등으로 이루어진 컨텐츠형 광고가 많은 것으로 생각됨. 그래서 그런 광고의 노출도를 올리고 광고 컨텐츠의 생산을 위해 어쩔 수 없는 경우가 아니였을까 생각됨.&lt;/li&gt;
&lt;li&gt;그런거 치고도 꽤 깔끔하고 직관적이라고 생각됨. 아마 기업 이미지나 이때까지 사용하던 사용자 경험이 큰 영향을 주고 있다고 생각됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/165</guid>
      <comments>https://hyeonsi.tistory.com/165#entry165comment</comments>
      <pubDate>Mon, 26 Jun 2023 23:22:05 +0900</pubDate>
    </item>
    <item>
      <title>2023.06.25(Sun.) &amp;lt;React 공용 컴포넌트에 관한 고찰 1 : Button&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/164</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_RpTPvuwQtvgLlbqeeETZMA.webp&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;1298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn3d0x/btslbxwZbXh/q2941BjdhGydWrl4BEMvVK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn3d0x/btslbxwZbXh/q2941BjdhGydWrl4BEMvVK/img.webp&quot; data-alt=&quot;제 코드가 어디선가 유출되고 있나 봅니다. 똑같군요.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn3d0x/btslbxwZbXh/q2941BjdhGydWrl4BEMvVK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn3d0x%2FbtslbxwZbXh%2Fq2941BjdhGydWrl4BEMvVK%2Fimg.webp&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;299&quot; height=&quot;499&quot; data-filename=&quot;1_RpTPvuwQtvgLlbqeeETZMA.webp&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;1298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제 코드가 어디선가 유출되고 있나 봅니다. 똑같군요.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제의 발단&lt;/h3&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;li&gt;스타일도 통일 시키고, 그에 따라 필요한 인자들을 넘겨주어 정형화되고 완벽한 어디에도 사용할 수 있는 컴포넌트를 만드는 과정에서 깨달은 것들을 적어본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Button Size&lt;/h3&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-25 오후 3.29.14.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LixCx/btslndYf0Qt/8GAriIv7a6K2jvYz4Fo6Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LixCx/btslndYf0Qt/8GAriIv7a6K2jvYz4Fo6Dk/img.png&quot; data-alt=&quot;width는 fit, height를 3가지 종류로 나눴다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LixCx/btslndYf0Qt/8GAriIv7a6K2jvYz4Fo6Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLixCx%2FbtslndYf0Qt%2F8GAriIv7a6K2jvYz4Fo6Dk%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;551&quot; height=&quot;249&quot; data-filename=&quot;스크린샷 2023-06-25 오후 3.29.14.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;width는 fit, height를 3가지 종류로 나눴다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687674703357&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Button = styled('button', {
  base: css`
    ${apply`border-1 rounded-[4px] px-4 bg-[#2ecc71] border-2 text-white bg-cyan-500 shadow-lg shadow-cyan-500/50`};
  `,
  variants: {
    size: {
      small: `h-[33px]`,
      medium: `h-[41px]`,
      large: `h-[49px]`,
    },
  },
  defaults: {
    size: 'large',
  },
});

export default function Projects() {
  return (
    &amp;lt;div className={tw`flex justify-center items-center gap-2 mt-[50vh]`}&amp;gt;
      &amp;lt;Button size='small'&amp;gt;Small&amp;lt;/Button&amp;gt;
      &amp;lt;Button size='medium'&amp;gt;Medium&amp;lt;/Button&amp;gt;
      &amp;lt;Button&amp;gt;Default Large&amp;lt;/Button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;li&gt;대부분 2 ~ 3개 정도의 버튼 스타일을 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. outlined&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-25 오후 3.43.00.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vXB7A/btslaGgUHCM/YsM7WnV4ofEP3jSd2m4OI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vXB7A/btslaGgUHCM/YsM7WnV4ofEP3jSd2m4OI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vXB7A/btslaGgUHCM/YsM7WnV4ofEP3jSd2m4OI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvXB7A%2FbtslaGgUHCM%2FYsM7WnV4ofEP3jSd2m4OI1%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;584&quot; height=&quot;320&quot; data-filename=&quot;스크린샷 2023-06-25 오후 3.43.00.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687675422241&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { styled } from '@twind/react';
import { apply, tw } from 'twind';
import { css } from 'twind/css';

const Button = styled('button', {
  base: css`
    ${apply`border-1 rounded-[4px] px-4 bg-[#2ecc71] border-2 text-white bg-cyan-500 shadow-lg shadow-cyan-500/50`};
  `,
  variants: {
    size: {
      small: `h-[33px]`,
      medium: `h-[41px]`,
      large: `h-[49px]`,
    },
    variant: {
      normal: ``,
      outlined: `border-[#2ecc71] text-[#2ecc71] bg-white`,
    },
  },
  defaults: {
    variant: 'normal',
    size: 'large',
  },
});

export default function Projects() {
  return (
    &amp;lt;div className={tw`flex justify-center items-center gap-2 mt-[50vh]`}&amp;gt;
      &amp;lt;Button&amp;gt;Normal&amp;lt;/Button&amp;gt;
      &amp;lt;Button variant='outlined'&amp;gt;Outlined&amp;lt;/Button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;li&gt;기본값을 잘 넘겨줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. hover&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면 기록 2023-06-25 오후 3.53.23.gif&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z6sfO/btslbRPMlbW/Fzc0axu4uzNUFkSvRIumIK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z6sfO/btslbRPMlbW/Fzc0axu4uzNUFkSvRIumIK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z6sfO/btslbRPMlbW/Fzc0axu4uzNUFkSvRIumIK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/z6sfO/btslbRPMlbW/Fzc0axu4uzNUFkSvRIumIK/img.gif&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;458&quot; height=&quot;337&quot; data-filename=&quot;화면 기록 2023-06-25 오후 3.53.23.gif&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687676181561&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { styled } from '@twind/react';
import { apply, tw } from 'twind';
import { css } from 'twind/css';

const Button = styled('button', {
  base: css`
    ${apply`border-1 rounded-[4px] px-4 bg-[#2ecc71] border-2 text-white bg-cyan-500 shadow-lg shadow-cyan-500/50`};
  `,
  variants: {
    size: {
      small: `h-[33px]`,
      medium: `h-[41px]`,
      large: `h-[49px]`,
    },
    variant: {
      normal: ``,
      outlined: `border-[#2ecc71] text-[#2ecc71] bg-white`,
      hover: `transition duration-150 ease-out hover:(ease-in bg-red-500)`,
    },
  },
  defaults: {
    variant: 'normal',
    size: 'large',
  },
});

export default function Projects() {
  return (
    &amp;lt;div className={tw`flex justify-center items-center gap-2 mt-[50vh]`}&amp;gt;
      &amp;lt;Button&amp;gt;Normal&amp;lt;/Button&amp;gt;
      &amp;lt;Button variant='hover'&amp;gt;Hover&amp;lt;/Button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;hover를 넣어주고 똑같이 만들어 준다.&lt;/li&gt;
&lt;li&gt;outline에 hover도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면 기록 2023-06-25 오후 3.59.15.gif&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FosTb/btslaRvZT8t/WvE5lkRg4GztZkDCH6kDkK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FosTb/btslaRvZT8t/WvE5lkRg4GztZkDCH6kDkK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FosTb/btslaRvZT8t/WvE5lkRg4GztZkDCH6kDkK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/FosTb/btslaRvZT8t/WvE5lkRg4GztZkDCH6kDkK/img.gif&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;409&quot; height=&quot;301&quot; data-filename=&quot;화면 기록 2023-06-25 오후 3.59.15.gif&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687676437699&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { styled } from '@twind/react';
import { apply, tw } from 'twind';
import { css } from 'twind/css';

const Button = styled('button', {
  base: css`
    ${apply`border-1 rounded-[4px] px-4 bg-[#2ecc71] border-2 text-white bg-cyan-500 shadow-lg shadow-cyan-500/50`};
  `,
  variants: {
    size: {
      small: `h-[33px]`,
      medium: `h-[41px]`,
      large: `h-[49px]`,
    },
    variant: {
      normal: ``,
      outlined: `border-[#2ecc71] text-[#2ecc71] bg-white`,
    },
    isHover: {
      normal: ``,
      true: `transition duration-150 ease-out hover:(ease-in bg-red-500)`,
    },
  },
  defaults: {
    variant: 'normal',
    size: 'large',
  },
});

export default function Projects() {
  return (
    &amp;lt;div className={tw`flex justify-center items-center gap-2 mt-[50vh]`}&amp;gt;
      &amp;lt;Button&amp;gt;Normal&amp;lt;/Button&amp;gt;
      &amp;lt;Button variant='outlined' isHover&amp;gt;
        Hover
      &amp;lt;/Button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&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;props 종류를 2개로 찢은 다음에 2개 다 넣어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 소감&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생각해 보니 button에 스타일 제외, 다른 속성과 이벤트를 넣을 것이 없다..! (onClick이 전부 아닌가...?)&lt;/li&gt;
&lt;li&gt;이후에 모달, 팝업, warring 등에는 다양한 인자를 props로 넘겨 사용할 수 있을 것이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/164</guid>
      <comments>https://hyeonsi.tistory.com/164#entry164comment</comments>
      <pubDate>Sun, 25 Jun 2023 16:05:40 +0900</pubDate>
    </item>
    <item>
      <title>2023.06.23(Fri.) &amp;lt;노션 주간 업무 보고 템플릿 공유&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/163</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-06-23-19-38-53.jpeg&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H83dL/btsk84WpDWD/KZskLugK14SP2RjnQq6PNK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H83dL/btsk84WpDWD/KZskLugK14SP2RjnQq6PNK/img.jpg&quot; data-alt=&quot;일주일치 노고를 씻겨주는 때껄룩. 보고 힐링 합시다. 의미는 없어요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H83dL/btsk84WpDWD/KZskLugK14SP2RjnQq6PNK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH83dL%2Fbtsk84WpDWD%2FKZskLugK14SP2RjnQq6PNK%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;412&quot; height=&quot;593&quot; data-filename=&quot;KakaoTalk_Photo_2023-06-23-19-38-53.jpeg&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일주일치 노고를 씻겨주는 때껄룩. 보고 힐링 합시다. 의미는 없어요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제의 발단&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;li&gt;그래서 템플릿을 찾으니, 잘 없어서 만들었다. 이거 한 장으로 CTO님께 이쁨 받는 글 잘 쓰는 개발팀, 동료들에게 생색 한 번 더 내는 개발자가 되자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 노션 링크&lt;/h2&gt;
&lt;figure id=&quot;og_1687516990729&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;A new tool for teams &amp;amp; individuals that blends everyday work apps into one.&quot; data-og-host=&quot;outrageous-tiger-d2b.notion.site&quot; data-og-source-url=&quot;https://outrageous-tiger-d2b.notion.site/c95cfabeae8341bd8e7b006e467489a9?v=e227353c9c604897b92df251ff010ee4&amp;amp;pvs=4&quot; data-og-url=&quot;https://outrageous-tiger-d2b.notion.site/c95cfabeae8341bd8e7b006e467489a9&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://outrageous-tiger-d2b.notion.site/c95cfabeae8341bd8e7b006e467489a9?v=e227353c9c604897b92df251ff010ee4&amp;amp;pvs=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://outrageous-tiger-d2b.notion.site/c95cfabeae8341bd8e7b006e467489a9?v=e227353c9c604897b92df251ff010ee4&amp;amp;pvs=4&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;주간 업무&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A new tool for teams &amp;amp; individuals that blends everyday work apps into one.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;outrageous-tiger-d2b.notion.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 유의점&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.44.12.png&quot; data-origin-width=&quot;1303&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnO8EA/btslas9We3b/sZkr4kyH55LJ1JdjtS9keK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnO8EA/btslas9We3b/sZkr4kyH55LJ1JdjtS9keK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnO8EA/btslas9We3b/sZkr4kyH55LJ1JdjtS9keK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnO8EA%2Fbtslas9We3b%2FsZkr4kyH55LJ1JdjtS9keK%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;1303&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.44.12.png&quot; data-origin-width=&quot;1303&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 창에서 새로 만들기를 하면 날짜를 더 늘릴 수 있고, 자동으로 매월 1일이 되면 다음 탭이 생깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.47.19.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5CpsH/btsk9RvvAdT/ox395tKn1yxpnVxGB9BUdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5CpsH/btsk9RvvAdT/ox395tKn1yxpnVxGB9BUdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5CpsH/btsk9RvvAdT/ox395tKn1yxpnVxGB9BUdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5CpsH%2Fbtsk9RvvAdT%2Fox395tKn1yxpnVxGB9BUdK%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;1376&quot; height=&quot;395&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.47.19.png&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.52.57.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MAtCD/btslaIkvzd7/cUUHmPWIN2P7eg6XZ6PWfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MAtCD/btslaIkvzd7/cUUHmPWIN2P7eg6XZ6PWfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MAtCD/btslaIkvzd7/cUUHmPWIN2P7eg6XZ6PWfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMAtCD%2FbtslaIkvzd7%2FcUUHmPWIN2P7eg6XZ6PWfK%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;1440&quot; height=&quot;761&quot; data-filename=&quot;스크린샷 2023-06-23 오후 7.52.57.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;li&gt;아래의 표에서 생성자도 동료의 이름으로 바꿔줍니다.&lt;/li&gt;
&lt;li&gt;귀찮다면, 동료에게 직접 생성하라고 합니다. (기본값이 저 버튼을 누른 사람이기 때문에 + 버튼을 눌러 이름만 추가해 주면 나머지는 자동으로 됩니다.)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ef5369; color: #ffffff;&quot;&gt;&lt;b&gt;위의 편집을 하지 않으면 올바르게 사용할 수 없습니다!&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. 템플릿 복사 하기&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;li&gt;위의 링크를 눌러 주간 업무 노션 페이지로 들어갑니다.&lt;/li&gt;
&lt;li&gt;우측 상단의 '복제' 버튼을 누르면 자동으로 복사 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff; background-color: #ef5369;&quot;&gt;가저가 셔서 편집한 이후, 사용하시면 됩니다! 복제, 복사 등등 마음대로 요리해서 사용하셔도 됩니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff; background-color: #ef5369;&quot;&gt;혹시나, 상업적인 이용은 불가능합니다. (될 것 같지도 않습니다...)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;질문은 댓글로... 고맙다는 인사는 마 고마, 됐심더...!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/163</guid>
      <comments>https://hyeonsi.tistory.com/163#entry163comment</comments>
      <pubDate>Fri, 23 Jun 2023 20:03:08 +0900</pubDate>
    </item>
    <item>
      <title>2023.06.19(Mon.) &amp;lt;React 모바일 웹 환경에서 키패드 처리&amp;gt;</title>
      <link>https://hyeonsi.tistory.com/162</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AL-Kennedy-smashed-keyboa-006.webp&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTAHJh/btskCoGImTR/I365rrKLkXeWkkCyCRUwU0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTAHJh/btskCoGImTR/I365rrKLkXeWkkCyCRUwU0/img.webp&quot; data-alt=&quot;키패드는 죄가 없는걸요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTAHJh/btskCoGImTR/I365rrKLkXeWkkCyCRUwU0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTAHJh%2FbtskCoGImTR%2FI365rrKLkXeWkkCyCRUwU0%2Fimg.webp&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;460&quot; height=&quot;276&quot; data-filename=&quot;AL-Kennedy-smashed-keyboa-006.webp&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;키패드는 죄가 없는걸요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제의 발단&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;chatGPT의 도입으로, 서비스에 대화형 인공지능 기능을 개발했다.&lt;/li&gt;
&lt;li&gt;개발은 그리 어렵지 않았다.(사실 백엔드 처리가 주요 이슈였기 때문에 에러 응답 처리 및 로딩 애니메이션, 유저 인터렉션, 컴포넌트 구현중 레이아웃 흐트러짐등 정도만 하면 되는 일이었다.)&lt;/li&gt;
&lt;li&gt;하지만 생각지도 못 한 복병이 있었다. 모바일 환경에서 대화를 위한 모바일 키패드가 화면을 절반 이상 가려버렸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 환경 세팅&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저, 개발 환경의 PC 상황에서는 키패드를 열 수 조차 없었다.&lt;/li&gt;
&lt;li&gt;하지만 검색결과 Nexus5, Nexus5X 기기에서는 완벽하진 않아도 적당히 열 수 있는 방법이 있다고 했다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-19 오후 9.33.34.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HL9IS/btskEo7g9sZ/5nikc2GgiH2TRwdXK7SGmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HL9IS/btskEo7g9sZ/5nikc2GgiH2TRwdXK7SGmK/img.png&quot; data-alt=&quot;'크기' 옆의 기기 종류를 눌러 추가 시켜주고, 가장 오른쪽의 기기 회전 아이콘에서 키보드로 바꿔주면 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HL9IS/btskEo7g9sZ/5nikc2GgiH2TRwdXK7SGmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHL9IS%2FbtskEo7g9sZ%2F5nikc2GgiH2TRwdXK7SGmK%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;896&quot; height=&quot;112&quot; data-filename=&quot;스크린샷 2023-06-19 오후 9.33.34.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;'크기' 옆의 기기 종류를 눌러 추가 시켜주고, 가장 오른쪽의 기기 회전 아이콘에서 키보드로 바꿔주면 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-19 오후 9.36.06.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LZQmg/btskuMh3e6m/HFU5LSKCKLVxs3VH2cSMRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LZQmg/btskuMh3e6m/HFU5LSKCKLVxs3VH2cSMRk/img.png&quot; data-alt=&quot;이렇게 다 열린다. 물론 보이는 화면은 매우 작다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LZQmg/btskuMh3e6m/HFU5LSKCKLVxs3VH2cSMRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLZQmg%2FbtskuMh3e6m%2FHFU5LSKCKLVxs3VH2cSMRk%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;504&quot; height=&quot;795&quot; data-filename=&quot;스크린샷 2023-06-19 오후 9.36.06.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이렇게 다 열린다. 물론 보이는 화면은 매우 작다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 어떻게 처리 할 것인가?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내용이 들어갈 공간을 window.innerHeight로 잡고 스크롤을 만들어 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1687178785210&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';

const Container = () =&amp;gt; {
	// state로 innerHeight를 잡아 줬다.
	const [innerHeight, setInnerHeight] = useState(0);
    
    // innerHeight가 바뀔경우 다시 계산한다.
    useEffect(() =&amp;gt; {
    	setInnerHeight(window.innerHeight);
  	}, [window.innerHeight]);
	
    // 위 헤더나, 푸터가 있을경우 innerHeight - 여분의 사이즈로 맞출 수 도 있다.
    return (
    	&amp;lt;div className={tw`h-[${window.innerHeight}px]`}&amp;gt;
        	&amp;lt;div className={tw`h-[${innerHeight}px])`}&amp;gt;뭔가 굉장히 길어질 수 있는 컨텐츠&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 여담&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 코드는 예시일 뿐이라 리소스 낭비가 심하다. useEffect에 timeout으로 유저의 동작이 멈췄을 경우만 innerHeight를 계산하는 방식이 옳을 것이다.&lt;/li&gt;
&lt;li&gt;useMemo를 이용하는 방법도 생각해 봤는데, 스크롤은 변화가 심해서 더 낭비일 것이다.&lt;/li&gt;
&lt;li&gt;프로젝트 초기 세팅이 twind라서 그 기반으로 예시 코드를 작성했다. 해석에는 문제가 없을 것이다. (사실 질문 환영)&lt;/li&gt;
&lt;li&gt;확실한 것은 다 구현이후, 올바르게 동작하는 것은 확인했지만, 처음부터 모바일 구성이 기획에 없었어서 급하게 사이즈만 줄여 넣자 라고 아쉽게 구현되었다. 처음에는 상단 헤더 또한 큰 사이즈를 차지해 곤란한 상황. 부랴부랴 맞춰 넣은 감이 없지 않다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Today I Learned</category>
      <author>HyeonSi</author>
      <guid isPermaLink="true">https://hyeonsi.tistory.com/162</guid>
      <comments>https://hyeonsi.tistory.com/162#entry162comment</comments>
      <pubDate>Mon, 19 Jun 2023 21:52:24 +0900</pubDate>
    </item>
  </channel>
</rss>