<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>태야</title>
    <link>https://fullmoon1344.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 00:06:01 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>태욘이</managingEditor>
    <item>
      <title>PostgreSQL RLS 정책 - 데이터 조회 불가한 상황 해결하기</title>
      <link>https://fullmoon1344.tistory.com/178</link>
      <description>&lt;h2&gt;상황 - 오류 없는 빈 값 조회&lt;/h2&gt;
&lt;p&gt;현재 개인 사이드 프로젝트에서 Supabse를 사용해서 백엔드를 구성하고 있다. 사용자 프로필 정보를 담는 profiles 테이블을 만들었다. 이 propfiles 테이블의 데이터를 조회할 때, 본인꺼만 조회할 수 있도록 RLS 정책을 지정해 놓았다. 개발 중 웹훅 수신 로직에서 사용자 이메일 값을 기반으로 profile에서 사용자 id를 파싱 해야하는 상황이 생겼다. 이때 profiles에 접근해서 응답을 보니 항상 빈 배열 값이 나오는 문제가 생겼다.&lt;/p&gt;
&lt;h2&gt;원인 분석&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Supabse의 profiles 테이블에 RLS(행 수준 보안)가 적용되어 있다.&lt;/li&gt;
&lt;li&gt;RLS 정책이 &amp;quot;본인(로그인된 유저)만 자신의 profile을 select 할 수 있다.&amp;quot;로 되어 있으면, 서버에서 웹훅(즉, 외부 시스템)이 호출할 때는 인증된 유저가 없으므로, 어떤 profile도 조회할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;해결 방법&lt;/h2&gt;
&lt;h4&gt;1. Service Role Key 사용&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Supabse의 Service Role Key(서버 전용 시크릿 키)를 사용하면, RLS를 무시하고 모든 데이터에 접근이 가능했다.&lt;/li&gt;
&lt;li&gt;현재는 Service Role Key를 사용하지 않는 supabse 객체를 클라이언트, 서버단 2개로 나누어서 사용 중이다.&lt;/li&gt;
&lt;li&gt;이 방식은 서버에서만 사용해야 하며, &lt;strong&gt;&lt;em&gt;클라이언트에서는 절대 노출하면 안 된다.&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-TS&quot;&gt;import { supabaseAdmin } from &amp;#39;@/lib/supabase-server&amp;#39;; // Service Role Key 사용

