<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>잡다로그</title>
    <link>https://snowflower19.tistory.com/</link>
    <description>대한민국의 흔한 IT 전공생의 전공 관련 잡다한 지식을 남겨두는 블로그. 잘하기보다 자라고 싶은 기록 여정.</description>
    <language>ko</language>
    <pubDate>Thu, 21 May 2026 02:53:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>날으는다람쥐</managingEditor>
    <image>
      <title>잡다로그</title>
      <url>https://tistory1.daumcdn.net/tistory/4438711/attach/793673df35c145c696439ce2cd0dd40d</url>
      <link>https://snowflower19.tistory.com</link>
    </image>
    <item>
      <title>Tanstack Query - 서버 컴포넌트 데이터 prefetching (SSR)</title>
      <link>https://snowflower19.tistory.com/332</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;서버 컴포넌트에서 API를 호출하게 되면, 초기 로딩 속도 향상, SEO 최적화, &lt;a href=&quot;https://dev.to/richardbray/understanding-and-preventing-fetch-waterfalls-in-react-454k&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;네트워크 워터폴&lt;/a&gt; 방지, 보안 강화, 번들 크기 감소의 이점을 가져갈 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;언제 &lt;b&gt;서&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;버 컴포&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;넌트&lt;/b&gt;에&lt;/span&gt;&lt;span&gt;서 API를&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출해&lt;/span&gt;&lt;span&gt;야 할까&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;페이지&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;초기&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;로드 시 필&lt;/span&gt;&lt;span&gt;요한 데이&lt;/span&gt;&lt;span&gt;터&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;SE&lt;/span&gt;&lt;span&gt;O가&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;중요한&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;콘텐츠&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;사&lt;/span&gt;&lt;span&gt;용자 상호&lt;/span&gt;&lt;span&gt;작용&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;없이 표&lt;/span&gt;&lt;span&gt;시되&lt;/span&gt;&lt;span&gt;는 데&lt;/span&gt;&lt;span&gt;이터&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;인증이&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;필요한 데&lt;/span&gt;&lt;span&gt;이터&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;언&lt;/span&gt;&lt;span&gt;제 &lt;b&gt;클라이&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;언트 컴&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;포넌트&lt;/b&gt;에&lt;/span&gt;&lt;span&gt;서 API를&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출해야&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;할까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;사&lt;/span&gt;&lt;span&gt;용자 상호&lt;/span&gt;&lt;span&gt;작용 후&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;필요한 데&lt;/span&gt;&lt;span&gt;이터&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(클&lt;/span&gt;&lt;span&gt;릭, 검&lt;/span&gt;&lt;span&gt;색 등&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;실&lt;/span&gt;&lt;span&gt;시간으&lt;/span&gt;&lt;span&gt;로 업데이&lt;/span&gt;&lt;span&gt;트되&lt;/span&gt;&lt;span&gt;는 데이터&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;폼 제출&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;후 처&lt;/span&gt;&lt;span&gt;리해야 하&lt;/span&gt;&lt;span&gt;는 데이터&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-indent=&quot;0&quot;&gt;&lt;span&gt;사&lt;/span&gt;&lt;span&gt;용자 세&lt;/span&gt;&lt;span&gt;션에&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;의&lt;/span&gt;&lt;span&gt;존하는 개&lt;/span&gt;&lt;span&gt;인화된 데&lt;/span&gt;&lt;span&gt;이터&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;서버 컴포넌트에서 API를 호출하고, 클라이언트 컴포넌트에게 전달하기 위해서는 &lt;b&gt;dehydration - hydration&lt;/b&gt; 과정이 필요하다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;HydrationBoundary&amp;gt; 컴포넌트기 이 과정에서 핵심적인 역할을 한다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버에서 직렬화된 데이터(dehydratedState)를 받는다.&lt;/li&gt;
&lt;li&gt;이 데이터를 클라이언트의 React Query 캐시로 주입한다.&lt;/li&gt;
&lt;li&gt;클라이언트에서 useQuery가 실행될 때 같은 쿼리 키에 대해 캐시된 데이터를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트&lt;/p&gt;
&lt;pre id=&quot;code_1744607377561&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';

export default async function Page() {
  const queryClient = new QueryClient();

  // 1. 서버에서 데이터를 미리 가져옴
  await queryClient.prefetchQuery({
    queryKey: ['list'],
    queryFn: getHomeList  // api 호출 함수
  });

  // 2. 가져온 데이터를 직렬화하여 클라이언트에 전달할 준비
  const dehydratedState = dehydrate(queryClient);

  // 3. HydrationBoundary를 통해 직렬화된 상태를 클라이언트 컴포넌트에 전달
  return (
    &amp;lt;HydrationBoundary state={dehydratedState}&amp;gt;
      &amp;lt;HomePage /&amp;gt;
    &amp;lt;/HydrationBoundary&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 style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 컴포넌트&lt;/p&gt;
&lt;pre id=&quot;code_1744607718910&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client'

export const HomePage = () =&amp;gt; {
  // 4. 서버에서 미리 가져온 캐시된 데이터를 사용
  // 동일한 queryKey로 useQuery를 호출하면 추가 네트워크 요청 없이 캐시된 데이터를 바로 사용
  const { data: HomeData } = useQuery({
    queryKey: ['list'],
    queryFn: getHomeList
  });
  
  if (!homeData) return &amp;lt;div&amp;gt;로딩 중...&amp;lt;/div&amp;gt;;
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;span&amp;gt; {HomeData.title} 입니다. &amp;lt;/span&amp;gt;
      HomeData.map((data) =&amp;gt; (
        &amp;lt;List key={data.company} info={data} /&amp;gt;
      ))
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/React</category>
      <category>nextjs</category>
      <category>react</category>
      <category>react query</category>
      <category>SEO 최적화</category>
      <category>Server API</category>
      <category>SSR</category>
      <category>tanstack query</category>
      <category>서버컴포넌트 api</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/332</guid>
      <comments>https://snowflower19.tistory.com/332#entry332comment</comments>
      <pubDate>Mon, 14 Apr 2025 14:23:08 +0900</pubDate>
    </item>
    <item>
      <title>Tanstack Query 시작하기 - 데이터 변경하기, useMutation (응답 처리, 변수 전달 등)</title>
      <link>https://snowflower19.tistory.com/331</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Mutations&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리(useQuery)와 달리, Mutation은 &lt;b&gt;데이터를 생성/업데이트/삭제&lt;/b&gt;하거나 서버 사이드 이펙트를 수행하는 데 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* React에서 &lt;a href=&quot;https://velog.io/@_dodo_hee/%ED%95%B8%EB%93%9C%EB%B6%81-%EB%A6%AC%EC%95%A1%ED%8A%B8-Side-Effect&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;사이드 이펙트&lt;/a&gt;란, 함수 내에서 동일한 입력에 대해 같은 결과를 보장할 수 없도록 하는 것이다. 서버에서 API 호출, 쿠키 이용 등 함수 실행 과정에서 &lt;b&gt;외부의 값을 변경(add, update, delete)&lt;/b&gt;하는 것이다. 이 사이드 이펙트를 처리하기 위한 hook이 useEffect이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useMutation&lt;/h4&gt;
&lt;pre id=&quot;code_1732959788715&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mutation = useMutation({
    mutationFn: (newTodo) =&amp;gt; {
      return axios.post('/todos', newTodo)
    },
  })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수인자&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutationFn: default mutation 함수가 정의되지 않은 경우 필수이다. 비동기 작업을 수행하고 Promise 객체를 반환하는 함수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환값은 다음과 같은 값을 포함한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutate: 변수를 전달하여 mutation을 실행하는 함수이며, 추가 콜백 옵션을 연결할 수 있다.&lt;/li&gt;
&lt;li&gt;mutateAsync: mutate와 유사하지만, Promise를 반환하여 await로 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;status&lt;/b&gt;: 현재 mutation의 상태를 나타낸다. idle(실행 전 초기), error, success, pending(실행 중)&lt;/li&gt;
&lt;li&gt;isIdle, isPending, isSuccess, isError: status 값에 따라 파생된 boolean 상태 변수이다. mutation은 언제든 이 중 하나의 상태에만 있을 수 있다.&lt;/li&gt;
&lt;li&gt;data: mutationFn의 반환값. 성공 여부만 반환하면 true와 같은 값, 업데이트된 데이터를 반환하면 바뀐 데이터를 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제: 서버에 새로운 todo 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1733901033524&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) =&amp;gt; {
      return axios.post('/todos', newTodo)
    },
  })

  return (
    &amp;lt;div&amp;gt;
      {mutation.isPending ? (
        'Adding todo...'
      ) : (
        &amp;lt;&amp;gt;
          {mutation.isError ? (
            &amp;lt;div&amp;gt;An error occurred: {mutation.error.message}&amp;lt;/div&amp;gt;
          ) : null}

          {mutation.isSuccess ? &amp;lt;div&amp;gt;Todo added!&amp;lt;/div&amp;gt; : null}

          &amp;lt;button
            onClick={() =&amp;gt; {
              mutation.mutate({ id: new Date(), title: 'Do Laundry' })
            }}
          &amp;gt;
            Create Todo
          &amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제와 같이, &lt;code&gt;mutate&lt;/code&gt; 함수를 호출하며 변수나 객체를 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutations은&amp;nbsp;&lt;code&gt;onSuccess&lt;/code&gt; 옵션, Query Client의 &lt;code&gt;invalidateQueries&lt;/code&gt; 함수와 &lt;code&gt;setQueryData&lt;/code&gt;함수와 함께 사용하면 아주 강력해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* mutation 함수는 비동기 함수이므로, React 16을 포함한 이전 버전에서는 이벤트 콜백에서 바로 사용할 수 없다. onSubmit에서 이벤트에 접근하려면 &lt;b&gt;&lt;code&gt;mutate&lt;/code&gt;를 다른 함수로 감싸&lt;/b&gt;주어야 한다. 이것은 &lt;a href=&quot;https://legacy.reactjs.org/docs/legacy-event-pooling.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React의 event pooling&lt;/a&gt; 때문!&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Mutation 변수 전달&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정보를 추가하거나 수정할 때와 같이 다양한 정보를 파라미터로 전달해야 하는 때가 있다. 그러나 useMutation은 &lt;b&gt;하나의 변수만&lt;/b&gt; 받을 수 있다. 여러 정보를 보내기 위해서는 객체를 사용해서 감싸주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733909429085&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mutation = useMutation({
  mutationFn: (title, body) =&amp;gt; updateTodo(title, body),  //   틀린 문법이고, 동작하지 않음
})
mutation.mutate('hello', 'world')

const mutation = useMutation({
  mutationFn: ({ title, body }) =&amp;gt; updateTodo(title, body),  // ✅ 여러 변수들을 위해 object(객체)를 활용한다.
})
mutation.mutate({ title: 'hello', body: 'world' })&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;동작을 반영한 새로고침&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Query Client의 &lt;code&gt;invalidateQueries&lt;/code&gt;함수와 사용했을 때 강력해진다는 것이 바로 아래와 같이 콜백 함수에서 활용할 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733913398314&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const useUpdateTodo = () =&amp;gt;
  useMutation({
    mutationFn: updateTodo,
    onSuccess: () =&amp;gt; {
      // ✅ 항상 할 일 목록을 무효화 한다.
      queryClient.invalidateQueries({
        queryKey: ['todos', 'list'],
      });
    },
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useQuery를 다루며 보았던 함수이다. '새로고침'의 개념이라고 생각하면 좋다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;변형 후 응답에 접근하기: mutate() 와 mutateAsync()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;useMutation을 사용하기 전에는, try-catch문 내부에서 await 키워드와 함께 API 함수를 호출해주었었다. 리팩토링을 하며 도입 중이기에, 지워도 되나 싶어 검색해보니 try-catch문과 함께 써도 되지만 아무 의미가 없고 대신 &lt;code&gt;mutateAsync&lt;/code&gt;을 &lt;a href=&quot;https://github.com/TanStack/query/discussions/3488&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;활용하라고 한다&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mutate&lt;/code&gt;는 아무것도 반환하지 않지만, &lt;code&gt;mutateAsync&lt;/code&gt;는 성공 시 변형의 결과를 포함하는 프로미스를 반환한다. 그래서 변형 응답에 접근해야 할 때 &lt;code&gt;mutateAsync&lt;/code&gt;를 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733911140454&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mutation = useMutation({ mutationFn: addTodo });
 
try {
  const todo = await mutation.mutateAsync(todo);
  console.log(todo);
} catch (error) {
  console.error(error);
} finally {
  console.log(&quot;done&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 &lt;code&gt;mutate&lt;/code&gt;를 사용해도 콜백을 통해 &lt;b&gt;data나 error에 접근&lt;/b&gt;할 수 있기 때문에 &lt;b&gt;항상 &lt;code&gt;mutate&lt;/code&gt;로 사용하기를 권장한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;muateAsync는 프로미스 제어권을 개발자에게 넘기기 때문에, 수동으로 오류를 잡아야 하고 미처 처리하지 못한 프로미스를 거부받을 수도 있다. 따라서 정말로 프로미스가 필요한 경우(여러 변형을 동시에 발동시키고 모두 완료되기까지 기다리고 싶거나, 콜백 지옥에 빠질 수 있는 종속적인 변형이 있는 경우 등)에 필요할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1733911800356&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const onSubmit = () =&amp;gt; {
  // ✅ onSuccess를 통해 응답에 접근한다.
  myMutation.mutate(someData, {
    onSuccess: (data) =&amp;gt; history.push(data.url),
  });
};

const onSubmit = async () =&amp;gt; {
  //   작동하지만, 에러 처리가 없다.
  const data = await myMutation.mutateAsync(someData);
  history.push(data.url);
};

const onSubmit = async () =&amp;gt; {
  //   문제는 없는 방법이지만 장황하다.
  try {
    const data = await myMutation.mutateAsync(someData);
    history.push(data.url);
  } catch (error) {
    // 아무것도 하지 않는다.
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;challenge&quot; data-emoticon-name=&quot;005&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/challenge/large/005.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/challenge/large/005.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://react-query.kro.kr/docs/guides-and-concepts/mutations&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://react-query.kro.kr/docs/guides-and-concepts/mutations&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733901819574&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;Mutations &amp;ndash; React Query 한글 문서&quot; data-og-description=&quot;Nextra: the next docs builder&quot; data-og-host=&quot;react-query.kro.kr&quot; data-og-source-url=&quot;https://react-query.kro.kr/docs/guides-and-concepts/mutations&quot; data-og-url=&quot;https://react-query.kro.kr/docs/guides-and-concepts/mutations&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://react-query.kro.kr/docs/guides-and-concepts/mutations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-query.kro.kr/docs/guides-and-concepts/mutations&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;Mutations &amp;ndash; React Query 한글 문서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nextra: the next docs builder&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-query.kro.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://velog.io/@hamjw0122/useMutation-1-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@hamjw0122/useMutation-1-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732959807921&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 제대로 사용해보기 (1) useMutation&quot; data-og-description=&quot;useMutation이란?&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@hamjw0122/useMutation-1-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@hamjw0122/useMutation-1-알아보기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMDtUb/hyXDgLxug1/4Vbgg8VsT5FqU5XtD4bJK0/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522,https://scrap.kakaocdn.net/dn/B6bl7/hyXDiJqpKp/wyk2AfvD1R7lwLSZIiZS01/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522,https://scrap.kakaocdn.net/dn/yPCHw/hyXGyKDmds/Nr35lnq2XnWXfDewcBoL4K/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522&quot;&gt;&lt;a href=&quot;https://velog.io/@hamjw0122/useMutation-1-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@hamjw0122/useMutation-1-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMDtUb/hyXDgLxug1/4Vbgg8VsT5FqU5XtD4bJK0/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522,https://scrap.kakaocdn.net/dn/B6bl7/hyXDiJqpKp/wyk2AfvD1R7lwLSZIiZS01/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522,https://scrap.kakaocdn.net/dn/yPCHw/hyXGyKDmds/Nr35lnq2XnWXfDewcBoL4K/img.png?width=1161&amp;amp;height=522&amp;amp;face=0_0_1161_522');&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 제대로 사용해보기 (1) useMutation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;useMutation이란?&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;size14&quot;&gt;&lt;a href=&quot;https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutations-only-take-one-argument-for-variables&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutations-only-take-one-argument-for-variables&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733909439572&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;Mastering Mutations in React Query&quot; data-og-description=&quot;Learn all about the concept of performing side effects on the server with React Query.&quot; data-og-host=&quot;tkdodo.eu&quot; data-og-source-url=&quot;https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutations-only-take-one-argument-for-variables&quot; data-og-url=&quot;https://tkdodo.eu/blog/mastering-mutations-in-react-query&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bP9YzS/hyXKuozdSQ/4qrSM5aC0v9wWZGyUk6kh1/img.png?width=1200&amp;amp;height=630&amp;amp;face=82_500_123_545,https://scrap.kakaocdn.net/dn/rebFy/hyXKyR1ksc/MazrafuQttX6sabv1S0EA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=82_500_123_545,https://scrap.kakaocdn.net/dn/csVt8Z/hyXKm48w3d/ToMPKOm14KrO8KnHCsaOOK/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360&quot;&gt;&lt;a href=&quot;https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutations-only-take-one-argument-for-variables&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutations-only-take-one-argument-for-variables&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bP9YzS/hyXKuozdSQ/4qrSM5aC0v9wWZGyUk6kh1/img.png?width=1200&amp;amp;height=630&amp;amp;face=82_500_123_545,https://scrap.kakaocdn.net/dn/rebFy/hyXKyR1ksc/MazrafuQttX6sabv1S0EA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=82_500_123_545,https://scrap.kakaocdn.net/dn/csVt8Z/hyXKm48w3d/ToMPKOm14KrO8KnHCsaOOK/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360');&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;Mastering Mutations in React Query&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn all about the concept of performing side effects on the server with React Query.&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;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://highjoon-dev.vercel.app/blogs/12-mastering-mutations-in-react-query#mutate-%EB%98%90%EB%8A%94-mutateasync-mutate-or-mutateasync&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://highjoon-dev.vercel.app/blogs/12-mastering-mutations-in-react-query#mutate-%EB%98%90%EB%8A%94-mutateasync-mutate-or-mutateasync&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733911494816&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;highjoon-dev&quot; data-og-description=&quot;highjoon's dev-log&quot; data-og-host=&quot;highjoon-dev.vercel.app&quot; data-og-source-url=&quot;https://highjoon-dev.vercel.app/blogs/12-mastering-mutations-in-react-query#mutate-%EB%98%90%EB%8A%94-mutateasync-mutate-or-mutateasync&quot; data-og-url=&quot;https://highjoon-dev.vercel.app&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jo7Pe/hyXKv8Q57E/KoofTKVewY9XCklmRzSzE1/img.jpg?width=1440&amp;amp;height=1080&amp;amp;face=0_0_1440_1080,https://scrap.kakaocdn.net/dn/bwYE5k/hyXKou8u4k/0lQlxhNJrVnaPBPC6h0fsk/img.jpg?width=1440&amp;amp;height=1080&amp;amp;face=0_0_1440_1080,https://scrap.kakaocdn.net/dn/1BOJa/hyXKpAN2lI/FHKyiQgMGhxNPgNDp2EHcK/img.png?width=1000&amp;amp;height=571&amp;amp;face=0_0_1000_571&quot;&gt;&lt;a href=&quot;https://highjoon-dev.vercel.app/blogs/12-mastering-mutations-in-react-query#mutate-%EB%98%90%EB%8A%94-mutateasync-mutate-or-mutateasync&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://highjoon-dev.vercel.app/blogs/12-mastering-mutations-in-react-query#mutate-%EB%98%90%EB%8A%94-mutateasync-mutate-or-mutateasync&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jo7Pe/hyXKv8Q57E/KoofTKVewY9XCklmRzSzE1/img.jpg?width=1440&amp;amp;height=1080&amp;amp;face=0_0_1440_1080,https://scrap.kakaocdn.net/dn/bwYE5k/hyXKou8u4k/0lQlxhNJrVnaPBPC6h0fsk/img.jpg?width=1440&amp;amp;height=1080&amp;amp;face=0_0_1440_1080,https://scrap.kakaocdn.net/dn/1BOJa/hyXKpAN2lI/FHKyiQgMGhxNPgNDp2EHcK/img.png?width=1000&amp;amp;height=571&amp;amp;face=0_0_1000_571');&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;highjoon-dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;highjoon's dev-log&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;highjoon-dev.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>mutateasync</category>
      <category>react</category>
      <category>react mutation</category>
      <category>react query</category>
      <category>react usemutatation</category>
      <category>react 라이브러리</category>
      <category>tanstack query</category>
      <category>tanstack query mutation</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/331</guid>
      <comments>https://snowflower19.tistory.com/331#entry331comment</comments>
      <pubDate>Wed, 11 Dec 2024 19:40:52 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 15일차 TIL - 프로그래머스 개인정보 수집 유효기간(JS/수학)</title>
      <link>https://snowflower19.tistory.com/329</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[15일차 미들러]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/150370&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/150370&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpUJ3k/btsK2cO3U1k/TgEKIKOjQGWma7b1gEHb61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpUJ3k/btsK2cO3U1k/TgEKIKOjQGWma7b1gEHb61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpUJ3k/btsK2cO3U1k/TgEKIKOjQGWma7b1gEHb61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpUJ3k%2FbtsK2cO3U1k%2FTgEKIKOjQGWma7b1gEHb61%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;1448&quot; height=&quot;852&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1451&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/budnv0/btsK2XjoQjy/pNt8ukKCPIspwMyyuGCC5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/budnv0/btsK2XjoQjy/pNt8ukKCPIspwMyyuGCC5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/budnv0/btsK2XjoQjy/pNt8ukKCPIspwMyyuGCC5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbudnv0%2FbtsK2XjoQjy%2FpNt8ukKCPIspwMyyuGCC5k%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;1451&quot; height=&quot;828&quot; data-origin-width=&quot;1451&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 조건을 따라 한줄한줄 구현하다보면, 날짜 계산 부분에서 급 따져볼 조건이 많아지는 것을 느끼게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12개월 이상이면 연도가 달라지고, 월도 수의 대소만으로 비교할 수 없다. 깔끔한 구현을 위해서는 조건문을 명료하게 작성해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732961719969&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(today, terms, privacies) {
    const [cYear, cMonth, cDay] = today.split(&quot;.&quot;).map((v)=&amp;gt;+v);
    const termsMap = {};
    terms.forEach((v)=&amp;gt;{
        const [term, limit] = v.split(&quot; &quot;);
        termsMap[term] = +limit;
    });
    
    const answer = [];
    
    privacies.forEach((v, i)=&amp;gt;{
        const [date, term] = v.split(&quot; &quot;);
        const [year, month, day] = date.split(&quot;.&quot;).map((v)=&amp;gt;+v);
        
        let limitYear = year + Math.floor(termsMap[term] / 12), 
            limitMonth = month + termsMap[term] % 12, 
            limitDay = day - 1;
        
        if (limitMonth &amp;gt; 12) {
            limitYear += 1;
            limitMonth %= 12;
        } 
        
        if (limitDay === 0) {
            limitDay = 28;
            limitMonth -= 1;
        }
        
        if (cYear &amp;gt; limitYear) {
          answer.push(i + 1);
        } else if (cYear === limitYear &amp;amp;&amp;amp; cMonth &amp;gt; limitMonth) {
          answer.push(i + 1);
        } else if (cYear === limitYear &amp;amp;&amp;amp; cMonth === limitMonth &amp;amp;&amp;amp; cDay &amp;gt; limitDay) {
          answer.push(i + 1);
        }
    })
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;출처: &lt;a href=&quot;https://velog.io/@real-bird/JS-프로그래머스-Lv1-개인정보-수집-유효기간&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@real-bird/JS-프로그래머스-Lv1-개인정보-수집-유효기간&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 순회는 굳이 for문 필요 없이 map이나 forEach 사용하자&lt;/li&gt;
&lt;li&gt;Object보다 Map객체가 순회할 때 빠르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Map은 객체와 다르게 &lt;b&gt;순서가 유지&lt;/b&gt;되고, &lt;b&gt;키 값&lt;/b&gt;이 &lt;b&gt;모든 데이터 타입&lt;/b&gt;을 허용한다.&lt;/li&gt;
&lt;li&gt;Map 객체의 값을 조회하려면 get 메서드를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트 준비</category>
      <category>프로그래머스 JS</category>
      <category>프로그래머스 개인정보수집유효기간</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/329</guid>
      <comments>https://snowflower19.tistory.com/329#entry329comment</comments>
      <pubDate>Sat, 30 Nov 2024 19:15:59 +0900</pubDate>
    </item>
    <item>
      <title>비동기, Promise, async/await (ft. useQuery의 queryFn)</title>
      <link>https://snowflower19.tistory.com/328</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 기본적인 기초적인 useQuery 사용법을 알아보았다.&lt;/p&gt;
&lt;figure id=&quot;og_1732954373036&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;Tanstack Query 시작하기  - 데이터 불러오기, 새로고침 (useQuery, queryClient.invalidateQueries 등)&quot; data-og-description=&quot;Query client공식 문서 : https://tanstack.com/query/latest/docs/reference/QueryClientQuery Client는 캐시와 상호작용하는 데 사용되며, 즉 쿼리 캐시를 제어하는 객체이다.Query Client Provier는 Query Client를 앱에 연결 및 &quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/318&quot; data-og-url=&quot;https://snowflower19.tistory.com/318&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQj5PH/hyXGFwdnef/cAk8DXYPlVKTc1nAKx8eBk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cLprq2/hyXGAaBATn/9gbvCkeZWzDXkwGKXjXu31/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/318&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/318&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQj5PH/hyXGFwdnef/cAk8DXYPlVKTc1nAKx8eBk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cLprq2/hyXGAaBATn/9gbvCkeZWzDXkwGKXjXu31/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;Tanstack Query 시작하기 - 데이터 불러오기, 새로고침 (useQuery, queryClient.invalidateQueries 등)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Query client공식 문서 : https://tanstack.com/query/latest/docs/reference/QueryClientQuery Client는 캐시와 상호작용하는 데 사용되며, 즉 쿼리 캐시를 제어하는 객체이다.Query Client Provier는 Query Client를 앱에 연결 및&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 사용하다보니 문득 궁금했던 async 키워드 여부&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Function은 promise 를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732955063153&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery({ queryKey: ['todos', todoId], queryFn: () =&amp;gt; fetchTodoById(todoId) })
useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () =&amp;gt; {
    const data = await fetchTodoById(todoId)
    return data
  },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위의 예시에서도 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;aysnc&lt;span&gt; 키워드가 붙기도 하고, 안붙기도 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tanstack Query는 내부적으로 queryFn에서&lt;b&gt; 반환된 값은 Promise로 간주&lt;/b&gt;한다. 따라서 비동기이든 아니든 동일하게 처리된다.&amp;nbsp;두 번째 useQuery구문처럼, &lt;b&gt;추가 비동기 작업이나 에러 핸들링&lt;/b&gt;이 필요한 경우 &lt;code&gt;async&lt;/code&gt;를 사용해 가독성 있는 코드를 작성하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비동기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 각 함수와 코드들이 순서대로 동작하는 방식을 동기(Synchronous)라고 부른다. 간단하고 직관적이지만, 작업이 오래 걸리거나 응답이 늦어지는 경우 성능과 UX에 영향을 줄 수 있다.&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;JS에서는 여러 작업을 동시에 처리하기 위해 &lt;b&gt;비동기(Asynchronous)&lt;/b&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;Promise&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 반환할 수 있다. &lt;i&gt;미래의 어떤 시점에 결과를 제공&lt;/i&gt;하겠다는 promise를 반환하는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;let promise = new Promise(function(resolve, reject) {
  // 생략
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promise에 전달되는 함수를 executor(실행자, 실행함수)라고 한다. executor는 자동으로 실행되고, 여기서 원하는 일이 처리되는 것이다. 처리가 끝나면 성공 여부에 따라 resolve나 reject를 호출한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzTSGn/btsK34Pyz4X/DxUKcOEijnXaO8EE7JeSz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzTSGn/btsK34Pyz4X/DxUKcOEijnXaO8EE7JeSz0/img.png&quot; data-alt=&quot;https://ko.javascript.info/promise-basics&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzTSGn/btsK34Pyz4X/DxUKcOEijnXaO8EE7JeSz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzTSGn%2FbtsK34Pyz4X%2FDxUKcOEijnXaO8EE7JeSz0%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;433&quot; height=&quot;208&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ko.javascript.info/promise-basics&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending(대기), fulfilled(이행), rejected(거부) 와 같은 3가지 state를 가진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Async&lt;/h4&gt;
&lt;pre id=&quot;code_1732957689285&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function f() {
  return 1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;async&lt;/code&gt;와 &lt;code&gt;await&lt;/code&gt;를 사용하면 프로미스를 좀 더 편하게 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;async&lt;/code&gt;는 &lt;code&gt;function&lt;/code&gt; 앞에 위치하는 키워드이다. 함수가 &lt;b&gt;항상 Promise를 반환&lt;/b&gt;하게 하며, 프로미스가 아닌 것은 프로미스로 감싸 반환한다. &lt;code&gt;async&lt;/code&gt; 함수 안에서만 &lt;code&gt;await&lt;/code&gt; 키워드를 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Await&lt;/h4&gt;
&lt;pre id=&quot;code_1732957679307&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function f() {

  let promise = new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; resolve(&quot;완료!&quot;), 1000)
  });
  let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)
  alert(result); // &quot;완료!&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;await&lt;/code&gt;는 말그대로 프로미스가 처리될 때까지 함수 실행을 '기다리게'한다. 함수를 호출하고 본문이 실행되는 도중에 &lt;code&gt;await&lt;/code&gt; 키워드가 붙은 곳에 도달하면, 실행이 잠시 중단되었다가 프로미스가 처리된 후 실행이 재개된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;promise가 처리되길 기다리는 동안 엔진이 다른 일을 할 수 있어, CPU 리소스가 낭비되지 않는다.&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;code&gt;await&lt;/code&gt;는 &lt;code&gt;promise.then&lt;/code&gt;보다 좀 더 세련되게 promise의 result 값을 얻을 수 있도록 해주는 문법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://springfall.cc/article/2022-11/easy-promise-async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://springfall.cc/article/2022-11/easy-promise-async-await&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732955704628&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Javascript] 비동기, Promise, async, await 확실하게 이해하기&quot; data-og-description=&quot;초보자 입장에서 헷갈리기 쉬운 자바스크립트의 Promise 에 대해 낱낱이 파헤칩니다. 더 나아가 async와 await을 올바르게 사용하는 법까지 소개합니다.&quot; data-og-host=&quot;springfall.cc&quot; data-og-source-url=&quot;https://springfall.cc/article/2022-11/easy-promise-async-await&quot; data-og-url=&quot;https://springfall.cc/article/2022-11/easy-promise-async-await&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wjzSe/hyXGLiTZ35/dPBE8KXh5cNDNSK8i8h4z1/img.jpg?width=752&amp;amp;height=551&amp;amp;face=0_0_752_551,https://scrap.kakaocdn.net/dn/ce7n0E/hyXGF33u2e/ApmdksPnpEGSg7t49iuUuk/img.jpg?width=752&amp;amp;height=551&amp;amp;face=0_0_752_551,https://scrap.kakaocdn.net/dn/barM3y/hyXGBN6iz2/OJuMP0RJWGVzkQ57WX1F8k/img.png?width=2570&amp;amp;height=2344&amp;amp;face=0_0_2570_2344&quot;&gt;&lt;a href=&quot;https://springfall.cc/article/2022-11/easy-promise-async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://springfall.cc/article/2022-11/easy-promise-async-await&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wjzSe/hyXGLiTZ35/dPBE8KXh5cNDNSK8i8h4z1/img.jpg?width=752&amp;amp;height=551&amp;amp;face=0_0_752_551,https://scrap.kakaocdn.net/dn/ce7n0E/hyXGF33u2e/ApmdksPnpEGSg7t49iuUuk/img.jpg?width=752&amp;amp;height=551&amp;amp;face=0_0_752_551,https://scrap.kakaocdn.net/dn/barM3y/hyXGBN6iz2/OJuMP0RJWGVzkQ57WX1F8k/img.png?width=2570&amp;amp;height=2344&amp;amp;face=0_0_2570_2344');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Javascript] 비동기, Promise, async, await 확실하게 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;초보자 입장에서 헷갈리기 쉬운 자바스크립트의 Promise 에 대해 낱낱이 파헤칩니다. 더 나아가 async와 await을 올바르게 사용하는 법까지 소개합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;springfall.cc&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732955695964&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JavaScript] Promise, executor, state&quot; data-og-description=&quot;Promise 객체는 콜백 지옥을 개선하기 위해 도입되었다. Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&quot; data-og-url=&quot;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hiUQo/hyXC8z2Tvc/oNWoIpNUdCjNTN307bZjek/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246,https://scrap.kakaocdn.net/dn/ld3vN/hyXGHt1mJ4/e95gl59v1FyWVXAtSvyVzK/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246,https://scrap.kakaocdn.net/dn/bfgMSt/hyXGyRoyX3/B6Y5OWlSkHMVKuCjz9p9mk/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246&quot;&gt;&lt;a href=&quot;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@jwp9633/JavaScript-Promise-executor-state&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hiUQo/hyXC8z2Tvc/oNWoIpNUdCjNTN307bZjek/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246,https://scrap.kakaocdn.net/dn/ld3vN/hyXGHt1mJ4/e95gl59v1FyWVXAtSvyVzK/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246,https://scrap.kakaocdn.net/dn/bfgMSt/hyXGyRoyX3/B6Y5OWlSkHMVKuCjz9p9mk/img.png?width=512&amp;amp;height=246&amp;amp;face=0_0_512_246');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JavaScript] Promise, executor, state&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Promise 객체는 콜백 지옥을 개선하기 위해 도입되었다. Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.&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;size14&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.javascript.info/async-await&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732957610138&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;async와 await&quot; data-og-description=&quot;&quot; data-og-host=&quot;ko.javascript.info&quot; data-og-source-url=&quot;https://ko.javascript.info/async-await&quot; data-og-url=&quot;https://ko.javascript.info/async-await&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5Z08p/hyXGK5mJ32/Db8AKLsYTLRDiMG0rKx3ik/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/s9LUS/hyXGzQi4u0/EJVGlVu072SLcTVkUEDBBK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.javascript.info/async-await&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5Z08p/hyXGK5mJ32/Db8AKLsYTLRDiMG0rKx3ik/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/s9LUS/hyXGzQi4u0/EJVGlVu072SLcTVkUEDBBK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&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;async와 await&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;ko.javascript.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732957870115&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;자바스크립트의 동기와 비동기 자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 하나의 작업만 수행할 수 있다. 즉, 이전 작업이 완료되어야 다음 작업을 수행할 수 있게 된다. 우리가 프&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMJuAi/hyXGCM1ysY/Kf2POuerTwbc8Ont2BvbfK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/eGEyz/hyXDdnTvHD/VqYugkzmSeDkyyZIXbLFc0/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/HDdn2/hyXDaLqQZP/KZSI6fN5e5pvkKed6Ry0mK/img.png?width=700&amp;amp;height=508&amp;amp;face=0_0_700_508&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMJuAi/hyXGCM1ysY/Kf2POuerTwbc8Ont2BvbfK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/eGEyz/hyXDdnTvHD/VqYugkzmSeDkyyZIXbLFc0/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/HDdn2/hyXDaLqQZP/KZSI6fN5e5pvkKed6Ry0mK/img.png?width=700&amp;amp;height=508&amp;amp;face=0_0_700_508');&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;자바스크립트의 동기와 비동기 자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 하나의 작업만 수행할 수 있다. 즉, 이전 작업이 완료되어야 다음 작업을 수행할 수 있게 된다. 우리가 프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732958179093&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;Promise - JavaScript | MDN&quot; data-og-description=&quot;Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JBs6r/hyXDktH1K8/gtYq5apIZDWG6jkHKZL7Kk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/mHxsk/hyXDe77Wwr/DGBEYWyIObwUSBFtV8Mwm0/img.png?width=801&amp;amp;height=297&amp;amp;face=0_0_801_297&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JBs6r/hyXDktH1K8/gtYq5apIZDWG6jkHKZL7Kk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/mHxsk/hyXDe77Wwr/DGBEYWyIObwUSBFtV8Mwm0/img.png?width=801&amp;amp;height=297&amp;amp;face=0_0_801_297');&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;Promise - JavaScript | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>js await</category>
      <category>JS 비동기</category>
      <category>react usequery</category>
      <category>tanstack query await</category>
      <category>usequery await</category>
      <category>usequery promise</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/328</guid>
      <comments>https://snowflower19.tistory.com/328#entry328comment</comments>
      <pubDate>Sat, 30 Nov 2024 18:17:31 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 12일차 TIL - 백준 2631 (JS/DP)</title>
      <link>https://snowflower19.tistory.com/325</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[12일차] 미들러&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/2631&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2631&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zxqHu/btsKZ1MjFZU/RlbioEc4sy3l3Ig86jDXC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zxqHu/btsKZ1MjFZU/RlbioEc4sy3l3Ig86jDXC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zxqHu/btsKZ1MjFZU/RlbioEc4sy3l3Ig86jDXC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzxqHu%2FbtsKZ1MjFZU%2FRlbioEc4sy3l3Ig86jDXC0%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;1324&quot; height=&quot;1019&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;1019&quot;/&gt;&lt;/span&gt;&lt;/figure&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;i&gt;제자리로 가야 할 아이들의 수&lt;/i&gt;를 구하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 번호(수열)중 이미 정렬된 번호를 찾기 위해서는 &lt;b&gt;부분 증가 수열&lt;/b&gt;을 찾아야 한다. 이를 찾는 데&lt;b&gt; DP&lt;/b&gt;가 활용된다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ov0ct/btsK0omZz23/bzS2ZXZAF5BFbW0YjV1hMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ov0ct/btsK0omZz23/bzS2ZXZAF5BFbW0YjV1hMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ov0ct/btsK0omZz23/bzS2ZXZAF5BFbW0YjV1hMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fov0ct%2FbtsK0omZz23%2FbzS2ZXZAF5BFbW0YjV1hMk%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;283&quot; height=&quot;482&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 수열에 대해 &lt;i&gt;최장 부분 증가 수열&lt;/i&gt;을 찾아야, 최소로 움직일 아이들의 인원수를 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://chanhuiseok.github.io/posts/algo-49/&quot;&gt;LIS 알고리즘&lt;/a&gt;이라고 하고, 이 수열을 알아내는 데 DP 또는 이분탐색이 쓰인다.&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;u&gt;1. DP 테이블 정의하기&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp[n] = n번째 원소를 마지막으로 하는 증가수열의 길이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;2. 점화식 설정하기&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 원소 : 인덱스 &lt;code&gt;i&lt;/code&gt; &amp;amp; 시작부터 i-1번째까지의 원소 : 인덱스 &lt;code&gt;j&lt;/code&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732724844665&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// j번째원소 &amp;lt; i번째원소 일때,
dp[i] = Math.max(dp[j] + 1, dp[i])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0부터 i까지의 원소를 지나오며, 최대값(최장 길이)를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 길이에서 i번째 원소가 추가된 수열이므로 1을 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;3. 초기값 설정하기&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732724928218&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[0] = 0
dp[1] = 1 // [배열의 첫 번째 원소] -&amp;gt; length: 1&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;pre id=&quot;code_1732725344190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  let n = Number(input[0]);
  let arr = [];
  for (let i = 1; i &amp;lt;= n; i++) {
    arr.push(Number(input[i]));
  }

  let d = Array(n).fill(1); // i 원소부터 시작하는 길이 1인 수열이 존재

  for (let i = 1; i &amp;lt;= n; i++) {
    for (let k = 0; k &amp;lt; i; k++) {
      // k = 0 ~ (i-1)
      if (arr[k] &amp;lt; arr[i]) {
        d[i] = Math.max(d[i], d[k] + 1);
      }
    }
  }

  console.log(n - Math.max(...d));
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DP는 한 문제를 작은 여러 개의 문제로 나눠, 그 값을 저장하여 푸는 알고리즘이다.&lt;br /&gt;1) 문제를 쪼개고 2) 답을 재활용한다.&lt;/li&gt;
&lt;li&gt;DP를 적용하기 위해서는 문제가 1) 겹치는 부분 문제 2) 최적 부분 구조 를 만족해야 한다.&lt;br /&gt;동일한 작은 문제들이 반복적으로 나타나는 경우에 사용 가능하다.&lt;/li&gt;
&lt;li&gt;즉 이중 순회와 같이 반복해야 하는 상황에서 미리 저장한 값으로 연산 횟수를 줄여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://velog.io/@soobin519/Python-%EB%B0%B1%EC%A4%80-2631-%EC%A4%84%EC%84%B8%EC%9A%B0%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@soobin519/Python-%EB%B0%B1%EC%A4%80-2631-%EC%A4%84%EC%84%B8%EC%9A%B0%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732724725978&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;[Python] 백준 2631_ 줄세우기&quot; data-og-description=&quot;https://www.acmicpc.net/problem/2631이 문제는 다이나믹 프로그래밍(DP)을 이용하여 해결할 수 있는 문제이다.(예제)73752614위 예제는 다음과 같이 옮겨 줄 세울 수 있다. 즉, 가장 긴 증가하는 부분수열을 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@soobin519/Python-%EB%B0%B1%EC%A4%80-2631-%EC%A4%84%EC%84%B8%EC%9A%B0%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@soobin519/Python-백준-2631-줄세우기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/61mOu/hyXDcoxuew/KmRKpP2KSrAhh2K4ge6ERk/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152,https://scrap.kakaocdn.net/dn/bumLPJ/hyXC8NcSVb/jW7ykhv6CmgXVeWcdjUCk0/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152,https://scrap.kakaocdn.net/dn/fUb4z/hyXDl6PQLi/03BLvxCh5qVPAca3NObKdK/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152&quot;&gt;&lt;a href=&quot;https://velog.io/@soobin519/Python-%EB%B0%B1%EC%A4%80-2631-%EC%A4%84%EC%84%B8%EC%9A%B0%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@soobin519/Python-%EB%B0%B1%EC%A4%80-2631-%EC%A4%84%EC%84%B8%EC%9A%B0%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/61mOu/hyXDcoxuew/KmRKpP2KSrAhh2K4ge6ERk/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152,https://scrap.kakaocdn.net/dn/bumLPJ/hyXC8NcSVb/jW7ykhv6CmgXVeWcdjUCk0/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152,https://scrap.kakaocdn.net/dn/fUb4z/hyXDl6PQLi/03BLvxCh5qVPAca3NObKdK/img.png?width=1152&amp;amp;height=1152&amp;amp;face=0_0_1152_1152');&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;[Python] 백준 2631_ 줄세우기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://www.acmicpc.net/problem/2631이 문제는 다이나믹 프로그래밍(DP)을 이용하여 해결할 수 있는 문제이다.(예제)73752614위 예제는 다음과 같이 옮겨 줄 세울 수 있다. 즉, 가장 긴 증가하는 부분수열을&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;a href=&quot;https://ddiyeon.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ddiyeon.tistory.com/61&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732724725422&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;[백준 2631번] 줄세우기 (python)&quot; data-og-description=&quot;문제 KOI 어린이집에는 N명의 아이들이 있다. 오늘은 소풍을 가는 날이다. 선생님은 1번부터 N번까지 번호가 적혀있는 번호표를 아이들의 가슴에 붙여주었다. 선생님은 아이들을 효과적으로 보호&quot; data-og-host=&quot;ddiyeon.tistory.com&quot; data-og-source-url=&quot;https://ddiyeon.tistory.com/61&quot; data-og-url=&quot;https://ddiyeon.tistory.com/61&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cQN9m8/hyXGIzjUef/2rpwDMaK2AoI13QamZQOp0/img.png?width=800&amp;amp;height=61&amp;amp;face=0_0_800_61,https://scrap.kakaocdn.net/dn/LITV2/hyXDnp2Kp6/neYFN5IkeddXTGsFH7wBp0/img.png?width=800&amp;amp;height=61&amp;amp;face=0_0_800_61,https://scrap.kakaocdn.net/dn/efmBF6/hyXGDSjvOA/kk5bEIAIPcrrXYif9QRI1k/img.jpg?width=750&amp;amp;height=750&amp;amp;face=371_291_504_436&quot;&gt;&lt;a href=&quot;https://ddiyeon.tistory.com/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ddiyeon.tistory.com/61&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cQN9m8/hyXGIzjUef/2rpwDMaK2AoI13QamZQOp0/img.png?width=800&amp;amp;height=61&amp;amp;face=0_0_800_61,https://scrap.kakaocdn.net/dn/LITV2/hyXDnp2Kp6/neYFN5IkeddXTGsFH7wBp0/img.png?width=800&amp;amp;height=61&amp;amp;face=0_0_800_61,https://scrap.kakaocdn.net/dn/efmBF6/hyXGDSjvOA/kk5bEIAIPcrrXYif9QRI1k/img.jpg?width=750&amp;amp;height=750&amp;amp;face=371_291_504_436');&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;[백준 2631번] 줄세우기 (python)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 KOI 어린이집에는 N명의 아이들이 있다. 오늘은 소풍을 가는 날이다. 선생님은 1번부터 N번까지 번호가 적혀있는 번호표를 아이들의 가슴에 붙여주었다. 선생님은 아이들을 효과적으로 보호&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ddiyeon.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hongjw1938.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hongjw1938.tistory.com/47&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732812253409&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;알고리즘 - Dynamic Programming(동적 계획법)&quot; data-og-description=&quot;1. 개요 DP, 즉 다이나믹 프로그래밍(또는 동적 계획법)은 기본적인 아이디어로 하나의 큰 문제를 여러 개의 작은 문제로 나누어서 그 결과를 저장하여 다시 큰 문제를 해결할 때 사용하는 것으로&quot; data-og-host=&quot;hongjw1938.tistory.com&quot; data-og-source-url=&quot;https://hongjw1938.tistory.com/47&quot; data-og-url=&quot;https://hongjw1938.tistory.com/47&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OpYW3/hyXGF3PsAV/PoTo6MIsQFdGRMvO0mXMY1/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355,https://scrap.kakaocdn.net/dn/bsdKVE/hyXGOmaPA6/aoA1jyLYGNR4KBePIBk4sK/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355,https://scrap.kakaocdn.net/dn/eMQcb1/hyXDdnETSr/sb2iynVpEMaKlGMYKNFPCk/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355&quot;&gt;&lt;a href=&quot;https://hongjw1938.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hongjw1938.tistory.com/47&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OpYW3/hyXGF3PsAV/PoTo6MIsQFdGRMvO0mXMY1/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355,https://scrap.kakaocdn.net/dn/bsdKVE/hyXGOmaPA6/aoA1jyLYGNR4KBePIBk4sK/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355,https://scrap.kakaocdn.net/dn/eMQcb1/hyXDdnETSr/sb2iynVpEMaKlGMYKNFPCk/img.png?width=491&amp;amp;height=355&amp;amp;face=0_0_491_355');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;알고리즘 - Dynamic Programming(동적 계획법)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 개요 DP, 즉 다이나믹 프로그래밍(또는 동적 계획법)은 기본적인 아이디어로 하나의 큰 문제를 여러 개의 작은 문제로 나누어서 그 결과를 저장하여 다시 큰 문제를 해결할 때 사용하는 것으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hongjw1938.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>dp js</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>백준 2631 dp</category>
      <category>백준 2631 js</category>
      <category>코딩테스트 준비</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/325</guid>
      <comments>https://snowflower19.tistory.com/325#entry325comment</comments>
      <pubDate>Thu, 28 Nov 2024 01:36:47 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 11일차 TIL - 백준 9655 (JS/DP)</title>
      <link>https://snowflower19.tistory.com/324</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[11일차 미들러]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/9655&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9655&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPyvyF/btsKTZ2Mnvz/rZK8K0MsfsOXyo3CETZY3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPyvyF/btsKTZ2Mnvz/rZK8K0MsfsOXyo3CETZY3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPyvyF/btsKTZ2Mnvz/rZK8K0MsfsOXyo3CETZY3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPyvyF%2FbtsKTZ2Mnvz%2FrZK8K0MsfsOXyo3CETZY3K%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;1040&quot; height=&quot;542&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 혹은 3 중 어떤 것을 가져도 상관없고, 누군가를 이기도록 설계해야 하는 코드도 아니다. 그래서 더 의아했지만, 문제의 문자 그대로 구현하면 되는 쉬운 그리디 문제라고 생각했다. 이 문제가 그리디에 속하는 이유는, 당장의 최적선택(-1 or -3)을 반복하기 때문.&amp;nbsp;그러나 -1 또는 -3 중&lt;b&gt; 어떤 것을 선택해도 상관이 없어(결과에 영향x)&lt;/b&gt;, 최적의 해를 선택했다고 보기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 유형을 굳이 따지자면 &lt;b&gt;DP&lt;/b&gt;이다. 나는 그렇게 풀지 않았지만, 코드 하단에 간단한 설명을 적어두었다.&lt;/p&gt;
&lt;pre id=&quot;code_1732291784255&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

const turn = (n) =&amp;gt; {
  if (n &amp;gt;= 3) {
    return n - 3;
  } else if (n &amp;gt;= 1) {
    return n - 1;
  } else {
    return n;
  }
};

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  let n = Number(input[0]);

  let ppl = true;       // ture - SK, false - CY
  let cur = turn(n);    // 게임 1회 진행 후

  while (cur &amp;gt; 0) {
    ppl = !ppl;			// 순서 바뀌고 시작
    if (cur == 0) {	   // cur = 현재 남은 돌 갯수
      return;
    } else if (cur &amp;gt; 0) {
      cur = turn(cur);
    }
  }

  if (ppl == true) {
    console.log(&quot;SK&quot;);
  } else {
    console.log(&quot;CY&quot;);
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ppl = !ppl&lt;/code&gt; 과 같이 기존 값을 몰라도 '기존 값의 반대'로 명시하고 싶어서 boolean 값을 사용했다. 시간은 좀 걸렸지만 (168ms) 정답처리 되었다.&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;DP 풀이로 풀 수 있는 문제는 1) 최적 부분 구조 2) 중복이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 최적 부분 구조: 각 턴의 최적은 이전 턴들의 최적에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 중복: 같은 수의 돌에 대한 상황이 반복 발생할 수 있다. (DP table 활용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀함수로 풀 수도 있지만, DP table을 활용하면 기존 계산 값 저장이 가능하니 훨씬 효율적일 것이다.&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;1. DP 테이블 정의하기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[n] = n개의 돌에 대한 게임 과정의 횟수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 점화식 찾기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 턴에서는 1개 또는 3개의 돌만 가져갈 수 있다.&lt;/li&gt;
&lt;li&gt;n개의 돌일 때는 n-1개일 때보다 1번 더 동작했을 것이다.&lt;/li&gt;
&lt;li&gt;그 동작이 곧 1개 혹은 3개를 가져가는 것이므로 점화식은 아래와 같아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732718068353&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[n] = min((dp[n-1]+1), (dp[n-3]+1))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 초기값 지정&lt;/p&gt;
&lt;pre id=&quot;code_1732718522351&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[0] = 0
dp[1] = 'SK'
dp[2] = 'CY'&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 문제를 풀다보면, 돌의 개수(N)이 &lt;b&gt;홀수&lt;/b&gt;인 경우 상근이가 이기고, &lt;b&gt;짝수&lt;/b&gt;인 경우 창용이가 이긴다는 것을 발견할 수도 있다. DP 연습용으로 풀이해보기 좋은 문제인 듯 하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DP = 최적 부분 구조 + 중복(DP table로 해결)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://beginnerdeveloper-lit.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://beginnerdeveloper-lit.tistory.com/83&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1732718538209&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;[C++] 백준 9655번 돌 게임&quot; data-og-description=&quot;1. 문제이해 9655번: 돌 게임 (acmicpc.net) 9655번: 돌 게임 상근이가 게임을 이기면 SK를, 창영이가 게임을 이기면 CY을 출력한다. www.acmicpc.net 탁자 위의 돌 N개가 있고, 상근이와 창영이는 게임을 하려&quot; data-og-host=&quot;beginnerdeveloper-lit.tistory.com&quot; data-og-source-url=&quot;https://beginnerdeveloper-lit.tistory.com/83&quot; data-og-url=&quot;https://beginnerdeveloper-lit.tistory.com/83&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/exoeH/hyXDl6PjXn/3b1vWYLKLQTd94gtzns7H1/img.jpg?width=800&amp;amp;height=388&amp;amp;face=0_0_800_388,https://scrap.kakaocdn.net/dn/bFJAND/hyXGClyv2i/1Ilmi7rtW1AOKJ2RtJMNqK/img.jpg?width=800&amp;amp;height=388&amp;amp;face=0_0_800_388,https://scrap.kakaocdn.net/dn/bycYDh/hyXDdufapz/t5dgrkeb39HmhwjtXdMCOK/img.jpg?width=1465&amp;amp;height=712&amp;amp;face=0_0_1465_712&quot;&gt;&lt;a href=&quot;https://beginnerdeveloper-lit.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://beginnerdeveloper-lit.tistory.com/83&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/exoeH/hyXDl6PjXn/3b1vWYLKLQTd94gtzns7H1/img.jpg?width=800&amp;amp;height=388&amp;amp;face=0_0_800_388,https://scrap.kakaocdn.net/dn/bFJAND/hyXGClyv2i/1Ilmi7rtW1AOKJ2RtJMNqK/img.jpg?width=800&amp;amp;height=388&amp;amp;face=0_0_800_388,https://scrap.kakaocdn.net/dn/bycYDh/hyXDdufapz/t5dgrkeb39HmhwjtXdMCOK/img.jpg?width=1465&amp;amp;height=712&amp;amp;face=0_0_1465_712');&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;[C++] 백준 9655번 돌 게임&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 문제이해 9655번: 돌 게임 (acmicpc.net) 9655번: 돌 게임 상근이가 게임을 이기면 SK를, 창영이가 게임을 이기면 CY을 출력한다. www.acmicpc.net 탁자 위의 돌 N개가 있고, 상근이와 창영이는 게임을 하려&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;beginnerdeveloper-lit.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>DP</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>백준 9655 dp</category>
      <category>백준 9655 js</category>
      <category>오블완</category>
      <category>코딩테스트 준비</category>
      <category>티스토리챌린지</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/324</guid>
      <comments>https://snowflower19.tistory.com/324#entry324comment</comments>
      <pubDate>Sat, 23 Nov 2024 01:31:28 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 9일차 TIL - Greedy 알고리즘, 백준 2212 (JS)</title>
      <link>https://snowflower19.tistory.com/322</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그리디 알고리즘 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;by. 99클럽장 김종범님&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징이 많지 않은 알고리즘으로, 오히려 그리디인 것을 모르고 풀었을 때 잘 푸는 경우가 있는 아이러니한 유형 (?). 알고리즘 유형이라기보다 방법론에 가깝다. 그래서 DP, 이분탐색 등 구현법은 다양해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'이 문제는 그리디로 풀어야지' 보다 풀다보니 '어 그리디네?' 하는 문제가 많다. 다른 방식들과 많이 결합되어 쓰인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 단계에서 현재 상황에서 최선이라고 생각되는 선택을 하는 방식&lt;/li&gt;
&lt;li&gt;단, 전체의 최적해를 보장하지는 않는다.&lt;/li&gt;
&lt;li&gt;시간 복잡도는 O(n) ~ O(nlogn)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 실행 시간 : 일반적으로 한 번의 탐색으로 답을 구할 수 있다.&lt;/li&gt;
&lt;li&gt;최적화 문제에 적합 : ex. 최단경로, 배낭문제 등&lt;/li&gt;
&lt;li&gt;메모리 효율성 : 별도의 메모리를 요구하지 않음 (매순간 최적 판단)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최적해를 보장하지 않다.&lt;/li&gt;
&lt;li&gt;조건이 많아질 경우 부적합 : 1 ~ 2개의 조건으로 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 그리디 알고리즘 문&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;최단 경로 문제 : (ex.다익스트라 알고리즘) 시작노드로부터 다른 모든 노드까지의 최단 경로를 구하는 문제
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 단계에서 현재까지 가장 짧은 경로를 가진 노드를 선택&lt;/b&gt;하고, 이를 기준으로 다른 경로를 갱신하며 해결&lt;/li&gt;
&lt;/ul&gt;
&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;b&gt;물건의 가치 대비 무게가 가장 높은 순&lt;/b&gt;으로 아이템을 선택하여 최대 가치를 구하는 방식으로 해결&lt;/li&gt;
&lt;/ul&gt;
&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;b&gt;시작 시간을 기준으로 회의를 처리&lt;/b&gt;하며 최소 회의실 수를 계산&lt;/li&gt;
&lt;/ul&gt;
&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;b&gt;최소한의 간선부터 선택하면서 사이클이 발생하지 않도록 연결&lt;/b&gt;하는 방식으로 해결&lt;/li&gt;
&lt;/ul&gt;
&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;b&gt;가장 가까운 노드를 선택&lt;/b&gt;하여 트리를 확장하는 방식&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&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;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래서 언제 써야 할까?&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재의 선택이 최적의 해&lt;/li&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;/b&gt;할 때 : 정렬이 되어있지 않으면, 모든 경우를 탐색해야 최적해를 찾을 수 있게 되기 때문&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[9일차 미들러]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/2212&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2212&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZfwL4/btsKI5Ck5wD/PqNIOm85e0JmpuWbYOujW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZfwL4/btsKI5Ck5wD/PqNIOm85e0JmpuWbYOujW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZfwL4/btsKI5Ck5wD/PqNIOm85e0JmpuWbYOujW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZfwL4%2FbtsKI5Ck5wD%2FPqNIOm85e0JmpuWbYOujW1%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;1307&quot; height=&quot;823&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVHnBt/btsKJw0HKjy/Jc4H6YyqvWfNhVkT1Wy2vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVHnBt/btsKJw0HKjy/Jc4H6YyqvWfNhVkT1Wy2vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVHnBt/btsKJw0HKjy/Jc4H6YyqvWfNhVkT1Wy2vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVHnBt%2FbtsKJw0HKjy%2FJc4H6YyqvWfNhVkT1Wy2vK%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;1297&quot; height=&quot;222&quot; data-origin-width=&quot;1297&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 예제의 출력값에 대한 설명이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;한 집중국이 [1, 3] 구간을 수신합니다. 영역의 길이는 2입니다.&lt;br /&gt;다른 집중국이 [6, 9] 구간을 수신합니다. 영역의 길이는 3입니다. 둘을 합하면 5가 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;출처:&amp;nbsp;&lt;a href=&quot;https://www.acmicpc.net/board/view/22142&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/board/view/22142&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;다양한 방법들이 있지만, &lt;a href=&quot;https://edder773.tistory.com/252&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;에서 정리한 코드가 참 깔끔하고 직관적이어서 참고해 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K개의 집중국을 세워, K개의 (연결된) 구간을 만들어야 한다.&amp;nbsp;센서들의 위치를 정렬한 뒤, 각 센서가 그 다음 센서와 얼마나 떨어져있는지 위치 차이를 계산한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731942720334&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const s = input[2]
    .split(&quot; &quot;)
    .map(Number)
    .sort((a, b) =&amp;gt; a - b);

  let diff = [];
  for (let i = 0; i &amp;lt; n - 1; i++) {
    diff.push(s[i + 1] - s[i]);
  }&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_1731942832922&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let ans = diff.slice(0, n - k).reduce((sum, value) =&amp;gt; sum + value, 0); // 0부터 n-k까지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빼놓기 위해 [0:n-k] 구간에 있는 값을 저장해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1731942699983&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const n = Number(input[0]);
  const k = Number(input[1]);
  const s = input[2]
    .split(&quot; &quot;)
    .map(Number)
    .sort((a, b) =&amp;gt; a - b);

  let diff = [];
  for (let i = 0; i &amp;lt; n - 1; i++) {
    diff.push(s[i + 1] - s[i]);
  }

  diff.sort((a, b) =&amp;gt; a - b);
  let ans = diff.slice(0, n - k).reduce((sum, value) =&amp;gt; sum + value, 0); // 0부터 n-k까지

  console.log(ans);
});&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>그리디 js</category>
      <category>백준 2212</category>
      <category>백준 2212 문제 설명</category>
      <category>오블완</category>
      <category>코딩테스트 준비</category>
      <category>티스토리챌린지</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/322</guid>
      <comments>https://snowflower19.tistory.com/322#entry322comment</comments>
      <pubDate>Tue, 19 Nov 2024 00:14:46 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 8일차 TIL - 프로그래머스 모의고사 (JS/완전탐색)</title>
      <link>https://snowflower19.tistory.com/321</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[8일차 미들러]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42840&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42840&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XFFMO/btsKKQMIcDk/2DkIyjybsELWnMktXQO0Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XFFMO/btsKKQMIcDk/2DkIyjybsELWnMktXQO0Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XFFMO/btsKKQMIcDk/2DkIyjybsELWnMktXQO0Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXFFMO%2FbtsKKQMIcDk%2F2DkIyjybsELWnMktXQO0Hk%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;514&quot; height=&quot;427&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;590&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;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴이 정해져 있고, 단순히 정답과 비교하여 채점하면 되는 문제이다. 이후 점수를 가장 많이 얻은 사람들)을 출력하면 된다. 이러한 문제는 직관적으로 모든 경우를 실행해본 뒤 정답을 출력할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 알고리즘에서는 &quot;&lt;b&gt;완전 탐색&lt;/b&gt;&quot;이라고 부른다.&lt;/p&gt;
&lt;pre id=&quot;code_1731744869982&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(answers) {
  const a = [1, 2, 3, 4, 5];
  const b = [2, 1, 2, 3, 2, 4, 2, 5];
  const c = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5];

  const score = [0, 0, 0];

  for (let i = 0; i &amp;lt; answers.length; i++) {
    if (answers[i] == a[i % 5]) score[0]++;
    if (answers[i] == b[i % 8]) score[1]++;
    if (answers[i] == c[i % 10]) score[2]++;
  }
    
    const max = Math.max(...score);

    let ans = [];
  for (let i = 0; i &amp;lt; score.length; i++){
      if (max == score[i]) ans.push(i+1);
  }
    
    return ans;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;배열 최대값 찾기 Math.max(...배열);&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>js 완전탐색</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>오블완</category>
      <category>코딩테스트 준비</category>
      <category>티스토리챌린지</category>
      <category>프로그래머스 모의고사 js</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/321</guid>
      <comments>https://snowflower19.tistory.com/321#entry321comment</comments>
      <pubDate>Sat, 16 Nov 2024 17:35:25 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] useCallback - useMemo와의 연관성, 4가지 사용법</title>
      <link>https://snowflower19.tistory.com/320</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;본 포스팅은 공식 문서를 번역 및 외부 포스팅 참고 후 정리한 글입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; useCallback&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useCallback은 리렌더링 간에 함수 정의를 캐싱해주는 훅이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 핸들러 함수가 자주 재생성되는 경우, 하위 컴포넌트에 props로 전달되는 함수가 자주 재생성되는 경우, 렌더링 최적화가 필요한 경우 등에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useCallback은 성능 최적화를 위한 용도로만 사용해야 한다&lt;/b&gt;. 만약 코드가 useCallback없이 작동하지 않는다면, 먼저 근본적인 문제를 해결해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1731485614359&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const cachedFn = useCallback(fn, dependencies)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fn : &lt;b&gt;캐싱할 함숫값&lt;/b&gt;. 첫 렌더링에서 이 함수를 반환한다. 다음 렌더링에서 dependencies이 변경되었다면 이번 렌더링에서 전달한 함수를 반환하고 나중에 재사용할 수 있도록 이를 저장한다.&lt;/li&gt;