const { data: userProfile } = await supabaseAdmin
  .from(&amp;#39;profiles&amp;#39;)
  .select(&amp;#39;id&amp;#39;)
  .eq(&amp;#39;email&amp;#39;, attr.user_email)
  .single();&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 웹훅 전용 RLS 정책 추가&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;만약 Service Role Key를 사용하지 않고, 일반 API Key로만 접근해야 한다면,&lt;/li&gt;
&lt;li&gt;웹훅에서 오는 요청의 IP나 특정 헤더, 혹은 별도의 인증 토큰을 기반으로 RLS 정책을 완화할 수 있다.&lt;/li&gt;
&lt;li&gt;하지만, Service Role Key를 사용하는 것이 더 쉽고 안전하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;결론&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;웹훅 등 서버에서 RLS를 우회해야 할 때는 Service Role Key를 사용하는 supabase client로 쿼리하기(쉽게 말해, 관리자용 supabse client)&lt;/li&gt;
&lt;li&gt;모든 곳에서 Service Role Key를 사용하는 supabase client로 하지말고, 필요한 부분에서만 사용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;적용 예시&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-TS&quot;&gt;import { supabaseAdmin } from &amp;#39;@/lib/supabase-server&amp;#39;; // Service Role Key 사용

let userId = &amp;#39;&amp;#39;;
if (attr.user_email) {
  const { data: userProfile } = await supabaseAdmin
    .from(&amp;#39;profiles&amp;#39;)
    .select(&amp;#39;id&amp;#39;)
    .eq(&amp;#39;email&amp;#39;, attr.user_email)
    .single();
  userId = userProfile?.id ?? &amp;#39;&amp;#39;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DB</category>
      <category>PostgreSQL</category>
      <category>RLS</category>
      <category>service role key</category>
      <category>supabse</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/178</guid>
      <comments>https://fullmoon1344.tistory.com/178#entry178comment</comments>
      <pubDate>Thu, 22 May 2025 23:16:28 +0900</pubDate>
    </item>
    <item>
      <title>CORS 개념 정리</title>
      <link>https://fullmoon1344.tistory.com/177</link>
      <description>&lt;h2&gt;개념&lt;/h2&gt;
&lt;p&gt;CORS는 Cross-Origin Resource Sharing의 약자로, &lt;strong&gt;서로 다른 출처 간의 리소스 요청을 제어하기 위한 보안 기능&lt;/strong&gt;이다. 브라우저는 보안상의 이유로 스크립트가 다른 도메인에 있는 리소스에 접근하는 것을 기본적으로 차단한다. 이 기본적인 보안 제한을 ‘&lt;strong&gt;동일 출처 정책(Same-Origin Policy)&lt;/strong&gt;’라고 한다.&lt;/p&gt;
&lt;p&gt;CORS는 이러한 동일 출처 정책의 제한을 우회할 수 있도록 서버에서 명시적으로 설정해, 브라우저가 다른 도메인에서 리소스를 요청하는 것을 허용하게 만드는 방법이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;출처(Origin)&lt;/strong&gt;: 도메인, 프로토콜, 포트를 포함한 것을 의미한다. 예를 들어, &lt;a href=&quot;http://example.com:3000&quot;&gt;http://example.com:3000&lt;/a&gt;과&lt;a href=&quot;https://example.com:3000&quot;&gt;https://example.com:3000&lt;/a&gt;은 서로 다른 출처다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;동일 출처 정책 (Same-Origin Policy)&lt;/strong&gt;: 웹 페이지가 자신과 다른 출처의 리소스를 로드하는 것을 제한하는 브라우저의 보안 정책이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;원인&lt;/h2&gt;
&lt;p&gt;클라이언트가 웹 서버에 &lt;strong&gt;다른 출처의 리소스에 접근&lt;/strong&gt;하려고 할 때 발생한다. 브라우저가 서버에 요청을 보내고, 서버가 올바른 &lt;strong&gt;CORS 헤더&lt;/strong&gt;를 포함하지 않으면 브라우저에서 요청이 차단된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;예를 들어, 웹 애플리케이션의 클라이언트가 &lt;code&gt;http://localhost:3000&lt;/code&gt;에서 실행 중이고, &lt;code&gt;http://api.example.com&lt;/code&gt;으로 API 요청을 보내려고 할 때, 출처가 다르기 때문에 브라우저는 보안 정책에 의해 요청을 차단할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;동작 방식&lt;/h2&gt;
&lt;p&gt;CORS 동작 방식은 브라우저와 서버 간의 HTTP 요청 및 응답 헤더를 통해, 서로 다른 출처에서 리소스를 공유하는 과정을 제어하는 것이다.&lt;/p&gt;
&lt;h4&gt;1. Simple Request(단순 요청)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;특정 조건을 만족할 때&lt;/strong&gt; preflight 요청을 보내지 않고 바로 요청이 이루어진다. 특정 조건은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 메서드는 &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, 또는 &lt;code&gt;HEAD&lt;/code&gt; 여야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt; 헤더가 다음과 같은 &lt;code&gt;POST&lt;/code&gt; 요청&lt;ul&gt;
&lt;li&gt;&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multipart/form-data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text/plain&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;헤더는 브라우저가 자동으로 추가하는 안전한 헤더여야 하며, 여기에는 다음과 같은 제한이 있다.&lt;ul&gt;
&lt;li&gt;Accept, Accept-Language, Content-Language, Content-Type (&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, &lt;code&gt;multipart/form-data&lt;/code&gt;, &lt;code&gt;text/plain&lt;/code&gt; 중 하나).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;단순 요청에서는 브라우저가 요청을 보낼 때 &lt;strong&gt;Origin 헤더&lt;/strong&gt;를 추가한다. 서버는 응답 시 해당 요청을 허용할지 여부를 결정하고, 다음과 같은 CORS 관련 헤더를 응답에 포함한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Origin&lt;/strong&gt;: 요청을 허용할 출처를 지정( &lt;code&gt;*&lt;/code&gt; 또는 특정 도메인).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Expose-Headers&lt;/strong&gt; (선택적): 클라이언트에서 접근 가능한 응답 헤더 목록을 지정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;브라우저는 서버의 응답에 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 헤더가 포함되어 있는지 확인하고, 클라이언트가 해당 출처의 리소스에 접근하는 것을 허용할지 결정한다.&lt;/p&gt;
&lt;h4&gt;2. Preflight Request(사전 요청)&lt;/h4&gt;
&lt;p&gt;브라우저가 보안 확인을 위해 실제 요청을 보내기 전에 서버에 미리 허락을 구하는 요청이다. 주로 민감한 요청에 대해 사용되며, 다음과 같은 경우에 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;요청 메서드가 &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;가 아닌 다른 메서드인 경우 (&lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;OPTIONS&lt;/code&gt; 등)&lt;/li&gt;
&lt;li&gt;요청 헤더가 안전하지 않은 커스텀 헤더를 포함하는 경우 (예: Authorization, Content-Type이 JSON 등)&lt;/li&gt;
&lt;li&gt;요청에 &lt;strong&gt;Credentials (쿠키, 인증 토큰 등)&lt;/strong&gt;이 포함된 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프리플라이트 요청은 &lt;strong&gt;OPTIONS 메서드&lt;/strong&gt;를 사용해 서버에 보내진다. 이 요청은 브라우저가 실제 요청을 보내도 안전한지 확인하기 위해 서버에게 허락을 구하는 과정이다. 프리플라이트 요청에서는 다음과 같은 헤더가 사용된다.&lt;/p&gt;
&lt;p&gt;Request Header(브라우저 → 서버)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Origin&lt;/strong&gt;: 요청을 보낸 출처.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Request-Method&lt;/strong&gt;: 실제 요청에서 사용하려는 HTTP 메서드.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Request-Headers&lt;/strong&gt; (선택적): 실제 요청에서 사용될 추가 헤더 목록.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;서버는 이러한 프리플라이트 요청에 대해 다음과 같은 응답을 보내야 한다.&lt;/p&gt;
&lt;p&gt;Response Header(서버 → 브라우저)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Origin&lt;/strong&gt;: 허용된 출처(&lt;code&gt;*&lt;/code&gt; 또는 특정 도메인)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Methods&lt;/strong&gt;: 클라이언트가 사용할 수 있는 HTTP 메서드 목록 (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; 등).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Headers&lt;/strong&gt;: 허용된 헤더 목록 (예: &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Content-Type&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt; (선택적): 클라이언트가 프리플라이트 응답을 캐시할 시간 (초 단위).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Credentials&lt;/strong&gt;: 클라이언트가 쿠키를 전송할 수 있도록 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;서버가 프리플라이트 요청에 대해 성공적으로 응답하면, 브라우저는 실제 요청을 서버로 전송하게 된다!&lt;/p&gt;
&lt;/br&gt;    

&lt;hr&gt;
&lt;h2&gt;CORS 요청 예시&lt;/h2&gt;
&lt;p&gt;브라우저가 CORS 요청을 보내고, 서버가 응답하는 예시다.&lt;/p&gt;
&lt;h4&gt;1. Simple Request 예시&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 &lt;code&gt;GET&lt;/code&gt; 요청을 보내는 경우&lt;ul&gt;
&lt;li&gt;여기서 브라우저는 응답에 포함된 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 헤더를 확인하여 클라이언트에서 이 데이터를 사용할 수 있게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 요청(client -&amp;gt; Server)
GET /api/data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000

// 응답(Server -&amp;gt; Client)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json

{
  &amp;quot;message&amp;quot;: &amp;quot;CORS 요청 성공&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2. Preflight Request 예시&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Preflight 요청(client -&amp;gt; Server)
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization

// Preflight 응답(Server -&amp;gt; Client)
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization
Access-Control-Max-Age: 86400&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Origin&lt;/strong&gt;: 해당 요청을 허용할 출처를 명시 (&lt;code&gt;http://localhost:3000&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Methods&lt;/strong&gt;: 허용 가능한 메서드(&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Headers&lt;/strong&gt;: &lt;code&gt;Authorization&lt;/code&gt; 헤더가 허용됨을 명시.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt;: 86400초 동안 프리플라이트 응답을 캐시함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Preflight 요청에 대한 성공적인 응답을 받은 후, 클라이언트는 실제 요청을 보낸다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 실제 요청(client -&amp;gt; Server)
DELETE /api/data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
Authorization: Bearer token123

// 실제 응답(Server -&amp;gt; Client)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json

{
  &amp;quot;message&amp;quot;: &amp;quot;리소스가 삭제되었습니다&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;CORS 관련 주요 HTTP 헤더 요약&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Origin&lt;/strong&gt;: 허용할 출처를 지정 (은 모든 출처를 허용).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Methods&lt;/strong&gt;: 클라이언트가 사용할 수 있는 HTTP 메서드를 지정.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Headers&lt;/strong&gt;: 클라이언트가 사용할 수 있는 추가 헤더를 지정.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Allow-Credentials&lt;/strong&gt;: 클라이언트가 쿠키나 인증 정보를 서버로 전송할 수 있도록 허용.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt;: 프리플라이트 요청의 응답을 캐시할 수 있는 시간을 지정 (초 단위).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access-Control-Expose-Headers&lt;/strong&gt;: 클라이언트에서 접근 가능한 응답 헤더를 지정.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;해결 방법&lt;/h2&gt;
&lt;h4&gt;1.서버 측에서 CORS 설정&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;서버가 올바른 CORS 헤더를 포함하도록 설정하는 것이 가장 일반적인 해결 방법이다.&lt;/li&gt;
&lt;li&gt;예를 들어, Node.js/Express 서버에서는 다음과 같이 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);
const cors = require(&amp;#39;cors&amp;#39;);
const app = express();

app.use(cors()); // 모든 출처에 대해 CORS 허용
// 또는 특정 출처만 허용하고 싶다면:
// app.use(cors({ origin: &amp;#39;http://localhost:3000&amp;#39; }));

app.get(&amp;#39;/api/data&amp;#39;, (req, res) =&amp;gt; {
  res.json({ message: &amp;#39;CORS 설정 성공&amp;#39; });
});

app.listen(5000, () =&amp;gt; {
  console.log(&amp;#39;Server is running on port 5000&amp;#39;);
});&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2.프록시 서버 사용&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트와 서버 간에 프록시 서버를 두어 요청을 중계함으로써 동일 출처 정책을 회피하는 방법이다.&lt;/li&gt;
&lt;li&gt;개발 환겨에서 주로 사용되며, 예를 들어 React에서는 &lt;code&gt;package.json&lt;/code&gt; 에 proxy 설정을 추가해 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;proxy&amp;quot;: &amp;quot;http://api.example.com&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;3.브라우저 확장 프로그램 사용(개발 환경)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Chrome 등 브라우저에서 제공하는 &lt;strong&gt;CORS 해제 확장 프로그램&lt;/strong&gt;을 사용해 에러를 일시적으로 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;하지만, 이 방법은 &lt;strong&gt;개발 환경에서만 사용하는 것이 좋다.&lt;/strong&gt; 보안상 이유로 운영 환경에서는 권장되지 않는다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.서버 설정 변경&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apache&lt;/strong&gt; 또는 &lt;strong&gt;Nginx&lt;/strong&gt; 같은 웹 서버를 사용하는 경우, 서버 설정 파일에서 CORS 헤더를 추가해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx&lt;/strong&gt;의 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;add_header &amp;#39;Access-Control-Allow-Origin&amp;#39; &amp;#39;*&amp;#39;;
add_header &amp;#39;Access-Control-Allow-Methods&amp;#39; &amp;#39;GET, POST, OPTIONS&amp;#39;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Access-Control-Allow-Credentials 사용 시 주의&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 쿠키 또는 인증 정보를 서버로 보내기 위해서는 &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt;를 설정하고, &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;에 &lt;code&gt;*&lt;/code&gt; 대신 특정 출처를 명시해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/br&gt;

&lt;hr&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CORS&lt;/strong&gt;는 브라우저 보안 정책으로 인해 발생하는 문제로, 클라이언트가 다른 출처의 리소스에 접근하려고 할 때 발생한다.&lt;/li&gt;
&lt;li&gt;서버에서 &lt;strong&gt;CORS 헤더&lt;/strong&gt;를 적절히 설정함으로써 이를 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;프록시 서버 사용이나 브라우저 확장 프로그램 같은 해결책이 있지만, 이는 개발 환경에서 주로 사용되며, 운영 환경에서는 서버에서 CORS 정책을 설정하는 것이 가장 바람직한 방법이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>네트워크</category>
      <category>CORS</category>
      <category>HTTP</category>
      <category>options</category>
      <category>동일 출처 정책</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/177</guid>
      <comments>https://fullmoon1344.tistory.com/177#entry177comment</comments>
      <pubDate>Thu, 22 May 2025 22:54:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준 9375] 패션왕 신해빈 - Python(해시맵, 집합)</title>
      <link>https://fullmoon1344.tistory.com/176</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9375&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9375&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742180186190&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

input = sys.stdin.readline

N = int(input().rstrip())
for _ in range(N):
    M = int(input().rstrip())
    clothes = {}

    for _ in range(M):
        (item, cloth_type) = list(input().rstrip().split())
        clothes[cloth_type] = clothes.get(cloth_type, 0) + 1

    result = 1
    for count in clothes.values():
        result *= (count + 1) #해당 종류의 의상을 선택하지 않는 경우 포함

    print(result - 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;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>해시</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/176</guid>
      <comments>https://fullmoon1344.tistory.com/176#entry176comment</comments>
      <pubDate>Mon, 17 Mar 2025 11:58:09 +0900</pubDate>
    </item>
    <item>
      <title>[백준 11478] 서로 다른 부분 문자열의 개수 - Python (해시맵)</title>
      <link>https://fullmoon1344.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11478&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11478&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;b&gt;1번째 풀이 - O(N^2)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741847043194&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

input = sys.stdin.readline

S = input().rstrip()

for i in range(len(S)):
    if S[i] in result:
        result[S[i]] += 1
    else:
        result[S[i]] = 1

    if i == len(S) - 1:
        break

    current_string = S[i]

    for j in range(i + 1, len(S)):
        current_string = current_string + S[j]
        if current_string in result:
            result[current_string] += 1
        else:
            result[current_string] = 1

print(len(result))&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;시간복잡도 O(n^2)이 걸리는 풀이로 작성했다. 해당 코드를 python의 set 자료형을 활용하면 간단하게 작성할 수 있다.&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;python set() 활용 풀이 - O(N^2)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741847148122&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

input = sys.stdin.readline

S = input().rstrip()

result = set()

for i in range(len(S)):
    for j in range(i + 1, len(S) + 1):
        result.add(S[i:j])
print(len(result))&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;set(집합) 자료형은 집합의 &lt;b&gt;중복을 제거&lt;/b&gt;하고 &lt;b&gt;순서를 보장하지 않는&lt;/b&gt; 리터럴 객체를 생성한다.&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>HASH</category>
      <category>Python</category>
      <category>백준 11478</category>
      <category>알고리즘 테스트</category>
      <category>해시맵</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/175</guid>
      <comments>https://fullmoon1344.tistory.com/175#entry175comment</comments>
      <pubDate>Thu, 13 Mar 2025 15:50:02 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1158] 요세푸스 문제 - Python(큐, 링크드 리스트)</title>
      <link>https://fullmoon1344.tistory.com/174</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1158&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1158&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;b&gt;1차 풀이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741680543077&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
import sys
input = sys.stdin.readline
[N, K] = list(map(int, input().strip().split()))

def get_answer():
    numbers = deque(range(1, N+1, 1))
    deleted_numbers = []

    i = -1
    count = 0
    while len(deleted_numbers) != N:
        i += 1

        if i &amp;gt;= N:
            i = 0

        if numbers[i] not in deleted_numbers:
            count += 1
            if count == K:
                count = 0
                deleted_numbers.append(numbers[i])
                continue
    return &quot;&amp;lt;&quot; + &quot;, &quot;.join(map(str, deleted_numbers)) + &quot;&amp;gt;&quot;

print(get_answer())&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;문제점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;91&quot; data-start=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;- 효율성 문제&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;456&quot; data-start=&quot;92&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;338&quot; data-start=&quot;92&quot;&gt;&lt;b&gt;삭제 여부 확인 방식:&lt;/b&gt;&lt;br /&gt;코드에서는 원래의 numbers 데크에서 값을 제거하지 않고, 별도의 deleted_numbers 리스트에 추가한 후, 매번 if numbers[i] not in deleted_numbers:로 삭제 여부를 확인한다. 이 방식은 리스트의 멤버십 검사가 O(n) 시간 복잡도를 가지므로, N이 5000과 같이 큰 경우 불필요하게 많은 연산을 수행하게 되어 성능 문제가 발생할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;456&quot; data-start=&quot;340&quot;&gt;&lt;b&gt;불필요한 자료구조 사용:&lt;/b&gt;&lt;br /&gt;데크(deque)를 선언해두었지만, 데크의 효율적인 회전(rotate) 기능이나 popleft() 등을 활용하지 않고 단순히 인덱스 접근 방식으로 사용하고 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개선된 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741680683657&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
import sys
input = sys.stdin.readline
[N, K] = list(map(int, input().strip().split()))

def get_answer():
    dq = deque(range(1, N+1, 1))
    result = []

    while dq:
        for _ in range(K - 1):
            dq.append(dq.popleft())
        result.append(dq.popleft())


    return &quot;&amp;lt;&quot; + &quot;, &quot;.join(map(str, result)) + &quot;&amp;gt;&quot;

print(get_answer())&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;개선 효과&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 매번 불필요한 인덱스 접근이나 멤버십 검사를 하지 않아 시간 복잡도를 &lt;b&gt;O(N)&lt;/b&gt;으로 줄일 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Python rotate 메서드 활용 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741680950655&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
import sys

input = sys.stdin.readline
N, K = map(int, input().split())
dq = deque(range(1, N + 1))
result = []

while dq:
    dq.rotate(-(K - 1))  # K-1번 왼쪽으로 회전
    result.append(dq.popleft())

print(&quot;&amp;lt;&quot; + &quot;, &quot;.join(map(str, result)) + &quot;&amp;gt;&quot;)&lt;/code&gt;&lt;/pre&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요세푸스 문제는 원형 구조를 활용해 특정 규칙에 따라 요소들을 제거하는 시뮬레이션 문제인데, 그 개념 자체는 &lt;b&gt;원형 연결 리스트 (circular linked list)&lt;/b&gt;와 매우 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;128&quot; data-start=&quot;118&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 포인트&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;611&quot; data-start=&quot;129&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;245&quot; data-start=&quot;129&quot;&gt;&lt;b&gt;원형 연결 리스트&lt;/b&gt;:&lt;br /&gt;노드들이 원형으로 연결되어 있어 마지막 노드의 다음이 첫 번째 노드가 된다. 요세푸스 문제에서는 이 구조를 통해 K번째 원소를 계속해서 찾아 제거하는 과정을 구현한다.&lt;/li&gt;
&lt;li data-end=&quot;463&quot; data-start=&quot;247&quot;&gt;&lt;b&gt;시뮬레이션&lt;/b&gt;:&lt;br /&gt;문제의 해결 방법은 실제로 원형 연결 리스트를 사용해 구현할 수도 있고, Python의 deque 같은 자료구조를 이용해 &lt;b&gt;원형 큐&lt;/b&gt;의 동작(회전 및 제거)을 시뮬레이션하는 방식으로도 구현할 수 있다.&lt;br /&gt;예를 들어, deque를 사용하면 rotate()나 popleft() 같은 메서드로 간단하게 원형 구조를 흉내낼 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;611&quot; data-start=&quot;465&quot;&gt;&lt;b&gt;알고리즘 선택&lt;/b&gt;:&lt;br /&gt;요세푸스 문제는 원형 연결 리스트를 이용하는 시뮬레이션 문제로 접근할 수 있지만, 경우에 따라 수학적인 공식을 활용해 더 효율적으로 풀 수도 있다. 단, 일반적으로 K가 임의의 값일 때는 시뮬레이션 방법이 가장 직관적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Circular Linked List</category>
      <category>Python</category>
      <category>백준 1158</category>
      <category>알고리즘 테스트</category>
      <category>원형 연결 리스트</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/174</guid>
      <comments>https://fullmoon1344.tistory.com/174#entry174comment</comments>
      <pubDate>Tue, 11 Mar 2025 17:13:07 +0900</pubDate>
    </item>
    <item>
      <title>[백준 3015] 오아시스 재결합 - Python(스택)</title>
      <link>https://fullmoon1344.tistory.com/173</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3015&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/3015&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;pre id=&quot;code_1741502307083&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

input = sys.stdin.readline
N = int(input().strip())
heights = list(int(input().strip()) for _ in range(N))

def get_answer():
    stack = [] #(높이, 동일 높이 연속 개수) 집합
    result = 0
    for h in heights:
        same_count = 1
        #현재 h보다 작은 사람들은 볼 수 없으므로 pop하고, 그들이 형성헀던 쌍의 수를 더함
        while stack and stack[-1][0] &amp;lt; h:
            result += stack[-1][1]
            stack.pop()

        # 같은 높이의 경우, 같은 높이 그룹의 사람들과는 모두 볼 수 있음
        if stack and stack[-1][0] == h:
            cnt = stack[-1][1]
            stack.pop()
            result += cnt # 같은 높이 사람들과의 쌍 추가

            # 만약 스택에 아직 사람이 남아 있다면, 그 다음 높이 같은 사람과도 볼 수 있음
            if stack:
                result += 1
            same_count = cnt + 1
        else:
            # 스택이 비어있지 않다면, 바로 앞 사람과는 항상 볼 수 있음
            if stack:
                result += 1
        stack.append((h, same_count))
    return result

print(get_answer())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 알고리즘의 핵심은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2037&quot; data-start=&quot;1853&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1899&quot; data-start=&quot;1853&quot;&gt;&lt;b&gt;왼쪽부터 순회&lt;/b&gt;하면서 스택에 (높이, 동일 높이 연속 개수)를 저장한다.&lt;/li&gt;
&lt;li data-end=&quot;1987&quot; data-start=&quot;1900&quot;&gt;&lt;b&gt;현재 사람보다 작은 사람들은 pop&lt;/b&gt;하여 그동안 형성된 쌍을 결과에 더하고,&lt;br /&gt;동일한 높이의 사람들은 하나의 그룹으로 묶어 개수를 관리한다.&lt;/li&gt;
&lt;li data-end=&quot;2037&quot; data-start=&quot;1988&quot;&gt;&lt;b&gt;남아 있는 스택의 최상단&lt;/b&gt;이 있다면 그 사람과는 볼 수 있으므로 1을 더한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2088&quot; data-start=&quot;2039&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 각 사람을 한 번씩만 처리하여 &lt;span&gt;&lt;span&gt;O(N)&lt;/span&gt;&lt;/span&gt;&amp;nbsp;시간 내에 해결할 수 있다.&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Python</category>
      <category>백준 3015</category>
      <category>스택 자료구조</category>
      <category>알고리즘 테스트</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/173</guid>
      <comments>https://fullmoon1344.tistory.com/173#entry173comment</comments>
      <pubDate>Sun, 9 Mar 2025 15:39:49 +0900</pubDate>
    </item>
    <item>
      <title>다음 큰 수(Next Greater Element) 문제 - Stack 활용</title>
      <link>https://fullmoon1344.tistory.com/172</link>
      <description>&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;b&gt;예시 문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예를 들어, 배열 A가 주어졌을 때A = [3, 5, 2, 7, 5]각 원소에 대해 오른쪽으로 이동하며, 처음 만나는 자신보다 크거나 같은 원소를 찾습니다.&lt;br /&gt;&lt;br /&gt;- 3의 오른쪽에는 5, 2, 7, 5가 있는데, 처음으로 3보다 크거나 같은 수는 5입니다.&lt;br /&gt;- 5의 오른쪽에는 2, 7, 5가 있는데, 2는 작으므로 건너뛰고, 다음 7은 5보다 크므로 7이 됩니다.&lt;br /&gt;- 2의 오른쪽에는 7, 5가 있으므로 7이 첫 번째 큰(또는 같은) 수입니다.&lt;br /&gt;- 7의 오른쪽에는 5만 있으나 5는 7보다 작으므로, 7은 오른쪽에 조건을 만족하는 원소가 없는 것으로 처리합니다.&lt;br /&gt;- 마지막 원소인 5는 오른쪽에 아무 원소도 없으므로 조건을 만족하는 원소가 없습니다.&lt;br /&gt;&lt;br /&gt;보통 조건을 만족하는 원소가 없을 경우 -1 또는 0 등 특정 값을 반환합니다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 이중 반복문을 사용하면, 각 원소마다 오른쪽의 모든 원소를 확인해야 하므로 시간복잡도가 &lt;b&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;n^&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;2&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;/b&gt;&lt;span&gt;&lt;b&gt;)&lt;/b&gt;가 된다. 하지만 &lt;b&gt;스택(stack)&lt;/b&gt;을 이용하면, 한 번의 순회로 각 원소를 효율적으로 처리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;스택을 사용하는 이유&lt;/span&gt;&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;스택의 특징&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택은 후입선출(LIFO) 구조다. 이 특성을 이용하면, 배열을 한 번 순회하면서 &quot;아직 답을 찾지 못한 원소들의 인덱스&quot;를 스택에 저장해 두고, 새로운 원소가 들어올 때마다 이전 원소들과 비교하여 조건(큰 혹은 큰/같은)을 만족하는지 빠르게 확인할 수 있다.&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 data-end=&quot;1055&quot; data-start=&quot;935&quot;&gt;각 원소는 스택에 한 번 push되고, 조건이 맞으면 pop되므로 각 원소가 스택에 들어가고 나오는 과정이 한 번씩 이루어집니다. 따라서 전체 시간 복잡도는 &lt;b&gt;&lt;span&gt;&lt;span&gt;O(n)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알고리즘 동작 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1175&quot; data-start=&quot;1077&quot;&gt;&lt;b&gt;초기화:&lt;/b&gt;&lt;br /&gt;빈 스택을 준비하고, 결과를 저장할 배열을 초기화한다. 결과 배열은 각 원소의 &quot;다음 큰(또는 같은) 수&quot;의 인덱스나 값을 저장할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;1597&quot; data-start=&quot;1177&quot;&gt;&lt;b&gt;배열 순회:&lt;/b&gt;&lt;br /&gt;배열의 인덱스를 0부터 n-1까지 순회한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-end=&quot;1597&quot; data-start=&quot;1229&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;1540&quot; data-start=&quot;1229&quot;&gt;&lt;b&gt;현재 원소와 비교:&lt;/b&gt;&lt;br /&gt;현재 원소(A[i])가 들어왔을 때, 스택의 최상단에 있는 인덱스(예: j)를 확인한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-end=&quot;1540&quot; data-start=&quot;1315&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;1460&quot; data-start=&quot;1315&quot;&gt;만약 A[i]가 A[j]보다 크거나(또는 크거나 같은) 하면, A[i]는 A[j]의 조건(다음 큰 또는 같은 수)을 만족하는 원소가 된다.&lt;br /&gt;따라서 결과 배열의 j번째 위치에 A[i] 또는 인덱스 i를 저장하고, 스택에서 j를 pop한다.&lt;/li&gt;
&lt;li data-end=&quot;1536&quot; data-start=&quot;1474&quot;&gt;이 과정을 스택이 비거나, 스택의 최상단 원소가 현재 원소보다 크거나 같은 조건을 만족할 때까지 반복한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1597&quot; data-start=&quot;1544&quot;&gt;&lt;b&gt;현재 인덱스 push:&lt;/b&gt;&lt;br /&gt;그 후, 현재 인덱스 i를 스택에 push한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1701&quot; data-start=&quot;1599&quot;&gt;&lt;b&gt;후처리:&lt;/b&gt;&lt;br /&gt;배열 순회가 끝난 후에도 스택에 남아있는 인덱스들은 오른쪽에 조건을 만족하는 원소가 없는 경우다. 이 경우, 결과 배열에 -1 또는 0을 기록한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Python 코드 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741241685617&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def next_greater_or_equal(arr):
    n = len(arr)
    result = [-1] * n  # 조건을 만족하는 원소가 없으면 -1로 처리
    stack = []  # 인덱스를 저장하는 스택

    for i in range(n):
        # 현재 원소 arr[i]가 스택의 최상단 원소보다 크거나 같으면 조건을 만족
        while stack and arr[stack[-1]] &amp;lt;= arr[i]:
            idx = stack.pop()
            result[idx] = arr[i]
        stack.append(i)
    
    return result

# 예시 실행
A = [3, 5, 2, 7, 5]
print(next_greater_or_equal(A))  # 출력 예: [5, 7, 7, -1, -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;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2609&quot; data-start=&quot;2557&quot;&gt;&lt;b&gt;문제:&lt;/b&gt; 각 원소에 대해 오른쪽에서 처음으로 자신보다 크거나 같은 원소를 찾는 문제.&lt;/li&gt;
&lt;li data-end=&quot;2653&quot; data-start=&quot;2610&quot;&gt;&lt;b&gt;단순 방법:&lt;/b&gt; 이중 반복문을 사용하면 &lt;b&gt;&lt;span&gt;&lt;span&gt;O(n^2)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&amp;nbsp;시간복잡도.&lt;/li&gt;
&lt;li data-end=&quot;2716&quot; data-start=&quot;2654&quot;&gt;&lt;b&gt;스택 사용:&lt;/b&gt; 스택을 이용하면 각 원소가 한 번씩만 처리되므로 &lt;b&gt;&lt;span&gt;&lt;span&gt;O(n)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&amp;nbsp;시간복잡도로 해결 가능.&lt;/li&gt;
&lt;li data-end=&quot;2790&quot; data-start=&quot;2717&quot;&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>알고리즘 &amp;amp; 자료구조</category>
      <category>next greater element</category>
      <category>Stack</category>
      <category>다음 큰 수</category>
      <category>스택</category>
      <category>자료구조</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/172</guid>
      <comments>https://fullmoon1344.tistory.com/172#entry172comment</comments>
      <pubDate>Thu, 6 Mar 2025 15:17:51 +0900</pubDate>
    </item>
    <item>
      <title>[백준 6198] 옥상 정원 꾸미기 - Python(스택)</title>
      <link>https://fullmoon1344.tistory.com/171</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6198&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/6198&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;&quot;다음 큰(또는 같은) 수(Next Greater or Equal Element)&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 단순 탐색 풀이 - O(n^2)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741238822226&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from functools import reduce
import sys

input = sys.stdin.readline
N = int(input().strip())
heights = list(int(input().strip()) for _ in range(N))

def get_answer():
    results = []

    while heights:
        height = heights.pop(0)
        count = 0
        for i in range(len(heights)):
            if heights[i] &amp;lt; height:
                count += 1
            else:
                break
        results.append(count)
    print(reduce(lambda acc, value: acc + value, results))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 스택 이용 최적화 풀이 - O(n)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741238856017&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from functools import reduce
import sys

input = sys.stdin.readline
N = int(input().strip())
heights = list(int(input().strip()) for _ in range(N))

def get_answer():
    stack = []
    total_visible = 0

    for i in range(N - 1, -1, -1):
        # 현재 빌딩보다 낮은 빌딩들은 스택에서 제거
        while stack and heights[stack[-1]] &amp;lt; heights[i]:
            stack.pop()

        # 스택에 차단 빌딩이 있으면 그 위치까지 빌딩 수 계산
        if stack:
            visible = stack[-1] - i - 1
        else:
            # 오른쪽의 모든 빌딩이 보이는 경우
            visible = N - i - 1
        total_visible += visible

        # 현재 빌딩 인덱스를 추가
        stack.append(i)

    return total_visible

print(get_answer())&lt;/code&gt;&lt;/pre&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 data-end=&quot;1863&quot; data-start=&quot;1775&quot;&gt;스택을 활용하면 오른쪽에서 왼쪽으로 한 번의 순회로 각 빌딩의 보이는 옥상 수를 구할 수 있어 전체 시간복잡도를 &lt;span&gt;&lt;span&gt;O(n)&lt;/span&gt;&lt;/span&gt;으로 최적화할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;1915&quot; data-start=&quot;1864&quot;&gt;이 방법은 각 빌딩이 스택에 단 한 번 들어가고 한 번씩만 제거되므로 효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Python</category>
      <category>백준 6198</category>
      <category>알고리즘</category>
      <category>알고리즘 테스트</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/171</guid>
      <comments>https://fullmoon1344.tistory.com/171#entry171comment</comments>
      <pubDate>Thu, 6 Mar 2025 15:03:32 +0900</pubDate>
    </item>
    <item>
      <title>[백준 2493] 탑 - Python(스택)</title>
      <link>https://fullmoon1344.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2493&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2493&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;b&gt;1. 단순 탐색 풀이 - O(n^2)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도 O(n^2)를 가지는 모든 요소를 탐색하는 방식으로 풀이했다. 당연히, 시간초과로 실패했다.&lt;/p&gt;
&lt;pre id=&quot;code_1741183821187&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]
    for i in range(len(heights), 0, -1):
        if i == 1:
            break
        current_top = heights[i - 1]
        for j in range(i - 1, 0, -1):
            left_top = heights[j - 1]
            if left_top &amp;gt;= current_top:
                answer[i - 1] = j
                break
    print(&quot; &quot;.join(map(str, answer)))

get_answer()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 스택 이용한 풀이 - 최악 O(n^2), 최선 O(n)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택을 사용하면 각 요소를 한 번만 처리(삽입/삭제)하게 되어, 이미 검사한 요소들을 재검사하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 스택 기반 알고리즘에서는 리스트를 한 번 순회하며, 현재 요소보다 작은(또는 큰) 후보들은 스택에서 제거하는 식으로 진행하여, 각 요소가 스택에 한 번 push되고 최대 한 번 pop되도록 한다.&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;br /&gt;예를 들어, 만약 리스트가 내림차순 정렬되어 있다면, 각 요소마다 내부 for 루프가 거의 전체 남은 요소를 검사하게 된다.&lt;br /&gt;따라서 전체 연산 횟수는 약 &lt;span&gt;&lt;span&gt;n+(n&amp;minus;1)+(n&amp;minus;2)+&amp;hellip;+1=O(n^2)&lt;/span&gt;&lt;/span&gt;가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1741184079866&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]

    while heights:
        height = heights.pop()

        for i in range(len(heights) - 1, -1, -1):
            if height &amp;lt; heights[i]:
                answer[len(heights)] = i + 1
                break

    print(&quot; &quot;.join(map(str, answer)))

get_answer()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 스택 이용한 풀이 - 최적화 O(n)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1741187577243&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]
    stack = [] # 인덱스 저장

    for i in range(len(heights)):
        height = heights[i]
        # 스택에 있는 인덱스의 높이가 현재보다 작으면, 이들은 현재 탑의 신호를 받을 수 없음
        while stack and heights[stack[-1]] &amp;lt; height:
            stack.pop()

        # 스택이 남아 있으면, 스택의 최상단 인덱스가 현재 탑의 신호를 수신하는 탑이다
        if stack:
            answer[i] = stack[-1] + 1
        else:
            answer[i] = 0

        stack.append(i) # 현재 탑 인덱스 추가

    print(&quot; &quot;.join(map(str, answer)))

get_answer()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 2493</category>
      <category>스택</category>
      <category>알고리즘</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/170</guid>
      <comments>https://fullmoon1344.tistory.com/170#entry170comment</comments>
      <pubDate>Thu, 6 Mar 2025 00:14:53 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] DFS, BFS</title>
      <link>https://fullmoon1344.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DFS(Depth-First Search)와 BFS(Breadth-First Search)는 그래프나 트리 탐색에서 사용하는 대표적인 탐색 알고리즘이다. 두 알고리즘 모두 특정 노드를 방문하고, 그 노드와 연결된 다른 노드들을 탐색하는 방식으로 동작한다. DFS는 깊이를 우선으로 탐색하고, BFS는 너비를 우선으로 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DFS (Depth-First Search, 깊이 우선 탐색)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 탐색할 때 가능한 한 &lt;b&gt;깊게&lt;/b&gt; 들어가는 방식으로 탐색한다. 즉, 한 경로를 따라 끝까지 가고, 더 이상 갈 곳이 없으면 다시 돌아와 다른 경로를 탐색한다. 이 방식은 &lt;b&gt;재귀&lt;/b&gt;나 &lt;b&gt;스택&lt;/b&gt;을 사용해 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DFS 예시: 그래프 탐색&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727850478844&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function dfs(graph, start, visited = new Set()) {
    // 현재 노드 방문 처리
    visited.add(start);
    console.log(start);

    // 현재 노드와 연결된 노드들을 재귀적으로 방문
    for (let neighbor of graph[start]) {
        if (!visited.has(neighbor)) {
            dfs(graph, neighbor, visited);
        }
    }
}
// 그래프 표현 (인접 리스트)
const graph = {
    1: [2, 3],
    2: [4],
    3: [5],
    4: [],
    5: []
}

dfs(graph, 1);  // 출력: 1 2 4 3 5&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;DFS의 시간 복잡도&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;시간 복잡도: &lt;b&gt;O(V + E)&lt;/b&gt; (V는 노드 수, E는 간선 수)&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.  BFS (Breadth-First Search, 너비 우선 탐색)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS는 탐색할 때 &lt;b&gt;가까운 노드부터&lt;/b&gt; 차례대로 방문한다. 즉, 특정 노드에서 연결된 모든 노드를 먼저 탐색한 후, 그 다음 깊이의 노드들을 탐색한다. 이 방식은 &lt;b&gt;큐(Queue)&lt;/b&gt; 자료 구조를 사용해 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BFS 예시: 최단 경로 탐색&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727850861571&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function bfs(graph, start) {
    const queue = [start]; // 탐색할 노드를 저장할 큐
    const visited = new Set(); // 방문한 노드를 저장할 집합
    visited.add(start);

    while (queue.length &amp;gt; 0) {
        const node = queue.shift(); // 탐색할 노드를 큐에서 꺼냄
        console.log(node);

        for (let neighbor of graph[node]) {
            if (!visited.has(neighbor)) {
                visited.add(neighbor);
                queue.push(neighbor);
            }
        }
    }
}

// 그래프 표현 (인접 리스트)
const graph = {
    1: [2, 3],
    2: [4],
    3: [5],
    4: [],
    5: []
};

bfs(graph, 1);  // 출력: 1 2 3 4 5&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;BFS의 시간 복잡도&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;시간 복잡도:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;O(V + E)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(V는 노드 수, E는 간선 수)&lt;/li&gt;
&lt;li&gt;모든 노드와 모든 간선을 한 번씩 방문하기 때문에 DFS와 마찬가지로 같은 복잡도를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;DFS와 BFS의 차이&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;탐색 방식: DFS는 깊이 우선으로 탐색하며, BFS는 너비 우선으로 탐색한다.&lt;/li&gt;
&lt;li&gt;구현 방식: DFS는 재귀나 스택을 사용하고, BFS는 큐를 사용해 구현한다.&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;DFS는 경로 탐색(예: 미로 찾기)나 백트래킹이 필요한 문제에 적합하다.&lt;/li&gt;
&lt;li&gt;BFS는 그래프에서 최단 경로를 찾는 문제에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;예시 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 미로 찾기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;: N x M 크기의 미로가 주어질 때, 출발점에서 도착점까지 최단 경로를 찾으세요. 벽은 통과할 수 없으며, 상하좌우로만 이동할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727854993964&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// BFS
function shortestPath(maze, start, end) {
    const directions = [[0, 1], [1, 0], [0, -1], [1, 0]];
    const queue = [[start, 0]]; // [현재위치, 이동거리]
    const visited = new Set();
    visited.add(start.toString());

    while (queue.length &amp;gt; 0) {
        const [[x, y], distance] = queue.shift();

        if (x === end[0] &amp;amp;&amp;amp; y === end[1]) return distance;

        for (let [dx, dy] of directions) {
            const newX = x + dx;
            const newY = y + dy;

            if (newX &amp;gt;= 0 &amp;amp;&amp;amp; newY &amp;gt;= 0 &amp;amp;&amp;amp;
                newX &amp;lt; maze.length &amp;amp;&amp;amp; newY &amp;lt; maze[0].length &amp;amp;&amp;amp;
                maze[newX][newY] === 0 &amp;amp;&amp;amp; !visited.has([newX, newY].toString())
            ) {
                visited.add([newX, newY].toString());
                queue.push([[newX, newY], distance + 1]);
            }
        }
    }

    return -1; // 도착할 수 없는 경우
}

const maze = [
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 0, 0, 0]
];

console.log(shortestPath(maze, [0, 0], [4, 4]));  // 출력: 8 (최단 경로 거리)&lt;/code&gt;&lt;/pre&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 섬 개수 세기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;: 2차원 배열에서 육지를 1, 바다를 0으로 나타내는 섬의 지도에서, 섬(연결된 1의 집합)의 개수를 구하세요.&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;pre id=&quot;code_1727855571730&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// DFS
function numIslands(grid) {
    let count = 0;

    function dfs(grid, x, y) {
        if (x &amp;lt; 0 || y &amp;lt; 0 || x &amp;gt;= grid.length || y &amp;gt;= grid[0].length || grid[x][y] === '0') {
            return;
        }

        grid[x][y] = '0'; // 방문한 곳은 0 처리
        dfs(grid, x + 1, y);
        dfs(grid, x - 1, y);
        dfs(grid, x, y + 1);
        dfs(grid, x, y - 1);
    }

    for (let i = 0; i &amp;lt; grid.length; i++) {
        for (let j = 0; j &amp;lt; grid[0].length; j++) {
            if (grid[i][j] === '1') {
                count++;
                dfs(grid, i, j);
            }
        }
    }

    return count;
}

const grid = [
    ['1', '1', '0', '0', '0'],
    ['1', '1', '0', '0', '0'],
    ['0', '0', '1', '0', '0'],
    ['0', '0', '0', '1', '1']
];

console.log(numIslands(grid));  // 출력: 3 (3개의 섬)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 &amp;amp; 자료구조</category>
      <category>bfs</category>
      <category>dfs</category>
      <category>javascript</category>
      <category>그래프탐색</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>탐색 알고리즘</category>
      <category>트리탐색</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/169</guid>
      <comments>https://fullmoon1344.tistory.com/169#entry169comment</comments>
      <pubDate>Wed, 2 Oct 2024 16:53:46 +0900</pubDate>
    </item>
    <item>
      <title>[git] cherry-pick</title>
      <link>https://fullmoon1344.tistory.com/168</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;cherry-pick&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;git cherry-pick &amp;lt;commit_hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git cherry-pick이란 다른 브랜치에 있는 커밋을 선택적으로 내 브랜치(현재 브랜치)에 적용시킬 때 사용하는 명령어다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;git cherry-pick &amp;lt;commit_hash_1&amp;gt; &amp;lt;commit_hash_2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 뒤에 commit hash를 나열해 주면 여러 개를 한 번에 적용시킬 수 있다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;git cherry-pick &amp;lt;commit_hash_1&amp;gt;...&amp;lt;commit_hash_4&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;commit_hash_1&amp;gt;...&amp;lt;commit_hash_4&amp;gt; 처럼 명령을 하면, 1번~4번 사이의 커밋들을 cherry-pick 하게 된다. 이때, hash1이 시간상 가장 빠른 커밋이고, &lt;b&gt;hash1은 cherry-pick에 포함되지 않는다.&lt;/b&gt;&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;git cherry-pick &amp;lt;commit&amp;gt;: 해당 커밋을 현재 브랜치에 적용합니다.&lt;/li&gt;
&lt;li&gt;git cherry-pick -n &amp;lt;commit&amp;gt;: 해당 커밋을 적용하지만 커밋을 생성하지 않습니다. (드라이 런 모드)&lt;/li&gt;
&lt;li&gt;git cherry-pick -x &amp;lt;commit&amp;gt;: 적용된 커밋에 원본 커밋의 정보를 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;conflict가 발생하는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cherry-pick 하려는 커밋과 현재 브랜치 사이에 conflict가 발생할 수 있다. 이때 사용가능한 옵션이 두 가지가 있다고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;conflict를 수정하고 cherry-pick 진행하기&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 경우는 merge를 할 때처럼, conflict난 파일들을 수정한다.&lt;/li&gt;
&lt;li&gt;git add 로 수정된 코드를 git에 올린다.&lt;/li&gt;
&lt;li&gt;git cherry-pick -continue 명령어를 통해 다시 진행시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;cherry-pick 중단하기&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git cherry-pick -abort 명령어를 사용해 중단시켜, cherry-pick 이전 상태로 돌아간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cherry-pick을 하는 경우 같은 내용을 갖고 있는 커밋이 여러 개 생기기 때문에, 나중에 누가 누굴 cherry-pick 했는지 모르는 상황이 생길 수 있다. 가능하다면 git rebase를 사용하는 방법이 좋다고 한다.&lt;/p&gt;</description>
      <category>git</category>
      <category>git</category>
      <category>git cherry-pick</category>
      <category>git commit 옮기기</category>
      <category>git conflict</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/168</guid>
      <comments>https://fullmoon1344.tistory.com/168#entry168comment</comments>
      <pubDate>Sat, 22 Jul 2023 17:19:26 +0900</pubDate>
    </item>
    <item>
      <title>Singleton Pattern 개념 익히기</title>
      <link>https://fullmoon1344.tistory.com/167</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴은 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 전체 시스템에서 오직 '한 개'만 생성되도록 보장하는 것을 목적으로 한다. &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이 패턴을 사용하면 전역 변수를 사용하지 않고도 특정 클래스의 인스턴스에 대한 전역 접근이 가능하며, 이를 통해 코드의 유연성과 유지 보수성을 높일 수 있다.&lt;/span&gt; 예를 들어 로깅 라이브러리를 구현할 때, 여러 곳에서 로그를 출력하게 된다고 해보자. 이때 각각의 로그 출력 메서드마다 로깅 객체를 생성된다면 시스템 리소스의 낭비가 발생할 수 있다. 따라서, singleton 패턴을 사용하여 로깅 객체를 전체 시스템에서 한 번만 생성하도록 보장할 수 있다.&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;다음은 Java로 나타낸 싱글톤 패턴의 예제 코드다.&lt;/p&gt;
&lt;pre id=&quot;code_1680791021004&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Logger {
    // 인스턴스 변수
    private static Logger instance = null;
    
    // 생성자를 private로 만들어 다른 클래스에서 인스턴스 생성을 막는다.
    private Logger() {}
    
    // getInstance() 메소드를 통해 전역적으로 유일한 인스턴스를 반환한다.
    public staitc Logger getInstance() {
    	if(instance == null) {
        	instance = new Logger();
        }
        return instance;
    }
    
    // 로그를 출력하는 메소드
    public void log(String message){
    	System.out.println(message);
    }
}

// Logger 인스턴스를 가져와서 로그를 출력한다.
Logger logger = Logger.getInstance();
logger.log(&quot;Hello, world!&quot;);&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;위 예시에서, Logger 클래스는 생성자를 private으로 만들어 다른 클래스에서 인스턴스를 생성할 수 없도록 했다. 대신, getInstance() 메서드를 통해 전역적으로 유일한 인스턴스를 반환하도록 했다. 이렇게 함으로써, Logger 클래스의 인스턴스는 오직 한 개만 생성될 수 있도록 보장할 수 있다. 위의 코드에서 Logger 클래스의 인스턴스를 가져와서&amp;nbsp; 로그를 출력하게 되는데, 이때 getInstance() 메서드를 사용해 전역적으로 유일한 인스턴스를 가져오도록 했다. 따라서, Logger 클래스의 인스턴스는 오직 한 개만 생성되며, 로그를 출력하는 코드에서는 이를 공유하여 사용할 수 있다.&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;Singleton 패턴의 &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;1. 전역적인 인스턴스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;singleton 패턴은 전역적으로 하나의 인스턴스만을 유지하므로, 모든 코드에서 해당 인스턴스에 대해 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 리소스 절약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 여러 번 생성하지 않으므로, 시스템 자원을 절약할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 데이터 공유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 인스턴스를 공유하기 때문에, 데이터를 공유하거나 데이터를 수정하기 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Singleton 패턴의 &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;1. 테스트가 어려움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역적인 인스턴스를 사용하므로, 테스트할때 다른 인스턴스를 사용할 수 없어 테스트가 어려울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 결합도 증가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 객체와의 결합도가 높아지므로, 유지보수가 어려워질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 멀티스레딩 이슈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴을 사용할 때, 멀티스레드 환경에서 동시에 인스턴스를 생성하려는 이슈가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;그럼 Singleton 패턴을 &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;1. 전역적으로 사용되는 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 객체가 전체 시스템에서 단 하나만 존재해야할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 자원의 낭비를 방지해야 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 여러 개 생성할 필요가 없으므로, 시스템 자원의 낭비를 방지하기 위해 싱글톤 패턴을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 공유 자원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴을 사용하여 전역적으로 인스턴스를 공유할 수 있다. 예를 들어, 로깅이나 설정 정보를 공유하는 경우에 사용하기 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 동일한 객체의 상태를 유지할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 패턴을 사용해 하나의 인스턴스를 사용하면, 해당 객체의 상태를 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Singleton 패턴은 다음과 같은 특징을 가지고 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #444654; color: #d1d5db; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스 내부에서 인스턴스를 생성하고, 외부에서 직접 생성할 수 없도록 생성자를 private으로 선언한다.&lt;/li&gt;
&lt;li&gt;유일한 인스턴스에 접근하기 위한 public static 메서드를 제공한다.&lt;/li&gt;
&lt;li&gt;인스턴스가 이미 생성되어 있으면 해당 인스턴스를 반환하고, 생성되어 있지 않으면 인스턴스를 생성한 후 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 TypeScript를 사용한 싱글톤 패턴 예제 코드다.&lt;/p&gt;
&lt;pre id=&quot;code_1680792321340&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Singleton {
    private static instance: Singleton;
    
    // 생성자를 private으로 선언해 외부에서 인스턴스 생성을 막는다.
    private constructor() {}
    
    // 인스턴스를 반환하는 메서드를 static으로 선언하여 전역적으로 접근할 수 있게한다.
    public static getInstance(): Singleton {
        if(!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
    
    public doSomething(): void {
    	console.log(&quot;Singleton instance is doing something...&quot;);
    }
}

// Singleton 클래스의 인스턴스를 생성할 수 없다.
// const instance = new Singleton(); // Error: 'constructor' is private.

// Singleton 클래스의 인스턴스는 getInstance 메서드를 통해서만 생성한다.
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

instance1.doSomething(); // &quot;Singleton instance is doing something...&quot;
instance2.doSOmething(); // &quot;Singleton instance is doing something...&quot;&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;위 코드에서 Singleton 클래스를 정의하고, private 생성자를 선언해 외부에서 인스턴스 생성을 막는다. 인스턴스를 반환하는 getInstance 메서드를 static으로 선언해 전역적으로 접근할 수 있도록 한다. 이때, 인스턴스가 생성되어 있지 않은 경우에만 인스턴스를 생성하고 반환하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 인스턴스를 사용할 때에는 getInstance 메서드를 호출해 인스턴스를 생성하고, 반환된 인스턴스에서 메서드를 호출한다. 인스턴스는 한 번 생성되면 전역적으로 유지되므로, 여러 곳에서 같은 인스턴스를 사용할 수 있다.&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>Singleton Pattern</category>
      <category>개발공부</category>
      <category>개발자</category>
      <category>디자인 패턴</category>
      <category>백엔드</category>
      <category>프론트엔드</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/167</guid>
      <comments>https://fullmoon1344.tistory.com/167#entry167comment</comments>
      <pubDate>Thu, 6 Apr 2023 23:51:20 +0900</pubDate>
    </item>
    <item>
      <title>Entity, Repository 개념 &amp;amp; 프론트엔드에 적용하기</title>
      <link>https://fullmoon1344.tistory.com/166</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Entity와 Repository는 개발에서 많이 사용되는 개념이다. Entity는 도메인 모델의 구성 요소로, 하나 이상의 속성을 가지며 유일하게 구분될 수 있는 식별자를 가지고 있다. Repository는 DB나 File system과 같은 저장소에 접근하는 객체이다. Repository는 Entity를 생성, 읽기, 수정, 삭제(CRUD)하는 데 사용된다.&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;예를 들어, 블로그 게시물을 저장하는 웹 애플리케이션을 만든다고 가정해보자. 이 애플리케이션에서는 게시물을 생성, 읽기, 수정, 삭제해야 한다. 게시물을 저장하는 DB가 있다면, Entity는 게시물을 나타내고 Repository는 DB에 접근하는 객체를 나타낸다. 여기서 Entity는 게시물의 속성을 가지고 있다. 예를 들어, 게시물의 제목, 내용, 작성자, 작성일 등이 게시물의 속성이 될 수 있다. 게시물의 식별자는 유일한 값을 가지며, 이를 통해 다른 게시물과 구분할 수 있다.&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;Repository는 DB와 같은 저장소에 접근하는 '객체'이다. 예를 들어, MySQL DB에 게시물을 조회하는 경우 Repository는 MySQL DB에 접근하여 데이터를 조회하고 변경하는 역할을 한다. Repository는 다음과 같은 인터페이스를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680644580740&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface PostRepository {
    create(post: Post): Promise&amp;lt;Post&amp;gt;;
    findById(id: number): Promise&amp;lt;Post | null&amp;gt;;
    findAll(): Promise&amp;lt;Post[]&amp;gt;;
    update(post: Post): Promise&amp;lt;Post&amp;gt;;
    delete(id: number): Promise&amp;lt;boolean&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 인터페이스는 게시물을 생성, 조회, 수정, 삭제하는 메서드를 정의한다. &lt;b&gt;create&lt;/b&gt; 메서드는 게시물을 생성하고, &lt;b&gt;findById&lt;/b&gt; 메서드는 주어진 ID에 해당하는 게시물을 조회한다. &lt;b&gt;findAll&lt;/b&gt; 메서드는 모든 게시물을 조회한다. &lt;b&gt;update&lt;/b&gt; 메서드는 주어진 게시물을 수정하고, &lt;b&gt;delete&lt;/b&gt; 메서드는 주어진 ID에 해당하는 게시물을 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Entity와 Repository 패턴은 도메인 모델과 데이터 액세스 로직을 분리하고, 단일 책임 원칙(Single Responsibility Principle)을 따르기 위해 사용된다. 이를 통해 코드의 가독성, 유지보수성, 확장성을 향상할 수 있다.&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;보통 프론트엔드에서는 Entity와 Repository 패턴을 직접적으로 적용하기보다는, Redux와 같은 상태관리 라이브러리를 사용해 상태를 관리한다. 하지만, 비즈니스 로직을 분리하고, 코드를 유지보수 가능한 상태로 유지하기 위해 Entity와 Repository를 적용할 수 있다. 예를 들어, 게시물을 생성하고 조회하는 간단한 React 애플리케이션을 만든다고 해보자. 이 애플리케이션에는 게시물을 생성, 목록 조회하는 기능을 제공한다.&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;먼저, Entity를 정의해 보자. 이 애플리케이션에서는 게시물이라는 모델이 필요하므로, 다음과 같은 'Post' 모델을 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1680644850715&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
  id: number;
  title: string;
  content: string;
  author: string;
  createdAt: Date;
}&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;다음으로 Repository를 정의해보자. 이 애플리케이션에서는 게시물 목록을 관리하기 위해 &amp;lsquo;PostRepository&amp;rsquo;를 만들어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1680644874864&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface PostRepository {
  create(post: Post): Promise&amp;lt;Post&amp;gt;;
  findAll(): Promise&amp;lt;Post[]&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &amp;lsquo;create&amp;rsquo; 메서드는 새로운 게시물을 생성하고, &amp;lsquo;findAll&amp;rsquo; 메서드는 모든 게시물을 조회하는 역할을 한다. 이 Repository를 구현하기 위해서는, 서버 API와 통신을 처리하는 클라이언트 코드를 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680644953297&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ApiPostRepository implements PostRepository {
  async create(post: Post): Promise&amp;lt;Post&amp;gt; {
    const response = await fetch('/api/posts', {
      method: 'POST',
      body: JSON.stringify(post),
      headers: {
        'Content-Type': 'application/json',
      },
    });
    return response.json();
  }

  async findAll(): Promise&amp;lt;Post[]&amp;gt; {
    const response = await fetch('/api/posts');
    return response.json();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 &lt;span style=&quot;color: #000000;&quot;&gt;ApiPostRepository&lt;/span&gt; 클래스는 HTTP API를 통해 게시물을 생성하고, 조회하는 역할을 수행한다. 이 클래스를 사용하여 게시물을 생성하고, 조회하는 React 컴포넌트를 작성할 수 있다. 간단한 예시 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680645053564&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function PostList() {
  const [posts, setPosts] = useState&amp;lt;Post[]&amp;gt;([]);

  useEffect(() =&amp;gt; {
    const repository = new ApiPostRepository();
    repository.findAll().then(setPosts);
  }, []);

  return (
    &amp;lt;div&amp;gt;
      {posts.map(post =&amp;gt; (
        &amp;lt;div key={post.id}&amp;gt;
          &amp;lt;h2&amp;gt;{post.title}&amp;lt;/h2&amp;gt;
          &amp;lt;p&amp;gt;{post.content}&amp;lt;/p&amp;gt;
          &amp;lt;p&amp;gt;By {post.author} on {post.createdAt.toISOString()}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  );
}

function PostForm() {
  const [post, setPost] = useState
	//..&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>Entity</category>
      <category>Frontend</category>
      <category>React</category>
      <category>repository</category>
      <category>개발자</category>
      <category>프론트엔드</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/166</guid>
      <comments>https://fullmoon1344.tistory.com/166#entry166comment</comments>
      <pubDate>Wed, 5 Apr 2023 06:53:14 +0900</pubDate>
    </item>
    <item>
      <title>[React Native] patch-package 사용</title>
      <link>https://fullmoon1344.tistory.com/165</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;patch-package&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서드파티 라이브러리를 커스텀한 상태가 배포상태에서도 지속되도록 관리해주는 패키지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, node_modules에 수정한 사항이 git으로 관리되고 어떠한 실행 환경에서도 적용되도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;patch-package를 사용할 시 매번 버전이 달라질 때마다 patch 파일의 버전을 인스톨 되어 있는 버전과 맞춰줘야 하는 &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;size18&quot;&gt;&lt;b&gt;1. 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install &amp;mdash;save-dev patch-package
&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;size18&quot;&gt;&lt;b&gt;2. 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// package.json

&quot;scripts&quot;: {
	&quot;postinstall&quot;: &quot;patch-package&quot;, // 해당 스크립트 추가
},&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;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 변경하고자 하는 패키지 코드 수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 코드를 원하는대로 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. patch 적용&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;npx patch-package [변경한 패키지 이름]
&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;size18&quot;&gt;&lt;b&gt;5. &amp;ldquo;patches&amp;rdquo;라는 폴더가 생기고, 폴더 안에 변경한 패치 내용이 저장되어 있음을 확인&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6. node_moduels 을 지웠다가 npm install 해보면 자동으로 패치내용들이 적용되어 있음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;주의) 수정한 패키지의 patches 버전과 package.json의 패키지 버전이 다르면 에러를 유발하니 버전 체크하는 것이 중요!&lt;/span&gt;&lt;/p&gt;</description>
      <category>JavaScript/React Native</category>
      <category>javascript</category>
      <category>patch-package</category>
      <category>React</category>
      <category>React Native</category>
      <category>개발공부</category>
      <category>개발자</category>
      <category>리액트 네이티브</category>
      <category>코딩공부</category>
      <category>프론트엔드</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/165</guid>
      <comments>https://fullmoon1344.tistory.com/165#entry165comment</comments>
      <pubDate>Wed, 7 Dec 2022 21:23:17 +0900</pubDate>
    </item>
    <item>
      <title>[React] 01. 리액트란 무엇인가?</title>
      <link>https://fullmoon1344.tistory.com/164</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서론&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선 리액트 공식 문서에서 리액트를 '&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리'로 소개하고 있다. SPA(Single Page Application)형태로 하나의 페이지에서 보여지는 컴포넌트 조합을 동적으로 바꿔가며 화면을 표현하는 형태다. Virtual DOM이라는 개념을 사용해 이러한 SPA를 구현하고 있다. 장, 단점이 존재하지만 자세한 내용은 나중에 다루도록 하겠다.&lt;/span&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;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React 특징&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;1. Component 구조&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. JSX&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Data Flow&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. Virtual DOM&lt;/span&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;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Component&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;컴포넌트&quot;라고 불리는 작고 고립된 부품을 이용해 복잡한 UI를 구성하도록 해주는 것이 리액트이다. 컴포넌트에는 몇 가지 종류가 있다. 크게 2가지로 나누자면 '함수형 컴포넌트', '클래스형 컴포넌트'가 있다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선 클래스형 컴포넌트는 다음과 같은 구조를 가지고 있다. 아래에 간단한 예시를 작성해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643029995322&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyClassComponent extends React.Component {
    render() {
    	return(
            &amp;lt;div className=&quot;my-component&quot;&amp;gt;
            	&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;
                &amp;lt;ul&amp;gt;
                    &amp;lt;li&amp;gt;React!&amp;lt;/li&amp;gt;
                    &amp;lt;li&amp;gt;React Native&amp;lt;/li&amp;gt;
                &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 컴포넌트는 재사용 가능하고 여러 컴포넌트들을 조합해 다양한 형태의 UI를 쉽게 만들 수 있다. 불필요한 반복적 코드를 줄이고 빠르게 UI를 구성할 수 있어 개발 생산성에 있어 장점을 가진다. 또한 작은 형태의 컴포넌트는 테스트하기 용이해 코드를 유지보수하기에도 도움이 된다. 아래에 여러 작은 컴포넌트를 조합한 예시 코드가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643031616223&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class App extends Component {
  render() {
    return (
      &amp;lt;Layout&amp;gt;
        &amp;lt;Header /&amp;gt;
        &amp;lt;Navigation /&amp;gt;
        &amp;lt;Content&amp;gt;
          &amp;lt;Sidebar&amp;gt;&amp;lt;/Sidebar&amp;gt;
          &amp;lt;Router /&amp;gt;
        &amp;lt;/Content&amp;gt;
        &amp;lt;Footer&amp;gt;&amp;lt;/Footer&amp;gt;
      &amp;lt;/Layout&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. JSX&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 HTML의 태그 문법과 비슷한 형태로 ui 화면을 표현하고 있다. HTML과 비슷하지만 HTML과는 다르다. 리액트에서는 JSX라 하는 JavaScript를 확장한 문법을 사용한다. 간단하게 JavaScript이지만 화면을 표시하기 위해서 조금 더 확장된 개념이라고 보면 된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;JSX란?&lt;br /&gt;Javascript를 확장한 문법입니다.&lt;br /&gt;&lt;br /&gt;React에서는 이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등 렌더링 로직이 본질적으로 다른 UI 로직과 연결된다는 사실을 받아들입니다.&lt;br /&gt;&lt;br /&gt;React는 별도의 파일에 마크업과 로직을 넣어 기술을 인위적으로 분리하는 대신, 둘 다 포함하는 &amp;ldquo;컴포넌트&amp;rdquo;라고 부르는 느슨하게 연결된 유닛으로 관심사를 분리합니다. 이후 섹션에서 다시 컴포넌트로 돌아오겠지만, JS에 마크업을 넣는 게 익숙해지지 않는다면 이 이야기가 확신을 줄 것입니다.&lt;br /&gt;&lt;br /&gt;React는 JSX 사용이 필수가 아니지만, 대부분의 사람은 JavaScript 코드 안에서 UI 관련 작업을 할 때 시각적으로 더 도움이 된다고 생각합니다. 또한 React가 더욱 도움이 되는 에러 및 경고 메시지를 표시할 수 있게 해줍니다.&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/introducing-jsx.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;출처: React 공식문서&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Data Flow - 단방향 데이터 바인딩&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단방향 데이터 바인딩은 간단히 말해 데이터의 흐름이 한 방향으로만 이뤄진다. 부모 요소에서 자식 요소로 전달이 되고 적절한 Event를 통해 데이터를 갱신하게 된다. 양방향 바인딩은 앱의 규모가 커질수록 추적하기 어려워지고 그에 따라 복잡해지게 된다. 이런 단점을 보완해 데이터 흐름을 보다 예측 가능하도록 하고자 리액트에서 단방향 형태로 바인딩을 하도록 했다고 한다. 단방향, 양방향 각각 장, 단점이 존재하기에 사용하는 프레임워크나 라이브러리에 따라 사용되는 데이터 바인딩 형태도 다 다르다. 조금 더 깊은 내용은 검색을 하면 많이 나오니 참고하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. Virtual DOM&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 화면을 그리기 위해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction&quot;&gt;DOM(Document Object Model)&lt;/a&gt;이라는 개념을 사용한다. DOM은 HTML 파일 내용을 토대로 만들어지는데, JavaScript와 같은 스크립팅 언어로 수정할 수 있도록 만들어진, 웹 페이지의 객체 지향 표현이다. DOM은 브라우저가 화면을 그리기 위해서 필요한 정보가 트리 형태로 저장된 데이터다. DOM에 변화가 생기면 렌더 트리를 재생성하고 레이아웃을 만들고 다시 보여주는 과정이 반복된다. DOM 트리가 재생성되는 과정에서 모든 요소들이 다시 계산되고 많은 연산을 반복하게 되면서 비효율적이게 된다. React에서는 이런 불필요하고 비효율적인 방식을 개선하고자 Virtual DOM이라는 개념을 도입했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 Virtual DOM은 UI의 이상적인 또는 가상의 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 실제 DOM과 동기화하는 프로그래밍적 개념이다. 뷰에서 변화가 생기면 실제 DOM에 적용되기 전 가상 DOM에서 먼저 적용이 되고 비교를 통해 최종적 결과가 실제 DOM으로 전달이 된다. 이런 DOM 관리 과정을 리액트 내에서 자동화, 추상화를 통해 불필요한 연산 비용을 줄이고 성능을 높이도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참고자료: &lt;a href=&quot;https://ko.reactjs.org/docs/faq-internals.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;리액트 공식문서 - Virtual DOM&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>React</category>
      <category>React Component</category>
      <category>react jsx</category>
      <category>react virtual dom</category>
      <category>Spa</category>
      <category>리액트 개념 공부</category>
      <category>프론트엔드</category>
      <category>프론트엔드 개발자</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/164</guid>
      <comments>https://fullmoon1344.tistory.com/164#entry164comment</comments>
      <pubDate>Mon, 24 Jan 2022 23:55:27 +0900</pubDate>
    </item>
    <item>
      <title>[2021 회고록] '새로운'것들이 많았던 2021년 서울살이</title>
      <link>https://fullmoon1344.tistory.com/163</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;2021년이 어느덧 지나가고 2022년을 맞이하면서 회고를 작성하게 되었다. 처음으로 작성해보는 회고록이라.. 어떤 내용으로 채워나가야 할지 모르겠다. 작년에 있었던 일들을 하나씩 떠올리며 글을 작성하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&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;720&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwbGKZ/btrpvS4UASn/R2yCSmz7HfHghC3TgZlPO1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwbGKZ/btrpvS4UASn/R2yCSmz7HfHghC3TgZlPO1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwbGKZ/btrpvS4UASn/R2yCSmz7HfHghC3TgZlPO1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cwbGKZ/btrpvS4UASn/R2yCSmz7HfHghC3TgZlPO1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;불행 중 다행?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;작년 1월, 2월 졸업을 앞두고 취직을 하기 위해 이곳저곳 이력서를 제출하고 면접을 보러 다니며 바쁘게 보냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기억으로는 40~50개 정도 지원했던 것 같다. 친구들은 전공을 살려 취직을 했고, 나는 조금은 다른 길인 개발자로 진로를 정하면서 불확실성에 대한 두려움이 많았었다. 그래도 처음으로 내가 하고 싶었던 일이었고 지금이 아니면 계속해서 수동적으로 삶을 살 거 같아 공부를 열심히 했던 기억이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;지원했던 회사들 중 몇 군데 면접 제의가 와서 서울과 대구를 왕복하며 면접을 보러 다녔다. 그렇게 첫 회사에 취직을 하게 됐다. 처음에는 원래 이런 식으로 일을 하는 것인가 싶었다. 아직 아무것도 모르는 상태였으니, 그냥 그런가 보다 하며 회사 생활을 했다. 동료 직원 분들도 다 좋으신 분들이었고 잘 지낼 수 있어 재밌었다. 하지만 회사가 경영적으로 문제가 있었는데.. 이 문제로 난 이직을 하게 됐다.(지금 생각해도 문제가 심히 많았다..) 첫 사회생활? 경험을 했다고 생각하고 빠르게 이직을 했다.&amp;nbsp; 다른 분들도 다 이직을 하셨다고 알고 있다. ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;새로운 회사, 첫 이직 도전!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2021년 6월, 첫 이직이자 나의 2번째 회사인 '반려생활'로 오게 됐다. 뭔가 생각했던 이직 과정보다 후다닥 빠르게 진행됐었다. 서류를 넣고 면접을 2번을 보고 합격하기까지 1주일 안에 이뤄졌다. 처음에는 솔직히 느낌이 살짝 싸했었다..ㅎㅎ 생각보다 너무 쉽게 이직을 해버려서 다들 이런가 하는 생각이 들었다. 아무튼, 성공적인 이직을 하고 현재까지 열심히 일 하고 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;처음 한 달 정도는 파일럿 과제를 수행하고 강의를 들었다. 로그인 페이지를 만드는 것이었는데 React와 TypeScript를 사용했다. 그리고 클래스형을 사용해 객체 지향 형태로 코드를 작성했었다. 간단할 줄 알았는데.. 쉽지 않았다.. 우선은 class 사용에 익숙하지 않았고 TypeScript 또한 처음으로 사용해봤기 때문이다. 이 과제를 수행하면서 레이아웃과 아키텍처에 대한 필요성을 알게 됐고, 그 외에 정말 많은 것들을 알게 됐다. 내가 여태 했던 것들은 정말 막? 하는 것이었던 걸.. 알 수 있었다. 아직까지도 갈 길이 멀다 ㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;입사한 지 2달쯤 돼서 첫 일을 하게 됐다. React Native로 만든 앱 프론트 단을 담당하게 됐다. 간단한 것부터 시작해 조금씩 더 많은 작업들을 해나갔다. 일을 하면서 느낀 것들이 정말 많다. 그중 하나가 코드를 작성할 때 고려해야 할 것들이 정말 많고, 단순히 코드를 작성할 줄 안다고 일을 잘하는 게 아니라는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;React Native를 사용하면서 가장 답답했던? 부분이 자료나 예시를 찾기가 쉽지 않았던 것이다. 관련 라이브러리나 자료들이 업데이트가 엄청 많았고 그래서 사용하던 기능들 중 바꿔야하는 부분들이 생각보다 많았었다. 뭐가 이리도 빠르게 바뀌는지.. 그래도 코드를 작성하고 배포, 빌드, 테스트까지 직접 해볼 수 있었고 그런 것들이 많은 공부가 됐다. 배포하니까 생각난 건데 안드로이드 배포할 때 구글 정책 때문에 reject을 진짜 수 없이 당했다..하.. 처음 2~3번은 그럴 수 있지 하다가 어느새 짜증이 났었다ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;거절 사유에 대한 명확한 정보를 주지 않고 거절만 하니 정말 답답했었다. 불과 몇일 전인 12월에도 새 버전이 같은 정책 사유(유저가 직접 콘텐츠를 만들고 참여하는 부분이 있으면, 유저나 콘텐츠에 대해 신고/차단 기능이 있어야 한다)로 거절당했다. 을의 입장이니 어쩌겠나.. 따라줘야지..ㅜㅜ 그래도 덕분에 '이런 문제도 고려해야 하는구나'&amp;nbsp; 알게 됐으니 좋게 생각하고 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1년간의 서울살이..그리고 2022년!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;서울에 올라와 많은 일도 있었고 좋은 사람들도 많이 만났던 한 해였다. 좋은 기억들만 남기고 새해에는 더 알차게 보내고자 한다. 2022년에는 해보고 싶은 일들을 다 진행하고 또 많은 사람들도 만났으면 좋겠다. 그리고 개발자로서 자기개발도 꾸준히 하고 계획한 것들을 착실하게 이루면서 인생에 있어 중요한 시점이 되는 한 해가 되길 바란다. 내년 이맘때쯤 작성할 새로운 회고록에 좋은 것들만 있었으면 좋겠다. 돈도 많이 벌고 여행도 많이 다닐거다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발자 인생스토리</category>
      <category>2021 회고록</category>
      <category>2022</category>
      <category>개발 회고록</category>
      <category>개발자</category>
      <category>프로 이직러</category>
      <category>프론트엔드</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/163</guid>
      <comments>https://fullmoon1344.tistory.com/163#entry163comment</comments>
      <pubDate>Sat, 1 Jan 2022 23:51:58 +0900</pubDate>
    </item>
    <item>
      <title>[React] react-testing-library 사용법 및 기본 개념</title>
      <link>https://fullmoon1344.tistory.com/162</link>
      <description>&lt;p&gt;리액트에서 테스트를 위해서 많이 사용하는 도구 중 하나인 &lt;code&gt;React Testing Library&lt;/code&gt;에 대한 포스트입니다.&lt;/p&gt;
&lt;p&gt;테스트에 사용되는 라이브러리들이 많으니 각자 본인에게 맞는 것을 활용해 공부하며 적용해 보는 것이 좋을 것 같습니다.&lt;/p&gt;
&lt;p&gt;아래에 리액트 공식 사이트에서 사용되는 테스트 유틸 및 라이브러리 예시를 확인해보세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Enzyme&lt;/code&gt;: React를 위한 JavaScript 테스트 유틸.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Jest&lt;/code&gt;: React를 포함한 JavaScript 테스트 프레임워크.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;react-testing-library&lt;/code&gt;: 가벼운 React DOM 테스트 유틸.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;React-unit&lt;/code&gt;: React를 위한 가벼운 단위테스트 라이브러리.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Skin-deep&lt;/code&gt;: 얕은 렌더링을 지원하는 React 테스트 유틸.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Unexpected-react&lt;/code&gt;: React 컴포넌트와 이벤트를 발생시켜주는 플러그인.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;설치&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn add @testing-library/react @testing-library/jest-dom

npm install --save react-testing-library @testing-library/jest-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;npm 또는 yarn을 이용해 &lt;code&gt;react-testing-library&lt;/code&gt;를 설치한다. &lt;code&gt;react-testing-library&lt;/code&gt; 가 &lt;code&gt;@testing-library/react&lt;/code&gt; 로 변경되었으니 주의!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jest-dom&lt;/code&gt; 도 &lt;code&gt;@testing-library/jest-dom&lt;/code&gt; 으로 변경됐다고 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;react-testing-library는 &amp;#39;사용자 관점&amp;#39;에서 테스트를 진행한다. 보통 Enzyme를 이용한 테스트 방식은 상태값, 상태 변수에 대해 테스트를 한다. 하지만 전자는 상태 관리는 컴포넌트의 구현 세부사항일 뿐이다. 즉, 상태 변수가 언제든 다른 컴포넌트로 옮겨지거나, react에서 vue.js로 바뀌더라도 테스트에는 문제가 없어야한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;다양한 쿼리&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;getBy*&lt;/strong&gt; 쿼리 (ex. &lt;code&gt;getByTestId&lt;/code&gt;, &lt;code&gt;getByText&lt;/code&gt;, &lt;code&gt;getByRole&lt;/code&gt;): 이 함수들은 &lt;strong&gt;동기적&lt;/strong&gt;(synchronous)이며 그 요소가 현재 DOM 안에 있는지 확인한다. 즉, 현재상태만 확인한다. 그렇지 않으면 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;findBy*&lt;/strong&gt; 쿼리 (ex. &lt;code&gt;findByText&lt;/code&gt;): 이 함수들은 &lt;strong&gt;비동기적&lt;/strong&gt;(asynchronous)이다. 그 요소를 찾을 때까지 일정 시간(기본 5초)을 기다린다. 만약 그 시간이 지난 후에도 요소를 찾을 수 없으면 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;queryBy*&lt;/strong&gt; 쿼리: 이 함수들은 getBy* 처럼 동기적이다. 하지만 요소를 찾을 수 없어도 에러를 발생시키지 않는다. 단지 &lt;code&gt;null&lt;/code&gt; 값을 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;테스트 디버깅하기&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-JavaScript&quot;&gt;render(&amp;lt;SomeComponent /&amp;gt;);
screen.debug(); //원하는 실행 지점에서 DOM트리를 console에 출력해, 디버깅할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;screen.debug()&lt;/code&gt; 는 브라우저의 개발 도구처럼 편리하고 상호작용적이진 않지만, 테스트 환경에서 어떤 일이 일어나고 있는지 확실히 알 수 있도록 도와준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-JavaScript&quot;&gt;const link = screen.getByRole(&amp;#39;link&amp;#39;, { name: /how it works/i });
screen.debug(link);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 &lt;code&gt;debug&lt;/code&gt; 함수에 파라미터를 전달하면 해당 요소만을 콘솔에 출력해준다.&lt;/p&gt;
&lt;h4&gt;Mocking&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jest.fn&lt;/code&gt;: Mock a function&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jest.mock&lt;/code&gt;: Mock a module → 자동적으로 모듈의 모든 함수를 mocking 해준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jest.spyOn&lt;/code&gt;: Spy or mock a function → 마찬가지로 모든 함수를 mocking 해주면서, 원래의 함수를 다시 복원할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;가장 기본적인 사용 방식은 함수를 mock 함수로 재할당하는 것이다. 재할당 된 함수가 쓰이는 어디서든지 mock 함수가 원래의 함수 대신 호출 될 것이다.&lt;/p&gt;
&lt;h5&gt;예시&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;jest.fn() mocking&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// app.js
import * as math from &amp;#39;./math.js&amp;#39;;

export const doAdd      = (a, b) =&amp;gt; math.add(a, b);
export const doSubtract = (a, b) =&amp;gt; math.subtract(a, b);
export const doMultiply = (a, b) =&amp;gt; math.multiply(a, b);
export const doDivide   = (a, b) =&amp;gt; math.divide(a, b);&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// math.js
export const add      = (a, b) =&amp;gt; a + b;
export const subtract = (a, b) =&amp;gt; b - a;
export const multiply = (a, b) =&amp;gt; a * b;
export const divide   = (a, b) =&amp;gt; b / a;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// app.test.js

import * as app from &amp;quot;./app&amp;quot;;
import * as math from &amp;quot;./math&amp;quot;;

math.add = jest.fn();
math.subtract = jest.fn();

test(&amp;quot;calls math.add&amp;quot;, () =&amp;gt; {
  app.doAdd(1, 2);
  expect(math.add).toHaveBeenCalledWith(1, 2);
});

test(&amp;quot;calls math.subtract&amp;quot;, () =&amp;gt; {
  app.doSubtract(1, 2);
  expect(math.subtract).toHaveBeenCalledWith(1, 2);
});&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;jest.mock() mocking&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;jest.mock(&amp;#39;./math.js&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드로 mocking하는 것은 본질적으로 아래 코드처럼 하는거랑 &lt;strong&gt;같다&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;export const add      = jest.fn();
export const subtract = jest.fn();
export const multiply = jest.fn();
export const divide   = jest.fn();&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// app.test.js

import * as app from &amp;quot;./app&amp;quot;;
import * as math from &amp;quot;./math&amp;quot;;

// Set all module functions to jest.fn
jest.mock(&amp;quot;./math.js&amp;quot;);

test(&amp;quot;calls math.add&amp;quot;, () =&amp;gt; {
  app.doAdd(1, 2);
  expect(math.add).toHaveBeenCalledWith(1, 2);
});

test(&amp;quot;calls math.subtract&amp;quot;, () =&amp;gt; {
  app.doSubtract(1, 2);
  expect(math.subtract).toHaveBeenCalledWith(1, 2);
});&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript/React</category>
      <category>javascript test code</category>
      <category>React Testing library</category>
      <category>TDD 테스트</category>
      <category>개발자</category>
      <category>리액트 테스트</category>
      <category>백엔드</category>
      <category>자바스크립트 테스트</category>
      <category>프론트엔드</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/162</guid>
      <comments>https://fullmoon1344.tistory.com/162#entry162comment</comments>
      <pubDate>Sat, 10 Jul 2021 13:52:40 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] Jest를 사용한 테스트 코드 짜기!</title>
      <link>https://fullmoon1344.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;참고로 이 글에서 말하는 테스트는 React환경에서 사용하기 위한 방법을 공부한 것을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우선, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;Jest&lt;/span&gt;를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1625400545117&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev jest

yarn add --dev jest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다음으로 간단한 예시로 &lt;b&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;sum.js &lt;/span&gt;&lt;/b&gt;파일을 하나 만들고, 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1625400589178&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sum(a, b) {
  return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 위의 코드를 테스트하기 위한 &lt;b&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;sum.test.js&lt;/span&gt;&lt;/b&gt; 테스트 파일을 하나 생성한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1625400662211&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { sum, sumOf } = require(&quot;./sum&quot;);

//test, it --&amp;gt; 새로운 테스트 작성
test(&quot;adds 1 + 2 to equal 3&quot;, () =&amp;gt; {
	expect(sum(1, 2)).toBe(3);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;앞서 만든 sum함수를 import해온 다음 테스트 구문에 활용해 제대로 원하는 결과가 테스트되는지 확인하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 테스트 코드를 실행해볼 차례인데, 다음과 같이 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;pakage.json&lt;/span&gt;파일에 추가해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1625400802930&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
	&quot;test&quot;: &quot;jest&quot;
}&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;size18&quot;&gt;아래 명령어를 통해 테스트를 실행하도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1625400876248&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;테스트 코드가 제대로 통과가 된다면, 다음과 같은 결과가 나타날 것이다. 참고로 아래 결과는 VSCode를 사용했을 때 나타난 결과이다.&lt;/p&gt;
&lt;pre id=&quot;code_1625400978542&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PASS  ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)&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;size18&quot;&gt;추가적으로 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;describe를&lt;/span&gt; 사용을 해보기 위해 sum.js파일에 새로운 함수를 추가해주자. 여기서 describe 메서드는 여러 테스트 케이스를 묶어주는 기능을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1625401092525&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// sum.js

function sum(a, b) {
  return a + b;
}

// 배열의 합
function sumOf(numbers) {
  return numbers.reduce((acc, current) =&amp;gt; acc + current, 0);
}

module.exports = { sum, sumOf };&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;size18&quot;&gt;그리고 테스트 코드 역시 수정해주고, 실행을 하게 되면 테스트 결과가 나타난다.&lt;/p&gt;
&lt;pre id=&quot;code_1625401130020&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// sum.test.js
const { sum, sumOf } = require(&quot;./sum&quot;);

describe(&quot;sum&quot;, () =&amp;gt; {
  it(&quot;calculates 1 + 2&quot;, () =&amp;gt; {
    expect(sum(1, 2)).toBe(3);
  });

  it(&quot;calculates all numbers&quot;, () =&amp;gt; {
    const array = [1, 2, 3, 4, 5];
    expect(sumOf(array)).toBe(15);
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;76&quot; data-filename=&quot;_2021-04-19__10.22.59.png&quot; width=&quot;403&quot; height=&quot;132&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6DXUm/btq8Qy1Jtdn/g6dDTKhBjfT5bSH68OV2SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6DXUm/btq8Qy1Jtdn/g6dDTKhBjfT5bSH68OV2SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6DXUm/btq8Qy1Jtdn/g6dDTKhBjfT5bSH68OV2SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6DXUm%2Fbtq8Qy1Jtdn%2Fg6dDTKhBjfT5bSH68OV2SK%2Fimg.png&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;76&quot; data-filename=&quot;_2021-04-19__10.22.59.png&quot; width=&quot;403&quot; height=&quot;132&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;test&lt;/span&gt; 대신 &lt;span style=&quot;background-color: #ffc1c8;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;it&lt;/span&gt;을 사용해도 같은 기능이다. 다만 &lt;span style=&quot;background-color: #ffc1c8;&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;it&lt;/span&gt;을 사용하게 되면, 테스트 케이스 설명을 영어로 작성할 경우 &quot;말이 되게&quot; 매끄럽게 작성 가능해져 사용한다고 한다.&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>Jest.js 테스트코드</category>
      <category>React Jest</category>
      <category>TDD</category>
      <category>리액트 테스트 코드</category>
      <category>자바스크립트 테스트 코드짜기</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/161</guid>
      <comments>https://fullmoon1344.tistory.com/161#entry161comment</comments>
      <pubDate>Sun, 4 Jul 2021 21:25:05 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 기본 타입 (Basic Types)</title>
      <link>https://fullmoon1344.tistory.com/160</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Boolean&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1614779298083&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let isDone: boolean = false;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Number&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서 숫자는 부동 소수점 값 또는 BigInteger 값이다. 16진수, 10진수, 8진수, 2진수 타입도 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1614779360250&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;String&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트와 똑같이 double quotes(&quot;&quot;), single quotes('')로 문자열을 감싼다.&lt;/p&gt;
&lt;pre id=&quot;code_1614779664204&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let color: string = &quot;blue&quot;;
color = 'red';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 backtick (`${ expr }`) 형태로 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1614779781203&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.

I'll be ${age + 1} years old next month.`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Array&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1614779870071&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let list: number[] = [1, 2, 3];

let list: Array&amp;lt;number&amp;gt; = [1, 2, 3]; //제네릭 타입방식
&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;span&gt;유니언 타입(다중 타입)의 &amp;lsquo;문자열과 숫자를 동시에 가지는 배열&amp;rsquo;도 선언할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617089063137&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Union 이용한 배열
let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];

let array: Array&amp;lt;string | number&amp;gt; = ['Apple', 1, 2, 'Banana', 'Mango', 3];&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;span&gt;&lt;span&gt;배열이 가지는 항목의 값을 단언할 수 없다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;any&lt;span&gt;를 사용할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617089102126&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let array: any[] = [0, 1, {}, [], 'str', false];&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;span&gt;&lt;span&gt;인터페이스(Interface)나 커스텀 타입(Type)을 사용할 수도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617089143512&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IUser {
  name: string,
  age: number,
  isValid: boolean
}

let userArr: IUser[] = [
  {
    name: 'Jiho',
    age: 25,
    isValid: true
  },
  {
    name: 'Juliet',
    age: 42,
    isValid: false
  },
  {
    name: 'Evan',
    age: 52,
    isValid: true
  }
];&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;span&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;readonly&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ReadonlyArray&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입을 사용해&amp;nbsp;&lt;/span&gt;읽기 전용 배열을 생성할 수도 있다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617089334339&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let arrA: readonly number[] = [1, 2, 3, 4];
let arrB: ReadonlyArray&amp;lt;number&amp;gt; = [0, 5, 5, 2];

arrA[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrA.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.

arrB[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrB.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;tuple&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Tuple&lt;/b&gt;&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_1614780029932&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Declare a tuple type
let x: [string, number];

// Initialize it
x = [&quot;hello&quot;, 10]; // OK

// Initialize it incorrectly
x = [10, &quot;hello&quot;]; // Error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Enum&lt;/b&gt;&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_1614780189516&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Color {
  Red, //0
  Green, //1
  Blue, //2
}
let c: Color = Color.Green;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 enum은 인덱스 번호가 0번부터 시작하지만, 따로 지정해 변경해줄 수 있다. 또는 모든 값에 수동으로 설정해주는 것도 가능하다. 이때 번호 대신 &lt;b&gt;문자열도 지정 가능하지만, 모든 값에 설정&lt;/b&gt;해줘야만 한다는 것을 주의해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1614780265037&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Color {
  Red = 1,
  Green, //2
  Blue, //3
}
let c: Color = Color.Green;

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}
let c: Color = Color.Green;

enum Color {
  Red = &quot;red&quot;,
  Green = &quot;green&quot;,
  Blue = &quot;blue&quot;,
}&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;리버스 매핑(Reverse Mapping)&lt;/b&gt; - &lt;span&gt;&lt;b&gt;숫자형 이넘에만 존재하는 특징&lt;/b&gt;이다. enum의 키(key)로 값(value)을 얻을 수 있고 값(value)으로 키(key)를 얻을 수도 있다. &lt;/span&gt;enum 요소에 해당하는 &lt;b&gt;번호를 통해 값을 얻을 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614780451708&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Color {
  Red = 1,
  Green,
  Blue,
}
let colorName: string = Color[2];

// Displays 'Green'
console.log(colorName);&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Void&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일반적으로 값을 반환하지 않는 함수에서 사용한다. &lt;/span&gt;&lt;b&gt;: void&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;위치는 함수가 반환 타입을 명시하는 곳이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;값을 반환하지 않는 함수는 &lt;b&gt;실제로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;undefined&lt;span&gt;를 반환한다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617090015986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function hello(msg: string): void {
  console.log(`Hello ${msg}`);
}
const hi: void = hello('world'); // Hello world
console.log(hi); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Object&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object&lt;span style=&quot;color: #333333;&quot;&gt;는 원시 타입이 아닌 타입을 나타낸다. &lt;b&gt;number&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;string&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;boolean&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;bigint&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;symbol&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;null&lt;span style=&quot;color: #333333;&quot;&gt;, 또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;undefined&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 아닌 나머지를 의미한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620269441914&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;declare function create(o: object | null): void;

create({ prop: 0 }); // 성공
create(null); // 성공

create(42); // 오류
create(&quot;string&quot;); // 오류
create(false); // 오류
create(undefined); // 오류&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;타입 단언(Type assertions)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;컴파일러에게 개발자가 명확하게 타입에 대해서 알려주는 방법이다. 예를 들어 타입스크립트의 타입인 string가 아닌 조금 더 명확한 &quot;some_type&quot;이라고 하는 명확한 타입이라고 알려주는 경우다.&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;&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;color: #333333;&quot;&gt;타입 단언의 사용법은 2가지 경우가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- Angle Braket 형태&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620269815815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let someValue: any = &quot;this is a string&quot;;

let strLength: number = (&amp;lt;string&amp;gt;someValue).length;&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;- as 형태&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620269842018&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let someValue: any = &quot;this is a string&quot;;

let strLength: number = (someValue as string).length;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 두 예제는 동일하며 어떤 것을 사용할지는 주로 선호에 따른 선택이다. 하지만 &lt;b&gt;TypeScript를 JSX&lt;/b&gt;와 함께 사용할 때는,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;as&lt;span style=&quot;color: #333333;&quot;&gt;-스타일의 단언만 허용된다.&lt;/span&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;&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;&lt;b&gt;Union&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개 이상의 타입을 지정해주고자 할 때 사용하며, &lt;b&gt;OR연산자 | &lt;/b&gt;&lt;span&gt;(vertical bar)를 를&lt;/span&gt; 사용해 타입들을 지정한다. &lt;span style=&quot;color: #333333;&quot;&gt;유니언 타입은 여러 타입 중 하나가 될 수 있는 값을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1617090289581&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let union: (string | number);
union = 'Hello type!';
union = 123;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript/TypeScript</category>
      <category>Typescript</category>
      <category>타입스크립트</category>
      <category>타입스크립트 기본타입</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/160</guid>
      <comments>https://fullmoon1344.tistory.com/160#entry160comment</comments>
      <pubDate>Tue, 22 Jun 2021 22:46:00 +0900</pubDate>
    </item>
    <item>
      <title>[백준 10866] 덱 - Python (Deque)</title>
      <link>https://fullmoon1344.tistory.com/159</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1621473474469&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;10866번: 덱&quot; data-og-description=&quot;첫째 줄에 주어지는 명령의 수 N (1 &amp;le; N &amp;le; 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/10866&quot; data-og-url=&quot;https://www.acmicpc.net/problem/10866&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2d1t4/hyKgAJqPE8/Xl6vL5B6ty9S4cHBwEt5x1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10866&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/10866&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2d1t4/hyKgAJqPE8/Xl6vL5B6ty9S4cHBwEt5x1/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;10866번: 덱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 주어지는 명령의 수 N (1 &amp;le; N &amp;le; 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;내 코드&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621473509641&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

class Deque:
    def __init__(self):
        self.result = list()

    def push_front(self, num):
        self.result.insert(0, num)

    def push_back(self, num):
        self.result.append(num)

    def pop_front(self):
        if(self.empty()):
            return -1
        else:
            return self.result.pop(0)

    def pop_back(self):
        if(self.empty()):
            return -1
        else:
            return self.result.pop()

    def front(self):
        if(self.empty()):
            return -1
        else:
            return self.result[0]

    def back(self):
        if(self.empty()):
            return -1
        else:
            return self.result[-1]

    def size(self):
        return len(self.result)

    def empty(self):
        if(self.size() == 0):
            return 1
        else:
            return 0


N = int(sys.stdin.readline())
deque = Deque()

for _ in range(N):
    input_split = sys.stdin.readline().split()
    oper = input_split[0]

    if(oper == 'push_front'):
        deque.push_front(int(input_split[1]))
    elif(oper == 'push_back'):
        deque.push_back(int(input_split[1]))
    elif(oper == 'pop_front'):
        print(deque.pop_front())
    elif(oper == 'pop_back'):
        print(deque.pop_back())
    elif(oper == 'front'):
        print(deque.front())
    elif(oper == 'back'):
        print(deque.back())
    elif(oper == 'empty'):
        print(deque.empty())
    elif(oper == 'size'):
        print(deque.size())
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Deque</category>
      <category>덱</category>
      <category>백준 10866</category>
      <category>알고리즘</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/159</guid>
      <comments>https://fullmoon1344.tistory.com/159#entry159comment</comments>
      <pubDate>Thu, 20 May 2021 10:19:16 +0900</pubDate>
    </item>
    <item>
      <title>[백준 7568] 덩치 - Python (브루트포스)</title>
      <link>https://fullmoon1344.tistory.com/158</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1621215690481&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;7568번: 덩치&quot; data-og-description=&quot;우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/7568&quot; data-og-url=&quot;https://www.acmicpc.net/problem/7568&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dqJG28/hyKdV1pn9q/7ySNcpZdgWgQ9MrdK5Cgl1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7568&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/7568&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dqJG28/hyKdV1pn9q/7ySNcpZdgWgQ9MrdK5Cgl1/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;7568번: 덩치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;내 코드&amp;gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621215711983&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input())
arr = [list(map(int, input().split())) for _ in range(n)]

counts = [1 for _ in range(n)]

for i in range(n):
    for j in range(n):
        if((arr[i][0] &amp;lt; arr[j][0]) and (arr[i][1] &amp;lt; arr[j][1])):
            counts[i] += 1

print(*counts)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신보다 키, 몸무게 모두 큰 사람 수를 단순히 모두 비교하면 되는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 7568 파이썬</category>
      <category>브루트포스</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/158</guid>
      <comments>https://fullmoon1344.tistory.com/158#entry158comment</comments>
      <pubDate>Mon, 17 May 2021 11:05:29 +0900</pubDate>
    </item>
    <item>
      <title>[Redux] To Do List 예제 - TypeScript, React</title>
      <link>https://fullmoon1344.tistory.com/157</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;순서&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;리덕스 &lt;b&gt;리듀서 모듈&lt;/b&gt; 생성(Ducks Pattern) - 한 파일에 액션, 액션 생성 함수, 리듀서를 작성하는 방식&lt;/li&gt;
&lt;li&gt;각 리듀서 모듈에서 액션타입(TS에서의 타입이 아님), 액션 생성 함수, 액션 객체 타입 선언(TS에서의 타입), 초기값, 기본값 타입들, 리듀서를 작성한다.&lt;/li&gt;
&lt;li&gt;'루트 리듀서'역할을 하는 index.ts를 만들어, 리덕스의 combineReducers를 이용해 만든 리듀서 모듈들을 하나로 통합해 export 한다.&lt;/li&gt;
&lt;li&gt;프로젝트 전체에 리듀서를 적용하고, 스토어에 접근 가능하도록 앱의 최상위 파일에서 &lt;b&gt;store&lt;/b&gt;를 생성해 Provider를 이용해 적용시킨다.&lt;/li&gt;
&lt;li&gt;필요한 컴포넌트들을 작성한다. 여기서, useSelector , useDispatch 를 이용해 각각 조금 더 쉽게 리듀서를 연결하고 스토어에 접근해 값을 사용하고, 액션을 발생시켜 상태를 변화시킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-04-16 오후 2.41.10.png&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;252&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rSUFU/btq2HJwoy1b/sVvoGKKhG7H6RZjdkjOCxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rSUFU/btq2HJwoy1b/sVvoGKKhG7H6RZjdkjOCxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rSUFU/btq2HJwoy1b/sVvoGKKhG7H6RZjdkjOCxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrSUFU%2Fbtq2HJwoy1b%2FsVvoGKKhG7H6RZjdkjOCxK%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-04-16 오후 2.41.10.png&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;252&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리듀서 모듈 생성&lt;/h4&gt;
&lt;p&gt;리덕스 사용에 필요한 액션, 액션 생성함수, 리듀서를 한 파일에 작성한다. 이러한 방식을 'Ducks 패턴'이라 한다.&lt;/p&gt;
&lt;p&gt;우선, 필요한 액션 타입을 상수로 설정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1618560708780&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ADD_TODO = &quot;todo/ADD_TODO&quot; as const;
const REMOVE_TODO = &quot;todo/REMOVE_TODO&quot; as const;
const TOGGLE_TODO = &quot;todo/TOGGLE_TODO&quot; as const;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 타입스크립트 문법인 &lt;span&gt;Type Assertions(타입 단언)을 사용해 액션 타입에 대한 타입 추론의 범위를 줄여 string 타입이 아닌 명확히 액션 타입을 추론하도록 해준다. 액션 타입들을 상수화 시켜두면 추후에 코드 수정에 있어 용이해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;다음으로 액션 생성 함수를 작성한다. 모듈 외부에서 사용할 수 있도록 export 해준다. Context api나 Redux 에서 액션을 dispatch를 이용해 액션을 발생시킬 때 해당 액션 생성 함수들을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1618561667572&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Action Creators
export const addTodo = (text: string) =&amp;gt; ({ type: ADD_TODO, payload: text });
export const removeTodo = (id: number) =&amp;gt; ({ type: REMOVE_TODO, payload: id });
export const toggleTodo = (id: number) =&amp;gt; ({ type: TOGGLE_TODO, payload: id });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음으로 액션 객체에 대한 타입들을 설정해줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1618562576285&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 액션 객체 타입 설정
type TodoAction =
  | ReturnType&amp;lt;typeof addTodo&amp;gt;
  | ReturnType&amp;lt;typeof removeTodo&amp;gt;
  | ReturnType&amp;lt;typeof toggleTodo&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;ReturnType&amp;lt;T&amp;gt; 는 유틸리티 타입의 한 종류로서 전역적으로 사용 가능하다. 기능으로는 &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;함수&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;T&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;의 &lt;b&gt;반환 타입&lt;/b&gt;&lt;/span&gt;으로 구성된 타입을 만들어준다. &lt;/span&gt;아래에 간단한 예시를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1618562316676&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;declare function f1(): { a: number, b: string }
type T0 = ReturnType&amp;lt;() =&amp;gt; string&amp;gt;;  // string
type T1 = ReturnType&amp;lt;(s: string) =&amp;gt; void&amp;gt;;  // void
type T2 = ReturnType&amp;lt;(&amp;lt;T&amp;gt;() =&amp;gt; T)&amp;gt;;  // {}
type T3 = ReturnType&amp;lt;(&amp;lt;T extends U, U extends number[]&amp;gt;() =&amp;gt; T)&amp;gt;;  // number[]
type T4 = ReturnType&amp;lt;typeof f1&amp;gt;;  // { a: number, b: string }
type T5 = ReturnType&amp;lt;any&amp;gt;;  // any
type T6 = ReturnType&amp;lt;never&amp;gt;;  // any
type T7 = ReturnType&amp;lt;string&amp;gt;;  // 오류
type T8 = ReturnType&amp;lt;Function&amp;gt;;  // 오류&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;ReturnType으로 인한 위의 액션 객체의 타입은 다음과 같이 될 것이다. 앞서 Type &lt;span style=&quot;color: #333333;&quot;&gt;Assertions으로 const로 지정을 해주지 않았다면 제대로 동작하지 않을 수도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;247&quot; data-filename=&quot;스크린샷 2021-04-16 오후 5.47.35.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVq4T/btq2Mz0pQqT/gIKTHGwvoV4xP0DKstPy81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVq4T/btq2Mz0pQqT/gIKTHGwvoV4xP0DKstPy81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVq4T/btq2Mz0pQqT/gIKTHGwvoV4xP0DKstPy81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVq4T%2Fbtq2Mz0pQqT%2FgIKTHGwvoV4xP0DKstPy81%2Fimg.png&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;247&quot; data-filename=&quot;스크린샷 2021-04-16 오후 5.47.35.png&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음으로 리듀서 함수에 전달 할 state에 대해 필요한 타입과 초기값들을 지정해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1618564099455&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export type Todo = {
  id: number;
  text: string;
  isToggle: boolean;
};

export type Todos = Todo[];

export const initialState: Todos = [
  {
    id: 0,
    text: &quot;타입스크립트 투두리스트&quot;,
    isToggle: false
  },
  {
    id: 1,
    text: &quot;타입스크립트 리덕스&quot;,
    isToggle: false
  }
];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로 앞서 작성한 state와 action 객체에 대한 타입을 이용해 리듀서 함수를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1618564177375&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Reducer
export default function todoReducer(state: Todos = initialState, action: TodoAction): Todos {
  switch (action.type) {
    case ADD_TODO:
      const id = Math.max(...state.map((todo) =&amp;gt; todo.id)) + 1; // 현재 있는 리스트 중 가장 큰 id값에 +1 한다.

      return state.concat({
        id,
        text: action.payload,
        isToggle: false
      });

    case REMOVE_TODO:
      return state.filter((todo) =&amp;gt; todo.id !== action.payload);

    case TOGGLE_TODO:
      return state.map((todo) =&amp;gt;
        todo.id === action.payload
          ? { ...todo, isToggle: !todo.isToggle }
          : todo
      );

    default:
      return state;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최종적인 리듀서 모듈의 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1618560274644&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/modules/todo.ts


// Action Type
const ADD_TODO = &quot;todo/ADD_TODO&quot; as const; // Type Assertions(현재 나타내는 타입보다 더 구체적인 타입을 나타내려 할 때 이용)
const REMOVE_TODO = &quot;todo/REMOVE_TODO&quot; as const;
const TOGGLE_TODO = &quot;todo/TOGGLE_TODO&quot; as const;

// Action Creators
export const addTodo = (text: string) =&amp;gt; ({ type: ADD_TODO, payload: text });
export const removeTodo = (id: number) =&amp;gt; ({ type: REMOVE_TODO, payload: id });
export const toggleTodo = (id: number) =&amp;gt; ({ type: TOGGLE_TODO, payload: id });

// 액션 객체 타입 설정
// ReturnType --&amp;gt; 타입스크립트의 특정함수의 반환 타입을 추출해내는 제네릭 타입으로
// 이를 통해 interface 중복작성을 피할 수 있다.
type TodoAction =
  | ReturnType&amp;lt;typeof addTodo&amp;gt;
  | ReturnType&amp;lt;typeof removeTodo&amp;gt;
  | ReturnType&amp;lt;typeof toggleTodo&amp;gt;;

//---- 리듀서에 전달 할 state에 대한 처리 ----
export type Todo = {
  id: number;
  text: string;
  isToggle: boolean;
};

export type Todos = Todo[];

export const initialState: Todos = [
  {
    id: 0,
    text: &quot;타입스크립트 투두리스트&quot;,
    isToggle: false
  },
  {
    id: 1,
    text: &quot;타입스크립트 리덕스&quot;,
    isToggle: false
  }
];

// ------------------------------------

// Reducer
export default function todoReducer(
  state: Todos = initialState,
  action: TodoAction
): Todos {
  switch (action.type) {
    case ADD_TODO:
      const id = Math.max(...state.map((todo) =&amp;gt; todo.id)) + 1;
      return state.concat({
        id,
        text: action.payload,
        isToggle: false
      });

    case REMOVE_TODO:
      return state.filter((todo) =&amp;gt; todo.id !== action.payload);

    case TOGGLE_TODO:
      return state.map((todo) =&amp;gt;
        todo.id === action.payload
          ? { ...todo, isToggle: !todo.isToggle }
          : todo
      );

    default:
      return state;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Root reducer 만들기&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;리덕스의 combineReducers를 이용해 만든 리듀서 모듈들을 하나로 통합해 만든 루트 리듀서 파일을 export 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1618567754917&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/modules/index.ts

import { combineReducers } from &quot;redux&quot;;
import todoReducer from &quot;./todo&quot;;

const rootReducer = combineReducers({
  // 여러 리듀서 모듈들을 하나로 병합한다.
  todoReducer
});

export default rootReducer;
export type RootState = ReturnType&amp;lt;typeof rootReducer&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;최상위 컴포넌트 Provider 감싸기&lt;/h4&gt;
&lt;p&gt;최상위 컴포넌트 파일인&amp;nbsp; 루트의 index.tsx 파일에서 &amp;lt;Provider&amp;gt; 컴포넌트로 하위 모든 컴포넌트에서 store에 접근이 가능하게 해 준다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1619414683651&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./index.tsx

import { render } from &quot;react-dom&quot;;
import React from &quot;react&quot;;
import { Provider } from &quot;react-redux&quot;;
import { createStore } from &quot;redux&quot;;

import rootReducer from &quot;./modules&quot;;
import App from &quot;./App&quot;;

const store = createStore(rootReducer); // store 생성

const rootElement = document.getElementById(&quot;root&quot;);
render(
  &amp;lt;Provider store={store}&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  rootElement
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;필요한 컴포넌트들 작성&lt;/h4&gt;
&lt;p&gt;Redux를 사용하기 위한 준비를 다 마쳤다. 이제 컴포넌트들을 작성하고 Redux 기능들을 활용하면 된다.&lt;/p&gt;
&lt;p&gt;src 폴더 안에 components폴더를 만들고, TodoApp.tsx 와 TodoAppContianer.tsx파일을 만든다. 각각의 코드는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1619415073928&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// /components/TodoApp.tsx

import React, { useState } from &quot;react&quot;;
import { Todos, Todo } from &quot;../modules/todo&quot;;
import { useDispatch } from &quot;react-redux&quot;;
import { addTodo, toggleTodo, removeTodo } from &quot;../modules/todo&quot;;

type Props = {
  todos: Todos;
};

const TodoApp = ({ todos }: Props) =&amp;gt; {
  // 디스패치 함수에 파라미터로 액션객체를 넣어주면 스토어로 전달하여 액션을 발생시키고,
  // 리듀서 모듈에 이 액션이 있다면 새로운 상태로 바뀌게 되는것
  const dispatch = useDispatch();
  const [input, setInput] = useState(&quot;&quot;);

  const handleSubmit = (e: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    e.preventDefault();
    dispatch(addTodo(input));
    setInput(&quot;&quot;);
  };

  const handleClick = (id: number) =&amp;gt; {
    dispatch(toggleTodo(id));
  };

  const handleRemove = (id: number) =&amp;gt; {
    dispatch(removeTodo(id));
  };

  const done = {
    textDecoration: &quot;line-through&quot;
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;form onSubmit={(e) =&amp;gt; handleSubmit(e)}&amp;gt;
          &amp;lt;input value={input} onChange={(e) =&amp;gt; setInput(e.target.value)} /&amp;gt;
          &amp;lt;button type=&quot;submit&quot;&amp;gt;등록&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;
        {todos.map((todo) =&amp;gt; {
          const { id, text, isToggle } = todo;
          return (
            &amp;lt;div
              key={id}
              onClick={() =&amp;gt; handleClick(id)}
              style={isToggle ? done : undefined}
            &amp;gt;
              {text}
              &amp;lt;button onClick={() =&amp;gt; handleRemove(id)}&amp;gt;삭제&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
          );
        })}
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default TodoApp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 코드에서 dispatch 함수의 인자로 '액션 생성함수(action creator)'를 넣어줬다. 즉, modules 폴더에서 작성한 리듀서 파일 안의 액션 생성 함수들의 결과로 '액션 객체' 값을 리턴 받아 dispatch 하게 된다. 그런 다음 reducer에서 액션 타입에 맞는 로직을 수행하게 되고 store의 상태 값을 변화시키게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1619415186842&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// /components/TodoAppContainer.tsx

import React from &quot;react&quot;;
import TodoApp from &quot;./TodoApp&quot;;
import { useSelector } from &quot;react-redux&quot;;
import { RootState } from &quot;../modules&quot;;

const TodoAppContainer = () =&amp;gt; {
  // useSelector :: 간단하게 리듀서 모듈과 연결해 데이터를 받아올 수 있다.
  const todos = useSelector((state: RootState) =&amp;gt; state.todoReducer);
  return &amp;lt;TodoApp todos={todos} /&amp;gt;;
};

export default TodoAppContainer;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useSelector는connect&lt;span&gt;&lt;span&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;235&quot; data-origin-height=&quot;87&quot; data-filename=&quot;스크린샷 2021-04-26 오후 2.49.41.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjOFDy/btq3yylS3sU/tUPoRq8HA76bsizkXgrMR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjOFDy/btq3yylS3sU/tUPoRq8HA76bsizkXgrMR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjOFDy/btq3yylS3sU/tUPoRq8HA76bsizkXgrMR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjOFDy%2Fbtq3yylS3sU%2FtUPoRq8HA76bsizkXgrMR0%2Fimg.png&quot; data-origin-width=&quot;235&quot; data-origin-height=&quot;87&quot; data-filename=&quot;스크린샷 2021-04-26 오후 2.49.41.png&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/Redux</category>
      <category>React Redux</category>
      <category>React Redux 예제</category>
      <category>React Todo List</category>
      <category>React Typescript</category>
      <category>리덕스</category>
      <category>타입스크립트 리덕스</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/157</guid>
      <comments>https://fullmoon1344.tistory.com/157#entry157comment</comments>
      <pubDate>Mon, 26 Apr 2021 14:54:52 +0900</pubDate>
    </item>
    <item>
      <title>[Redux] Redux 개념 익히기</title>
      <link>https://fullmoon1344.tistory.com/156</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;Redux는 애플리케이션 상태를 관리하기 위한 오픈 소스 JavaScript 라이브러리다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Redux 등장 배경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;리덕스가 등장하기 이전 프론트엔드에서 데이터 흐름(형상)을 관리하는 방식은 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;MVC 패턴&lt;/span&gt;이었다. &lt;span style=&quot;color: #333333;&quot;&gt;MVC 패턴의&lt;/span&gt; &lt;span&gt;큰 특징 중 하나가 &lt;b&gt;&amp;lsquo;양방향 데이터 흐름&amp;rsquo;&lt;/b&gt;이다. 모델이 변경된다면 뷰 또&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;한&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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;b&gt;설계하기 간단하고 코드 작성하기 쉬운 장점&lt;/b&gt;이 있다. 하지만 애플케이션 규모가 커진다면 문제가 생긴다. &lt;b&gt;한 개의 모델이 여러 개의 뷰를 조작하고 한 개의 뷰가 여러 개의 모델을 조작한다면 데이터 흐름을 이해하기 힘들어진다. 버그를 찾기 어려워지고 데이터 흐름을 추적하는 데 많은 시간을 투자해야 하는 단점이 있다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li id=&quot;2d30&quot; data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&amp;mdash; 데이터의 형식이나 구조를 관리한다. 모델에 맞지 않는 데이터는 흐름을 제어받을 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;4a4e&quot; data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 코드가 사용자에게 보여지는 부분을 담당한다. 사용자에게 보여지는 모습과 형태를 관리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;34ab&quot; data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Controller&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 변화하는 데이터를 관리한다. View에서 발생하는 이벤트로 변경되는 데이터나 서버로부터 받은 변경된 데이터를 Model과 View에 업데이트해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4qlaB/btqYQw0wn1x/qdY5zACe7a0hsFoGkF9wk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4qlaB/btqYQw0wn1x/qdY5zACe7a0hsFoGkF9wk1/img.png&quot; data-alt=&quot;출처: http://aalmiray.github.io/griffon-patterns&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4qlaB/btqYQw0wn1x/qdY5zACe7a0hsFoGkF9wk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4qlaB%2FbtqYQw0wn1x%2FqdY5zACe7a0hsFoGkF9wk1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: http://aalmiray.github.io/griffon-patterns&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Flux 등장&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;페이스북에서 MVC 패턴으로 데이터 흐름을 관리하는 데 많은 어려움을 겪고 있었고 그의 대안으로 Flux라는 새로운 아키텍처 패턴을 개발하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o2whN/btqYTNnoja1/omi1EIHc8LrTf5chciK5k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o2whN/btqYTNnoja1/omi1EIHc8LrTf5chciK5k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o2whN/btqYTNnoja1/omi1EIHc8LrTf5chciK5k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo2whN%2FbtqYTNnoja1%2Fomi1EIHc8LrTf5chciK5k1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;Flux는 MVC 패턴에서 겪은 복잡한 상황을 개선하는 것이 목적이었고 그 방법으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;rsquo; 단방향 데이터 흐름&amp;rsquo;을&lt;/b&gt;&lt;span&gt; 적용한 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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 data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;View는 MVC 패턴과 달리 데이터를 변경시키지 않고 Action을 넘겨준다. &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;Action은 반드시 Dispatcher를 지나게 되고 Dispatcher를 통해서 데이터 변경이 일어나고 View는 변경된 데이터를 Store를 통해서 전달받는다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;이러한 단방향 데이터 흐름은 기존의 MVC 패턴에 있던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;lsquo;상태의 전이&amp;rsquo;&lt;/b&gt;&lt;span&gt;(뷰와 모델 사이의 데이터 변경이 연결된 수많은 곳으로 따라 변경되는 현상) 현상을 없애주고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;lsquo;예측 가능하다&amp;rsquo;는&lt;/b&gt;&lt;span&gt; 특징을 갖는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. Redux의 등장&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;2015년에 Dan Abramov에 의해서 React + Flux의 구조에 &amp;lsquo;Reducer&amp;rsquo;를 결합한 &amp;lsquo;Redux&amp;rsquo;가 등장하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H4uWu/btqYIjnBRY4/ilm4FzsoUmUk2h0HIFNTz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H4uWu/btqYIjnBRY4/ilm4FzsoUmUk2h0HIFNTz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H4uWu/btqYIjnBRY4/ilm4FzsoUmUk2h0HIFNTz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH4uWu%2FbtqYIjnBRY4%2Film4FzsoUmUk2h0HIFNTz0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;리덕스의 특징 3가지&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. Single source of truth&amp;nbsp; &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;모든 상태는 &lt;b&gt;하나의 스토어&lt;/b&gt; 안에 하나의 객체 트리 구조로 저장 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. State is read-only&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법뿐이다. &lt;span&gt;데이터의 변경은 Reducer만 할 수 있다. Reducer 이외의 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;&lt;span&gt;공간에서는 데이터(상태)는 읽기 모드인 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;3. Changes are made with pure functions&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;변화를-일으키는-함수-리듀서는-순수한-함수여야-합니다.&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;변화를 일으키는 함수, 리듀서는 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;순수한 함수여야 한다.&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이전의 상태는 절대로 건드리지 않고, 변화를 일으킨 새로운 상태 객체를 만들어서 반환합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;만약 매번 같은 입력에 대해 같은 결과를 리턴하지 않는 경우는, 예를 들어 랜덤 숫자를 생성한다던지 혹은, 네트워크에 요청을 한다던지 등 이러한 작업은 결코 &lt;b&gt;순수하지 않은 작업이므로, 리듀서 함수의 바깥에서 처리&lt;/b&gt;해줘야 한다. 그런 것을 하기 위해서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;리덕스 미들웨어를&lt;/b&gt;&lt;span&gt; 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span&gt;&lt;b&gt;리덕스의 구성요소&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span&gt;&lt;b&gt;- Store&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;&lt;b&gt;상태를 관리하고 저장되는 장소&lt;/b&gt;이며, 직접적으로 스토어에 접근해서는 안되고 dispatch, subscribe, getState 같은 함수들을 이용해 스토어의 state에 접근한다. 스토어는&amp;nbsp;&lt;span&gt;현재의 앱 상태와, 리듀서가 들어가 있고, 추가적으로 몇 가지 내장 함수들을 포함하고 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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 data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;Dispatch 함수&lt;/b&gt; :&amp;nbsp; 액션을 발생시켜 store에 상태 변화가 필요하다는 것을 알리는 역할을 한다. 호출된 액션은 reducer 함수를 호출시키고, 액션에 맞는 로직과 상태 변화 과정을 거친다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;getState 함수&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;: 현재 애플리케이션의 state 값에 접근한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;Subscribe 함수&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;: 함수 형태의 값을 파라미터로 받고, action이 dispatch 될 때마다 전달받은 함수를 호출한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;- Reducer 함수&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;dispatch에 의해 액션이 전달되면, &lt;b&gt;액션 객체 값&lt;/b&gt;과 &lt;b&gt;기존 state 값&lt;/b&gt;을 참조해 새로운 state 값을 반환해주는 역할을 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614509741778&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const reducer = (currentState, action) =&amp;gt; {
	
    // 상태 업데이트 로직
    
    return newState;
}


const reducer = (prevState, action) =&amp;gt; {
  switch(action.type) {
    case 'SAVE_MONEY' :
    return {
      ...
    }
    default ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;- Action 객체&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;액션 객체는 반드시 &lt;b&gt;type 영역을 필수&lt;/b&gt;로 설정해줘야 하며, 그 외의 어떤 데이터는 개발자가 마음대로 넣을 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614509347025&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 액션 객체

{
  type: &quot;ADD_TODO&quot;,
  data: {
    id: 0,
    text: &quot;액션 객체 데이터&quot;
  }
}

// 액션 생성함수 --&amp;gt; 인자를 받아와 액션 객체를 반환해주는 함수다.
const changeInput = text =&amp;gt; ({ 
  type: &quot;CHANGE_INPUT&quot;,
  text
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/Redux</category>
      <category>react state</category>
      <category>redux</category>
      <category>redux action</category>
      <category>redux dispatch</category>
      <category>redux store</category>
      <category>리덕스</category>
      <category>리액트 상태관리</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/156</guid>
      <comments>https://fullmoon1344.tistory.com/156#entry156comment</comments>
      <pubDate>Sun, 28 Feb 2021 20:33:45 +0900</pubDate>
    </item>
    <item>
      <title>[React] Memoization Hooks - useMemo, useCallback</title>
      <link>https://fullmoon1344.tistory.com/155</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;메모이제이션&lt;/b&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&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 기본 개념&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;useMemo와&lt;span&gt;&amp;nbsp;&lt;/span&gt;useCallback는 메모이제이션 기능을 지원하는 리액트의 내장 훅으로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;퍼포먼스 최적화&lt;/b&gt;&lt;/span&gt;를 위하여 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;useMemo는 메모이제이션된 값을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;useCallback은 메모이제이션된 콜백을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;리액트는 실제로는 &lt;b&gt;상태가 변경되는 컴포넌트와 그 이하의 모든 자식 컴포넌트가 랜더링의 대상&lt;/b&gt;이 된다. 문제는 자식 컴포넌트의 상태가 변경되지 않아도(갱신될 필요가 없어도) 불필요한 랜더링이 일어난다는 것이다. 자바스크립트에서&amp;nbsp;&lt;span&gt;함수도 참조형 데이터이기 때문에 늘 새로운 값으로 취급되어 동일성을 보장받지 못하므로 &lt;b&gt;리액트에서 매번 새로운 렌더링&lt;/b&gt; 대상이 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;2. useMemo&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1614259806633&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const memoizedValue = useMemo(() =&amp;gt; computeExpensiveValue(a, b), [a, b]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&amp;ldquo;생성(create)&amp;rdquo; 함수와 그것의 의존성 값의 배열을 전달해야 한다.&lt;/span&gt;useMemo&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열이 없는 경우 매 렌더링 때마다 새 값을 계산하게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useMemo&lt;span style=&quot;color: #000000;&quot;&gt;로 전달된 함수는 렌더링 중에 실행된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무분별하게 useMemo를 사용하면 오히려 성능이 저하될 수 있다. 따라서 최대한 사용하지 않고 useEffect Hook 등을 활용해 비동기적으로 동작하도록 고안하는 것이 좋다. (리액트 공식 문서에서 추천하는 방식)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. useCallback&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1614260299082&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const memoizedCallback = useCallback(
  () =&amp;gt; {
    doSomething(a, b);
  },
  [a, b],
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;콜백과 그것의 의존성 값을 배열로 만들어 인자로 전달하며, 메모이제이션된 콜백함수를 반환한다. &lt;span&gt;그 메모이제이션된 버전은 &lt;b&gt;콜백의 의존성이 변경되었을 때에만 변경&lt;/b&gt;된다. &lt;span&gt;이것은, 불필요한 렌더링을 방지하기 위해(예로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;shouldComponentUpdate&lt;span&gt;를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;useCallback(fn, deps)&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;useMemo(() =&amp;gt; fn, deps)&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;와 같다고 한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useEffect&lt;span&gt;와 마찬가지로 두 번째 인자로 빈 배열([])을 넣으면 어떤 상태 값에도 반응하지 않으며, 두 번째 인자로 아무것도 넣지 않으면 모든 상태 변화에 반응한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>JavaScript/React</category>
      <category>react hooks</category>
      <category>react useCallback</category>
      <category>react useMemo</category>
      <category>리액트</category>
      <category>리액트 최적화</category>
      <category>리액트 훅</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/155</guid>
      <comments>https://fullmoon1344.tistory.com/155#entry155comment</comments>
      <pubDate>Thu, 25 Feb 2021 22:47:08 +0900</pubDate>
    </item>
    <item>
      <title>[WEB] 쿠키(Cookie)와 세션(Session) 개념 익히기</title>
      <link>https://fullmoon1344.tistory.com/154</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;HTTP의 특징&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;HTTP&amp;nbsp;프로토콜의&amp;nbsp;특징이자&amp;nbsp;약점을&amp;nbsp;보완하기&amp;nbsp;위해서&amp;nbsp;사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;HTTP 프로토콜 환경에서 서버는 클라이언트가 누구인지 확인해야 한다. 그 이유는 HTTP 프로토콜이 connectionless, stateless 한 특성이 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;(Hypertext Transfer Protocol)는 인터넷상에서 데이터를 주고 받기 위해 서버/클라이언트 모델을 따르는&amp;nbsp;통신규약을 의미한다. 이 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP 프로토콜에는&amp;nbsp;비연결성(Connectionless)과&amp;nbsp;비상태성(Stateless)이&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;라는 특징을 가진다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이는 서버의 자원을 절약하기 위해 모든 사용자의 요청마다 연결과 해제의 과정을 거치기 때문에 연결 상태가 유지되지 않고, 연결 해제 후에 상태 정보가 저장되지 않는다는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하지만, 이로 인해 사용자를 식별할 수 없어서 같은 사용자가 요청을 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여러 번 하더라도 매번 새로운 사용자로 인식하는 단점이 있다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 HTTP의 비연결 성과 비상 태성을 보완하여 서버가 클라이언트를 식별하게 해주는 것이 Cookie와 Session이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Connectionless&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;클라이언트가 요청을 한 후 응답을 받으면 그 연결을 끊어 버리는 특징&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;HTTP는 먼저 클라이언트가 request를 서버에 보내면, 서버는 클라이언트에게 요청에 맞는 response를 보내고 접속을 끊는 특성이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Stateless&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;통신이 끝나면 상태를 유지하지 않는 특징&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;연결을 끊는 순간 클라이언트와 서버의 통신이 끝나며 상태 정보는 유지하지 않는 특성이 있다.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;쿠키 (Cookie)&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;쿠키는 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용자 인증이 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지된다는 특징이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참조한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;클라이언트에 300개까지 쿠키 저장 가능, 하나의 도메인당 20개의 값만 가질 수 있음, 하나의 쿠키값은 4KB까지 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;Response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;쿠키는 사용자가 따로 요청하지 않아도 브라우저가 Request시에 Request Header를 넣어서 자동으로 서버에 전송한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이름, 값, 만료 날짜/시간(쿠키 저장기간), 경로 정보 등이&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt; 들어있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;서버가 가지고 있는 것이 아니라 사용자에게 저장되기 때문에, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;임의로 고치거나 지울 수 있고, 가로채기도 쉬워&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;보안이 취약하다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쿠키의 구성 요소&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;이름&amp;nbsp;:&amp;nbsp;각각의&amp;nbsp;쿠키를&amp;nbsp;구별하는&amp;nbsp;데&amp;nbsp;사용되는&amp;nbsp;이름&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;값&amp;nbsp;:&amp;nbsp;쿠키의&amp;nbsp;이름과&amp;nbsp;관련된&amp;nbsp;값&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;유효시간&amp;nbsp;:&amp;nbsp;쿠키의&amp;nbsp;유지시간&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;도메인&amp;nbsp;:&amp;nbsp;쿠키를&amp;nbsp;전송할&amp;nbsp;도메인&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;경로&amp;nbsp;:&amp;nbsp;쿠키를&amp;nbsp;전송할&amp;nbsp;요청&amp;nbsp;경로&lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;쿠키의 동작 프로세스&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;클라이언트가&amp;nbsp;페이지를&amp;nbsp;요청&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;서버에서&amp;nbsp;쿠키를&amp;nbsp;생성&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;HTTP&amp;nbsp;헤더에&amp;nbsp;쿠키를&amp;nbsp;포함시켜&amp;nbsp;응답&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;브라우저가&amp;nbsp;종료되어도&amp;nbsp;쿠키&amp;nbsp;만료&amp;nbsp;기간이&amp;nbsp;있다면&amp;nbsp;클라이언트에서&amp;nbsp;보관하고&amp;nbsp;있음&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;같은&amp;nbsp;요청을&amp;nbsp;할&amp;nbsp;경우&amp;nbsp;HTTP&amp;nbsp;헤더에&amp;nbsp;쿠키를&amp;nbsp;함께&amp;nbsp;보냄&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;서버에서&amp;nbsp;쿠키를&amp;nbsp;읽어&amp;nbsp;이전&amp;nbsp;상태&amp;nbsp;정보를&amp;nbsp;변경할&amp;nbsp;필요가&amp;nbsp;있을&amp;nbsp;때&amp;nbsp;쿠키를&amp;nbsp;업데이트하여&amp;nbsp;변경된&amp;nbsp;쿠키를&amp;nbsp;HTTP&amp;nbsp;헤더에&amp;nbsp;포함시켜&amp;nbsp;응답&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;세션 (Session)&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;세션은 쿠키를 기반하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 &lt;b&gt;서버 측에서 관리한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;서버에서는 클라이언트를 구분하기 위해 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증 상태를 유지한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;접속 시간에 제한을 두어 일정 시간 응답이 없다면 정보가 유지되지 않게 설정이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용자에 대한 정보를 서버에 두기 때문에 쿠키보다 보안에 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;접속자 수가 많은 웹 사이트인 경우 서버에 과부하를 주게 되므로 성능 저하의 요인이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;클라이언트가&amp;nbsp;Request를&amp;nbsp;보내면,&amp;nbsp;해당&amp;nbsp;서버의&amp;nbsp;엔진이&amp;nbsp;클라이언트에게&amp;nbsp;유일한&amp;nbsp;ID를&amp;nbsp;부여하는&amp;nbsp;데&amp;nbsp;이것이&amp;nbsp;세션 ID다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot; data-darkreader-inline-color=&quot;&quot;&gt;세션 동작 프로세스&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트가 서버에 요청했을 때, 필요에 따라 세션에 클라이언트에 대한 데이터를 저장하고 세션 아이디를 응답을 통해 발급해준다. (브라우저 단에서 관리될 수 있도록 쿠키로 발급하는 게 일반적인 구조)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트는 발급받은 세션 아이디를 쿠키로 저장한다. (ex. JSESSIONID)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트는 다시 서버에 요청할 때, 세션 아이디를 서버에 전달하여 상태 정보를 서버가 활용할 수 있도록 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Cookie&lt;/td&gt;
&lt;td&gt;Session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장위치&lt;/td&gt;
&lt;td&gt;클라이언트&lt;/td&gt;
&lt;td&gt;서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장형식&lt;/td&gt;
&lt;td&gt;text&lt;/td&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;리소스&lt;/td&gt;
&lt;td&gt;클라이언트의 리소스&lt;/td&gt;
&lt;td&gt;서버의 리소스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;용량제한&lt;/td&gt;
&lt;td&gt;도메인당 20개, 1쿠키당 4KB&lt;/td&gt;
&lt;td&gt;제한 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만료시점&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;쿠키 저장시 설정(설정 없을 시에는 브라우저 종료시 만료)&lt;/td&gt;
&lt;td&gt;알 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>HTTP</category>
      <category>세션</category>
      <category>웹 쿠키세션</category>
      <category>쿠키</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/154</guid>
      <comments>https://fullmoon1344.tistory.com/154#entry154comment</comments>
      <pubDate>Tue, 8 Dec 2020 00:56:49 +0900</pubDate>
    </item>
    <item>
      <title>[Express] Body-Parser 미들웨어 다루기</title>
      <link>https://fullmoon1344.tistory.com/153</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1604417061625&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;expressjs/body-parser&quot; data-og-description=&quot;Node.js body parsing middleware. Contribute to expressjs/body-parser development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/expressjs/body-parser&quot; data-og-url=&quot;https://github.com/expressjs/body-parser&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PeMsX/hyH7gSPp11/7uNdqzZ2jGwE23W97tNcs0/img.png?width=140&amp;amp;height=141&amp;amp;face=0_0_140_141&quot;&gt;&lt;a href=&quot;https://github.com/expressjs/body-parser&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/expressjs/body-parser&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PeMsX/hyH7gSPp11/7uNdqzZ2jGwE23W97tNcs0/img.png?width=140&amp;amp;height=141&amp;amp;face=0_0_140_141');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;expressjs/body-parser&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Node.js body parsing middleware. Contribute to expressjs/body-parser development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1604418328517&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;What are express.json() and express.urlencoded()?&quot; data-og-description=&quot;I cannot find any documentation on express.json() and express.urlencoded(). What do each of them do exactly?&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/23259168/what-are-express-json-and-express-urlencoded/51844327&quot; data-og-url=&quot;https://stackoverflow.com/questions/23259168/what-are-express-json-and-express-urlencoded&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5QygR/hyH7mZOETh/KtVmXHc11sXhKnrSyAHZsk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23259168/what-are-express-json-and-express-urlencoded/51844327&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/23259168/what-are-express-json-and-express-urlencoded/51844327&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5QygR/hyH7mZOETh/KtVmXHc11sXhKnrSyAHZsk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;What are express.json() and express.urlencoded()?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;I cannot find any documentation on express.json() and express.urlencoded(). What do each of them do exactly?&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Parsing 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;parsing&lt;/b&gt; : &lt;span&gt;가지고 있는 데이터를 내가 원하는 형태의 데이터로 &amp;lsquo;가공'하는 과정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;parser&lt;/b&gt; : 파싱 과정을 하는 모듈이나 메서드를 일컫는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;내가 원하는 형식에 맞춰 해석하는 용도이므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;bodyParser&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;뿐만 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;cookieParser&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;JSON.parse&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;JSON.stringifyJSON.stringify.. 등 파서의 종류는 셀 수 없이 많다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;예를 들어 외국어를 번역할 때 어떤 언어인지 정하고 해당 언어에 맞게 구문을 해석해주는 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;parser&lt;span&gt;, 그에 따라 실제 번역하는 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;compiler&lt;span&gt;라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. BodyParser&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span&gt;&lt;span&gt;HTTPpost put 요청 시 request body에 들어오는 데이터 값을 읽을 수 있는 구문으로 파싱함과 동시에 &lt;br /&gt;req.body 로 입력해주어 응답 과정에서 요청에 body 프로퍼티를 새로이 쓸 수 있게 해주는 미들웨어&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;클라이언트 측에서 API post와 put 메서드로 요청 시 (get delete는 불가능&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;) body를 포함하여 보낼 수 있는데 이 값을 서버 측에서 받는다고 그대로 사용할 수 있는 것이 아니고, 서버 내에서 해석 가능한 형태로 변형해야 사용할 수 있게 되는 것이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;API 요청에서 받은 body 값을 파싱하는 역할을 수행하는 것이 bodyParser라는 미들웨어이다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;설치&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1607064632728&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm insatll body-parser --save&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;예제&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1607061586401&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;/email_post&quot; method=&quot;post&quot;&amp;gt;
      email: &amp;lt;input type=&quot;text&quot; name=&quot;email&quot; /&amp;gt;&amp;lt;br /&amp;gt;
      &amp;lt;input type=&quot;submit&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;- body-parser 사용하지 않은 경우&lt;/h4&gt;
&lt;pre id=&quot;code_1607061837413&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// body-parser 없이 사용하면
app.post('/email_post', function (req, res) {
  console.log(req.body);
  res.send('post response');
});

// undefined 출력된다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- body-parser 사용한 경우&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1607065817920&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;post나 put으로 넘어온 데이터 중 json형식이나 encoding 된 url 데이터를 분석하도록&amp;nbsp; express 서버에 설정해주는 구문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607061919096&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require('express');
const app = express();
const bodyParser = require('body-parser');


// 클라이언트에서 오는 응답이 json형태 or 
// json이 아닌 그냥 post(urlencoding된) 데이터를 파싱
app.use(express.json()) // for parsing application/json
app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded

app.post('/email_post', function (req, res) {
  console.log(req.body);
  console.log(req.body.email);
  res.send('post response');
});

/*
출력
  { email: 'asd@asdasdasd.com' }
  'asd@asdasdasd.com'
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;express 문서에 따르면 미들웨어 없이 &lt;b&gt;req.body 에 접근하는 경우에는 기본으로undefined&lt;/b&gt; 가 설정되어 있으므로 bodyParser, multer와 같은 미들웨어를 사용하여 요청 데이터 값에 접근해야 한다고 되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;클라이언트 측에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;{ name: 'John', age:...}와req.body&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;혹은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;req.body.name&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;req.body.job&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;등으로 해당 데이터에 곧바로 접근할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Node.js/Express</category>
      <category>express body-parser</category>
      <category>nodejs body-parser</category>
      <category>parsing</category>
      <category>post 데이터 파싱</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/153</guid>
      <comments>https://fullmoon1344.tistory.com/153#entry153comment</comments>
      <pubDate>Fri, 4 Dec 2020 16:13:50 +0900</pubDate>
    </item>
    <item>
      <title>[React] Class Component vs Function Component</title>
      <link>https://fullmoon1344.tistory.com/152</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Component Create&lt;/h2&gt;
&lt;pre id=&quot;code_1606885514180&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클래스 컴포넌트
class ClassComp extends React.Component {
  render() {
    return (
      &amp;lt;div className=&quot;container&quot;&amp;gt;
        &amp;lt;h2&amp;gt;class style component&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;Number : 2&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1606885542288&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수형 컴포넌트
function FuncComp(props) {
  return (
    &amp;lt;div className=&quot;container&quot;&amp;gt;
      &amp;lt;h2&amp;gt;function style component&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Number : 2&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Props&lt;/h2&gt;
&lt;pre id=&quot;code_1606885643480&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { Component } from 'react'

export default class App extends Component {
  render() {
    return (
      &amp;lt;div className=&quot;container&quot;&amp;gt;
        &amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;
        &amp;lt;FuncComp initNumber={2}&amp;gt;&amp;lt;/FuncComp&amp;gt;
        &amp;lt;ClassComp initNumber={2}&amp;gt;&amp;lt;/ClassComp&amp;gt;
      &amp;lt;/div&amp;gt;
    )
  }
}

// 함수형
// 인자로 props라는 이름으로 편의상 쓰는 것(다른 이름도 가능)
function FuncComp(props) {
  return (
    &amp;lt;div className=&quot;container&quot;&amp;gt;
      &amp;lt;h2&amp;gt;function style component&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Number : {props.initNumber}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// 클래스형
class ClassComp extends React.Component {
  render() {
    return (
      &amp;lt;div className=&quot;container&quot;&amp;gt;
        &amp;lt;h2&amp;gt;class style component&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;Number : {this.props.initNumber}&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qFTeP/btqOSxbm8CJ/2qhig0IgXPtJrfvy0gAhd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qFTeP/btqOSxbm8CJ/2qhig0IgXPtJrfvy0gAhd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qFTeP/btqOSxbm8CJ/2qhig0IgXPtJrfvy0gAhd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqFTeP%2FbtqOSxbm8CJ%2F2qhig0IgXPtJrfvy0gAhd1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. State&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606899595661&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수형
function FuncComp(props) {
  // state 초기화
  let numberState = useState(props.initNumber); // --&amp;gt; useState는 무조건 배열이 리턴
  let number = numberState[0]; //state 사용, 배열의 첫번째: 상태값
  let setNumber = numberState[1]; // state 변경, 배열의 두번째: 상태를 바꿀수 있는 함수

  // 간단 축약형 사용법
  let [_date, setDate] = useState((new Date()).toString());

  return (
    &amp;lt;div className=&quot;container&quot;&amp;gt;
      &amp;lt;h2&amp;gt;function style component&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Number : {number}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Date : {_date}&amp;lt;/p&amp;gt;
      &amp;lt;input type=&quot;button&quot; value=&quot;random&quot; onClick={
        function () {
          setNumber(Math.random());
        }
      } /&amp;gt;
      &amp;lt;input type=&quot;button&quot; value=&quot;date&quot; onClick={
        function () {
          setDate((new Date()).toString());
        }
      } /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Function 컴포넌트에서 state를 사용하기 위해서는 useState Hooks를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;useState(초기값)&lt;/b&gt; - [state value, func] 배열 return&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 data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;useState는 무조건 &lt;b&gt;2개&lt;/b&gt;의 값 요소를 갖는&amp;nbsp;&lt;b&gt;배열&lt;/b&gt;이 리턴된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;초기값&lt;/b&gt;을 줬을 때, 리턴된 배열의 &lt;b&gt;첫 번째 요소&lt;/b&gt;가 그 값이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;리턴된 배열의 &lt;b&gt;2번째 값은 함수&lt;/b&gt;로, state를 바꿀 수 있는 함수다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606899646713&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클래스형
class ClassComp extends React.Component {
  // state 초기화
  state = {
    number: this.props.initNumber,
    date: (new Date()).toString()
  }
  render() {
    return (
      &amp;lt;div className=&quot;container&quot;&amp;gt;
        &amp;lt;h2&amp;gt;class style component&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;Number : {this.state.number}&amp;lt;/p&amp;gt; {/*state 사용 */}
        &amp;lt;p&amp;gt;Date : {this.state.date}&amp;lt;/p&amp;gt;
        &amp;lt;input type=&quot;button&quot; value=&quot;random&quot; onClick={
          function () {
            this.setState({ number: Math.random() }); {/*state 변경 */ }
          }.bind(this)
        } /&amp;gt;
        &amp;lt;input type=&quot;button&quot; value=&quot;date&quot; onClick={
          function () {
            this.setState({ date: (new Date()).toString() }); {/*state 변경 */ }
          }.bind(this)
        } /&amp;gt;
      &amp;lt;/div&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Class 컴포넌트에서 안 좋은 점이 bind를 해줘야 하는 것이다. 바인딩을 안 하고 사용하는 방법들이 존재한다. (ex. 화살표 함수...)&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 data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;state 초기화 : constructor를 사용하거나, 위와 같이 사용해 초기화할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;state 사용 : this.state를 이용한다. 여기서 render 함수 안에서 바인딩을 해줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;state 변경 : this.setState를 이용한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjjEYK/btqO0R7wXGD/4HAkn2BVvf5SElc91atVA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjjEYK/btqO0R7wXGD/4HAkn2BVvf5SElc91atVA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjjEYK/btqO0R7wXGD/4HAkn2BVvf5SElc91atVA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjjEYK%2FbtqO0R7wXGD%2F4HAkn2BVvf5SElc91atVA1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Life cycle&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;515&quot; data-filename=&quot;lifecycle.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Su4Bg/btqOS9WcS5K/w4BJA8s3gnkQSSbLgYtZ7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Su4Bg/btqOS9WcS5K/w4BJA8s3gnkQSSbLgYtZ7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Su4Bg/btqOS9WcS5K/w4BJA8s3gnkQSSbLgYtZ7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSu4Bg%2FbtqOS9WcS5K%2Fw4BJA8s3gnkQSSbLgYtZ7K%2Fimg.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;515&quot; data-filename=&quot;lifecycle.png&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;useEffect(&amp;lt;function&amp;gt;, &amp;lt;Array&amp;gt;)&lt;/span&gt;&amp;nbsp;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == 생략&lt;/b&gt; : 첫 렌더링 + state 변화에 따른 모든 렌더링 시에 동작한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == [ ]&lt;/b&gt; : 첫 렌더링 시에만 동작한다. componentDidMount()와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == [state]&lt;/b&gt; : 첫 렌더링 시 동작하고, 배열 안에 넣어준 값의 상태가 변할 때마다 계속 동작한다. componentDidMount() &amp;amp; componentDidUpdate()&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Clean up&lt;/b&gt; : &lt;b&gt;return&lt;/b&gt;으로 &lt;b&gt;함수&lt;/b&gt;를 지정해주면 clean up작업을 한다. componentWillUnmount()와 같은 동작이다. 즉, &lt;/span&gt;컴포넌트가 수명을 다하고 사라질 때 어떤 행동을 하는 것을 의미한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>hooks</category>
      <category>Life Cycle</category>
      <category>useEffect</category>
      <category>useState</category>
      <category>리액트 생명주기</category>
      <category>리액트 클래스 컴포넌트</category>
      <category>리액트 함수형 컴포넌트</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/152</guid>
      <comments>https://fullmoon1344.tistory.com/152#entry152comment</comments>
      <pubDate>Thu, 3 Dec 2020 00:49:50 +0900</pubDate>
    </item>
    <item>
      <title>[React] 이벤트 처리하기</title>
      <link>https://fullmoon1344.tistory.com/151</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;React 엘리먼트에서 이벤트를 처리하는 방식은 DOM 엘리먼트에서 이벤트를 처리하는 방식과 매우 유사하다. 몇 가지 차이는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;React의 이벤트는 소문자 대신 &lt;b&gt;캐멀 케이스&lt;/b&gt;(camelCase)를 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;JSX를 사용하여 문자열이 아닌 &lt;b&gt;함수로 이벤트 핸들러를 전달한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React에서는 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 반환해도 기본 동작을 방지할 수 없습니다. 반드시&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;preventDefault&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 명시적으로 호출해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ES6 클래스&lt;span style=&quot;color: #000000;&quot;&gt;를 사용하여 컴포넌트를 정의할 때, 일반적인 패턴은 이벤트 핸들러를 &lt;b&gt;클래스의 메서드로&lt;/b&gt; 만드는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1606749255564&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 콜백에서 `this`가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
    this.handleClick = this.handleClick.bind(this);
  }
  
  // 토글 이벤트 메서드
  handleClick() {
    this.setState(state =&amp;gt; ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      &amp;lt;button onClick={this.handleClick}&amp;gt;
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      &amp;lt;/button&amp;gt;
    );
  }
}

ReactDOM.render(
  &amp;lt;Toggle /&amp;gt;,
  document.getElementById('root')
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;- this의 의미와 binding&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JSX &lt;b&gt;콜백 안&lt;/b&gt;에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;this&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;의 의미&lt;/b&gt;에 대해 주의해야 합니다. JavaScript에서 클래스 메서드는 기본적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;u&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;바인딩&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;되어 있지 않다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;this.handleClick&lt;span style=&quot;color: #000000;&quot;&gt;을 바인딩하지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;onClick&lt;span style=&quot;color: #000000;&quot;&gt;에 전달하였다면, 함수가 실제 호출될 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;this&lt;span style=&quot;color: #000000;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;undefined&lt;span style=&quot;color: #000000;&quot;&gt;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이는 React만의 특수한 동작이 아니며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JavaScript에서 함수가 작동하는 방식&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;span style=&quot;color: #000000;&quot;&gt;의 일부다. 일반적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;onClick={this.handleClick}&lt;span style=&quot;color: #000000;&quot;&gt;과 같이 &lt;b&gt;뒤에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;()&lt;span style=&quot;color: #000000;&quot;&gt;를 사용하지 않고 메서드를 참조할 경우, 해당 메서드를 바인딩 해야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- binding 하지 않는 2가지 방법&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;리액트 문서에서 소개하는 실험적인 문법인 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;u&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;letter-spacing: 0px; color: #006dd7;&quot; href=&quot;https://babeljs.io/docs/plugins/transform-class-properties/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;퍼블릭 클래스 필드 문법&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1606749625387&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LoggingButton extends React.Component {
  // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
  // 주의: 이 문법은 *실험적인* 문법입니다.
  handleClick = () =&amp;gt; {
    console.log('this is:', this);
  }

  render() {
    return (
      &amp;lt;button onClick={this.handleClick}&amp;gt;
        Click me
      &amp;lt;/button&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 클래스 필드 문법을 사용하고 있지 않다면, 콜백에 &lt;/span&gt;화살표 함수&lt;span style=&quot;color: #000000;&quot;&gt;를 사용하는 방법도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1606749680393&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
    return (
      &amp;lt;button onClick={() =&amp;gt; this.handleClick()}&amp;gt;
        Click me
      &amp;lt;/button&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 문법의 문제점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;LoggingButton&lt;span style=&quot;color: #000000;&quot;&gt;이 렌더링될 때마다 다른 콜백이 생성된다는 것이다. 대부분의 경우 문제가 되지 않으나, 콜백이 하위 컴포넌트에 props로서 전달된다면 그 컴포넌트들은 추가로 다시 렌더링을 수행할 수도 있다. 이러한 종류의 성능 문제를 피하고자, &lt;b&gt;생성자 안에서 바인딩&lt;/b&gt;하거나 &lt;b&gt;클래스 필드 문법&lt;/b&gt;을 사용하는 것을 &lt;span style=&quot;color: #006dd7;&quot;&gt;권장&lt;/span&gt;합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 이벤트 핸들러에 인자 전달하기&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1606750004034&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button onClick={(e) =&amp;gt; this.deleteRow(id, e)}&amp;gt;Delete Row&amp;lt;/button&amp;gt;
&amp;lt;button onClick={this.deleteRow.bind(this, id)}&amp;gt;Delete Row&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위 두 줄은 동등하며 각각&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;화살표 함수&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;u&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Function.prototype.bind&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 경우 모두 React 이벤트를 나타내는&lt;span&gt;&amp;nbsp;&lt;/span&gt;e&lt;span&gt;&amp;nbsp;&lt;/span&gt;인자가 ID 뒤에 두 번째 인자로 전달된다. 화살표 함수를 사용하면 명시적으로 인자를 전달해야 하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;bind를 사용할 경우 추가 인자가 자동으로 전달된다.&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>react event</category>
      <category>리액트 이벤트 핸들러</category>
      <category>리액트 이벤트처리</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/151</guid>
      <comments>https://fullmoon1344.tistory.com/151#entry151comment</comments>
      <pubDate>Tue, 1 Dec 2020 00:29:52 +0900</pubDate>
    </item>
    <item>
      <title>[React] State 다루기</title>
      <link>https://fullmoon1344.tistory.com/150</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. State&lt;/h2&gt;
&lt;ul&gt;
&lt;li id=&quot;3438&quot; data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;props와의 차이점이라면, state는 컴포넌트 내부에 존재하고 있기 때문에, 상태 값 변경이 가능하다는 것이다.(즉, 구현하는 쪽에 중점을 둔다.)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;this.setState() 메소드를 통해서 상태 값을 변경해준다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-selectable-paragraph=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;상위 컴포넌트의 state는 하위 컴포넌트의 props로 전달된다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606731023292&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { Component } from 'react'
import Subject from './components/Subject';
import Content from './components/Content';
import TOC from './components/TOC';

class App extends Component {
  // 초기 this.state를 지정하는 class constructor
  constructor(props) {
    super(props);
    this.state = {
      subject: { title: &quot;WEB&quot;, sub: &quot;World Wide Web!&quot; },
      contents: [
        { id: 1, title: 'HTML', desc: 'HTML is for information' },
        { id: 2, title: 'CSS', desc: 'CSS is for design' },
        { id: 3, title: 'JavaScript', desc: 'JavaScript is for interactive' }
      ]
    }
  }

	// this.state를 이용해 state 값 사용
     // 상위 컴포넌트의 state는 하위 컴포넌트의 props로 전달된다.!!!!!!
  render() {
    return (
      &amp;lt;div className=&quot;App&quot;&amp;gt;
        &amp;lt;Subject title={this.state.subject.title} sub={this.state.subject.sub} /&amp;gt;
        &amp;lt;TOC data={this.state.contents} /&amp;gt;
        &amp;lt;Content /&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}


export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 컴포넌트는 항상&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;props&lt;span style=&quot;color: #000000;&quot;&gt;로 기본 constructor를 호출해야 한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Constructor(생성자) : JS Class 문법&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;constructor&lt;span&gt;&amp;nbsp;&lt;/span&gt;메소드는&lt;span&gt;&amp;nbsp;class 로&lt;/span&gt; 생성된 &lt;b&gt;객체를 생성하고 초기화&lt;/b&gt;하기 위한 특수한 메서드다.&amp;nbsp;&amp;nbsp;&quot;constructor&quot;라는 이름을 가진 특수한 메서드는 클래스 안에 &lt;b&gt;한 개만 존재&lt;/b&gt;할 수 있다. 만약 클래스에 여러 개의 constructor 메서드가 존재하면 SyntaxError&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 발생할 것이다. &lt;/span&gt;constructor는 부모 클래스의 constructor를 호출하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;super&lt;/b&gt; 키워드를 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;State&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;객체를 사용하고 싶다면 컴포넌트를 생성할 때 가장 윗부분(&lt;/span&gt;render()&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수보다 먼저)에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;constructor()&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 적어준다. 컴포넌트 생성자에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;super&lt;span&gt;를 호출하기 전에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;this&lt;span&gt;를 사용할 수 없기 때문이다. &lt;span&gt;즉, 컴포넌트의 시작 부분에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;constructor()constructor()라는 함수가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;컴포넌트의 초기화&lt;/b&gt;&lt;span&gt;를 시켜줘야&lt;span&gt;&amp;nbsp;&lt;/span&gt;StateState에 값을 넣어 사용할 수 있는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span&gt;- &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;super(props)를 써야 하는 이유&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;자바스크립트에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;supersuper는 부모 클래스 생성자의 참조다. 그리고 자바스크립트는 언어적 제약사항으로서 &lt;b&gt;생성자에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;super를&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;Hooks를 사용한다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;super&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나&lt;span&gt; &lt;/span&gt;this에 대해 고민하지 않아도 된다!!!&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. setState&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 컴포넌트에서 state를 직접 바꾸면 안 된다. &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;this.state를 지정할 수 있는 유일한 공간은 바로 constructor입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1606737558087&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Wrong
this.state.comment = 'Hello';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다음과 같이 setState() 함수를 사용해서 state를 변경해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1606737583894&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Correct
this.setState({comment: 'Hello'});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- State 업데이트는 비동기적일 수도 있다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;this.props와&lt;span&gt;&amp;nbsp;&lt;/span&gt;this.state가 &lt;b&gt;비동기적으로 업데이트될 수 있기&lt;/b&gt; 때문에 다음 state를 계산할 때 해당 값에 의존해서는 안 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1606738301928&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;props와 state의 비동기적 업데이트를 고려해 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;함수를 인자로 사용하는&lt;/b&gt; 다른 형태의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;setState()를 사용하면 된다. &lt;span style=&quot;color: #000000;&quot;&gt;그 함수는 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;이전 state&lt;/span&gt;&lt;/b&gt;를 &lt;span style=&quot;color: #006dd7;&quot;&gt;첫 번째 인자&lt;/span&gt;로 받아들일 것이고, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;업데이트가 적용된 시점의 props&lt;/span&gt;&lt;/b&gt;를 &lt;span style=&quot;color: #006dd7;&quot;&gt;두 번째 인자&lt;/span&gt;로 받아들일 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1606738440196&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Correct
this.setState((state, props) =&amp;gt; ({
  counter: state.counter + props.increment
}));

// Correct
this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 독립적으로 state들을 업데이트할 수 있다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606741461503&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }
  
  // 독립적으로 각각 업데이트
  componentDidMount() {
    fetchPosts().then(response =&amp;gt; {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response =&amp;gt; {
      this.setState({
        comments: response.comments
      });
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>react state</category>
      <category>setState</category>
      <category>this.state</category>
      <category>리액트 state</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/150</guid>
      <comments>https://fullmoon1344.tistory.com/150#entry150comment</comments>
      <pubDate>Mon, 30 Nov 2020 22:46:43 +0900</pubDate>
    </item>
    <item>
      <title>[React] Component 생성 &amp;amp; props 다루기</title>
      <link>https://fullmoon1344.tistory.com/149</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Function Component &amp;amp; Class Component&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606707269323&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수형 컴포넌트
function Welcome(props) {
  return &amp;lt;h1&amp;gt;Hello, {props.name}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 함수는 데이터를 가진 하나의 &amp;ldquo;props&amp;rdquo; (props는 속성을 나타내는 데이터) 객체 인자를 받은 후 React 엘리먼트를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606707285527&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클래스형 컴포넌트
class Welcome extends React.Component {
  render() {
    return &amp;lt;h1&amp;gt;Hello, {this.props.name}&amp;lt;/h1&amp;gt;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;ES6 Class를 사용해 컴포넌트를 정의하면 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;- 함수형에서 클래스형 컴포넌트로 바꾸기&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;React.Component를 확장하는 동일한 이름의&lt;span&gt;&amp;nbsp;&lt;/span&gt;ES6 class를 생성합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;render()라고 불리는 빈 메서드를 추가합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;함수의 내용을&lt;span&gt;&amp;nbsp;&lt;/span&gt;render()&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드 안으로 옮깁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;render()&lt;span&gt;&amp;nbsp;&lt;/span&gt;내용 안에 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;props를&lt;span&gt;&amp;nbsp;&lt;/span&gt;this.props로 변경합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;※ 주의&lt;/h4&gt;
&lt;h4 id=&quot;user-defined-components-must-be-capitalized&quot; data-ke-size=&quot;size20&quot;&gt;사용자 정의 컴포넌트는 반드시 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;대문자&lt;/span&gt;로 시작해야 한다.&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Element가 소문자로 시작하는 경우에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;lt;div&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;lt;span&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 내장 컴포넌트라는 것을 뜻하며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;'div'&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;'span'&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 문자열 형태로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;React.createElement&lt;span style=&quot;color: #000000;&quot;&gt;에 전달된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;lt;Foo /&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;와 같이 대문자로 시작하는 타입들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;React.createElement(Foo)&lt;span style=&quot;color: #000000;&quot;&gt;의 형태로 컴파일되며 JavaScript 파일 내에 사용자가 정의했거나 import 한 컴포넌트를 가리킨다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Props&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;props-are-read-only&quot; data-ke-size=&quot;size20&quot;&gt;props는 읽기 전용이다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size14&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;App.js&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1606708648007&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// APP.js
import React, { Component } from 'react'
import Subject from './components/Subject'; //컴포넌트 import
import Content from './components/Content'; //컴포넌트 import


class App extends Component {
  render() {
    return (
      &amp;lt;div className=&quot;App&quot;&amp;gt;
        &amp;lt;Subject title=&quot;WEB&quot; sub=&quot;Wold Wide Web!&quot; /&amp;gt; // props 전달
        리액트 공부하기!
        &amp;lt;Content title=&quot;HTML&quot; desc=&quot;HTML is ...&quot; /&amp;gt; // props 전달
      &amp;lt;/div&amp;gt;
    );
  }
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하위 컴포넌트에 전달할 데이터를 props로 전달한다. 여기선 &amp;lt;Subject&amp;gt;, &amp;lt;Content&amp;gt; 각 컴포넌트에 porps를 전달해줬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style2&quot;&gt;Subject.js&lt;/blockquote&gt;
&lt;pre id=&quot;code_1606708965054&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Subject.js
import React, { Component } from 'react'

export default class Subject extends Component {
  render() {
    console.log('Subject render!');
    return (
      &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;{this.props.title}&amp;lt;/h1&amp;gt; // 전달받은 props를 사용해 렌더링한다.
        {this.props.sub}
      &amp;lt;/header&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Content.js&lt;/blockquote&gt;
&lt;pre id=&quot;code_1606709053471&quot; class=&quot;scala&quot; data-ke-language=&quot;scala&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Content.js
import React, { Component } from 'react'

export default class Content extends Component {
  render() {
    return (
      &amp;lt;article&amp;gt;
        &amp;lt;h2&amp;gt;{this.props.title}&amp;lt;/h2&amp;gt; // 전달받은 props를 사용해 렌더링한다.
        {this.props.desc}
      &amp;lt;/article&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전달받은 props를 사용할 때는 현재 Class를 가리키는 this를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;this.props.(전달받은 props 명)&lt;/b&gt; 형식으로 중괄호와 함께 사용해주면 받은 데이터를 렌더링 할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- 결과 화면&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KvceO/btqOH9AP70a/zzcym4AmaYrBttxyWSyjL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KvceO/btqOH9AP70a/zzcym4AmaYrBttxyWSyjL0/img.png&quot; data-alt=&quot;&amp;amp;amp;lt;결과 화면&amp;amp;amp;gt;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KvceO/btqOH9AP70a/zzcym4AmaYrBttxyWSyjL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKvceO%2FbtqOH9AP70a%2Fzzcym4AmaYrBttxyWSyjL0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;&amp;lt;결과 화면&amp;gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>React Component</category>
      <category>React props</category>
      <category>리액트 props component</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/149</guid>
      <comments>https://fullmoon1344.tistory.com/149#entry149comment</comments>
      <pubDate>Mon, 30 Nov 2020 13:16:37 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1992] 쿼드트리 - Python (분할정복, 재귀)</title>
      <link>https://fullmoon1344.tistory.com/148</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1606205055534&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1992번: 쿼드트리&quot; data-og-description=&quot;첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1&amp;le;N &amp;le;64의 범위를 가진다. 두 번째 줄부터는 길이 N 의 문자열이 N 개 들어온다. 각 문자열은 0 또는&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1992&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1992&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bSbqix/hyIkASu4Jx/FIF8EaXJdLk7tHINFTt7Kk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1992&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1992&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bSbqix/hyIkASu4Jx/FIF8EaXJdLk7tHINFTt7Kk/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;&gt;1992번: 쿼드트리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1&amp;le;N &amp;le;64의 범위를 가진다. 두 번째 줄부터는 길이 N 의 문자열이 N 개 들어온다. 각 문자열은 0 또는&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;lt;내 코드&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606205094323&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input())
video = [list(input()) for _ in range(n)]


def divide(x, y, n):
    ans = []
    check = True
    color = video[x][y]

    for i in range(x, x+n):
        if not check:
            break
        for j in range(y, y+n):
            if color != video[i][j]:
                check = False
                ans.append(&quot;(&quot;)
                ans.extend(divide(x, y, n//2))
                ans.extend(divide(x, y+n//2, n//2))
                ans.extend(divide(x+n//2, y, n//2))
                ans.extend(divide(x+n//2, y+n//2, n//2))
                ans.append(&quot;)&quot;)
                return ans
    return color


print(&quot;&quot;.join(divide(0, 0, n)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;list.append와 list.extend의 차이에 대해 알게 됐다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1606205289850&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = [1, 2, 3]
b = [4, 5, 6]

a.append(b)
print(a) # [1, 2, 3, [4, 5, 6]]

a = [1, 2, 3]
b = [4, 5, 6]

a.extend(b)
print(a) # [1, 2, 3, 4, 5, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;append는 해당 리스트(b) 그 자체를 리스트(a) 끝에 넣어준다. 반면에 extend는 해당 리스트(b) 안의 요소들을 (a)리스트 끝에 넣어 확장시켜준다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>append</category>
      <category>extend</category>
      <category>백준 1992 파이썬</category>
      <category>분할정복</category>
      <category>재귀</category>
      <category>쿼드트리</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/148</guid>
      <comments>https://fullmoon1344.tistory.com/148#entry148comment</comments>
      <pubDate>Tue, 24 Nov 2020 17:10:56 +0900</pubDate>
    </item>
    <item>
      <title>[백준 2630] 색종이 만들기 - Python (분할정복, 재귀)</title>
      <link>https://fullmoon1344.tistory.com/147</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1606189513111&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2630번: 색종이 만들기&quot; data-og-description=&quot;첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2630&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2630&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gUFK8/hyIkDauHmz/RMPEg5zkTiOhqy9yl4Mff1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2630&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2630&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gUFK8/hyIkDauHmz/RMPEg5zkTiOhqy9yl4Mff1/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;&gt;2630번: 색종이 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;lt;내 코드&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1606189561948&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
paper = [list(map(int, input().split())) for _ in range(N)]

white, blue = 0, 0


def divide(x, y, n):
    global white, blue
    color = paper[x][y]
    check = True

    for i in range(x, x+n):
        if not check:
            break

        for j in range(y, y+n):
            if color != paper[i][j]:
                check = False
                divide(x, y, n//2)  # 1사분면
                divide(x, y+n//2, n//2)  # 2사분면
                divide(x+n//2, y, n//2)  # 3사분면
                divide(x+n//2, y+n//2, n//2)  # 4사분면
                break
    if check:
        if color:
            blue += 1
        else:
            white += 1


divide(0, 0, N)
print(white)
print(blue)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 문제는 처음 접하는 개념인 '쿼드트리'를 만드는 것이었다. 트리의 자식노드가 4개씩 분할하는 방법이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기서 분할 된 영역 안에서 같은 색인지 판단해 다르다면 또 다시 나누면서 찾아간다. 일반 동적 계획법과 다르게 분할 정복은 중복되는 하위 문제가 없다는 것이 차이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 2630</category>
      <category>분할정복</category>
      <category>재귀</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/147</guid>
      <comments>https://fullmoon1344.tistory.com/147#entry147comment</comments>
      <pubDate>Tue, 24 Nov 2020 12:50:43 +0900</pubDate>
    </item>
    <item>
      <title>[React] React Hooks - useState, useEffect</title>
      <link>https://fullmoon1344.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;Hooks는 리액트 v16.8에 새로 도입된 기능으로서, &lt;b&gt;함수형 컴포넌트&lt;/b&gt;에서도 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능 등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해 준다. &lt;b&gt;Hook은 &lt;span style=&quot;color: #000000;&quot;&gt;함수형 컴포넌트에서 React의 특징을 갖게 해주는 함수다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Hook 사용 규칙&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;최상위(at the top level)&lt;/b&gt;에서만 Hook을 호출해야 한다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 말아야한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React 함수 컴포넌트&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;내에서만 Hook을 호출해야 한다. 일반 JavaScript 함수에서는 Hook을 호출해서는 안 됩니다. (Hook을 호출할 수 있는 곳이 딱 한 군데 더 있다. 바로 직접 작성한 custom Hook 내이다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. useState&lt;/h2&gt;
&lt;pre id=&quot;code_1605246585121&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;h4 data-ke-size=&quot;size20&quot;&gt;- 형태&lt;/h4&gt;
&lt;pre id=&quot;code_1605246737823&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [value, setValue] = useState(0); // [현재 state, 업데이트 함수]와 state 초기값&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1605247106649&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ExampleWithManyStates() {
  // 상태 변수를 여러 개 선언했습니다!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;현재의 state값&lt;/b&gt;과 &lt;b&gt;이 값을 업데이트하는 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;함수&lt;/span&gt;&lt;/b&gt;를 &lt;i&gt;배열 할당 형태로&lt;/i&gt; 사용한다. 이 함수를 이벤트 핸들러나 다른 곳에서 호출해 사용 가능하다. state 초기값을 첫 번째 렌더링에만 딱 한번 사용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. useEffect&lt;/h2&gt;
&lt;pre id=&quot;code_1605248227294&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// React가 DOM을 업데이트한 뒤에 문서의 타이틀을 바꾸는 컴포넌트
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // componentDidMount, componentDidUpdate와 비슷합니다
  useEffect(() =&amp;gt; {
    // 브라우저 API를 이용해 문서의 타이틀을 업데이트합니다
    document.title = `You clicked ${count} times`;
  });

  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;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;useEffect는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다. &lt;span style=&quot;color: #000000;&quot;&gt;React class의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;componentDidMount&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;componentDidUpdate&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;componentWillUnmount&lt;span style=&quot;color: #000000;&quot;&gt;와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-1. useEffect의 두 번째 인수 사용&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 특정 값을 넣어주는 경우&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1605253623508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  document.title = `You clicked ${count} times`;
}, [count]); // count가 바뀔 때만 effect를 재실행합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2번째 인자로 배열이 주어지는데, 배열 안 값이 리렌더링 시에 변경되지 않는다면 리액트는 effect를 건너뛴다. 즉 안의 값이 변할 때만 리렌더링을 한다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열 내에 여러 개의 값이 있다면 그중의 단 하나만 다를지라도 리액트는 effect를 재실행합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 빈 배열을 인자로 줄 경우&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1605253892147&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    console.log('마운트 될 때만 실행됩니다.');
  }, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;빈 배열(&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[]&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)을 넘기게 되면, effect 안의 prop과 state는 초깃값을 유지하게 된다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;화면에 가장 처음 렌더링 될 때만 실행되고 업데이트할 경우에는 실행할 필요가 없는 경우에 빈 배열을 설정해준다. &lt;span style=&quot;color: #000000;&quot;&gt;effect를 실행하고 이를 정리(clean-up)하는 과정을 (마운트와 마운트 해제 시에) 딱 한 번씩만 실행하고 싶다면, 빈 배열(&lt;/span&gt;[]&lt;span style=&quot;color: #000000;&quot;&gt;)을 두 번째 인수로 넘기면 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;useEffect(&amp;lt;function&amp;gt;, &amp;lt;Array&amp;gt;)&lt;/span&gt;&amp;nbsp;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == 생략&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 첫 렌더링 + state 변화에 따른 모든 렌더링 시에 동작한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == [ ]&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 첫 렌더링 시에만 동작한다. componentDidMount()와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;&amp;lt;Array&amp;gt; == [state]&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 첫 렌더링 시 동작하고, 배열 안에 넣어준 값의 상태가 변할 때마다 계속 동작한다. componentDidMount() &amp;amp; componentDidUpdate()&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;Clean up&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;return&lt;/b&gt;으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;함수&lt;/b&gt;를 지정해주면 clean up작업을 한다. componentWillUnMount()와 같은 동작이다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;컴포넌트가 수명을 다하고 사라질 때 어떤 행동을 하는 것을 의미한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2. 정리(Clean-up)를 이용하는 Effects&lt;/h4&gt;
&lt;pre id=&quot;code_1605256018645&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; useEffect(() =&amp;gt; {
    console.log('effect');
    console.log(name);
    
    // effect 이후에 어떻게 정리(clean-up)할 것인지 표시
    return () =&amp;gt; {
      console.log('cleanup');
      console.log(name);
    };
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/React</category>
      <category>react hooks</category>
      <category>useEffect</category>
      <category>useState</category>
      <category>리액트 훅</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/146</guid>
      <comments>https://fullmoon1344.tistory.com/146#entry146comment</comments>
      <pubDate>Fri, 13 Nov 2020 18:01:10 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] http-proxy-middleware 모듈</title>
      <link>https://fullmoon1344.tistory.com/145</link>
      <description>&lt;figure id=&quot;og_1605018642635&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;http-proxy-middleware&quot; data-og-description=&quot;The one-liner node.js proxy middleware for connect, express and browser-sync&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/http-proxy-middleware&quot; data-og-url=&quot;https://www.npmjs.com/package/http-proxy-middleware&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IE9qK/hyIb1aN0hG/iu0UwkpaMmvyKhp8lywj3k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/http-proxy-middleware&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/http-proxy-middleware&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IE9qK/hyIb1aN0hG/iu0UwkpaMmvyKhp8lywj3k/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;&gt;http-proxy-middleware&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The one-liner node.js proxy middleware for connect, express and browser-sync&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 설치&lt;/h2&gt;
&lt;pre id=&quot;code_1605017999092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install http-proxy-middleware --save-dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- client 단 src파일 아래 '&lt;span style=&quot;color: #000000;&quot;&gt;setupProxy.js' 파일을 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 다음과 같이 proxy 설정을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) &lt;span style=&quot;color: #333333;&quot;&gt;http-proxy-middleware v1.0 이전&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1605018154657&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const proxy = require('http-proxy-middleware');

// src/setupProxy.js
module.exports = function(app) {
    app.use(
        proxy('/api', {
            target: &quot;http://localhost:5000/&quot;, 
            changeOrigin: true
        })
    );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;proxy 설정을 추가해줌으로 /api로 시작되는 API는 target으로 설정된 서버 URL로 호출하도록 설정된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) http-proxy-middleware v1.0 이후 수정&lt;/h4&gt;
&lt;pre id=&quot;code_1605018297761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const createProxyMiddleware = require('http-proxy-middleware');

// src/setupProxy.js
module.exports = function(app) {
    app.use(
        createProxyMiddleware('/api', {
            target: &quot;http://localhost:5000/&quot;, 
            changeOrigin: true
        })
    );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;- 기본형태&lt;/h4&gt;
&lt;pre id=&quot;code_1605018351721&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { createProxyMiddleware } = require('http-proxy-middleware');
 
const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' });
//                                      		\____/   \_____________________________/
//                                       	 	  |                    |
//                                      		context             options
 
// 'apiProxy' is now ready to be used as middleware in a server.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;context로 설정한 주소가 tartget으로 설정한 서버 쪽 url로 proxing 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여러 옵션들은 차차 필요할 때 사용하면 될 듯하다.&lt;/p&gt;</description>
      <category>Node.js</category>
      <category>http-proxy-middleware 사용법</category>
      <category>Node.js proxy</category>
      <category>프록시</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/145</guid>
      <comments>https://fullmoon1344.tistory.com/145#entry145comment</comments>
      <pubDate>Tue, 10 Nov 2020 23:29:56 +0900</pubDate>
    </item>
    <item>
      <title>[Express] express.Router() 와 express() 차이</title>
      <link>https://fullmoon1344.tistory.com/144</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1604502014854&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Differences between express.Router and app.get?&quot; data-og-description=&quot;I'm starting with NodeJS and Express 4, and I'm a bit confused. I been reading the Express website, but can't see when to use a route handler or when to use express.Router. As I could see, if I wa...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get&quot; data-og-url=&quot;https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TJMHC/hyH7vQ8kx0/muZmj9ZF8QxCntVXv7zhv0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/28305120/differences-between-express-router-and-app-get&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TJMHC/hyH7vQ8kx0/muZmj9ZF8QxCntVXv7zhv0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Differences between express.Router and app.get?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;I'm starting with NodeJS and Express 4, and I'm a bit confused. I been reading the Express website, but can't see when to use a route handler or when to use express.Router. As I could see, if I wa...&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;app.js&lt;/blockquote&gt;
&lt;pre id=&quot;code_1604502049513&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var express = require('express'),
    dogs    = require('./routes/dogs'), // 모듈로 만든 라우터들을 사용
    cats    = require('./routes/cats'),
    birds   = require('./routes/birds');

var app = express();

app.use('/dogs',  dogs);
app.use('/cats',  cats);
app.use('/birds', birds);

app.listen(3000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;use메서드&lt;/b&gt;는 모든 HTTP 메서드에 대해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;요청 주소만 일치하면 실행&lt;/b&gt;되지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;get, post, put, patch, delete&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 메서드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;주소뿐만 아니라 HTTP 메서드까지 일치하는&lt;/b&gt; 요청일 때만 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- express()는 listen()을 이용해 수신한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style2&quot;&gt;routes/dog.js&lt;/blockquote&gt;
&lt;pre id=&quot;code_1604547787661&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var express = require('express');

var router = express.Router();

router.get('/', function(req, res) {
    res.send('GET handler for /dogs route.');
});

router.post('/', function(req, res) {
    res.send('POST handler for /dogs route.');
});

module.exports = router; // 라우터를 모듈화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;router에도&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;app처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;use, get, post, put, patch, delete&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;같은 메서드를 붙일 수 있다. use를 제외하고는 각각 HTTP 요청 메서드와 상응한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;라우터에서는 반드시 요청에 대한 응답을 보내거나 에러 핸들러로 요청을 넘겨야 한다. 응답을 보내지 않으면 브라우저는 계속 응답을 기다린다. &lt;span&gt;res객체에 들어 있는 메서드들로 응답을 보낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- Router는 요청에 대해 listen()을 사용하지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size18&quot; data-ke-style=&quot;style2&quot;&gt;두 가지 경우 차이점&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;var app = express()가 호출되면 앱 객체가 반환된다. 이것을 &lt;b&gt;Main app&lt;/b&gt;으로 생각한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;var router = express.Router()가 호출되면 &lt;span style=&quot;color: #000000;&quot;&gt;Main app&lt;/span&gt;과는 조금 다른 &lt;b&gt;Mini app&lt;/b&gt;이 반환된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;코드(미들웨어)가 복잡해지는 것을 방지하고 재사용성을 높이기 위해 각 라우터를 별도의 모듈로 만드는 것이다. 즉, 각 기능에 맞는 Mini app을 만들고 그것들을 Main app에서 불러와 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예에서, /dogs 경로에 대한 코드는 메인 앱을 복잡하게 하지 않도록 자체 파일로 이동했다. /cats와 /birds에 대한 코드는 그들 자신의 파일에서 유사한 구조가 될 것이다. 이 코드를 세 개의 미니 앱으로 분리하면 각각을 위한 logic 작업을 따로 할 수 있고, 나머지 두 개에 어떤 영향을 미칠지 걱정하지 않아도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Node.js/Express</category>
      <category>express router</category>
      <category>express.js</category>
      <category>node.js</category>
      <category>라우터</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/144</guid>
      <comments>https://fullmoon1344.tistory.com/144#entry144comment</comments>
      <pubDate>Thu, 5 Nov 2020 13:04:41 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Mongoose 모듈 사용하기</title>
      <link>https://fullmoon1344.tistory.com/143</link>
      <description>&lt;figure id=&quot;og_1604372783640&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;mongoose&quot; data-og-description=&quot;Mongoose MongoDB ODM&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/mongoose&quot; data-og-url=&quot;https://www.npmjs.com/package/mongoose&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLFti7/hyH7i3EpRN/FdreAYXhnjvBMqUtfRFLeK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/mongoose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/mongoose&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLFti7/hyH7i3EpRN/FdreAYXhnjvBMqUtfRFLeK/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;&gt;mongoose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Mongoose MongoDB ODM&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. Mongoose ?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Node.js 비동기 환경에서 동작하도록 디자인 된 몽고DB 모델링 툴이다. Mongoose는 promise와 callback 모두 지원한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 설치&lt;/h2&gt;
&lt;pre id=&quot;code_1604330369262&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install mongoose --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 불러오기(importing)&lt;/h2&gt;
&lt;pre id=&quot;code_1604330417117&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Using Node.js `require()`
const mongoose = require('mongoose');
 
// Using ES6 imports
import mongoose from 'mongoose';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 몽고DB 연결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 하나의 DB만 사용한다면, mongoose.connect()를 사용하고, 만약 추가적인 연결들이 필요할 경우에 mongoose.createConnection()을 사용한다. 두 가지 경우 모두 mongodb://URI 또는 host, database, port, options 같은 파라미터를 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604331032107&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// npm공식 문서 사용 예시
mongoose.connect('mongodb://localhost/my_database', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false,
  useCreateIndex: true
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604331072619&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용 예시
// config.mongoURI --&amp;gt; 몽고DB url을 외부 모듈(config라는 명)로 만들어 불러서 사용한 경우
mongoose.connect(config.mongoURI, {
  useNewUrlParser: true, 
  useUnifiedTopology: true, 
  useCreateIndex: true, 
  useFindAndModify: false
})
.then(() =&amp;gt; console.log('MongoDB Conneted...'))
.catch(err =&amp;gt; console.log(err));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 몽고 DB 가 Local 일 경우 주소만 해당되지만 클라우드를 통해서 올 때는 비밀번호까지 들어가기 때문에 조심해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. 스키마 정의 &amp;amp; 모델화&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604404198043&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mySchema = mongoose.Schema({
    name : 'string',
    address : 'string',
    age : 'number'
});&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1604404425450&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// mongoose.model('ModelName', mySchema)
const MyModel = mongoose.model('ModelName');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;정의된 스키마를 객체처럼 사용할 수 있도록 model() 함수로 컴파일한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;6. 모델 객체 생성 후 데이터 입력&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1604406993947&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const newInfo = new MyModel({name:'Hong Gil Dong', address:'서울시 강남구', age:'26'});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;new를 이용해 모델 객체를 생성해 데이터를 입력해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 데이터 저장&lt;/h2&gt;
&lt;pre id=&quot;code_1604407124536&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;newInfo.save(function(error, data){
    if(error){
        console.log(error);
    }else{
        console.log('Saved!')
    }
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Model.save(function(err,product));&lt;span&gt;&amp;nbsp;&lt;/span&gt;: Callback&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Model.save().then(resolved,rejected);&lt;span&gt;&amp;nbsp;&lt;/span&gt;: Promise&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 데이터 가져오기&lt;/h2&gt;
&lt;pre id=&quot;code_1604408302418&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// find(): 전체 데이터 불러오기, findOne() : 데이터 하나 가져오기
MyModel.find({}, function (err, docs) {
  // docs.forEach
});

// 예제
newInfo.find(function(error, students){
    console.log('--- Read all ---');
    if(error){
        console.log(error);
    }else{
        console.log(students);
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 데이터 수정하기&lt;/h2&gt;
&lt;pre id=&quot;code_1604409620184&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 특정 id에 해당하는 데이터 수정
MyModel.findOne({_id:'585b777f7e2315063457e4ac'}, function(error,student){
    if(error){
        console.log(error);
    }else{
        student.name = '김철수';
        student.save(function(error,modified_student){
            if(error){
                console.log(error);
            }else{
                console.log(modified_student);
            }
        });
    }
});

// update() 이용방법도 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 데이터 삭제하기&lt;/h2&gt;
&lt;pre id=&quot;code_1604409823541&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyModel.findOne({title:'아바타'}, function(err, doc) {
   if ( doc ) {
      doc.name = '홍길동';
      doc.remove(function(err, product) {
         console.log('Find and Remove : ', err, product);
      });         
   }
});

// remove()함수 이용 
MyModel.remove({job:'학생'}).then(resolved, rejected);&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Node.js</category>
      <category>Mongoose 모듈</category>
      <category>node.js</category>
      <category>몽고db</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/143</guid>
      <comments>https://fullmoon1344.tistory.com/143#entry143comment</comments>
      <pubDate>Tue, 3 Nov 2020 22:25:35 +0900</pubDate>
    </item>
    <item>
      <title>[백준 14889] 스타트와 링크 - Python (브루트포스, 백트래킹)</title>
      <link>https://fullmoon1344.tistory.com/142</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1604321100546&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;14889번: 스타트와 링크&quot; data-og-description=&quot;예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14889&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14889&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Qqgxk/hyH5ZRFphW/6KXSfhkflgP0vntZmMvjk0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14889&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14889&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Qqgxk/hyH5ZRFphW/6KXSfhkflgP0vntZmMvjk0/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;&gt;14889번: 스타트와 링크&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604321169909&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import combinations

N = int(input())
S = list(list(map(int, input().split())) for _ in range(N))
comb = combinations(range(N), N//2)  # 0 ~ N-1을 N/2 만큼 씩 조합
ans = float('inf')  # inf는 어떤 숫자와 비교해도 무조건 크다고 판정
# -inf는 어떤 수보다 무조건 작다

for c in comb:
    start_team = list(c)
    link_team = list(set(range(N)) - set(c))  # 차집합 후 리스트화

    start_ability, link_ability = 0, 0

    for i in range(N//2 - 1):
        for j in range(i+1, N//2):
            start_ability += S[start_team[i]][start_team[j]
                                              ] + S[start_team[j]][start_team[i]]
            link_ability += S[link_team[i]][link_team[j]] + \
                S[link_team[j]][link_team[i]]

    ans = min(ans, abs(start_ability - link_ability))

print(ans)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이번 문제는 백트래킹 문제지만 브루트포스로 풀었다. 다른 사람의 풀이를 참조하면서 새로운 개념들을 몇 가지 알게 됐다. 집합을 이용해 팀을 나누는 방법을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;inf - 어떤 숫자와 비교해도 무조건 크다고 판정되는 파이썬 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;-inf - &lt;span style=&quot;color: #333333;&quot;&gt;어떤 숫자와 비교해도 무조건 작다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>INF</category>
      <category>백준 14889 파이썬</category>
      <category>브루트포스</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/142</guid>
      <comments>https://fullmoon1344.tistory.com/142#entry142comment</comments>
      <pubDate>Mon, 2 Nov 2020 22:03:50 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Express.js 란 무엇인가?</title>
      <link>https://fullmoon1344.tistory.com/141</link>
      <description>&lt;blockquote data-ke-size=&quot;size26&quot; data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Express 공식 문서를 참조했습니다.&lt;/span&gt;&lt;/i&gt;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1. Express?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;: Node.js를 위한 빠르고 개방적인 웹 프레임워크, &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span&gt;최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. 설치&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1604250689692&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install express --save

npm install express&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3. 기본 사용법&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1604250747887&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) =&amp;gt; {
  res.send('Hello World!')
})

app.listen(port, () =&amp;gt; {
  console.log(`Example app listening at http://localhost:${port}`)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;4. 라우팅&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1) 구조&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1604250790453&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.METHOD(PATH, HANDLER)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- app : express() 객체 인스턴스&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- METHOD : HTTP 요청 메소드(get, post, put, delete...)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- PATH : 서버에서의 경로 url&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- HANDLER : 라우트가 성공했을 때 실행되는 함수&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2) 예제&lt;/span&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 라우트 메소드&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1604251020544&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var express = require('express');
var app = express();
// 홈 화면으로 라우팅 되며, hello world가 출력
app.get('/', function(req, res) {
  res.send('hello world');
});

// GET method route
app.get('/', function (req, res) {
  res.send('GET request to the homepage');
});

// POST method route
app.post('/', function (req, res) {
  res.send('POST request to the homepage');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 라우트 경로&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;요청 메소드와의 조합을 통해, 요청이 이루어질 수 있는 엔드포인트를 정의한다. 라우트 경로는 문자열, 문자열 패턴 또는 정규식 일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 라우트 핸들러&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하나의 콜백 함수는 하나의 라우트를 처리할 수 있다. 예를 들면 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1604251354374&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;미들웨어와 비슷하게 작동하는 여러 콜백 함수를 제공하여 요청을 처리할 수 있다. 유일한 차이점은 이러한 콜백은&amp;nbsp;next('route')를 호출하여 나머지 라우트 콜백을 우회할 수도 있다는 점이다. 이러한 메커니즘을 이용하면 라우트에 대한 사전 조건을 지정한 후, 현재의 라우트를 계속할 이유가 없는 경우에는 제어를 후속 라우트에 전달할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604251597254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2개 이상의 콜백 함수는 하나의 라우트를 처리할 수 있다(next 오브젝트를 반드시 지정해야 함)
app.get('/example/b', function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1604292241014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 하나의 콜백 함수 배열은 하나의 라우트를 처리할 수 있다.
var cb0 = function (req, res, next) {
  console.log('CB0');
  next();
}

var cb1 = function (req, res, next) {
  console.log('CB1');
  next();
}

var cb2 = function (req, res) {
  res.send('Hello from C!');
}

app.get('/example/c', [cb0, cb1, cb2]);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1604292268579&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 독립적인 함수와 함수 배열의 조합은 하나의 라우트를 처리할 수 있다.
var cb0 = function (req, res, next) {
  console.log('CB0');
  next();
}

var cb1 = function (req, res, next) {
  console.log('CB1');
  next();
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from D!');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;5. 미들웨어&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1) 작성&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu6scU/btqMeGCKnvR/3EpTzgLpuX3msfkmGPENN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu6scU/btqMeGCKnvR/3EpTzgLpuX3msfkmGPENN0/img.png&quot; data-alt=&quot;&amp;amp;amp;lt;출처: https://expressjs.com&amp;amp;amp;gt;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu6scU/btqMeGCKnvR/3EpTzgLpuX3msfkmGPENN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu6scU%2FbtqMeGCKnvR%2F3EpTzgLpuX3msfkmGPENN0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;&amp;lt;출처: https://expressjs.com&amp;gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2) 예제&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1604294487887&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var express = require('express');
var app = express();

// 미들웨어 함수 작성 후 변수 할당
var requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};

// 미들웨어 함수를 로드하려면 미들웨어 함수를 지정하여 app.use()를 호출
// 루트 경로 라우트 로드 전에 먼저 로드 된다.
app.use(requestTime);

// 콜백 함수는 미들웨어 함수가 req(요청 오브젝트)에 추가하는 특성을 사용한다.
app.get('/', function (req, res) {
  var responseText = 'Hello World!';
  responseText += 'Requested at: ' + req.requestTime + '';
  res.send(responseText);
});

app.listen(3000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- app.use() : 사용하고자 하는 미들웨어 함수를 로드한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;미들웨어의 로드 순서는 중요하며, 먼저 로드되는 미들웨어 함수가 먼저 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;만약 위 예제에서 루트 경로 라우링 이후 app.use()를 하면 루트 경로의 라우트 핸들러가 요청-응답 주기를 종료하므로 요청은 절대로 requestTime에 도달하지 못한다. &lt;b&gt;(스택 내의 그다음 미들웨어 함수에 요청을 전달하는 next() 요청이 없기 때문이다.)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #c0d1e7;&quot;&gt;현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는&amp;nbsp;next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;사용자는 요청 오브젝트, 응답 오브젝트, 스택 내의 그 다음 미들웨어 함수, 그리고 모든 Node.js API에 대한 액세스 권한을 가지고 있으므로, 미들웨어 함수에 대한 가능성, 중요성은 끝이 없다고 설명돼있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3) 여러 미들웨어 함수 종류&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRBaUg/btqMfjHwC7l/jMwFSyoguAKbjgJGOaKah0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRBaUg/btqMfjHwC7l/jMwFSyoguAKbjgJGOaKah0/img.png&quot; data-alt=&quot;&amp;amp;amp;lt;출처: https://expressjs.com/ko/resources/middleware.html&amp;amp;amp;gt;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRBaUg/btqMfjHwC7l/jMwFSyoguAKbjgJGOaKah0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRBaUg%2FbtqMfjHwC7l%2FjMwFSyoguAKbjgJGOaKah0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;&amp;lt;출처: https://expressjs.com/ko/resources/middleware.html&amp;gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 외에도 유명한 여러 미들웨어가 많으니 필요에 따라 사용하면 될 듯하다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Node.js</category>
      <category>express.js</category>
      <category>Node.js 프레임워크</category>
      <category>서버 프레임워크</category>
      <category>웹 개발</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/141</guid>
      <comments>https://fullmoon1344.tistory.com/141#entry141comment</comments>
      <pubDate>Mon, 2 Nov 2020 14:49:31 +0900</pubDate>
    </item>
    <item>
      <title>[백준 14888] 연산자 끼워넣기 - Python (백트래킹, DFS, 브루트포스)</title>
      <link>https://fullmoon1344.tistory.com/140</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603796093101&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;14888번: 연산자 끼워넣기&quot; data-og-description=&quot;첫째 줄에 수의 개수 N(2 &amp;le; N &amp;le; 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 &amp;le; Ai &amp;le; 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수,&amp;nbsp;&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14888&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14888&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/m4gDr/hyH06p8arN/dALV6dXWXNmaeeNNIzQsZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14888&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14888&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/m4gDr/hyH06p8arN/dALV6dXWXNmaeeNNIzQsZk/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;&gt;14888번: 연산자 끼워넣기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 수의 개수 N(2 &amp;le; N &amp;le; 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 &amp;le; Ai &amp;le; 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수,&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603796137248&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
nums = list(map(int, input().split()))
add, sub, mul, div = map(int, input().split())

# 비교값
max_ = -1e9
min_ = 1e9


def dfs(cnt, res, add, sub, mul, div):
    global max_, min_
    # 재귀 탈출 조건
    if cnt == N:
        max_ = max(res, max_)
        min_ = min(res, min_)
        return

    if add:
        dfs(cnt+1, res+nums[cnt], add-1, sub, mul, div)
    if sub:
        dfs(cnt+1, res-nums[cnt], add, sub-1, mul, div)
    if mul:
        dfs(cnt+1, res*nums[cnt], add, sub, mul-1, div)
    if div:
        dfs(cnt+1, int(res/nums[cnt]), add, sub, mul, div-1)


dfs(1, nums[0], add, sub, mul, div)
print(max_)
print(min_)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;백트래킹을 이용해 모든 경우를 다 탐색하는 방법이다. 아직까지 재귀와 백트래킹 개념이 어려운 것 같다..&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>dfs</category>
      <category>백준 14888 파이썬</category>
      <category>백트래킹</category>
      <category>브루트포스</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/140</guid>
      <comments>https://fullmoon1344.tistory.com/140#entry140comment</comments>
      <pubDate>Tue, 27 Oct 2020 20:29:04 +0900</pubDate>
    </item>
    <item>
      <title>[백준 15652] N과 M (4) - Python (백트래킹, DFS)</title>
      <link>https://fullmoon1344.tistory.com/139</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603724872779&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;15652번: N과 M (4)&quot; data-og-description=&quot;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15652&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15652&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2kfrF/hyHZtZ6ZDi/s9uybqbYpZ2JAkULI4O280/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15652&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15652&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2kfrF/hyHZtZ6ZDi/s9uybqbYpZ2JAkULI4O280/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;&gt;15652번: N과 M (4)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603724917996&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
N, M = map(int, sys.stdin.readline().split())
visited = [0 for _ in range(N)]
arr = []


def dfs(cnt):
    if cnt == M:
        print(*arr)
        return

    for i in range(N):
        if visited[i] == 0:
            arr.append(i+1)

            dfs(cnt+1)  # 다음 깊이 탐색
            visited[i] = 1

            arr.pop()  # 탐사한 내용 제거
            # 시작 탐색 값만 제외시키도록 나머진 초기화
            for j in range(i+1, N):
                visited[j] = 0


dfs(0)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>dfs</category>
      <category>백준 15652 파이썬</category>
      <category>백트래킹</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/139</guid>
      <comments>https://fullmoon1344.tistory.com/139#entry139comment</comments>
      <pubDate>Tue, 27 Oct 2020 00:08:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준 15651] N과 M (3) - Python (백트래킹, DFS)</title>
      <link>https://fullmoon1344.tistory.com/138</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603724045669&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;15651번: N과 M (3)&quot; data-og-description=&quot;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15651&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15651&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJAExN/hyHZnMjjon/cj60iiLAKvZrFowul9UITK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15651&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15651&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJAExN/hyHZnMjjon/cj60iiLAKvZrFowul9UITK/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;&gt;15651번: N과 M (3)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603724104080&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N, M = map(int, input().split())
#visited = [0 for _ in range(N)]
arr = []


def dfs(cnt):
    if cnt == M:
        print(*arr)
        return

    for i in range(N):
        arr.append(i+1)
        dfs(cnt+1)  # 다음 깊이 탐색
        arr.pop()  # 탐사한 내용 제거


dfs(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앞의 (1), (2) 순열, 조합 문제랑은 다르게 같은 수 중복 제거가 없다. 중복이 허용되고 그냥 탐사한 내용만 제거해주면 간단하게 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>dfs</category>
      <category>백준 15651 파이썬</category>
      <category>백트래킹</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/138</guid>
      <comments>https://fullmoon1344.tistory.com/138#entry138comment</comments>
      <pubDate>Mon, 26 Oct 2020 23:56:55 +0900</pubDate>
    </item>
    <item>
      <title>[백준 15650] N과 M (2) - Python (백트래킹, DFS, 조합)</title>
      <link>https://fullmoon1344.tistory.com/137</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603721095091&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;15650번: N과 M (2)&quot; data-og-description=&quot;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15650&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15650&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c1VxdW/hyHZAyz6gM/7vrk9tihHmNR41ECAbTKA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15650&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15650&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c1VxdW/hyHZAyz6gM/7vrk9tihHmNR41ECAbTKA1/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;&gt;15650번: N과 M (2)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603721144147&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N, M = map(int, input().split())
visited = [0 for _ in range(N)]
arr = []

def dfs(cnt):
    if cnt == M:
        print(*arr)
        return

    for i in range(N):
        if visited[i] == 0:
            visited[i] = 1  # 중복 제거
            arr.append(i+1)

            dfs(cnt+1)  # 다음 깊이 탐색
            arr.pop()  # 탐사한 내용 제거

            # 순열 부분과의 차이점
            for j in range(i+1, N):
                visited[j] = 0

dfs(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음 i의 DFS 탐색에 사용 된 i값을 제외시키는 것이 순열과 다른 조합의 차이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;&quot;&gt;파이썬 itertools 모듈의 combinations를 사용한 방법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603721389888&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import combinations

N, M = map(int, input().split())

p = combinations(range(1, N+1), M)
for i in p:
    print(*i)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>dfs</category>
      <category>백준 15650 파이썬</category>
      <category>백트래킹</category>
      <category>조합</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/137</guid>
      <comments>https://fullmoon1344.tistory.com/137#entry137comment</comments>
      <pubDate>Mon, 26 Oct 2020 23:10:53 +0900</pubDate>
    </item>
    <item>
      <title>[백준 15649] N과 M(1) - Python (백트래킹, DFS, 순열)</title>
      <link>https://fullmoon1344.tistory.com/136</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603711998751&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;15649번: N과 M (1)&quot; data-og-description=&quot;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/15649&quot; data-og-url=&quot;https://www.acmicpc.net/problem/15649&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/edXfUV/hyHZwCPKBK/789S5dT8slKpFVP2G2a2Dk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15649&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/15649&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/edXfUV/hyHZwCPKBK/789S5dT8slKpFVP2G2a2Dk/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;&gt;15649번: N과 M (1)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603712043625&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N, M = map(int, input().split())
visited = [0 for _ in range(N)]
arr = []


def dfs(cnt):
    if cnt == M:
        print(*arr)
        return

    for i in range(N):
        if visited[i] == 0:
            visited[i] = 1  # 중복 제거
            arr.append(i+1)

            dfs(cnt+1)  # 다음 깊이 탐색

            visited[i] = 0  # 탐사 완료 후 다시 초기화
            arr.pop()  # 탐사한 내용 제거


dfs(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DFS와 백트래킹을 이용해 푸는 문제다. 재귀가 사용되다 보니 이해를 하는데 꽤 힘들었다..&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;파이썬 itertools 모듈을 사용한 방법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603713037861&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import permutations

N, M = map(int, input().split())

p = permutations(range(1, N+1), M)
for i in p:
    print(*i)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>dfs</category>
      <category>백준 15649 파이썬</category>
      <category>백트래킹</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/136</guid>
      <comments>https://fullmoon1344.tistory.com/136#entry136comment</comments>
      <pubDate>Mon, 26 Oct 2020 20:35:40 +0900</pubDate>
    </item>
    <item>
      <title>[백준 14226] 이모티콘 - Python (BFS, DP)</title>
      <link>https://fullmoon1344.tistory.com/135</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603602606539&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;14226번: 이모티콘&quot; data-og-description=&quot;영선이는 매우 기쁘기 때문에, 효빈이에게 스마일 이모티콘을 S개 보내려고 한다. 영선이는 이미 화면에 이모티콘 1개를 입력했다. 이제, 다음과 같은 3가지 연산만 사용해서 이모티콘을 S개 만&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/14226&quot; data-og-url=&quot;https://www.acmicpc.net/problem/14226&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/da44cL/hyHZrfHDRf/OdYWOmvcR4KwfyhtCGGu1K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14226&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/14226&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/da44cL/hyHZrfHDRf/OdYWOmvcR4KwfyhtCGGu1K/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;&gt;14226번: 이모티콘&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;영선이는 매우 기쁘기 때문에, 효빈이에게 스마일 이모티콘을 S개 보내려고 한다. 영선이는 이미 화면에 이모티콘 1개를 입력했다. 이제, 다음과 같은 3가지 연산만 사용해서 이모티콘을 S개 만&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603602668189&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


def bfs():

    while q:
        s, c = q.popleft()
        # 복사, 붙여넣기, 삭제 후 1초씩 증가 &amp;amp;&amp;amp; 다녀간 곳 q에 추가
        # 복사
        if check[s][s] == -1:
            check[s][s] = check[s][c] + 1
            q.append((s, s))
        # 붙여넣기
        if s+c &amp;lt;= S and check[s+c][c] == -1:
            check[s+c][c] = check[s][c] + 1
            q.append((s+c, c))
        # 삭제
        if s-1 &amp;gt;= 0 and check[s-1][c] == -1:
            check[s-1][c] = check[s][c] + 1
            q.append((s-1, c))


S = int(input())
q = deque()
MAX = 1001
check = [[-1]*MAX for _ in range(MAX)]

check[1][0] = 0
q.append((1, 0))

bfs()

ans = -1
for i in range(S):
    if check[S][i] != -1:
        if ans == -1 or ans &amp;gt; check[S][i]:
            ans = check[S][i]
print(ans)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;처음에 문제를 복사 - 붙여 넣기 - 복사- 붙여 넣기... 하는 것으로 잘못 이해했었다. 실제로는 클립보드에 들어있는 이모티콘은 여러 번 화면에 붙여 넣기가 가능한 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 문제는 기존의 BFS 문제랑 조금 차이가 있다. 기존에는 입력을 그래프 형태로 주어졌는데, 이 문제는 본인이 새로 그래프를 만들어야 했다. 복사, 붙여넣기, 삭제 부분을 작성하고 &lt;span style=&quot;color: #333333;&quot;&gt;현재 s,c에 대한&amp;nbsp; 시간 check [s][c] 값에&lt;/span&gt; +1초씩 해준다.&amp;nbsp; 그리고 찾고자 하는 입력 값 check [s] 중 최솟값을 찾아 출력하면 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>bfs</category>
      <category>백준 14226 파이썬</category>
      <category>최솟값 찾기</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/135</guid>
      <comments>https://fullmoon1344.tistory.com/135#entry135comment</comments>
      <pubDate>Sun, 25 Oct 2020 14:33:32 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1261] 알고스팟 - Python (우선순위 큐, BFS)</title>
      <link>https://fullmoon1344.tistory.com/134</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603536522956&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1261번: 알고스팟&quot; data-og-description=&quot;첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 &amp;le; N, M &amp;le; 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은&amp;nbsp;벽을 의미&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1261&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1261&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/e6jii/hyHZkUKxs5/XJl41kyCkkJSqa6KjEOAqK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1261&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1261&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/e6jii/hyHZkUKxs5/XJl41kyCkkJSqa6KjEOAqK/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;&gt;1261번: 알고스팟&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 &amp;le; N, M &amp;le; 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은&amp;nbsp;벽을 의미&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603536574136&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
import sys


def bfs():
    dx = [1, -1, 0, 0]
    dy = [0, 0, 1, -1]

    while(q):

        cnt, x, y = heapq.heappop(q)  # (0,0,0)형식
        done[x][y] = 1

        if x == N-1 and y == M-1:
            return cnt

        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]

            if (0 &amp;lt;= nx &amp;lt; N) and (0 &amp;lt;= ny &amp;lt; M) and done[nx][ny] == 0:
                done[nx][ny] = 1

                # 길이 있는 경우 우선순위(최소힙)가 가도록 한다.
                if maps[nx][ny] == '0':
                    heapq.heappush(q, (cnt, nx, ny))
                elif maps[nx][ny] == '1':
                    heapq.heappush(q, (cnt+1, nx, ny))
                


M, N = map(int, sys.stdin.readline().split())
maps = [list(str(sys.stdin.readline())) for _ in range(N)]
q = []
done = [[0]*M for _ in range(N)]

heapq.heappush(q, (0, 0, 0))

print(bfs())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;BFS와 우선수위 큐를 이용하는 독특한 문제이다. 처음에는 BFS로만 문제를 풀려고 했지만 어려웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;heap을 사용하는 이유는 '벽을 가장 적게 부수고 이동하는 경우'가 항상 우선순위에 있는 채로 BFS를 해야하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여기서 길(0)이 있다면 우선순위를 줘서 그 길부터 가도록 한다. 만약 길이 없고 벽(1)만 있다면 cnt+1을 해줘 벽을 부순다. 우선순위가 낮은 값부터 탐색하게 되서 원하는 동작이 구현된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>bfs</category>
      <category>백준 1261 파이썬</category>
      <category>우선순위 큐</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/134</guid>
      <comments>https://fullmoon1344.tistory.com/134#entry134comment</comments>
      <pubDate>Sat, 24 Oct 2020 19:53:19 +0900</pubDate>
    </item>
    <item>
      <title>[백준 11286] 절댓값 힙 - Python (우선순위 큐, 힙)</title>
      <link>https://fullmoon1344.tistory.com/133</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603436699969&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;11286번: 절댓값 힙&quot; data-og-description=&quot;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 0이 아니라면&amp;nbsp;배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11286&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11286&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IPunP/hyHYlNiDIe/AEv0DDkf8zE2Ej5PPXW2Lk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11286&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11286&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IPunP/hyHYlNiDIe/AEv0DDkf8zE2Ej5PPXW2Lk/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;&gt;11286번: 절댓값 힙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 0이 아니라면&amp;nbsp;배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603436742675&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
from sys import stdin

N = int(stdin.readline())
heap = []

for i in range(N):
    num = int(stdin.readline())
    if num == 0:
        if heap:
            print(heapq.heappop(heap)[1])
        else:
            print(0)

    else:
        heapq.heappush(heap, [abs(num), num]) # 절댓값 처리해 작은 값부터 힙에 넣는다.(우선순위, 값)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;push 할 때는 절댓값을 기준으로 최소 힙으로 정렬해주면, pop의 경우에 절댓값이 작은 값이 먼저 pop 되고 절댓값이 작은 경우가 여러 개일 경우에는 기존 값이 작은 수가 먼저 출력되게 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 11286 파이썬</category>
      <category>절댓값 힙</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/133</guid>
      <comments>https://fullmoon1344.tistory.com/133#entry133comment</comments>
      <pubDate>Fri, 23 Oct 2020 16:13:35 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1927] 최소 힙 - Python( 우선순위 큐, 힙)</title>
      <link>https://fullmoon1344.tistory.com/132</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603436397583&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1927번: 최소 힙&quot; data-og-description=&quot;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1927&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1927&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c4ooBl/hyHXeIZhVH/grTaKybxZcl4I1WLO37rxk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1927&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1927&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c4ooBl/hyHXeIZhVH/grTaKybxZcl4I1WLO37rxk/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;&gt;1927번: 최소 힙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603436429311&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
from sys import stdin
N = int(stdin.readline())
heap = []
for i in range(N):
    num = int(stdin.readline())
    if num == 0:
        if heap:
            print(heapq.heappop(heap)[1])
        else:
            print(0)

    else:
        heapq.heappush(heap, [num, num])&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>Heap</category>
      <category>백준 1927 파이썬</category>
      <category>최소힙</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/132</guid>
      <comments>https://fullmoon1344.tistory.com/132#entry132comment</comments>
      <pubDate>Fri, 23 Oct 2020 16:00:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준 11279] 최대 힙 - Python ( 우선순위 큐, 힙)</title>
      <link>https://fullmoon1344.tistory.com/131</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603433791578&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;11279번: 최대 힙&quot; data-og-description=&quot;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11279&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11279&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ffaBg/hyHYnEiKlb/eFU9or3kcikUWyjdSnlusK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11279&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11279&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ffaBg/hyHYnEiKlb/eFU9or3kcikUWyjdSnlusK/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;&gt;11279번: 최대 힙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째 줄에 연산의 개수 N(1&amp;le;N&amp;le;100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603433838757&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
from sys import stdin

N = int(stdin.readline())
heap = []

for i in range(N):
    num = int(stdin.readline())
    if num == 0:
        if heap:
            print(-heapq.heappop(heap))  # 음수된 값에서 가장 작은 것 빼서 다시 음수하면 최대값임
        else:
            print(0)
    else:
        heapq.heappush(heap, -num)  # 음수처리해 최대힙처럼 구성
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;파이썬에서는 최소 힙만 제공해준다. 따라서 이 문제에서는 최대 힙을 활용해야 하기에 기본 힙 모듈을 최대 힙으로 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;방식은 여러 가지가 있는 것 같다. 내가 사용한 방법은 우선 heappush를 할 때 넣어주는 값을 음수 처리를 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;그다음 heappop으로 값을 꺼내면 최솟값이 나오는데 이것을 다시 음수를 해주면 최댓값이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아래는 튜플, 리스트 형식을 활용한 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603434218340&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
from sys import stdin
N = int(stdin.readline())
heap = []
for i in range(N):
    num = int(stdin.readline())
    if num == 0:
        if heap:
            print(heapq.heappop(heap)[1])  # 값은 리스트or 튜플형태로 나오기에 1번째 요소를 뽑으면 값이 된다.
        else:
            print(0)

    else:
        heapq.heappush(heap, [-num, num])  # 값을 넣을 때 반대로 우선순위 처리해준다. (우선순위, 값)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>heapq</category>
      <category>백준 11279 파이썬</category>
      <category>우선순위 큐</category>
      <category>힙 정렬</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/131</guid>
      <comments>https://fullmoon1344.tistory.com/131#entry131comment</comments>
      <pubDate>Fri, 23 Oct 2020 15:26:33 +0900</pubDate>
    </item>
    <item>
      <title>[백준 10973] 이전 순열 - Python (순열)</title>
      <link>https://fullmoon1344.tistory.com/130</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603376574586&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;10973번: 이전 순열&quot; data-og-description=&quot;첫째&amp;nbsp;줄에 입력으로 주어진 순열의 이전에 오는 순열을 출력한다. 만약, 사전순으로 가장 처음에 오는 순열인 경우에는 -1을 출력한다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/10973&quot; data-og-url=&quot;https://www.acmicpc.net/problem/10973&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bBrMWl/hyHYm55FOV/lBdAcbc40QlQ8t0WKGFkik/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10973&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/10973&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bBrMWl/hyHYm55FOV/lBdAcbc40QlQ8t0WKGFkik/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;&gt;10973번: 이전 순열&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;첫째&amp;nbsp;줄에 입력으로 주어진 순열의 이전에 오는 순열을 출력한다. 만약, 사전순으로 가장 처음에 오는 순열인 경우에는 -1을 출력한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603376698427&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
nums = list(map(int, input().split()))

k = N-2
check = False
while k &amp;gt; -1:
    if nums[k] &amp;gt; nums[k+1]:
        for i in range(k+1, len(nums)):
            if nums[i] &amp;lt; nums[k]:  # k 이후에서 nums[k]보다 작은 것중 가장 큰 인덱스 찾음
                m = i
                check = True
    if check == True:
        break

    k -= 1


if check == False:
    print(-1)
else:
    # k, m 값 바꾸기
    nums[k], nums[m] = nums[m], nums[k]

    # k 이후 값 내림차순 정렬
    tmp = nums[k+1:]
    tmp.sort(reverse=True)

    ans = nums[:k+1] + tmp
    print(*ans)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;진짜 짜증나는 문제였다..&lt;/p&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 10973 파이썬</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/130</guid>
      <comments>https://fullmoon1344.tistory.com/130#entry130comment</comments>
      <pubDate>Thu, 22 Oct 2020 23:25:37 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1476] 날짜 계산 - Python (브루트포스)</title>
      <link>https://fullmoon1344.tistory.com/129</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1603294120838&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1476번: 날짜 계산&quot; data-og-description=&quot;준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는&amp;nbsp;나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1476&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1476&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DXNdj/hyHWd4q6Ji/khvUAPBfAKJ13SCCCjIc40/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1476&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1476&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DXNdj/hyHWd4q6Ji/khvUAPBfAKJ13SCCCjIc40/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;&gt;1476번: 날짜 계산&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는&amp;nbsp;나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;lt;내 코드&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603294171995&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;E, S, M = map(int, input().split())

e, s, m = 1, 1, 1
year = 0
while True:
    year += 1

    if e == E and s == S and m == M:
        print(year)
        break

    e += 1
    s += 1
    m += 1

    if e == 16:
        e = 1
    if s == 29:
        s = 1
    if m == 20:
        m = 1&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘 문제풀기/백준 - Python</category>
      <category>백준 1476 파이썬</category>
      <category>브루트포스</category>
      <author>태욘이</author>
      <guid isPermaLink="true">https://fullmoon1344.tistory.com/129</guid>
      <comments>https://fullmoon1344.tistory.com/129#entry129comment</comments>
      <pubDate>Thu, 22 Oct 2020 00:29:46 +0900</pubDate>
    </item>
  </channel>
</rss>