&lt;li&gt;dependencies : fn 내에서 &lt;b&gt;참조되는 모든 반응형 값&lt;/b&gt;(props, state, 컴포넌트 안에서 선언된 모든 변수와 함수)의 목록. 항목 수가 일정해야 하며, 인라인으로 작성해야 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ useCallback은 컴포넌트 최상위 레벨 또는 자체 훅에서만 호출할 수 있고, 반복문이나 조건문 내부에서 호출할 수 없다. 필요하다면 새 컴포넌트로 분리해서 state를 새 컴포넌트로 옮겨야 한다. (* 모든 Hook의 주의사항)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useMemo와의 연관성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리렌더링 방지와 성능 최적화 키워드를 들으면, useMemo가 떠오를 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo는 useCallback과 함께 자주 쓰인다. 두 hook은 모두 무언가 전달할 때 memoixation(캐싱)할 수 있게 해주어 자식 컴포넌트를 최적화할 때 유용하다. 차이점은 &lt;i&gt;무엇을 캐싱&lt;/i&gt;하는지 이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useMemo : 호출한 함수의 &lt;b&gt;결과&lt;/b&gt;값 캐싱 &amp;rarr; 즉, &lt;b&gt;연산값&lt;/b&gt; 재사용&lt;/li&gt;
&lt;li&gt;useCallback : 함수 &lt;b&gt;자체&lt;/b&gt;를 캐싱 &amp;rarr; 즉, &lt;b&gt;함수&lt;/b&gt; 재사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 useMemo의 개념이 익숙하다면, 아래와 같이 이해하는 것이 도움이 될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731486766307&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// (React 내부의) 간단한 구현
function useCallback(fn, dependencies) {
  return useMemo(() =&amp;gt; fn, dependencies);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useCallback이 유용한 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통의 사이트는 memoization이 필요하지 않다. 그러나 앱이 도형을 이동하는 것 같이 &lt;i&gt;미세한 상호작용을 하는&lt;/i&gt; 그림 편집기 같은 경우, memozation이 유용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useCallback으로 함수를 캐싱하는 것은 다음과 같은 경우에 유용하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;memo로 감싸진 컴포넌트에 prop으로 넘길 때, 넘긴 함수가 나중에 &lt;b&gt;어떤 Hook의 의존성으로 사용될 때&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;예를 들어, useCallback으로 감싸진 다른 함수가 이 함수에 의존하거나, useEffect에서 이 함수에 의존할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 원칙을 따르면 굳이 memoization을 사용하지 않고, 가독성을 지키고 특정 상호작용을 가속할 수도 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컴포넌트가 다른 컴포넌트를 시각적으로 감싸고 있을 때 JSX을 자식으로 받게 한다.&lt;/li&gt;
&lt;li&gt;가능한 한 로컬 상태를 선호하고, 컴포넌트 간 상태 공유를 과도하게 하지 않는다. (ex. 폼이나 항목이 hover되었는지)&lt;/li&gt;
&lt;li&gt;렌더링 로직을 순수하게 유지한다.&lt;/li&gt;
&lt;li&gt;상태를 업데이트하는 불필요한 Effects를피한다.&lt;/li&gt;
&lt;li&gt;Effects에서 불필요한 의존성을 제거한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useCallback 사용법1: 컴포넌트의 리렌더링 건너뛰기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 리렌더링될 때 모든 자식도 재귀적으로 재렌더링된다. 따라서 리렌더링에 많은 계산이 요구되면 컴포넌트 최적화가 필요해진다.&lt;/p&gt;
&lt;pre id=&quot;code_1731485988274&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useCallback } from 'react';

function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) =&amp;gt; {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 useCallback 훅으로 감싸서 자식 컴포넌트에 넘기는 함수를 캐싱할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useCallback에게 1) 리렌더링 간에 캐싱할 &lt;b&gt;함수&lt;/b&gt; 정의, 2) 함수에서 사용되는 컴포넌트 내부의 모든 값을 포함하고 있는 &lt;b&gt;의존성&lt;/b&gt; &lt;b&gt;목록&lt;/b&gt; 을 전달해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;code&gt;handleSubmit&lt;/code&gt; 함수를 ProductPage(부모)에서 ShippingForm(자식) 컴포넌트로 전달한다고 가정할 때,&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731486135685&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ProductPage({ productId, referrer, theme }) {
  // ...
  return (
    &amp;lt;div className={theme}&amp;gt;
      &amp;lt;ShippingForm onSubmit={handleSubmit} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ProductPage가 다른 theme(prop)값으로 리렌더링할 때, ShippingForm 컴포넌트도 같이 리렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 아래 코드와 같이 ShippingForm을 memo로 감싸면, 마지막 렌더링과 동일한 props(여기서, &lt;code&gt;handleSubmit()&lt;/code&gt;)일 때 리렌더링을 건너뛸 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731486326347&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { memo } from 'react';

const ShippingForm = memo(function ShippingForm({ onSubmit }) {
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, useCallback 없이 &lt;code&gt;handleSubmit&lt;/code&gt;을 정의한다면, 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1731486368392&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ProductPage({ productId, referrer, theme }) {
  // theme이 바뀔때마다 다른 함수가 될 것...
  function handleSubmit(orderDetails) {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }
  
  return (
    &amp;lt;div className={theme}&amp;gt;
      {/* ... 그래서 ShippingForm의 props는 같은 값이 아니므로 매번 리렌더링 할 것임.*/}
      &amp;lt;ShippingForm onSubmit={handleSubmit} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자바스크립트에서 &lt;code&gt;function () {}&lt;/code&gt; 나 &lt;code&gt;() =&amp;gt; {}&lt;/code&gt;은 항상 다른 함수를 생성한다.&lt;/b&gt; 따라서 SippingForm의 props는 절대 같아질 수 없고 &lt;i&gt;memo 최적화는 동작하지 않을 것&lt;/i&gt;이다.&lt;/p&gt;
&lt;pre id=&quot;code_1731486533877&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ProductPage({ productId, referrer, theme }) {
  // React에게 리렌더링 간에 함수를 캐싱하도록 요청한다...
  const handleSubmit = useCallback((orderDetails) =&amp;gt; {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]); // ...이 의존성이 변경되지 않는 한...

  return (
    &amp;lt;div className={theme}&amp;gt;
      {/* ...ShippingForm은 같은 props를 받게 되고 리렌더링을 건너뛸 수 있다.*/}
      &amp;lt;ShippingForm onSubmit={handleSubmit} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &amp;nbsp;&lt;code&gt;handleSubmit&lt;/code&gt;을 useCallback으로 감싸서 리렌더링 같에 같&lt;b&gt;은 함수라는 것을 보장&lt;/b&gt;해줄 수 있다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useCallback 사용법2:&lt;span&gt;&amp;nbsp; Memoized 콜백에서 상태 업데이트하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;memoized 콜백에서 이전 상태를 기반으로 상태를 업데이트해야 할 때가 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1731487332081&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TodoList() {
  const [todos, setTodos] = useState([]);

  const handleAddTodo = useCallback((text) =&amp;gt; {
    const newTodo = { id: nextId++, text };
    setTodos(todos =&amp;gt; [...todos, newTodo]);
  }, []); // ✅ todos 의존성은 필요하지 않습니다.
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;code&gt;handleAddTodo&lt;/code&gt;함수는 todo로부터 다음 할 일을 계산하여 이를 의존성에 명시하려고 했지만, 다음 상태 계산을 위해 어떤 상태를 읽는 경우는 &lt;b&gt;업데이트 함수&lt;/b&gt;를 대신 넘겨주는 것이 좋다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useCallback 사용법3: &lt;span&gt;&lt;/span&gt;Effect가 너무 자주 실행되는 것을 방지하기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Effect 안에서 함수를 호출해야 할 때, 모든 반응형 값이 의존성으로 선언되어야 한다. 그러나 어떤 의존성은 매 렌더링마다 변경되곤 한다. 예를 들어, 아래와 같이 &lt;code&gt;createOptions&lt;/code&gt;를 의존성으로 선언하면 Effect가 채팅방과 계속 재연결되는 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1731488580375&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; useEffect(() =&amp;gt; {
    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () =&amp;gt; connection.disconnect();
  }, [createOptions]); //   문제점: 이 의존성은 매 렌더링마다 변경된다.
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해, Effect에서 호출하려는 함수를 useCallback으로 감쌀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731488680451&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const createOptions = useCallback(() =&amp;gt; {
    return {
      serverUrl: 'https://localhost:1234',
      roomId: roomId
    };
  }, [roomId]);  // ✅ roomId가 변경될 때만 변경됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 roomId가 같다면, createOptions 함수는 같다는 것을 보장한다. 그러나, 함수를 Effect 안으로 이동시켜&lt;b&gt; 함수 의존성을 제거하는 것이 더 좋다&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1731488760754&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() =&amp;gt; {
    function createOptions() { // ✅ useCallback이나 함수 의존성이 필요하지 않다.
      return {
        serverUrl: 'https://localhost:1234',
        roomId: roomId
      };
    }

    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () =&amp;gt; connection.disconnect();
  }, [roomId]); // ✅ roomId가 변경될 때만 변경된다.
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useCallback 사용법4: &lt;span&gt;&lt;/span&gt;커스텀 Hook 최적화하기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;커스텀 Hook을 작성하는 경우, 반환하는 모든 함수를 useCallback으로 감싸는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1731488888602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function useRouter() {
  const { dispatch } = useContext(RouterStateContext);

  const navigate = useCallback((url) =&amp;gt; {
    dispatch({ type: 'navigate', url });
  }, [dispatch]);

  const goBack = useCallback(() =&amp;gt; {
    dispatch({ type: 'back' });
  }, [dispatch]);

  return {
    navigate,
    goBack,
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;이렇게 하면 Hook을 사용하는 컴포넌트가 필요할 때 가지고 있는 코드를 최적화할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useCallback#usage&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/reference/react/useCallback&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731486867988&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;useCallback &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/reference/react/useCallback#usage&quot; data-og-url=&quot;https://ko.react.dev/reference/react/useCallback&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JCBP0/hyXzIF9x2y/ZaJNi72GzcCBZZKtvCMKi0/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/IRMTe/hyXzRb1IHc/tW17busmXVMvxUzki4kDKK/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useCallback#usage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/reference/react/useCallback#usage&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JCBP0/hyXzIF9x2y/ZaJNi72GzcCBZZKtvCMKi0/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/IRMTe/hyXzRb1IHc/tW17busmXVMvxUzki4kDKK/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;useCallback &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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react.vlpt.us/basic/18-useCallback.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://react.vlpt.us/basic/18-useCallback.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731486868794&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;18. useCallback 를 사용하여 함수 재사용하기 &amp;middot; GitBook&quot; data-og-description=&quot;No results matching &amp;quot;&amp;quot;&quot; data-og-host=&quot;react.vlpt.us&quot; data-og-source-url=&quot;https://react.vlpt.us/basic/18-useCallback.html&quot; data-og-url=&quot;https://react.vlpt.us/basic/18-useCallback.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bBSZau/hyXzWj6nE2/keXGesoGBjxpGnOIN94oak/img.png?width=807&amp;amp;height=790&amp;amp;face=0_0_807_790&quot;&gt;&lt;a href=&quot;https://react.vlpt.us/basic/18-useCallback.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react.vlpt.us/basic/18-useCallback.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bBSZau/hyXzWj6nE2/keXGesoGBjxpGnOIN94oak/img.png?width=807&amp;amp;height=790&amp;amp;face=0_0_807_790');&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;18. useCallback 를 사용하여 함수 재사용하기 &amp;middot; GitBook&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;No results matching &quot;&quot;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react.vlpt.us&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>React useCallback</category>
      <category>react usecallback uesmemo</category>
      <category>React useMemo</category>
      <category>react 함수 캐싱</category>
      <category>usecallback usememo 차이</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/320</guid>
      <comments>https://snowflower19.tistory.com/320#entry320comment</comments>
      <pubDate>Wed, 13 Nov 2024 18:09:21 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 7일차 TIL - 백준 2847 (JS/Greedy)</title>
      <link>https://snowflower19.tistory.com/319</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[7일차 미들러]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/2847&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2847&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciDAic/btsKGBVC8zv/R5f8HRSkQEB3puAjX9yCUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciDAic/btsKGBVC8zv/R5f8HRSkQEB3puAjX9yCUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciDAic/btsKGBVC8zv/R5f8HRSkQEB3puAjX9yCUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciDAic%2FbtsKGBVC8zv%2FR5f8HRSkQEB3puAjX9yCUK%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;1057&quot; height=&quot;635&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;낮은 레벨 (ex. i단계)의 점수는 언제나 높은 레벨 (직후 단계 ex. i+1단계)보다 작아야 한다. 점수 내리기를 최소한으로 하기 위해서는 1만큼만 작도록 내리면 된다. 이 개념을 구현하기만 하면 풀이는 끝난다.&lt;/p&gt;
&lt;pre id=&quot;code_1731431210040&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  let n = Number(input[0]);
  let arr = Array(n).fill(0);
  let ans = 0;

  for (let i = 1; i &amp;lt;= n; i++) {
    arr[i - 1] = Number(input[i]);
  }

  for (let i = arr.length - 1; i &amp;gt; 0; i--) {
    if (arr[i] &amp;lt;= arr[i - 1]) {
      ans = ans + (arr[i - 1] - arr[i] + 1);
      arr[i - 1] = arr[i] - 1;
    }
  }

  console.log(ans);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이는 사실 떠오르는 대로 풀었지만, 알고리즘 분류는 &quot;그리디&quot;로 되어 있다.&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;앞으로의 선택에 영향을 미치지 않는&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;이 문제에서도, 전체적으로 최소 ans를 찾기 보다, 배열을 순회하며 현재 인덱스에서 가장 좋은 선택(각 단계에서 이전 요소가 현재 요소보다 작아지도록 조정)을 한 것이다.&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;figure id=&quot;og_1731571800934&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;[Pyhton/코테] 그리디(Greedy) 알고리즘&quot; data-og-description=&quot;그리디 알고리즘 : 현재 상황에서 지금 당장 좋은 것만 고르는 방법. ex) 루트부터 시작하여 거쳐 가는 노드 값의 합을 최대로 만들고 싶다. A. 매 상황에서 가장 큰 값 고르기 위의 경우에서도 확&quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/124&quot; data-og-url=&quot;https://snowflower19.tistory.com/124&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/w2tKJ/hyXwmkFOJp/7pKpKfuhaNdQkBshCK9Yd0/img.png?width=744&amp;amp;height=373&amp;amp;face=0_0_744_373,https://scrap.kakaocdn.net/dn/ygwOi/hyXwuwcDTC/GJ0FlYl8g4mhrqHaYkG7w0/img.png?width=744&amp;amp;height=373&amp;amp;face=0_0_744_373,https://scrap.kakaocdn.net/dn/bpV0Uc/hyXzKD8QSk/QHPB2wSCKZnCR6AHmwiLYk/img.png?width=1165&amp;amp;height=538&amp;amp;face=0_0_1165_538&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/124&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/w2tKJ/hyXwmkFOJp/7pKpKfuhaNdQkBshCK9Yd0/img.png?width=744&amp;amp;height=373&amp;amp;face=0_0_744_373,https://scrap.kakaocdn.net/dn/ygwOi/hyXwuwcDTC/GJ0FlYl8g4mhrqHaYkG7w0/img.png?width=744&amp;amp;height=373&amp;amp;face=0_0_744_373,https://scrap.kakaocdn.net/dn/bpV0Uc/hyXzKD8QSk/QHPB2wSCKZnCR6AHmwiLYk/img.png?width=1165&amp;amp;height=538&amp;amp;face=0_0_1165_538');&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;[Pyhton/코테] 그리디(Greedy) 알고리즘&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;그리디 알고리즘 : 현재 상황에서 지금 당장 좋은 것만 고르는 방법. ex) 루트부터 시작하여 거쳐 가는 노드 값의 합을 최대로 만들고 싶다. A. 매 상황에서 가장 큰 값 고르기 위의 경우에서도 확&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1731945860065&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;[99클럽 코테 스터디] 9일차 TIL - Greedy 알고리즘, 백준 2212 (JS)&quot; data-og-description=&quot;그리디 알고리즘 by. 99클럽장 김종범님특징이 많지 않은 알고리즘으로, 오히려 그리디인 것을 모르고 풀었을 때 잘 푸는 경우가 있는 아이러니한 유형 (?). 알고리즘 유형이라기보다 방법론에 가&quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/322&quot; data-og-url=&quot;https://snowflower19.tistory.com/322&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/H74NF/hyXzJGdH9l/rkP8KfqTbkHrShyUQKjiB0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/da7BkS/hyXzQ6pBsY/If726nkjwWtUzkXruMDzA0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/ZNY4h/hyXzLYlpkb/QeP9BUnWXm7ix17f0FREw0/img.png?width=1307&amp;amp;height=823&amp;amp;face=0_0_1307_823&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/322&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/322&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/H74NF/hyXzJGdH9l/rkP8KfqTbkHrShyUQKjiB0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/da7BkS/hyXzQ6pBsY/If726nkjwWtUzkXruMDzA0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/ZNY4h/hyXzLYlpkb/QeP9BUnWXm7ix17f0FREw0/img.png?width=1307&amp;amp;height=823&amp;amp;face=0_0_1307_823');&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;[99클럽 코테 스터디] 9일차 TIL - Greedy 알고리즘, 백준 2212 (JS)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;그리디 알고리즘 by. 99클럽장 김종범님특징이 많지 않은 알고리즘으로, 오히려 그리디인 것을 모르고 풀었을 때 잘 푸는 경우가 있는 아이러니한 유형 (?). 알고리즘 유형이라기보다 방법론에 가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>그리디 js</category>
      <category>백준 2847 그리디</category>
      <category>오블완</category>
      <category>코딩테스트 준비</category>
      <category>티스토리챌린지</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/319</guid>
      <comments>https://snowflower19.tistory.com/319#entry319comment</comments>
      <pubDate>Wed, 13 Nov 2024 02:09:57 +0900</pubDate>
    </item>
    <item>
      <title>Tanstack Query 시작하기  - 데이터 불러오기, 새로고침 (useQuery, queryClient.invalidateQueries 등)</title>
      <link>https://snowflower19.tistory.com/318</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Query client&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;공식 문서 : &lt;a href=&quot;https://tanstack.com/query/latest/docs/reference/QueryClient&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tanstack.com/query/latest/docs/reference/QueryClient&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Query Client&lt;/code&gt;는 캐시와 상호작용하는 데 사용되며, 즉 쿼리 캐시를 제어하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Query Client Provier&lt;/code&gt;는 &lt;code&gt;Query Client&lt;/code&gt;를 앱에 연결 및 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1731321655798&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient()

function App() {
  return &amp;lt;QueryClientProvider client={queryClient}&amp;gt;...&amp;lt;/QueryClientProvider&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;중요한 기본값&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useQuery&lt;/code&gt; 또는 &lt;code&gt;useInfiniteQuery&lt;/code&gt;를 통해 생성된 쿼리 인스턴스는 기본적으로 &lt;b&gt;캐시된 데이터를 오래된(stale) 것으로 간주&lt;/b&gt;한다. 이는 캐시된 데이터가 최신 상태가 아닐 수 있다고 판단하여 새로고침이 필요하다고 보는 것을 의미한다. 이 동작은 데이터를 안정적으로 &lt;b&gt;최신 상태로 유지&lt;/b&gt;하기 위해 제공하는 기본 전략이다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;이를 방지하기 위해서는 &lt;code&gt;staleTime&lt;/code&gt; 옵션 값을 길게 지정해서 쿼리를 구성하면, 쿼리가 데이터를 자주 다시 가져오지 않게 된다. 또한 오래된 쿼리의 자동 재조회를 막기 위해서는 &lt;code&gt;refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval&lt;/code&gt;과 같은 옵션을 사용해 변경할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;데이터 불러오기&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://tanstack.com/query/latest/docs/framework/react/reference/useQuery&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useQuery&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useQuery&lt;/code&gt;는 여러 개의 쿼리를 fetch할 수 있는 훅(Hook)이다.&lt;/p&gt;
&lt;pre id=&quot;code_1731321962514&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const query = useQuery({ queryKey: [], queryFn: () =&amp;gt; {} });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수 인자&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tanstack.com/query/latest/docs/framework/react/guides/query-keys#simple-query-keys&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;queryKey&lt;/a&gt;: fetching해오는 데이터를 구분하기 위한 &lt;b&gt;고유한 값&lt;/b&gt;. Tanstack Query는 쿼리 키에 기반하여 쿼리 캐싱을 관리한다. 직렬화 가능하고, 데이터에 대해 고유하다면 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;queryFun: &lt;b&gt;데이터를 요청&lt;/b&gt;하는데에 사용될 쿼리 함수이다. &lt;span style=&quot;color: #666666; font-family: 'Noto Serif KR';&quot;&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://tanstack.com/query/latest/docs/framework/react/guides/query-functions#queryfunctioncontext&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;QueryFuncionContext&lt;/a&gt;&lt;/span&gt;를 받고, 데이터(혹은 오류)를 promise로 반환한다. (데이터가 undefined는 될 수 없음)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731322451469&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ids = [1, 2, 3]
const results = useQueries({
  queries: ids.map((id) =&amp;gt; ({
    queryKey: ['post', id],
    queryFn: () =&amp;gt; fetchPost(id),
    staleTime: Infinity,
  })),
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환값은 다음과 같은 값을 포함한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;status: QueryStatus (pending /&amp;nbsp;error /&amp;nbsp;success 세 가지로 나뉜다.)&lt;/li&gt;
&lt;li&gt;data: TData (기본값은 undefined.)&lt;/li&gt;
&lt;li&gt;dataUpdatedAt: number&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://tanstack.com/query/latest/docs/framework/react/reference/useQueries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useQueries&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;combine 옵션&lt;/b&gt;을 사용하면 여러 쿼리를 동시에 실행한 뒤, 각 쿼리의 상태와 결과를 하나로 조합할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731322577300&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ids = [1, 2, 3]        // 각 요소는 개별 API 호출에 사용
const combinedQueries = useQueries({
  queries: ids.map((id) =&amp;gt; ({    // 배열의 각 요소에 대해 쿼리 객체를 생성
    queryKey: ['post', id],
    queryFn: () =&amp;gt; fetchPost(id),
  })),
  combine: (results) =&amp;gt; {
    return {
      data: results.map((result) =&amp;gt; result.data),    // 각 쿼리의 result.data를 모은 배열
      pending: results.some((result) =&amp;gt; result.isPending),
    }
  },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;combine 함수는 변경되거나, 쿼리의 결과가 바뀌면 재동작(re-run)한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, inline된 combine 함수는 매 랜더마다 실행될 것이다. 이것을 피하기 위해서는 combine 함수를 &lt;code&gt;useCallback&lt;/code&gt;으로 감싸거나, 의존성이 없는 함수로 추출해야 한다. &amp;rarr; Memoization&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;code&gt;combinedQueries&lt;/code&gt;가 가질 수 있는 값 예시는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1731323189775&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const combinedQueries = {
  data: [
    { id: 1, title: &quot;Post 1&quot;, content: &quot;Content of post 1&quot; },
    { id: 2, title: &quot;Post 2&quot;, content: &quot;Content of post 2&quot; },
    { id: 3, title: &quot;Post 3&quot;, content: &quot;Content of post 3&quot; }
  ],
  pending: false // 모든 쿼리가 완료된 경우
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;데이터 새로고침&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;useQuery에서 반환된 refetch 함수를 사용&lt;/p&gt;
&lt;pre id=&quot;code_1731336306047&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const refreshData = () =&amp;gt; {
    query.refetch();
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. queryClient.invalidateQueries를 사용하여 캐시된 데이터를 무효화하고, 해당 쿼리를 다시 실행&lt;/p&gt;
&lt;pre id=&quot;code_1731333690811&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const refreshData = () =&amp;gt; {
    queryClient.invalidateQueries(['cellPageData']);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;refetch&lt;/code&gt;와 &lt;code&gt;invalidateQueries&lt;/code&gt;의 차이점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;refetch: 현재 쿼리를 수동으로 다시 실행한다. 쿼리가 &lt;b&gt;이미 캐시된 상태&lt;/b&gt;에서도 다시 데이터를 가져올 수 있다.&lt;/li&gt;
&lt;li&gt;invalidateQueries: 지정한 쿼리 키에 해당하는 쿼리의 &lt;b&gt;캐시를 무효화&lt;/b&gt;하고, 그 후 자동으로 다시 실행되도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;queryClient.invalidateQueries&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;캐시를 무효화&lt;/i&gt;한다는 것은, 캐시된 &lt;b&gt;데이터의 상태를 Stale로&lt;/b&gt; 바꾼다는 말과 같다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;Tanstack Query는 캐시된 데이터를 세 가지 상태 중 하나로 관리한다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Fresh (신선): 데이터가 최신 상태이며 다시 가져올 필요가 없음.&lt;/li&gt;
&lt;li&gt;Stale (오래됨): 데이터가 더 이상 최신이 아니며, 다시 가져올 필요가 있음.&lt;/li&gt;
&lt;li&gt;Inactive (비활성화됨): 데이터가 현재 UI에서 사용되지 않으며, 다시 가져오지 않음&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;invalidateQueries&lt;/code&gt;를 호출하여 특정 쿼리를 stale로 표시하면, 데이터가 오래되었음을 명시적으로 표시하여 필요 시 적절히 갱신될 수 있도록 도와준다.&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;code&gt;refetch&lt;/code&gt;는 데이터 변경과 관계없이 데이터를 즉시 새로고침하고 싶을 때, &lt;code&gt;invalidateQueries&lt;/code&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;code&gt;query.isFetching&lt;/code&gt; 을 활용하면 된다. 데이터가 전혀 없고 쿼리가 처음 실행되는 경우에만 ture가 되는 &lt;code&gt;query.isLoading&lt;/code&gt;과 달리, &lt;code&gt;query.isFetching&lt;/code&gt;은 데이터를 가져오는 동안 언제든지 true가 된다. 이미 데이터에 캐시가 있더라도 네트워크 요청 수행 시 true이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react tanstack query</category>
      <category>react 상태관리</category>
      <category>tanstack query</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/318</guid>
      <comments>https://snowflower19.tistory.com/318#entry318comment</comments>
      <pubDate>Mon, 11 Nov 2024 23:50:27 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 6일차 TIL - 백준 14916 (JS/DP)</title>
      <link>https://snowflower19.tistory.com/317</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[6일차 미들러]&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/14916&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14916&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1319&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2b6fb/btsKEbuUZ0B/4bn0ScFmKgeZrFhroHCZP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2b6fb/btsKEbuUZ0B/4bn0ScFmKgeZrFhroHCZP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2b6fb/btsKEbuUZ0B/4bn0ScFmKgeZrFhroHCZP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2b6fb%2FbtsKEbuUZ0B%2F4bn0ScFmKgeZrFhroHCZP1%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;1319&quot; height=&quot;723&quot; data-origin-width=&quot;1319&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동전의 최소 갯수를 알기 위해 5원을 최대한 많이 사용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5로 나누어 떨어지면 정답(갯수)에 카운트하고, 아니면 2를 뺀 뒤 정답을 카운트한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 5와 2만으로 거스름돈을 줄 수 없는 경우는 n 연산의 결과가 -1이 된다. (0이면 나누어 떨어진 것, 1이면 2로 한번 더 빼서 결론적으로 -1이 됨)&lt;/p&gt;
&lt;pre id=&quot;code_1731209436457&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  let n = Number(input[0]);
  let ans = 0;

  while (true) {
    if (n % 5 == 0) {
      ans += n / 5;
      break;
    } else {
      n -= 2;
      ans += 1;
    }

    if (n &amp;lt; 0) {
      break;
    }
  }

  if (n &amp;lt; 0) {
    console.log(-1);
  } else {
    console.log(ans);
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;source: &lt;a href=&quot;https://velog.io/@tunaman95/%EB%B0%B1%EC%A4%80-14916%EB%B2%88-%EA%B1%B0%EC%8A%A4%EB%A6%84%EB%8F%88-Python&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@tunaman95/%EB%B0%B1%EC%A4%80-14916%EB%B2%88-%EA%B1%B0%EC%8A%A4%EB%A6%84%EB%8F%88-Python&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백준 알고리즘 분류를 보니, 수학/DP/그리디 로 분류된다.&lt;/li&gt;
&lt;li&gt;예외 경우부터 처리하는 것이 아니라 기본 로직을 짜고, 예외 경우를 어떻게 처리할 지 (ex. 예외 시 나올 결과값 등) 생각하자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>백준 14916 js</category>
      <category>오블완</category>
      <category>코딩테스트 준비</category>
      <category>티스토리챌린지</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/317</guid>
      <comments>https://snowflower19.tistory.com/317#entry317comment</comments>
      <pubDate>Sun, 10 Nov 2024 12:37:33 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 5일차 TIL - 백준 2644 (JS/BFS)</title>
      <link>https://snowflower19.tistory.com/316</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[5일차 미들러]&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/2644&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2644&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZSXFg/btsKwNgqZTc/LD2OP43pP2gr7CusWKEc6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZSXFg/btsKwNgqZTc/LD2OP43pP2gr7CusWKEc6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZSXFg/btsKwNgqZTc/LD2OP43pP2gr7CusWKEc6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZSXFg%2FbtsKwNgqZTc%2FLD2OP43pP2gr7CusWKEc6k%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;1048&quot; height=&quot;637&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;촌수 계산은 곧 무방향 그래프일 때, 시작 노드에서 끝 노드까지의 최단 거리 중 방문한 간선의 총 수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 최단 거리를 보장하지 않고, &lt;b&gt;BFS는 최단 거리를 보장&lt;/b&gt;한다. DFS로도 해당 문제를 풀 수 있지만, 보장이 되는 BFS로 풀이해보았다.&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;핵심 BFS 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문을 사용해서 구현했다. 이 때, 큐에 방문할 노드와&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;차수(degree, 촌수)를 함께 저장&lt;/b&gt;하는 것이 풀이의 핵심이다.&lt;/p&gt;
&lt;pre id=&quot;code_1730705783004&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const bfs = (g, total, start, end) =&amp;gt; {
  const visited = Array(total + 1).fill(false);
  const queue = [[start, 0]];
  visited[start] = true;

  while (queue.length) {
    const [node, degree] = queue.shift();

    if (node == end) {
      return degree;
    }

    for (const next of g[node]) {
      if (!visited[next]) {
        visited[next] = true;
        queue.push([next, degree + 1]);
      }
    }
  }
  return -1;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 아래와 같다. 입력을 받고, 무방향 그래프로 만들어주는 부분은 코드 아래 부분에 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730705085618&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const bfs = (g, total, start, end) =&amp;gt; {
  const visited = Array(total + 1).fill(false);
  const queue = [[start, 0]];
  visited[start] = true;

  while (queue.length) {
    const [node, degree] = queue.shift();

    if (node == end) {
      return degree;
    }

    for (const next of g[node]) {
      if (!visited[next]) {
        visited[next] = true;
        queue.push([next, degree + 1]);
      }
    }
  }
  return -1;
};

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const total = Number(input[0]);
  const [a, b] = input[1].split(&quot; &quot;).map(Number);
  const num = Number(input[2]);

  const g = Array.from({ length: total + 1 }, () =&amp;gt; []);

  for (let i = 0; i &amp;lt; num; i++) {
    const [n, m] = input[i + 3].split(&quot; &quot;).map(Number);
    g[n].push(m);
    g[m].push(n);
  }

  g.forEach((list) =&amp;gt; list.sort((a, b) =&amp;gt; a - b));

  console.log(bfs(g, total, a, b));
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DFS는 최단 거리를 보장하지 않고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;BFS는 최단 거리를 보장&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;배열 순회 반복문은 다음과 같이 쓸 수도 있다. &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;for&amp;nbsp;(const&amp;nbsp;next&amp;nbsp;of&amp;nbsp;g[node])&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 풀이로 접근했을 때 방문한 간선의 길이를 반환했었다. 그러나 DFS가 찾은 경로는 최단 경로가 아닐 수도 있기 때문에 틀린 답을 내었다.&lt;/p&gt;
&lt;pre id=&quot;code_1730702966939&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dfs = (g, n, start, end) =&amp;gt; {
  const visited = Array(n).fill(false);
  const stack = [];
  const result = [];

  stack.push(start);

  while (stack.length) {
    const node = stack.pop();

    if (node == end) {
      return result.length;
    }

    if (!visited[node]) {
      visited[node] = true;
      result.push(node);

      for (let i = g[node].length - 1; i &amp;gt;= 0; i--) {
        const next = g[node][i];
        if (!visited[next]) stack.push(next);
      }
    }
  }
  return -1;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>js dfs</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>백준 2644</category>
      <category>백준 2644 js</category>
      <category>자바스크립트 DFS</category>
      <category>자바스크립트 코테</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/316</guid>
      <comments>https://snowflower19.tistory.com/316#entry316comment</comments>
      <pubDate>Mon, 4 Nov 2024 16:34:58 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 4일차 TIL - 백준 24479 (JS/DFS)</title>
      <link>https://snowflower19.tistory.com/315</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[4일차 미들러] DFS&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/24479&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/24479&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEjlMd/btsKs0tzNvX/5Qz0pYUKWiYd7LU5lJNln1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEjlMd/btsKs0tzNvX/5Qz0pYUKWiYd7LU5lJNln1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEjlMd/btsKs0tzNvX/5Qz0pYUKWiYd7LU5lJNln1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEjlMd%2FbtsKs0tzNvX%2F5Qz0pYUKWiYd7LU5lJNln1%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;1042&quot; height=&quot;732&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 DFS 문제이다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DFS를 배우고 연습한 뒤, 한동안 까먹은 후 다시 구현하기를 반복하고서 얻은 꿀팁을 4가지로 간추려 정리해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 구현(반복문 활용)의 기본은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인자로 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;dfs(그래프, 시작노드)&lt;/span&gt; 를 받음&lt;/li&gt;
&lt;li&gt;방문 여부 확인 배열, stack 필요&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DFS 구현의 핵심은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;무방향 그래프 배열 만들기&lt;/li&gt;
&lt;li&gt;DFS 재귀 혹은 반복문으로 구현하기&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 JS로 DFS를 구현하기 위한 내용들은 &lt;a href=&quot;https://snowflower19.tistory.com/311&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;옛 포스팅&lt;/a&gt;을 참고했다.&lt;/p&gt;
&lt;figure id=&quot;og_1730390404940&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;[JS/코테] 백준 1260 DFS와 BFS&quot; data-og-description=&quot;DFS와&amp;nbsp;BFS문제 및 조건 설명: https://www.acmicpc.net/problem/1260알고리즘 설계 idea.상세 구현 방법은 다 다르지만, BFS, DFS 구현의 스타트는 각각&amp;nbsp;Stack, Queue를 기준으로 반복한다는 것이다.방문할 것들&quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/311&quot; data-og-url=&quot;https://snowflower19.tistory.com/311&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bpO942/hyXpuQAtF2/GyilRqnRDe6KdEpubL6vMK/img.png?width=800&amp;amp;height=546&amp;amp;face=0_0_800_546,https://scrap.kakaocdn.net/dn/vCfdM/hyXsZ9cy7I/QkKEWxXkdI1B8SQvKKsux1/img.png?width=800&amp;amp;height=546&amp;amp;face=0_0_800_546,https://scrap.kakaocdn.net/dn/CV5WL/hyXptc4ZAJ/6S8E6YVdJkLltK2NdXtSl0/img.png?width=1304&amp;amp;height=890&amp;amp;face=0_0_1304_890&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/311&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/311&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bpO942/hyXpuQAtF2/GyilRqnRDe6KdEpubL6vMK/img.png?width=800&amp;amp;height=546&amp;amp;face=0_0_800_546,https://scrap.kakaocdn.net/dn/vCfdM/hyXsZ9cy7I/QkKEWxXkdI1B8SQvKKsux1/img.png?width=800&amp;amp;height=546&amp;amp;face=0_0_800_546,https://scrap.kakaocdn.net/dn/CV5WL/hyXptc4ZAJ/6S8E6YVdJkLltK2NdXtSl0/img.png?width=1304&amp;amp;height=890&amp;amp;face=0_0_1304_890');&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;[JS/코테] 백준 1260 DFS와 BFS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DFS와&amp;nbsp;BFS문제 및 조건 설명: https://www.acmicpc.net/problem/1260알고리즘 설계 idea.상세 구현 방법은 다 다르지만, BFS, DFS 구현의 스타트는 각각&amp;nbsp;Stack, Queue를 기준으로 반복한다는 것이다.방문할 것들&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1730390377755&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

const dfs = (g, start) =&amp;gt; {
  let visited = Array(g.length).fill(false);
  let result = Array(g.length - 1).fill(0);
  let index = 1;
  const stack = [start];

  while (stack.length) {
    let node = stack.pop();

    if (!visited[node]) {
      visited[node] = true;
      result[node - 1] = index;
      index++;

      for (let i = g[node].length - 1; i &amp;gt;= 0; i--) {
        const next = g[node][i];
        if (!visited[next]) stack.push(next);
      }
    }
  }

  return result;
};

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const [n, m, r] = input[0].split(&quot; &quot;).map(Number);

  let g = Array.from({ length: n + 1 }, () =&amp;gt; []);
  for (let i = 1; i &amp;lt;= m; i++) {
    const [u, v] = input[i].split(&quot; &quot;).map(Number);
    g[u].push(v);
    g[v].push(u);
  }

  g.forEach((list) =&amp;gt; list.sort((a, b) =&amp;gt; a - b));

  console.log(dfs(g, r).join(&quot;\n&quot;));
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 배열을 만들 때는 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Array(길이).fill(초기값);&lt;/span&gt;으로 선언한다. 이차원 배열일 경우 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Array.from({length: 길이}, () =&amp;gt; []);&lt;/span&gt; 로 선언할 수 있다.&lt;/li&gt;
&lt;li&gt;배열의 요소들만 출력하려고 할 때, &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;배열.join(&quot; &quot;)&lt;/span&gt; 이면 요소 사이에 공백을, &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;배열.join(&quot;\n&quot;);&lt;/span&gt; 이면 요소 사이에 줄바꿈을 넣어 출력할 수 있다.&lt;/li&gt;
&lt;li&gt;DFS 구현 스탭 : 방문배열과 스택을 만든다. &amp;rarr; (시작)노드를 스택에 넣고, 방문처리한다. &amp;rarr; 스택이 빌 때까지 반복한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>js dfs</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>백준 24479</category>
      <category>백준 24479 js</category>
      <category>자바스크립트 DFS</category>
      <category>자바스크립트 코테</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/315</guid>
      <comments>https://snowflower19.tistory.com/315#entry315comment</comments>
      <pubDate>Fri, 1 Nov 2024 01:04:39 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 3일차 TIL - 프로그래머스 입국심사 (JS/이분탐색)</title>
      <link>https://snowflower19.tistory.com/314</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[3일차 미들러] 이분탐색&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/43238&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tslb0/btsKqQx5pd4/eI9bscqbQihnIBr0UW8z1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tslb0/btsKqQx5pd4/eI9bscqbQihnIBr0UW8z1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tslb0/btsKqQx5pd4/eI9bscqbQihnIBr0UW8z1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftslb0%2FbtsKqQx5pd4%2FeI9bscqbQihnIBr0UW8z1k%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;1118&quot; height=&quot;815&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 사람의 심사가 끝나는 시간(이하 t)을 반환해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심사관이 몇 명이든, &quot;모든 사람의 심사가 끝나는 시간(t)&quot;동안 심사를 했을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, t시간 동안 심사한 인원이 n명이면 된다.&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;찾을 값(t)을 심사 시간으로 나눈 몫 (= t동안 심사한 인원)의 총합이 n인가에 따라 조건을 만족하는 값을 빠르게 찾을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730305627286&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, times) {
    times.sort((a, b) =&amp;gt; a - b);
    
    let left = 0;
    let right = times[times.length - 1] * n;    // Math.max(...times) * n
    
    while (left &amp;lt; right){
        let mid = Math.floor((left+right)/2);
        
        let sum = 0;
        times.forEach((j) =&amp;gt; sum += Math.floor(mid/j));
        
        if (sum &amp;lt; n){
            left = mid + 1;            
        } else{
            right = mid;
        }
    }
    
    return left;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오름차순 정렬은 배열.sort((a, b) =&amp;gt; a - b);&lt;/li&gt;
&lt;li&gt;이분탐색에서 종료 조건(반환값)을 잘 확인하자. &lt;b&gt;반환값을 기준으로 풀이를 생각&lt;/b&gt;하는 것은 어느 알고리즘이든 항상 도움이 된다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>js 이분탐색</category>
      <category>js 입국심사</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>이분탐색</category>
      <category>코딩테스트준비</category>
      <category>프로그래머스 입국심사</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/314</guid>
      <comments>https://snowflower19.tistory.com/314#entry314comment</comments>
      <pubDate>Thu, 31 Oct 2024 01:47:47 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 2일차 TIL - 백준 11561 징검다리 (JS/이분탐색)</title>
      <link>https://snowflower19.tistory.com/313</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[2일차 미들러] 이분탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/11561&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11561&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;928&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovzt8/btsKor6Ibtc/oXd4QJdikMdxxm7QbiPgBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovzt8/btsKor6Ibtc/oXd4QJdikMdxxm7QbiPgBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovzt8/btsKor6Ibtc/oXd4QJdikMdxxm7QbiPgBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fovzt8%2FbtsKor6Ibtc%2FoXd4QJdikMdxxm7QbiPgBk%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;1317&quot; height=&quot;928&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;928&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대값은 최소 간격으로 이동할 때 구할 수 있다. 즉 1+2+3+..+n 일 경우가 최대로 징검다리를 밟는 경우가 되는 것이다.&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;그런데 이 공식으로 n이 딱 떨어지지 않는다면, 조건을 위배하게 된다. 마지막 n번 징검다리를 무조건 밟기 위해서는 기존의 간격들 중 가장 크게 점프하며 마무리하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 n=8일 때, 1+2+3 &amp;ne; 8, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;1+2+3+4&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;ne; 8 이지만, 1+2+&lt;b&gt;5&lt;/b&gt; = 8 가 될 수 있는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730217390613&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const t = Number(input[0]);
  const c = -2 * Math.floor(1e16);
  const mx = Math.floor((-1 + Math.sqrt(1 - 4 * c)) / 2) + 1;

  for (i = 1; i &amp;lt;= t; i += 1) {
    const n = Number(input[i]);
    let left = 0;
    let right = mx;
    let ans = 0;

    while (left &amp;lt; right) {
      const mid = Math.floor((left + right) / 2);
      if (Math.floor(((mid + 1) * mid) / 2) &amp;lt;= n) {
        // 등차수열 합
        ans = mid;
        left = mid + 1;
      } else {
        right = mid;
      }
    }
    console.log(ans);
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자 연산 중 최대(소)를 찾는 유형은 여전히 이분탐색. 문제에 따라 조건을 잘 확인하여, 변형할 수 있다.&lt;/li&gt;
&lt;li&gt;최대값(왼쪽값)을 잘 확인하자.&lt;/li&gt;
&lt;li&gt;줄마다 입력받기는 input[열번호]&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/313</guid>
      <comments>https://snowflower19.tistory.com/313#entry313comment</comments>
      <pubDate>Wed, 30 Oct 2024 00:59:56 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 1일차 TIL - 백준 1072 게임 (JS/이분탐색)</title>
      <link>https://snowflower19.tistory.com/312</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1일차 미들러] 이분탐색&lt;/b&gt;&lt;br /&gt;문제: &lt;a href=&quot;https://www.acmicpc.net/problem/1072&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1072&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nO1MM/btsKlQ0TZwK/9sRIvt0LF1QyGyYhgpYemk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nO1MM/btsKlQ0TZwK/9sRIvt0LF1QyGyYhgpYemk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nO1MM/btsKlQ0TZwK/9sRIvt0LF1QyGyYhgpYemk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnO1MM%2FbtsKlQ0TZwK%2F9sRIvt0LF1QyGyYhgpYemk%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;1302&quot; height=&quot;839&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;839&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 연산을 통해 특정 값을 찾아야 하는 문제는, &lt;b&gt;이분 탐색&lt;/b&gt;으로 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1730129227587&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const [x, y] = input[0].split(&quot; &quot;).map(Number);
  const z = Math.floor((y / x) * 100);
  let left = 0;
  let right = x;
  let res = x;

  if (z &amp;gt;= 99) {
    console.log(-1);
  } else {
    while (left &amp;lt;= right) {
      let mid = Math.floor((left + right) / 2);
      let newz = Math.floor((100 * (y + mid)) / (x + mid));
      if (newz &amp;gt; z) {
        res = mid;
        right = mid - 1;
      } else {
        left = mid + 1;
      }
    }
    console.log(res);
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제에서 어려웠던 점은, 승률이 99% 이상이면 더이상 오를 승률이 없다는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 승률 계산 방식에 따라 백준에서 오답으로 뜨기도 했다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730129971930&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const z = Math.floor((y / x) * 100);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y / x의 결과가 소수점으로 나올 수 있기 때문에, 이 결과를 100으로 곱한 후 Math.floor를 적용하게 된다. 이 때 소수점 아래 자리 수에 따라 잘못된 결과가 나올 수 있는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1730129979536&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const z = Math.floor((100 * y) / x);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Math.floor 을 적용하기 전에 100을 곱하는 방식이 더 정확하고 안전한 계산이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소수점을 버리고 정수를 반환하는 JS 연산 Math.floor()&lt;/li&gt;
&lt;li&gt;큰 숫자가 나올 것 같은 연산 문제는 이분탐색을 고려해보자!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://khsung0.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://khsung0.tistory.com/11&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1730129239855&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;[BOJ] Python 백준 1072번 게임 실버 3&quot; data-og-description=&quot;https://www.acmicpc.net/problem/1072&amp;nbsp;1072번: 게임김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형&quot; data-og-host=&quot;khsung0.tistory.com&quot; data-og-source-url=&quot;https://khsung0.tistory.com/11&quot; data-og-url=&quot;https://khsung0.tistory.com/11&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cnyFij/hyXpua4GeX/3BT51FCvri66drk64FJkB1/img.jpg?width=146&amp;amp;height=153&amp;amp;face=0_0_146_153,https://scrap.kakaocdn.net/dn/c0lj7C/hyXpryDZJ4/P0SYdkjFgUBLnb8qiKtwW0/img.jpg?width=146&amp;amp;height=153&amp;amp;face=0_0_146_153&quot;&gt;&lt;a href=&quot;https://khsung0.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://khsung0.tistory.com/11&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cnyFij/hyXpua4GeX/3BT51FCvri66drk64FJkB1/img.jpg?width=146&amp;amp;height=153&amp;amp;face=0_0_146_153,https://scrap.kakaocdn.net/dn/c0lj7C/hyXpryDZJ4/P0SYdkjFgUBLnb8qiKtwW0/img.jpg?width=146&amp;amp;height=153&amp;amp;face=0_0_146_153');&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;[BOJ] Python 백준 1072번 게임 실버 3&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://www.acmicpc.net/problem/1072&amp;nbsp;1072번: 게임김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;khsung0.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-1072-%EA%B2%8C%EC%9E%84-javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-1072-%EA%B2%8C%EC%9E%84-javascript&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1730129243554&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;[백준] 1072 게임 - javascript&quot; data-og-description=&quot;https://www.acmicpc.net/problem/1072✔ 알고리즘 : 이분탐색✔ 문제의 조건에서 앞으로의 모든게임에서 지지 않는다고 했으므로 새로운 확률을 계산할 때 분모와 분자에 각각 mid를 더해줘서 계산해야함&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-1072-%EA%B2%8C%EC%9E%84-javascript&quot; data-og-url=&quot;https://velog.io/@ywc8851/백준-1072-게임-javascript&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brRWc6/hyXpqzKOVE/VGHufdku7xGRYjFIu3o4n0/img.png?width=1082&amp;amp;height=703&amp;amp;face=0_0_1082_703,https://scrap.kakaocdn.net/dn/bBxvrC/hyXpvOAYuQ/BU9F2QMPUFKVhbngcF8c80/img.png?width=1082&amp;amp;height=703&amp;amp;face=0_0_1082_703,https://scrap.kakaocdn.net/dn/eqMaDY/hyXpzwFy3Y/J4PdoRQCw36sZ8dOmaVqO0/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=465_465_603_615&quot;&gt;&lt;a href=&quot;https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-1072-%EA%B2%8C%EC%9E%84-javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-1072-%EA%B2%8C%EC%9E%84-javascript&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brRWc6/hyXpqzKOVE/VGHufdku7xGRYjFIu3o4n0/img.png?width=1082&amp;amp;height=703&amp;amp;face=0_0_1082_703,https://scrap.kakaocdn.net/dn/bBxvrC/hyXpvOAYuQ/BU9F2QMPUFKVhbngcF8c80/img.png?width=1082&amp;amp;height=703&amp;amp;face=0_0_1082_703,https://scrap.kakaocdn.net/dn/eqMaDY/hyXpzwFy3Y/J4PdoRQCw36sZ8dOmaVqO0/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=465_465_603_615');&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;[백준] 1072 게임 - javascript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://www.acmicpc.net/problem/1072✔ 알고리즘 : 이분탐색✔ 문제의 조건에서 앞으로의 모든게임에서 지지 않는다고 했으므로 새로운 확률을 계산할 때 분모와 분자에 각각 mid를 더해줘서 계산해야함&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/99Club</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/312</guid>
      <comments>https://snowflower19.tistory.com/312#entry312comment</comments>
      <pubDate>Tue, 29 Oct 2024 00:45:40 +0900</pubDate>
    </item>
    <item>
      <title>[JS/코테] 백준 1260 DFS와 BFS</title>
      <link>https://snowflower19.tistory.com/311</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;DFS와&amp;nbsp;BFS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 및 조건 설명: &lt;a href=&quot;https://www.acmicpc.net/problem/1260&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1260&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5KNbA/btsJ79jYHWJ/HdoUmKQia93Gj8pFDUFIW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5KNbA/btsJ79jYHWJ/HdoUmKQia93Gj8pFDUFIW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5KNbA/btsJ79jYHWJ/HdoUmKQia93Gj8pFDUFIW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5KNbA%2FbtsJ79jYHWJ%2FHdoUmKQia93Gj8pFDUFIW0%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;1304&quot; height=&quot;890&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;알고리즘 설계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;idea.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상세 구현 방법은 다 다르지만, BFS, DFS 구현의 스타트는 각각&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Stack, Queue를 기준&lt;/b&gt;으로 반복한다는 것이다.&lt;br /&gt;방문할 것들이 저장되고, 꺼내서 검사한다.&lt;/li&gt;
&lt;li&gt;stack을 사용하는 DFS 구현을&lt;span&gt;&amp;nbsp;&lt;/span&gt;Iterative DFS 라고 한다.&lt;/li&gt;
&lt;li&gt;입력값은 &lt;b&gt;인접리스트&lt;/b&gt;로 받는다. 이 때, 번호가 작은 정점을 우선으로 하기 위해 &lt;b&gt;오름차순으로 정렬&lt;/b&gt;한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;step.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값은 인접리스트로 받고, 오름차순 정렬한다. 양방향 그래프이므로 두 번 push 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력값은 배열을 문자열로 바꾸어서 숫자만 출력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1729393892319&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let input = []; // 전체 입력값을 넣어줄 리스트 생성
let g = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const [n, m, v] = input[0].split(&quot; &quot;).map(Number);

  g = Array.from({ length: n + 1 }, () =&amp;gt; []);

  for (let i = 1; i &amp;lt;= m; i++) {
    const [a, b] = input[i].split(&quot; &quot;).map(Number);
    g[a].push(b);
    g[b].push(a);
  }
  // 오름차순 정렬
  g.forEach((adjList) =&amp;gt; adjList.sort((a, b) =&amp;gt; a - b));

  console.log(dfs(n, g, v).join(&quot; &quot;));
  console.log(bfs(n, g, v).join(&quot; &quot;));
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Iterative DFS는 &lt;a href=&quot;https://chamdom.blog/dfs-using-js/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 포스팅&lt;/a&gt;을 참고했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; &amp;nbsp; * 부모 노드는 추가할 필요없지만, 다른 응용 문제를 위해 연습 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변형한 부분은, 번호가 작은 정점을 우선 방문하기 위해 역순으로 stack에 추가해준 부분이다.&lt;/p&gt;
&lt;pre id=&quot;code_1729393964134&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dfs = (n, g, start) =&amp;gt; {
  let result = [];
  let visited = Array(n + 1).fill(false);
  const stack = [start];

  while (stack.length) {
    const cur = stack.pop();

    if (!visited[cur]) {
      visited[cur] = true;
      result.push(cur);

      // 큰 숫자부터 스택에 넣어야 pop할 때 작은 숫자부터 나오도록 역순 정렬
      for (let i = g[cur].length - 1; i &amp;gt;= 0; i--) {
        const node = g[cur][i];
        if (!visited[node]) stack.push(node);
      }
    }
  }

  return result;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS도 반복문과 queue를 사용해서 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queue에 추가(push)할 때는 이미 오름차순 정렬되어 있으므로 순차 반복해준다. queue 이므로 pop대신 shift 메서드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1729394082947&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const bfs = (n, g, start) =&amp;gt; {
  let result = [];
  let visited = Array(n + 1).fill(false);
  const queue = [start];
  visited[start] = true;

  while (queue.length) {
    const cur = queue.shift();
    result.push(cur);

    // 작은 숫자부터 큐에 넣기 위해 오름차순 정렬
    for (const node of g[cur]) {
      if (!visited[node]) {
        visited[node] = true;
        queue.push(node);
      }
    }
  }

  return result;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;알고리즘 구현&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 모든 구현을 합친 정답 코드는 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729394187817&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const readline = require(&quot;readline&quot;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const dfs = (n, g, start) =&amp;gt; {
  let result = [];
  let visited = Array(n + 1).fill(false);
  const stack = [start];

  while (stack.length) {
    const cur = stack.pop();

    if (!visited[cur]) {
      visited[cur] = true;
      result.push(cur);

      for (let i = g[cur].length - 1; i &amp;gt;= 0; i--) {
        const node = g[cur][i];
        if (!visited[node]) stack.push(node);
      }
    }
  }

  return result;
};

const bfs = (n, g, start) =&amp;gt; {
  let result = [];
  let visited = Array(n + 1).fill(false);
  const queue = [start];
  visited[start] = true;

  while (queue.length) {
    const cur = queue.shift();
    result.push(cur);

    for (const node of g[cur]) {
      if (!visited[node]) {
        visited[node] = true;
        queue.push(node);
      }
    }
  }

  return result;
};

let input = [];
let g = [];

rl.on(&quot;line&quot;, (line) =&amp;gt; {
  input.push(line);
}).on(&quot;close&quot;, () =&amp;gt; {
  const [n, m, v] = input[0].split(&quot; &quot;).map(Number);

  g = Array.from({ length: n + 1 }, () =&amp;gt; []);

  for (let i = 1; i &amp;lt;= m; i++) {
    const [a, b] = input[i].split(&quot; &quot;).map(Number);
    g[a].push(b);
    g[b].push(a);
  }
  g.forEach((adjList) =&amp;gt; adjList.sort((a, b) =&amp;gt; a - b));

  console.log(dfs(n, g, v).join(&quot; &quot;));
  console.log(bfs(n, g, v).join(&quot; &quot;));
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정리하자면, 구현을 위해서는 ①원본 그래프(input값) ②Stack/Queue ③방문확인배열 이 필요하다.&lt;/li&gt;
&lt;li&gt;입력값을 정리하는 것 또한 중요하다. 오늘 문제에서는 인접 리스트로, 오름차순 정렬로 그래프를 정리했다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구조 분해 할당&lt;/a&gt;은 배열을 펼치고 반복할 필요 없이 유용하게 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;✔️이차원 배열 선언&lt;/p&gt;
&lt;pre id=&quot;code_1729351564746&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  let numArr = new Array(n + 1).fill([]);
  let numArr = new Array(n + 1).fill().map(() =&amp;gt; []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째와 같이 선언하면, 이차원 배열 내부 원소는 모두 &lt;b&gt;같은 참조&lt;/b&gt;를 가지게 되어 특정 인덱스에 값을 추가하는 것이 불가능하고 모든 인덱스에 값이 추가된다. 각 인덱스에 서로 다른 배열을 넣어주기 위해서는 반복문을 이용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729394288404&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let numArr = Array.from({ length: n + 1 }, () =&amp;gt; []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fill 메서드는 배열을 undefined로 채운 후, map을 통해 요소를 추가하도록 동작한다. 그러나 Array.from 메서드를 사용하면 배열을 생성하면서 각 요소에 콜백 함수가 바로 실행되기 때문에 조-금 더 효율적이라고 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://chamdom.blog/dfs-using-js/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://chamdom.blog/dfs-using-js/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1729352402504&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;[알고리즘] JavaScript로 구현하는 DFS&quot; data-og-description=&quot;dfs에 대해 알아보기 전에 우선 그래프에 대한 이해가 필요하다. 그래프에 대한 설명은 여기 에 자세히 정리해두었다. DFS란? DFS(Depth-First-Search) 는 &amp;hellip;&quot; data-og-host=&quot;chamdom.blog&quot; data-og-source-url=&quot;https://chamdom.blog/dfs-using-js/&quot; data-og-url=&quot;https://chamdom.blog/dfs-using-js/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jNVyT/hyXlKYTg23/lQrPA1h7kTMl1dB7ocBTbK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/6eHPG/hyXhJURrlc/rB1B7GnnGXAPvEkNoy5aA0/img.png?width=480&amp;amp;height=255&amp;amp;face=0_0_480_255,https://scrap.kakaocdn.net/dn/l4O8g/hyXlItd2xM/c4wfoyNeagNGUwvqaLLvOK/img.png?width=480&amp;amp;height=255&amp;amp;face=0_0_480_255&quot;&gt;&lt;a href=&quot;https://chamdom.blog/dfs-using-js/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chamdom.blog/dfs-using-js/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jNVyT/hyXlKYTg23/lQrPA1h7kTMl1dB7ocBTbK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/6eHPG/hyXhJURrlc/rB1B7GnnGXAPvEkNoy5aA0/img.png?width=480&amp;amp;height=255&amp;amp;face=0_0_480_255,https://scrap.kakaocdn.net/dn/l4O8g/hyXlItd2xM/c4wfoyNeagNGUwvqaLLvOK/img.png?width=480&amp;amp;height=255&amp;amp;face=0_0_480_255');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[알고리즘] JavaScript로 구현하는 DFS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;dfs에 대해 알아보기 전에 우선 그래프에 대한 이해가 필요하다. 그래프에 대한 설명은 여기 에 자세히 정리해두었다. DFS란? DFS(Depth-First-Search) 는 &amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chamdom.blog&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>javascript 코테</category>
      <category>js dfs bfs</category>
      <category>node.js 코테</category>
      <category>백준 1260 js</category>
      <category>백준 1260 node.js</category>
      <category>코딩테스트</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/311</guid>
      <comments>https://snowflower19.tistory.com/311#entry311comment</comments>
      <pubDate>Sun, 20 Oct 2024 12:22:16 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] Props대신 Context 활용하기, useContext (ft. Prop drilling)</title>
      <link>https://snowflower19.tistory.com/310</link>
      <description>&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;Prop Drilling&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2iyx6/btsJPdIqq6q/9f5TR6fNT58hszeebDHyx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2iyx6/btsJPdIqq6q/9f5TR6fNT58hszeebDHyx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2iyx6/btsJPdIqq6q/9f5TR6fNT58hszeebDHyx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2iyx6%2FbtsJPdIqq6q%2F9f5TR6fNT58hszeebDHyx0%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;425&quot; height=&quot;870&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;Props를 사용하면 UI 트리를 통해 명시적으로 데이터를 사용하는 컴포넌트에 전달할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;그러나 많은 컴포넌트에서 같은 prop이 필요한 경우 복잡해질 수 있다. 트리 상 높은 컴포넌트에 있는 데이터를 끌어올리다 보면 &quot;Prop drilling&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&quot; 을 초래할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfLuA5/btsJPScnMO0/bz9SMkkAeIY7aVEKLesim0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfLuA5/btsJPScnMO0/bz9SMkkAeIY7aVEKLesim0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfLuA5/btsJPScnMO0/bz9SMkkAeIY7aVEKLesim0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfLuA5%2FbtsJPScnMO0%2Fbz9SMkkAeIY7aVEKLesim0%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;344&quot; height=&quot;870&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;870&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&lt;span style=&quot;background-color: #fbf595;&quot;&gt;주요 컴포넌트들에 props가 여러 번 거쳐가는 것은 그리 이상한 일이 아님을 잊지 않는다.&lt;/span&gt;&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;3. Context를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;Context는 컴포넌트가 트리 상 아래에 위치한 모든 곳에 데이터를 제공하도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;컴포넌트의 최상위 수준에서 호출하여 context를&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;읽고 구독하여 사용할 수 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;테마 지정(ex.다크모드), 현재 사용자/계정 정보, 라우팅, 상태 관리 등에 사용된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;일반적으로 &lt;b&gt;트리의 다른 부분에서 멀리 떨어져 있는 컴포넌트들이 같은 정보가 필요한 상황&lt;/b&gt;에서는 context를 선택해 볼 만하다.&lt;/span&gt;&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;size16&quot;&gt;Context를 전달하기는 다음과 같은 과정으로 요약된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #23272f; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;export const MyContext = createContext(defaultValue)&lt;/code&gt;로 context를 생성하고 내보낸다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useContext(MyContext)&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Hook에 전달해 얼마나 깊이 있든 자식 컴포넌트가 읽을 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;자식을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;&amp;lt;MyContext.Provider value={...}&amp;gt;&lt;/code&gt;로 감싸 부모로부터 context를 받도록 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context를 전달하는 자세한 단계는&amp;nbsp;아래와 같다.&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. Context 생성하기&lt;/h4&gt;
&lt;pre id=&quot;code_1727430035953&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createContext } from 'react';

export const LevelContext = createContext(1);  // 기본값 1로 설정&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Context 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useContext&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; 훅과 생성한 Context를 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 값 props로 읽어오는 대신 context에서 값을 읽어온다.&amp;nbsp;아래 예시에서, useContext는 React에게 Heading 컴포넌트가 &lt;code&gt;LevelContext&lt;/code&gt;를 읽으려 한다고 알려준다.&lt;/p&gt;
&lt;pre id=&quot;code_1727430076180&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// heading.js
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  const level = useContext(LevelContext);
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Context 제공하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;Context를 제공하지 않으면 React는 이전 단계에서 지정한 기본값을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;context를 자식들에게 &lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;제공하기 위해&lt;span&gt; &lt;/span&gt;&lt;/span&gt;context &lt;b&gt;provider로 감싸준다. &lt;/b&gt;&lt;code&gt;Context.Provider&lt;/code&gt;는 특정 값을 하위 컴포넌트로 전달하기 위한 일종의 &quot;컨테이너&quot; 역할을 하는 셈이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727430637958&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// section.js
import { LevelContext } from './LevelContext.js';

export default function Section({ level, children }) {
  return (
    &amp;lt;section className=&quot;section&quot;&amp;gt;
      &amp;lt;LevelContext.Provider value={level}&amp;gt;
        {children}
      &amp;lt;/LevelContext.Provider&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;이는 React에게 자식 컴포넌트가 context를 요구하면 가장 근처의 context 값을 주라고 알려준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727431562429&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// heading.js
export default function Heading({ children }) {
  const level = useContext(LevelContext);
  switch (level) {
    case 1:
      return &amp;lt;h1&amp;gt;{children}&amp;lt;/h1&amp;gt;;
    case 2:
      return &amp;lt;h2&amp;gt;{children}&amp;lt;/h2&amp;gt;;
    // ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;level 값을 prop로 부터 읽어오는 것이 아닌, 가장 근처의 &lt;code&gt;LevelContext&lt;/code&gt; 값을 요청하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1727431499235&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// app.js
export default function Page() {
  return (
    &amp;lt;Section level={1}&amp;gt;
      &amp;lt;Heading&amp;gt;Title&amp;lt;/Heading&amp;gt;
      &amp;lt;Section level={2}&amp;gt;
        &amp;lt;Heading&amp;gt;Heading&amp;lt;/Heading&amp;gt;
        &amp;lt;Heading&amp;gt;Heading&amp;lt;/Heading&amp;gt;
        &amp;lt;Heading&amp;gt;Heading&amp;lt;/Heading&amp;gt;
    //...
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &amp;lt;Heading&amp;gt;을 &lt;code&gt;&amp;lt;LevelContect.Provider value ={level}&amp;gt;&lt;/code&gt; 로 감싸주었으니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Heading은 &lt;code&gt;useContext&lt;/code&gt;를 사용해 가장 근처의 &lt;code&gt;levelContext&lt;/code&gt;의 값을 요청하게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1. 같은 컴포넌트에서 context를 사용&amp;amp;제공하기&lt;/h4&gt;
&lt;pre id=&quot;code_1727431897674&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// section.js
export default function Section({ children }) {
  const level = useContext(LevelContext);
  return (
    &amp;lt;section className=&quot;section&quot;&amp;gt;
      &amp;lt;LevelContext.Provider value={level + 1}&amp;gt;
        {children}
      &amp;lt;/LevelContext.Provider&amp;gt;
    &amp;lt;/section&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제처럼 levelContext를 받아오면(context 제공), 위의 컴포넌트에서 level을 읽고 자동으로 level+1을 아래로 전달 (context 사용) 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최상위의 &lt;code&gt;&amp;lt;Section&amp;gt;&lt;/code&gt; 컴포넌트는 Provider로 감싸지지 않았기 때문에 기존 level값이 아닌, &lt;code&gt;createContext()&lt;/code&gt;로 context를 생성할 때 넣어준 기본값을 가지게 된다. 이후 중첩된 &amp;lt;Section&amp;gt; 컴포넌트부터 상위 context 값을 받아와 +1 하여 활용할 수 있게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;useContext&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;context를 사용하기 위해 활용한 React Hook에 대해서도 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1727429260277&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useContext } from 'react';

function MyComponent() {
  const theme = useContext(ThemeContext);
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;createContext&lt;/code&gt;로 생성한 context&lt;/li&gt;
&lt;li&gt;context 자체는 정보를 담고 있지 않으며, 컴포넌트에서 제공하거나 읽을 수 있는 정보의 종류를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환값&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;호출하는 컴포넌트에 대한 context 값을 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;이 값은 트리에서 호출하는 컴포넌트 위의 가장 가까운 &lt;/span&gt;&lt;code&gt;SomeContext.Provider&lt;/code&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&lt;b&gt;에 전달된 value로 결정&lt;/b&gt;된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;컨텍스트가 변경되면 React는 자동으로 해당 context 를 읽는 컴포넌트를 다시 렌더링한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #23272f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;주의 사항&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useContext()&lt;span style=&quot;color: #23272f; text-align: start;&quot;&gt;는 &lt;span style=&quot;background-color: #fbf595;&quot;&gt;항상 호출하는 컴포넌트 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fbf595;&quot;&gt;&lt;b&gt;상위&lt;/b&gt;&lt;span style=&quot;color: #23272f; text-align: start;&quot;&gt;&lt;b&gt;에서&lt;/b&gt; 가장 가까운 provider를 찾는다.&lt;/span&gt;&lt;/span&gt;&amp;nbsp;useContext()&lt;span style=&quot;color: #23272f; text-align: start;&quot;&gt;를 호출하는 컴포넌트 &lt;b&gt;안의&lt;/b&gt; provider는 &lt;/span&gt;고려하지 않는다.&lt;/li&gt;
&lt;li&gt;즉 useContext() 호출해서 context 값을 사용할 컴포넌트의 &lt;b&gt;상위 컴포넌트&lt;/b&gt; 중 하나를&lt;b&gt; context provider로 감싸야&lt;/b&gt; 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useContext&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/reference/react/useContext&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1727429231057&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;useContext &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/reference/react/useContext&quot; data-og-url=&quot;https://ko.react.dev/reference/react/useContext&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cCdiGk/hyXaAowDLY/uqQImbjnfggf0c5lVsb821/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/KGdPd/hyXayYxH2L/1KoQbWDtcZQcVa62OOkU5k/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useContext&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/reference/react/useContext&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cCdiGk/hyXaAowDLY/uqQImbjnfggf0c5lVsb821/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/KGdPd/hyXayYxH2L/1KoQbWDtcZQcVa62OOkU5k/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;useContext &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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Context를 사용해 데이터를 깊게 전달하기&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/passing-data-deeply-with-context#step-3-provide-the-context&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/learn/passing-data-deeply-with-context#step-3-provide-the-context&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1727429650198&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;Context를 사용해 데이터를 깊게 전달하기 &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/learn/passing-data-deeply-with-context#step-3-provide-the-context&quot; data-og-url=&quot;https://ko.react.dev/learn/passing-data-deeply-with-context&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MEzwA/hyW6CnZNlO/LdKtOGh5qsFZ2EPTddvyc1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/sqKfE/hyW6IBKgnP/AhbbShix7CmRSuoNqgHv8K/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/passing-data-deeply-with-context#step-3-provide-the-context&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/learn/passing-data-deeply-with-context#step-3-provide-the-context&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MEzwA/hyW6CnZNlO/LdKtOGh5qsFZ2EPTddvyc1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/sqKfE/hyW6IBKgnP/AhbbShix7CmRSuoNqgHv8K/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;Context를 사용해 데이터를 깊게 전달하기 &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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>react context</category>
      <category>react prop drilling</category>
      <category>react prop 대체</category>
      <category>react useContext</category>
      <category>react 복잡한 prop</category>
      <category>useContext</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/310</guid>
      <comments>https://snowflower19.tistory.com/310#entry310comment</comments>
      <pubDate>Fri, 27 Sep 2024 21:13:27 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] Firebase 연동 후 CRUD 기능 구현 (3) - Update (Delete)</title>
      <link>https://snowflower19.tistory.com/309</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Firebase 초기화 및 Firestore 데이터베이스 불러오기&lt;/h4&gt;
&lt;pre id=&quot;code_1726752790439&quot; class=&quot;typescript&quot; style=&quot;background-color: #f6f7f8; color: #555555; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;// src\lib\firestore\index.ts
const firebaseConfig = {..};
app = initializeApp(firebaseConfig);
db = getFirestore(app);

export const getMyCollection = collection(db, 'collection-name');&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 수정(Update) 메서드 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제를 위해서 문서를 삭제하는 방법도 있지만, 이후 데이터 복원 등 문제를 최소화하기 위해서 데이터에 alive 속성을 추가하여 boolean 값으로 관리하는 방식을 선택했다.&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;먼저 확실한 문서 삭제가 필요하신 분들을 위해 간략히 설명하자면, deletDoc 메서드를 활용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1726661644430&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { doc, deleteDoc } from &quot;firebase/firestore&quot;;

await deleteDoc(doc(db, &quot;collection-name&quot;, &quot;DC&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; ️삭제에 대한 더 많은 정보는 &lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt; 참고 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정을 위해서는 updateDoc 메서드를 사용한다. 문서의 일부 필드의 값을 수정(새 값으로 덮어쓰기)하는 데 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1726752554633&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lib/firestore/card.ts
const getMyCollection = collection(db, 'collection-name');

async function deleteRequest(id: string) {
  const requestRef = doc(getMyCollection(), id);
  await updateDoc(requestRef, {
    alive: false,
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 alive 속성을 수정해주면 삭제와 같은 효과를 줄 수 있다.&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;배열 요소 업데이트를 위해서는 인자에 arrayUnion 을 활용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726753051188&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function updateRequest(id: string) {
  const cellRef = doc(getMyCollection(), id);
  await updateDoc(cellRef, {
    requestArr: arrayUnion('add data to array'),
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 메서드 호출&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 필요한 부분에 정의한 메서드를 바로 호출해서 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726756963048&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\page\MyPage.tsx
import { addPrayerRequest } from '../lib/firestore/card';

const MyPage: React.FC = () =&amp;gt; {
 const submitChanges = async () =&amp;gt; {
    // const id = 'doc id'
    try {
      await updateRequest(docId);
    } catch (error) {
        logger.error('Error adding prayer request:', error);
      }
    }
  };

  return (
    &amp;lt;button onClick={submitChanges} type=&quot;button&quot;&amp;gt;
       수정하기
    &amp;lt;/button&amp;gt;
  );
};

export default MyPage;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update-data&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update-data&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726757326306&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&quot; data-og-description=&quot;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&quot; data-og-host=&quot;firebase.google.com&quot; data-og-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update-data&quot; data-og-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update-data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update-data&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;firebase.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update_elements_in_an_array&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update_elements_in_an_array&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726757307259&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&quot; data-og-description=&quot;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&quot; data-og-host=&quot;firebase.google.com&quot; data-og-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update_elements_in_an_array&quot; data-og-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update_elements_in_an_array&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko#update_elements_in_an_array&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;firebase.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>Firestore</category>
      <category>firestore 삭제</category>
      <category>firestore 수정</category>
      <category>react</category>
      <category>react db</category>
      <category>react db 연동</category>
      <category>react firestore</category>
      <category>react nosql</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/309</guid>
      <comments>https://snowflower19.tistory.com/309#entry309comment</comments>
      <pubDate>Thu, 19 Sep 2024 23:48:53 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] Firebase 연동 후 CRUD 기능 구현 (2) - CREATE / 객체 참조 / 쿼리</title>
      <link>https://snowflower19.tistory.com/308</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase 및 Firestore 연동은 이전 포스팅을 참고해주세요 ;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 연동하기 (Firebase 인스턴스 초기화)&lt;/p&gt;
&lt;figure id=&quot;og_1726327153800&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.js/Vite] Firestore와 연동하고 콘솔 창에서 테스트하기&quot; data-og-description=&quot;Firebase에서 Firestore의 컬렉션, 문서, 필드 등을 추가한 이후의 작업입니다. 그 작업은 아래 링크 윗부분 참고 !&amp;nbsp;React + Firestore 연동 방법firebase는 구글에서 만든 개발 플랫폼으로 이것을 사용하면 d&quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/306&quot; data-og-url=&quot;https://snowflower19.tistory.com/306&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cH4YXK/hyW2QlsJ3w/FIhsFrgFu4u7qbkIEjLez0/img.png?width=800&amp;amp;height=611&amp;amp;face=0_0_800_611,https://scrap.kakaocdn.net/dn/bhT1sI/hyW2VtuLRg/PkO93e1Kq7J53Hw6sutfhk/img.png?width=800&amp;amp;height=611&amp;amp;face=0_0_800_611,https://scrap.kakaocdn.net/dn/SJS1F/hyW2P7UA6G/ZZsJppOsWkEwmnnnjjRr3K/img.png?width=1650&amp;amp;height=451&amp;amp;face=0_0_1650_451&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/306&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/306&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cH4YXK/hyW2QlsJ3w/FIhsFrgFu4u7qbkIEjLez0/img.png?width=800&amp;amp;height=611&amp;amp;face=0_0_800_611,https://scrap.kakaocdn.net/dn/bhT1sI/hyW2VtuLRg/PkO93e1Kq7J53Hw6sutfhk/img.png?width=800&amp;amp;height=611&amp;amp;face=0_0_800_611,https://scrap.kakaocdn.net/dn/SJS1F/hyW2P7UA6G/ZZsJppOsWkEwmnnnjjRr3K/img.png?width=1650&amp;amp;height=451&amp;amp;face=0_0_1650_451');&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.js/Vite] Firestore와 연동하고 콘솔 창에서 테스트하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Firebase에서 Firestore의 컬렉션, 문서, 필드 등을 추가한 이후의 작업입니다. 그 작업은 아래 링크 윗부분 참고 !&amp;nbsp;React + Firestore 연동 방법firebase는 구글에서 만든 개발 플랫폼으로 이것을 사용하면 d&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 연동 후 db연결 및 함수 작성&lt;/p&gt;
&lt;figure id=&quot;og_1726327119733&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.js] Firebase 연동 후 CRUD 기능 구현 (1) - READ&quot; data-og-description=&quot;1. 연동Firebase와 Firestore 데이터베이스를 연동 및 불러오기한다.* 연동의 자세한 설명은 이전 포스팅을 참고해주세요 !// src\lib\firestore\index.tsconst firebaseConfig = {..};app = initializeApp(firebaseConfig);db = get&quot; data-og-host=&quot;snowflower19.tistory.com&quot; data-og-source-url=&quot;https://snowflower19.tistory.com/307&quot; data-og-url=&quot;https://snowflower19.tistory.com/307&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dmoXR5/hyW227gXjH/aMftY3OWZXgqmESfjI4TMK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ceZrTz/hyW2P09qHX/3NdCLubFnXzMgD1wBOMyGK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://snowflower19.tistory.com/307&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://snowflower19.tistory.com/307&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dmoXR5/hyW227gXjH/aMftY3OWZXgqmESfjI4TMK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ceZrTz/hyW2P09qHX/3NdCLubFnXzMgD1wBOMyGK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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.js] Firebase 연동 후 CRUD 기능 구현 (1) - READ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 연동Firebase와 Firestore 데이터베이스를 연동 및 불러오기한다.* 연동의 자세한 설명은 이전 포스팅을 참고해주세요 !// src\lib\firestore\index.tsconst firebaseConfig = {..};app = initializeApp(firebaseConfig);db = get&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;snowflower19.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Firebase 초기화 및 Firestore 데이터베이스 불러오기&lt;/h4&gt;
&lt;pre id=&quot;code_1726327957062&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\index.ts
const firebaseConfig = {..};
app = initializeApp(firebaseConfig);
db = getFirestore(app);

export const getMyCollection = collection(db, 'collection-name');&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 생성 메서드 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 문서에 유의미한(원하는) 아이디를 추가하려면 setDoc,&lt;/p&gt;
&lt;pre id=&quot;code_1726375671260&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { doc, setDoc } from &quot;firebase/firestore&quot;;

await setDoc(doc(db, &quot;collection-name&quot;, &quot;new-id&quot;), data);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;자동으로 생성되는 아이디를 사용하려면 addDoc 를 사용한다.&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726375727921&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { collection, addDoc } from &quot;firebase/firestore&quot;;

const docRef = await addDoc(collection(db, &quot;collection-name&quot;), {
  name: &quot;Sam&quot;,
  level: &quot;12&quot;
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 메서드 호출&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 만들기 위해 필요한 정보들을 포함해 메서드를 호출해준다. 위 메서드 설명의 data에 해당하는 부분이다.&lt;/p&gt;
&lt;pre id=&quot;code_1726329902210&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\card.tsx
import { addDoc, collection } from 'firebase/firestore';
import { getDb } from '.';

export async function addRequest() {
  const cardRef = collection(getDb(), 'request-dev');
  const cardDoc = await addDoc(cardRef, {
    content: ['테스트1', '테스트2', '테스트3'],
    date: '2024-09-12',
    user: '사용자1',
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 문서의 &lt;b&gt;필드가 참조 객체&lt;/b&gt;로 이뤄진다면, 아래와 같이 참조할 객체를 참조 변수에 저장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 요청 컬렉션에 새로 추가할 문서에 '김진수'라는 사용자(user) 문서가 필드값이라면 아래와 같이 (1) User 컬렉션으로부터 (2) 문서를 가져오고, (3) 문서의 컬렉션을 참조한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726376610601&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// User문서들 가져오기
const userCollectionRef = collection(db, 'user-dev'); // (1)
const userDocsSnapshot = await getDocs(userCollectionRef);  // (2)

// 첫 번째 문서의 참조 생성
const firstUserDoc = userDocsSnapshot.docs[0];	// 보통 한 컬렉션에 문서가 여러개임
const userRef = firstUserDoc.ref;  // (3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ref 는 참조를 의미하는, Firestore에서 특정 컬렉션이나 문서를 가리키는 &lt;b&gt;경로&lt;/b&gt;(위치)이다. 데이터는 포함하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ref는 데이터의 위치만 나타내고, snapshot은 데이터를 포함하고 있어서 실제 데이터를 읽어오고 처리할 때 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1726376386342&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 요청 컬렉션 참조 생성
const requestCollectionRef = collection(db, 'request-dev');

// 요청 컬렉션에 새로운 문서 추가
const cardDoc = await addDoc(requestCollectionRef, {
  content: param.content,            // 요청 내용
  date: Timestamp.fromDate(date),    // 날짜 필드
  user: userRef,                     // 참조한 user 문서
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서의 참조를 생성할 때, 첫 번째 문서를 참조해온 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726376891264&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const firstUserDoc = userDocsSnapshot.docs[0];	// 보통 한 컬렉션에 문서가 여러개임
const userRef = firstUserDoc.ref;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 보통 한 컬렉션에 여러 문서가 있기 때문이고 실제로 사용할 때는 &lt;b&gt;쿼리&lt;/b&gt;를 통해 하나의 문서만을 반환하도록 코딩한다.&lt;/p&gt;
&lt;pre id=&quot;code_1726376921100&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const userQuery = query(collection(db, 'user-dev'), where('name', '==', '김진수'));
const userDocs = await getDocs(userQuery);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리가 틀렸거나, 데이터가 없는 경우를 대비하여 오류 처리도 잊지 말자!&lt;/p&gt;
&lt;pre id=&quot;code_1726377003901&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const userQuery = query(collection(db, 'user-dev'), where('name', '==', '김진수'));
const userDocs = await getDocs(userQuery);
if (userDocs.empty) {
  throw new Error('No user found with the name');
}
const userRef = userDocs.docs[0].ref;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726375497553&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&quot; data-og-description=&quot;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&quot; data-og-host=&quot;firebase.google.com&quot; data-og-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; data-og-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&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;Cloud Firestore에 데이터 추가 &amp;nbsp;|&amp;nbsp; Firebase&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 문서에서는 Cloud Firestore 데이터를 일괄 쓰려면 다음을 참조하세요. 트랜잭션 및 일괄 쓰기. 개요 다음 중 한 가지 방법으로 Cloud Firestore에 데이터를 쓸 수 있습니다. 문서 식별자를 명시적으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;firebase.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>Firestore</category>
      <category>react</category>
      <category>react db</category>
      <category>react db create</category>
      <category>react firestore</category>
      <category>react 데이터 생성</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/308</guid>
      <comments>https://snowflower19.tistory.com/308#entry308comment</comments>
      <pubDate>Sun, 15 Sep 2024 14:13:40 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] Firebase 연동 후 CRUD 기능 구현 (1) - READ</title>
      <link>https://snowflower19.tistory.com/307</link>
      <description>&lt;pre id=&quot;code_1726929727800&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myDoc = await getDoc(doc(getMyCollection(), MY_ID));
const myDocData = myDoc.data();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 연동&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase 인스턴스 초기화와 Firestore 데이터베이스를 연동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* 연동의 자세한 설명은 이전 포스팅을 참고해주세요 ;)&lt;/p&gt;
&lt;pre id=&quot;code_1726321913607&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\index.ts
const firebaseConfig = {..};

app = initializeApp(firebaseConfig);
db = getFirestore(app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화(불러오기)가 되지 않았을 경우를 대비해서 try-catch 문으로 예외처리 해주는 것이 좋다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. (선택) 응답 데이터 형식 정의하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에서 받아온 데이터를 변수에 저장할 때, 미리 반환 타입을 지정해두면 오류를 방지할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726327580567&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { DocumentReference, Timestamp } from 'firebase/firestore';

export type MyDoc = {
  name: string;
  cellArr: DocumentReference[];
};

export type RequestDoc = {
  content: string[];
  date: Timestamp;
  user: DocumentReference;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 타입 단언을 사용하면 타입 안정성을 보장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726327856347&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const familyData = familyDoc.data() as FamilyDoc; // 데이터가 FamilyDoc 타입임을 명시&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 컬렉션 불러오기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 컬렉션을 불러온다.&lt;/p&gt;
&lt;pre id=&quot;code_1726321757029&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\index.ts
export const getMyCollection = () =&amp;gt; collection(getDb(), 'collection-name');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기, 쓰기 등 다양한 상황에 컬렉션에 자주 접근하게 될테니 index.ts 와 같이 한 파일에 모아서 정리해 두어도 좋다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. [READ] 문서 불러오기 &amp;amp; 데이터 접근하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 데이터를 불러오는 함수를 만든다. 불러온 데이터를 반환해야 하므로, Promise를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1726322677148&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\tab.ts
export async function getTab(): Promise&amp;lt;Tab[]&amp;gt; {
    // 불러오기
    const myDoc = await getDoc(getMyCollection());
    // 오류처리
    if (!myDoc.exists()) {
      throw new Error('My Document not found');
    }
    
    // 데이터 접근
    const myData = myDoc.data() as MyDoc;
    // 데이터 가공 후 반환
    ..
    return myData;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션의 전체 문서가 아닌, &lt;b&gt;특정 ID&lt;/b&gt;를 가진 문서를 &lt;b&gt;참조&lt;/b&gt;하기 위해서 doc 함수를 사용해서 참조를 생성한 뒤 getDoc로 데이터를 읽어올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726322080188&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\tab.ts
const MY_ID = '';

export async function getTab(): Promise&amp;lt;Tab[]&amp;gt; {
    // 참조 객체로 불러오기
    const myRef = doc(getMyCollection(), MY_ID);
    const myDoc = await getDoc(myRef);
    ..
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서의 필드 정보들은 data() 메서드로 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9PFLZ/btsJGBQxEEb/whtyl8y22QvJBogoFSyH5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9PFLZ/btsJGBQxEEb/whtyl8y22QvJBogoFSyH5K/img.png&quot; data-alt=&quot;필드 데이터에 바로 접근할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9PFLZ/btsJGBQxEEb/whtyl8y22QvJBogoFSyH5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9PFLZ%2FbtsJGBQxEEb%2Fwhtyl8y22QvJBogoFSyH5K%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;613&quot; height=&quot;263&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;필드 데이터에 바로 접근할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서의 특정 데이터만 불러오기 위해 쿼리가 필요하다면 아래와 같이 불러올 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726322782118&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore\tab.ts
const requestQuery = query(
    getRequestCollection(),
    where('user', 'in', cellData.memberArr),
);
const requestDocs = await getDocs(requestQuery);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. [READ] 화면에 표시하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect 내부에 firestore에 접근할 API 함수를 호출해준다. 데이터를 변수에 저장하고, 필요한 곳에 props로 보내는 등 작업을 완료해서 화면에 띄워본다.&lt;/p&gt;
&lt;pre id=&quot;code_1726325724724&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\page\MyPage.tsx
const MyPage: React.FC = () =&amp;gt; {
  const [myData, setMyData] = useState&amp;lt;Info[]&amp;gt;(total);

  useEffect(() =&amp;gt; {
    const fetchTabs = async () =&amp;gt; {
      const fetchedTabs = await getTabModels();
      setMyData(fetchedTabs);
    };
    fetchTabs();
  }, []);

  return (
    &amp;lt;div&amp;gt;
        &amp;lt;TabPage data={myData}/&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default MyPage;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 가져오기 (read)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726325206041&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;데이터 가져오기 &amp;nbsp;|&amp;nbsp; Firestore &amp;nbsp;|&amp;nbsp; Google Cloud&quot; data-og-description=&quot;의견 보내기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 데이터 가져오기 Firestore에 저장된 데이터 검색 방법에는 세 가지가 있습니다. 문서, 문서 컬렉&quot; data-og-host=&quot;cloud.google.com&quot; data-og-source-url=&quot;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&quot; data-og-url=&quot;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbPDer/hyW2SDy0AN/G9dKnIawyXxMcQwKbcLy11/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cloud.google.com/firestore/docs/query-data/get-data?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbPDer/hyW2SDy0AN/G9dKnIawyXxMcQwKbcLy11/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;데이터 가져오기 &amp;nbsp;|&amp;nbsp; Firestore &amp;nbsp;|&amp;nbsp; Google Cloud&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의견 보내기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 데이터 가져오기 Firestore에 저장된 데이터 검색 방법에는 세 가지가 있습니다. 문서, 문서 컬렉&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cloud.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>firestore read</category>
      <category>firestore 데이터 가져오기</category>
      <category>react</category>
      <category>react db 연동</category>
      <category>react firestore</category>
      <category>react nosql</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/307</guid>
      <comments>https://snowflower19.tistory.com/307#entry307comment</comments>
      <pubDate>Sun, 15 Sep 2024 00:18:08 +0900</pubDate>
    </item>
    <item>
      <title>[React.js/Vite] Firestore와 연동하고 콘솔 창에서 테스트하기</title>
      <link>https://snowflower19.tistory.com/306</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase에서 Firestore의 컬렉션, 문서, 필드 등을 추가한 이후의 작업입니다. 그 작업은 아래 링크 윗부분 참고 !&lt;/p&gt;
&lt;figure id=&quot;og_1726299535317&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 + Firestore 연동 방법&quot; data-og-description=&quot;firebase는 구글에서 만든 개발 플랫폼으로 이것을 사용하면 db 구축이나 호스팅 등을 쉽게 할 수 있습니다. firestore는 firebase이라는 플랫폼에서 제공하는 NoSQL 형식의 DB입니다. firebase를 통해 Serverle&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@dkdlel102/React-Firestore-%EC%97%B0%EB%8F%99-%EB%B0%A9%EB%B2%95&quot; data-og-url=&quot;https://velog.io/@dkdlel102/React-Firestore-연동-방법&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/O1dVw/hyW22synHV/aEuKVKoJoGa0t3LiGmBugk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/rg3tU/hyW2U2ntTP/PbHo7QSzbHQfuoLbFBtheK/img.png?width=2748&amp;amp;height=1000&amp;amp;face=0_0_2748_1000,https://scrap.kakaocdn.net/dn/nYaoh/hyW2XELyWI/AjW7axptHGYXC2464pd7T0/img.png?width=1638&amp;amp;height=994&amp;amp;face=0_0_1638_994&quot;&gt;&lt;a href=&quot;https://velog.io/@dkdlel102/React-Firestore-%EC%97%B0%EB%8F%99-%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@dkdlel102/React-Firestore-%EC%97%B0%EB%8F%99-%EB%B0%A9%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/O1dVw/hyW22synHV/aEuKVKoJoGa0t3LiGmBugk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/rg3tU/hyW2U2ntTP/PbHo7QSzbHQfuoLbFBtheK/img.png?width=2748&amp;amp;height=1000&amp;amp;face=0_0_2748_1000,https://scrap.kakaocdn.net/dn/nYaoh/hyW2XELyWI/AjW7axptHGYXC2464pd7T0/img.png?width=1638&amp;amp;height=994&amp;amp;face=0_0_1638_994');&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 + Firestore 연동 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;firebase는 구글에서 만든 개발 플랫폼으로 이것을 사용하면 db 구축이나 호스팅 등을 쉽게 할 수 있습니다. firestore는 firebase이라는 플랫폼에서 제공하는 NoSQL 형식의 DB입니다. firebase를 통해 Serverle&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;h4 data-ke-size=&quot;size20&quot;&gt;1. Firestore 설치하기&lt;/h4&gt;
&lt;pre id=&quot;code_1726299193557&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install firestore&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2.Firebase SDK 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firestore.tsx 파일을 만들어준다. 파일 이름은 원하는대로 지어도 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6f7WS/btsJDayuVt1/IhPQJaasiyROld9puh7AiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6f7WS/btsJDayuVt1/IhPQJaasiyROld9puh7AiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6f7WS/btsJDayuVt1/IhPQJaasiyROld9puh7AiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6f7WS%2FbtsJDayuVt1%2FIhPQJaasiyROld9puh7AiK%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;672&quot; height=&quot;514&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;1125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase 내 프로젝트 &amp;gt; 프로젝트 설정 &amp;gt; 내 앱 &amp;gt; SDK 설정 및 구성에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API key 정보들은 보안을 위해 환경변수로서 적어주고, 일반적으로는 process.env. 로, Vite를 사용하면 import.meta.env로 불러와준다.&lt;/p&gt;
&lt;pre id=&quot;code_1726299284148&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore.tsx

import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore/lite';

const firebaseConfig = {
  // firebase 설정과 관련된 개인 정보
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY, // process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, // process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
  measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API key 정보들은 보안을 위해 다른 파일에 환경변수 처리해준다. 변수 이름은 REACT_APP으로 시작해야 하고, vite를 사용하면 VITE_FRIEBASE_ 로 시작해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1726299244811&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .env
VITE_FIREBASE_API_KEY=&quot;..&quot;
VITE_FIREBASE_AUTH_DOMAIN=&quot;...firebaseapp.com&quot;
VITE_FIREBASE_PROJECT_ID=&quot;..&quot;
VITE_FIREBASE_STORAGE_BUCKET=&quot;..&quot;
VITE_FIREBASE_MESSAGING_SENDER_ID=&quot;2..&quot;
VITE_FIREBASE_APP_ID=&quot;..&quot;
VITE_FIREBASE_MEASUREMENT_ID=&quot;..&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 firestore.tsx 파일에서 firebase와 연동 후, db를 불러와서 firestore 변수에 저장해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1726319349617&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore.tsx
..
const firebaseConfig = { .. };

// firebaseConfig 정보로 firebase 시작
const app = initializeApp(firebaseConfig);

// firebase의 firestore 인스턴스를 변수에 저장
const db = getFirestore(app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 연동 자체는 끝난다. 그럼 잘 연동되었는지 빠르게 확인하기 위해서 다음 테스트 코드를 활용하며 기본 사용법을 익혀보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 데이터를 가져오도록 쿼리 함수를 작성한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firebase를 시작하고, 그 인스턴스를 firestore에 저장해 연동했다면 이제 원하는 컬렉션과 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRUD 중 간단한 Read로 테스트 해보자. 단일 데이터를 읽기 위해 getDocs 함수를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726319289754&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src\lib\firestore.tsx
..
export async function getTest() {
  const querySnapshot = await getDocs(collection(db, 'collection-name'));
  querySnapshot.forEach((doc) =&amp;gt; {
    console.log(`${doc.id} =&amp;gt; `, doc.data());
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection-name 이라는 컬렉션에 있는 데이터들을 읽어올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726299792287&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect } from 'react';
import { getTest } from './lib/firebase'; // Adjust the path based on your project structure

const TestComponent = () =&amp;gt; {
  useEffect(() =&amp;gt; {
    // Call the getTest function when the component mounts
    getTest()
      .then(() =&amp;gt; {
        console.log('데이터 fetch 성공');
      })
      .catch((error) =&amp;gt; {
        console.error('데이터 fetching 오류 발생: ', error);
      });
  }, []); // The empty array ensures this runs once when the component mounts

  return &amp;lt;div&amp;gt;Check the console for Firestore data&amp;lt;/div&amp;gt;;
};

export default TestComponent;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때는 이렇게 useEffect로 감싸서 호출하고, 테스트를 위해 콘솔 로그로 찍어본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/besEyj/btsJCKNNmF2/G9Hf7iTDA66zzStgXgQvYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/besEyj/btsJCKNNmF2/G9Hf7iTDA66zzStgXgQvYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/besEyj/btsJCKNNmF2/G9Hf7iTDA66zzStgXgQvYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbesEyj%2FbtsJCKNNmF2%2FG9Hf7iTDA66zzStgXgQvYK%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;1650&quot; height=&quot;451&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 연동하고 읽어오기를 마치면, 콘솔창에 Firestore에 등록한 왼쪽 데이터들이 오른쪽 콘솔창에서 뜨는 모습을 확인할 수 있다.&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;이제 firestore를 연동한 CRUD 기능을 보다 체계적으로 구현해보자 !&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>firebase react 연동</category>
      <category>Firestore CRUD</category>
      <category>firestore 연동</category>
      <category>react firestore</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/306</guid>
      <comments>https://snowflower19.tistory.com/306#entry306comment</comments>
      <pubDate>Sat, 14 Sep 2024 22:19:34 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] 특정 props 변경에 따른 state 변수 초기화</title>
      <link>https://snowflower19.tistory.com/305</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;특정 props가 변경될 때 마다 state 변수를 초기화하기 위해서는 어떻게 해야 할까?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 상황:&lt;/p&gt;
&lt;pre id=&quot;code_1722956338559&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App: React.FC = () =&amp;gt; {
  const item1: Info = {
    id: 0,
    name: '',
    date: '',
    content: [''],
  };
  // ...
  return(
    &amp;lt;div&amp;gt;
      &amp;lt;Card data={item1} /&amp;gt;
      &amp;lt;Card data={item2} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

const Card: React.FC&amp;lt;OwnProps&amp;gt; = ({data, ... }) =&amp;gt; {
  const [content, setContent] = useState(data.content);
  const [title, setTitle] = useState(data.name);
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Card 컴포넌트에서 객체인 data를 props로 받는다. content와 title은 prop에 의해 초기화된다.&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;(1) 각 컴포넌트를 전부 다른 위치에&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; 렌더링하거나 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;(2) &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;key를 이용해 각 컴포넌트의 state를 초기화해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;(1)의 방법은, Card 컴포넌트의 수가 많아지면 JSX에서 각각 렌더링하는 것이 번거로워질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723285970992&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Card index={item1.id} data={item1} /&amp;gt;
&amp;lt;Card index={item2.id} data={item2} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;따라서 위와 같이 key값을 주어 각 컴포넌트를 구분하면, 렌더링 시 마다 각 컴포넌트의 state값을 초기화시킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723285868630&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App: React.FC = () =&amp;gt; {
  const [dataArr, setDataArr] = useState&amp;lt;Info[]&amp;gt;([]);
  // ...
  return(
    &amp;lt;div&amp;gt;
      dataArr.map((item, index) =&amp;gt; (
          &amp;lt;Card key={index} data={item /&amp;gt;
        ))
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;주의할 것은, 아래와 같이 map을 활용해서 여러 요소를 랜더링할 때 key값을 &lt;b&gt;index로 주어서는 안된다&lt;/b&gt;는 것이다. 데이터에 따라 같은 index를 가지게 되어 결국 &quot;같은 위치&quot;로 인식되는 것과 동일해지기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 이렇게 동작할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 화면의 각 컴포넌트는 완전히 분리된 state를 가진다. 각 Card 컴포넌트마다 독립된 content와 title state를 가지게 되는 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React는 같은 자리의 같은 컴포넌트는 state를 보존한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI 트리에서 없어지거나, 다른 컴포넌트가 렌더링되지 않는 한 유지하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJcGC4/btsIWcDrzhk/gCi1qVU7mM2oHRRRaY32lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJcGC4/btsIWcDrzhk/gCi1qVU7mM2oHRRRaY32lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJcGC4/btsIWcDrzhk/gCi1qVU7mM2oHRRRaY32lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJcGC4%2FbtsIWcDrzhk%2FgCi1qVU7mM2oHRRRaY32lK%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;369&quot; height=&quot;387&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;같은 자리&quot;는, 컴포넌트가 어느 부모 밑의 몇 번째 자식인지 등을 의미한다. 말그대로 &lt;b&gt;UI 트리에서의 위치&lt;/b&gt;를 의미하는 것이다. 이는 같은 &quot;주소&quot;라고도 볼 수 있다. root의 첫 번재 자식으로 말이다.&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;밑의 코드처럼, prop인 isFancy의 값이 변경되어도, App의 첫 번째 자식으로 Counter 컴포넌트가 바뀌는 셈이다.&amp;nbsp; React의 관점에서는 같은 위치에 같은 컴포넌트이므로, &lt;b&gt;&lt;i&gt;같은 Counter 컴포넌트&lt;/i&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;[❌제거되어 state 초기화 되는 경우]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서, 두 번째 Counter 컴포넌트가 showB state에 의해 보여지지 않게되면, UI 트리에서도 제거된다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; state 값 초기화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ ⭕ props 변경에도 state 초기화 안되는 경우]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밑의 코드에서, prop인 isFancy값에 따라 Counter 컴포넌트가 렌더링되지만, 언제나 root(&amp;lt;div&amp;gt;)의 첫 번째 자식이 되므로 같은 위치의 같은 컴포넌트로 인식하게 된다. &amp;rarr; state 값 유지&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/canHMZ/btsIW63mbCt/XNO5hLs42IM0cDJ5SBvzf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/canHMZ/btsIW63mbCt/XNO5hLs42IM0cDJ5SBvzf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/canHMZ/btsIW63mbCt/XNO5hLs42IM0cDJ5SBvzf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcanHMZ%2FbtsIW63mbCt%2FXNO5hLs42IM0cDJ5SBvzf0%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;543&quot; height=&quot;209&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;같은 자리의 &lt;/span&gt;다른&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; 컴포넌트 타입으로 바꾸거나, 같은 위치에 다른 컴포넌트를 렌더링할 때 컴포넌트는 그의 전체 서브 트리의 state를 초기화한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;따라서 &lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;경험상(rule of thumb) &lt;/span&gt;리렌더링할 때 state를 유지하고 싶다면, 트리 구조가 &quot;같아야&quot; 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;같은 위치에서 state를 초기화하기 위해서는&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;1. 다른 위치에 컴포넌트를 렌더링하거나&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;2. key를 이용해 state를 초기화해야 한다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;같은 위치의 같은 컴포넌트이지만, 개념적으로 다른 컴포넌트일 때가 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;UI에 같은 위치에 나타나지만, 날짜에 따라 일정들이 다르게 보이는 것처럼 말이다. 따라서 1번 해결방법대로 할 수 없는 경우 key값을 활용하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/preserving-and-resetting-state#option-1-rendering-a-component-in-different-positions&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/learn/preserving-and-resetting-state#option-1-rendering-a-component-in-different-positions&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;✔️key를 이용해 state를 초기화하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;key를 이용하면 React에게 단지 &lt;/span&gt;&lt;i&gt;첫 번째&lt;/i&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; 컴포넌트나 &lt;/span&gt;&lt;i&gt;두 번째&lt;/i&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;컴포넌트&lt;/span&gt;가 아니라 특정한 카운터라고 말해줄 수 있다. 트리 어디에서 나타나든 고유한 컴포넌트임을 알려줄 수 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;를 명시하면 React는 &lt;b&gt;부모 내에서&lt;/b&gt;의 순서 대신에 &lt;/span&gt;&lt;b&gt;key&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: start;&quot;&gt;&lt;b&gt; 자체를 위치의 일부로 사용&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723285407895&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{isPlayerA ? (
  &amp;lt;Counter key=&quot;Taylor&quot; person=&quot;Taylor&quot; /&amp;gt;
) : (
  &amp;lt;Counter key=&quot;Sarah&quot; person=&quot;Sarah&quot; /&amp;gt;
)}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제와 같이 JSX에서 &quot;같은 위치&quot;에 나타나더라도 다른 key값을 주게 되면, 컴포넌트마다 고유한 state를 가지게 되어 초기화가 가능해진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;♀️좋지 않은 방법: useEffect 의존성에 props를 추가하고, 내부에서 set 실행&lt;/h4&gt;
&lt;pre id=&quot;code_1723014108016&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Card: React.FC&amp;lt;OwnProps&amp;gt; = ({ data, ... }) =&amp;gt; {
  const [newContent, setNewContent] = useState(data.content);
  const [newTitle, setNewTitle] = useState(data.name);

  useEffect(() =&amp;gt; {
    setNewContent(data.content);
    setNewTitle(data.name);
  }, [data]);
  //...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 prop가 바뀔 때마다, state를 업데이트(값 변경) 하기 위해 useEffect에서 state를 변경하게 되면 컴포넌트의 렌더링이 끝난 후 상태 변경이 발생하여, 이로 인해 React가 컴포넌트를 다시 렌더링하게 되어 자식 컴포넌트가 두 번 렌더링될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 전체 컴포넌트 트리의 state를 초기화하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://ko.react.dev/reference/react/useState#resetting-state-with-a-key&quot;&gt;컴포넌트에 다른key를 전달하는 방법&lt;/a&gt; 을 사용하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/preserving-and-resetting-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/learn/preserving-and-resetting-state&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723284453088&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;State를 보존하고 초기화하기 &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/learn/preserving-and-resetting-state&quot; data-og-url=&quot;https://ko.react.dev/learn/preserving-and-resetting-state&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/goVyH/hyWOkTMXQV/5wLzBYtOwPApejavtFqaJ1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/uYSrx/hyWOdUEeNT/vrebZLZ75IYsta0KmFY3N1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/preserving-and-resetting-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/learn/preserving-and-resetting-state&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/goVyH/hyWOkTMXQV/5wLzBYtOwPApejavtFqaJ1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/uYSrx/hyWOdUEeNT/vrebZLZ75IYsta0KmFY3N1/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;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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>React key</category>
      <category>react state 초기화</category>
      <category>React UseEffect</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/305</guid>
      <comments>https://snowflower19.tistory.com/305#entry305comment</comments>
      <pubDate>Sat, 10 Aug 2024 19:33:37 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] useMemo</title>
      <link>https://snowflower19.tistory.com/304</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt; useMemo&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo는 재렌더링 사이에 계산 결과를 캐싱할 수 있게 해주는 훅이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재계산 생략, 컴포넌트 재렌더링 생략, 다른 훅의 종속성 메모화, 함수 메모화 등에 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;calculateValue : 캐싱하려는 값을 계산하는 함수. 인자를 받지 않고, 모든 타입 값 반환 가능.&lt;/li&gt;
&lt;li&gt;dependencies : calculateValue 내에서 참조된 모든 반응형 값들의 목록이다. props, state와 선언된 모든 변수와 함수가 포함된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1722669515176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useMemo(calculateValue, dependencies)&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;useMemo는 초기 랜더링 중에 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;calculateValue&lt;/span&gt; 함수를 호출한다.&amp;nbsp;&lt;br /&gt;&amp;rarr; 다음 렌더링에서 dependencies가 변했으면 재호출하여 결과 반환 및 저장하고, 변하지 않았으면 동일한 값 다시 반환한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ useMemo는 컴포넌트 최상위 레벨 또는 자체 훅에서만 호출할 수 있고, 반복문이나 조건문 내부에서 호출할 수 없다. 만일 필요하다면 새 컴포넌트를 추출하고 상태를 그 안으로 옮겨야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useMemo 사용법1 : 비용이 높은 로직의 재계산 생략&lt;/h4&gt;
&lt;pre id=&quot;code_1722669476061&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMemo } from 'react';

function TodoList({ todos, tab, theme }) {
  const visibleTodos = useMemo(() =&amp;gt; filterTodos(todos, tab), [todos, tab]);
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #23272f; text-align: left;&quot;&gt;인수를 받지 않고, 계산하려는 값을 반환하는 calculateValue(계산 함수)와 종속성 목록을 인자로 useMemo를 호출한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Memoization이 유용한 경우&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;useMemo는 처음 렌더링을 &lt;i&gt;더 빠르게 만들지 않는다&lt;/i&gt;. 업데이트 시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;불필요한 작업을 건너뛰는&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데 도움이 될 뿐이다.&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;useMemo로 최적화 하는 것이 유용한 경우는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;useMemo에 입력하려는 계산이 눈에 띄게 느리고, 종속성이 거의 변경되지 않는 경우&lt;/li&gt;
&lt;li&gt;memo로 감싸진 컴포넌트에 props를 전달할 경우 - 값이 변경되지 않았다면 랜더링을 건너 뛴다.&lt;/li&gt;
&lt;li&gt;전달한 값을 나중의 일부 Hook의 종속성으로 이용할 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 외는 계산을 useMemo로 감싸는 것에 대한 이득이 없지만, 문제 되는 것이 없어 최대한 많이 메모하기도 한다. 그러나 코드 가독성이 떨어지고, '항상 새로운' 단일 값만으로도 전체 컴포넌트에 대한 메모화가 깨질 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useMemo 사용법2 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트 재랜더링 건너뛰기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;useMemo는 하위 컴포넌트 재랜더링 성능 최적화에 도움이 될 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722671510188&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function TodoList({ todos, tab, theme }) {
  // ...
  return (
    &amp;lt;div className={theme}&amp;gt;
      &amp;lt;List items={visibleTodos} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모인 TodoList가 다른 theme로 다시 랜더링되면 자식인 List 컴포넌트 또한 다시 랜더링된다. 이 때, List를 memo를 통해 감싸 props가 마지막 랜더링 시점과 동일할 때 &lt;b&gt;다시 랜더링하는 것을 생략&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722671570565&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { memo } from 'react';

const List = memo(function List({ items }) {
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변경하면 List는 모든 props가 마지막 랜더링 때와 동일한 경우 다시 랜더링하지 않게 된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useMemo 사용법3 :&lt;span&gt; 다른 Hook의 종속성 메모화&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1722951270446&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Dropdown({ allItems, text }) {
  const searchOptions = { matchMode: 'whole-word', text };

  const visibleItems = useMemo(() =&amp;gt; {
    return searchItems(allItems, searchOptions);
  }, [allItems, searchOptions]); //   주의: 컴포넌트 본문에서 생성된 객체에 대한 종속성
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위와 같이 객체에 의존하는 연산이 있는 컴포넌트는 리렌더링 될 때, &lt;b&gt;객체&lt;/b&gt;(&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;code&gt;searchOptions&lt;/code&gt;)&lt;/span&gt;&lt;b&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;&lt;span&gt;☝  해결 방법:&amp;nbsp; &lt;b&gt;객체 자체를 메모한 뒤 종속성에 전달&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722951157831&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Dropdown({ allItems, text }) {
  const searchOptions = useMemo(() =&amp;gt; {
    return { matchMode: 'whole-word', text };
  }, [text]); // ✅ text가 변경될 때만 변경

  const visibleItems = useMemo(() =&amp;gt; {
    return searchItems(allItems, searchOptions);
  }, [allItems, searchOptions]); // ✅ allItems이나 searchOptions이 변경될 때만 변경
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드로 하면 객체 내부의 text가 변경되지 않으면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;searchOptions 객체도 변경되지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; ✌  해결 방법: useMemo 계산 함수 &lt;b&gt;내부에&lt;/b&gt; &lt;b&gt;객체&amp;nbsp;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;선언&lt;/b&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722952100654&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Dropdown({ allItems, text }) {
  const visibleItems = useMemo(() =&amp;gt; {
    const searchOptions = { matchMode: 'whole-word', text };
    return searchItems(allItems, searchOptions);
  }, [allItems, text]); // ✅ allItems이나 text가 변경될 때만 변경
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 연산이 text 에 직접적으로 의존하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;useMemo 사용법4 :&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수 메모화&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1722955478788&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function ProductPage({ productId, referrer }) {
  function handleSubmit(orderDetails) {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails
    });
  }

  return &amp;lt;Form onSubmit={handleSubmit} /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memo로 감싸져 있는 컴포넌트에 함수인 prop(&lt;code&gt;handleSubmit&lt;/code&gt;)를 전달하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prop의 변경 여부와 관계 없이 렌더링 될 때마다 다른 함수를 생성하게 되어 메모이제이션의 목적을 흐리게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo를 활용해서 보내고자 하는 함수를 감쌀 수 있다. 그러나,&lt;/p&gt;
&lt;pre id=&quot;code_1722955598895&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function Page({ productId, referrer }) {
  const handleSubmit = useCallback((orderDetails) =&amp;gt; {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails
    });
  }, [productId, referrer]);

  return &amp;lt;Form onSubmit={handleSubmit} /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo로 함수를 메모할 때, 대신 useCallback으로 함수를 감싸면 함수 중첩을 피할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 다음 포스팅에서 useCallback에 대해 알아보도록 하자 !&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useMemo&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/reference/react/useMemo&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722669107760&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;useMemo &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/reference/react/useMemo&quot; data-og-url=&quot;https://ko.react.dev/reference/react/useMemo&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JgEZT/hyWGV9pQ7n/bbJyUzvGmbMPNmoJDYEkS1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/lbgxw/hyWKDMOkTc/YdKT3k4AhtDHdby84YzcO1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useMemo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/reference/react/useMemo&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JgEZT/hyWGV9pQ7n/bbJyUzvGmbMPNmoJDYEkS1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/lbgxw/hyWKDMOkTc/YdKT3k4AhtDHdby84YzcO1/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;useMemo &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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>react hook</category>
      <category>react state 변수 기억</category>
      <category>React useMemo</category>
      <category>react 메모화</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/304</guid>
      <comments>https://snowflower19.tistory.com/304#entry304comment</comments>
      <pubDate>Tue, 6 Aug 2024 23:48:54 +0900</pubDate>
    </item>
    <item>
      <title>[React.js] Hook소개, useState, useEffect</title>
      <link>https://snowflower19.tistory.com/303</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt; Hook 소개&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 &amp;ldquo;연동(hook into)&amp;ldquo;할 수 있게 해주는 함수이다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;문제 배경:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React 프로젝트에서는 상태 관련 로직들과 사이드 이펙트( React 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 작업 )가 있는 컴포넌트들을 유지보수 해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;문제와 해결의 한계:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&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;span style=&quot;color: #333333; text-align: left;&quot;&gt;React와 &lt;u&gt;상태 관리 라이브러리&lt;/u&gt;를 함께 결합해서 사용해왔지만, 이는 종종 너무 많은 추상화를 하고, 컴포넌트 재사용을 더 어렵게 만든다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook의 등장:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 해결하기 위해, 생명주기 메서드를 기반으로 쪼개기 보다 Hook을 통해 &lt;u&gt;서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나누는&lt;/u&gt; 방법을 사용할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook의 특징:&lt;/p&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;u&gt;&lt;b&gt; Hook은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와준다.&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Hook은 React 16.8 부터 추가된 기능으로, Class 없이도 State와 다른 React의 기능들을 사용할 수 있게 해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;rules-of-hooks&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✌️ Hook 사용 규칙&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Hook은 그냥 JavaScript 함수이지만, 두 가지 규칙을 준수해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;최상위에서만 Hook을 호출한다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다.&lt;/li&gt;
&lt;li&gt;함수 컴포넌트 내에서만 Hook을 호출해야 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; State Hook&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useState&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState는 함수형 컴포넌트에서 상태(state)를 관리하기 위해 사용한다. state는 컴포넌트의 랜더링과 재랜더링을 트리거하며, 동적인 데이터를 관리할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state의 초기 값을 인자로 받고, 변수와 해당 변수를 갱신할 수 있는 함수 두 가지 쌍을 반환한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721652478341&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';

function Example() {
  // &quot;count&quot;라는 새 상태 변수를 선언합니다
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;You clicked {count} times&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Click me
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이 state는 컴포넌트가 다시 렌더링 되어도 그대로 유지된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState는 &lt;b&gt;현재의 state값&lt;/b&gt;과 이 값을 &lt;b&gt;업데이트 하는 함수&lt;/b&gt;를 쌍으로 제공한다. 이 함수를 다른 곳에서 호출해서 state값을 변경할 수 있다. 이는 Javascipt Class의 this.setState와 거의 유사하지만, useState Hook의 state는 객체일 필요가 없다는 점에서 &lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-state.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;차이가 있다&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 업데이트 하는 방법에는 1) updater 함수를 사용 2) 직접 다음 값 전달 이 있다. 현재 상태에 기반한 여러 번의 업데이트가 필요한 상황에서는 updater 함수를 사용하는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1721656693195&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [age, setAge] = useState(42);

// 직접 값 전달
function handleClick() {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}

// updater 함수 사용
function handleClick() {
  setAge(a =&amp;gt; a + 1); // setAge(42 =&amp;gt; 43)
  setAge(a =&amp;gt; a + 1); // setAge(43 =&amp;gt; 44)
  setAge(a =&amp;gt; a + 1); // setAge(44 =&amp;gt; 45)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태에 객체와 배열을 넣고 업데이트 하기 위해서는 직접 값을 변경하지 말고, 전체 값을 복사한 뒤 새로운 상태로 업데이트해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721657177152&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//   이런 식으로 state의 객체를 변경하면 안된다:
form.firstName = 'Taylor';

// ✅ 새로운 객체로 상태를 대체해야 한다.
setForm({
  ...form,
  firstName: 'Taylor'
});&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;중복 배제 원칙에 따라, state 변수는 최소한이어야 한다. 다음과 같은 기준을 통해 state 로 사용할 것인지 말 것인지 결정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333d4b; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 부모로부터 Props를 통해 전달됩니까? 그러면 확실히 state가 아닙니다.&lt;/p&gt;
&lt;p style=&quot;color: #333d4b; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 시간이 지나도 변하지 않나요? 그러면 확실히 state가 아닙니다.&lt;/p&gt;
&lt;p style=&quot;color: #333d4b; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 컴포넌트 안의 다른 state나 Props를 가지고 계산 가능한가요? 그렇다면 state가 아닙니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚡Effect Hook&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useEffect&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect는 컴포넌트를 외부 시스템(네트워크나 브라우저 API, 라이브러리들)과 연결할 수 있게 해주는 React 훅이다. 컴포넌트가 랜더링될 때와 state 또는 props가 변경될 때 수행되어야 하는 side effects를 처리하기 위해 사용된다.&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: #000000; text-align: start;&quot;&gt;Effects는 컴포넌트 안에 선언되어있기 때문에 props와 state에 접근할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기본적으로 React는 매 렌더링 이후에 effects를 실행한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722227370718&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  // setup
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() =&amp;gt; {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () =&amp;gt; {
      connection.disconnect();
    };
  }, [serverUrl, roomId]); // [dependencies]
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect에는 2개의 인수가 필요한데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 외부 시스템과 컴포넌트를 연결하는 &lt;b&gt;설정코드&lt;/b&gt;가 포함된 설정 함수&lt;/p&gt;
&lt;pre id=&quot;code_1722227502660&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const connection = createConnection(serverUrl, roomId);
connection.connect();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템과 연결을 해제하는 &lt;b&gt;정리코드&lt;/b&gt;가 포함된 정리함수도 반환할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722227549581&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return () =&amp;gt; {
  connection.disconnect();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 2. 함수 내부에서 사용하는 값들을 포함하는 의존성 배열 이 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1722227599834&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;}, [serverUrl, roomId]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔️실행 단계&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트가 화면에 추가(마운트 시)되면 React는 &lt;b&gt;설정 함수를 실행&lt;/b&gt;한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의존성이 변경&lt;/b&gt;된 후 다시 렌더링할 때마다, &lt;br /&gt;React는 먼저 기존의 props와 state로 &lt;b&gt;정리 함수&lt;/b&gt;를 실행한 다음,&lt;br /&gt;새로운 props와 state값으로 &lt;b&gt;설정 함수&lt;/b&gt;를 실행한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;컴포넌트가 화면에서 제거(마운트 해제)되면 React는 &lt;b&gt;정리 함수&lt;/b&gt;를 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템과의 연결 예시에는 &lt;a href=&quot;https://ko.react.dev/reference/react/useEffect#examples-connecting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;아래와 같은 것&lt;/a&gt;들이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;채팅 서버와 연결&lt;/li&gt;
&lt;li&gt;전역 브라우저 이벤트 감시&lt;/li&gt;
&lt;li&gt;애니메이션 동작시키기&lt;/li&gt;
&lt;li&gt;모달 대화 상자 제어하기&lt;/li&gt;
&lt;li&gt;요소의 가시성 추적&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useState&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.react.dev/reference/react/useState&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722228256052&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;useState &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/reference/react/useState&quot; data-og-url=&quot;https://ko.react.dev/reference/react/useState&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chWw9o/hyWGXSCsiF/BuKkLZeDQEKkNH2hObHgYk/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/b8VWSe/hyWGZQrFgU/HkkDkF8nwghseS2SKpJJG1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/reference/react/useState&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/reference/react/useState&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chWw9o/hyWGXSCsiF/BuKkLZeDQEKkNH2hObHgYk/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/b8VWSe/hyWGZQrFgU/HkkDkF8nwghseS2SKpJJG1/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;useState &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;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react.dev/reference/react/useEffect#fetching-data-with-effects&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://react.dev/reference/react/useEffect#fetching-data-with-effects&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721658801102&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;useEffect &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/reference/react/useEffect#fetching-data-with-effects&quot; data-og-url=&quot;https://react.dev/reference/react/useEffect&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FnV65/hyWCPAJOuI/03WbBK4MAeG0HB7Zi8kS50/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/DGrqW/hyWCJm0FVi/zd46BSVIxFOxfkttqxbrxk/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://react.dev/reference/react/useEffect#fetching-data-with-effects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react.dev/reference/react/useEffect#fetching-data-with-effects&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FnV65/hyWCPAJOuI/03WbBK4MAeG0HB7Zi8kS50/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/DGrqW/hyWCJm0FVi/zd46BSVIxFOxfkttqxbrxk/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;useEffect &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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>react hook</category>
      <category>React UseEffect</category>
      <category>React UseState</category>
      <category>react 상태관리</category>
      <category>리액트 훅</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/303</guid>
      <comments>https://snowflower19.tistory.com/303#entry303comment</comments>
      <pubDate>Mon, 29 Jul 2024 13:45:09 +0900</pubDate>
    </item>
    <item>
      <title>[Flutter] Google Login 오류 - google_sign_in 패키지, unauthorized_client</title>
      <link>https://snowflower19.tistory.com/302</link>
      <description>&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;문제상황&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;63&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0voQV/btsIeGYqnEL/uTJmy83n66OKupntBZjY41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0voQV/btsIeGYqnEL/uTJmy83n66OKupntBZjY41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0voQV/btsIeGYqnEL/uTJmy83n66OKupntBZjY41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0voQV%2FbtsIeGYqnEL%2FuTJmy83n66OKupntBZjY41%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;1083&quot; height=&quot;63&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;63&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase Authentication을 사용하지 않고, 패키지를 통해 백엔드 서버와의 HTTP 요청을 직접 처리하여 Google Login을 구현하고 있었다. Dio와 &lt;a href=&quot;https://pub.dev/packages/google_sign_in&quot;&gt;google_sign_in 패키지&lt;/a&gt;를 사용하고 있었고(24.02 기준 최신 버전), 서버로 /auth/google/callback 요청 시 위와 같은 unauthorized_client 오류를 반환했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;원인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허용하지 않은 리다이렉션 url로부터 요청. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;오류 메시지는 결국 서버에서 Google login을 시도했을 때 반환된 오류 메시지.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 API 주소( 나의 경우, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;/auth/google/callback&lt;span&gt; &lt;/span&gt;&lt;/span&gt;)를 리다이렉션 url로 등록한 Web Client ID 사용&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림5.png&quot; data-origin-width=&quot;2740&quot; data-origin-height=&quot;2179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c58izM/btsIeBQmTJc/9HHsZIDVDY7p2Li8ul5DWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c58izM/btsIeBQmTJc/9HHsZIDVDY7p2Li8ul5DWK/img.png&quot; data-alt=&quot;박스친 부분에 URL로 등록해주기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c58izM/btsIeBQmTJc/9HHsZIDVDY7p2Li8ul5DWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc58izM%2FbtsIeBQmTJc%2F9HHsZIDVDY7p2Li8ul5DWK%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;450&quot; height=&quot;358&quot; data-filename=&quot;그림5.png&quot; data-origin-width=&quot;2740&quot; data-origin-height=&quot;2179&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;박스친 부분에 URL로 등록해주기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;디버깅 과정&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;4641&quot; data-origin-height=&quot;4305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQa6di/btsId4yKrve/u2YRHLczGn3NFEt4FNBPBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQa6di/btsId4yKrve/u2YRHLczGn3NFEt4FNBPBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQa6di/btsId4yKrve/u2YRHLczGn3NFEt4FNBPBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQa6di%2FbtsId4yKrve%2Fu2YRHLczGn3NFEt4FNBPBk%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;538&quot; height=&quot;4305&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;4641&quot; data-origin-height=&quot;4305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전반적인 로그인 흐름은 위와 같았다. flutter 패키지 google_sign_in 에서 오류가 발생한 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 보안을 이유로 패키지를 통해 사용자 정보를 받아오지 않고, 서버를 통해 로그인한 후 토큰만 받아와 저장하는 방식을 선택했다.&amp;nbsp;( *이러한 방식을 권한 부여 승인 코드 방식(Authorization Code Grant)라고 한다. &lt;a href=&quot;https://charming-kyu.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; 개념 참고 링크&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 메세지 구글링 후 아래 링크를 통해 인사이트를 얻었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1719425096913&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;&amp;quot;error&amp;quot;: &amp;quot;unauthorized_client&amp;quot;&quot; data-og-description=&quot;I am testing what will be a web app auth flow. I have created an MCC account, and also have a google developer client ID and secret. &amp;nbsp;I go through the web app flow that I mimic via the Google Sandbox token process. From this process, I am able to get a re&quot; data-og-host=&quot;groups.google.com&quot; data-og-source-url=&quot;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&quot; data-og-url=&quot;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://groups.google.com/g/adwords-api/c/Mfe_H7cs9q8&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;&quot;error&quot;: &quot;unauthorized_client&quot;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I am testing what will be a web app auth flow. I have created an MCC account, and also have a google developer client ID and secret. &amp;nbsp;I go through the web app flow that I mimic via the Google Sandbox token process. From this process, I am able to get a re&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;groups.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo8TSX/btsIejI8P5D/lnV0dQdTtBnRHNnD6Jmx0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo8TSX/btsIejI8P5D/lnV0dQdTtBnRHNnD6Jmx0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo8TSX/btsIejI8P5D/lnV0dQdTtBnRHNnD6Jmx0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo8TSX%2FbtsIejI8P5D%2FlnV0dQdTtBnRHNnD6Jmx0k%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;597&quot; height=&quot;377&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;401이 클라이언트 오류임을 알았으니, 오류 메세지 그대로 클라이언트가 &lt;b&gt;인증되지 않은 사용자&lt;/b&gt;여서 문제가 생겼음을 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS에서는 client ID와 web client ID 둘 다 동작하지만, Android에서는 web client만 동작했다.&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;우선 웹이 아닌데 웹 클라이언트 ID를 인자로 함수를 호출해야 하는 이유는 잘 모르겠지만, 사용중인 플러터 패키지가 요구한 부분이라 우선 따랐달까.. 이게 바로 생태계가 미흡한 Flutter의 약점이 아닐까 싶다. ㅎ&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;잠깐, 패키지의 Web Client ID 사용에 대해 ...&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;b&gt;백엔드 서버를 나타내는 OAuth 2.0 클라이언트 ID를 가져와야 하기 때문이었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2019년에 iOS에서는 idtoken이 발급되는데, Android에서는 안된다는 이슈가 올라왔다. Firebase가 없어서 그렇다는 답변이 대다수였는데, 해당 글에 아주 최근 Firebase 없이도 해결하는 방법을 적은 댓글 발견하여 첨부해본다.&lt;/p&gt;
&lt;figure id=&quot;og_1719424780005&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Request ID token option for google_sign_in plugin &amp;middot; Issue #20903 &amp;middot; flutter/flutter&quot; data-og-description=&quot;With google_sign_in v3.0.4, there is no option to request an ID token. On iOS, the ID token is always returned, but on Android it is null unless the builder option is provided. Here is the document...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/flutter/flutter/issues/20903#issuecomment-1433172720&quot; data-og-url=&quot;https://github.com/flutter/flutter/issues/20903&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CmOE0/hyWrTbAlN1/bzyRTCVyfKTVrq8b2n19kK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1018_142_1061_188&quot;&gt;&lt;a href=&quot;https://github.com/flutter/flutter/issues/20903#issuecomment-1433172720&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/flutter/flutter/issues/20903#issuecomment-1433172720&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CmOE0/hyWrTbAlN1/bzyRTCVyfKTVrq8b2n19kK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1018_142_1061_188');&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;Request ID token option for google_sign_in plugin &amp;middot; Issue #20903 &amp;middot; flutter/flutter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;With google_sign_in v3.0.4, there is no option to request an ID token. On iOS, the ID token is always returned, but on Android it is null unless the builder option is provided. Here is the document...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 GCP에 웹 클라이언트를 등록하고, 프로젝트에서 android 파일에 웹 클라이언트 ID를 추가하라는 내용이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱이&amp;nbsp;&lt;a href=&quot;https://developers.google.com/identity/sign-in/android/backend-auth?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드 서버로 인증&lt;/a&gt;하거나&amp;nbsp;&lt;a href=&quot;https://developers.google.com/identity/sign-in/android/offline-access?hl=ko&quot;&gt;백엔드 서버에서 Google API에 액세스&lt;/a&gt;하는 경우 &lt;b&gt;백엔드 서버를 나타내는 OAuth 2.0 클라이언트 ID를 가져와야 하기 때문&lt;/b&gt;이란다. (아래 공식문서 참고)&lt;/p&gt;
&lt;figure id=&quot;og_1719424766352&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;Android 인증 관리자 API &amp;nbsp;|&amp;nbsp; Authentication &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 Android 인증 관리자 API 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 그림 1: 패스키, 비&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/identity/android-credential-manager?hl=ko#get_your_backend_servers_oauth_20_client_id&quot; data-og-url=&quot;https://developers.google.com/identity/android-credential-manager?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2HTzr/hyWrMXQmMa/kxOm9sfCdgDCEofkQAFqs0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://developers.google.com/identity/android-credential-manager?hl=ko#get_your_backend_servers_oauth_20_client_id&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/identity/android-credential-manager?hl=ko#get_your_backend_servers_oauth_20_client_id&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2HTzr/hyWrMXQmMa/kxOm9sfCdgDCEofkQAFqs0/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;Android 인증 관리자 API &amp;nbsp;|&amp;nbsp; Authentication &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 Android 인증 관리자 API 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 그림 1: 패스키, 비&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;다시 로그인 오류 해결로 ...&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 ID에 대해 생각하다 문득 생각난 것은, GCP에 등록해둔 web 클라이언트 ID가 2개라는 것이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FdbEn/btsIejh4IQg/EatllpbJDLckAiuXR8wQlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FdbEn/btsIejh4IQg/EatllpbJDLckAiuXR8wQlk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;264&quot; data-filename=&quot;Untitled (5).png&quot; style=&quot;width: 59.5373%; margin-right: 10px;&quot; data-widthpercent=&quot;60.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FdbEn/btsIejh4IQg/EatllpbJDLckAiuXR8wQlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFdbEn%2FbtsIejh4IQg%2FEatllpbJDLckAiuXR8wQlk%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;631&quot; height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyPWev/btsIcBR79ce/aU8QV9FNSvDTtorfm10p4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyPWev/btsIcBR79ce/aU8QV9FNSvDTtorfm10p4k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;806&quot; data-filename=&quot;Untitled (7).png&quot; style=&quot;width: 18.0485%; margin-right: 10px;&quot; data-widthpercent=&quot;18.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyPWev/btsIcBR79ce/aU8QV9FNSvDTtorfm10p4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyPWev%2FbtsIcBR79ce%2FaU8QV9FNSvDTtorfm10p4k%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;806&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGGyE/btsIeAjCVCC/U2KwWce6TpK1k0uTiWrEY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGGyE/btsIeAjCVCC/U2KwWce6TpK1k0uTiWrEY0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1996&quot; data-origin-height=&quot;2475&quot; data-filename=&quot;그림3.png&quot; style=&quot;width: 20.0886%;&quot; data-widthpercent=&quot;20.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGGyE/btsIeAjCVCC/U2KwWce6TpK1k0uTiWrEY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGGyE%2FbtsIeAjCVCC%2FU2KwWce6TpK1k0uTiWrEY0%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;1996&quot; height=&quot;2475&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 사진은 firebase에 프로젝트를 등록하면서 자동으로 생긴 것 같고, 중간 사진은 서버 친구가 필요에 의해 따로 만들었던 것이다. Flutter 프로젝트에 등록하고 사용중이던 client ID는 GCP에 자동으로 생겼던 client ID였다(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;Spring 서버로부터 생성된 client ID와 다른 점은 &lt;b&gt;Redirect URLs&lt;/b&gt;였다. 검색 후 서버가 만든 web client ID로 로그인을 했더니 오류 메시지 없이 로그인이 해결되었다  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림2.png&quot; data-origin-width=&quot;3672&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqSg3N/btsIe1nH3Cd/Kl5rfMHIlgiuNHF7KuMTxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqSg3N/btsIe1nH3Cd/Kl5rfMHIlgiuNHF7KuMTxK/img.png&quot; data-alt=&quot;로그인 성공 후 반환한 log&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqSg3N/btsIe1nH3Cd/Kl5rfMHIlgiuNHF7KuMTxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqSg3N%2FbtsIe1nH3Cd%2FKl5rfMHIlgiuNHF7KuMTxK%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;3672&quot; height=&quot;385&quot; data-filename=&quot;그림2.png&quot; data-origin-width=&quot;3672&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인 성공 후 반환한 log&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 구글 로그인(플러터에서 패키지 활용해서 구글로 로그인 요청하는 로그인) 과정에서 함수가 반환하는 값(=구글이 인증 성공해서 반환한 값)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리해본 로그인 흐름은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨&amp;nbsp;뒤에&amp;nbsp;serverAuthCode를&amp;nbsp;우리&amp;nbsp;서버의&amp;nbsp;api에&amp;nbsp;요청하면서&amp;nbsp;보내주어야&amp;nbsp;함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 서버가 그 인증 코드와 함께 (다시 한 번) 구글에게 로그인 요청&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 인증 코드 가지고 있으니 정상적으로 유저의 정보 반환&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;이다. 구글은 GCP에 등록된 &lt;b&gt;허용한 클라이언트로부터, 허용한 리다이렉션 url 요청에 대해서만 인증&lt;/b&gt;을 해주기 때문에 Unauthorized 오류를 뱉은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허용한 클라이언트(GCP에 등록함)였지만, 허용하지 않은 리다이렉션 url로부터 요청이 왔기 때문이다.&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;OAuth 2.0 개념과 구현 방식 모두 이해하기 쉽지 않아서 더 오래 걸렸던 것 같은 디버깅 일지 끗&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 Flutter에서 google_sign_in 패키지를 활용해 로그인 구현 중, 오류 나는 부분이 있다면 댓글 혹은 이메일로 연락주시면 최대한 도움 드리겠습니다 -  &lt;/p&gt;</description>
      <category>App/Flutter</category>
      <category>Flutter</category>
      <category>flutter google login</category>
      <category>flutter google login unauthorized_client</category>
      <category>flutter google_sign_in</category>
      <category>Flutter 구글 로그인</category>
      <category>Google Login</category>
      <category>google_sign_in error</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/302</guid>
      <comments>https://snowflower19.tistory.com/302#entry302comment</comments>
      <pubDate>Thu, 27 Jun 2024 03:07:52 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 26일차 TIL - 스택/큐, Baseball Game</title>
      <link>https://snowflower19.tistory.com/297</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[26일차] 스택/큐&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/baseball-game/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/baseball-game/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bALHiO/btsIbHYcWEm/dhaxJjo8OXi0JKWIhlylj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bALHiO/btsIbHYcWEm/dhaxJjo8OXi0JKWIhlylj1/img.png&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;723&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.6885%; margin-right: 10px;&quot; data-widthpercent=&quot;46.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bALHiO/btsIbHYcWEm/dhaxJjo8OXi0JKWIhlylj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbALHiO%2FbtsIbHYcWEm%2FdhaxJjo8OXi0JKWIhlylj1%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;909&quot; height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEYqEu/btsIcSEuzK4/kRkhlOGDIWiXD1fsZbd4pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEYqEu/btsIcSEuzK4/kRkhlOGDIWiXD1fsZbd4pK/img.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;681&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.1488%;&quot; data-widthpercent=&quot;53.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEYqEu/btsIcSEuzK4/kRkhlOGDIWiXD1fsZbd4pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEYqEu%2FbtsIcSEuzK4%2FkRkhlOGDIWiXD1fsZbd4pK%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;996&quot; height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: 이상한 규칙을 가진 야구에서 점수를 유지하려고 한다. 경기는 빈 기록으로 시작된다. 문자열 배열 operations가 주어진다. operations[i]는 기록에 반드시 적용해야 하는 i번째 연산이다. 연산은 다음 중 하나이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정수 x : 새로운 점수 x를 기록한다.&lt;/li&gt;
&lt;li&gt;+ : 이전 두 번의 점수를 더해서 새로운 점수로 기록한다.&lt;/li&gt;
&lt;li&gt;D : 이전 점수의 두 배를 새로운 점수로 기록한다.&lt;/li&gt;
&lt;li&gt;C : 이전 점수를 무효화시키고, 기록에서 지운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 연산들을 적용한 후의 점수의 합을 반환하라.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 그대로 연산자들을 차례대로 구현해주면 되는 문제이다. 가장 최근의 원소를 제거할 때는 pop 연산을 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒷 원소에 접근하고 싶다면, 음수 인덱스를 활용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1719297037320&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def calPoints(self, operations: List[str]) -&amp;gt; int:
        score = []

        for i in operations:
            if i == '+':
                p1 = score[-1]
                p2 = score[-2]
                score.append(p1+p2)
            elif i == 'D':
                p1 = score[-1]
                score.append(p1 * 2)
            elif i == 'C':
                score.pop()
            else:
                score.append(int(i))
            
        ans = 0
        for i in score:
            ans += i

        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 pop remove del 연산의 차이는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;remove : 삭제하려는 항목이 리스트에 없는 경우 ValueError 반환&lt;/li&gt;
&lt;li&gt;pop : 맨 뒤의 원소 혹은 인덱스로 원하는 요소 제거 후 반환&lt;/li&gt;
&lt;li&gt;del: 인덱스로 원하는 요소 제거, 요소 반환 X. 인덱스 슬라이싱 활용 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hgk5722.tistory.com/286&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hgk5722.tistory.com/286&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1719298526853&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;[Python] 파이썬 remove(), pop(), del 차이점&quot; data-og-description=&quot;파이썬은 리스트에서 요소를 삭제하는 3가지 방법을 지원합니다.&amp;nbsp;remove, pop, del이 그것입니다.&amp;nbsp;비슷하지만 조금씩 다른 3가지 함수에 대해 알아보겠습니다.&amp;nbsp;&amp;nbsp;1. remove()&amp;nbsp;&amp;nbsp;remove()는 파이썬 리스트&quot; data-og-host=&quot;hgk5722.tistory.com&quot; data-og-source-url=&quot;https://hgk5722.tistory.com/286&quot; data-og-url=&quot;https://hgk5722.tistory.com/286&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhl4nf/hyWoNw6rYU/mSwKFVGKOBl02N7iETiwW0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bNIIXW/hyWrOHS9oi/ECGThgO6t6ygT5Srd8mKLk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://hgk5722.tistory.com/286&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hgk5722.tistory.com/286&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhl4nf/hyWoNw6rYU/mSwKFVGKOBl02N7iETiwW0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bNIIXW/hyWrOHS9oi/ECGThgO6t6ygT5Srd8mKLk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;[Python] 파이썬 remove(), pop(), del 차이점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;파이썬은 리스트에서 요소를 삭제하는 3가지 방법을 지원합니다.&amp;nbsp;remove, pop, del이 그것입니다.&amp;nbsp;비슷하지만 조금씩 다른 3가지 함수에 대해 알아보겠습니다.&amp;nbsp;&amp;nbsp;1. remove()&amp;nbsp;&amp;nbsp;remove()는 파이썬 리스트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hgk5722.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬</category>
      <category>파이썬 스택</category>
      <category>파이썬 코테</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/297</guid>
      <comments>https://snowflower19.tistory.com/297#entry297comment</comments>
      <pubDate>Tue, 25 Jun 2024 15:55:36 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 25일차 TIL - 스택/큐, Removing Stars From a String</title>
      <link>https://snowflower19.tistory.com/296</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[25일차 미들러] 스택/큐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/removing-stars-from-a-string/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/removing-stars-from-a-string/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu6lRv/btsIaSSWMQt/MVVdtklsgFuIEjHbLronWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu6lRv/btsIaSSWMQt/MVVdtklsgFuIEjHbLronWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu6lRv/btsIaSSWMQt/MVVdtklsgFuIEjHbLronWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu6lRv%2FbtsIaSSWMQt%2FMVVdtklsgFuIEjHbLronWk%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;768&quot; height=&quot;721&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: *을 포함하는 문자열 s가 주어진다. 한 동작에서 s에서 *을 하나 선택하고, 그 별의 왼쪽에서 가장 가까운 별이 아닌 문자열을 제거할 수 있다. 별 자신도 제거할 수 있다. 모든 별이 제거된 후의 문자열을 반환하라.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값은 항상 연산 가능하도록 주어진다. 결과 문자열은 항상 고유하게 나타난다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문으로 문자열 s를 순회하면서, 스택에 문자를 추가해주는 방식으로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자가 *인 경우, 스택의 맨 위 요소를 제거한다. 일반 문자인 경우, 스택에 추가한다. 이 스택에는 *이 아닌 문자열, 곧 결과만 저장되는 셈이다.&lt;/p&gt;
&lt;pre id=&quot;code_1719233990106&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def removeStars(self, s: str) -&amp;gt; str:
        stack = []
        
        for char in s:
            if char == '*':
                if stack:
                    stack.pop()
            else:
                stack.append(char)
        
        return ''.join(stack)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 할까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트를 문자열로 바꿀 때는 ''.join(리스트)&lt;/li&gt;
&lt;li&gt;답을 저장하기 위한 &lt;b&gt;변수를 따로 두면&lt;/b&gt;&amp;nbsp;문제가 간단하게 풀릴 때가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;오답노트&lt;/u&gt;.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;while 문을 사용할 때는 종료 조건을 잘 정하는 것이 중요하다. while문과 인덱스를 사용하는 것은 for문을 사용하는 것과 비슷하므로 종료 조건이 헷갈릴 때는 for문을 사용하자.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;while문을 통해 전체 문자열을 순회할 생각이었는데, pop할 때마다 인덱스를 조정해주지 않으면 IndexError가 발생할 수 있다. (예를 들어, 맨 마지막/앞이었을 때 등)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;또한 중간 위치의 원소를 pop(또는 remove)으로 제거하면, 제거된 원소 뒤에 위치한 모든 원소들을 전부 1칸씩 당겨와야 하므로 &lt;b&gt;시간 복잡도가 O(N)&lt;/b&gt;이 된다.&lt;/li&gt;
&lt;li&gt;그러므로, 위의 solution처럼, 따로 스택 변수를 두어 맨 앞의 원소를 pop / push 하는 것이 좋다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1719233852230&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def removeStars(self, s: str) -&amp;gt; str:

        d = list(s)
        i = 0

        while 0 &amp;lt;= i &amp;lt; len(d):
            if d[i] == '*':
                d.pop(i-1) # 일반 문자 제거
                d.pop(i-1) # * 제거
                i -= 1  # 두 문자열이 제거 되었으므로 다시 검사
            else:
                i += 1

        return ''.join(d)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>스택</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 스택</category>
      <category>파이썬 코테</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/296</guid>
      <comments>https://snowflower19.tistory.com/296#entry296comment</comments>
      <pubDate>Mon, 24 Jun 2024 23:18:31 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 24일차 TIL - 스택/큐, Number of Recent Calls</title>
      <link>https://snowflower19.tistory.com/295</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[24일차] 스택/큐&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/number-of-recent-calls/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/number-of-recent-calls/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBfAIL/btsH8nzI1uP/3UBjNeKJji6BoZO7F2Kiz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBfAIL/btsH8nzI1uP/3UBjNeKJji6BoZO7F2Kiz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBfAIL/btsH8nzI1uP/3UBjNeKJji6BoZO7F2Kiz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBfAIL%2FbtsH8nzI1uP%2F3UBjNeKJji6BoZO7F2Kiz0%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;1018&quot; height=&quot;788&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;* 번역:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;여기 RecentCounter 클래스가 있다. 이 클래스는 특정 시간 프레임 내에서 최근 요청의 수를 세는 기능을 제공한다. 클래스를 정의하여라.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RecentCounter(): 초기화 메서드. 최근 요청 수를 0으로 초기화한다.&lt;/li&gt;
&lt;li&gt;int ping(int t): 시간 t에서의 새로운 요청을 추가하고, [t - 3000, t] 시간 범위 내의 요청 수를 반환힌다. 즉, 최근 3000 밀리초 동안 발생한 요청 수를 반환한다. 반환된 요청 수에는 새로 추가된 요청도 포함된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 호출보다 엄격히 큰 시간 t를 사용하도록 보장된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;pre id=&quot;code_1719189751803&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RecentCounter:

    def __init__(self):
        self.r = []
        

    def ping(self, t: int) -&amp;gt; int:
        ans = 0

        self.r.append(t)
        
        start = t-3000
        end = t

        for i in self.r:
            if start &amp;lt;= i &amp;lt;= end:
                ans += 1
        
        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 멤버 변수 선언 시에도 self를 붙여주어야 한다.&lt;/li&gt;
&lt;li&gt;범위를 비교할 때는 in 이 아니라 부등호를 사용하면 된다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오답노트. 두 번째 나다어 포인트. 범위 비교를 잘못 접근해서 시간초과 판정을 받았다.&lt;/p&gt;
&lt;pre id=&quot;code_1719190253398&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RecentCounter:

    def __init__(self):
        self.r = []
        

    def ping(self, t: int) -&amp;gt; int:
        ans = 0

        self.r.append(t)
        
        start = t-3000
        end = t

        for i in self.r:
            if i in range(start, end+1):
                ans += 1
        
        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 range()를 쓸 것이 아니라 간단하게 &lt;code&gt;if start &amp;lt;= i &amp;lt;= end&lt;/code&gt; 를 써주면 될 일.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 스택</category>
      <category>파이썬 큐</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/295</guid>
      <comments>https://snowflower19.tistory.com/295#entry295comment</comments>
      <pubDate>Mon, 24 Jun 2024 09:53:48 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 23일차 TIL - 스택/큐, Number of Students Unable to Eat Lunch</title>
      <link>https://snowflower19.tistory.com/294</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[23일차] 스택/큐&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/number-of-students-unable-to-eat-lunch/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/number-of-students-unable-to-eat-lunch/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1621&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2ro7K/btsH7VwxSb0/tkahFUfk3CHA1joAlsTIb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2ro7K/btsH7VwxSb0/tkahFUfk3CHA1joAlsTIb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2ro7K/btsH7VwxSb0/tkahFUfk3CHA1joAlsTIb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2ro7K%2FbtsH7VwxSb0%2FtkahFUfk3CHA1joAlsTIb1%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;1621&quot; height=&quot;812&quot; data-origin-width=&quot;1621&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: 학교&amp;nbsp;카페테리아는&amp;nbsp;점심시간에&amp;nbsp;원형과&amp;nbsp;정사각형&amp;nbsp;샌드위치를&amp;nbsp;제공합니다.&amp;nbsp;이&amp;nbsp;샌드위치는&amp;nbsp;각각&amp;nbsp;0번과&amp;nbsp;1번으로&amp;nbsp;구분됩니다.&amp;nbsp;모든&amp;nbsp;학생들은&amp;nbsp;줄을&amp;nbsp;서&amp;nbsp;있으며,&amp;nbsp;각&amp;nbsp;학생은&amp;nbsp;원형&amp;nbsp;샌드위치나&amp;nbsp;정사각형&amp;nbsp;샌드위치를&amp;nbsp;선호합니다. &lt;br /&gt;&lt;br /&gt;샌드위치의&amp;nbsp;수는&amp;nbsp;학생&amp;nbsp;수와&amp;nbsp;같습니다.&amp;nbsp;샌드위치는&amp;nbsp;스택에&amp;nbsp;쌓여&amp;nbsp;있습니다.&amp;nbsp;각&amp;nbsp;단계에서&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;일이&amp;nbsp;일어납니다: &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;줄의&amp;nbsp;맨&amp;nbsp;앞에&amp;nbsp;있는&amp;nbsp;학생이&amp;nbsp;스택&amp;nbsp;맨&amp;nbsp;위에&amp;nbsp;있는&amp;nbsp;샌드위치를&amp;nbsp;선호하면,&amp;nbsp;그&amp;nbsp;샌드위치를&amp;nbsp;가져가서&amp;nbsp;줄을&amp;nbsp;떠납니다. &lt;br /&gt;2.&amp;nbsp;그렇지&amp;nbsp;않으면,&amp;nbsp;그&amp;nbsp;학생은&amp;nbsp;샌드위치를&amp;nbsp;가져가지&amp;nbsp;않고&amp;nbsp;줄의&amp;nbsp;끝으로&amp;nbsp;갑니다. &lt;br /&gt;3.&amp;nbsp;이런&amp;nbsp;과정이&amp;nbsp;반복되다가&amp;nbsp;더&amp;nbsp;이상&amp;nbsp;줄에&amp;nbsp;있는&amp;nbsp;학생들이&amp;nbsp;스택&amp;nbsp;맨&amp;nbsp;위의&amp;nbsp;샌드위치를&amp;nbsp;원하지&amp;nbsp;않게&amp;nbsp;되면,&amp;nbsp;그들은&amp;nbsp;샌드위치를&amp;nbsp;먹을&amp;nbsp;수&amp;nbsp;없게&amp;nbsp;됩니다. &lt;br /&gt;&lt;br /&gt;두&amp;nbsp;개의&amp;nbsp;정수&amp;nbsp;배열&amp;nbsp;students와&amp;nbsp;sandwiches가&amp;nbsp;주어집니다.&amp;nbsp;sandwiches[i]는&amp;nbsp;스택에서&amp;nbsp;i번째&amp;nbsp;샌드위치의&amp;nbsp;종류를&amp;nbsp;나타내며&amp;nbsp;(i&amp;nbsp;=&amp;nbsp;0은&amp;nbsp;스택의&amp;nbsp;맨&amp;nbsp;위에&amp;nbsp;있는&amp;nbsp;샌드위치입니다),&amp;nbsp;students[j]는&amp;nbsp;초기&amp;nbsp;줄에서&amp;nbsp;j번째&amp;nbsp;학생의&amp;nbsp;선호도를&amp;nbsp;나타냅니다&amp;nbsp;(j&amp;nbsp;=&amp;nbsp;0은&amp;nbsp;줄의&amp;nbsp;맨&amp;nbsp;앞에&amp;nbsp;있는&amp;nbsp;학생입니다).&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;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;My Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문을 통해 count변수를 세어주었다. 모두가 먹지 않는 이상, 즉 &lt;code&gt;students&lt;/code&gt; 큐가 비지 않는 이상 반복하도록 &lt;code&gt;while&lt;/code&gt;문을 짰다. 1회 반복에서는 가장 앞에 있는 학생 한 명씩 검사하고, 못 먹으면 맨 앞 학생이 다시 맨 뒤로 가야 하므로 pop하여, 다시 push(&lt;code&gt;append()&lt;/code&gt;)하고 count 변수를 1 증가 시킨다.&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;첫 번째 원소를 꺼내는 pop(0)과 맨 뒤에 덧붙이는 append를 효율적으로 처리할 수 있는 deque 자료형을 사용하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1719155182965&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

class Solution:
    def countStudents(self, students: List[int], sandwiches: List[int]) -&amp;gt; int:
        students = deque(students)
        sandwiches = deque(sandwiches)
        count = 0 # 못 먹는 사람 수

        while students:
            if students[0] == sandwiches[0]:
                students.popleft()
                sandwiches.popleft()
                count = 0 # 학생이 샌드위치 가져가면 리셋
            else:
                students.append(students.popleft())
                count += 1
            
            # 모두가 못 먹으면
            if count == len(students):
                return len(students)
        
        return 0&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 문자 그대로 구현하려고 해서 위의 코드는 꽤 복잡했다. 그러나 문제에서 원하는 답을 내기 위해서는 아래와 같이 단순히 해결도 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1719073382311&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def countStu(stu,sand):
	# 샌드위치에 원소가 남았다면
    while sand:
		# 맨 앞 샌드위치가 학생 목록 중 있다면
        if sand[0] in stu:
			# 학생 목록에서 원소 제거
            stu.remove(sand[0])
			# 샌드위치 목록에서 맨 앞 원소 제거
            sand.pop(0)
		# 첫 원소가 학생 목록에 없다면 (남은 학생 아무도 원하지 않음) 반복 종료
        else:break
	# 학생들이 먹을 수 있는 모든 샌드위치를 없애고 나서 남은 샌드위치의 길이가
    # 곧 먹을 수 없는 샌드위치의 갯수다.
    return len(sand)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 앞의 샌드위치 (sandwiches[0])를 원하는 학생들이 있는지 검사하고, 있다면 그 학생을 제거한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;오답노트&lt;/u&gt;. 초기에 작성했던 코드는 아래와 같았다. 계속해서 순회하되, 모두가 가져갔거나 현재(남은) 인원 모두가 원하는 샌드위치를 먹지 못할 경우 return으로 반복문을 종료했다. 현재 사람이 원하는 샌드위치를 가졌을 때는 pop() 메서드를 사용했기 때문에 리스트(스택)에서 자동으로 없어진다.&lt;/p&gt;
&lt;pre id=&quot;code_1719155022399&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def countStudents(self, students: List[int], sandwiches: List[int]) -&amp;gt; int:
        while True:
            if len(students) == 0:
                return 0
            
            stu = students.pop(0)
            sand = sandwiches.pop(0)
            count = 0 # 못 먹는 사람 수

            for i in students:
                if i not in sandwiches:
                    count += 1
                
        if count == len(students):  # 현재 인원 전부 다 못 먹으면
                return count

            if stu != sand:
                students.append(stu)
                sandwiches.insert(0, sand)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 문제점은 다음과 같고, 해결한 답이 글 위쪽의 My Solution 부분이다.&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;rarr; 그런데 검사를 위해 while문 안에서 for문을 중첩하여, 불필요하게 반복마다 전체를 다 검사하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 해답 코드에서는 학생 수를 세는 &lt;b&gt;count 변수와 현재 남은 학생 수를 비교&lt;/b&gt;하며 종료 조건을 구현했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복문 사용 시, 어떤 내용이 반복적으로 수행되어야 하는지를 최대한 간단하게 생각하자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 스택</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/294</guid>
      <comments>https://snowflower19.tistory.com/294#entry294comment</comments>
      <pubDate>Sun, 23 Jun 2024 01:26:19 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 22일차 TIL - 정렬, Find Target Indices After Sorting Array</title>
      <link>https://snowflower19.tistory.com/293</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[22일차] 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/find-target-indices-after-sorting-array/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/find-target-indices-after-sorting-array/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;787&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQiL9I/btsH7x3wUx1/V9xxZyqn3yIlwap7b6squk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQiL9I/btsH7x3wUx1/V9xxZyqn3yIlwap7b6squk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQiL9I/btsH7x3wUx1/V9xxZyqn3yIlwap7b6squk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQiL9I%2FbtsH7x3wUx1%2FV9xxZyqn3yIlwap7b6squk%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;1471&quot; height=&quot;787&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;787&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역:&amp;nbsp;0부터 시작하는 인덱스를 가진 정수 배열 nums와 목표 요소 target가 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 인덱스란 nums[i] == target이 되는 인덱스 i를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nums를 비내림차순(오름차순)으로 정렬한 후 목표 인덱스의 리스트를 반환하라. 만약 목표 인덱스가 없다면 빈 리스트를 반환하세요. 반환된 리스트는 오름차순으로 정렬되어 있어야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 메소드 sort()를 활용하면 쉽게 해결할 수 있다. 그러나 해당 메소드의 시간 복잡도는 O(nlogn)이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719020398192&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def targetIndices(self, nums: List[int], target: int) -&amp;gt; List[int]:
        ans = []
        nums.sort()

        for i in range(len(nums)):
            if nums[i] == target:
                ans.append(i)

        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Another Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 복잡도 O(n)인 풀이는 다음과 같다. 정렬 메소드를 사용하지 않고 순회를 통해서도 문제를 해결할 수 있다. 실제로 정렬된 배열을 만들어 인덱스를 '찾기'보다, 오름차순 정렬이라는 개념을 활용하여 인덱스를 '계산'하는 방식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1719020592626&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def targetIndices(self, nums: List[int], target: int) -&amp;gt; List[int]:
        lessThanEqual = 0
        onlyLess = 0
        for i in nums:
            if i &amp;lt;= target:
                lessThanEqual += 1
            if i &amp;lt; target:
                onlyLess += 1
        return list(range(onlyLess, lessThanEqual))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 할까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 복잡도가 중요한 응용 문제가 될 경우, 문제에서 요구하는 것을 &quot;그대로&quot; 구현하는 방법보다 문제가 요구하는 결과를 낼 수 있는 방법이 무엇인지 생각해보자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬</category>
      <category>파이썬 정렬</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/293</guid>
      <comments>https://snowflower19.tistory.com/293#entry293comment</comments>
      <pubDate>Sat, 22 Jun 2024 10:46:22 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 21일차 TIL - 정렬, Top K Frequent Elements</title>
      <link>https://snowflower19.tistory.com/292</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[21일차] 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/top-k-frequent-elements/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/top-k-frequent-elements/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2kGtv/btsH6zFStpl/U5Mk5l4M67Eb0Nk7MhM2fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2kGtv/btsH6zFStpl/U5Mk5l4M67Eb0Nk7MhM2fK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2kGtv/btsH6zFStpl/U5Mk5l4M67Eb0Nk7MhM2fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2kGtv%2FbtsH6zFStpl%2FU5Mk5l4M67Eb0Nk7MhM2fK%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;987&quot; height=&quot;687&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;687&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: 정수 배열 nums와 정수 k가 주어질 때, 가장 자주 등장하는 원소들 k개를 반환하라. 반환할 답은 순서대로여야 한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;My Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열을 순회하며 등장 횟수를 세고, 가장 많이 등장한 k개 원소를 출력하는 방식을 그대로 코드에 적용했다.&lt;/p&gt;
&lt;pre id=&quot;code_1718875230184&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def topKFrequent(self, nums: List[int], k: int) -&amp;gt; List[int]:
        ans = []
        key = set(nums)
        dic = {i: 0 for i in key}

        for i in nums:
            dic[i] += 1

        # value값을 기준으로 내림차순 정렬
        sorted_items = sorted(dic.items(), key=lambda x: x[1], reverse=True)

        # 빈출 원소 저장
        for i in range(k):
            ans.append(sorted_items[i][0])

        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 흐름 및 내용은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 nums 배열이 어떤 정수를 포함하고 있는지, set 메서드를 통해 &lt;b&gt;중복을 제거&lt;/b&gt;한 집합 자료형을 만들어 key값으로 쓸 리스트를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1718875491315&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;key = set(nums)
dic = {i: 0 for i in key}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 거치면 &lt;code&gt;dic = {1: 0, 2: 0, 4:0}&lt;/code&gt; 와 같이 value는 0으로 초기화 된 사전형 변수를 얻게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 등장한 원소를 반환해야 하므로, 등장 횟수를 의미하는 value를 기준으로 정렬을 수행해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전 자료형은 sort 메서드가 없고, 사전의 요소들을 &lt;b&gt;튜플 형태로 바꾸어서 리스트 자료형에 저장&lt;/b&gt;해주면 sort 메서드를 사용할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1718875573314&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# value값을 기준으로 정렬
sorted_items = sorted(dic.items(), key=lambda x: x[1], reverse=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;sorted_items&lt;/code&gt; 는 &lt;code&gt;[(1, 3), (2, 2), (3, 1)]&lt;/code&gt; 와 같은 형태로, (숫자, 등장횟수)를 의미하는 튜플을 원소로 가지고, 등장 횟수를 기준으로 내림차순으로 저장된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718876102801&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 빈출 원소 저장
for i in range(k):
    ans.append(sorted_items[i][0])

return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 인덱싱이 가능하므로, 튜플의 첫 원소&lt;code&gt;[i][0]&lt;/code&gt; 를 답으로 출력한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 반복문 외에도 한 줄로 아래와 같이 쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718876454768&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return [x for x,y in sorted_items[0:k]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답에 적지 않은 이유는 Runtime이 반복문을 사용했을 때보다 적지 않은 것 같아서  &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Another Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;collections&lt;/code&gt;모듈의 &lt;code&gt;Counter&lt;/code&gt; 클래스에서 &lt;code&gt;most_common()&lt;/code&gt; 메서드를 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드는 데이터의 개수를 셀 때 유용한 클래스로, key-value 형태의 데이터를 다루는 해시(hash)문제에서 유용하게 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1718896828698&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import Counter

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -&amp;gt; List[int]:
        return list(map(lambda x:x[0], Counter(nums).most_common(k)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Counter 클래스의 most_common 메서드는 정말 이 문제를 위해 만들어졌다고 할 정도로 딱 이 기능을 수행하는 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map함수는 Counter(nums).most_common(k)의 결과로 반환되는 리스트 [(1, 3), (2, 1)] 에서 튜플의 첫 원소만을 반환하기 위해 사용했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;lambda&lt;/b&gt;는 이름 없는 (한 줄의) 함수를 의미하고, lambda 매개변수: 표현식 으로 정의할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;map&lt;/b&gt;함수는 리스트(반복 가능한 객체)의 모든 원소에 원하는 동작을 수행시킬 수 있다.&lt;/li&gt;
&lt;li&gt;collections 모듈의 &lt;b&gt;Counter 클래스&lt;/b&gt;는 데이터의 갯수를 세는 데 사용된다.&lt;/li&gt;
&lt;li&gt;딕셔너리형은 sort() 메서드가 적용 안된다.&lt;/li&gt;
&lt;li&gt;리스트 중복 제거는 dict()가 아닌 set()&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1718874758802&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lst = [1, 1, 1, 2, 2, 3]
unique_lst = list(set(lst))
print(unique_lst)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@eunhye_/python-collections-Counter&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@eunhye_/python-collections-Counter&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718897031541&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;파이썬 collections 모듈의 Counter 사용하기&quot; data-og-description=&quot;데이터의 개수를 셀 때 매우 유용한 파이썬의 collections 모듈의 Counter 클래스Hash와 같이 알고리즘 문제를 풀 때에도 유용하게 사용할 수 있다.collections 모듈의 Counter 클래스는 별도 패키지 설치 없&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@eunhye_/python-collections-Counter&quot; data-og-url=&quot;https://velog.io/@eunhye_/python-collections-Counter&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bG2fFI/hyWoFkyjM1/rPncKHfMphJBkNPrQWmEmk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/FUitK/hyWoPAHRe8/INEHvkrC6DirnCaG4ROLf0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420&quot;&gt;&lt;a href=&quot;https://velog.io/@eunhye_/python-collections-Counter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@eunhye_/python-collections-Counter&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bG2fFI/hyWoFkyjM1/rPncKHfMphJBkNPrQWmEmk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/FUitK/hyWoPAHRe8/INEHvkrC6DirnCaG4ROLf0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420');&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;파이썬 collections 모듈의 Counter 사용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터의 개수를 셀 때 매우 유용한 파이썬의 collections 모듈의 Counter 클래스Hash와 같이 알고리즘 문제를 풀 때에도 유용하게 사용할 수 있다.collections 모듈의 Counter 클래스는 별도 패키지 설치 없&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>정렬</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 코테</category>
      <category>파이썬 코테 정렬</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/292</guid>
      <comments>https://snowflower19.tistory.com/292#entry292comment</comments>
      <pubDate>Fri, 21 Jun 2024 00:25:17 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 20일차 TIL - 문자열, Decode the Message</title>
      <link>https://snowflower19.tistory.com/291</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[20일차] 문자열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/decode-the-message/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/decode-the-message/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btnRQQ/btsH42IsG2s/AgHpdEnqvumD9zlVwyypVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btnRQQ/btsH42IsG2s/AgHpdEnqvumD9zlVwyypVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btnRQQ/btsH42IsG2s/AgHpdEnqvumD9zlVwyypVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtnRQQ%2FbtsH42IsG2s%2FAgHpdEnqvumD9zlVwyypVk%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;996&quot; height=&quot;685&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: 주어진 문자열 key와 message가 있고, 각각 암호키와 비밀 메시지를 나타낸다. message를 해독하는 과정은 다음과 같다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) key에 등장하는 26개 알파벳 소문자의 첫 등장을 대체 표의 순서로 사용한다.&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;3) message의 각 문자는 테이블을 활용해서 대체된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 공백 ' '은 공백으로 변환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 주어진 key = &quot;happy boy&quot;라면(실제 키에는 알파벡의 각 문자가 적어도 한 번은 등장한다), 우리는 ['h'&amp;nbsp;-&amp;gt;&amp;nbsp;'a',&amp;nbsp;'a'&amp;nbsp;-&amp;gt;&amp;nbsp;'b',&amp;nbsp;'p'&amp;nbsp;-&amp;gt;&amp;nbsp;'c',&amp;nbsp;'y'&amp;nbsp;-&amp;gt;&amp;nbsp;'d',&amp;nbsp;'b'&amp;nbsp;-&amp;gt;&amp;nbsp;'e',&amp;nbsp;'o'&amp;nbsp;-&amp;gt;&amp;nbsp;'f']인 대체표를 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;message를 해독한 결과를 반환하라.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알파벳을 문자열, 사전 등을 활용해서 직접 비교하는 방법도 있지만 아스키 코드를 활용해서 깔끔하게 정수 계산으로 문제를 해결할 수도 있다. alph의 인덱스가 곧 알파벳, 값이 key변수값이 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718805429068&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def decodeMessage(self, key: str, message: str) -&amp;gt; str:
        alph = []
        ans = ''

        for i in key:
            if i != ' ' and i not in alph:
                alph.append(i)

        # a : 97
        for i in message:
            if i == ' ':
                ans += ' '
            else:
                ans += chr(alph.index(i) + 97)

        return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alph 배열에 알파벳 순서대로 암호키 문자가 저장되어 있다. 소문자 a의 아스키 코드는 97이므로 인덱스에 97을 더해주면, 문자열의 아스키 코드값이 나온다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718806257945&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ans += chr(alph.index(i) + 97)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Another Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 풀이를 떠올리기 전에는 key가 알파벳, value가 key변수를 차례대로 저장하는 사전형 변수를 사용해야 하나 고민했었다. 아스키 코드로 풀면서 간단해졌지만, 다른 사람들의 풀이를 통해 사전형 변수를 굳이 쓰지 않고 &lt;b&gt;문자열로도 인덱스를&lt;/b&gt; 활용할 수 있음을 배웠다.&lt;/p&gt;
&lt;pre id=&quot;code_1718805556546&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def decodeMessage(self, key: str, message: str) -&amp;gt; str:
        alphabet=&quot;abcdefghijklmnopqrstuvwxyz&quot;
        key= key.replace(&quot; &quot;,&quot;&quot;)
        res=&quot;&quot;
        for i in key:
            if i not in res:
                res+=i
        
        s=&quot;&quot;
        for i in range(len(message)):
            if message[i]==&quot; &quot;:
                s+=&quot; &quot;
            else:
                s+=alphabet[res.index(message[i])]
        return s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 변수를 순회하며 공백을 처리해주지 않고도, replace 함수를 사용하여 공백을 제거할 수 있음을 알게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1718805584083&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;key= key.replace(&quot; &quot;,&quot;&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;.replace(' ', '')&lt;/code&gt;로 한 번에 문자열의 공백을 제거할 수 있다.&lt;/li&gt;
&lt;li&gt;파이썬에서 아스키 코드 관련 함수는 &lt;code&gt;ord(문자)&lt;/code&gt;와 &lt;code&gt;char(숫자)&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;대문자 A의 아스키 코드는 65, 소문자 a의 아스키 코드는 97이다. 이후 알파벳 순서대로 코드도 1씩 증가한다.&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;문자열 붙이기는 &lt;code&gt;append()&lt;/code&gt;가 아닌 + 연산자를 사용해야 한다. 혹은 &lt;code&gt;join()&lt;/code&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;580&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TAA5i/btsH5OioHCv/5dKnOmmQ2QNaKDs0277So0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TAA5i/btsH5OioHCv/5dKnOmmQ2QNaKDs0277So0/img.png&quot; data-alt=&quot;에러 기록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TAA5i/btsH5OioHCv/5dKnOmmQ2QNaKDs0277So0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTAA5i%2FbtsH5OioHCv%2F5dKnOmmQ2QNaKDs0277So0%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;580&quot; height=&quot;219&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;에러 기록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>코테 문자열</category>
      <category>파이썬 문자열</category>
      <category>파이썬 코테 문자열</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/291</guid>
      <comments>https://snowflower19.tistory.com/291#entry291comment</comments>
      <pubDate>Wed, 19 Jun 2024 23:12:33 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 19일차 TIL - 문자열, Shuffle String</title>
      <link>https://snowflower19.tistory.com/290</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[19일차 미들러]&lt;/b&gt; 문자열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/minimum-suffix-flips/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/minimum-suffix-flips/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;911&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvB9Cs/btsH3yN4l8I/FCYNOj8BfuHamLovrLVZ31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvB9Cs/btsH3yN4l8I/FCYNOj8BfuHamLovrLVZ31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvB9Cs/btsH3yN4l8I/FCYNOj8BfuHamLovrLVZ31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvB9Cs%2FbtsH3yN4l8I%2FFCYNOj8BfuHamLovrLVZ31%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;1113&quot; height=&quot;911&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;911&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 대충 번역: 길이가 n인 이진수 문자열 target 이 주어진다. 길이가 n이고 전부 0으로 초기화된 또 다른 이진수 문자열 s도 있다. s를 target과 같게 만들고자 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 연산에서, 인덱스 i를 골라 [i, n-1] 범위(i포함 ~ 끝까지) 내의 모든 비트들을 뒤집는다. 뒤집는 것은 0을 1로, 1을 0으로 바꾸는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s를 target과 같게 만들기 위한 최소 연산 횟수를 반환하라.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞 자리와 다를 경우에 연산이 수행된다. 결국 최소 횟수는 target 문자열이 앞자리와 다른 뒷자리를 몇 개 가지고 있는지를 계산하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1718720098799&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution(object):
    def minFlips(self, target):
        &quot;&quot;&quot;
        :type target: str
        :rtype: int
        &quot;&quot;&quot;
        
        answer = 0
        flag = '0'

        for i in target:
            if i != flag:
                flag = i
                answer += 1 

        return answer&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Another Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 제출자의 정답 중, 한 줄 코딩이 있었다. 활용한 개념은 문자열이 바뀌는 시점의 횟수를 세는 것으로 나와 같았다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718806844781&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import groupby

class Solution:
    def minFlips(self, target: str) -&amp;gt; int:
        return len(list(groupby(&quot;0&quot; + target)))-1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;groupby 메서드를 사용하여 문자열을 그룹화 하였다는 것이 특징이다. 이 메서드는 같은 정수나 키 값도 아닌 &lt;b&gt;연속되는 동일한 값들을 그룹화&lt;/b&gt;하기 때문에&amp;nbsp;해당 문제에 사용하기 아주 적절하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;그러나 groupby는 데이터 분석에서 써 봤던 기억이 있는 함수로.. 코딩테스트에서, 특히 효율성 검사를 하는 경우에는 안 쓰는 것이 권장되는 듯 하다.&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;예를 들어, 결과를 ('key', 'group')인 리스트로 출력한다고 하면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;target이 &quot;110&quot;일 경우, groupby의 결과는 [('1', '11'), ('0', '0')]&lt;/li&gt;
&lt;li&gt;target이 &quot;111001&quot; 일 경우, grooypby의 결과는 [('1', '111'), ('0', '00'), ('1', '1')]&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;target이 &quot;010101&quot;일 경우, groupby로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;[('0', ['0']), ('1', ['1']), ('0', ['0']), ('1', ['1']), ('0', ['0']), ('1', ['1'])]&lt;/li&gt;
&lt;/ul&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;target의 첫 원소가 0이든 1이든 그룹화가 되어진다. 그러나 문제에서 문자열 s가 전부 0으로 초기화 되어 있기 때문에, 0으로 시작할 경우 뒤집을 필요가 없다. 이를 계산에 적용하기 위해서는 그룹의 갯수에서 1을 빼주어야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target의 첫 원소가 1인 경우에는 바로 뒤집기를 해주어야 한다는 점에서 &quot;0&quot;을 맨 앞에 붙여 그룹화를 시행해주는 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제는 이해 참고용, 문제 조건을 보고 해결책을 찾으면 수월하다.&lt;/li&gt;
&lt;li&gt;문제 자체를 전부 구현해내기 보다 핵심을 파악하자 ~ !&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 문자열</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 groupby</category>
      <category>파이썬 문자열</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/290</guid>
      <comments>https://snowflower19.tistory.com/290#entry290comment</comments>
      <pubDate>Tue, 18 Jun 2024 23:18:14 +0900</pubDate>
    </item>
    <item>
      <title>[Python/코테] 프로그래머스 입국심사</title>
      <link>https://snowflower19.tistory.com/289</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로그래머스 입국심사&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/43238&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718025415882&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/48WSj/hyWlk7RtOf/259YfSQn72ntEGy4ScvRj0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cgsGNj/hyWlnQ4nuB/ZJBYmD6L3Hn92vWVaJCLu1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/48WSj/hyWlk7RtOf/259YfSQn72ntEGy4ScvRj0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cgsGNj/hyWlnQ4nuB/ZJBYmD6L3Hn92vWVaJCLu1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czBLvP/btsHVc4eXjP/a3KJU4XjRYQsvDmtQGIQ80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czBLvP/btsHVc4eXjP/a3KJU4XjRYQsvDmtQGIQ80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czBLvP/btsHVc4eXjP/a3KJU4XjRYQsvDmtQGIQ80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczBLvP%2FbtsHVc4eXjP%2Fa3KJU4XjRYQsvDmtQGIQ80%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;1208&quot; height=&quot;798&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;알고리즘 설계&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;idea.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제한 사항을 확인해보면 기다리는 사람, 걸리는 시간 등의 최대값이 꽤 큰 것을 확인할 수 있다. 반복문을 잘못 사용하면 쉽게 시간초과가 발생한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 사람에 대해 어떤 심사대에서 심사를 받을지를 결정하는 대신, 특정 시간 내에 모든 사람이 심사를 받을 수 있는지를 판단하는 방식으로 접근할 수 있다. 즉 시간을 기준으로 가능한 사람 수를 계산한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조건을 만족하는 최소 시간을 찾기 위해, &lt;b&gt;이진 탐색&lt;/b&gt;을 활용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;step.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;각 심사관이 기준 시간동안 처리할 수 있는 사람 수를 계산한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. n명보다 많거나 적다면 기준 시간을 줄이거나늘리면서 다시 1번 검사를 반복한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;알고리즘 구현&lt;/h4&gt;
&lt;pre id=&quot;code_1718676968724&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(n, times):
    # 최소 시간
    left = 1
    # 최대 시간
    right = max(times) * n
    
    while left &amp;lt; right:
        mid = (left + right) // 2
        # mid 시간 내에 처리할 수 있는 총 사람 수 계산
        total = sum(mid // time for time in times)
        
        # 총 처리할 수 있는 사람 수가 n 이상이면, 시간을 줄여도 될 가능성
        if total &amp;gt;= n:
            right = mid
        else:
            # 그렇지 않으면, 더 많은 시간이 필요
            left = mid + 1
    
    return left&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;출처: chatgpt&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제 예시에도 나왔듯, 누군가는 더 짧은 심사대를 이용하기 위해 기다렸다가 심사를 받는 경우도 있다. 이 경우는 mid시간으로 심사 시간을 나눴을 때의 나머지가 될 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉 아래와 같이 반복되며 최적의 mid를 구할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mid = (left + right) // 2 = (24 + 30) // 2 = 27&lt;/li&gt;
&lt;li&gt;각 심사관이 27분 동안 처리할 수 있는 사람 수:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 심사관: 27 // 7 = 3명&lt;/li&gt;
&lt;li&gt;두 번째 심사관: 27 // 10 = 2명&lt;/li&gt;
&lt;li&gt;총 처리할 수 있는 사람 수: 3 + 2 = 5명&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;total &amp;lt; n이므로, left = mid + 1 = 28&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mid&amp;nbsp;=&amp;nbsp;(left&amp;nbsp;+&amp;nbsp;right)&amp;nbsp;//&amp;nbsp;2&amp;nbsp;=&amp;nbsp;(28&amp;nbsp;+&amp;nbsp;30)&amp;nbsp;//&amp;nbsp;2&amp;nbsp;=&amp;nbsp;29 &lt;/li&gt;
&lt;li&gt;각&amp;nbsp;심사관이&amp;nbsp;29분&amp;nbsp;동안&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;사람&amp;nbsp;수: &lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫&amp;nbsp;번째&amp;nbsp;심사관:&amp;nbsp;29&amp;nbsp;//&amp;nbsp;7&amp;nbsp;=&amp;nbsp;4명 &lt;/li&gt;
&lt;li&gt;두&amp;nbsp;번째&amp;nbsp;심사관:&amp;nbsp;29&amp;nbsp;//&amp;nbsp;10&amp;nbsp;=&amp;nbsp;2명 &lt;/li&gt;
&lt;li&gt;총&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;사람&amp;nbsp;수:&amp;nbsp;4&amp;nbsp;+&amp;nbsp;2&amp;nbsp;=&amp;nbsp;6명 &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;total&amp;nbsp;&amp;gt;=&amp;nbsp;n이므로,&amp;nbsp;right&amp;nbsp;=&amp;nbsp;mid&amp;nbsp;=&amp;nbsp;29&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;이 문제에서는 특정 시간 내에 모든 사람이 심사를 받을 수 있는지를 판단하는 방식으로 접근할 수 있다고 했다. 출력해야 하는 값이 '걸리는 시간' 이므로, 출력해야 하는 값을 기준으로 문제 해결의 힌트를 얻을 수 있을 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;--&amp;gt; 너무 복잡하면 &lt;b&gt;정반대로&lt;/b&gt; 생각해보자 !&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;이진탐색&lt;/b&gt;은 정렬된 배열에서 값을 찾을 때 뿐만 아니라, 조건을 만족하는 최적/최대최소(경계) 값을 찾을 때 효율적이다. 즉 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;특정 조건을 만족하는 값을 찾는 문제&lt;/span&gt;&lt;/b&gt;에서 많이 쓰인다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>이진탐색</category>
      <category>입국심사 파이썬</category>
      <category>코딩테스트</category>
      <category>코테 이진탐색</category>
      <category>파이썬 이진탐색</category>
      <category>파이썬 코테</category>
      <category>프로그래머스 입국심사</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/289</guid>
      <comments>https://snowflower19.tistory.com/289#entry289comment</comments>
      <pubDate>Tue, 18 Jun 2024 11:18:41 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 18일차 TIL - 문자, Iterator for Combination</title>
      <link>https://snowflower19.tistory.com/288</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[18일차 미들러] 문자열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/iterator-for-combination/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/iterator-for-combination/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;817&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqfRr9/btsH129wx8d/WuKzMrTYJNseGw0GaxuSI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqfRr9/btsH129wx8d/WuKzMrTYJNseGw0GaxuSI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqfRr9/btsH129wx8d/WuKzMrTYJNseGw0GaxuSI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqfRr9%2FbtsH129wx8d%2FWuKzMrTYJNseGw0GaxuSI1%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;940&quot; height=&quot;817&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 대충 번역: CombinationIterator 클래스를 설계하라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp; CombinationIterator(string characters, int combinationLength)는 고유한 영어 소문자로 정렬된 문자열 characters와 숫자 combinationLength를 인자로 받아 객체를 초기화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- next()는 길이가 combinationLength인 다음 조합을 사전순으로 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hasNext()는 다음 조합이 존재할 경우에만 true를 반환한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 문자열 characters를 combinationLength 길이만큼 출력할 것이다. 이 때 next가 호출될 때마다 사전식으로 정렬된 문자열이 차례대로 반환되는 것이다. 예를 들어, &quot;abc&quot;, 2로 객체가 초기화 되었을 때는 &quot;ab&quot;-&quot;ac&quot;-&quot;bc&quot; 순서로 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 가능한 모든 조합을 구하고 사전식으로 정렬하는 방식을 생각했다. 이 방식대로 하려면 현재 어디까지 출력했는지, 인덱스, 가능한 모든 조합 배열과 같은 멤버 변수가 추가로 필요하다. 구현에는 실패해서 홈페이지에 공개된 정답 코드를 참고했다.&lt;/p&gt;
&lt;pre id=&quot;code_1718671857946&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CombinationIterator(object):

    def __init__(self, characters: str, combinationLength: int):
        self.c = characters
        self.n = combinationLength
        self.i = 0
        self.ans = []
        self.permute('', 0)

    def permute(self, word, start):
        if len(word) == self.n:
            self.ans.append(word)
            return
        else:
            for i in range(start, len(self.c)):
                self.permute(word + self.c[i], i+1)
        
    def next(self) -&amp;gt; str:
        ans = self.ans[self.i]
        self.i += 1
        return ans 

    def hasNext(self) -&amp;gt; bool:
        return self.i &amp;lt; len(self.ans)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 모든 조합으로 가능한 문자열을 찾는 premute 함수이다.&lt;/p&gt;
&lt;pre id=&quot;code_1718672093380&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; def permute(self, word, start):
        if len(word) == self.n:
            self.ans.append(word)
            return&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀를 활용하였고, 종료 조건은 combinationLength 길이만큼 문자열이 길어졌을 때이다. 그 후 정답을 저장하는 멤버 변수 ans에 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718672106569&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        else:
            for i in range(start, len(self.c)):
                self.permute(word + self.c[i], i+1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;start는 인자로 받고, 문자열 전체 길이까지 반복하여 문자열을 끝까지 순회한다.&amp;nbsp;만들어지고 있는 단어 word에 현재 위치(인덱스)의 문자를 덧붙인다.&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;나는 처음 조건을 만족한 문자열(길이가 self.n)이 append 되면 더 이상 순회가 없는 것이라고 생각했다. 그러나 for문에서 재귀를 호출하고 있기 때문에 해당 루프의 호출은 끝났어도, 계속해서 차례로 호출되며 문자열이 만들어지는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에 제시된 예시로 permute함수 동작 과정을 도식화해 보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;day18_recursion.png&quot; data-origin-width=&quot;3539&quot; data-origin-height=&quot;1291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbzWOb/btsH2aNosY6/SubhDdHHDCLh3z8u5CqvGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbzWOb/btsH2aNosY6/SubhDdHHDCLh3z8u5CqvGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbzWOb/btsH2aNosY6/SubhDdHHDCLh3z8u5CqvGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbzWOb%2FbtsH2aNosY6%2FSubhDdHHDCLh3z8u5CqvGk%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;3539&quot; height=&quot;1291&quot; data-filename=&quot;day18_recursion.png&quot; data-origin-width=&quot;3539&quot; data-origin-height=&quot;1291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀를 구현하기 위해서는 여러 스탭을 한 번에 생각해서는 안된다. 무조건 하나의, n번째 케이스에 대한 구조를 짜야한다.&lt;/li&gt;
&lt;li&gt;반복문에서 재귀를 구현하면 많은 경우를 커버할 수 있다. 또 다시 위의 나다어처럼, 단계만 구분하고 통채로 재귀 함수 처리 해버려야 안꼬인다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>코테 문자열 조합</category>
      <category>파이썬 조합</category>
      <category>파이썬 코테 조합</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/288</guid>
      <comments>https://snowflower19.tistory.com/288#entry288comment</comments>
      <pubDate>Tue, 18 Jun 2024 10:19:37 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 17일차 TIL - 배열, Group the People Given the Group Size They Belong To</title>
      <link>https://snowflower19.tistory.com/287</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[17일차 미들러] 배열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/group-the-people-given-the-group-size-they-belong-to/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/group-the-people-given-the-group-size-they-belong-to/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sPTdU/btsH1FexC7l/Io43992xXkmME2FXRTGCrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sPTdU/btsH1FexC7l/Io43992xXkmME2FXRTGCrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sPTdU/btsH1FexC7l/Io43992xXkmME2FXRTGCrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsPTdU%2FbtsH1FexC7l%2FIo43992xXkmME2FXRTGCrk%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;1368&quot; height=&quot;730&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 대충 번역: n명의 사람이 몇 개의 그룹으로 나뉘어져있다. 각 사람은 0부터 n-1까지의 고유한 ID를 가지고 있다. 정수 배열 groupSizes가 주어지고, groupSizes[i] 는 사람 i가 있는 그룹의 크기를 나타낸다. 예를 들어, groupSizes[1] = 3 이면, 사람1은 크기가 3인 그룹에 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 사람이 그룹에 속하게끔 그룹들을 반환하. 각 사람 i는 groupSizes[i] 크기의 그룹에 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 사람은 정확히 하나의 그룹에만 있고, 모든 사람은 반드시 그룹에 속해있다. 만약 여러 답이 있다면, 아무 것이나 반환하라. 주어진 입력값에 대해 적어도 하나의 유효한 정답이 주어진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 주어진 배열을, 정답 배열과 비슷한 형태로 모은 뒤 슬라이싱 해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1718551873475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import defaultdict

class Solution(object):
    def groupThePeople(self, groupSizes):
        &quot;&quot;&quot;
        :type groupSizes: List[int]
        :rtype: List[List[int]]
        &quot;&quot;&quot;
        group = defaultdict(list)
        answer = []

        # group = {2: [2, 4, 5, 6], 4: [1, 3, 7, 8]}일 때,
        # answer = [[2, 4], [5, 6], [1, 3, 7, 8]]
        # group[i] = 크기가 i인 배열에 속하는 사람 번호
        for i in range(len(groupSizes)):
            size = groupSizes[i]
            group[size].append(i)

        # group 값들 슬라이싱
        for size, ppl in group.items():
            if len(ppl) &amp;gt; size:
                k = 0
                while k &amp;lt; len(ppl):
                    answer.append(ppl[k:k+size])
                    k += size
            else:
                answer.append(group[size])

        return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;group 사전형 변수는 결국 key가 사이즈, value가 속하는 사람들의 번호 배열을 나타내게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;group의 key값이 그룹의 크기가 되므로, 0부터 size까지, size+1부터 size+size 까지 .... 배열(그룹)을 만들게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사전형 변수를 순회할 때는 for key, value in dict&lt;b&gt;.items()&lt;/b&gt;: 로 해주어야 한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬</category>
      <category>파이썬 배열</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/287</guid>
      <comments>https://snowflower19.tistory.com/287#entry287comment</comments>
      <pubDate>Mon, 17 Jun 2024 00:36:09 +0900</pubDate>
    </item>
    <item>
      <title>[Python/코테] LeetCode 2433. Find The Original Array of Prefix Xor</title>
      <link>https://snowflower19.tistory.com/286</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;2433.&amp;nbsp;Find&amp;nbsp;The&amp;nbsp;Original&amp;nbsp;Array&amp;nbsp;of&amp;nbsp;Prefix&amp;nbsp;Xor&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/find-the-original-array-of-prefix-xor/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/find-the-original-array-of-prefix-xor/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nZNiR/btsH0pXOmw1/iufw2QVuAXp5chdmDwR3Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nZNiR/btsH0pXOmw1/iufw2QVuAXp5chdmDwR3Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nZNiR/btsH0pXOmw1/iufw2QVuAXp5chdmDwR3Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnZNiR%2FbtsH0pXOmw1%2Fiufw2QVuAXp5chdmDwR3Ik%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;638&quot; height=&quot;736&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 대충 번역: 크기가 n인 정수 배열 pref가 주어진다. pref[i] = arr[0] ^ arr[1] ^ ... ^ arr[i] 을 만족하는 크기가 n인 배열 arr을 찾아 반환하라. ^는 비트 XOR 연산자이다.&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;알고리즘 설계&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;idea.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제 조건을 수식으로 만들면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1718435198902&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pref[0] = arr[0]
pref[1] = arr[0] ^ arr[1]
pref[2] = arr[0] ^ arr[1] ^ arr[2]
...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;arr[0] = pref[0] 이므로, 아래 두 식이 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718435237370&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pref[1] = pref[0] ^ arr[1] 
# 즉,
arr[1] = pref[0] ^ pref[1]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;따라서, 다음과 같은 수식도 성립한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718435309919&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pref[2] = pref[1] ^ arr[2]
# 즉,
arr[2] = pref[1] ^ pref[2]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 아래와 같은 식이 성립하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1718435397393&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arr[i] = pref[i] ^ pref[i-1] # 0 &amp;lt; i&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;step.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기값 arr[0] = pref[0] 을 지정해주고, 찾은 식을 대입하여 arr 값들을 계산해낸.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;알고리즘 구현&lt;/h4&gt;
&lt;pre id=&quot;code_1718435444956&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution(object):
    def findArray(self, pref):
        &quot;&quot;&quot;
        :type pref: List[int]
        :rtype: List[int]
        &quot;&quot;&quot;

        n = len(pref)

        arr = [0]
        arr[0] = pref[0]

        for i in range(1, n):
            # arr[i] = pref[i] ^ pref[i-1]
            arr.append(pref[i] ^ pref[i-1])

        return arr&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트 연산자는 다음과 같은 설명을 만족한다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(출처: &lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://wikidocs.net/20704&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/20704&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #24292f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비트 단위로 연산을 수행합니다.&lt;/li&gt;
&lt;li&gt;0은 거짓으로 1은 참으로 연산하여 결과를 1과 0으로 반환합니다.&lt;/li&gt;
&lt;li&gt;&quot;^(xor)&quot;연산은 두개의 값이 다를 때만 참인 연산입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>코딩테스트</category>
      <category>파이썬 비트연산자</category>
      <category>파이썬 코테</category>
      <category>파이썬 코테 배열</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/286</guid>
      <comments>https://snowflower19.tistory.com/286#entry286comment</comments>
      <pubDate>Sat, 15 Jun 2024 16:11:41 +0900</pubDate>
    </item>
    <item>
      <title>[99클럽 코테 스터디] 16일차 TIL - 배열, Number of Good Pairs</title>
      <link>https://snowflower19.tistory.com/285</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[16일차] 배열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: &lt;a href=&quot;https://leetcode.com/problems/number-of-good-pairs/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/number-of-good-pairs/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euFcgt/btsHZRm85cF/Im0oODzcyxuVrYEBMMV4RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euFcgt/btsHZRm85cF/Im0oODzcyxuVrYEBMMV4RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euFcgt/btsHZRm85cF/Im0oODzcyxuVrYEBMMV4RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuFcgt%2FbtsHZRm85cF%2FIm0oODzcyxuVrYEBMMV4RK%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;641&quot; height=&quot;662&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 번역: 주어진 정수 배열 nums에 대해, 좋은 쌍(good pairs)를 반환하라. 좋은 쌍이란, i &amp;lt; j 에 대해 nums[i] == nums[j] 을 만족하는 쌍 (i, j)이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Solution&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 조건에 nums의 길이는 100이하라고 했으므로, 중첩 for문을 사용해도 시간 초과가 날 것 같지 않다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for 문 자체에서 i &amp;lt; j 조건을 적용해주고, if문으로 값 조건을 확인했다.&lt;/p&gt;
&lt;pre id=&quot;code_1718432776400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution(object):
    def numIdenticalPairs(self, nums):
        &quot;&quot;&quot;
        :type nums: List[int]
        :rtype: int
        &quot;&quot;&quot;
        answer = 0
        
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                if nums[i] == nums[j]:
                    answer += 1

        return answer&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나다어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;나는 다음에 어떻게 풀까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;for문, 배열의 인덱스는 문제 풀이에 요긴하게 쓰인다.&lt;/li&gt;
&lt;li&gt;파이썬은 1초에 2천만번, 2*10^7 번의 연산이 가능하다. &lt;br /&gt;즉 시간제한이 1초, n = 10^5(10만)일 때 O(N^2)의 알고리즘은 10^10(100억)번 연산이 필요하므로, 시간초과가 나게 된다. 이 경우 O(NlogN)으로 짜야, 16*10^5 번의 연산으로 수행 가능하다. (log100,000 = 약 16)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자 취업</category>
      <category>코딩테스트</category>
      <category>코딩테스트 준비</category>
      <category>파이썬 배열</category>
      <category>파이썬 코테 배열</category>
      <category>항해99</category>
      <author>날으는다람쥐</author>
      <guid isPermaLink="true">https://snowflower19.tistory.com/285</guid>
      <comments>https://snowflower19.tistory.com/285#entry285comment</comments>
      <pubDate>Sat, 15 Jun 2024 15:29:11 +0900</pubDate>
    </item>
  </channel>
</rss>